How the draw is proven fair
The whole point of an on-chain lottery is that you don’t have to trust us. Everything needed to recompute any winner is public. This page documents exactly how.
Draw algorithm
The selection is deterministic given public inputs. In plain pseudocode, here is exactly what runs at every draw (one every 20 minutes):
# Jackpot winner selection — algorithm v1.0.0
# Published per draw: the eligible-holder snapshot and seedHex.
function selectWinner(holders, seedHex):
# 1. Eligible set: all holders minus excludedAddresses (applied
# upstream), minus any zero / below-minimum balances.
eligible = holders.filter(h => h.balance > 0)
# 2. Canonical order: lowercase every address, sort ascending.
eligible = sortByAddressAsc(lowercaseAddresses(eligible))
# 3. Cumulative ticket ranges in base units: holder i owns the
# half-open range [prefix_i, prefix_i + balance_i).
total = sum(h.balance for h in eligible)
ranges = cumulative(eligible)
# 4. Map the seed to a ticket. seedHex is the drand round randomness
# (0x hex); hash its raw bytes, read big-endian, reduce mod total.
# The seed alone is hashed — no timestamp or round is mixed in.
pick = uint256(keccak256(bytes(seedHex))) mod total
# 5. Winner = first holder whose cumulative > pick (range holds pick).
winner = ranges.firstWhere(r => r.cumulative > pick)
return { winner, pick, total }
# Snapshot commitment (the hash recorded with every draw):
# snapshotHash = keccak256(utf8( join("\n",
# ["<address>:<balanceBaseUnits>" for each holder],
# lowercased and sorted ascending by address) ))Address ordering is fixed (ascending by address), so two people running the same snapshot and seed always arrive at the same winner.
Randomness source
Robinhood Chain has no native VRF yet, so each draw’s seed comes from the drand public randomness beacon run by the League of Entropy — a distributed network that emits a fresh, publicly verifiable random value on a fixed schedule. The beacon round for each draw is pinned in advance, so nobody (including us) can predict or grind it, and anyone can fetch the exact same value afterward from the drand quicknet chain at api.drand.sh.
Every draw publishes its beacon seed and a hash of the holder snapshot next to the winner, so the whole selection can be recomputed from public data. Seeds are recorded in the draws table. If Robinhood Chain ships a native VRF later, it can replace drand without changing how draws are verified.
Eligibility & exclusions
Every wallet holding $JACKPOT is eligible by default, weighted by balance. A small, versioned list of system addresses is excluded so the pool can only be won by real holders:
Current eligible supply: 0 JACKPOT. Exact addresses are published on-chain and updated only through a public changelog.
Verify a draw yourself
- 1Download the snapshot for the draw: GET /api/draws/{id}/snapshot. It returns the exact canonical preimage in the `canonical` field, plus the holder list, snapshotHash, seedHex, pick and winner.
- 2Hash the canonical text and confirm keccak256(utf8(canonical)) equals the draw’s snapshotHash. canonical is raw text, not JSON: one lowercased "address:balanceBaseUnits" line per holder, sorted ascending by address, joined by newlines.
- 3Fetch the matching round from the drand quicknet chain (api.drand.sh) and confirm its randomness equals the recorded seedHex.
- 4Compute pick = keccak256(seedHex bytes) mod totalEligibleSupply, then walk the cumulative ranges (holders sorted ascending by address) to the holder whose range contains pick.
- 5Confirm the result matches the published winner and the on-chain payout transaction.
CoinFlip fairness
The CoinFlip game is a separate product from the 20-minute draw, but it holds to the same principle: you don’t have to trust the operator. Robinhood Chain has no native VRF, so each flip is settled by a commit-reveal scheme that makes the outcome fixed before you bet and verifiable after.
How a flip is settled
- 1Commit. The operator generates secret serverSeeds and pre-commits batches of keccak256(serverSeed) to the contract. Only the hashes are on-chain — the seeds stay secret until a bet consumes one.
- 2Bet. You call heads or tails, approve the stake, and place the bet with your own clientSeed. The bet is bound to the next unused commitment, your address, and a unique betId — none of which the operator can change after the fact.
- 3Reveal. The operator publishes the matching serverSeed. Anyone can check keccak256(serverSeed) equals the hash committed up front, so the seed can't be swapped for a more favourable one.
- 4Resolve. The outcome is the parity of keccak256(serverSeed, betId, player, clientSeed): even is heads, odd is tails. Because every input was fixed before the reveal, the result is fully determined and no party can steer it.
No reveal? You win by default.
An operator could try to censor a losing-for-the-house reveal by staying silent. To make that pointless, a bet left unresolved past the reveal timeout (10 minutes) can be claimed by the player as a win, straight from the contract. Silence costs the house, so revealing honestly is always the operator’s best move.
Verify a flip yourself
Every resolved bet emits its serverSeed on-chain next to the betId, player, and clientSeed from placement. That is everything needed to recompute the result:
# Verify a CoinFlip outcome — recompute it from on-chain data
# Inputs are all public once the operator reveals: serverSeed, betId,
# player, clientSeed. The commitment H(serverSeed) was posted BEFORE the bet.
# 1. Confirm the commitment the bet consumed matches the revealed seed.
assert keccak256(serverSeed) == committedHash # from the BetPlaced batch
# 2. Recompute the outcome hash exactly as the contract does.
h = keccak256(abi.encodePacked(
serverSeed, # bytes32
betId, # uint256
player, # address
clientSeed)) # bytes32
# 3. The landed side is the parity of that hash.
side = (uint256(h) & 1 == 0) ? "heads" : "tails" # even = heads
# 4. You won if the side you called equals the landed side.
won = (side == yourCall)The demo on /flip runs this exact rule locally, so a simulated flip verifies by the same four steps as a live on-chain one. The CoinFlip contract address is also exempt from the $JACKPOTtransfer tax, so bets aren’t double-taxed on the way in and out.
FAQ
Disclaimers
Not affiliated with Robinhood Markets, Inc. Jackpot is an independent project and is not affiliated with, endorsed by, or connected to Robinhood Markets, Inc. or its subsidiaries. All third-party names and marks belong to their respective owners and are used only for identification.
Jackpot is experimental software involving a volatile crypto asset and an on-chain lottery mechanic. Nothing here is financial, legal, or tax advice. Lotteries and token sales are restricted or prohibited in some jurisdictions — it is your responsibility to know your local law before participating.
Token contract address and chain are to be finalized before launch. Figures shown while in preview are illustrative mock data.