Provably fair dice
Every roll is derived from a randomness beacon that was committed to before your bet existed. You do not have to take our word for any of it — the seeds, the commitments and the arithmetic are all on chain, and the verifiers on this page run entirely in your browser.
How a roll is made
The house commits, in advance
Before a single bet is placed, we generate a long chain of random seeds and publish only the hash of the first one on chain. Each seed in the chain is the SHA-256 of the next, so the whole sequence is locked the moment that one hash is public.
You bet on a round nobody can see yet
Your bet is bound to the round after the next one due to be revealed. Its seed has not been published, so no one — not you, not a baker, not the house — can know what it is when your stake lands.
The seed is revealed and checked
The oracle publishes the seed. The contract refuses it unless its SHA-256 matches the value already stored from the previous round. A substituted seed would have to break SHA-256 to be accepted.
Your roll falls out of the maths
The roll is SHA-256 over the revealed seed, your own client seed, and your bet id — reduced mod 6. Anyone can recompute it. The verifier below does exactly that, in your browser.
Where the randomness comes from
Two independent sources, one from each side of the bet. Neither party can see the other's before committing to their own.
The house — 32 bytes, once
When a chain is created, the server draws 32 bytes from the operating system's cryptographic random number generator — the same source that generates encryption keys. That single draw is the only randomness the house ever contributes. Every one of the million seeds after it is plain SHA-256 derivation, which is what lets you verify the chain without trusting us.
The seed it all derives from is encrypted with AES-256-GCM and never leaves the server. Only its hash is published, before any bet exists.
You — 16 bytes, every bet
Your browser generates a fresh client seed for each bet, from crypto.getRandomValues— the browser's own cryptographic generator. We never see it before it arrives, because it travels inside your bet transaction, and it is mixed into your roll alongside the beacon.
It is visible in your own transaction on TzKT, so you can check the value we used is the value you sent.
“Could you not just keep generating chains until you got a good one?”
No, because there is no such thing as a good one. A roll is not decided by the seed alone — it is decided by the seed together with your client seed and your bet id, and neither of those exists when the chain is created. There is nothing to aim at. Searching for a favourable chain would mean searching for one that beats bets nobody has placed yet, made by players who have not generated their seeds yet.
Once the chain's first hash is published, the sequence is fixed. From that point the only thing anyone can do is reveal the seeds in order and let the arithmetic run.
The contracts, on shadownet
Read the code and every seed ever revealed. Nothing here needs our cooperation.
Randomness beacon
KT1KJqsWsj2UK4j2C4nAVbV9RTXw1MKdSA2Yholds the seed chain — the beacons bigmap is where a revealed seed lives
Checking the chain for yourself
Everything above rests on one claim: the seed that decided your roll was fixed before you placed the bet. Here is how you check that claim yourself, without taking our word for any step.
- 1
Start at your seed
Your roll used the seed for one specific round. It is in the beacon contract's beacons bigmap, keyed by round number, and it was also emitted as an event when it was revealed.
- 2
Hash it, and you get the round before
SHA-256 of your seed equals the seed revealed one round earlier. Not a coincidence — the chain was built by hashing backwards from a secret, so hashing forwards walks you back through it.
- 3
Keep going, all the way to round 0
Every seed hashes to its predecessor. Each of those is an independent check; none of them requires trusting us.
- 4
Round 0 hashes to the origination commitment
And that value was written into the contract when it was created — a timestamped, immutable fact recorded before anyone had bet anything. This is the step that makes the rest mean something.
To fake a single roll, we would need a seed that hashes to a value already published — a SHA-256 preimage. The seed is 32 bytes, so finding one means searching a space of 2256. Merely counting that high, at the theoretical minimum energy per step, would take on the order of a trillion times the Sun's entire lifetime output.
Watch the chain being verified
Every seed the beacon has published, hashed one at a time in your browser, and checked against the round before it.
Check the chain held
Every seed must hash to the one revealed before it. This is what stops the house swapping a seed after seeing your bet.
Verify it yourself, offline
const { createHash } = require("crypto");
const beacon = "<seed the oracle published for your round>";
const clientSeed = "<the client seed from your bet>";
const betId = 0n;
const sha = (b) => createHash("sha256").update(b).digest();
// bytes(betId): minimal big-endian, empty for zero (Michelson NAT-to-BYTES)
let idHex = betId === 0n ? "" : betId.toString(16);
if (idHex.length % 2) idHex = "0" + idHex;
const digest = sha(Buffer.concat([
Buffer.from(beacon, "hex"),
sha(Buffer.from(clientSeed, "hex")),
Buffer.from(idHex, "hex"),
]));
console.log("Rolled:", Number(BigInt("0x" + digest.toString("hex")) % 6n) + 1);