Provably fair
Every Crash, Dice and Plinko result on Aurion is generated from three inputs: a server seed we commit to before you play, a client seed, and a nonce. You can recompute any result yourself — in this page, in your own browser — and check that we couldn't have changed it.
How it works
- Before play, we publish the SHA-256 hash of a secret server seed. The hash proves which seed we'll use without revealing it.
- The result is HMAC-SHA256(server seed, client seed : nonce). For Dice and Plinko you can set your own client seed; the nonce counts your bets. Crash rounds use a public random client seed and a round counter.
- Afterwards we reveal the server seed. Hash it: it must match the commitment. Recompute the result: it must match what happened.
Verify a result
The exact algorithm
const enc = new TextEncoder()
// HMAC-SHA256(key = serverSeed, message), via the browser's Web Crypto API
async function hmac(key, message) {
const k = await crypto.subtle.importKey('raw', enc.encode(key), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'])
return new Uint8Array(await crypto.subtle.sign('HMAC', k, enc.encode(message)))
}
async function floatStream(serverSeed, clientSeed, nonce, count) {
const out = []
for (let cursor = 0; out.length < count; cursor++) {
const block = await hmac(serverSeed, `${clientSeed}:${nonce}:${cursor}`)
for (let i = 0; i < 32 && out.length < count; i += 4) {
out.push(block[i] / 256 + block[i + 1] / 256 ** 2 + block[i + 2] / 256 ** 3 + block[i + 3] / 256 ** 4)
}
}
return out
}
async function diceRoll(serverSeed, clientSeed, nonce) {
const [f] = await floatStream(serverSeed, clientSeed, nonce, 1)
return Math.floor(f * 10000) // 0..9999
}
async function plinkoPath(serverSeed, clientSeed, nonce, rows) {
const floats = await floatStream(serverSeed, clientSeed, nonce, rows)
return floats.map((f) => (f < 0.5 ? 1 : -1)) // +1 right, -1 left
}
async function crashPoint(serverSeed, clientSeed, nonce) { // no cursor
const digest = await hmac(serverSeed, `${clientSeed}:${nonce}`)
const hex = Array.from(digest, (b) => b.toString(16).padStart(2, '0')).join('')
const h = BigInt('0x' + hex.slice(0, 52)) % 2n ** 52n
const e = 2n ** 52n
if (h % 33n === 0n) return 1 // instant crash
const mult = Number((100n * e - h) / (e - h)) / 100 // exact integer floor
return Math.min(mult, 1000)
}