Settlement Compute¶
Datei:
core/settlementCompute.ts(299 Zeilen) Typ: Reine Berechnung (keine Side-Effects, keine I/O) Aufgerufen von:cli/run.ts
Kernfunktion¶
Eingabe-Datenstruktur¶
interface PoolInput {
pot: bigint; // potAcc - Regular Pot (60% vom Pot-Anteil)
rollover: bigint; // rolloverAcc - 5% Rollover
jackpotReserve: bigint; // jackpotReserveAcc - 40% vom Pot-Anteil
jackpotNoHitStreak: number; // Tage ohne 6er
winnersByClass: {
2: WinnerRef[]; // 3 Richtige
3: WinnerRef[]; // 4 Richtige
4: WinnerRef[]; // 5 Richtige
5: WinnerRef[]; // 6 Richtige (Jackpot)
}
}
Berechnungsalgorithmus¶
Schritt 1: Gesamtpot berechnen¶
gesamtpot = pot + rollover
newRollover = gesamtpot * 5% // Für nächsten Tag
payoutBasis = gesamtpot * 95% // Verfügbar für Auszahlung
Schritt 2: Klassen-Anteile vom payoutBasis¶
| Klasse | Treffer | Anteil am payoutBasis |
|---|---|---|
| classId 2 | 3 Richtige | 0% (keine Auszahlung) |
| classId 3 | 4 Richtige | 60% |
| classId 4 | 5 Richtige | 40% |
| classId 5 | 6 Richtige | Separate Jackpot-Reserve |
// settlementCompute.ts:90-95
const CLASS_SHARE_BP: Record<number, number> = {
2: 0, // 3er - kein Gewinn
3: 6000, // 4er - 60%
4: 4000, // 5er - 40%
5: 0 // 6er - aus jackpotReserve
};
Schritt 3: Jackpot-Logik¶
Drei mögliche Szenarien pro Pool:
A) 6er getroffen → gesamte jackpotReserve → an Gewinner
jackpotHit = true
jackpotPayout = jackpotReserve
B) Kein 6er, aber noHitStreak >= Trigger-Tage → Redistribution
jackpotReserve wird 50/50 auf s5 und s4 verteilt
(zusätzlich zu deren regulären Anteilen)
C) Kein 6er, Streak unter Trigger → Reserve wächst weiter
jackpotReserve bleibt unverändert
// Pseudocode
if (winners6er.length > 0) {
// Szenario A: Jackpot getroffen
jackpotPayout = jackpotReserve;
jackpotHit = true;
} else if (noHitStreak >= jackpotMissTriggerDays) {
// Szenario B: Redistribution
s5bonus = jackpotReserve / 2;
s4bonus = jackpotReserve - s5bonus; // Dust an s4
jackpotPayout = jackpotReserve;
} else {
// Szenario C: Reserve wächst
jackpotPayout = 0n;
}
Schritt 4: Gleichmäßige Verteilung¶
// settlementCompute.ts:105-121
function distributeEven(share: bigint, n: number): bigint[] {
if (n === 0) return [];
const base = share / BigInt(n); // Grundbetrag
const remainder = share % BigInt(n); // Rest (Dust)
const amounts = Array(n).fill(base);
// Ersten `remainder` Gewinnern je +1 (Dust-Handling)
for (let i = 0; i < Number(remainder); i++) {
amounts[i] += 1n;
}
return amounts;
}
Invariante: sum(amounts) === share - exakt, kein Rundungsverlust.
Ausgabe-Datenstruktur¶
interface SettlementComputeResult {
poolPayoutArr: bigint[]; // [5] Regular-Pot-Auszahlungen
poolJackpotPayoutArr: bigint[]; // [5] Jackpot-Reserve-Auszahlungen
jackpotHitArr: boolean[]; // [5] Jackpot getroffen?
totalPayout: bigint; // Summe aller Auszahlungen
winnersRaw: WinnerRaw[]; // Pro-Tip-Gewinner
winnersAgg: WinnerAgg[]; // Pro-Spieler-Aggregation
poolsAudit: PoolAudit[]; // Audit-Trail pro Pool
}
Audit-Trail¶
Für jeden Pool wird ein detaillierter Audit-Record erstellt:
{
"poolIndex": 0,
"pot": "1500000000",
"rollover": "200000000",
"jackpotReserve": "800000000",
"gesamtpot": "1700000000",
"payoutBasis": "1615000000",
"newRollover": "85000000",
"noHitBefore": 3,
"noHitAfter": 4,
"redistribute": false,
"classShares": {
"s3": "969000000",
"s4": "646000000",
"s5": "0",
"s6": "0"
},
"classWinners": { "2": 0, "3": 12, "4": 3, "5": 0 },
"poolPaid": "1615000000",
"jackpotPaid": "0"
}
Charity-Berechnung¶
Datei:
core/charityCompute.ts(11 Zeilen)
function computeCharity(
operatorFeeTotal: bigint,
affiliateTotal: bigint,
charityRateBp: number
): bigint {
const basis = operatorFeeTotal - affiliateTotal;
return (basis * BigInt(charityRateBp)) / 10000n;
}
Formel: charity = (feeAcc - affiliateTotal) * charityRateBp / 10000
Zahlenbeispiel¶
Tag 20500, Pool 0:
Eingabe:
pot = 10.000 USDT
rollover = 2.000 USDT
jackpotReserve= 5.000 USDT
noHitStreak = 2
Berechnung:
gesamtpot = 12.000 USDT
newRollover = 600 USDT (5%)
payoutBasis = 11.400 USDT (95%)
s4-Anteil (4er, 60%) = 6.840 USDT
s3-Anteil (5er, 40%) = 4.560 USDT
3 Gewinner mit 4 Richtigen:
6.840 / 3 = 2.280 USDT pro Gewinner
1 Gewinner mit 5 Richtigen:
4.560 / 1 = 4.560 USDT
Kein 6er → jackpotReserve bleibt 5.000 USDT
noHitStreak: 2 → 3
Ausgabe:
poolPayout = 11.400 USDT
poolJackpotPayout = 0 USDT
totalPayout = 11.400 USDT
Invarianten¶
Datei:
core/invariants.ts(18 Zeilen)
// Winners-Merkle-Root darf NIEMALS 0x0 sein
// (Dummy-Leaf wird eingefügt wenn keine Gewinner)
assertMerkleRootNonZero(merkleRoot);
// Affiliate-Root MUSS 0x0 sein wenn affiliateTotal == 0
// Affiliate-Root MUSS non-zero sein wenn affiliateTotal > 0
assertAffiliateRootRule(affiliateRoot, affiliateTotal);