For the complete documentation index, see llms.txt. This page is also available as Markdown.

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)

Contract
Address

The mainnet coordinator is deployed with FAST_MODE_ENABLED = false; blockhash-only requests revert at request time.

Testnet contracts (chain 50312)

Contract
Address

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.

Mode
Cost per request
End-to-end latency

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 your rawFulfillRandomWords execution. Max 2_500_000.

  • commitDelayBlocks (uint16): number of blocks between request and reveal. Min 16, max 200.

  • numWords (uint32): number of uint256 words to deliver. Max 500.

  • useVerifiableEntropy (bool): true for drand mix (Verifiable), false for blockhash-only (Fast). On mainnet, false reverts with FastModeDisabled().

How it works

Fast mode

  1. Consumer calls coordinator.requestRandomWords(req) with useVerifiableEntropy: false.

  2. Coordinator records the request under revealBlock = block.number + commitDelayBlocks.

  3. 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.

  4. At revealBlock, Reactivity calls coordinator.onEvent(...).

  5. The coordinator computes seed = keccak256(blockhash(revealBlock - 1), requestId, consumer, revealBlock) and derives numWords words.

  6. Words are delivered through consumer.rawFulfillRandomWords(requestId, words) with the caller-specified callbackGasLimit.

Verifiable mode

Steps 1 through 4 are identical.

  1. The coordinator captures blockhash(revealBlock - 1) and fires a Somnia Agents JSON API Request against https://api.drand.sh/public/latest, extracting the randomness field. A DrandRequested(requestId, agentRequestId) event is emitted so consumers can link the request to its receipt in the Agent Explorer.

  2. When the validator subcommittee reaches consensus on the drand fetch, the platform calls coordinator.handleDrandResponse(...).

  3. 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

Parameter
Bound
Reason

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.sh and against the on-chain agentRequestId receipt.

  • Consumer callbacks are wrapped in a try/catch on the coordinator side. A reverting consumer does not stall other requests at the same reveal block; the failure surfaces in RandomWordsFulfilled with callbackSuccess = false.

  • Always guard rawFulfillRandomWords with msg.sender == COORDINATOR. Otherwise anyone could call it with a chosen words[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