---
name: quiver-integration
description: Integrate Quiver verifiable on-chain randomness (VRF/RNG) on Robinhood Chain. Use when adding randomness to a smart contract or dApp — writing a QuiverConsumer contract, requesting and fulfilling random numbers (push or pull flow), using the @quiver/sdk TypeScript client, or running a Fletcher keeper / registering as a randomness provider. Robinhood Chain mainnet is chainId 4663; testnet is 46630.
---

# Quiver integration

Quiver is a verifiable on-chain randomness protocol (a VRF/RNG) for Robinhood Chain. It is a
two-party **commit–reveal** scheme: the final random number is

```
randomNumber = keccak256(userRandom ‖ providerReveal ‖ blockHash?)
```

Both contributions are committed **before** either is revealed, so neither the requester nor the
provider can bias the outcome. The result is fair as long as **either** party is honest.

Roles:
- **Coordinator** — the on-chain `QuiverCoordinator` contract that escrows commitments, assigns
  sequence numbers, and produces/verifies randomness.
- **Provider** — commits a hash chain up front and reveals one link per request.
- **Keeper (Fletcher)** — watches for requests and calls the reveal for the push flow.
- **Consumer** — your contract, which requests randomness and receives the result.

## Decide the flow first

- **Push (callback) flow — default, recommended.** You request; a keeper delivers the result to
  your contract's `quiverCallback` automatically. Inherit `QuiverConsumer`. Use this unless you
  have a reason not to.
- **Pull flow.** You request, then reveal yourself later by submitting both revelations. No keeper
  needed, but you (or your backend) must drive the reveal. Use `QuiverCoordinator.request` +
  `reveal`, or the SDK's `request()` / `reveal()`.

For depth on either flow, read `references/consumer-contract.md`.

## Network constants (verified live)

Always confirm against the on-chain values before mainnet use (`getFee`, `getProviderSequenceNumber`).

| | Robinhood Chain (mainnet) | Robinhood Testnet |
|---|---|---|
| chainId | `4663` | `46630` |
| RPC | `https://rpc.mainnet.chain.robinhood.com` | `https://rpc.testnet.chain.robinhood.com` |
| Explorer | `https://robinhoodchain.blockscout.com` | `https://explorer.testnet.chain.robinhood.com` |
| Coordinator | `0x8cF4f562301fA966F153eE1e3D46D975DF21C9a3` | `0x1da30d6465f657F11B4D7F6Db0B16aD79152fb40` |
| Default provider | `0xeB8E79d3495638Dde48336D01A1f1229822bB016` | `0xc84CC91131b63d9BECFDe7b2DB3D0C653B690541` |
| Fee | `0` wei (subject to change) | `0` wei |

Native gas token is ETH (18 decimals). See `references/networks.md` for the full record.

## Quickstart: consumer contract (push flow)

Inherit `QuiverConsumer`, request in your entrypoint, and settle in `_fulfillRandomness`. Install
the integration contracts (Foundry): `forge install camdengrieh/quiver-kit`, then add the remapping
`quiver/=lib/quiver-kit/src/` to `remappings.txt` (or `foundry.toml`) and:

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

import {QuiverConsumer} from "quiver/QuiverConsumer.sol";

contract CoinFlip is QuiverConsumer {
    mapping(uint64 => address) public playerOf;
    mapping(uint64 => bool) public headsOf;

    event Flipped(address indexed player, uint64 indexed seq, bool heads);

    // Pass the coordinator + default provider for your target chain (see table above).
    constructor(address coordinator, address provider)
        QuiverConsumer(coordinator, provider)
    {}

    /// Flip a coin. Forward at least `flipFee()` to cover the provider fee.
    function flip(bytes32 userRandom) external payable returns (uint64 seq) {
        seq = _requestRandomness(userRandom); // forwards msg.value as the fee
        playerOf[seq] = msg.sender;
    }

    function flipFee() external view returns (uint128) {
        return _randomnessFee();
    }

    /// Called ONLY by the coordinator (the base contract enforces this).
    function _fulfillRandomness(uint64 seq, address, bytes32 rnd) internal override {
        address player = playerOf[seq];
        if (player == address(0)) return;      // unknown/replayed seq — ignore, never revert
        bool heads = uint256(rnd) & 1 == 0;
        headsOf[seq] = heads;
        emit Flipped(player, seq, heads);
    }

    receive() external payable {}              // hold ETH to pay fees
}
```

### Non-negotiable rules for `_fulfillRandomness`

1. **Never revert on the happy path.** A revert routes the randomness into the coordinator's retry
   buffer (recoverable later via `retryCallback`) instead of settling. Guard unknown sequence
   numbers with an early `return`, not a `require`.
2. **Do not override `quiverCallback`.** The base contract already gates it to the coordinator and
   dispatches to `_fulfillRandomness`. Put all logic in `_fulfillRandomness`.
3. **Correlate by `sequenceNumber`.** `_requestRandomness` returns the `seq`; store per-request
   state keyed by it, and match the callback's `seq`.
4. **Fund the contract.** The fee is paid from the contract's balance. Either forward `msg.value`
   (as above) or pre-fund it. Read the current fee with `_randomnessFee()` / `getFee(provider)`.
5. **Treat `userRandom` as public.** In the push flow it appears in calldata. Protocol safety comes
   from the provider's prior commitment, not from this value's secrecy — but do not derive secrets
   from it.
6. **Keep the callback lean.** Keepers cap callback gas; heavy logic risks running out. Store the
   result and do expensive work in a separate user-triggered call.

## Quickstart: TypeScript (SDK)

```ts
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { QuiverClient, robinhoodTestnet } from "@quiver/sdk";

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const transport = http();
const publicClient = createPublicClient({ chain: robinhoodTestnet, transport });
const walletClient = createWalletClient({ account, chain: robinhoodTestnet, transport });

const quiver = new QuiverClient({
  coordinator: "0x1da30d6465f657F11B4D7F6Db0B16aD79152fb40", // testnet
  publicClient,
  walletClient,
});

const provider = "0xc84CC91131b63d9BECFDe7b2DB3D0C653B690541"; // testnet default provider

// Push flow: request, then wait for the keeper to deliver.
const { sequenceNumber } = await quiver.requestRandomness(provider);
const { randomNumber } = await quiver.waitForFulfillment(provider, sequenceNumber);
```

Full client surface (constructor, `request`, `reveal`, `waitForFulfillment`, static helpers) is in
`references/typescript-sdk.md`.

## Running a keeper or becoming a provider

If the user wants to **operate** randomness rather than consume it — run the Fletcher keeper, or
register their own hash-chain provider (commitment, rotation, fees, withdrawals) — read
`references/keeper-and-provider.md`.

## Reference material

- `references/consumer-contract.md` — push vs pull in depth, the full `IQuiverCoordinator` interface,
  events/errors, retry buffer, and `useBlockhash`.
- `references/typescript-sdk.md` — `@quiver/sdk` client, hash-chain helpers, chain exports.
- `references/keeper-and-provider.md` — Fletcher keeper setup and provider registration/rotation.
- `references/networks.md` — verified addresses, RPCs, explorers, chain facts.

Canonical docs: https://quiver.foundation/docs · agent index: https://quiver.foundation/llms.txt
