Using Verifiable Randomness (VRF)
Somnia VRF is a native VRF coordinator built on Somnia's Reactivity primitive and mixed with drand via Somnia Agents. Consumers pay only their tx gas; the coordinator pays for entropy delivery out of its own balance. No off-chain fulfiller runs behind it.
Two entropy modes share one coordinator:
Fast: entropy from
blockhash(revealBlock - 1). Cheap and low-latency, but the block producer for the reveal block can influence it. Testnet-grade only.Verifiable: the reveal block's hash is mixed with a drand round pulled through the Somnia Agents JSON API Request agent. Publicly verifiable seed; compromising either drand or the reveal-block producer alone does not compromise the result. The only mode available on mainnet.
The live demo connects your wallet, flips a coin, and shows the whole cycle end-to-end.
Mainnet contracts (chain 5031)
SomniaVRFCoordinator
CoinFlip (example consumer)
Somnia Agents platform
The mainnet coordinator is deployed with FAST_MODE_ENABLED = false; blockhash-only requests revert at request time.
Testnet contracts (chain 50312)
SomniaVRFCoordinator
CoinFlip (example consumer)
Somnia Agents platform
Both modes are enabled on the testnet coordinator.
Cost and latency
Every request is paid out of the coordinator's balance, not the consumer's. Consumers only pay the tx gas for requestRandomWords.
Fast
~0.00067 native
~4 seconds (16-block commit delay)
Verifiable
~0.10 native
~10 seconds (adds drand fetch)
Consumer contract
A consumer only needs two things: the coordinator address and the two selectors it will use (requestRandomWords outbound, rawFulfillRandomWords inbound). No package install, no external repo dependency. The example below is a full, drop-in consumer that just needs the coordinator address at construction:
Deploy it against the mainnet coordinator:
Trigger a flip and watch the result land on-chain:
Request shape
callbackGasLimit(uint32): max gas for yourrawFulfillRandomWordsexecution. Max2_500_000.commitDelayBlocks(uint16): number of blocks between request and reveal. Min16, max200.numWords(uint32): number of uint256 words to deliver. Max500.useVerifiableEntropy(bool):truefor drand mix (Verifiable),falsefor blockhash-only (Fast). On mainnet,falsereverts withFastModeDisabled().
How it works
Fast mode
Consumer calls
coordinator.requestRandomWords(req)withuseVerifiableEntropy: false.Coordinator records the request under
revealBlock = block.number + commitDelayBlocks.If the coordinator does not already have a subscription for
revealBlock, it schedules one against the Somnia Reactivity precompile. Multiple requests at the same reveal block share a single subscription.At
revealBlock, Reactivity callscoordinator.onEvent(...).The coordinator computes
seed = keccak256(blockhash(revealBlock - 1), requestId, consumer, revealBlock)and derivesnumWordswords.Words are delivered through
consumer.rawFulfillRandomWords(requestId, words)with the caller-specifiedcallbackGasLimit.
Verifiable mode
Steps 1 through 4 are identical.
The coordinator captures
blockhash(revealBlock - 1)and fires a Somnia Agents JSON API Request againsthttps://api.drand.sh/public/latest, extracting therandomnessfield. ADrandRequested(requestId, agentRequestId)event is emitted so consumers can link the request to its receipt in the Agent Explorer.When the validator subcommittee reaches consensus on the drand fetch, the platform calls
coordinator.handleDrandResponse(...).The coordinator computes
seed = keccak256(drand, blockhash, requestId, consumer, revealBlock)and delivers words.
If the drand fetch fails on-chain (network partition, consensus timeout, round rollover), the coordinator marks the request fulfilled with callbackSuccess = false in RandomWordsFulfilled, so consumers can retry without stalling the queue.
Bounds and defaults
commitDelayBlocks
16 min, 200 max
Long enough to raise the cost of a single-validator grinding attack; short enough to keep blockhash inside the 256-block EVM lookback.
callbackGasLimit
2,500,000 max
Coordinator-side ceiling so one bad consumer cannot stall the reveal block.
numWords
1 to 500
Each word costs one keccak256 at fulfillment.
Security notes
Fast mode is testnet-grade. Entropy is
blockhash(revealBlock - 1), which the reveal-block producer can influence. Use it for testing, dev tooling, and games where the outcome is not high-value. The mainnet coordinator rejects this mode at request time.Verifiable mode is trust-minimized. drand's League of Entropy is an eight-organisation threshold BLS committee with roughly five years of uptime; a 4-of-8 drand compromise plus Somnia validator collusion at the same reveal block is the joint attack. The coordinator does not cryptographically verify the BLS signature on-chain (that would require a BLS precompile), but any observer can independently check the drand round against
api.drand.shand against the on-chainagentRequestIdreceipt.Consumer callbacks are wrapped in a
try/catchon the coordinator side. A reverting consumer does not stall other requests at the same reveal block; the failure surfaces inRandomWordsFulfilledwithcallbackSuccess = false.Always guard
rawFulfillRandomWordswithmsg.sender == COORDINATOR. Otherwise anyone could call it with a chosenwords[0]and forge the result.
Try it live
The showcase has a chain toggle for testnet and mainnet, connects your wallet, and runs the CoinFlip contract end-to-end. Verifiable-mode reveals include a link to the drand fetch's receipt in the Agent Explorer.
Last updated