Crash, Slide & Double
Crash, Slide and Double are played in rounds shared by every player. One result decides everyone’s bet, so the result cannot depend on any single player’s seed pair. Instead, each round gets its randomness from two sources the casino cannot control:
- a server seed that is committed (as a hash) before bets open, and
- a drand beacon, a public random value published by an independent network after bets close.
The casino fixes its seed before the beacon exists, and nobody (the casino included) knows the beacon while bets are open. Neither side can steer the result.
How a round is built
- Commit. When a round opens, a fresh random server seed
Sis drawn (64 hex characters) and its hashsha256(S)is published. This is the Server Seed (Hashed) of the round, the same kind of commitment as in the other games. - Fix the beacon. While bets are open, the round announces which drand
round
Rwill decide it, and bets close one second beforeRis published. Nobody can place a bet knowingrandomness(R). - Draw. When
Ris published, the randomness is verified against the drand public key and the result is computed from it (below). - Reveal. The server seed
Sis published with the result, so anyone can check the commitment and recompute the outcome.
The drand beacon
drand is a public randomness beacon run by the League of Entropy. The rounds use its quicknet chain:
| Chain hash | 52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971 |
| Period | one round every 3 seconds |
| Genesis | Unix time 1692803367 |
| Scheme | bls-unchained-g1-rfc9380 (BLS signatures, unchained) |
| Public key | 83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a |
Round R is published at Unix time 1692803367 + (R − 1) × 3. Each round’s
signature is a BLS threshold signature over the round number, and its
randomness is sha256(signature). Nobody can produce the signature before
the network does, and anybody can check it against the public key above.
You can read any round from a public relay:
https://api.drand.sh/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971/public/10000000{
"round": 10000000,
"randomness": "5e6508f1a1f689accfc8b027dec7eb999b20b4f66582b2621231cabf9601bf57",
"signature": "a187c9f5800521e986b5658924696682071ce7ae965486e8fd08bc2c4758bdbb9ed9ecb7beee392a38214e321ecd434f"
}The server checks the BLS signature of the beacon before it uses it, and any relay
(api.drand.sh, api2.drand.sh, api3.drand.sh, drand.cloudflare.com) gives
the same values. To check the signature yourself, use a drand client library with
the chain hash above.
From beacon to result
The result uses the same generator as the other games, with these inputs:
| Input | Value in a shared round |
|---|---|
| Server seed | The round’s committed seed S |
| Client seed | The beacon’s randomness (64 hex characters) |
| Nonce | The drand round number R |
| Cursor | 0, one float |
float = first float of HMAC_SHA256(key = S, message = `${randomness}:${R}:0`)That float is then mapped by the game:
import Decimal from "decimal.js";
// Crash and Slide: the same formula, with the round's house edge.
function crashPoint(float, houseEdgePercent) {
const edge = Math.min(99, Math.max(0, houseEdgePercent));
const point = (1e8 / (float * 1e8)) * ((100 - edge) / 100);
return Math.max(new Decimal(point).times(100).floor().div(100).toNumber(), 1);
}
// Double: 15 fixed segments, 0 is green, 1 to 7 are red, 8 to 14 are black.
function doubleSegment(float) {
const segment = Math.floor(float * 15);
return { segment, color: segment < 1 ? "green" : segment < 8 ? "red" : "black" };
}- Crash and Slide return a multiplier of at least
1.00×, with a default house edge of 1% (Crash) and 2% (Slide). The edge used by a round is recorded with the round, and the round modal shows it. - Double has a fixed wheel of 15 segments: 1 green (pays 14×), 7 red and 7 black (pay 2×).
If the beacon does not arrive
If a round’s beacon is not available 30 seconds after its publication time, the round has no result. It is cancelled and every bet is refunded. The server seed of a cancelled round is published as well, since it decided nothing.
Verify a round
In the game
Open a finished round from the game’s history. Its modal has a Provably Fair section with the round’s Server Seed (Hashed), Server Seed (Revealed), Client Seed (drand randomness) and Nonce (drand round), and your browser runs three checks:
- Server seed matches its hash:
sha256(serverSeed)equals the hash published before bets opened. - drand randomness matches its signature:
sha256(signature)equals the beacon’s randomness. - Recomputed result: the result recalculated from the revealed seed, the randomness and the round number equals the one the game showed.
The server seed is revealed when the round ends; before that the modal says so. A finished round remains available afterwards, not only while it is in the recent history.
By hand
You need the round’s revealed server seed, its hash, and the drand round number. Everything else (randomness, signature) comes from drand.
import { createHash } from "node:crypto";
import { generateFloats, hashServerSeed } from "./fairness.mjs";
// from the round modal
const { serverSeed, hashedServerSeed, drandRound } = round;
// from api.drand.sh/<chain hash>/public/<drandRound>
const { randomness, signature } = beacon;
const committed = hashServerSeed(serverSeed) === hashedServerSeed;
const beaconLinked =
createHash("sha256").update(Buffer.from(signature, "hex")).digest("hex") === randomness;
const [float] = generateFloats({
serverSeed,
clientSeed: randomness,
nonce: drandRound,
cursor: 0,
count: 1,
});
console.log(committed, beaconLinked, crashPoint(float, 1));fairness.mjs is the reference code.
Worked example
This example pairs a real beacon, drand round 10000000 (published on
4 August 2024 at 20:29:24 UTC), with a made-up server seed. Real round seeds are
random.
Server seed S | 3f1c9a52d7e84b60a1c5e2f9087b4d63c0a9e1f25b7d8c4e6a30912f5d7b8ce1 |
Hash sha256(S) | 4ef210e8bede0521e7e318c889dcd226dd2203d1e3e1a1838ac8c0a3722d4459 |
| Beacon randomness | 5e6508f1a1f689accfc8b027dec7eb999b20b4f66582b2621231cabf9601bf57 |
Nonce R | 10000000 |
| HMAC message | 5e6508f1a1f689accfc8b027dec7eb999b20b4f66582b2621231cabf9601bf57:10000000:0 |
| First float | 0.37639040639624 |
- Crash at a 1% house edge:
(1 / 0.37639…) × 0.99 = 2.630…, floored to 2.63×. - Slide at a 2% house edge:
(1 / 0.37639…) × 0.98 = 2.603…, floored to 2.60×. - Double:
floor(0.37639… × 15) = 5, a red segment: red.
EC-VRF mode
When an operator uses EC-VRF mode, the round has no secret
seed to reveal. The engine’s VRF key takes the place of the server seed (its public
key is the commitment), the client seed becomes the beacon randomness joined to the
round’s group (${randomness}:${group}, so that two groups landing on the same drand
round cannot share a float), and the round carries a VRF proof and output.