Skip to content
D20DAODocs
Keeper LabExplorerStatusGitHub

PROTOCOL / GUIDE

Source inputs

The drand beacon, Arc's recipe registry and catalogs, and Robinhood's round binding.

Read the repository reference
01

The drand beacon

The current source is drand's evmnet, a threshold BLS network run by the League of Entropy that signs one round every 3 seconds (scheme bls-bn254-unchained-on-g1 on BN254, chain hash 0x04f1e9062b8a81f848fded9c12306733282b2727ecced50032187751166ec8c3). An epoch commits one round: its number in decimal as the data, its scheduled time, genesis + (round − 1) × period, as the timestamp and the beacon's 64-byte BLS signature. The registry has the stateless D20BeaconVerifier check that signature onchain under the group key registered with the recipe; the verifier has no owner, is not behind a proxy and has the same address on both networks. A round is accepted at publication if its scheduled time is not in the future and at most 240 seconds old. The keeper normally publishes the round that was current at the epoch's start.

02

Robinhood Chain: one beacon, bound per request

The Robinhood coordinator registers drand evmnet as beacon 0, with the same chain hash and group key, checked by the same stateless D20BeaconVerifier. A request binds the round scheduled at least 3 seconds after its block's timestamp. A round's randomness is sha256 of its verified signature, drand's own randomness value. The owner can register another beacon and schedule a change at least 10 minutes ahead (scheduleBeacon); a request keeps the beacon and round it bound. There is no recipe registry. The sections below describe Arc.

03

Arc: recipe registry

Epoch sources are recipes in an owner-managed, append-only registry in EpochEntropy. A recipe is a canonical request, whose keccak256 is the query hash its records are bound to; a data template that fixes the exact record it accepts; and a body, which for a beacon is the canonical request itself and for a signed API recipe the JSON keepers send to the provider's gateway. The owner, the DAO treasury Safe on Arc Mainnet, appends signed API recipes with registerRecipe and beacons with registerBeacon, each of which emits RecipeRegistered with the complete definition. A registered recipe never changes, so a changed listing becomes a new id. Recipe 11 is the drand evmnet beacon. Recipes 0 to 10 are signed API recipes that served earlier epochs and stay in the contract. Every registry registers six of them as built-in recipes: 0 Hyperliquid BTC daily notional volume (metaAndAssetCtxs with dex empty, projecting the BTC symbol and dayNtlVlm), 1 dRPC Ethereum mainnet block hash (eth_call of Multicall3 getLastBlockHash at latest), 2 TickerLayer BTCUSD last trade, 3 TickerLayer ETHUSD last trade, 4 Nodary ETH/USD feed and 5 dRPC Base block hash. Recipes 6 to 10 are the passthrough form of recipes 0, 1, 2, 4 and 5: the same listings reached through the provider's passthrough path, answered with the same signed records under other request hashes, so they keep the built-in templates. getRecipe(id) returns a definition, beaconOf(id) a beacon's registration and recipeCount() the number registered. Because the registry is onchain and owner-managed, adding a source later is an owner transaction (registerRecipe or registerBeacon, then scheduleCatalog) rather than a contract upgrade: the catalog can grow without changing the addresses, interfaces or fees an application uses.

04

Arc: catalog per epoch

Each epoch selects from the catalog in force for it: 1 to 10 slots, each a registered recipe with its signer; catalogAt(epochId) returns the hash, recipes and signers an epoch uses. The catalog in force on both networks is [11], the drand evmnet beacon alone. A beacon slot's signer is slotSigner(recipe), an identity derived from the registration and not a key, and scheduleCatalog accepts no other for it. The owner schedules catalogs with scheduleCatalog at least two epochs ahead. Arc Testnet: [0,1,2,4,5] from epoch 966 (2026-09-17), [6,7,8,9,10] from epoch 10108 (2026-09-28) and [11], the drand evmnet beacon, from epoch 11319 (2026-09-30). Arc Mainnet: [0,1,2,4,5] from epoch 848 (2026-09-18), [6,7,8,9,10] from epoch 10070 (2026-09-28) and [11], the drand evmnet beacon, from epoch 12448 (2026-10-01). Before those catalogs each registry used its initial catalog: recipes 0 to 3 with the four signers set at initialization, two of them TickerLayer recipes that share a signer. Epochs before [11] committed signed API records and are verified under the catalog each used. The rollout catalog [0,1,2,4,5] listed the Hyperliquid, dRPC, TickerLayer, Nodary and dRPC signers; neighbouring slots, including the last and the first, never shared a signer, so every fallback moved to another provider. Several recipes do not mean independent entropy sources. Each network's registry upgrade is recorded in its deployment manifest.

