FAIRNESS · COLLECTOR CRYPT VRF

Built so nobody - including us - can rig it.

Every live draw's winner comes from an on-chain ECVRF proof (RFC 9381) under a key we froze on-chain before any draw ran — so the operator cannot bias which holder wins. Verify the proof in your browser or on Collector Crypt. Older draws used commit-reveal; the verifier below auto-detects and checks either.

FOUR STEPS - ONE VIEWPORT
Freeze > Snapshot > Prove > Roll
  1. Freeze
    We froze our VRF key on-chain before any draw
    Our ECVRF public key is registered + FROZEN on-chain (Collector Crypt VRF program) under a fixed owner + label. Once frozen it cannot be swapped — every proof verifies against this one key.
    vrf_authority: frozen(pk, owner, label)
  2. Snapshot
    Every holder balance is recorded at the close slot
    When the window closes we record a Merkle root of every eligible $GACHA balance at that exact Solana slot. Nobody touches your balance — we just take a tamper-evident snapshot the weighted pick runs over.
    snapshotMerkleRoot = MerkleRoot(holders[closeSlot])
  3. Prove
    alpha binds the slot, we publish the ECVRF proof
    The proof input alpha = sha256(slotHash ‖ drawId) binds the FUTURE slot blockhash — nobody (including us) can precompute it. We produce the ECVRF proof and commit sha256(proof) on-chain.
    alpha = sha256(slotHash ‖ drawId)
  4. Roll
    beta[0:32] picks the winner
    The roll is the first 32 bytes of the VRF output (beta). We walk the prefix-sum of weights — first holder whose slice covers the offset wins. verifyVRF confirms the proof; the roll is forced by it.
    roll = beta[0:32] (beta = ECVRF output)
SECTION 02

Recompute the roll. Right here. In your browser.

Paste any draw id. For a VRF draw the widget recomputes the alpha binding, verifies the ECVRF proof itself (verifyVRF, RFC 9381) against the operator key, and confirms the roll IS beta[0:32] - all in your browser. For an older commit-reveal draw it re-runs the same HMAC-SHA-256 recompute. If our published roll does not match, the badge turns red.

● ON-CHAIN VERIFIER
Paste a draw id. Recompute the roll in your browser.

Try #1674, #1664, #1663

Enter a draw id to verify.

Want the full story of a single draw — which cards were in the pot, who won at what odds, and this proof, all on one page? Every draw has one: open any row in history and hit its FAIRNESS link, or go to /raffle/fairness/<draw id> directly.

REFERENCE IMPLEMENTATION

Copy-paste. Run it locally. Same answer.

fairness.tsTypeScript
import { verifyVRF, vrfProofToHash } from '@collectorcrypt/vrf-client';
import { sha256 } from '@noble/hashes/sha2';

// alpha binds the resolved slot hash + draw id (32B slot hash || utf8 id)
const alpha = sha256(concat(slotHashBytes, utf8(String(drawId))));

// 1. the ECVRF proof must verify under our FROZEN operator key
const ok = verifyVRF(operatorPubkey, alpha, proof); // RFC 9381 -> true

// 2. the published roll must be the first 32 bytes of the VRF output
const roll = vrfProofToHash(proof).slice(0, 32); // === published rollHex

Node ≥ 20 · no dependencies · same output as the browser widget.

SECTION 03

What we cannot do