Fairness
OPDuel Originals
Limbo
Plinko
Keno
Roulette
Mines
Blackjack
Roll
Dice
Provably Fair Keno
Keno draws 10 numbers from a pool of 40. Pick your numbers before the draw, and your payout scales with how many you match.
Verify Game Fairness
The algorithm uses selection without replacement - each number is removed from the pool after being drawn, ensuring no duplicates.
The Algorithm
// Step 1: Generate 10 floats (needs 40 bytes = 2 hash rounds)
const floats = [];
for (let round = 0; round < 2; round++) {
const message = `${clientSeed}:${nonce}:${round}`;
const hash = HMAC_SHA256(serverSeed, message);
// Extract floats from this round's hash
for (let i = 0; i < 8 && floats.length < 10; i++) {
const bytes = hash.slice(i * 4, i * 4 + 4);
let float = 0;
for (let j = 0; j < 4; j++) {
float += bytes[j] / Math.pow(256, j + 1);
}
floats.push(float);
}
}
// Step 2: Select numbers without replacement
const pool = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40];
const result = [];
for (let i = 0; i < 10; i++) {
const index = Math.floor(floats[i] * pool.length);
result.push(pool.splice(index, 1)[0]);
}How It Works
- Generate floats - Keno needs 10 random numbers. Since each hash provides 32 bytes and each float uses 4 bytes, a single hash can produce 8 floats. Keno requires 2 hash rounds (rounds 0 and 1) to get all 10 floats.
- Create the pool - Start with numbers 1 through 40 in an array
- Selection without replacement - For each of the 10 picks:
- Multiply the float by the current pool size
- Floor to get an index
- Remove and record the number at that index
- The pool shrinks by 1 each time
This method ensures every number has an equal chance of being selected, and no number can be drawn twice.
Example
Given these inputs:
- Server Seed:
c5e7146c5863d7d647c93ffe7d4ec13fffbb7311108fab0c067d97bcc2b32d55 - Client Seed:
LuckyClientSeed777 - Nonce:
1
Keno needs 10 floats (40 bytes), requiring 2 hash rounds. The floats extracted:
| Float | Value |
|---|---|
| 1 | 0.55862593 |
| 2 | 0.28559087 |
| 3 | 0.36156606 |
| 4 | 0.48979709 |
| 5 | 0.38374817 |
| 6 | 0.59817973 |
| 7 | 0.64279013 |
| 8 | 0.34504414 |
| 9 | 0.28650033 |
| 10 | 0.45604396 |
Selection without replacement:
| Pick | Float | Pool Size | Index | Number Selected |
|---|---|---|---|---|
| 1 | 0.558626 | 40 | floor(0.558626 × 40) = 22 | 23 |
| 2 | 0.285591 | 39 | floor(0.285591 × 39) = 11 | 12 |
| 3 | 0.361566 | 38 | floor(0.361566 × 38) = 13 | 15 |
| 4 | 0.489797 | 37 | floor(0.489797 × 37) = 18 | 21 |
| 5 | 0.383748 | 36 | floor(0.383748 × 36) = 13 | 16 |
| 6 | 0.598180 | 35 | floor(0.598180 × 35) = 20 | 26 |
| 7 | 0.642790 | 34 | floor(0.642790 × 34) = 21 | 28 |
| 8 | 0.345044 | 33 | floor(0.345044 × 33) = 11 | 13 |
| 9 | 0.286500 | 32 | floor(0.286500 × 32) = 9 | 10 |
| 10 | 0.456044 | 31 | floor(0.456044 × 31) = 14 | 20 |
The 10 selected numbers: 23, 12, 15, 21, 16, 26, 28, 13, 10, 20.

