# Integrating with Quiver

This guide shows how to consume Quiver randomness from a smart contract (push and pull
flows) and from off-chain TypeScript. For the mechanism behind it, see
[protocol-design.md](protocol-design.md).

---

## Choosing a flow

| | **Push** (`requestWithCallback`) | **Pull** (`request` → `reveal`) |
| --- | --- | --- |
| Delivery | Coordinator calls your `quiverCallback` | You call `reveal` and read the return value |
| Who fulfills | The provider's Fletcher keeper | You (or anyone with both secrets) |
| Your value secret? | No (emitted for the keeper) | Yes, until you reveal |
| `blockhash` mixing | No | Optional |
| Best for | Games, mints, most dapps (hands-off UX) | Max trust-minimization; you run your own reveal |

Most integrations want the **push flow**. Use the **pull flow** when you need the
requester's contribution to stay secret until reveal and/or want `blockhash` folded in.

---

## Push flow (recommended)

### 1. Inherit `QuiverConsumer`

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

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

contract Lottery is QuiverConsumer {
    constructor(address quiver, address provider) QuiverConsumer(quiver, provider) {}

    // Draw randomness. `msg.value` (or the contract's balance) must cover the fee.
    function drawWinner(bytes32 userRandom) external returns (uint64 seq) {
        seq = _requestRandomness(userRandom);
        // record `seq -> round` so the callback can resolve the right draw
    }

    // Called ONLY by the coordinator. Put your logic here.
    function _fulfillRandomness(uint64 seq, address /*provider*/, bytes32 rnd) internal override {
        uint256 winnerIndex = uint256(rnd) % participants.length;
        // settle the round with `winnerIndex`
    }
}
```

The base contract handles the coordinator-only check on the callback, quoting/paying the
fee, and dispatching to `_fulfillRandomness`. You never write the raw `quiverCallback`.

### 2. Fund the fee

`_requestRandomness` calls `requestWithCallback{value: fee}` using **the consumer
contract's balance**. Ensure the contract holds enough ETH, or forward `msg.value` in
your request function and keep a buffer. Read the current fee with `_randomnessFee()`.

### 3. Generate a good `userRandomNumber`

Pass fresh, unpredictable 32 bytes each request. Off-chain callers should use a CSPRNG
(`QuiverClient.generateUserRandom()`); on-chain, derive from values not known to the
provider in advance where possible. Even a weak user value cannot let the *provider*
bias the result, but a predictable one weakens your independent contribution.

### Callback rules (important)

- **Keep it lean and non-reverting.** If `_fulfillRandomness` reverts or runs out of gas,
  the coordinator buffers the randomness and emits `CallbackFailed`; recover it later via
  `retryCallback(provider, seq)`. Don't rely on this as normal flow.
- **Be idempotent / defensive.** Ignore unknown or already-resolved sequence numbers
  (see the `CoinFlip`/`DiceGame` examples), since `retryCallback` can re-invoke you.
- **Never trust `tx.origin` or re-enter assumptions.** Only `msg.sender == coordinator`
  is guaranteed (enforced for you by the base contract).

---

## Pull flow

Use the coordinator directly (or the SDK). You keep `userRandom` secret and reveal it
yourself.

```solidity
import {IQuiverCoordinator} from "quiver/interfaces/IQuiverCoordinator.sol";

// 1. Commit
bytes32 userRandom = /* your secret 32 bytes */;
bytes32 commitment = coordinator.constructUserCommitment(userRandom);
uint64 seq = coordinator.request{value: coordinator.getFee(provider)}(provider, commitment, true /*useBlockhash*/);

// 2. Fetch providerRevelation for `seq` from the provider's endpoint (off-chain),
//    then reveal:
bytes32 rnd = coordinator.reveal(provider, seq, userRandom, providerRevelation);
```

With `useBlockhash = true`, reveal within 256 blocks of the request (older block hashes
are unavailable on-chain and would fold in `bytes32(0)`).

---

## Off-chain with `@quiver/sdk`

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

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

const quiver = new QuiverClient({coordinator, publicClient, walletClient});

// Push flow: request, then await the RandomnessRevealed event
const {sequenceNumber} = await quiver.requestRandomness(provider);
const {randomNumber} = await quiver.waitForFulfillment(provider, sequenceNumber);

// Pull flow: request, then reveal yourself
const userRandom = QuiverClient.generateUserRandom();
const {sequenceNumber: s} = await quiver.request(provider, userRandom, {useBlockhash: true});
const providerRevelation = /* from the provider's endpoint */;
const {randomNumber: rnd} = await quiver.reveal(provider, s, userRandom, providerRevelation);
```

Hash-chain helpers (`commitmentOf`, `revelationOf`, `combineRandomValues`,
`userCommitmentOf`) are also exported for tooling and tests.

---

## Testing your integration (Foundry)

You don't need a live provider to test — build the chain in-process with the test helper
and reveal manually. See `test/examples/Examples.t.sol` for a complete pattern:

```solidity
import {HashChainLib} from "quiver-test/helpers/HashChain.sol"; // or copy it in

bytes32 seed = keccak256("test-seed");
uint64 N = 64;
vm.prank(provider);
coordinator.register(0, HashChainLib.commitment(seed, N), "", N, 100, "");

// request via your consumer, then fulfill as the provider would:
coordinator.revealWithCallback(provider, seq, userRandom, HashChainLib.revelation(seed, N, seq));
```

---

## Common pitfalls

- **Fee changes.** Quote `getFee` immediately before requesting; providers can update
  fees. The SDK does this for you. Overpayment is refunded by the coordinator.
- **`vm.prank` + external helper.** In tests, computing the commitment via an external
  call to the coordinator consumes a pending `vm.prank`. Compute it locally
  (`keccak256(abi.encodePacked(userRandom))`) instead.
- **Assuming instant fulfillment.** The push flow is asynchronous — design your UX around
  a pending → resolved lifecycle keyed by the sequence number.
- **Reusing a `userRandomNumber`.** Use a fresh value per request.
