Agora Docs

Provable fairness

Each result is derived from two values: one set by the player, one committed by the operator before the player's is set. Neither party can determine it alone.

The roll is sha256(ourSeed : yourSeed : count), rejection-sampled and reduced mod 1,000,000, where ourSeed is one link of a hash chain generated per address, yourSeed is set by the player before ourSeed is revealed, and count is the player's draw number incremented on-chain. The prize is the first tier whose cumulative weight exceeds the roll.

Commitment

Before an address's first draw the operator publishes root = sha256^N(secret) on-chain for it. Each settlement reveals one link s satisfying sha256(s) == currentTip, then advances the tip.

Rejection sampling

2^256 is not divisible by 1,000,000, so a naive modulo would over-weight the lowest buckets and break the published odds. Values at or above floor(2^256 / 1,000,000) × 1,000,000 are discarded and the hash re-rolled.

On-chain derivation

sha256 is a cheap precompile, so settlement does not accept a claimed result. It verifies the revealed seed chains correctly, computes the roll, and looks up the prize on-chain. A mismatch reverts rather than paying out.

Verification

All inputs are public after settlement. Reference implementation, Node, no dependencies:

const { createHash } = require('crypto')
const sha = b => createHash('sha256').update(b).digest()
const H = h => Buffer.from(h.slice(2), 'hex')
const LIMIT = ((2n ** 256n - 1n) / 1000000n) * 1000000n

function roll(ourSeed, yourSeed, count) {
  const n = Buffer.alloc(8); n.writeBigUInt64BE(BigInt(count))
  let h = sha(Buffer.concat([H(ourSeed), Buffer.from(':'), H(yourSeed), Buffer.from(':'), n]))
  while (BigInt('0x' + h.toString('hex')) >= LIMIT) h = sha(h)
  return BigInt('0x' + h.toString('hex')) % 1000000n
}

Two conditions establish a settled draw: sha256(ourSeed) equals the tip published before that draw, and roll matches the settled result. GET /gacha/verify/{spinId} returns both, and the interface performs the same computation client-side.

Chain entropy

There is no usable on-chain entropy on Robinhood Chain. block.prevrandao is the constant 1, blockhash is byte-identical across roughly 160 consecutive blocks, and no VRF is available. Hence commit–reveal.