Prizes
Rabbithole awards three fixed prizes every month:
The three prizes total 5,000 USDC. The public snapshot commits the wallets, entry counts, and three winners. It does not include the prize amounts; those follow the fixed schedule above.
How Entries Are Calculated
At the end of each week, Rabbithole determines your tier from the USD value of rewards you claimed during that week, before any campaign multiplier. See Tiers and Leaderboard for the thresholds and cutoff. Every campaign has a campaign multiplier. The default is1×, and selected campaigns carry 2× or 5×. The multiplier weights the USD you claimed from that campaign before your tier multiplier is applied. It does not change your tier.
Worked Example
Suppose you claim$10.25 from a 1× campaign and $1.50 from a 5× campaign during one week.
- Your claimed value before multipliers is
$11.75, which puts you in Gold for that week. - Your weighted claimed value is
$10.25 × 1 + $1.50 × 5 = $17.75. - Gold has a
2×tier multiplier, so the result is$17.75 × 2 = 35.5. - The result is rounded down, giving you 35 entries for the week.
The Public Snapshot
When the fourth week closes, the entry pool is frozen in a public snapshot:raffleId identifies the draw, numberOfWinners fixes the number of draws, rows lists every eligible wallet with its entry count, and totalTickets is the sum of those counts. Entry counts are strings so large integers stay exact.
Wallet addresses are lowercase and sorted lexicographically. canonicalIndex is the wallet’s zero-based position in that order. The order matters when reproducing the winners.
On-Chain Commitment
Before requesting randomness, Rabbithole hashes the snapshot withkeccak256. The hash covers the exact UTF-8 bytes the endpoint returns, so hashing the raw response body reproduces it. If you rebuild the object yourself, serialize it with no whitespace and preserve this property order:
versionraffleIdnumberOfWinnersrowstotalTickets
canonicalIndex, walletAddress, then ticketCount. Rows stay in ascending canonicalIndex order with no extra properties.
The production RaffleManager is deployed on Base mainnet (chain ID 8453) at 0xbd4452ca27e71c9bd00fac9da50460d617d5d3ff. Read getRaffle(raffleId).snapshotHash and compare it case-insensitively with your computed hash. A match proves that the published wallets, entry counts, and winner count are the inputs committed before the draw.
Pyth Randomness
RaffleManager requests randomness from Pyth Entropy V2. Pyth fulfills the request in a later transaction, and RaffleManager records the finalrandomNumber on-chain in the RaffleFulfilled event and the raffle record.
You can inspect RaffleManager events on BaseScan. For example, this fulfillment transaction recorded the random number for raffle 2.
Reproducing the Winners
For each draw indexi, starting at 0, calculate:
canonicalIndex, adding their entry counts until the target falls inside a wallet’s range. Record that wallet, remove it and all of its entries, and then calculate the next draw against the new remainingEntries total. Because a winning wallet leaves the pool, each wallet can win at most once per raffle.
Draw 0 is prize rank 1, draw 1 is prize rank 2, and so on.
TypeScript Verifier
Save this asverify-raffle.mts, install viem and tsx, and run it with a snapshot URL, the RaffleManager address, and a Base RPC endpoint:
2, the verifier reports a single winner, 0x822e04f2050e146f824a908c5dd1bfd34935df8a. Anyone running it against the public snapshot and the on-chain random number gets the same result.
The verifier needs only the public snapshot, the RaffleManager address, and an RPC endpoint. Use a trusted Base RPC provider for independent verification.
Claiming Prizes
Winners are shown on the raffle page after Pyth fulfills the randomness request and Rabbithole runs the draw. If you won, the raffle page shows your prize and a Claim button once the prize is ready to be paid. Prize rank1 receives 3,000 USDC, rank 2 receives 1,500 USDC, and rank 3 receives 500 USDC.