Provably Fair Slots

How to prove we didn't cheat

Every spin's result is math — not a decision. The server commits to a secret seed before you bet, your browser adds its own randomness per spin, and anyone with the revealed seed can re-run the calculation and check that the grid you saw is the one the math produces. This page explains exactly how to do it.

How a spin is decided

  1. 1
    Server commits to a seed
    When you open a session we generate a random 32-byte serverSeed and show you its hash serverSeedHash = keccak256(serverSeed). You see this hash in the Seed bar above the reels. We can't change the seed after committing without changing this hash.
  2. 2
    Your browser adds a fresh seed per spin
    On every Spin, your browser generates a random 32-byte clientSeed using the Web Crypto API. The server never sees it before you submit it. A monotonic nonce counts up so every spin has a unique input.
  3. 3
    Server runs the math and shows the result
    reelHash = keccak256(serverSeed, clientSeed, nonce). The first 20 bytes give the 5 reel stops (each byte-group mod 30). Reading 4 consecutive symbols from each reel strip gives you a 5×4 grid. Paytable is applied — totalWin is calculated and credited.
  4. 4
    You get the seed back and verify
    After 50 spins the seed auto-rotates, or you can press 🍀 Reset Luck to force it. At that moment the original serverSeed is revealed to you. You can then check keccak256(revealedSeed) == hash and rerun the spin math for any historical spin you took.

What you need to save

Auditing only works if you have the evidence. Before the session ends:

  • Server seed hash — from the Seed bar on the slots page. Copy it or screenshot when your session opens.
  • Per-spin clientSeed and nonce — shown in the “Provably fair” expander for the most recent spin. To audit all 50, you'd currently need to log each one yourself. A built-in history exporter is on the roadmap.
  • The grid you saw — for proving the server showed you the correct visual. Screenshot or note the 5×4 symbols per spin.
  • !Reveal the seed by pressing 🍀 Reset Luck or waiting for auto-rotate. You can't verify without it.

Verifier

Paste a revealed serverSeed, theclientSeed you used, and the spinnonce. Runs in your browser — nothing is sent to us.

Paste all three values as shown in your records. Seeds are 0x + 64 hex characters. Nonce is an integer.

About Reset Luck 🍀

Pressing 🍀 Reset Luck immediately ends the current seed session: the server reveals the seed you were committed to, and commits a fresh one. Your next spin uses the new seed.

It's two things at once. From a game perspective: feeling unlucky on a cold streak? Get a new seed and start over. From a fairness perspective: every reset is your cue to verify the batch of spins you just took. The natural cadence of "I feel like resetting" becomes a natural audit trail.

Seeds also auto-rotate every 50 spins, so if you never reset, you still get a reveal cycle.

What's enforced on-chain

Seed commitment is on Arbitrum. When your session starts, the operator callscommitSlotSeed(player, hash)on the RouletteBatchV5 contract. The tx is public and immutable. The Seed strip on the slots page shows an “on-chain ↗” link to this tx.

Settlement is on-chain too. When the session ends (either Reset Luck or after 50 spins), the contract callssettleSlotSession(player, revealedSeed, netDeltaCents). The contract itself verifies keccak256(revealedSeed) == committedHash — if the operator tries to reveal a different seed than what was committed, the tx reverts and no balance moves. Your net P&L is applied: losses move from your on-chain balance to the bankroll, wins the opposite direction.

What's still operator-trust: the operator computes the net P&L number submitted at settle time. The individual spin math is still off-chain for UX speed (gas-free spins). So the operator could theoretically mis-report the net across all your spins. But — you can detect this by replaying every (revealedSeed, clientSeed, nonce) locally and summing the wins yourself; any mismatch is proof. The operator can't tamper retroactively because the seed is now bound to the on-chain commit.

V5 roadmap: the contract will re-run each spin's reel+paytable math internally at settle time, so even net P&L can't be mis-stated. For now, V5 gets us from “trust us entirely” to “we're cryptographically bound to the seed, and you can audit the math yourself.”

Algorithm reference

Full logic, to the byte:

// Reel strip (same for all 5 reels). 30 stops.
REEL = [0, 2, 1, 3, 0, 4, 2, 1, 5, 0, 3, 2, 6, 1, 4, 0, 3, 7, 1, 5, 2, 6, 0, 3, 8, 4, 2, 6, 5, 9]

// Symbols:  0=Lemon  1=Cherry  2=Orange  3=Grape  4=Watermelon
//           5=Clover 6=Bell    7=Seven   8=Wild   9=Scatter

// For each spin:
//   hash = keccak256(abi.encodePacked(serverSeed, clientSeed, nonce))
//   bytes = the 32 bytes of that hash
//   For reel r in 0..4:
//     stop_r = uint32_big_endian(bytes[r*4 .. r*4+3]) mod 30
//     visible column = [REEL[stop_r], REEL[(stop_r+1)%30], REEL[(stop_r+2)%30], REEL[(stop_r+3)%30]]

// Then apply the paytable over 20 paylines. WILD substitutes for everything except SCATTER.
// See the full paytable on the slots page under "Paytable".