How to verify a provably fair bet
What you need
To verify one bet you need:
- The server seed used for the bet, revealed after rotation.
- The hashed server seed you were shown before playing.
- The client seed active when you placed the bet.
- The bet's nonce.
- The game and any settings that affect the outcome, such as plinko rows and risk, or number of mines.
You also need the casino's documentation of its scheme, because implementations differ.
Step 1: record the commitment before you play
Open the casino's fairness or seed settings and copy the hashed server seed. Store it outside the casino: a note, a screenshot, a message to yourself. This step is often skipped, and it is the one that protects you if the casino later shows a different hash in its history.
While you are there, change the client seed to something of your own. Write it down.
Step 2: play and note your bets
Play normally. If you want to verify specific bets, note their bet IDs or nonces from your history.
Step 3: rotate the seed pair
Click rotate, change seed or similar. The casino reveals the old server seed and shows a new hash for the next one. Your nonce resets.
Step 4: check the hash
Compute the SHA-256 hash of the revealed server seed and compare it with the hash you recorded. In the common scheme, the hash is computed over the seed as text, exactly as displayed. Every character must match.
If it does not match, check you have the right seed pair. If you do, the commitment has failed, and no result from that seed can be trusted.
Step 5: recompute the random numbers
The common scheme generates bytes like this:
HMAC-SHA256(key = serverSeed, message = clientSeed:nonce:round)
- The key is the server seed.
- The message joins the client seed, nonce and round with colons. Round starts at 0.
- Each HMAC produces 32 bytes.
Bytes are turned into floats four at a time. For bytes b1, b2, b3, b4:
float = b1/256 + b2/256^2 + b3/256^3 + b4/256^4
This gives a number from 0 up to, but not including, 1. One HMAC gives 8 floats. If a game needs more, the round number increases to 1, 2 and so on, and the bytes continue from the new HMAC. This is the cursor.
Worked example
Using made-up seeds:
| Value | Example |
|---|---|
| Server seed | 5f3c9a1e7b2d4c6f8a0e1b3d5c7f9a2e4b6d8c0f1a3e5b7d9c2f4a6e8b0d1c3f |
| Hashed server seed (SHA-256 of the seed text) | c498ac2d22fbdecd7e5ad767c84166d53b7fe56abe1db19415a8197da745c7de |
| Client seed | my-client-seed |
| Nonce | 0 |
| HMAC message | my-client-seed:0:0 |
| HMAC-SHA256 output | e946af2393f1e064d53af4f70125abe23306dca539cf9f4e88f1c58b5f154d12 |
| First four bytes | 233, 70, 175, 35 |
| First float | 0.91123... |
233/256 + 70/65,536 + 175/16,777,216 + 35/4,294,967,296 = 0.91123 to five places.
You can paste the server seed and client seed into our verifier and check the hash and the dice result in this table.
Step 6: apply the game formula
Each game turns floats into a result. These are the common formulas; casinos may use different ones.
Dice
roll = floor(float x 10,001) / 100
This gives a result from 0.00 to 100.00. In the example, 0.91123 x 10,001 = 9,113.2, floored to 9,113, giving a roll of 91.13.
Limbo
A common approach is:
result = floor((1 - houseEdge) / float x 100) / 100, with a minimum of 1.00.
With a 1% house edge, 0.99 / 0.91123 = 1.0864, giving 1.08x in the example. A target of 2.00x would have lost.
Plinko
Each row uses one float. A common rule is floor(float x 2): 0 means the ball goes left, 1 means right. Count the right moves to find the landing slot, then look up the multiplier in the payout table for your row count and risk level. A 16-row game needs 16 floats, so it uses round 0 and round 1 of the HMAC.
Mines
The mine layout is a shuffle of the 25 tile positions. In a common implementation, the game starts with a list of positions 0 to 24 and, for each step i, picks floor(float x (25 - i)) as an index into the remaining list, removes that position and appends it to the shuffled order. The first N positions in the shuffled order are mines. Multiple HMAC rounds are used as needed.
Step 7: compare
Compare your recomputed result with the casino's bet record. For dice and limbo, compare the number. For plinko, compare the slot. For mines, compare the mine positions shown after the round.
Reading a mismatch
Most mismatches come from inputs, not fraud. Check in this order:
- Nonce off by one. Some casinos start at 0, some at 1.
- Wrong client seed. If you changed it mid-session, earlier bets used the old one.
- Wrong seed pair. Make sure the revealed server seed belongs to the period of the bet.
- Game settings. Plinko rows and risk, number of mines.
- Different scheme. The casino may use a different message format, byte conversion or formula. Read its documentation.
If all inputs are correct, the casino documents the common scheme, and the result still does not match, keep screenshots of the bet, seeds and documentation and contact the casino. Please also send us the details through the feedback form below.
Limits to keep in mind
- Verification proves results followed the committed seeds and formula. It does not change the house edge.
- It applies only to games built on the scheme. Third-party slots and live games are outside it.
- Multiplayer games such as crash often use a shared hash chain instead of your seeds. Use the casino's own verification instructions for those.
Verifying in code
If you prefer to check without any website, this Node.js snippet implements the common scheme. It prints the SHA-256 hash of the server seed, the first float, and the dice roll for the worked example above.
const crypto = require('crypto');
function floats(serverSeed, clientSeed, nonce, count) {
const out = [];
for (let round = 0; out.length < count; round++) {
const h = crypto.createHmac('sha256', serverSeed)
.update(`${clientSeed}:${nonce}:${round}`)
.digest();
for (let i = 0; i < 32 && out.length < count; i += 4) {
out.push(h[i] / 256 + h[i + 1] / 256 ** 2 + h[i + 2] / 256 ** 3 + h[i + 3] / 256 ** 4);
}
}
return out;
}
const serverSeed = '5f3c9a1e7b2d4c6f8a0e1b3d5c7f9a2e4b6d8c0f1a3e5b7d9c2f4a6e8b0d1c3f';
const hash = crypto.createHash('sha256').update(serverSeed).digest('hex');
const [f] = floats(serverSeed, 'my-client-seed', 0, 1);
const dice = Math.floor(f * 10001) / 100;
console.log(hash, f.toFixed(5), dice);
Change the seeds and nonce to your own values. For other games, request more floats and apply the casino's documented formula. If the casino's documentation differs from this scheme, follow the documentation.
Keeping a verification log
If you play regularly, keep a simple log for each seed pair: the hashed server seed and the date you recorded it, your client seed, the revealed server seed, the range of nonces used, and the bets you verified. It takes a minute and gives you evidence if you ever need to raise a dispute.