The oracle is open: any Solana program can read the daily record or request its own random number from the day's quantum entropy pool. Lotteries, NFT reveals, whitelists, tournaments and DAOs get a number that nobody, including us, can choose.
Draft API · program not deployed yet
How a number is made
Measure. At 17:50 UTC the day's experiment runs on an IBM quantum processor, thousands of shots, every outcome kept in order.
Commit. The SHA-256 of the raw outcomes is written on Solana before the sealing drand round exists.
Seal. The first drand quicknet round after the commit is confirmed on Solana is combined with the outcomes: value = SHA-256(outcomes ‖ drand).
Publish. The daily record holds backend, job id, counts, the Bell statistic, commit, drand round and value. The raw outcomes go to Arweave.
Pool. The same outcomes, split into 256-bit chunks under a Merkle root, form the day's entropy pool for on-demand requests.
Two ways to use it: read the daily value (free, one number per day), or request a fresh number from the pool (many per day, each sealed with its own future drand round).
Rollout
Phase 1, built: no custom program. Commit, reveal, snapshot, vote tally, draw and payout are memo transactions signed by the oracle wallet, so their order is fixed on chain. The daily certificate is a Metaplex Core asset whose frozen on-chain attributes hold the whole record, so programs can already read it.
Phase 2: the Anchor program below: records as PDAs, the entropy pool, and payouts that only the program can make.
Draft API · names and layout may change
DailyRecord account
One PDA per day, seeds ["record", day], where day is days since 1970-01-01 as u32 little-endian.
Field
Type
Meaning
experiment
u8
0 CHSH, 1 Mermin GHZ, 2 sweep (the holders' choice)
backend · job_id
[u8;16] · [u8;20]
IBM processor and job
shots
u32
total shots across settings
stat_milli
i32
Bell statistic × 1000 (S, M or V)
commit
[u8;32]
SHA-256 of the raw outcomes, written before the seal
drand_round · drand_randomness
u64 · [u8;32]
the quicknet round that sealed the day
value
[u8;32]
SHA-256(outcomes ‖ drand), the day's number
pool_root · pool_chunks
[u8;32] · u32
Merkle root of the entropy pool and its size
status
u8
Announced → Committed → Sealed
// TypeScript (Anchor client)const [pda] = PublicKey.findProgramAddressSync([Buffer.from("record"), u32le(day)], ORACLE_ID)
const rec = await oracle.account.dailyRecord.fetch(pda)
if (rec.status.sealed) use(rec.value)
// Rust, inside your programlet rec = DailyRecord::try_deserialize(&mut &ctx.accounts.record.data.borrow()[..])?;
require!(rec.status == Status::Sealed, MyError::NotSealedYet);
let i = uniform(&rec.value, entries); // rejection sampling, no modulo bias
Draft API · names and layout may change
On-demand numbers from the entropy pool
Request. Your program calls request_randomness(seed). A Request PDA stores your seed, the next unused pool chunk and a target drand round about 6 seconds in the future.
Fulfil. When that round is out, a keeper submits the chunk with its Merkle proof and the round. The program checks the proof against the committed pool root and stores result = SHA-256(chunk ‖ drand ‖ seed).
Consume. Your program reads the Request account in a later instruction (or receives a CPI callback) and uses result.
let req = request_randomness(CpiContext::new(oracle, accounts), my_seed)?;
// … later, once status == Fulfilledlet r: [u8; 32] = ctx.accounts.request.result;
Each chunk is used once. The chunk was fixed when the pool was committed, the drand round did not exist when you asked, and your seed separates you from everyone else.
Recipes
Lotteries & raffles
Commit the entry list (Merkle root) before requesting. Winner = uniform(result, entries). Several winners: hash-chain the result.
NFT trait reveal
Store a request at mint; at reveal, traits = f(result, token index). Nobody, the creator included, knows the traits at mint time.
Whitelists & airdrops
Pick k of n with a Fisher–Yates shuffle seeded by the result. Publish the list and the proof with it.
Tournaments
Brackets, seeding, map picks and tie-breaks, one request per round so nothing is known in advance.
DAO sortition
Draw a council or jury from token holders: one per wallet or weighted by balance, from a snapshot committed before the request.
Games
Loot, dice and card shuffles for turn-based games. Fulfilment takes seconds, so it does not fit real-time action.
Check any number yourself
Download the day's raw outcomes from Arweave (link in the record).
SHA-256 of the outcomes must equal commit. The commit transaction must be older than the drand round and the first commit of that day in the oracle wallet's history.
The holder snapshot memo must also be older than the drand round, and the first of its day: the list was fixed before anyone could know the number.
Fetch the drand quicknet round and verify its BLS signature with any drand client.
SHA-256(outcomes ‖ randomness) must equal value. For pool requests, check the chunk's Merkle proof and SHA-256(chunk ‖ drand ‖ seed).
Recompute the Bell statistic from the counts and compare it with stat_milli; the IBM job id is published too.
Rebuild the winner from the published holder list with the same rejection sampling. All of this is one command: node oracle.mjs verify-draw draw.json --oracle <address>, no key needed.
What is guaranteed, and what is not
Nobody can choose the result. The outcomes are locked before the drand round exists, and drand is run by an independent network.
The quantum part is published, not trustless. We submit the IBM job; IBM does not offer public third-party verification. Job id, code, counts and the Bell statistic are public.
drand on chain: at first the keeper posts the round and anyone checks it off-chain. On-chain BLS verification comes once it is practical on Solana.
Not a loophole-free Bell test. One chip, not distant stations.
Holders are wallets. Accounts owned by a program (curve, pools, vaults, and multisig vaults too) are excluded from the snapshot.
The pool can be refused, not steered. The operator could fail to answer a request, which is visible, but cannot change its chunk or the drand round.
Not for keys. The numbers are public. Never derive wallets or secrets from them.
Not real-time. Requests settle in seconds, the daily value once a day.