05

Arc: selection and repeated data

D20_EPOCH_SELECT, catalog hash, epoch ID and the start-minus-one anchor hash determine a selector modulo the catalog's slot count; a one-slot catalog such as the drand catalog always selects its only slot. Consecutive epochs may select the same recipe and, with a beacon, commit the same round: a round stays acceptable for 240 seconds and an epoch lasts 200 blocks, about 100 seconds on Arc, so one round can serve several consecutive epochs. Their epoch hashes still differ, because each commitment binds its epoch ID, start block and anchor, but their data hashes, attestation hashes and signatures can repeat. Identify an epoch by its ID or epoch hash, never by its round. The protocol does not resample to force different bytes. A catalog of several sources has a fallback: if the selected source yields no valid packet, attempt n (1 to count − 1) uses the source n slots later and can be committed only from n × 20 blocks into the epoch; the committed source identifies the attempt, and replay rejects a fallback committed before its window. The drand catalog has one source and no fallback, so while no fresh round can be obtained no epoch is published. Epoch parameters bind distinct commitments, but hashing public data does not create fresh or secret entropy. A valid saved packet is published only for live demand.

06

Arc: exact record policy

At publication an attestation, whether a signed record or a beacon round stamped with its scheduled time, cannot be future-dated or more than 240 seconds old. Each recipe's data template fixes its record byte for byte. Matching is greedy, never backtracks and must consume the whole record, so extra fields, whitespace, reordered keys, uppercase hex and trailing bytes are rejected. A beacon recipe's template accepts one round number, 1 to 19 digits without a leading zero. The timestamp must equal the round's scheduled time, and the verifier registered for the recipe must accept the 64-byte BLS signature within a fixed gas allowance; a transaction whose gas cannot give the verifier that allowance reverts with BeaconGasTooLow instead of reading a valid round as invalid. A signed API record carries a canonical 65-byte low-s EIP-191 signature that binds query hash, timestamp and exact UTF-8 data. Its template's literal segments fix key order, names and punctuation; HEX segments match an exact number of lowercase hex characters; DECIMAL segments match JSON numbers and decimal strings; INTEGER segments match bounded positive integers such as 13-digit millisecond timestamps. Raw signed data is at most 128 bytes and is never cropped. Malformed, oversized or stale data is rejected rather than cropped or silently replaced. The keeper never refreshes a saved signed record to look for another outcome; it replaces a saved beacon round only when that round is too old to be accepted, and only until a commit of its epoch has been sent.

07

Arc: publishers

The registry committer publishes epochs. The owner can also allow up to four backup committers, so a second keeper can take over publication automatically if the first stops. A backup committer publishes under exactly the committer's rules and has no other role; it earns the keeper share of the requests it serves itself, while a request served by any other wallet pays the committer. Any round scheduled within the 240 seconds before the publication block is valid, so the committer chooses which round an epoch commits. A beacon signature shows what the beacon's key signed for that round, not that the beacon is unbiased or independent of its operators, and a signed API record establishes what a provider's gateway signed, not that the value is unbiased. The owner chooses which recipes, beacons and signers future epochs use. The registry trusts the answer of a registered verifier and does not check that the chain hash names the network of the key or that the verifier's code is the reviewed D20BeaconVerifier, so the owner vouches for each registration, which BeaconRegistered publishes.

Work with your agents

Copy a task-specific prompt with the protocol references and integration rules.

 

View prompt
Review D20DAO epoch source evidence for my integration. Use the catalog in force for each epoch (catalogAt) and each recipe's registered definition (getRecipe, beaconOf for a beacon, or the SDK's built-in recipes). Verify the original epoch packet, a drand round or a signed API record, against its recipe and the epoch commitment; distinguish signature provenance from source truthfulness and randomness guarantees.

