> For the complete documentation index, see [llms.txt](https://docs.somnia.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf.md).

# 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](https://vrf-showcase.vercel.app) connects your wallet, flips a coin, and shows the whole cycle end-to-end.

## Mainnet contracts (chain 5031)

| Contract                      | Address                                                                                                                            |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `SomniaVRFCoordinator`        | [`0x50AAEE5ECE41627F2bc4b64Ad29a4659a5C018cb`](https://explorer.somnia.network/address/0x50AAEE5ECE41627F2bc4b64Ad29a4659a5C018cb) |
| `CoinFlip` (example consumer) | [`0x0c80964dB885091194F547eaDf95249b74Ba651D`](https://explorer.somnia.network/address/0x0c80964dB885091194F547eaDf95249b74Ba651D) |
| Somnia Agents platform        | [`0x5E5205CF39E766118C01636bED000A54D93163E6`](https://explorer.somnia.network/address/0x5E5205CF39E766118C01636bED000A54D93163E6) |

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

## Testnet contracts (chain 50312)

| Contract                      | Address                                                                                                                                    |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `SomniaVRFCoordinator`        | [`0x0834459256bbb8d2efee23dc6c3f1722266182dd`](https://shannon-explorer.somnia.network/address/0x0834459256bbb8d2efee23dc6c3f1722266182dd) |
| `CoinFlip` (example consumer) | [`0x9c220e99bf41df220ce54147a0a10a025a151e63`](https://shannon-explorer.somnia.network/address/0x9c220e99bf41df220ce54147a0a10a025a151e63) |
| Somnia Agents platform        | [`0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776`](https://shannon-explorer.somnia.network/address/0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776) |

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:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface ISomniaVRFCoordinator {
    struct RandomWordsRequest {
        uint32 callbackGasLimit;
        uint16 commitDelayBlocks;
        uint32 numWords;
        bool useVerifiableEntropy;
    }
    function requestRandomWords(RandomWordsRequest calldata req)
        external returns (uint256 requestId);
}

contract CoinFlip {
    ISomniaVRFCoordinator public immutable COORDINATOR;

    enum Result { Pending, Heads, Tails }
    mapping(uint256 => Result) public flips;

    event Landed(uint256 indexed requestId, Result result);

    error OnlyCoordinator(address caller);

    constructor(address coordinator) {
        COORDINATOR = ISomniaVRFCoordinator(coordinator);
    }

    /// Fires a Verifiable-mode request. Reveal ~10s later.
    function flipVerifiable() external returns (uint256 requestId) {
        return COORDINATOR.requestRandomWords(
            ISomniaVRFCoordinator.RandomWordsRequest({
                callbackGasLimit: 100_000,
                commitDelayBlocks: 16,
                numWords: 1,
                useVerifiableEntropy: true
            })
        );
    }

    /// Called by the coordinator after the reveal + drand fetch.
    /// The coordinator guarantees msg.sender == COORDINATOR when it calls
    /// this selector, so a single check is all that's needed.
    function rawFulfillRandomWords(uint256 requestId, uint256[] calldata words)
        external
    {
        if (msg.sender != address(COORDINATOR)) revert OnlyCoordinator(msg.sender);
        Result r = (words[0] & 1) == 0 ? Result.Heads : Result.Tails;
        flips[requestId] = r;
        emit Landed(requestId, r);
    }
}
```

Deploy it against the mainnet coordinator:

```bash
forge create CoinFlip.sol:CoinFlip \
  --rpc-url https://api.infra.mainnet.somnia.network \
  --private-key $PRIVATE_KEY \
  --constructor-args 0x50AAEE5ECE41627F2bc4b64Ad29a4659a5C018cb \
  --broadcast --gas-limit 30000000
```

Trigger a flip and watch the result land on-chain:

```bash
# fire the request
cast send $COINFLIP "flipVerifiable()" \
  --rpc-url https://api.infra.mainnet.somnia.network \
  --private-key $PRIVATE_KEY --gas-limit 6000000

# 10 seconds later, read the result (0 = pending, 1 = heads, 2 = tails)
cast call $COINFLIP "flips(uint256)(uint8)" $REQUEST_ID \
  --rpc-url https://api.infra.mainnet.somnia.network
```

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

5. 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.
6. When the validator subcommittee reaches consensus on the drand fetch, the platform calls `coordinator.handleDrandResponse(...)`.
7. 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](https://vrf-showcase.vercel.app) 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.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
