Game Lifecycle
A QuantumRaffle V2 deployment can run an arbitrary number of sequential
games. Each game has its own locked-in configuration, its own pools,
its own Power Slots, and its own claim windows. Games never overlap — game
N+1 cannot start until game N is over.
The five phases
1. Initialization
- Host deploys the contract with an initial configuration.
gameIdstarts at0(no active game).- Host sends ETH to start game 1. The constructor's config is locked into
game 1, the host's deposit counts as the seeding entry, and a
GameStartedevent is emitted with the locked-indepositFeeBpsandminTicketsBeforeEnd.
2. Active game (entries)
Players enter by sending ETH. There are two entry paths:
| Path | Calldata | Prizes go to |
|---|---|---|
receive() |
empty | msg.sender (default behavior) |
fallback() |
exactly 20 bytes | the address encoded in calldata (cold-wallet override) |
Each entry updates:
entrantCount[gameId]entrants[gameId][entrantId]— the address recorded for ticket ownershipprizeRecipient[gameId][entrantId]— set only whenfallback()was usedprizePool[gameId]andadoptionBonusPrizePool[gameId][cohort]prizePerWinner[gameId]lastTimestamp[gameId]— resets the inactivity countdown
A GameEntered event fires per transaction.
3. Game over
The game ends when:
block.timestamp > lastTimestamp[gameId] + deadline
AND
entrantCount[gameId] >= minTicketsBeforeEnd
If minTicketsBeforeEnd is non-zero and the floor has not yet been reached,
the inactivity countdown is paused regardless of how much wall time has
elapsed. New entries continue to be accepted until the floor is met. Once
the floor is hit, the deadline behaves normally and is reset by every
subsequent entry. See Minimum Tickets Before End.
When the game is over, no new entries are accepted and claim functions unlock.
4. Claiming
| Action | Function | Who |
|---|---|---|
| Claim grand prize | claimPrize(gameId, entrantId) |
each Power Slot holder |
| Claim adoption bonus (default path) | batchClaimAdoptionBonusPrize(gameId, identifications) |
each eligible entrant |
| Claim adoption bonus (heavy-receive fallback) | claimAdoptionBonusPrize(gameId, entrantId, cohortId) |
each eligible entrant |
| Sweep the last cohort's pool | clearLeftoverAdoptionBonus(gameId) |
host |
| Withdraw accrued fees (any time) | withdrawDepositFees() |
host |
Both grand-prize and adoption-bonus payouts route through getPrizeRecipient,
so a cold-wallet override set with fallback() at entry time is honored.
See Adoption Bonus & Cohorts for how cohorts unlock and which pools each cohort can claim from. See Cold-Wallet Prize Routing for the override mechanism.
5. Next game
- (Optional) Host updates the next game's parameters via
setNextGameConfig(...). This is the only way to changedepositFeeBps,minTicketsBeforeEnd, the log bases, the proportions, the entry amount, or the deadline. The change takes effect when the next game starts. - Host sends ETH to start the next game. The current
nextGameConfigis snapshotted intogameConfigRecord[newGameId]. - Cycle repeats.
What a participant sees per game
A participant who sends k * entryAmount wei in a single deposit:
- Receives
ksequential entrant IDs (the post-depositentrantCountminusk - 1through the post-depositentrantCountitself). - Has
msg.value % entryAmountof dust refunded. - Has
effectiveValue * depositFeeBps / 10000skimmed off intoaccruedDepositFees(host revenue), whereeffectiveValue = k * entryAmount. - Has the remainder split between the grand-prize pool and the current-cohort adoption-bonus pool per the locked proportions.
The fee/floor lock guarantee
Every parameter — entryAmount, deadline, both log bases, both
proportions, depositFeeBps, and minTicketsBeforeEnd — is captured into
gameConfigRecord[gameId] at the moment gameId starts. The host
cannot mutate the active game's config; setNextGameConfig only writes
to nextGameConfig. This is a deliberate guarantee for participants: the
fee, floor, and rules they see when they enter are the rules that apply
for the entire raffle.