Read these public references first; do not guess signatures, addresses or limits:
- This guide: https://d20dao.org/docs/sources (full text of every guide: https://d20dao.org/llms-full.txt, agent guide: https://d20dao.org/agents.md)
- SDK README and AGENTS.md: https://github.com/d20dao/d20-sdk (also inside the installed package). It lists the randomness options, callback gas bounds, how to wait for results, compiler setup and gas needs for refunds and retries.
- Protocol source (contracts and replay library): https://github.com/d20dao/d20-sdk/tree/main/protocol, pinned to commit 98e537fb249dd0d3365b8d78e6040a9323a65a88 by PROTOCOL-PROVENANCE.json
- Deployment manifests: https://d20dao.org/deployments/arc-mainnet.json, https://d20dao.org/deployments/arc-testnet.json, https://d20dao.org/deployments/robinhood-mainnet.json, https://d20dao.org/deployments/robinhood-testnet.json
- Integration skills and templates: https://github.com/d20dao/skills (d20-consumer: methods.md, RandomnessConsumer.sol, mappings.mjs)
- Explorer for checking requests: https://d20dao.org/explorer (arc.d20dao.org for Arc Mainnet, arc-testnet.d20dao.org for Arc Testnet; Robinhood requests are read from the chain)
- Agent API, for agents and services without a contract: https://d20dao.org/docs/agents (0.05 USDC per call over x402)
If a link is unavailable, use the installed package's README, AGENTS.md, ABIs and protocol sources.

Networks: Arc Mainnet chain 5042 (live, USDC; RPC https://rpc.mainnet.arc.io; explorer https://explorer.arc.io); Arc Testnet chain 5042002 (build and test here first, USDC; RPC https://rpc.testnet.arc.io; explorer https://testnet.arcscan.app); Robinhood Chain chain 4663 (live, ETH; RPC https://rpc.mainnet.chain.robinhood.com; explorer https://robinhoodchain.blockscout.com); Robinhood Chain Testnet chain 46630 (build and test here first, ETH; RPC https://rpc.testnet.chain.robinhood.com; explorer https://explorer.testnet.chain.robinhood.com). Native USDC on Arc and ETH on Robinhood Chain have 18 decimals. The consumer interface is the same on both; Arc serves requests through epochs, Robinhood Chain binds each request to a future drand round. Compile with Solidity 0.8.28 and EVM version cancun, importing contracts from the installed @d20dao/vrf-sdk package. Take the coordinator proxy address from the manifest of the network you target and check that its implementation matches the manifest. Any consumer contract can request; there is no allowlist, and EOAs cannot request.

Requests and fees: use the D20VRFRequests helpers from the SDK for every randomness option (raw word, dice, custom dice, coin flip, ranges, choose one or many, shuffle). Never hardcode the fee. In a contract, quote with quoteFee(callbackGasLimit) inside the requesting transaction. Off chain, quote with quoteFeeAt(callbackGasLimit, latest block baseFeePerGas) plus a buffer, or the SDK's quoteRequestFee with an ethers v6 provider, because eth_call reports a base fee of 0. Choose one payment pattern deliberately: collect a buffered quote from the user, pay the exact fee from the contract and return the change; or forward msg.value and accept that the excess becomes refund credit of the refund address.

Results: store the delivered word or mapped result in your consumer when the authenticated callback arrives, keyed by request ID, and have the application read that state. Off chain, wait for the coordinator's fulfillment events or poll getRequest (getRoundRequest on Robinhood Chain); the SDK documents the fields and mapped-result readers. A browser app must bundle the ESM package. Delivery usually takes a few seconds; measured Arc Testnet completion times, including under burst load, are at https://d20dao.org/benchmarks (observations of the testnet keepers, not an SLA). On Robinhood Chain a typical delivery takes 5–8 seconds. An unserved request can be refunded strictly after its 60-second deadline at the refund ratio fixed when it was requested. Give refund and callback-retry transactions explicit gas limits as documented.

Keep callbacks small and authenticated through D20VRFConsumer, and never request randomness from inside a callback. An accepted proof with a failed callback is paid service: retry the same stored result, never reroll. Optional _onRefund(requestId) means the refund recipient was paid or credited, not that the consumer received funds, and notification retries must not pay twice. Keep application assets and settlement separate from RNG fees.

For verification, keep the original epoch packet, request and target context and accepted proof evidence. Mathematical replay alone does not prove chain inclusion; check receipts and the implementation history too.

Implement only the requested changes. Do not ask for keeper, VRF, deployer or bot secrets, and do not send funded or mainnet transactions without my explicit authorization. Compile, run lifecycle tests on a local chain or a testnet, then report changed files, results, anything unverified, and any documentation that was unclear or missing.