How a provably fair crash round is actually generated
A crash multiplier is derived from a hash of a server seed the operator committed to before the round, combined with a client seed the player controls. Publish the commitment first and anyone can check afterwards that the result was not chosen.
The problem it solves
A player cannot see your random number generator. They have to take your word that the 1.02x they got was not chosen because they had staked more than usual. "Provably fair" is the arrangement that removes the need to take anybody's word for it.
It works by making the operator commit to the outcome before knowing the stake, and by making that commitment checkable afterwards. Nothing about it requires trusting the operator, the player, or us.
The three inputs
- A server seed, generated by us and kept secret until the round ends. Its SHA-256 hash is published before the round opens: that hash is the commitment.
- A client seed, which the player sets and can change whenever they like. This is what stops us from choosing a server seed that produces a convenient result, because we would have to know the client seed first.
- A nonce, the round number for that pair of seeds. It makes each round in a sequence distinct without needing new seeds every time.
Turning a hash into a multiplier
The three inputs are concatenated and hashed. The first bytes of that hash are read as an integer and mapped onto a distribution whose expected return matches the game's published RTP.
The house edge lives in exactly one place: a small share of outcomes is forced to the lowest multiplier. Everything else follows from the hash. That is what makes the maths auditable — there is one constant to check, not a model to trust.
// The verification, in full. Nothing here is specific to us.
const crypto = require('node:crypto');
function crashPoint(serverSeed, clientSeed, nonce, houseEdge = 0.01) {
const h = crypto
.createHmac('sha256', serverSeed)
.update(`${clientSeed}:${nonce}`)
.digest('hex');
// The instant-bust share. This is the house edge, and it is the only
// number in the scheme that is a choice rather than a consequence.
const bustChance = Math.floor(2 ** 52 / houseEdge);
if (parseInt(h.slice(0, 13), 16) % bustChance === 0) return 1.00;
const r = parseInt(h.slice(0, 13), 16) / 2 ** 52;
return Math.floor((100 * (1 - houseEdge)) / (1 - r)) / 100;
}What to check, as a player
After a round, the server seed is revealed. Three things must hold, and all three are arithmetic rather than opinion:
- The SHA-256 of the revealed server seed equals the hash published before the round.
- Running the function above on the revealed seed, your client seed and the nonce reproduces the multiplier you saw.
- The nonce increased by exactly one since your last round on that seed pair.
If any of those fails, the result was changed after the fact. If all three hold, it could not have been.
Where this is weaker than it sounds
Provable fairness proves that a specific round was not tampered with. It does not prove the house edge is what was advertised, and it does not prove the operator is solvent. Those are different questions and it is worth being honest that one scheme does not answer all three.
The edge is checkable a different way: play enough rounds and the distribution is what it is. Publish the constant, and anybody with the round history can test it. We publish ours on every game page for that reason.
Try it against a real API
Sandbox keys are issued instantly, with play money and every game enabled. No card, no call, and the documentation is public whether or not you sign up.
More notes
- What a casino wallet integration actually has to doFive endpoints: balance, debit, credit, refund and rollback. The only hard requirement is that debit and credit are idempotent on the transaction id you are given — everything else is detail.
- RTP, house edge and the number operators should actually checkRTP is the long-run share of stakes returned to players; the house edge is what is left. The number worth checking is not the advertised RTP but whether the running game reproduces it — which requires the operator to be able to see their own round history.
- Idempotency in a betting API, and why the key is yoursThe safest design gives the caller the idempotency key rather than generating one. If you mint the transaction id, a retry after a timeout is unambiguous — and a timeout is the one failure every integration eventually meets.