@d20dao/vrf-sdk 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/API.md ADDED
@@ -0,0 +1,2190 @@
1
+ # D20DAO coordinator API reference
2
+
3
+ <!-- Generated by scripts/api-reference.mjs from abi/*.json and scripts/api-descriptions.mjs. Do not edit by hand. -->
4
+
5
+ Every function, event and error of `D20VRFCoordinator` and `EpochEntropy`, generated from the ABIs of `@d20dao/vrf-sdk` 0.4.0. Regenerate with `npm run build && npm run api-reference`; `npm test` fails when this file is out of date.
6
+
7
+ - Package: `@d20dao/vrf-sdk` 0.4.0
8
+ - Protocol source: commit `de5f82eb9fc749c80e83270f57cde9908ddcf1f3`, copied to [`protocol/contracts/`](protocol/contracts/) (hashes in `PROTOCOL-PROVENANCE.json`)
9
+ - Compiler: solc 0.8.28+commit.7893614a.Emscripten.clang, EVM version `cancun`
10
+ - `abi/D20VRFCoordinator.json` SHA-256: `4764ba62745e109f3b968b21ed23e88da739a4a26906fc2ed3192fa23b8d79c1`
11
+ - `abi/EpochEntropy.json` SHA-256: `684ee3dfe0745f141831aee896db6436d490d5e9715e61661c86b044d1c3260e`
12
+
13
+ This reference describes that source. A deployment runs it only while the implementation behind each proxy is the one the deployment manifest records for that commit: check when you integrate and whenever a proxy emits `Upgraded` (README [Security and trust](README.md#security-and-trust)).
14
+
15
+ ## Conventions
16
+
17
+ - **Addresses.** Call the proxies listed in README [Deployments](README.md#deployments). The ABIs are those of the implementations; `D20Proxy` adds no functions of its own.
18
+ - **Units.** Fees and credits are wei of native USDC, which has 18 decimals (`1e18` is 1 USDC). Deadlines and timestamps are Unix seconds. Ratios are basis points (10000 is 100%).
19
+ - **Callers.** "Anyone" means any account or contract; "Any contract" means `msg.sender` must have code. Owner-only functions revert `OwnableUnauthorizedAccount` for other callers.
20
+ - **Selectors.** Functions and errors show their 4-byte selector, events their topic 0, so revert data and logs can be matched by hand.
21
+ - **Decoding errors.** `coordinatorAbi` and `epochEntropyAbi` (`@d20dao/vrf-sdk/abi`) contain every custom error below. When your consumer calls the coordinator with an ordinary Solidity call, a coordinator revert is passed through unchanged, so a wallet sending a transaction to your consumer sees the coordinator's selector. Decode with `coordinator.interface.parseError(data)` in ethers or `decodeErrorResult({ abi: coordinatorAbi, data })` in viem, or build your consumer's contract object from its ABI plus the coordinator's error entries. Your consumer's own errors, such as `OnlyCoordinator` and `InvalidCoordinator` from `D20VRFConsumer`, are only in your consumer's ABI. Arithmetic overflow reverts with `Panic(uint256)`.
22
+ - **ethers v6 results.** Structs and multiple return values arrive as `Result` objects, which are arrays. A named value is also a property unless its name collides with an `Array` or `Result` member, such as `length`, `values`, `keys`, `map` or `filter`; read such a value with `result.getValue(name)`, by position, or from `result.toObject()`. Unnamed outputs, such as those of `pricing()`, are positional only.
23
+ - **Consumer contracts.** `D20VRFConsumer` implements `rawFulfillRandomness(requestId, randomness)` and `onRefund(requestId)`; both revert `OnlyCoordinator` unless called by the coordinator proxy given to its constructor, which reverts `InvalidCoordinator` for an address without code.
24
+
25
+ ## Contents
26
+
27
+ - [D20VRFCoordinator](#coordinator)
28
+ - [Types](#coordinator-types)
29
+ - [Requesting](#coordinator-requesting)
30
+ - [Pricing and refund settings](#coordinator-pricing-and-refund-settings)
31
+ - [Reading request state and results](#coordinator-reading-request-state-and-results)
32
+ - [Settlement, refunds and credits](#coordinator-settlement-refunds-and-credits)
33
+ - [Settlement balances](#coordinator-settlement-balances)
34
+ - [Keeper and proof functions](#coordinator-keeper-and-proof-functions)
35
+ - [Keeper, key and configuration reads](#coordinator-keeper-key-and-configuration-reads)
36
+ - [Owner administration](#coordinator-owner-administration)
37
+ - [Constants](#coordinator-constants)
38
+ - [Events](#coordinator-events)
39
+ - [Errors](#coordinator-errors)
40
+ - [EpochEntropy](#registry)
41
+ - [Types](#registry-types)
42
+ - [Epoch state for consumers and verifiers](#registry-epoch-state-for-consumers-and-verifiers)
43
+ - [Registry reads](#registry-registry-reads)
44
+ - [Source selection and publication](#registry-source-selection-and-publication)
45
+ - [Recipes and catalogs](#registry-recipes-and-catalogs)
46
+ - [Owner administration](#registry-owner-administration)
47
+ - [Constants](#registry-constants)
48
+ - [Events](#registry-events)
49
+ - [Errors](#registry-errors)
50
+
51
+ ## <a id="coordinator"></a>D20VRFCoordinator
52
+
53
+ The consumer entry point. Call the coordinator proxy for your chain (README [Deployments](README.md#deployments)). `ID20VRF` in `contracts/interfaces/ID20VRF.sol` declares the five functions a consumer contract needs (`quoteFee`, `quoteFeeAt`, `requestRandomness`, `requestMappedRandomness`, `getMappedResult`); `coordinatorAbi` from `@d20dao/vrf-sdk/abi` carries everything below. In Solidity, declare a local interface for any other function you call.
54
+
55
+ Requests, fulfillment, `storeBlockHash`, retries, `refundRequest` and withdrawals are `nonReentrant`. A consumer callback (`rawFulfillRandomness`, `onRefund`) that calls one of them fails with `ReentrancyGuardReentrantCall`, and the coordinator records the callback as failed. Views stay callable from callbacks.
56
+
57
+ ### <a id="coordinator-types"></a>Types
58
+
59
+ #### <a id="coordinator-type-d20vrfcoordinator-request"></a>`D20VRFCoordinator.Request`
60
+
61
+ Returned by `getRequest`. It does not contain the escrowed fee or the refund ratio; read `requestFeePaid` and `requestRefundBps`.
62
+
63
+ Source: `D20VRFCoordinator.sol` lines 59–77
64
+
65
+ | Field | Type | Meaning |
66
+ | --- | --- | --- |
67
+ | `consumer` | `address` | Contract that made the request; receives `rawFulfillRandomness` and `onRefund`. |
68
+ | `callbackGasLimit` | `uint32` | Gas forwarded to `rawFulfillRandomness` at fulfillment, and the lowest `gasLimit` that `retryCallback` accepts. |
69
+ | `requestBlock` | `uint64` | Block of the request transaction. |
70
+ | `targetBlock` | `uint64` | Block whose hash enters the seed: `max(requestBlock, committedBlock + 1)` of the epoch. 0 until the epoch packet is published. |
71
+ | `deadline` | `uint64` | Request block timestamp plus `RESPONSE_TIMEOUT` (60 seconds), in Unix seconds. A proof accepted in a block with timestamp at or before `deadline` serves the request; `refundRequest` needs a block timestamp after it. |
72
+ | `refundAddress` | `address` | Fixed recipient of the expiry refund and of any overpayment credit. |
73
+ | `clientSeed` | `bytes32` | Value supplied by the consumer, bound into the seed and emitted in `RandomnessRequested`. |
74
+ | `mappingHash` | `bytes32` | `keccak256(abi.encode(operation, lower, upper, count, population))` of the stored spec; `hashMapping(spec)` in TypeScript. |
75
+ | `blockHash` | `bytes32` | Target block hash once `storeBlockHash` or fulfillment stored it; zero before. |
76
+ | `randomness` | `bytes32` | Accepted VRF output. Zero until `fulfilled`. |
77
+ | `proofHash` | `bytes32` | `keccak256(abi.encode(proof))` of the accepted proof. Zero until `fulfilled`. |
78
+ | `transcriptHash` | `bytes32` | `keccak256(abi.encode(TRANSCRIPT_DOMAIN, chainId, coordinator, requestId, protocolConfigurationHash, blockHash, proofHash, randomness, mappingHash, epochId, epochHash))`. Zero until `fulfilled`. |
79
+ | `fulfilled` | `bool` | A proof was accepted. Final: the word can no longer change. |
80
+ | `delivered` | `bool` | A callback attempt succeeded. Stays false after a failed callback until `retryCallback` succeeds; it says nothing about acceptance. |
81
+ | `refunded` | `bool` | `refundRequest` settled the request. Never true together with `fulfilled`. |
82
+ | `epochId` | `uint64` | Epoch of `requestBlock`, fixed at creation; never zero for an existing request. |
83
+ | `epochHash` | `bytes32` | Commitment of the published epoch packet. Zero until the packet is published. |
84
+
85
+ #### <a id="coordinator-type-randomnessmapping-spec"></a>`RandomnessMapping.Spec`
86
+
87
+ A randomness mapping stored with a request ("mapping" here is not a Solidity `mapping`). The TypeScript `MappingSpec` returned by `builtins` has the same five fields in the same order, with `lower` and `upper` as `bigint`, and ethers encodes it for this struct unchanged. Valid combinations: README [Randomness options](README.md#randomness-options).
88
+
89
+ Source: `libraries/RandomnessMapping.sol` lines 7–14
90
+
91
+ | Field | Type | Meaning |
92
+ | --- | --- | --- |
93
+ | `operation` | `RandomnessMapping.Operation` | Enum encoded as uint8: 0 Raw, 1 DiceRoll, 2 CoinFlip, 3 NumberRange, 4 ChooseOne, 5 ChooseMany, 6 Shuffle. |
94
+ | `lower` | `uint256` | NumberRange minimum; zero for every other operation. |
95
+ | `upper` | `uint256` | DiceRoll sides or NumberRange maximum; zero otherwise. |
96
+ | `count` | `uint32` | Values returned: the dice count; 1 for CoinFlip, NumberRange and ChooseOne; the number of choices for ChooseMany; the population for Shuffle; 0 for Raw. |
97
+ | `population` | `uint32` | Number of items (1 to 256) for ChooseOne, ChooseMany and Shuffle; zero otherwise. |
98
+
99
+ #### <a id="coordinator-type-vrf-proof"></a>`VRF.Proof`
100
+
101
+ Chainlink secp256k1 VRF proof, verified by the unmodified vendored `VRF.sol`. ABI-encoded it is 416 bytes: the `FulfillmentEvidence` packet.
102
+
103
+ Source: `vendor/VRF.sol` lines 570–580
104
+
105
+ | Field | Type | Meaning |
106
+ | --- | --- | --- |
107
+ | `pk` | `uint256[2]` | VRF public key; must equal `(publicKeyX, publicKeyY)`. |
108
+ | `gamma` | `uint256[2]` | Proof point. The accepted word is `keccak256(abi.encode(3, gamma))`. |
109
+ | `c` | `uint256` | Proof challenge scalar. |
110
+ | `s` | `uint256` | Proof response scalar. |
111
+ | `seed` | `uint256` | Must equal `requestSeed(requestId)`. |
112
+ | `uWitness` | `address` | Address of `c·pk + s·G`, checked by the verifier. |
113
+ | `cGammaWitness` | `uint256[2]` | Precomputed `c·gamma`, checked by the verifier. |
114
+ | `sHashWitness` | `uint256[2]` | Precomputed `s·hashToCurve(pk, seed)`, checked by the verifier. |
115
+ | `zInv` | `uint256` | Inverse of the projective z coordinate of `cGammaWitness + sHashWitness`. |
116
+
117
+ ### <a id="coordinator-requesting"></a>Requesting
118
+
119
+ Requests must come from a contract and pay at least the fee computed in their own transaction; see README [Paying for a request](README.md#paying-for-a-request). The registry call a request makes (`checkpointEpoch` for the current epoch) cannot fail in practice, so requests revert only with the errors listed.
120
+
121
+ #### <a id="coordinator-fn-quotefee"></a>`quoteFee`
122
+
123
+ ```solidity
124
+ function quoteFee(uint32 callbackGasLimit) external view returns (uint256)
125
+ ```
126
+
127
+ Selector `0xc9caa0c3` · Caller: Anyone (view) · Source: `D20VRFCoordinator.sol` lines 228–233
128
+
129
+ Fee for a request with this `callbackGasLimit` priced at `block.basefee`, that is `quoteFeeAt(callbackGasLimit, block.basefee)`. Exact inside the requesting transaction, which is how `D20VRFRequests` helpers pay. Through `eth_call` the base fee is commonly reported as 0 (observed on Arc), so the answer collapses to `minFee` and a transaction sent with it reverts `IncorrectFee`. Off-chain, quote with `quoteFeeAt` and the latest header base fee plus a buffer, as `quoteRequestFee` does. It does not check the gas limit range.
130
+
131
+ **Errors:** [`FeeOverflow`](#coordinator-error-feeoverflow).
132
+
133
+ #### <a id="coordinator-fn-quotefeeat"></a>`quoteFeeAt`
134
+
135
+ ```solidity
136
+ function quoteFeeAt(uint32 callbackGasLimit, uint256 baseFee) external view returns (uint256)
137
+ ```
138
+
139
+ Selector `0x26fa8481` · Caller: Anyone (view) · Source: `D20VRFCoordinator.sol` lines 220–227
140
+
141
+ `max(minFee, feeMultiplier × baseFee × (fulfillGasOverhead + callbackGasLimit))` over the live pricing for a base fee in wei that you supply; with `feeMultiplier` 0 it returns `minFee`. A quote, not a reservation: pricing can change before your transaction. A `baseFee` large enough to overflow uint256 reverts with `Panic(0x11)` instead of `FeeOverflow`.
142
+
143
+ **Errors:** [`FeeOverflow`](#coordinator-error-feeoverflow).
144
+
145
+ #### <a id="coordinator-fn-requestrandomness"></a>`requestRandomness`
146
+
147
+ ```solidity
148
+ function requestRandomness(bytes32 clientSeed, uint32 callbackGasLimit, address _refundAddress) external payable returns (uint256 requestId)
149
+ ```
150
+
151
+ Selector `0x9849d1e5` · Caller: Any contract · Source: `D20VRFCoordinator.sol` lines 235–240, 248–287
152
+
153
+ Creates a raw request (spec all zero) with `msg.sender` as consumer and returns its ID. Needs `msg.value` at least the fee computed in this transaction; escrows exactly that fee and credits any excess to `_refundAddress` as refund credit. Fixes the request block, epoch, client seed, refund address, fee, refund ratio (`refundBps`) and a deadline of `block.timestamp + RESPONSE_TIMEOUT`. After acceptance the consumer receives `rawFulfillRandomness(requestId, randomness)` with exactly `callbackGasLimit` gas. Checks run in this order: caller has code, refund address non-zero, gas limit in range, fee, mapping, epoch started.
154
+
155
+ **Emits:** [`FeeOverpaymentCredited`](#coordinator-event-feeoverpaymentcredited), [`RandomnessRequested`](#coordinator-event-randomnessrequested), [`MappingRequested`](#coordinator-event-mappingrequested).
156
+
157
+ **Errors:** [`ContractConsumerRequired`](#coordinator-error-contractconsumerrequired), [`InvalidRefundAddress`](#coordinator-error-invalidrefundaddress), [`InvalidCallbackGas`](#coordinator-error-invalidcallbackgas), [`FeeOverflow`](#coordinator-error-feeoverflow), [`IncorrectFee`](#coordinator-error-incorrectfee), [`EpochUnavailable`](#coordinator-error-epochunavailable).
158
+
159
+ #### <a id="coordinator-fn-requestmappedrandomness"></a>`requestMappedRandomness`
160
+
161
+ ```solidity
162
+ function requestMappedRandomness(bytes32 clientSeed, uint32 callbackGasLimit, address _refundAddress, RandomnessMapping.Spec spec) external payable returns (uint256 requestId)
163
+ ```
164
+
165
+ Selector `0xe6b41a8c` · Caller: Any contract · Source: `D20VRFCoordinator.sol` lines 242–246, 248–287
166
+
167
+ Same as `requestRandomness`, storing `spec` with the request (`getMapping`, `mappingHash`). The callback still carries the raw word; read the mapped values with `getMappedResult`. `D20VRFRequests` helpers call this function and pay `quoteFee` from the calling contract balance.
168
+
169
+ **Emits:** [`FeeOverpaymentCredited`](#coordinator-event-feeoverpaymentcredited), [`RandomnessRequested`](#coordinator-event-randomnessrequested), [`MappingRequested`](#coordinator-event-mappingrequested).
170
+
171
+ **Errors:** [`ContractConsumerRequired`](#coordinator-error-contractconsumerrequired), [`InvalidRefundAddress`](#coordinator-error-invalidrefundaddress), [`InvalidCallbackGas`](#coordinator-error-invalidcallbackgas), [`FeeOverflow`](#coordinator-error-feeoverflow), [`IncorrectFee`](#coordinator-error-incorrectfee), [`InvalidMapping`](#coordinator-error-invalidmapping), [`EpochUnavailable`](#coordinator-error-epochunavailable).
172
+
173
+ ### <a id="coordinator-pricing-and-refund-settings"></a>Pricing and refund settings
174
+
175
+ Views, callable by anyone. They describe requests created from now on; an existing request settles from its own snapshots (`requestFeePaid`, `requestRefundBps`).
176
+
177
+ | Function | Selector | Meaning | Source |
178
+ | --- | --- | --- | --- |
179
+ | <a id="coordinator-fn-pricing"></a>`pricing() returns (uint256, uint16, uint32)` | `0x7ce91411` | Live `(minFee, feeMultiplier, fulfillGasOverhead)`. The outputs are unnamed, so read them by position. | lines 211–213 |
180
+ | <a id="coordinator-fn-minfee"></a>`minFee() returns (uint256)` | `0x24ec7590` | Minimum fee in wei, at most `MAX_MIN_FEE` (10 USDC). | line 44 |
181
+ | <a id="coordinator-fn-feemultiplier"></a>`feeMultiplier() returns (uint16)` | `0xe5a70ef7` | Base-fee multiplier, 0 to `MAX_FEE_MULTIPLIER` (20); 0 makes every fee `minFee`. | lines 46–47 |
182
+ | <a id="coordinator-fn-fulfillgasoverhead"></a>`fulfillGasOverhead() returns (uint32)` | `0x19d40839` | Gas added to `callbackGasLimit` in the fee formula, `MIN_FULFILL_GAS_OVERHEAD` to `MAX_FULFILL_GAS_OVERHEAD`. | line 48 |
183
+ | <a id="coordinator-fn-refundbps"></a>`refundBps() returns (uint16)` | `0xec8c9a0b` | Current refund ratio in basis points (5000 to 10000), copied into each new request. Not the ratio of an existing request: use `requestRefundBps(requestId)`. | lines 49–50 |
184
+
185
+ ### <a id="coordinator-reading-request-state-and-results"></a>Reading request state and results
186
+
187
+ Views, callable by anyone, including from a callback. Request IDs start at 1 and increase by one; functions taking a `requestId` revert `UnknownRequest` for an ID that was never issued. README [Reading results](README.md#reading-results) shows a polling loop.
188
+
189
+ #### <a id="coordinator-fn-getrequest"></a>`getRequest`
190
+
191
+ ```solidity
192
+ function getRequest(uint256 requestId) external view returns (D20VRFCoordinator.Request result)
193
+ ```
194
+
195
+ Selector `0xc58343ef` · Caller: Anyone (view) · Source: `D20VRFCoordinator.sol` lines 289–308, 558–563
196
+
197
+ Full state of a request, see [`D20VRFCoordinator.Request`](#coordinator-type-d20vrfcoordinator-request). `targetBlock` and `epochHash` are resolved from the registry, so they become non-zero as soon as the epoch packet is published. `fulfilled` means the word is final; `delivered` only reports that a callback succeeded. A request that is not `fulfilled` in a block whose timestamp is after `deadline` has expired and can only be refunded. When polling, read the latest block before `getRequest`, so that a proof included up to that block is visible.
198
+
199
+ **Errors:** [`UnknownRequest`](#coordinator-error-unknownrequest).
200
+
201
+ #### <a id="coordinator-fn-getmapping"></a>`getMapping`
202
+
203
+ ```solidity
204
+ function getMapping(uint256 requestId) external view returns (RandomnessMapping.Spec)
205
+ ```
206
+
207
+ Selector `0xede9ba8b` · Caller: Anyone (view) · Source: `D20VRFCoordinator.sol` lines 340–343
208
+
209
+ The stored [`RandomnessMapping.Spec`](#coordinator-type-randomnessmapping-spec); all fields zero (Raw) for `requestRandomness`.
210
+
211
+ **Errors:** [`UnknownRequest`](#coordinator-error-unknownrequest).
212
+
213
+ #### <a id="coordinator-fn-getmappedresult"></a>`getMappedResult`
214
+
215
+ ```solidity
216
+ function getMappedResult(uint256 requestId) external view returns (uint256[])
217
+ ```
218
+
219
+ Selector `0x8f09a3e6` · Caller: Anyone (view) · Source: `D20VRFCoordinator.sol` lines 345–349
220
+
221
+ The accepted word mapped with the stored spec: `[uint256(word)]` for a raw request, otherwise the values in README [Randomness options](README.md#randomness-options). Part of `ID20VRF`. Reverts `NotFulfilled` until a proof is accepted, so an expired or refunded request never has a result. Gas grows with the mapping; a 256-item shuffle is expensive onchain.
222
+
223
+ **Errors:** [`UnknownRequest`](#coordinator-error-unknownrequest), [`NotFulfilled`](#coordinator-error-notfulfilled).
224
+
225
+ #### <a id="coordinator-fn-maprandomness"></a>`mapRandomness`
226
+
227
+ ```solidity
228
+ function mapRandomness(bytes32 randomness, RandomnessMapping.Spec spec) external pure returns (uint256[])
229
+ ```
230
+
231
+ Selector `0x41c2a199` · Caller: Anyone (pure) · Source: `D20VRFCoordinator.sol` lines 351–356
232
+
233
+ Maps any word with any valid spec, like the SDK `mapRandomness(word, spec)` off-chain. It does not show that a request was fulfilled.
234
+
235
+ **Errors:** [`InvalidMapping`](#coordinator-error-invalidmapping).
236
+
237
+ #### <a id="coordinator-fn-requestfeepaid"></a>`requestFeePaid`
238
+
239
+ ```solidity
240
+ function requestFeePaid(uint256 requestId) external view returns (uint256)
241
+ ```
242
+
243
+ Selector `0xef7cc992` · Caller: Anyone (view) · Source: `D20VRFCoordinator.sol` lines 331–334
244
+
245
+ Fee escrowed by the request (`feePaid` in `RandomnessRequested`), excluding any overpayment. The keeper share, the protocol share and the refund are computed from it.
246
+
247
+ **Errors:** [`UnknownRequest`](#coordinator-error-unknownrequest).
248
+
249
+ #### <a id="coordinator-fn-requestrefundbps"></a>`requestRefundBps`
250
+
251
+ ```solidity
252
+ function requestRefundBps(uint256 requestId) external view returns (uint16)
253
+ ```
254
+
255
+ Selector `0x5d170fd7` · Caller: Anyone (view) · Source: `D20VRFCoordinator.sol` lines 335–338
256
+
257
+ Refund ratio the request copied from `refundBps` at creation. An expiry refund pays `requestFeePaid × requestRefundBps / 10000`; a later `setRefundBps` does not change it.
258
+
259
+ **Errors:** [`UnknownRequest`](#coordinator-error-unknownrequest).
260
+
261
+ #### <a id="coordinator-fn-refundcallbackdelivered"></a>`refundCallbackDelivered`
262
+
263
+ ```solidity
264
+ function refundCallbackDelivered(uint256) external view returns (bool)
265
+ ```
266
+
267
+ Selector `0x281d3157` · Caller: Anyone (view) · Source: `D20VRFCoordinator.sol` lines 103, 511
268
+
269
+ True once an `onRefund` notification for the request succeeded, at `refundRequest` or `retryRefundCallback`. Returns false for unknown IDs instead of reverting.
270
+
271
+ #### <a id="coordinator-fn-nextrequestid"></a>`nextRequestId`
272
+
273
+ ```solidity
274
+ function nextRequestId() external view returns (uint256)
275
+ ```
276
+
277
+ Selector `0x6a84a985` · Caller: Anyone (view) · Source: `D20VRFCoordinator.sol` lines 51, 173, 261
278
+
279
+ ID the next request will receive. Issued IDs are 1 to `nextRequestId() - 1`.
280
+
281
+ ### <a id="coordinator-settlement-refunds-and-credits"></a>Settlement, refunds and credits
282
+
283
+ Recovery calls need no value or role. They forward gas to the consumer and revert `InsufficientCallbackGas` rather than forward less, so the transaction gas limit must cover it (README [Gas for refund and retry calls](README.md#gas-for-refund-and-retry-calls)). Refund credit and keeper credit are pull balances: only the holder withdraws them.
284
+
285
+ #### <a id="coordinator-fn-refundrequest"></a>`refundRequest`
286
+
287
+ ```solidity
288
+ function refundRequest(uint256 requestId) external
289
+ ```
290
+
291
+ Selector `0x7411484e` · Caller: Anyone · Source: `D20VRFCoordinator.sol` lines 466–490, 502–513
292
+
293
+ Refunds an unfulfilled request once a block timestamp is after its deadline. Marks it refunded, sends `feePaid × requestRefundBps / 10000` to the fixed refund address with a 30,000-gas transfer, or adds it to that address's refund credit if the transfer fails, and adds the rest of the fee to `earnedFees`. Then calls `onRefund(requestId)` on the consumer with 100,000 gas; a failed notification does not undo the refund. The caller receives nothing. Measured minimum transaction gas limit 302,558 to 357,517; use 400,000.
294
+
295
+ **Emits:** [`RequestRefundedTo`](#coordinator-event-requestrefundedto), [`RefundCallbackAttempted`](#coordinator-event-refundcallbackattempted).
296
+
297
+ **Errors:** [`UnknownRequest`](#coordinator-error-unknownrequest), [`RefundNotAvailable`](#coordinator-error-refundnotavailable), [`InsufficientCallbackGas`](#coordinator-error-insufficientcallbackgas).
298
+
299
+ #### <a id="coordinator-fn-retrycallback"></a>`retryCallback`
300
+
301
+ ```solidity
302
+ function retryCallback(uint256 requestId, uint32 gasLimit) external
303
+ ```
304
+
305
+ Selector `0xdd11c275` · Caller: Anyone · Source: `D20VRFCoordinator.sol` lines 456–464, 613–630
306
+
307
+ Calls `rawFulfillRandomness` again with the same accepted word after a failed callback, forwarding `gasLimit` (30,000 to 1,000,000 and not below the request's `callbackGasLimit`). Sets `delivered` on success. Pays nobody and never changes the word. Transaction gas limit: about `gasLimit + 250,000`.
308
+
309
+ **Emits:** [`CallbackAttempted`](#coordinator-event-callbackattempted).
310
+
311
+ **Errors:** [`UnknownRequest`](#coordinator-error-unknownrequest), [`NotFulfilled`](#coordinator-error-notfulfilled), [`AlreadyDelivered`](#coordinator-error-alreadydelivered), [`InvalidCallbackGas`](#coordinator-error-invalidcallbackgas), [`InsufficientCallbackGas`](#coordinator-error-insufficientcallbackgas).
312
+
313
+ #### <a id="coordinator-fn-retryrefundcallback"></a>`retryRefundCallback`
314
+
315
+ ```solidity
316
+ function retryRefundCallback(uint256 requestId, uint32 gasLimit) external
317
+ ```
318
+
319
+ Selector `0x054f6962` · Caller: Anyone · Source: `D20VRFCoordinator.sol` lines 492–500, 502–513
320
+
321
+ Repeats a failed `onRefund` notification for a refunded request with `gasLimit` (100,000 to 1,000,000). Never transfers funds again. Transaction gas limit: about `gasLimit + 150,000`.
322
+
323
+ **Emits:** [`RefundCallbackAttempted`](#coordinator-event-refundcallbackattempted).
324
+
325
+ **Errors:** [`UnknownRequest`](#coordinator-error-unknownrequest), [`NotRefunded`](#coordinator-error-notrefunded), [`RefundCallbackAlreadyDelivered`](#coordinator-error-refundcallbackalreadydelivered), [`InvalidCallbackGas`](#coordinator-error-invalidcallbackgas), [`InsufficientCallbackGas`](#coordinator-error-insufficientcallbackgas).
326
+
327
+ #### <a id="coordinator-fn-withdrawrefundcredit"></a>`withdrawRefundCredit`
328
+
329
+ ```solidity
330
+ function withdrawRefundCredit(address recipient) external
331
+ ```
332
+
333
+ Selector `0x445071f2` · Caller: Refund-credit holder · Source: `D20VRFCoordinator.sol` lines 515–525
334
+
335
+ Sends all of the caller's refund credit, `refundCredits(msg.sender)`, to `recipient` with all remaining gas. Credit comes from overpayment and from refund transfers that failed, and belongs to the request's refund address, so that address must make the call. If `recipient` rejects the transfer the call reverts and the credit stays.
336
+
337
+ **Emits:** [`RefundCreditWithdrawn`](#coordinator-event-refundcreditwithdrawn).
338
+
339
+ **Errors:** [`InvalidRefundAddress`](#coordinator-error-invalidrefundaddress), [`NoRefundCredit`](#coordinator-error-norefundcredit), [`TransferFailed`](#coordinator-error-transferfailed).
340
+
341
+ #### <a id="coordinator-fn-withdrawfees"></a>`withdrawFees`
342
+
343
+ ```solidity
344
+ function withdrawFees(address recipient) external
345
+ ```
346
+
347
+ Selector `0x164e68de` · Caller: Fee recipient · Source: `D20VRFCoordinator.sol` lines 527–536
348
+
349
+ Sends all `earnedFees` to `recipient`. Fees accrue at acceptance (fee minus keeper share) and from the part of a refunded fee that is not returned; open escrow is never included. With nothing earned it sends zero without reverting.
350
+
351
+ **Emits:** [`FeesWithdrawn`](#coordinator-event-feeswithdrawn).
352
+
353
+ **Errors:** [`OnlyFeeRecipient`](#coordinator-error-onlyfeerecipient), [`InvalidConfig`](#coordinator-error-invalidconfig), [`TransferFailed`](#coordinator-error-transferfailed).
354
+
355
+ #### <a id="coordinator-fn-withdrawkeepercredit"></a>`withdrawKeeperCredit`
356
+
357
+ ```solidity
358
+ function withdrawKeeperCredit(address recipient) external
359
+ ```
360
+
361
+ Selector `0xf62c546b` · Caller: Keeper-credit holder · Source: `D20VRFCoordinator.sol` lines 538–547
362
+
363
+ Sends all of the caller's keeper credit (keeper-share transfers that failed) to `recipient`.
364
+
365
+ **Emits:** [`KeeperCreditWithdrawn`](#coordinator-event-keepercreditwithdrawn).
366
+
367
+ **Errors:** [`InvalidConfig`](#coordinator-error-invalidconfig), [`NoKeeperCredit`](#coordinator-error-nokeepercredit), [`TransferFailed`](#coordinator-error-transferfailed).
368
+
369
+ ### <a id="coordinator-settlement-balances"></a>Settlement balances
370
+
371
+ Views, callable by anyone. Amounts are in wei of native USDC.
372
+
373
+ | Function | Selector | Meaning | Source |
374
+ | --- | --- | --- | --- |
375
+ | <a id="coordinator-fn-refundcredits"></a>`refundCredits(address) returns (uint256)` | `0x61137e40` | Refund credit that an address can withdraw with `withdrawRefundCredit`. | line 57 |
376
+ | <a id="coordinator-fn-totalrefundcredits"></a>`totalRefundCredits() returns (uint256)` | `0x6e0842e1` | Sum of all refund credit held by the coordinator. | line 56 |
377
+ | <a id="coordinator-fn-earnedfees"></a>`earnedFees() returns (uint256)` | `0xb1b3ffd9` | Protocol fees that the fee recipient can withdraw. | line 52 |
378
+ | <a id="coordinator-fn-feerecipient"></a>`feeRecipient() returns (address)` | `0x46904840` | Address allowed to call `withdrawFees`; changed with `setFeeRecipient`. | line 40 |
379
+ | <a id="coordinator-fn-keeperfeebps"></a>`keeperFeeBps() returns (uint16)` | `0x0eab7d63` | Keeper share of each accepted fee in basis points (0 to 10000). Read at acceptance, not snapshotted: a change applies to open requests accepted afterwards. It only splits the escrowed fee; what the consumer paid and can be refunded does not change. | lines 41, 433 |
380
+ | <a id="coordinator-fn-keepercredits"></a>`keeperCredits(address) returns (uint256)` | `0xf5c764f6` | Keeper credit that an address can withdraw with `withdrawKeeperCredit`. | line 42 |
381
+ | <a id="coordinator-fn-totalkeepercredits"></a>`totalKeeperCredits() returns (uint256)` | `0xc7281b7a` | Sum of all keeper credit held by the coordinator. | line 43 |
382
+
383
+ ### <a id="coordinator-keeper-and-proof-functions"></a>Keeper and proof functions
384
+
385
+ Proof submission is permissionless: anyone holding a valid proof may submit it, and the keeper share goes to the submitting wallet when the registry authorizes it as its committer or a backup committer, and to `committer()` otherwise. Consumers normally only read `getRequest`. Besides the custom errors listed, proof functions can revert with `Error(string)` messages from the vendored VRF verifier, such as `invalid proof`, which are not in the ABI.
386
+
387
+ #### <a id="coordinator-fn-fulfillrandomness"></a>`fulfillRandomness`
388
+
389
+ ```solidity
390
+ function fulfillRandomness(uint256 requestId, VRF.Proof proof) external
391
+ ```
392
+
393
+ Selector `0xef7c2b19` · Caller: Anyone · Source: `D20VRFCoordinator.sol` lines 389–397, 413–448
394
+
395
+ Accepts a proof for a request that is not fulfilled, not refunded and not past its deadline; acceptance in a block with timestamp equal to `deadline` is timely. Stores the target block hash if needed, verifies the proof against `requestSeed(requestId)`, stores the word, proof hash and transcript hash, sets `fulfilled`, adds `feePaid` minus the keeper share to `earnedFees` and calls the consumer with `callbackGasLimit` gas. It then sends the keeper share (`keeperFeeBps` of `feePaid`) with 30,000 gas to the submitting wallet when the registry answers `isAuthorizedCommitter` true for it (the committer or an allowed backup committer) and to `committer()` otherwise, or records it as that wallet's keeper credit; a registry call that reverts also pays `committer()`. A failing callback does not revert the fulfillment.
396
+
397
+ **Emits:** [`BlockHashStored`](#coordinator-event-blockhashstored), [`RequestServed`](#coordinator-event-requestserved), [`ProofVerified`](#coordinator-event-proofverified), [`RandomnessFulfilled`](#coordinator-event-randomnessfulfilled), [`FulfillmentEvidence`](#coordinator-event-fulfillmentevidence), [`CallbackAttempted`](#coordinator-event-callbackattempted), [`KeeperFeePaid`](#coordinator-event-keeperfeepaid).
398
+
399
+ **Errors:** [`UnknownRequest`](#coordinator-error-unknownrequest), [`AlreadyFulfilled`](#coordinator-error-alreadyfulfilled), [`RequestRefunded`](#coordinator-error-requestrefunded), [`RequestExpired`](#coordinator-error-requestexpired), [`NotReady`](#coordinator-error-notready), [`BlockHashUnavailable`](#coordinator-error-blockhashunavailable), [`WrongPublicKey`](#coordinator-error-wrongpublickey), [`WrongSeed`](#coordinator-error-wrongseed), [`EvidencePacketTooLarge`](#coordinator-error-evidencepackettoolarge), [`InsufficientCallbackGas`](#coordinator-error-insufficientcallbackgas).
400
+
401
+ #### <a id="coordinator-fn-fulfillrandomnessbatch"></a>`fulfillRandomnessBatch`
402
+
403
+ ```solidity
404
+ function fulfillRandomnessBatch(uint256[] ids, VRF.Proof[] proofs) external
405
+ ```
406
+
407
+ Selector `0x9497b180` · Caller: Anyone · Source: `D20VRFCoordinator.sol` lines 399–411
408
+
409
+ Fulfills up to `MAX_FULFILL_BATCH` (16) requests, one proof each. Members already fulfilled, refunded or past their deadline, including an ID repeated in the batch, are skipped with `FulfillmentSkipped`; every other member runs exactly like `fulfillRandomness` and emits the same events, so an unknown ID, an unready member or an invalid proof reverts the whole batch.
410
+
411
+ **Emits:** [`FulfillmentSkipped`](#coordinator-event-fulfillmentskipped), [`BlockHashStored`](#coordinator-event-blockhashstored), [`RequestServed`](#coordinator-event-requestserved), [`ProofVerified`](#coordinator-event-proofverified), [`RandomnessFulfilled`](#coordinator-event-randomnessfulfilled), [`FulfillmentEvidence`](#coordinator-event-fulfillmentevidence), [`CallbackAttempted`](#coordinator-event-callbackattempted), [`KeeperFeePaid`](#coordinator-event-keeperfeepaid).
412
+
413
+ **Errors:** [`InvalidBatch`](#coordinator-error-invalidbatch), [`UnknownRequest`](#coordinator-error-unknownrequest), [`NotReady`](#coordinator-error-notready), [`BlockHashUnavailable`](#coordinator-error-blockhashunavailable), [`WrongPublicKey`](#coordinator-error-wrongpublickey), [`WrongSeed`](#coordinator-error-wrongseed), [`EvidencePacketTooLarge`](#coordinator-error-evidencepackettoolarge), [`InsufficientCallbackGas`](#coordinator-error-insufficientcallbackgas).
414
+
415
+ #### <a id="coordinator-fn-storeblockhash"></a>`storeBlockHash`
416
+
417
+ ```solidity
418
+ function storeBlockHash(uint256 requestId) external returns (bytes32)
419
+ ```
420
+
421
+ Selector `0x262fd733` · Caller: Anyone · Source: `D20VRFCoordinator.sol` lines 368–372, 564–579
422
+
423
+ Resolves the target block from the published epoch, stores its hash if not stored yet and returns it. Fulfillment does this automatically; calling it earlier keeps a request provable after its target leaves the 256-block `BLOCKHASH` window. Needs `block.number` at least `targetBlock + confirmationBlocks`. Works on any request, whatever its status.
424
+
425
+ **Emits:** [`BlockHashStored`](#coordinator-event-blockhashstored).
426
+
427
+ **Errors:** [`UnknownRequest`](#coordinator-error-unknownrequest), [`NotReady`](#coordinator-error-notready), [`BlockHashUnavailable`](#coordinator-error-blockhashunavailable).
428
+
429
+ #### <a id="coordinator-fn-verifyrequestproof"></a>`verifyRequestProof`
430
+
431
+ ```solidity
432
+ function verifyRequestProof(uint256 requestId, VRF.Proof proof) external view returns (bytes32)
433
+ ```
434
+
435
+ Selector `0x0846de99` · Caller: Anyone (view) · Source: `D20VRFCoordinator.sol` lines 358–366
436
+
437
+ Returns the word a proof yields for the request's seed, without changing state. A valid proof is not acceptance: check `getRequest(requestId).fulfilled`.
438
+
439
+ **Errors:** [`UnknownRequest`](#coordinator-error-unknownrequest), [`NotReady`](#coordinator-error-notready), [`BlockHashUnavailable`](#coordinator-error-blockhashunavailable), [`WrongPublicKey`](#coordinator-error-wrongpublickey), [`WrongSeed`](#coordinator-error-wrongseed).
440
+
441
+ #### <a id="coordinator-fn-requestseed"></a>`requestSeed`
442
+
443
+ ```solidity
444
+ function requestSeed(uint256 requestId) external view returns (uint256)
445
+ ```
446
+
447
+ Selector `0xa9df851a` · Caller: Anyone (view) · Source: `D20VRFCoordinator.sol` lines 374–379, 581–589
448
+
449
+ Seed the proof must use: `keccak256(abi.encode(SEED_DOMAIN, chainId, coordinator, keyHash, requestId, consumer, clientSeed, mappingHash, requestBlock, targetBlock, blockHash, epochId, epochHash))` as uint256. Available only after publication and `confirmationBlocks` confirmations of the target block.
450
+
451
+ **Errors:** [`UnknownRequest`](#coordinator-error-unknownrequest), [`NotReady`](#coordinator-error-notready), [`BlockHashUnavailable`](#coordinator-error-blockhashunavailable).
452
+
453
+ #### <a id="coordinator-fn-getproofcontext"></a>`getProofContext`
454
+
455
+ ```solidity
456
+ function getProofContext(uint256 requestId) external view returns (uint256 seed, uint64 deadline, bool fulfilled, bool refunded)
457
+ ```
458
+
459
+ Selector `0xcf14de9d` · Caller: Anyone (view) · Source: `D20VRFCoordinator.sol` lines 381–387
460
+
461
+ `requestSeed` together with `deadline`, `fulfilled` and `refunded`. It reverts `NotReady` like `requestSeed`, so it is not a status read for waiting requests; use `getRequest`.
462
+
463
+ **Errors:** [`UnknownRequest`](#coordinator-error-unknownrequest), [`NotReady`](#coordinator-error-notready), [`BlockHashUnavailable`](#coordinator-error-blockhashunavailable).
464
+
465
+ #### <a id="coordinator-fn-getpendingrequestids"></a>`getPendingRequestIds`
466
+
467
+ ```solidity
468
+ function getPendingRequestIds(uint256 fromId, uint256 limit) external view returns (uint256[] ids, uint256 nextCursor)
469
+ ```
470
+
471
+ Selector `0xfdfe72e6` · Caller: Anyone (view) · Source: `D20VRFCoordinator.sol` lines 310–329
472
+
473
+ Scans `limit` (1 to 256) request IDs from `fromId` (at least 1) and returns those not fulfilled, not refunded and not past their deadline, with the ID to continue from. Continue with `nextCursor` until it equals `nextRequestId()`. The answer can be stale by the time a transaction lands.
474
+
475
+ **Errors:** [`InvalidScan`](#coordinator-error-invalidscan).
476
+
477
+ ### <a id="coordinator-keeper-key-and-configuration-reads"></a>Keeper, key and configuration reads
478
+
479
+ Views, callable by anyone. Nothing here has a setter except through an upgrade.
480
+
481
+ | Function | Selector | Meaning | Source |
482
+ | --- | --- | --- | --- |
483
+ | <a id="coordinator-fn-lastservedrequestid"></a>`lastServedRequestId() returns (uint256)` | `0xef54e226` | ID of the most recently accepted request; 0 before the first. | lines 53, 435 |
484
+ | <a id="coordinator-fn-lastservedindex"></a>`lastServedIndex() returns (uint256)` | `0x7e176eed` | Number of accepted requests so far: the `serveIndex` of the latest `RequestServed`. | lines 54, 436 |
485
+ | <a id="coordinator-fn-servedrequestat"></a>`servedRequestAt(uint256) returns (uint256)` | `0xf9a4acc6` | Request ID accepted at a serve index (from 1); 0 for an index not used yet. | lines 55, 436 |
486
+ | <a id="coordinator-fn-keyhash"></a>`keyHash() returns (bytes32)` | `0x61728f39` | `keccak256(abi.encode(publicKey))` of the VRF key; indexed in `RandomnessRequested` and `ProofVerified`. | lines 37, 180 |
487
+ | <a id="coordinator-fn-publickeyx"></a>`publicKeyX() returns (uint256)` | `0xfa6df55d` | x coordinate of the VRF public key. | line 35 |
488
+ | <a id="coordinator-fn-publickeyy"></a>`publicKeyY() returns (uint256)` | `0xd7a6f6e8` | y coordinate of the VRF public key. | line 36 |
489
+ | <a id="coordinator-fn-confirmationblocks"></a>`confirmationBlocks() returns (uint16)` | `0x460a58aa` | Blocks after the target block before the seed and proofs become available (1 to 64, set at initialization). | lines 45, 566 |
490
+ | <a id="coordinator-fn-epochregistry"></a>`epochRegistry() returns (address)` | `0x2b12cb69` | The `EpochEntropy` proxy that supplies epochs and the keeper-share recipient. | line 33 |
491
+ | <a id="coordinator-fn-protocolconfigurationhash"></a>`protocolConfigurationHash() returns (bytes32)` | `0x155cf49b` | Hash of the initialized configuration (public key, initial fee recipient, initial minimum fee, confirmations, registry, initial catalog hash, first epoch start, epoch length 200) under `CONFIG_DOMAIN`. Bound into every transcript hash. | lines 32, 190 |
492
+ | <a id="coordinator-fn-initialfeerecipient"></a>`initialFeeRecipient() returns (address)` | `0x308c2d6b` | Fee recipient given to `initialize`, used by replay. The live payout address is `feeRecipient()`. | lines 39, 182 |
493
+ | <a id="coordinator-fn-initialminfee"></a>`initialMinFee() returns (uint256)` | `0xb3839295` | Minimum fee given to `initialize`, used by replay. The live minimum is `minFee()`. | lines 105, 185 |
494
+
495
+ ### <a id="coordinator-owner-administration"></a>Owner administration
496
+
497
+ Owner-only functions revert `OwnableUnauthorizedAccount` for anyone else. No setter can change an existing request, the VRF key, the registry or the confirmations.
498
+
499
+ #### <a id="coordinator-fn-setpricing"></a>`setPricing`
500
+
501
+ ```solidity
502
+ function setPricing(uint256 nextMinFee, uint16 multiplier, uint32 overhead) external
503
+ ```
504
+
505
+ Selector `0x4c729ce6` · Caller: Owner · Source: `D20VRFCoordinator.sol` lines 205–210
506
+
507
+ Sets `minFee` (at most `MAX_MIN_FEE`, 10 USDC), `feeMultiplier` (at most `MAX_FEE_MULTIPLIER`, 20) and `fulfillGasOverhead` (`MIN_FULFILL_GAS_OVERHEAD` to `MAX_FULFILL_GAS_OVERHEAD`, 100,000 to 2,000,000 gas). Affects requests created afterwards; open requests keep their escrowed fee.
508
+
509
+ **Emits:** [`PricingChanged`](#coordinator-event-pricingchanged).
510
+
511
+ **Errors:** [`OwnableUnauthorizedAccount`](#coordinator-error-ownableunauthorizedaccount), [`InvalidConfig`](#coordinator-error-invalidconfig).
512
+
513
+ #### <a id="coordinator-fn-setrefundbps"></a>`setRefundBps`
514
+
515
+ ```solidity
516
+ function setRefundBps(uint16 next) external
517
+ ```
518
+
519
+ Selector `0x55a94d1b` · Caller: Owner · Source: `D20VRFCoordinator.sol` lines 214–218
520
+
521
+ Sets the refund ratio for requests created afterwards, `MIN_REFUND_BPS` (5000) to 10000.
522
+
523
+ **Emits:** [`RefundBpsChanged`](#coordinator-event-refundbpschanged).
524
+
525
+ **Errors:** [`OwnableUnauthorizedAccount`](#coordinator-error-ownableunauthorizedaccount), [`InvalidConfig`](#coordinator-error-invalidconfig).
526
+
527
+ #### <a id="coordinator-fn-setkeeperfeebps"></a>`setKeeperFeeBps`
528
+
529
+ ```solidity
530
+ function setKeeperFeeBps(uint16 next) external
531
+ ```
532
+
533
+ Selector `0xe140f0ca` · Caller: Owner · Source: `D20VRFCoordinator.sol` lines 201–204
534
+
535
+ Sets the keeper share, 0 to 10000 basis points. Read at each acceptance, so it also applies to open requests accepted later.
536
+
537
+ **Emits:** [`KeeperFeeBpsChanged`](#coordinator-event-keeperfeebpschanged).
538
+
539
+ **Errors:** [`OwnableUnauthorizedAccount`](#coordinator-error-ownableunauthorizedaccount), [`InvalidConfig`](#coordinator-error-invalidconfig).
540
+
541
+ #### <a id="coordinator-fn-setfeerecipient"></a>`setFeeRecipient`
542
+
543
+ ```solidity
544
+ function setFeeRecipient(address next) external
545
+ ```
546
+
547
+ Selector `0xe74b981b` · Caller: Owner · Source: `D20VRFCoordinator.sol` lines 197–200
548
+
549
+ Sets the address allowed to withdraw `earnedFees`, including fees earned before the change. The zero address is rejected.
550
+
551
+ **Emits:** [`FeeRecipientChanged`](#coordinator-event-feerecipientchanged).
552
+
553
+ **Errors:** [`OwnableUnauthorizedAccount`](#coordinator-error-ownableunauthorizedaccount), [`InvalidConfig`](#coordinator-error-invalidconfig).
554
+
555
+ #### <a id="coordinator-fn-owner"></a>`owner`
556
+
557
+ ```solidity
558
+ function owner() external view returns (address)
559
+ ```
560
+
561
+ Selector `0x8da5cb5b` · Caller: Anyone (view)
562
+
563
+ Current owner: upgrade authority and the only account that can call the setters.
564
+
565
+ #### <a id="coordinator-fn-pendingowner"></a>`pendingOwner`
566
+
567
+ ```solidity
568
+ function pendingOwner() external view returns (address)
569
+ ```
570
+
571
+ Selector `0xe30c3978` · Caller: Anyone (view)
572
+
573
+ Account nominated by `transferOwnership` that has not accepted yet; zero when none.
574
+
575
+ #### <a id="coordinator-fn-transferownership"></a>`transferOwnership`
576
+
577
+ ```solidity
578
+ function transferOwnership(address newOwner) external
579
+ ```
580
+
581
+ Selector `0xf2fde38b` · Caller: Owner
582
+
583
+ Starts a two-step transfer by nominating `newOwner`; ownership moves only when that account calls `acceptOwnership`. A new call replaces the nomination, and the zero address cancels it.
584
+
585
+ **Emits:** [`OwnershipTransferStarted`](#coordinator-event-ownershiptransferstarted).
586
+
587
+ **Errors:** [`OwnableUnauthorizedAccount`](#coordinator-error-ownableunauthorizedaccount).
588
+
589
+ #### <a id="coordinator-fn-acceptownership"></a>`acceptOwnership`
590
+
591
+ ```solidity
592
+ function acceptOwnership() external
593
+ ```
594
+
595
+ Selector `0x79ba5097` · Caller: Pending owner
596
+
597
+ Completes the transfer to the caller and clears the nomination.
598
+
599
+ **Emits:** [`OwnershipTransferred`](#coordinator-event-ownershiptransferred).
600
+
601
+ **Errors:** [`OwnableUnauthorizedAccount`](#coordinator-error-ownableunauthorizedaccount).
602
+
603
+ #### <a id="coordinator-fn-renounceownership"></a>`renounceOwnership`
604
+
605
+ ```solidity
606
+ function renounceOwnership() external view
607
+ ```
608
+
609
+ Selector `0x715018a6` · Caller: Owner · Source: `D20VRFCoordinator.sol` lines 194–195
610
+
611
+ Disabled and declared `view`: the owner gets `RenounceDisabled` and anyone else `OwnableUnauthorizedAccount`, so the contract always has an owner and upgrade authority can only move through an accepted transfer.
612
+
613
+ **Errors:** [`OwnableUnauthorizedAccount`](#coordinator-error-ownableunauthorizedaccount), [`RenounceDisabled`](#coordinator-error-renouncedisabled).
614
+
615
+ #### <a id="coordinator-fn-upgradetoandcall"></a>`upgradeToAndCall`
616
+
617
+ ```solidity
618
+ function upgradeToAndCall(address newImplementation, bytes data) external payable
619
+ ```
620
+
621
+ Selector `0x4f1ef286` · Caller: Owner, through the proxy
622
+
623
+ UUPS upgrade: points the proxy at `newImplementation`, which must report the ERC-1967 slot from `proxiableUUID`, and delegatecalls `data` when it is non-empty. An upgrade can change any behavior described here. Integrators check the implementation when they integrate and again whenever a proxy emits `Upgraded` or the deployment manifest records an upgrade (README [Security and trust](README.md#security-and-trust)).
624
+
625
+ **Emits:** [`Upgraded`](#coordinator-event-upgraded).
626
+
627
+ **Errors:** [`UUPSUnauthorizedCallContext`](#coordinator-error-uupsunauthorizedcallcontext), [`OwnableUnauthorizedAccount`](#coordinator-error-ownableunauthorizedaccount), [`ERC1967InvalidImplementation`](#coordinator-error-erc1967invalidimplementation), [`UUPSUnsupportedProxiableUUID`](#coordinator-error-uupsunsupportedproxiableuuid), [`ERC1967NonPayable`](#coordinator-error-erc1967nonpayable), [`AddressEmptyCode`](#coordinator-error-addressemptycode), [`FailedCall`](#coordinator-error-failedcall).
628
+
629
+ #### <a id="coordinator-fn-proxiableuuid"></a>`proxiableUUID`
630
+
631
+ ```solidity
632
+ function proxiableUUID() external view returns (bytes32)
633
+ ```
634
+
635
+ Selector `0x52d1902d` · Caller: Anyone (view)
636
+
637
+ ERC-1822 check used by `upgradeToAndCall`. Returns the ERC-1967 implementation slot when called on an implementation contract directly and reverts through the proxy.
638
+
639
+ **Errors:** [`UUPSUnauthorizedCallContext`](#coordinator-error-uupsunauthorizedcallcontext).
640
+
641
+ #### <a id="coordinator-fn-initialize"></a>`initialize`
642
+
643
+ ```solidity
644
+ function initialize(uint256[2] publicKey, address initialOwner, address recipient, uint256 fee, uint16 confirmations, address registry, uint16 keeperBps) external
645
+ ```
646
+
647
+ Selector `0x56b95b47` · Caller: Once, by `D20Proxy` at deployment · Source: `D20VRFCoordinator.sol` lines 170–191
648
+
649
+ Sets owner, VRF public key, fee recipient, minimum fee, confirmations, registry and keeper share, with `feeMultiplier` 5, `fulfillGasOverhead` 300,000 and `refundBps` 10000. A public key that is not on the curve can also revert with an `Error(string)` from the verifier.
650
+
651
+ **Emits:** [`OwnershipTransferred`](#coordinator-event-ownershiptransferred), [`Initialized`](#coordinator-event-initialized).
652
+
653
+ **Errors:** [`InvalidInitialization`](#coordinator-error-invalidinitialization), [`OwnableInvalidOwner`](#coordinator-error-ownableinvalidowner), [`InvalidConfig`](#coordinator-error-invalidconfig), [`InvalidPublicKey`](#coordinator-error-invalidpublickey).
654
+
655
+ ### <a id="coordinator-constants"></a>Constants
656
+
657
+ Views returning values fixed in the implementation code.
658
+
659
+ | Constant | Returns | Value | Selector | Meaning |
660
+ | --- | --- | --- | --- | --- |
661
+ | <a id="coordinator-fn-min_callback_gas"></a>`MIN_CALLBACK_GAS` | `uint32` | `30_000` | `0x4374e10c` | Lowest `callbackGasLimit` and `retryCallback` gas limit. |
662
+ | <a id="coordinator-fn-max_callback_gas"></a>`MAX_CALLBACK_GAS` | `uint32` | `1_000_000` | `0x6d9809a0` | Highest callback or notification gas limit. |
663
+ | <a id="coordinator-fn-response_timeout"></a>`RESPONSE_TIMEOUT` | `uint64` | `60 seconds` | `0x11e219d7` | Seconds from the request block timestamp to `deadline`. |
664
+ | <a id="coordinator-fn-refund_callback_gas"></a>`REFUND_CALLBACK_GAS` | `uint32` | `100_000` | `0x8a8ae284` | Gas for the first `onRefund` notification, and the lowest `retryRefundCallback` gas limit. |
665
+ | <a id="coordinator-fn-max_fulfill_batch"></a>`MAX_FULFILL_BATCH` | `uint256` | `16` | `0x1d7e0cc4` | Most requests per `fulfillRandomnessBatch`. |
666
+ | <a id="coordinator-fn-max_evidence_packet_bytes"></a>`MAX_EVIDENCE_PACKET_BYTES` | `uint256` | `512` | `0x7fcf2f33` | Upper bound on the `FulfillmentEvidence` packet (actual size 416 bytes). |
667
+ | <a id="coordinator-fn-max_min_fee"></a>`MAX_MIN_FEE` | `uint256` | `10e18` | `0x8483d43e` | Highest `minFee`: 10 USDC in 18-decimal native units. |
668
+ | <a id="coordinator-fn-max_fee_multiplier"></a>`MAX_FEE_MULTIPLIER` | `uint16` | `20` | `0xb6994144` | Highest `feeMultiplier`. |
669
+ | <a id="coordinator-fn-min_fulfill_gas_overhead"></a>`MIN_FULFILL_GAS_OVERHEAD` | `uint32` | `100_000` | `0x35ccd0b4` | Lowest `fulfillGasOverhead`. |
670
+ | <a id="coordinator-fn-max_fulfill_gas_overhead"></a>`MAX_FULFILL_GAS_OVERHEAD` | `uint32` | `2_000_000` | `0x2a06b47d` | Highest `fulfillGasOverhead`. |
671
+ | <a id="coordinator-fn-min_refund_bps"></a>`MIN_REFUND_BPS` | `uint16` | `5000` | `0xaf7718f1` | Lowest `refundBps` (50%). |
672
+ | <a id="coordinator-fn-seed_domain"></a>`SEED_DOMAIN` | `bytes32` | `keccak256("D20_VRF_SEED")` | `0x6000054d` | Domain tag of `requestSeed`. |
673
+ | <a id="coordinator-fn-transcript_domain"></a>`TRANSCRIPT_DOMAIN` | `bytes32` | `keccak256("D20_VRF_TRANSCRIPT")` | `0xab2fde00` | Domain tag of the transcript hash. |
674
+ | <a id="coordinator-fn-config_domain"></a>`CONFIG_DOMAIN` | `bytes32` | `keccak256("D20_VRF_CONFIG")` | `0x3624ffed` | Domain tag of `protocolConfigurationHash`. |
675
+ | <a id="coordinator-fn-upgrade_interface_version"></a>`UPGRADE_INTERFACE_VERSION` | `string` | `"5.0.0"` | `0xad3cb1cc` | OpenZeppelin UUPS interface version: upgrades go through `upgradeToAndCall` only. |
676
+
677
+ ### <a id="coordinator-events"></a>Events
678
+
679
+ **Request lifecycle**
680
+
681
+ #### <a id="coordinator-event-randomnessrequested"></a>`RandomnessRequested`
682
+
683
+ ```solidity
684
+ event RandomnessRequested(uint256 indexed requestId, address indexed consumer, bytes32 indexed keyHash, bytes32 clientSeed, uint64 requestBlock, uint32 callbackGasLimit, uint256 feePaid, address refundAddress, uint64 deadline)
685
+ ```
686
+
687
+ Topic 0 `0xaf91b17376114a36689aa115062983bda7b43263a891fb8de0cc69d30d4240ad` · Emitted by: [`requestRandomness`](#coordinator-fn-requestrandomness), [`requestMappedRandomness`](#coordinator-fn-requestmappedrandomness) · Source: `D20VRFCoordinator.sol` lines 139–143, 284–285
688
+
689
+ A request was created. `feePaid` is the escrowed fee, not `msg.value`; `deadline` is the block timestamp plus 60 seconds. Read `requestId` from this log in the request receipt, filtering by the coordinator address and event name: with an overpayment, `FeeOverpaymentCredited` comes first.
690
+
691
+ #### <a id="coordinator-event-mappingrequested"></a>`MappingRequested`
692
+
693
+ ```solidity
694
+ event MappingRequested(uint256 indexed requestId, bytes32 indexed mappingHash, RandomnessMapping.Spec spec)
695
+ ```
696
+
697
+ Topic 0 `0xbe1c93f40bd74ff9acd22dc40818e36d04e0b8a49b8238d0537c63219c2336dd` · Emitted by: [`requestRandomness`](#coordinator-fn-requestrandomness), [`requestMappedRandomness`](#coordinator-fn-requestmappedrandomness) · Source: `D20VRFCoordinator.sol` lines 158, 286
698
+
699
+ Emitted right after `RandomnessRequested` with the stored spec (all zero for a raw request) and its hash.
700
+
701
+ #### <a id="coordinator-event-feeoverpaymentcredited"></a>`FeeOverpaymentCredited`
702
+
703
+ ```solidity
704
+ event FeeOverpaymentCredited(uint256 indexed requestId, address indexed refundAddress, uint256 amount)
705
+ ```
706
+
707
+ Topic 0 `0x8ae693db98f043f48e8f427375449ed5576aba97575e4f7f93ff2c1f6c75dcb5` · Emitted by: [`requestRandomness`](#coordinator-fn-requestrandomness), [`requestMappedRandomness`](#coordinator-fn-requestmappedrandomness) · Source: `D20VRFCoordinator.sol` lines 152, 278–283
708
+
709
+ `msg.value` exceeded the fee and `amount` was added to `refundCredits(refundAddress)`, independently of what happens to the request. Emitted before `RandomnessRequested`.
710
+
711
+ #### <a id="coordinator-event-blockhashstored"></a>`BlockHashStored`
712
+
713
+ ```solidity
714
+ event BlockHashStored(uint256 indexed requestId, uint64 targetBlock, bytes32 blockHash)
715
+ ```
716
+
717
+ Topic 0 `0x81bc3b4ec75af0fb9ed3521d7c766d8f995d04b0735c17d61ff9d468b0f04911` · Emitted by: [`fulfillRandomness`](#coordinator-fn-fulfillrandomness), [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch), [`storeBlockHash`](#coordinator-fn-storeblockhash) · Source: `D20VRFCoordinator.sol` lines 144, 572–579
718
+
719
+ The target block hash of the request was stored. Emitted once per request: by `storeBlockHash`, or by fulfillment if the hash was not stored before.
720
+
721
+ #### <a id="coordinator-event-requestserved"></a>`RequestServed`
722
+
723
+ ```solidity
724
+ event RequestServed(uint256 indexed requestId, uint256 indexed serveIndex)
725
+ ```
726
+
727
+ Topic 0 `0x2012511e6cebd578bcabff1ef3346edb032cbab8622e9f23d9f15d7d1037267f` · Emitted by: [`fulfillRandomness`](#coordinator-fn-fulfillrandomness), [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch) · Source: `D20VRFCoordinator.sol` lines 160, 435–437
728
+
729
+ A proof was accepted. `serveIndex` counts accepted requests from 1 (`lastServedIndex`, `servedRequestAt`).
730
+
731
+ #### <a id="coordinator-event-proofverified"></a>`ProofVerified`
732
+
733
+ ```solidity
734
+ event ProofVerified(uint256 indexed requestId, bytes32 indexed keyHash, uint256 seed, bytes32 proofHash)
735
+ ```
736
+
737
+ Topic 0 `0x55bb25be3ecd9f68ceae7cdabf4eabe2e0940bd8fc1c26c0d68ad5f7c5d08d22` · Emitted by: [`fulfillRandomness`](#coordinator-fn-fulfillrandomness), [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch) · Source: `D20VRFCoordinator.sol` lines 159, 438
738
+
739
+ Seed and hash of the accepted proof.
740
+
741
+ #### <a id="coordinator-event-randomnessfulfilled"></a>`RandomnessFulfilled`
742
+
743
+ ```solidity
744
+ event RandomnessFulfilled(uint256 indexed requestId, bytes32 randomness, address indexed submitter)
745
+ ```
746
+
747
+ Topic 0 `0x9c82683ee7932041c254d206bcce4241d66a811d53ee7191799cc120777b2b87` · Emitted by: [`fulfillRandomness`](#coordinator-fn-fulfillrandomness), [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch) · Source: `D20VRFCoordinator.sol` lines 145, 439
748
+
749
+ A proof was accepted and `randomness` is final. `submitter` sent the transaction; `KeeperFeePaid` names the wallet that received the keeper share, which is `submitter` only when the registry authorizes it.
750
+
751
+ #### <a id="coordinator-event-fulfillmentevidence"></a>`FulfillmentEvidence`
752
+
753
+ ```solidity
754
+ event FulfillmentEvidence(uint256 indexed requestId, bytes32 indexed transcriptHash, bytes packet)
755
+ ```
756
+
757
+ Topic 0 `0xa121bbea897439460dfb08c3e6d6af064bc1f31e9477471828a87c5596b92e77` · Emitted by: [`fulfillRandomness`](#coordinator-fn-fulfillrandomness), [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch) · Source: `D20VRFCoordinator.sol` lines 163–164, 450–454
758
+
759
+ The accepted proof as a 416-byte ABI-encoded packet, indexed by `transcriptHash`. Decode it with `decodeEvidencePacket`; take evidence from this log, not from calldata, since a batch carries several proofs.
760
+
761
+ #### <a id="coordinator-event-callbackattempted"></a>`CallbackAttempted`
762
+
763
+ ```solidity
764
+ event CallbackAttempted(uint256 indexed requestId, bool success, uint32 gasLimit)
765
+ ```
766
+
767
+ Topic 0 `0x70f64c0739e827900ae6f2e1317601653f4080bc857f423671fc58d5822f1f4a` · Emitted by: [`retryCallback`](#coordinator-fn-retrycallback), [`fulfillRandomness`](#coordinator-fn-fulfillrandomness), [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch) · Source: `D20VRFCoordinator.sol` lines 146, 613–630
768
+
769
+ Result of calling `rawFulfillRandomness` with `gasLimit` gas, at fulfillment and at each `retryCallback`. `success` false means the consumer reverted, ran out of gas or has no code; the word is accepted either way.
770
+
771
+ #### <a id="coordinator-event-keeperfeepaid"></a>`KeeperFeePaid`
772
+
773
+ ```solidity
774
+ event KeeperFeePaid(uint256 indexed requestId, address indexed keeper, uint256 amount, bool paid)
775
+ ```
776
+
777
+ Topic 0 `0x7605929b04963e0365f647d9ab12e7ac4aeba5474bb80f1e1554fccad0584683` · Emitted by: [`fulfillRandomness`](#coordinator-fn-fulfillrandomness), [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch) · Source: `D20VRFCoordinator.sol` lines 153, 442–447
778
+
779
+ At acceptance, when the keeper share is non-zero: `amount` went to `keeper`, the submitter when the registry authorizes it and `committer()` otherwise, by a 30,000-gas transfer (`paid` true) or was added to `keeperCredits(keeper)` (`paid` false). Emitted after `CallbackAttempted`.
780
+
781
+ #### <a id="coordinator-event-fulfillmentskipped"></a>`FulfillmentSkipped`
782
+
783
+ ```solidity
784
+ event FulfillmentSkipped(uint256 indexed requestId, uint8 reason)
785
+ ```
786
+
787
+ Topic 0 `0x45d96bda73a91db41bdeab56114e5d7b9f42c9f38add9e2d2c6d6f5761203ca3` · Emitted by: [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch) · Source: `D20VRFCoordinator.sol` lines 161–162, 407–408
788
+
789
+ A batch member was left untouched: `reason` 1 already fulfilled, 2 refunded, 3 past its deadline.
790
+
791
+ #### <a id="coordinator-event-requestrefundedto"></a>`RequestRefundedTo`
792
+
793
+ ```solidity
794
+ event RequestRefundedTo(uint256 indexed requestId, address indexed refundAddress, uint256 amount, bool paid)
795
+ ```
796
+
797
+ Topic 0 `0x0f6107d218fea62a20553f3700dba7c94dcf653bd2027c0bf1ebe0832f42a506` · Emitted by: [`refundRequest`](#coordinator-fn-refundrequest) · Source: `D20VRFCoordinator.sol` lines 155, 488
798
+
799
+ An expired request was refunded: `amount` (`feePaid × requestRefundBps / 10000`) was sent to `refundAddress` (`paid` true) or added to its refund credit (`paid` false).
800
+
801
+ #### <a id="coordinator-event-refundcallbackattempted"></a>`RefundCallbackAttempted`
802
+
803
+ ```solidity
804
+ event RefundCallbackAttempted(uint256 indexed requestId, address indexed consumer, bool success, uint32 gasLimit)
805
+ ```
806
+
807
+ Topic 0 `0x88448c9fbcfc67f28f0266e82766e402ccd28115fbb84edb6e5b2597effe83d8` · Emitted by: [`refundRequest`](#coordinator-fn-refundrequest), [`retryRefundCallback`](#coordinator-fn-retryrefundcallback) · Source: `D20VRFCoordinator.sol` lines 157, 502–513
808
+
809
+ Result of calling `onRefund(requestId)` on `consumer` with `gasLimit` gas: 100,000 at `refundRequest`, the caller's limit at `retryRefundCallback`.
810
+
811
+ **Credits and withdrawals**
812
+
813
+ #### <a id="coordinator-event-refundcreditwithdrawn"></a>`RefundCreditWithdrawn`
814
+
815
+ ```solidity
816
+ event RefundCreditWithdrawn(address indexed owner, address indexed recipient, uint256 amount)
817
+ ```
818
+
819
+ Topic 0 `0x9d520065b24fda0469128acd3f3078de7e43d70ab762aeea8c741bde25070192` · Emitted by: [`withdrawRefundCredit`](#coordinator-fn-withdrawrefundcredit) · Source: `D20VRFCoordinator.sol` lines 156, 524
820
+
821
+ `owner`, the credit holder (not the contract owner), withdrew `amount` of refund credit to `recipient`.
822
+
823
+ #### <a id="coordinator-event-keepercreditwithdrawn"></a>`KeeperCreditWithdrawn`
824
+
825
+ ```solidity
826
+ event KeeperCreditWithdrawn(address indexed keeper, address indexed recipient, uint256 amount)
827
+ ```
828
+
829
+ Topic 0 `0x22f05c41968705c032a86a65f8fda7e64483ea5e6b6b27a920d1fbfab94267ba` · Emitted by: [`withdrawKeeperCredit`](#coordinator-fn-withdrawkeepercredit) · Source: `D20VRFCoordinator.sol` lines 154, 546
830
+
831
+ `keeper` withdrew `amount` of keeper credit to `recipient`.
832
+
833
+ #### <a id="coordinator-event-feeswithdrawn"></a>`FeesWithdrawn`
834
+
835
+ ```solidity
836
+ event FeesWithdrawn(address indexed recipient, uint256 amount)
837
+ ```
838
+
839
+ Topic 0 `0xc0819c13be868895eb93e40eaceb96de976442fa1d404e5c55f14bb65a8c489a` · Emitted by: [`withdrawFees`](#coordinator-fn-withdrawfees) · Source: `D20VRFCoordinator.sol` lines 147, 535
840
+
841
+ The fee recipient withdrew `amount` of earned fees to `recipient`.
842
+
843
+ **Administration and upgrades**
844
+
845
+ #### <a id="coordinator-event-pricingchanged"></a>`PricingChanged`
846
+
847
+ ```solidity
848
+ event PricingChanged(uint256 minFee, uint16 feeMultiplier, uint32 fulfillGasOverhead)
849
+ ```
850
+
851
+ Topic 0 `0x32806eb5e21ac2f5fb7d11f898c2995e19fdf203c8a5aeed8b824506cd0d44ff` · Emitted by: [`setPricing`](#coordinator-fn-setpricing) · Source: `D20VRFCoordinator.sol` lines 150, 209
852
+
853
+ New `minFee`, `feeMultiplier` and `fulfillGasOverhead` for requests created afterwards.
854
+
855
+ #### <a id="coordinator-event-refundbpschanged"></a>`RefundBpsChanged`
856
+
857
+ ```solidity
858
+ event RefundBpsChanged(uint16 previousBps, uint16 newBps)
859
+ ```
860
+
861
+ Topic 0 `0x21e3c4cf3007c4ee385a3936593ff4cfa23fc17bca1175b6350c546f9810e0d8` · Emitted by: [`setRefundBps`](#coordinator-fn-setrefundbps) · Source: `D20VRFCoordinator.sol` lines 151, 217
862
+
863
+ New refund ratio for requests created afterwards.
864
+
865
+ #### <a id="coordinator-event-keeperfeebpschanged"></a>`KeeperFeeBpsChanged`
866
+
867
+ ```solidity
868
+ event KeeperFeeBpsChanged(uint16 previousBps, uint16 newBps)
869
+ ```
870
+
871
+ Topic 0 `0xa648a60f1d22511c1cc898ca69b633d1a1114079e83734b9ab9a13e0e28c68b7` · Emitted by: [`setKeeperFeeBps`](#coordinator-fn-setkeeperfeebps) · Source: `D20VRFCoordinator.sol` lines 149, 203
872
+
873
+ New keeper share, applied at later acceptances, including of requests already open.
874
+
875
+ #### <a id="coordinator-event-feerecipientchanged"></a>`FeeRecipientChanged`
876
+
877
+ ```solidity
878
+ event FeeRecipientChanged(address indexed previousRecipient, address indexed newRecipient)
879
+ ```
880
+
881
+ Topic 0 `0x0bc21fe5c3ab742ff1d15b5c4477ffbacf1167e618228078fa625edebe7f331d` · Emitted by: [`setFeeRecipient`](#coordinator-fn-setfeerecipient) · Source: `D20VRFCoordinator.sol` lines 148, 199
882
+
883
+ New address allowed to withdraw earned fees.
884
+
885
+ #### <a id="coordinator-event-ownershiptransferstarted"></a>`OwnershipTransferStarted`
886
+
887
+ ```solidity
888
+ event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner)
889
+ ```
890
+
891
+ Topic 0 `0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700` · Emitted by: [`transferOwnership`](#coordinator-fn-transferownership)
892
+
893
+ `transferOwnership` nominated `newOwner`; the zero address means a nomination was cancelled.
894
+
895
+ #### <a id="coordinator-event-ownershiptransferred"></a>`OwnershipTransferred`
896
+
897
+ ```solidity
898
+ event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)
899
+ ```
900
+
901
+ Topic 0 `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` · Emitted by: [`acceptOwnership`](#coordinator-fn-acceptownership), [`initialize`](#coordinator-fn-initialize)
902
+
903
+ Ownership moved: from the zero address at initialization, and at each `acceptOwnership`.
904
+
905
+ #### <a id="coordinator-event-upgraded"></a>`Upgraded`
906
+
907
+ ```solidity
908
+ event Upgraded(address indexed implementation)
909
+ ```
910
+
911
+ Topic 0 `0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b` · Emitted by: [`upgradeToAndCall`](#coordinator-fn-upgradetoandcall), proxy deployment
912
+
913
+ The proxy now runs `implementation`. Emitted by the proxy at deployment and at every `upgradeToAndCall`. Compare the address with the deployment manifest; an implementation you have not reviewed means stop and review before sending more requests.
914
+
915
+ #### <a id="coordinator-event-initialized"></a>`Initialized`
916
+
917
+ ```solidity
918
+ event Initialized(uint64 version)
919
+ ```
920
+
921
+ Topic 0 `0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2` · Emitted by: [`initialize`](#coordinator-fn-initialize)
922
+
923
+ `initialize` ran on the proxy (`version` 1). Each implementation contract also emitted it once at construction with `version` 2^64 − 1, which locks the implementation against initialization.
924
+
925
+ ### <a id="coordinator-errors"></a>Errors
926
+
927
+ **Requesting**
928
+
929
+ #### <a id="coordinator-error-contractconsumerrequired"></a>`ContractConsumerRequired`
930
+
931
+ `error ContractConsumerRequired()` · Selector `0x2b99db1e` · Source: `D20VRFCoordinator.sol` lines 111, 251
932
+
933
+ **Raised by:** [`requestRandomness`](#coordinator-fn-requestrandomness), [`requestMappedRandomness`](#coordinator-fn-requestmappedrandomness).
934
+
935
+ The caller of a request function has no code: an externally owned account, or a contract still running its constructor.
936
+
937
+ **What to do:** Send the request through a deployed consumer contract (README [Integrate a consumer](README.md#integrate-a-consumer)), and not from its constructor.
938
+
939
+ #### <a id="coordinator-error-invalidrefundaddress"></a>`InvalidRefundAddress`
940
+
941
+ `error InvalidRefundAddress()` · Selector `0xe2fe2726` · Source: `D20VRFCoordinator.sol` lines 125, 252, 517
942
+
943
+ **Raised by:** [`requestRandomness`](#coordinator-fn-requestrandomness), [`requestMappedRandomness`](#coordinator-fn-requestmappedrandomness), [`withdrawRefundCredit`](#coordinator-fn-withdrawrefundcredit).
944
+
945
+ A request named the zero address as refund address, or `withdrawRefundCredit` named the zero address as recipient.
946
+
947
+ **What to do:** Pass a non-zero address that can receive a plain native transfer or call `withdrawRefundCredit`.
948
+
949
+ #### <a id="coordinator-error-invalidcallbackgas"></a>`InvalidCallbackGas`
950
+
951
+ `error InvalidCallbackGas()` · Selector `0x35883c54` · Source: `D20VRFCoordinator.sol` lines 113, 462, 498, 609–611
952
+
953
+ **Raised by:** [`requestRandomness`](#coordinator-fn-requestrandomness), [`requestMappedRandomness`](#coordinator-fn-requestmappedrandomness), [`retryCallback`](#coordinator-fn-retrycallback), [`retryRefundCallback`](#coordinator-fn-retryrefundcallback).
954
+
955
+ A gas limit is out of range: a request `callbackGasLimit` outside 30,000 to 1,000,000; a `retryCallback` limit outside that range or below the request's `callbackGasLimit`; a `retryRefundCallback` limit outside 100,000 to 1,000,000.
956
+
957
+ **What to do:** Use a limit inside the range; retry with at least the original limit.
958
+
959
+ #### <a id="coordinator-error-incorrectfee"></a>`IncorrectFee`
960
+
961
+ `error IncorrectFee(uint256 expected, uint256 actual)` · Selector `0xdcf6afcb` · Source: `D20VRFCoordinator.sol` lines 112, 255
962
+
963
+ **Raised by:** [`requestRandomness`](#coordinator-fn-requestrandomness), [`requestMappedRandomness`](#coordinator-fn-requestmappedrandomness).
964
+
965
+ `actual` (`msg.value`) is below `expected`, the fee computed in the request transaction. No request was created.
966
+
967
+ **What to do:** Quote again with `quoteFeeAt(callbackGasLimit, latestBlock.baseFeePerGas)` plus a buffer (`quoteRequestFee`) and resend. A contract paying in the same transaction sends `quoteFee(callbackGasLimit)`. Never quote with `quoteFee` through `eth_call`.
968
+
969
+ #### <a id="coordinator-error-feeoverflow"></a>`FeeOverflow`
970
+
971
+ `error FeeOverflow()` · Selector `0x8181adca` · Source: `D20VRFCoordinator.sol` lines 136, 225
972
+
973
+ **Raised by:** [`quoteFee`](#coordinator-fn-quotefee), [`quoteFeeAt`](#coordinator-fn-quotefeeat), [`requestRandomness`](#coordinator-fn-requestrandomness), [`requestMappedRandomness`](#coordinator-fn-requestmappedrandomness).
974
+
975
+ The dynamic fee exceeds the uint96 escrow limit. Within the pricing bounds that needs a base fee above about 1.3e21 wei.
976
+
977
+ **What to do:** Not expected on a live chain. For `quoteFeeAt`, check that `baseFee` is in wei.
978
+
979
+ #### <a id="coordinator-error-invalidmapping"></a>`InvalidMapping`
980
+
981
+ `error InvalidMapping()` · Selector `0x07a966e0` · Source: `libraries/RandomnessMapping.sol` lines 19, 21–39
982
+
983
+ **Raised by:** [`requestMappedRandomness`](#coordinator-fn-requestmappedrandomness), [`mapRandomness`](#coordinator-fn-maprandomness).
984
+
985
+ The spec breaks the rules for its operation (README [Randomness options](README.md#randomness-options)). Declared in `RandomnessMapping`.
986
+
987
+ **What to do:** Build specs with the `D20VRFRequests` helpers or TypeScript `builtins`, which enforce the same bounds.
988
+
989
+ #### <a id="coordinator-error-epochunavailable"></a>`EpochUnavailable`
990
+
991
+ `error EpochUnavailable()` · Selector `0x0b3487b8` · Source: `D20VRFCoordinator.sol` lines 109, 259
992
+
993
+ **Raised by:** [`requestRandomness`](#coordinator-fn-requestrandomness), [`requestMappedRandomness`](#coordinator-fn-requestmappedrandomness).
994
+
995
+ The request block is before the registry's first epoch: `epochForBlock(block.number)` is 0.
996
+
997
+ **What to do:** Not expected on the Arc deployments, whose epochs have started. Check that you call the coordinator proxy for your chain; on a new deployment, wait for `firstEpochStart`.
998
+
999
+ **Reading and recovery**
1000
+
1001
+ #### <a id="coordinator-error-unknownrequest"></a>`UnknownRequest`
1002
+
1003
+ `error UnknownRequest()` · Selector `0x6d080297` · Source: `D20VRFCoordinator.sol` lines 114, 549–552
1004
+
1005
+ **Raised by:** [`getRequest`](#coordinator-fn-getrequest), [`getMapping`](#coordinator-fn-getmapping), [`getMappedResult`](#coordinator-fn-getmappedresult), [`requestFeePaid`](#coordinator-fn-requestfeepaid), [`requestRefundBps`](#coordinator-fn-requestrefundbps), [`refundRequest`](#coordinator-fn-refundrequest), [`retryCallback`](#coordinator-fn-retrycallback), [`retryRefundCallback`](#coordinator-fn-retryrefundcallback), [`fulfillRandomness`](#coordinator-fn-fulfillrandomness), [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch), [`storeBlockHash`](#coordinator-fn-storeblockhash), [`verifyRequestProof`](#coordinator-fn-verifyrequestproof), [`requestSeed`](#coordinator-fn-requestseed), [`getProofContext`](#coordinator-fn-getproofcontext).
1006
+
1007
+ No request has this ID: 0, or not below `nextRequestId()`. An unknown ID also reverts a whole `fulfillRandomnessBatch`.
1008
+
1009
+ **What to do:** Take `requestId` from the `RandomnessRequested` log of the request receipt, and read from the same chain and coordinator proxy.
1010
+
1011
+ #### <a id="coordinator-error-notfulfilled"></a>`NotFulfilled`
1012
+
1013
+ `error NotFulfilled()` · Selector `0x07bc6c3e` · Source: `D20VRFCoordinator.sol` lines 118, 347, 459
1014
+
1015
+ **Raised by:** [`getMappedResult`](#coordinator-fn-getmappedresult), [`retryCallback`](#coordinator-fn-retrycallback).
1016
+
1017
+ `getMappedResult` or `retryCallback` on a request without an accepted proof, including an expired or refunded one.
1018
+
1019
+ **What to do:** Poll `getRequest(requestId)` until `fulfilled`. Once a block timestamp is after `deadline` without fulfillment, the request has expired and only `refundRequest` applies.
1020
+
1021
+ #### <a id="coordinator-error-alreadydelivered"></a>`AlreadyDelivered`
1022
+
1023
+ `error AlreadyDelivered()` · Selector `0xb9f79653` · Source: `D20VRFCoordinator.sol` lines 119, 460
1024
+
1025
+ **Raised by:** [`retryCallback`](#coordinator-fn-retrycallback).
1026
+
1027
+ `retryCallback` on a request whose callback already succeeded.
1028
+
1029
+ **What to do:** Nothing to retry.
1030
+
1031
+ #### <a id="coordinator-error-refundnotavailable"></a>`RefundNotAvailable`
1032
+
1033
+ `error RefundNotAvailable()` · Selector `0x0b4d6981` · Source: `D20VRFCoordinator.sol` lines 128, 471
1034
+
1035
+ **Raised by:** [`refundRequest`](#coordinator-fn-refundrequest).
1036
+
1037
+ `refundRequest` on a request that is fulfilled, already refunded, or not yet past its deadline (the block timestamp must be greater than `deadline`).
1038
+
1039
+ **What to do:** Read `getRequest`: use the result if `fulfilled`, stop if `refunded`, otherwise retry after a block with a later timestamp than `deadline`.
1040
+
1041
+ #### <a id="coordinator-error-notrefunded"></a>`NotRefunded`
1042
+
1043
+ `error NotRefunded()` · Selector `0xfae7079c` · Source: `D20VRFCoordinator.sol` lines 131, 495
1044
+
1045
+ **Raised by:** [`retryRefundCallback`](#coordinator-fn-retryrefundcallback).
1046
+
1047
+ `retryRefundCallback` on a request that has not been refunded.
1048
+
1049
+ **What to do:** Call `refundRequest` after the deadline first.
1050
+
1051
+ #### <a id="coordinator-error-refundcallbackalreadydelivered"></a>`RefundCallbackAlreadyDelivered`
1052
+
1053
+ `error RefundCallbackAlreadyDelivered()` · Selector `0x6502f8ae` · Source: `D20VRFCoordinator.sol` lines 132, 496
1054
+
1055
+ **Raised by:** [`retryRefundCallback`](#coordinator-fn-retryrefundcallback).
1056
+
1057
+ `retryRefundCallback` after an `onRefund` notification already succeeded (`refundCallbackDelivered`).
1058
+
1059
+ **What to do:** Nothing to retry.
1060
+
1061
+ #### <a id="coordinator-error-insufficientcallbackgas"></a>`InsufficientCallbackGas`
1062
+
1063
+ `error InsufficientCallbackGas()` · Selector `0xa2c23f0d` · Source: `D20VRFCoordinator.sol` lines 122, 480, 505, 621–622
1064
+
1065
+ **Raised by:** [`refundRequest`](#coordinator-fn-refundrequest), [`retryCallback`](#coordinator-fn-retrycallback), [`retryRefundCallback`](#coordinator-fn-retryrefundcallback), [`fulfillRandomness`](#coordinator-fn-fulfillrandomness), [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch).
1066
+
1067
+ Too little gas remained to forward the full callback budget and keep the coordinator's reserve: `gasLimit + gasLimit/63 + 140,000` before a fulfillment callback, `100,000 + 100,000/63 + 140,000` after refund settlement, `gasLimit + gasLimit/63 + 50,000` before a refund notification. The coordinator reverts instead of forwarding less.
1068
+
1069
+ **What to do:** Raise the transaction gas limit: 400,000 for `refundRequest`, `gasLimit + 250,000` for `retryCallback`, `gasLimit + 150,000` for `retryRefundCallback` (README [Gas for refund and retry calls](README.md#gas-for-refund-and-retry-calls)). `eth_estimateGas` finds the minimum.
1070
+
1071
+ **Credits and withdrawals**
1072
+
1073
+ #### <a id="coordinator-error-norefundcredit"></a>`NoRefundCredit`
1074
+
1075
+ `error NoRefundCredit()` · Selector `0x1d59da8e` · Source: `D20VRFCoordinator.sol` lines 129, 519
1076
+
1077
+ **Raised by:** [`withdrawRefundCredit`](#coordinator-fn-withdrawrefundcredit).
1078
+
1079
+ `withdrawRefundCredit` from an address without refund credit. Credit is keyed by the refund address, which must be `msg.sender`.
1080
+
1081
+ **What to do:** Call from the refund address; `refundCredits(address)` shows the balance.
1082
+
1083
+ #### <a id="coordinator-error-transferfailed"></a>`TransferFailed`
1084
+
1085
+ `error TransferFailed()` · Selector `0x90b8ec18` · Source: `D20VRFCoordinator.sol` lines 124, 523, 534, 545
1086
+
1087
+ **Raised by:** [`withdrawRefundCredit`](#coordinator-fn-withdrawrefundcredit), [`withdrawFees`](#coordinator-fn-withdrawfees), [`withdrawKeeperCredit`](#coordinator-fn-withdrawkeepercredit).
1088
+
1089
+ The `recipient` of `withdrawRefundCredit`, `withdrawFees` or `withdrawKeeperCredit` rejected the native transfer. Balances are unchanged.
1090
+
1091
+ **What to do:** Choose a recipient that accepts plain native transfers.
1092
+
1093
+ #### <a id="coordinator-error-onlyfeerecipient"></a>`OnlyFeeRecipient`
1094
+
1095
+ `error OnlyFeeRecipient()` · Selector `0x07d8ed3d` · Source: `D20VRFCoordinator.sol` lines 123, 529
1096
+
1097
+ **Raised by:** [`withdrawFees`](#coordinator-fn-withdrawfees).
1098
+
1099
+ `withdrawFees` from an address other than `feeRecipient()`.
1100
+
1101
+ **What to do:** Only the fee recipient withdraws protocol fees.
1102
+
1103
+ #### <a id="coordinator-error-nokeepercredit"></a>`NoKeeperCredit`
1104
+
1105
+ `error NoKeeperCredit()` · Selector `0x0d106640` · Source: `D20VRFCoordinator.sol` lines 130, 541
1106
+
1107
+ **Raised by:** [`withdrawKeeperCredit`](#coordinator-fn-withdrawkeepercredit).
1108
+
1109
+ `withdrawKeeperCredit` from an address without keeper credit.
1110
+
1111
+ **What to do:** `keeperCredits(address)` shows the balance.
1112
+
1113
+ **Proofs and keepers**
1114
+
1115
+ #### <a id="coordinator-error-notready"></a>`NotReady`
1116
+
1117
+ `error NotReady()` · Selector `0x9488aaa6` · Source: `D20VRFCoordinator.sol` lines 115, 566
1118
+
1119
+ **Raised by:** [`fulfillRandomness`](#coordinator-fn-fulfillrandomness), [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch), [`storeBlockHash`](#coordinator-fn-storeblockhash), [`verifyRequestProof`](#coordinator-fn-verifyrequestproof), [`requestSeed`](#coordinator-fn-requestseed), [`getProofContext`](#coordinator-fn-getproofcontext).
1120
+
1121
+ The request cannot be proven yet: its epoch packet is not published, or `block.number` is below `targetBlock + confirmationBlocks`.
1122
+
1123
+ **What to do:** For a consumer this only means the request is still waiting. Keepers retry after publication and confirmations.
1124
+
1125
+ #### <a id="coordinator-error-blockhashunavailable"></a>`BlockHashUnavailable`
1126
+
1127
+ `error BlockHashUnavailable()` · Selector `0xbfc9f0d3` · Source: `D20VRFCoordinator.sol` lines 116, 569
1128
+
1129
+ **Raised by:** [`fulfillRandomness`](#coordinator-fn-fulfillrandomness), [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch), [`storeBlockHash`](#coordinator-fn-storeblockhash), [`verifyRequestProof`](#coordinator-fn-verifyrequestproof), [`requestSeed`](#coordinator-fn-requestseed), [`getProofContext`](#coordinator-fn-getproofcontext).
1130
+
1131
+ The target block hash was never stored and is outside the 256-block `BLOCKHASH` window. The request can no longer be fulfilled.
1132
+
1133
+ **What to do:** Call `refundRequest` after the deadline. Keepers call `storeBlockHash` before the window closes.
1134
+
1135
+ #### <a id="coordinator-error-alreadyfulfilled"></a>`AlreadyFulfilled`
1136
+
1137
+ `error AlreadyFulfilled()` · Selector `0x4a4117f9` · Source: `D20VRFCoordinator.sol` lines 117, 393
1138
+
1139
+ **Raised by:** [`fulfillRandomness`](#coordinator-fn-fulfillrandomness).
1140
+
1141
+ `fulfillRandomness` on a fulfilled request.
1142
+
1143
+ **What to do:** Nothing to do; read the result.
1144
+
1145
+ #### <a id="coordinator-error-requestrefunded"></a>`RequestRefunded`
1146
+
1147
+ `error RequestRefunded()` · Selector `0xe0dec416` · Source: `D20VRFCoordinator.sol` lines 127, 394
1148
+
1149
+ **Raised by:** [`fulfillRandomness`](#coordinator-fn-fulfillrandomness).
1150
+
1151
+ `fulfillRandomness` on a refunded request.
1152
+
1153
+ **What to do:** The request is settled and will never have a result.
1154
+
1155
+ #### <a id="coordinator-error-requestexpired"></a>`RequestExpired`
1156
+
1157
+ `error RequestExpired()` · Selector `0xfef01cd2` · Source: `D20VRFCoordinator.sol` lines 126, 395
1158
+
1159
+ **Raised by:** [`fulfillRandomness`](#coordinator-fn-fulfillrandomness).
1160
+
1161
+ `fulfillRandomness` in a block whose timestamp is after the request's deadline.
1162
+
1163
+ **What to do:** The request can only be refunded with `refundRequest`.
1164
+
1165
+ #### <a id="coordinator-error-wrongpublickey"></a>`WrongPublicKey`
1166
+
1167
+ `error WrongPublicKey()` · Selector `0x2b0bb68e` · Source: `D20VRFCoordinator.sol` lines 120, 594
1168
+
1169
+ **Raised by:** [`fulfillRandomness`](#coordinator-fn-fulfillrandomness), [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch), [`verifyRequestProof`](#coordinator-fn-verifyrequestproof).
1170
+
1171
+ The proof's `pk` is not the coordinator's VRF key.
1172
+
1173
+ **What to do:** Only proofs from the configured key are accepted.
1174
+
1175
+ #### <a id="coordinator-error-wrongseed"></a>`WrongSeed`
1176
+
1177
+ `error WrongSeed()` · Selector `0xf36cbea4` · Source: `D20VRFCoordinator.sol` lines 121, 596
1178
+
1179
+ **Raised by:** [`fulfillRandomness`](#coordinator-fn-fulfillrandomness), [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch), [`verifyRequestProof`](#coordinator-fn-verifyrequestproof).
1180
+
1181
+ The proof's `seed` differs from `requestSeed(requestId)`.
1182
+
1183
+ **What to do:** Prove the stored seed; it cannot change.
1184
+
1185
+ #### <a id="coordinator-error-evidencepackettoolarge"></a>`EvidencePacketTooLarge`
1186
+
1187
+ `error EvidencePacketTooLarge()` · Selector `0xcfbc3ebf` · Source: `D20VRFCoordinator.sol` lines 134, 452
1188
+
1189
+ **Raised by:** [`fulfillRandomness`](#coordinator-fn-fulfillrandomness), [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch).
1190
+
1191
+ The encoded proof exceeds `MAX_EVIDENCE_PACKET_BYTES`. A proof always encodes to 416 bytes, so valid calls never reach this bound.
1192
+
1193
+ **What to do:** None expected.
1194
+
1195
+ #### <a id="coordinator-error-invalidbatch"></a>`InvalidBatch`
1196
+
1197
+ `error InvalidBatch()` · Selector `0x33b094a1` · Source: `D20VRFCoordinator.sol` lines 137, 404
1198
+
1199
+ **Raised by:** [`fulfillRandomnessBatch`](#coordinator-fn-fulfillrandomnessbatch).
1200
+
1201
+ `fulfillRandomnessBatch` with no IDs, more than 16, or a different number of proofs.
1202
+
1203
+ **What to do:** Send 1 to 16 IDs with one proof each.
1204
+
1205
+ #### <a id="coordinator-error-invalidscan"></a>`InvalidScan`
1206
+
1207
+ `error InvalidScan()` · Selector `0x3e6249a2` · Source: `D20VRFCoordinator.sol` lines 133, 316
1208
+
1209
+ **Raised by:** [`getPendingRequestIds`](#coordinator-fn-getpendingrequestids).
1210
+
1211
+ `getPendingRequestIds` with `fromId` 0, `limit` 0 or `limit` above 256.
1212
+
1213
+ **What to do:** Start at 1 and page with at most 256.
1214
+
1215
+ **Administration, initialization and upgrades**
1216
+
1217
+ #### <a id="coordinator-error-invalidconfig"></a>`InvalidConfig`
1218
+
1219
+ `error InvalidConfig()` · Selector `0x35be3ac8` · Source: `D20VRFCoordinator.sol` lines 108, 174–175, 198, 202, 207, 216, 530, 539
1220
+
1221
+ **Raised by:** [`withdrawFees`](#coordinator-fn-withdrawfees), [`withdrawKeeperCredit`](#coordinator-fn-withdrawkeepercredit), [`setPricing`](#coordinator-fn-setpricing), [`setRefundBps`](#coordinator-fn-setrefundbps), [`setKeeperFeeBps`](#coordinator-fn-setkeeperfeebps), [`setFeeRecipient`](#coordinator-fn-setfeerecipient), [`initialize`](#coordinator-fn-initialize).
1222
+
1223
+ A value is out of bounds: in `initialize` (zero fee recipient, confirmations 0 or above 64, keeper share above 10000, minimum fee above 10 USDC, registry without code), `setFeeRecipient` with zero, `setKeeperFeeBps` above 10000, `setPricing` outside its bounds, `setRefundBps` outside 5000 to 10000, or a zero `recipient` for `withdrawFees` or `withdrawKeeperCredit`.
1224
+
1225
+ **What to do:** Use values within the bounds given for each function.
1226
+
1227
+ #### <a id="coordinator-error-invalidpublickey"></a>`InvalidPublicKey`
1228
+
1229
+ `error InvalidPublicKey()` · Selector `0xa2d0fee8` · Source: `D20VRFCoordinator.sol` lines 110, 177
1230
+
1231
+ **Raised by:** [`initialize`](#coordinator-fn-initialize).
1232
+
1233
+ `initialize` with a VRF public key that is not on secp256k1.
1234
+
1235
+ **What to do:** Deployment-time only.
1236
+
1237
+ #### <a id="coordinator-error-reentrancyguardreentrantcall"></a>`ReentrancyGuardReentrantCall`
1238
+
1239
+ `error ReentrancyGuardReentrantCall()` · Selector `0x3ee5aeb5`
1240
+
1241
+ **Raised by:** any `nonReentrant` function entered from a callback or transfer.
1242
+
1243
+ A `nonReentrant` coordinator function (a request, `storeBlockHash`, a fulfillment, a retry, `refundRequest` or a withdrawal) was entered while another was running, for example a request made inside `rawFulfillRandomness` or `onRefund`. Inside a callback the coordinator catches it and records the callback as failed.
1244
+
1245
+ **What to do:** Do not call coordinator state-changing functions from callbacks; request again in a separate transaction.
1246
+
1247
+ #### <a id="coordinator-error-ownableunauthorizedaccount"></a>`OwnableUnauthorizedAccount`
1248
+
1249
+ `error OwnableUnauthorizedAccount(address account)` · Selector `0x118cdaa7`
1250
+
1251
+ **Raised by:** [`setPricing`](#coordinator-fn-setpricing), [`setRefundBps`](#coordinator-fn-setrefundbps), [`setKeeperFeeBps`](#coordinator-fn-setkeeperfeebps), [`setFeeRecipient`](#coordinator-fn-setfeerecipient), [`transferOwnership`](#coordinator-fn-transferownership), [`acceptOwnership`](#coordinator-fn-acceptownership), [`renounceOwnership`](#coordinator-fn-renounceownership), [`upgradeToAndCall`](#coordinator-fn-upgradetoandcall).
1252
+
1253
+ `account` is not the owner (owner-only functions) or not the pending owner (`acceptOwnership`).
1254
+
1255
+ **What to do:** Only the owner can administer or upgrade the contract; on Arc Mainnet that is the DAO treasury Safe recorded in the deployment manifest.
1256
+
1257
+ #### <a id="coordinator-error-ownableinvalidowner"></a>`OwnableInvalidOwner`
1258
+
1259
+ `error OwnableInvalidOwner(address owner)` · Selector `0x1e4fbdf7`
1260
+
1261
+ **Raised by:** [`initialize`](#coordinator-fn-initialize).
1262
+
1263
+ `initialize` was given the zero address as owner.
1264
+
1265
+ **What to do:** Deployment-time only.
1266
+
1267
+ #### <a id="coordinator-error-renouncedisabled"></a>`RenounceDisabled`
1268
+
1269
+ `error RenounceDisabled()` · Selector `0x89051165` · Source: `D20VRFCoordinator.sol` lines 135, 195
1270
+
1271
+ **Raised by:** [`renounceOwnership`](#coordinator-fn-renounceownership).
1272
+
1273
+ The owner called `renounceOwnership`, which is disabled.
1274
+
1275
+ **What to do:** Move ownership with `transferOwnership` and `acceptOwnership`.
1276
+
1277
+ #### <a id="coordinator-error-invalidinitialization"></a>`InvalidInitialization`
1278
+
1279
+ `error InvalidInitialization()` · Selector `0xf92ee8a9`
1280
+
1281
+ **Raised by:** [`initialize`](#coordinator-fn-initialize).
1282
+
1283
+ `initialize` on a proxy that is already initialized, or on an implementation contract, whose initializers are disabled at construction.
1284
+
1285
+ **What to do:** None: initialization happens once, atomically, when `D20Proxy` is deployed.
1286
+
1287
+ #### <a id="coordinator-error-notinitializing"></a>`NotInitializing`
1288
+
1289
+ `error NotInitializing()` · Selector `0xd7e6bcf8`
1290
+
1291
+ **Raised by:** no public function (declared by OpenZeppelin `Initializable`).
1292
+
1293
+ Declared by the OpenZeppelin initializer helpers. No public function of this contract can reach it.
1294
+
1295
+ **What to do:** None.
1296
+
1297
+ #### <a id="coordinator-error-uupsunauthorizedcallcontext"></a>`UUPSUnauthorizedCallContext`
1298
+
1299
+ `error UUPSUnauthorizedCallContext()` · Selector `0xe07c8dba`
1300
+
1301
+ **Raised by:** [`upgradeToAndCall`](#coordinator-fn-upgradetoandcall), [`proxiableUUID`](#coordinator-fn-proxiableuuid).
1302
+
1303
+ `upgradeToAndCall` called on the implementation instead of through the proxy, or `proxiableUUID` called through the proxy.
1304
+
1305
+ **What to do:** Owner upgrade procedure only.
1306
+
1307
+ #### <a id="coordinator-error-uupsunsupportedproxiableuuid"></a>`UUPSUnsupportedProxiableUUID`
1308
+
1309
+ `error UUPSUnsupportedProxiableUUID(bytes32 slot)` · Selector `0xaa1d49a4`
1310
+
1311
+ **Raised by:** [`upgradeToAndCall`](#coordinator-fn-upgradetoandcall).
1312
+
1313
+ The new implementation reports a `proxiableUUID` other than the ERC-1967 implementation slot.
1314
+
1315
+ **What to do:** Owner upgrade procedure only.
1316
+
1317
+ #### <a id="coordinator-error-erc1967invalidimplementation"></a>`ERC1967InvalidImplementation`
1318
+
1319
+ `error ERC1967InvalidImplementation(address implementation)` · Selector `0x4c9c8ce3`
1320
+
1321
+ **Raised by:** [`upgradeToAndCall`](#coordinator-fn-upgradetoandcall).
1322
+
1323
+ The new implementation has no code or no `proxiableUUID`.
1324
+
1325
+ **What to do:** Owner upgrade procedure only.
1326
+
1327
+ #### <a id="coordinator-error-erc1967nonpayable"></a>`ERC1967NonPayable`
1328
+
1329
+ `error ERC1967NonPayable()` · Selector `0xb398979f`
1330
+
1331
+ **Raised by:** [`upgradeToAndCall`](#coordinator-fn-upgradetoandcall).
1332
+
1333
+ `upgradeToAndCall` was sent value with empty `data`.
1334
+
1335
+ **What to do:** Owner upgrade procedure only.
1336
+
1337
+ #### <a id="coordinator-error-addressemptycode"></a>`AddressEmptyCode`
1338
+
1339
+ `error AddressEmptyCode(address target)` · Selector `0x9996b315`
1340
+
1341
+ **Raised by:** [`upgradeToAndCall`](#coordinator-fn-upgradetoandcall).
1342
+
1343
+ Declared by OpenZeppelin `Address` for the delegatecall in `upgradeToAndCall`. Not reachable in practice, because the new implementation must already have code.
1344
+
1345
+ **What to do:** None.
1346
+
1347
+ #### <a id="coordinator-error-failedcall"></a>`FailedCall`
1348
+
1349
+ `error FailedCall()` · Selector `0xd6bda275`
1350
+
1351
+ **Raised by:** [`upgradeToAndCall`](#coordinator-fn-upgradetoandcall).
1352
+
1353
+ The initialization call made by `upgradeToAndCall` reverted without revert data.
1354
+
1355
+ **What to do:** Owner upgrade procedure only.
1356
+
1357
+ ## <a id="registry"></a>EpochEntropy
1358
+
1359
+ The epoch registry. A consumer never needs to call it: a request fixes its epoch automatically, and the coordinator reads the registry for targets and for the keeper-share recipient. The first group and the events matter to consumers and verifiers (README [Replay and verification](README.md#replay-and-verification)); publication and administration are listed for completeness.
1360
+
1361
+ Epoch sources are recipes in an owner-managed, append-only registry: each has an id, a canonical request whose `keccak256` is the query hash its signer signs, a data template that fixes the exact signed bytes the registry accepts (README [Data templates](README.md#data-templates)) and the JSON body keepers post to the provider gateway. A catalog lists 1 to `MAX_SOURCES` registered recipes with one signer each; each epoch selects from the catalog in force for it (`catalogAt`).
1362
+
1363
+ ### <a id="registry-types"></a>Types
1364
+
1365
+ #### <a id="registry-type-epochentropy-epoch"></a>`EpochEntropy.Epoch`
1366
+
1367
+ Returned by `getEpoch`; all zero until the epoch is published.
1368
+
1369
+ Source: `EpochEntropy.sol` lines 45–48
1370
+
1371
+ | Field | Type | Meaning |
1372
+ | --- | --- | --- |
1373
+ | `epochHash` | `bytes32` | Epoch commitment: `keccak256(abi.encode(EPOCH_DOMAIN, chainId, registry, catalogHash, epochId, epochStart, anchorHash, source, queryHash, dataHash, attestationHash))`. |
1374
+ | `catalogHash` | `bytes32` | Hash of the catalog in force for the epoch (`catalogAt`) at publication. |
1375
+ | `anchorHash` | `bytes32` | Hash of block `epochStart - 1`, which selects the source. |
1376
+ | `source` | `uint8` | Committed slot in the epoch's catalog, 0 to `sourceCountAt(epochId) - 1`: the selected slot or a fallback. The recipe is the catalog's recipe at that slot. |
1377
+ | `queryHash` | `bytes32` | `keccak256` of the canonical request of the slot's recipe. |
1378
+ | `dataHash` | `bytes32` | `keccak256` of the signed response data. |
1379
+ | `attestationHash` | `bytes32` | `keccak256(abi.encode(queryHash, timestamp, dataHash, keccak256(signature)))`. |
1380
+ | `signedAt` | `uint256` | Attestation timestamp in Unix seconds. |
1381
+ | `committedBlock` | `uint64` | Publication block. Requests of the epoch target `max(requestBlock, committedBlock + 1)`. |
1382
+
1383
+ #### <a id="registry-type-epochentropy-selection"></a>`EpochEntropy.Selection`
1384
+
1385
+ Returned by `getEpochSelection` and `getEpochFallbackSelection`.
1386
+
1387
+ Source: `EpochEntropy.sol` lines 43–44, 262–273
1388
+
1389
+ | Field | Type | Meaning |
1390
+ | --- | --- | --- |
1391
+ | `source` | `uint8` | Slot in the epoch's catalog. |
1392
+ | `recipe` | `uint8` | Registered recipe id at that slot. |
1393
+ | `airnode` | `address` | Signer of that slot in the catalog in force for the epoch. |
1394
+ | `selector` | `bytes32` | `keccak256(abi.encode(SELECT_DOMAIN, catalogHash, epochId, anchor))`; the slot is `(selector mod count + attempt) mod count` with `count = sourceCountAt(epochId)`. |
1395
+ | `queryHash` | `bytes32` | `keccak256` of `canonicalRequest`. |
1396
+ | `canonicalRequest` | `string` | Canonical request of the recipe, as `getRecipe` returns it. |
1397
+
1398
+ #### <a id="registry-type-epochentropy-attestation"></a>`EpochEntropy.Attestation`
1399
+
1400
+ Signed source response passed to `commitEpoch` and `commitEpochFallback`.
1401
+
1402
+ Source: `EpochEntropy.sol` lines 42, 280–300
1403
+
1404
+ | Field | Type | Meaning |
1405
+ | --- | --- | --- |
1406
+ | `timestamp` | `uint256` | Signing time in Unix seconds; not in the future and at most `MAX_ATTESTATION_AGE` old at publication. |
1407
+ | `data` | `bytes` | Signed response bytes, at most `MAX_DATA_BYTES` (128), matching the data template of the slot's recipe exactly. |
1408
+ | `signature` | `bytes` | Signature over `toEthSignedMessageHash(keccak256(abi.encodePacked(queryHash, timestamp, data)))`. |
1409
+
1410
+ ### <a id="registry-epoch-state-for-consumers-and-verifiers"></a>Epoch state for consumers and verifiers
1411
+
1412
+ Views, callable by anyone. Epoch IDs start at 1; each epoch lasts `EPOCH_LENGTH` (200) blocks.
1413
+
1414
+ #### <a id="registry-fn-epochforblock"></a>`epochForBlock`
1415
+
1416
+ ```solidity
1417
+ function epochForBlock(uint256 number) external view returns (uint64)
1418
+ ```
1419
+
1420
+ Selector `0x7018ebb1` · Caller: Anyone (view) · Source: `EpochEntropy.sol` lines 235–237
1421
+
1422
+ Epoch containing a block: 0 before `firstEpochStart`, otherwise `1 + (number - firstEpochStart) / 200`. A request belongs to `epochForBlock(requestBlock)`.
1423
+
1424
+ #### <a id="registry-fn-epochstart"></a>`epochStart`
1425
+
1426
+ ```solidity
1427
+ function epochStart(uint64 epochId) external view returns (uint64)
1428
+ ```
1429
+
1430
+ Selector `0xa1587509` · Caller: Anyone (view) · Source: `EpochEntropy.sol` lines 231–234
1431
+
1432
+ First block of an epoch: `firstEpochStart + (epochId - 1) × 200`.
1433
+
1434
+ **Errors:** [`InvalidEpoch`](#registry-error-invalidepoch).
1435
+
1436
+ #### <a id="registry-fn-getepoch"></a>`getEpoch`
1437
+
1438
+ ```solidity
1439
+ function getEpoch(uint64 epochId) external view returns (EpochEntropy.Epoch)
1440
+ ```
1441
+
1442
+ Selector `0x12a02c82` · Caller: Anyone (view) · Source: `EpochEntropy.sol` line 252
1443
+
1444
+ The published [`EpochEntropy.Epoch`](#registry-type-epochentropy-epoch) record, or all zero while unpublished; it never reverts. A non-zero `epochHash` means published.
1445
+
1446
+ #### <a id="registry-fn-catalogat"></a>`catalogAt`
1447
+
1448
+ ```solidity
1449
+ function catalogAt(uint64 epochId) external view returns (bytes32 hash, uint8[] recipes, address[] signers)
1450
+ ```
1451
+
1452
+ Selector `0xec993599` · Caller: Anyone (view) · Source: `EpochEntropy.sol` lines 212–220, 226–230
1453
+
1454
+ The catalog in force for an epoch: its hash and the recipe id and signer of each slot, in slot order. That is the latest scheduled version whose `fromEpoch` is at or below `epochId`, otherwise the initial catalog: recipes 0 to 3 with the initial signers and hash `catalogHash()`. Replay needs this catalog, not the initial signer getters.
1455
+
1456
+ #### <a id="registry-fn-sourcecountat"></a>`sourceCountAt`
1457
+
1458
+ ```solidity
1459
+ function sourceCountAt(uint64 epochId) external view returns (uint256)
1460
+ ```
1461
+
1462
+ Selector `0x3edc6b12` · Caller: Anyone (view) · Source: `EpochEntropy.sol` lines 221–225, 226–230
1463
+
1464
+ Number of slots in the catalog in force for an epoch, and so the number of selection attempts, 0 to count - 1.
1465
+
1466
+ #### <a id="registry-fn-getrecipe"></a>`getRecipe`
1467
+
1468
+ ```solidity
1469
+ function getRecipe(uint8 recipe) external view returns (bytes32 queryHash, string canonicalRequest, bytes template, string body)
1470
+ ```
1471
+
1472
+ Selector `0xba01b103` · Caller: Anyone (view) · Source: `EpochEntropy.sol` lines 132–136, 139–142
1473
+
1474
+ A registered recipe: `queryHash` (`keccak256` of `canonicalRequest`), the canonical request its signer signs, the data template its signed data must match and the JSON body keepers post to the provider gateway. Registered recipes never change. `readEpochRecipes` in `@d20dao/vrf-sdk/epoch` reads recipes with this view and checks each query hash.
1475
+
1476
+ **Errors:** [`InvalidConfig`](#registry-error-invalidconfig).
1477
+
1478
+ #### <a id="registry-fn-reciperequest"></a>`recipeRequest`
1479
+
1480
+ ```solidity
1481
+ function recipeRequest(uint8 recipe) external view returns (string)
1482
+ ```
1483
+
1484
+ Selector `0x7ce4b6e0` · Caller: Anyone (view) · Source: `EpochEntropy.sol` lines 137–142
1485
+
1486
+ Canonical request of a registered recipe, the same string `getRecipe` returns.
1487
+
1488
+ **Errors:** [`InvalidConfig`](#registry-error-invalidconfig).
1489
+
1490
+ #### <a id="registry-fn-recipecount"></a>`recipeCount`
1491
+
1492
+ ```solidity
1493
+ function recipeCount() external view returns (uint256)
1494
+ ```
1495
+
1496
+ Selector `0x69cfdf74` · Caller: Anyone (view) · Source: `EpochEntropy.sol` line 131
1497
+
1498
+ Number of registered recipes; ids run from 0 to `recipeCount() - 1`.
1499
+
1500
+ ### <a id="registry-registry-reads"></a>Registry reads
1501
+
1502
+ Views, callable by anyone.
1503
+
1504
+ | Function | Selector | Meaning | Source |
1505
+ | --- | --- | --- | --- |
1506
+ | <a id="registry-fn-firstepochstart"></a>`firstEpochStart() returns (uint64)` | `0x219f2428` | First block of epoch 1: the initialization block plus 200. | lines 40, 83 |
1507
+ | <a id="registry-fn-committer"></a>`committer() returns (address)` | `0x5bc8e8f9` | Primary publishing address. The coordinator pays it the keeper share of the requests it serves itself and of every request whose proof came from a wallet this registry does not authorize. | lines 39, 281 |
1508
+ | <a id="registry-fn-isbackupcommitter"></a>`isBackupCommitter(address account) returns (bool)` | `0x1d97e417` | Whether an address may publish epochs besides `committer()`. | line 118 |
1509
+ | <a id="registry-fn-isauthorizedcommitter"></a>`isAuthorizedCommitter(address account) returns (bool)` | `0x1579ab83` | Whether an address may publish epochs at all: `committer()` or an allowed backup committer. The coordinator reads it to decide whether a proof submitter earns the keeper share. | lines 119–121 |
1510
+ | <a id="registry-fn-backupcommittercount"></a>`backupCommitterCount() returns (uint256)` | `0xa815c5bf` | Number of allowed backup committers, at most `MAX_BACKUP_COMMITTERS`. | lines 63, 107–117 |
1511
+ | <a id="registry-fn-cataloghash"></a>`catalogHash() returns (bytes32)` | `0x830c083a` | Initial catalog hash, bound into `protocolConfigurationHash`. Never changes; `catalogAt` gives the catalog of an epoch. | lines 41, 84 |
1512
+ | <a id="registry-fn-epochanchors"></a>`epochAnchors(uint64) returns (bytes32)` | `0x48a030fb` | Checkpointed anchor of an epoch (hash of block `epochStart - 1`); zero until a request, `checkpointEpoch` or publication stores it. | lines 53, 241–244 |
1513
+ | <a id="registry-fn-hyperliquidsigner"></a>`hyperliquidSigner() returns (address)` | `0xf2a12563` | Initial-catalog signer of slot 0 (recipe 0, Hyperliquid BTC volume). Never changes; see `catalogAt`. | line 34 |
1514
+ | <a id="registry-fn-ethereumblocksigner"></a>`ethereumBlockSigner() returns (address)` | `0xd25eacfc` | Initial-catalog signer of slot 1 (recipe 1, Ethereum block hash). Never changes; see `catalogAt`. | lines 35–36 |
1515
+ | <a id="registry-fn-btctradesigner"></a>`btcTradeSigner() returns (address)` | `0xb3b9cbb0` | Initial-catalog signer of slot 2 (recipe 2, TickerLayer BTCUSD). Never changes; see `catalogAt`. | line 37 |
1516
+ | <a id="registry-fn-ethtradesigner"></a>`ethTradeSigner() returns (address)` | `0x3a700168` | Initial-catalog signer of slot 3 (recipe 3, TickerLayer ETHUSD). Never changes; see `catalogAt`. | line 38 |
1517
+
1518
+ ### <a id="registry-source-selection-and-publication"></a>Source selection and publication
1519
+
1520
+ Used by keepers. Publication is restricted to the committer and backup committers; the selection views and `checkpointEpoch` are open to anyone.
1521
+
1522
+ #### <a id="registry-fn-getepochselection"></a>`getEpochSelection`
1523
+
1524
+ ```solidity
1525
+ function getEpochSelection(uint64 epochId) external view returns (EpochEntropy.Selection s)
1526
+ ```
1527
+
1528
+ Selector `0xec4960ad` · Caller: Anyone (view) · Source: `EpochEntropy.sol` lines 253, 262–273
1529
+
1530
+ The selected source of an epoch, attempt 0, as an [`EpochEntropy.Selection`](#registry-type-epochentropy-selection).
1531
+
1532
+ **Errors:** [`InvalidEpoch`](#registry-error-invalidepoch), [`PreparationClosed`](#registry-error-preparationclosed), [`AnchorUnavailable`](#registry-error-anchorunavailable), [`InvalidConfig`](#registry-error-invalidconfig).
1533
+
1534
+ #### <a id="registry-fn-getepochfallbackselection"></a>`getEpochFallbackSelection`
1535
+
1536
+ ```solidity
1537
+ function getEpochFallbackSelection(uint64 epochId, uint8 attempt) external view returns (EpochEntropy.Selection s)
1538
+ ```
1539
+
1540
+ Selector `0x0e5a0e02` · Caller: Anyone (view) · Source: `EpochEntropy.sol` lines 254–255, 262–273
1541
+
1542
+ The source for attempt 0 to `sourceCountAt(epochId) - 1`; attempt n uses the slot n positions after the selected one.
1543
+
1544
+ **Errors:** [`InvalidFallback`](#registry-error-invalidfallback), [`InvalidEpoch`](#registry-error-invalidepoch), [`PreparationClosed`](#registry-error-preparationclosed), [`AnchorUnavailable`](#registry-error-anchorunavailable), [`InvalidConfig`](#registry-error-invalidconfig).
1545
+
1546
+ #### <a id="registry-fn-fallbackopensat"></a>`fallbackOpensAt`
1547
+
1548
+ ```solidity
1549
+ function fallbackOpensAt(uint64 epochId, uint8 attempt) external view returns (uint64)
1550
+ ```
1551
+
1552
+ Selector `0x98208050` · Caller: Anyone (view) · Source: `EpochEntropy.sol` lines 256–260
1553
+
1554
+ First block at which an attempt may be published: `epochStart + attempt × FALLBACK_DELAY_BLOCKS` (20). With at most `MAX_SOURCES` (10) slots the last window opens 180 blocks into the epoch.
1555
+
1556
+ **Errors:** [`InvalidFallback`](#registry-error-invalidfallback), [`InvalidEpoch`](#registry-error-invalidepoch).
1557
+
1558
+ #### <a id="registry-fn-nextepochtoprepare"></a>`nextEpochToPrepare`
1559
+
1560
+ ```solidity
1561
+ function nextEpochToPrepare(uint256 number) external view returns (uint64)
1562
+ ```
1563
+
1564
+ Selector `0xc78fafe2` · Caller: Anyone (view) · Source: `EpochEntropy.sol` line 238
1565
+
1566
+ Same value as `epochForBlock(number)`.
1567
+
1568
+ #### <a id="registry-fn-checkpointepoch"></a>`checkpointEpoch`
1569
+
1570
+ ```solidity
1571
+ function checkpointEpoch(uint64 epochId) external returns (bytes32 anchor)
1572
+ ```
1573
+
1574
+ Selector `0x16de78cb` · Caller: Anyone · Source: `EpochEntropy.sol` lines 239–244, 245–251
1575
+
1576
+ Stores the anchor of a started epoch (hash of block `epochStart - 1`) if not stored yet, and returns it. The coordinator calls it on every request, so the anchor of an epoch with requests survives the 256-block `BLOCKHASH` window.
1577
+
1578
+ **Errors:** [`InvalidEpoch`](#registry-error-invalidepoch), [`PreparationClosed`](#registry-error-preparationclosed), [`AnchorUnavailable`](#registry-error-anchorunavailable).
1579
+
1580
+ #### <a id="registry-fn-commitepoch"></a>`commitEpoch`
1581
+
1582
+ ```solidity
1583
+ function commitEpoch(uint64 epochId, EpochEntropy.Attestation a) external
1584
+ ```
1585
+
1586
+ Selector `0xb1580277` · Caller: Committer or backup committer · Source: `EpochEntropy.sol` lines 274, 280–300
1587
+
1588
+ Publishes the packet of the selected source once per epoch, from the epoch start: checks that the attestation is not future-dated and at most 240 seconds old, that its data matches the data template of the slot's recipe exactly, and that the slot's signer in the epoch's catalog signed it. Stores the record and emits the packet. Publishing earns nothing by itself: the keeper share of each request goes to the authorized wallet that submits its accepted proof, or to `committer()` when the submitter is not authorized.
1589
+
1590
+ **Emits:** [`EpochCommitted`](#registry-event-epochcommitted).
1591
+
1592
+ **Errors:** [`OnlyCommitter`](#registry-error-onlycommitter), [`AlreadyCommitted`](#registry-error-alreadycommitted), [`InvalidEpoch`](#registry-error-invalidepoch), [`FallbackNotOpen`](#registry-error-fallbacknotopen), [`AnchorUnavailable`](#registry-error-anchorunavailable), [`InvalidConfig`](#registry-error-invalidconfig), [`InvalidTime`](#registry-error-invalidtime), [`InvalidData`](#registry-error-invaliddata), [`ECDSAInvalidSignatureLength`](#registry-error-ecdsainvalidsignaturelength), [`ECDSAInvalidSignatureS`](#registry-error-ecdsainvalidsignatures), [`ECDSAInvalidSignature`](#registry-error-ecdsainvalidsignature), [`InvalidSigner`](#registry-error-invalidsigner), [`PacketTooLarge`](#registry-error-packettoolarge).
1593
+
1594
+ #### <a id="registry-fn-commitepochfallback"></a>`commitEpochFallback`
1595
+
1596
+ ```solidity
1597
+ function commitEpochFallback(uint64 epochId, uint8 attempt, EpochEntropy.Attestation a) external
1598
+ ```
1599
+
1600
+ Selector `0x5768d9a1` · Caller: Committer or backup committer · Source: `EpochEntropy.sol` lines 275–279, 280–300
1601
+
1602
+ Publishes fallback attempt 1 to `sourceCountAt(epochId) - 1`, using the slot `attempt` positions after the selected source, once `fallbackOpensAt(epochId, attempt)` is reached. Same checks as `commitEpoch`.
1603
+
1604
+ **Emits:** [`EpochCommitted`](#registry-event-epochcommitted).
1605
+
1606
+ **Errors:** [`InvalidFallback`](#registry-error-invalidfallback), [`OnlyCommitter`](#registry-error-onlycommitter), [`AlreadyCommitted`](#registry-error-alreadycommitted), [`InvalidEpoch`](#registry-error-invalidepoch), [`FallbackNotOpen`](#registry-error-fallbacknotopen), [`AnchorUnavailable`](#registry-error-anchorunavailable), [`InvalidConfig`](#registry-error-invalidconfig), [`InvalidTime`](#registry-error-invalidtime), [`InvalidData`](#registry-error-invaliddata), [`ECDSAInvalidSignatureLength`](#registry-error-ecdsainvalidsignaturelength), [`ECDSAInvalidSignatureS`](#registry-error-ecdsainvalidsignatures), [`ECDSAInvalidSignature`](#registry-error-ecdsainvalidsignature), [`InvalidSigner`](#registry-error-invalidsigner), [`PacketTooLarge`](#registry-error-packettoolarge).
1607
+
1608
+ ### <a id="registry-recipes-and-catalogs"></a>Recipes and catalogs
1609
+
1610
+ Owner-only; on Arc Mainnet the owner is the DAO treasury Safe. A recipe or catalog never changes a published epoch.
1611
+
1612
+ #### <a id="registry-fn-registerrecipe"></a>`registerRecipe`
1613
+
1614
+ ```solidity
1615
+ function registerRecipe(string canonicalRequest, bytes template, string body) external returns (uint8 recipe)
1616
+ ```
1617
+
1618
+ Selector `0x5add5c50` · Caller: Owner · Source: `EpochEntropy.sol` lines 123–130, 143–152
1619
+
1620
+ Appends an immutable recipe and returns its id, the next index. The canonical request is 1 to `MAX_REQUEST_BYTES` (1024) bytes, the body 1 to `MAX_BODY_BYTES` (2048) bytes, and the template must be a well-formed data template of at most `MAX_TEMPLATE_BYTES` (256) bytes; at most `MAX_RECIPES` (256) recipes exist. The contract does not check that the body canonicalizes to the request; keepers refuse a recipe whose body does not. A changed listing is registered as a new id.
1621
+
1622
+ **Emits:** [`RecipeRegistered`](#registry-event-reciperegistered).
1623
+
1624
+ **Errors:** [`OwnableUnauthorizedAccount`](#registry-error-ownableunauthorizedaccount), [`InvalidRecipe`](#registry-error-invalidrecipe), [`InvalidTemplate`](#registry-error-invalidtemplate).
1625
+
1626
+ #### <a id="registry-fn-schedulecatalog"></a>`scheduleCatalog`
1627
+
1628
+ ```solidity
1629
+ function scheduleCatalog(uint8[] recipes, address[] signers, uint64 fromEpoch) external
1630
+ ```
1631
+
1632
+ Selector `0x42984450` · Caller: Owner · Source: `EpochEntropy.sol` lines 190–211
1633
+
1634
+ Schedules a catalog for epochs from `fromEpoch`, which must be at least two epochs after the current one: 1 to `MAX_SOURCES` (10) distinct registered recipe ids with one non-zero signer each, in slot order. Its hash is `keccak256(abi.encode(RECIPE_DOMAIN, recipes, signers))`. If the latest scheduled version has not taken effect yet (its `fromEpoch` is after the current epoch) it is replaced, so that version never applies; this can return the next epoch to the previous catalog. The current epoch keeps its catalog.
1635
+
1636
+ **Emits:** [`CatalogScheduled`](#registry-event-catalogscheduled).
1637
+
1638
+ **Errors:** [`OwnableUnauthorizedAccount`](#registry-error-ownableunauthorizedaccount), [`InvalidConfig`](#registry-error-invalidconfig), [`InvalidEpoch`](#registry-error-invalidepoch).
1639
+
1640
+ #### <a id="registry-fn-initializereciperegistry"></a>`initializeRecipeRegistry`
1641
+
1642
+ ```solidity
1643
+ function initializeRecipeRegistry() external
1644
+ ```
1645
+
1646
+ Selector `0x8700b456` · Caller: Owner, once per proxy, as the `upgradeToAndCall` data of the recipe-registry upgrade · Source: `EpochEntropy.sol` lines 87–95
1647
+
1648
+ Registers built-in recipes 0 to 5 on a registry initialized before the recipe registry, whose initial catalog selects recipes 0 to 3. It runs once per proxy (reinitializer version 2) and refuses a registry that already has recipes, which includes every registry initialized by this implementation, or a catalog scheduled under the earlier hardcoded recipe ids.
1649
+
1650
+ **Emits:** [`RecipeRegistered`](#registry-event-reciperegistered), [`Initialized`](#registry-event-initialized).
1651
+
1652
+ **Errors:** [`InvalidInitialization`](#registry-error-invalidinitialization), [`OwnableUnauthorizedAccount`](#registry-error-ownableunauthorizedaccount), [`InvalidConfig`](#registry-error-invalidconfig).
1653
+
1654
+ ### <a id="registry-owner-administration"></a>Owner administration
1655
+
1656
+ Owner-only functions revert `OwnableUnauthorizedAccount` for anyone else. No setter can change a published epoch.
1657
+
1658
+ #### <a id="registry-fn-setcommitter"></a>`setCommitter`
1659
+
1660
+ ```solidity
1661
+ function setCommitter(address next) external
1662
+ ```
1663
+
1664
+ Selector `0xdd51ce22` · Caller: Owner · Source: `EpochEntropy.sol` lines 100–103
1665
+
1666
+ Changes the primary publishing address, which is also the keeper-share recipient the coordinator reads at each acceptance.
1667
+
1668
+ **Emits:** [`CommitterChanged`](#registry-event-committerchanged).
1669
+
1670
+ **Errors:** [`OwnableUnauthorizedAccount`](#registry-error-ownableunauthorizedaccount), [`InvalidConfig`](#registry-error-invalidconfig).
1671
+
1672
+ #### <a id="registry-fn-setbackupcommitter"></a>`setBackupCommitter`
1673
+
1674
+ ```solidity
1675
+ function setBackupCommitter(address account, bool allowed) external
1676
+ ```
1677
+
1678
+ Selector `0xd870d0c6` · Caller: Owner · Source: `EpochEntropy.sol` lines 104–117
1679
+
1680
+ Allows or removes a backup committer: a separate wallet that may call `commitEpoch` and `commitEpochFallback` under exactly the committer's rules, for example a follower keeper that takes over while the primary keeper is down. It has no other role, and the coordinator pays it the keeper share of the requests whose accepted proofs it submits itself. Reverts for the zero address, for allowing the current committer, for a call that does not change the address's status, and for more than `MAX_BACKUP_COMMITTERS` (4).
1681
+
1682
+ **Emits:** [`BackupCommitterSet`](#registry-event-backupcommitterset).
1683
+
1684
+ **Errors:** [`OwnableUnauthorizedAccount`](#registry-error-ownableunauthorizedaccount), [`InvalidConfig`](#registry-error-invalidconfig).
1685
+
1686
+ #### <a id="registry-fn-owner"></a>`owner`
1687
+
1688
+ ```solidity
1689
+ function owner() external view returns (address)
1690
+ ```
1691
+
1692
+ Selector `0x8da5cb5b` · Caller: Anyone (view)
1693
+
1694
+ Current owner: upgrade authority and the only account that can call the setters.
1695
+
1696
+ #### <a id="registry-fn-pendingowner"></a>`pendingOwner`
1697
+
1698
+ ```solidity
1699
+ function pendingOwner() external view returns (address)
1700
+ ```
1701
+
1702
+ Selector `0xe30c3978` · Caller: Anyone (view)
1703
+
1704
+ Account nominated by `transferOwnership` that has not accepted yet; zero when none.
1705
+
1706
+ #### <a id="registry-fn-transferownership"></a>`transferOwnership`
1707
+
1708
+ ```solidity
1709
+ function transferOwnership(address newOwner) external
1710
+ ```
1711
+
1712
+ Selector `0xf2fde38b` · Caller: Owner
1713
+
1714
+ Starts a two-step transfer by nominating `newOwner`; ownership moves only when that account calls `acceptOwnership`. A new call replaces the nomination, and the zero address cancels it.
1715
+
1716
+ **Emits:** [`OwnershipTransferStarted`](#registry-event-ownershiptransferstarted).
1717
+
1718
+ **Errors:** [`OwnableUnauthorizedAccount`](#registry-error-ownableunauthorizedaccount).
1719
+
1720
+ #### <a id="registry-fn-acceptownership"></a>`acceptOwnership`
1721
+
1722
+ ```solidity
1723
+ function acceptOwnership() external
1724
+ ```
1725
+
1726
+ Selector `0x79ba5097` · Caller: Pending owner
1727
+
1728
+ Completes the transfer to the caller and clears the nomination.
1729
+
1730
+ **Emits:** [`OwnershipTransferred`](#registry-event-ownershiptransferred).
1731
+
1732
+ **Errors:** [`OwnableUnauthorizedAccount`](#registry-error-ownableunauthorizedaccount).
1733
+
1734
+ #### <a id="registry-fn-renounceownership"></a>`renounceOwnership`
1735
+
1736
+ ```solidity
1737
+ function renounceOwnership() external view
1738
+ ```
1739
+
1740
+ Selector `0x715018a6` · Caller: Owner · Source: `EpochEntropy.sol` lines 97–98
1741
+
1742
+ Disabled and declared `view`: the owner gets `RenounceDisabled` and anyone else `OwnableUnauthorizedAccount`, so the contract always has an owner and upgrade authority can only move through an accepted transfer.
1743
+
1744
+ **Errors:** [`OwnableUnauthorizedAccount`](#registry-error-ownableunauthorizedaccount), [`RenounceDisabled`](#registry-error-renouncedisabled).
1745
+
1746
+ #### <a id="registry-fn-upgradetoandcall"></a>`upgradeToAndCall`
1747
+
1748
+ ```solidity
1749
+ function upgradeToAndCall(address newImplementation, bytes data) external payable
1750
+ ```
1751
+
1752
+ Selector `0x4f1ef286` · Caller: Owner, through the proxy
1753
+
1754
+ UUPS upgrade: points the proxy at `newImplementation`, which must report the ERC-1967 slot from `proxiableUUID`, and delegatecalls `data` when it is non-empty. An upgrade can change any behavior described here. Integrators check the implementation when they integrate and again whenever a proxy emits `Upgraded` or the deployment manifest records an upgrade (README [Security and trust](README.md#security-and-trust)).
1755
+
1756
+ **Emits:** [`Upgraded`](#registry-event-upgraded).
1757
+
1758
+ **Errors:** [`UUPSUnauthorizedCallContext`](#registry-error-uupsunauthorizedcallcontext), [`OwnableUnauthorizedAccount`](#registry-error-ownableunauthorizedaccount), [`ERC1967InvalidImplementation`](#registry-error-erc1967invalidimplementation), [`UUPSUnsupportedProxiableUUID`](#registry-error-uupsunsupportedproxiableuuid), [`ERC1967NonPayable`](#registry-error-erc1967nonpayable), [`AddressEmptyCode`](#registry-error-addressemptycode), [`FailedCall`](#registry-error-failedcall).
1759
+
1760
+ #### <a id="registry-fn-proxiableuuid"></a>`proxiableUUID`
1761
+
1762
+ ```solidity
1763
+ function proxiableUUID() external view returns (bytes32)
1764
+ ```
1765
+
1766
+ Selector `0x52d1902d` · Caller: Anyone (view)
1767
+
1768
+ ERC-1822 check used by `upgradeToAndCall`. Returns the ERC-1967 implementation slot when called on an implementation contract directly and reverts through the proxy.
1769
+
1770
+ **Errors:** [`UUPSUnauthorizedCallContext`](#registry-error-uupsunauthorizedcallcontext).
1771
+
1772
+ #### <a id="registry-fn-initialize"></a>`initialize`
1773
+
1774
+ ```solidity
1775
+ function initialize(address[4] signers, address initialOwner, address initialCommitter) external
1776
+ ```
1777
+
1778
+ Selector `0xfda9f5ca` · Caller: Once, by `D20Proxy` at deployment · Source: `EpochEntropy.sol` lines 78–86
1779
+
1780
+ Sets the four initial-catalog signers of recipes 0 to 3, the owner and the committer, and registers built-in recipes 0 to 5. Epoch 1 starts 200 blocks after the initialization block.
1781
+
1782
+ **Emits:** [`OwnershipTransferred`](#registry-event-ownershiptransferred), [`RecipeRegistered`](#registry-event-reciperegistered), [`Initialized`](#registry-event-initialized).
1783
+
1784
+ **Errors:** [`InvalidInitialization`](#registry-error-invalidinitialization), [`OwnableInvalidOwner`](#registry-error-ownableinvalidowner), [`InvalidConfig`](#registry-error-invalidconfig).
1785
+
1786
+ ### <a id="registry-constants"></a>Constants
1787
+
1788
+ Views returning values fixed in the implementation code.
1789
+
1790
+ | Constant | Returns | Value | Selector | Meaning |
1791
+ | --- | --- | --- | --- | --- |
1792
+ | <a id="registry-fn-epoch_length"></a>`EPOCH_LENGTH` | `uint64` | `200` | `0xac4746ab` | Blocks per epoch. |
1793
+ | <a id="registry-fn-max_attestation_age"></a>`MAX_ATTESTATION_AGE` | `uint256` | `240 seconds` | `0xb9f7bb8d` | Oldest attestation accepted at publication, in seconds; also exported by `@d20dao/vrf-sdk/epoch`. |
1794
+ | <a id="registry-fn-max_packet_bytes"></a>`MAX_PACKET_BYTES` | `uint256` | `2048` | `0x48cad11b` | Upper bound on the `EpochCommitted` packet. |
1795
+ | <a id="registry-fn-fallback_delay_blocks"></a>`FALLBACK_DELAY_BLOCKS` | `uint64` | `20` | `0x0a48a95d` | Blocks between fallback windows. |
1796
+ | <a id="registry-fn-max_sources"></a>`MAX_SOURCES` | `uint256` | `10` | `0x64aefc06` | Most slots in a catalog. |
1797
+ | <a id="registry-fn-max_recipes"></a>`MAX_RECIPES` | `uint256` | `256` | `0x0c148333` | Most registered recipes; ids are `uint8`. |
1798
+ | <a id="registry-fn-max_request_bytes"></a>`MAX_REQUEST_BYTES` | `uint256` | `1024` | `0x4b62e5b4` | Longest canonical request of a recipe. |
1799
+ | <a id="registry-fn-max_body_bytes"></a>`MAX_BODY_BYTES` | `uint256` | `2048` | `0x2ade18c2` | Longest gateway request body of a recipe. |
1800
+ | <a id="registry-fn-max_data_bytes"></a>`MAX_DATA_BYTES` | `uint256` | `128` | `0xf1d11a6c` | Longest signed data a template accepts. |
1801
+ | <a id="registry-fn-max_template_bytes"></a>`MAX_TEMPLATE_BYTES` | `uint256` | `256` | `0x30a3bee5` | Longest data template. |
1802
+ | <a id="registry-fn-max_backup_committers"></a>`MAX_BACKUP_COMMITTERS` | `uint256` | `4` | `0xc195af66` | Most backup committers allowed at once. |
1803
+ | <a id="registry-fn-recipe_domain"></a>`RECIPE_DOMAIN` | `bytes32` | `keccak256("D20_EPOCH_RECIPES")` | `0xacea73c8` | Domain tag of catalog hashes. |
1804
+ | <a id="registry-fn-select_domain"></a>`SELECT_DOMAIN` | `bytes32` | `keccak256("D20_EPOCH_SELECT")` | `0x10181587` | Domain tag of the source selector. |
1805
+ | <a id="registry-fn-epoch_domain"></a>`EPOCH_DOMAIN` | `bytes32` | `keccak256("D20_EPOCH")` | `0xbece738c` | Domain tag of the epoch commitment. |
1806
+ | <a id="registry-fn-upgrade_interface_version"></a>`UPGRADE_INTERFACE_VERSION` | `string` | `"5.0.0"` | `0xad3cb1cc` | OpenZeppelin UUPS interface version: upgrades go through `upgradeToAndCall` only. |
1807
+
1808
+ ### <a id="registry-events"></a>Events
1809
+
1810
+ **Epochs, recipes and catalogs**
1811
+
1812
+ #### <a id="registry-event-epochcommitted"></a>`EpochCommitted`
1813
+
1814
+ ```solidity
1815
+ event EpochCommitted(uint64 indexed epochId, bytes32 indexed epochHash, bytes packet)
1816
+ ```
1817
+
1818
+ Topic 0 `0xc9db8d1389570196eda0f2c6c4e2f78429c2f812db30b6b1022b5e0b5162ef72` · Emitted by: [`commitEpoch`](#registry-fn-commitepoch), [`commitEpochFallback`](#registry-fn-commitepochfallback) · Source: `EpochEntropy.sol` lines 71, 297–299
1819
+
1820
+ An epoch was published. `packet` is `abi.encode(canonicalRequest, attestation)`: decode it with `decodeEpochEvidencePacket` and verify with `replayEpochCommitment`. Requests of the epoch now have a target block.
1821
+
1822
+ #### <a id="registry-event-reciperegistered"></a>`RecipeRegistered`
1823
+
1824
+ ```solidity
1825
+ event RecipeRegistered(uint8 indexed recipe, bytes32 indexed queryHash, string canonicalRequest, bytes template, string body)
1826
+ ```
1827
+
1828
+ Topic 0 `0xbc5c1e3f4647d7f37dc8b4fe5e26e7c58ae35b8c9fb1db0233ddedba0d7f5fd8` · Emitted by: [`registerRecipe`](#registry-fn-registerrecipe), [`initializeRecipeRegistry`](#registry-fn-initializereciperegistry), [`initialize`](#registry-fn-initialize) · Source: `EpochEntropy.sol` lines 74, 151
1829
+
1830
+ Recipe `recipe` was registered. The event carries the complete definition, so every recipe can be rebuilt from logs; `getRecipe` returns the same values.
1831
+
1832
+ #### <a id="registry-event-catalogscheduled"></a>`CatalogScheduled`
1833
+
1834
+ ```solidity
1835
+ event CatalogScheduled(uint64 indexed fromEpoch, bytes32 indexed catalogHash, uint8[] recipes, address[] signers)
1836
+ ```
1837
+
1838
+ Topic 0 `0xc91bcd562b1edaabdb2772ada76957511610c715ef892b9c9af1f35be93c3c4f` · Emitted by: [`scheduleCatalog`](#registry-fn-schedulecatalog) · Source: `EpochEntropy.sol` lines 73, 210
1839
+
1840
+ A catalog was scheduled for epochs from `fromEpoch`: recipe ids and signers in slot order. A later `CatalogScheduled` emitted while this version has not taken effect replaces it, so when rebuilding catalogs from history drop replaced versions, or read `catalogAt(epochId)`.
1841
+
1842
+ **Administration and upgrades**
1843
+
1844
+ #### <a id="registry-event-committerchanged"></a>`CommitterChanged`
1845
+
1846
+ ```solidity
1847
+ event CommitterChanged(address indexed previousCommitter, address indexed newCommitter)
1848
+ ```
1849
+
1850
+ Topic 0 `0x3f67cc70f736070aaac75db90cef1ab4047521b73e8a38d02852e8bf1a91e7e0` · Emitted by: [`setCommitter`](#registry-fn-setcommitter) · Source: `EpochEntropy.sol` lines 72, 102
1851
+
1852
+ New primary publishing address; it also receives the keeper share of proofs submitted by wallets the registry does not authorize.
1853
+
1854
+ #### <a id="registry-event-backupcommitterset"></a>`BackupCommitterSet`
1855
+
1856
+ ```solidity
1857
+ event BackupCommitterSet(address indexed account, bool allowed)
1858
+ ```
1859
+
1860
+ Topic 0 `0x20380b8c17d904db7d905a51f1538057d280a6cecca38882832ba0261b39fa66` · Emitted by: [`setBackupCommitter`](#registry-fn-setbackupcommitter) · Source: `EpochEntropy.sol` lines 75, 116
1861
+
1862
+ `account` may now publish epochs (`allowed` true) or no longer may (`allowed` false).
1863
+
1864
+ #### <a id="registry-event-ownershiptransferstarted"></a>`OwnershipTransferStarted`
1865
+
1866
+ ```solidity
1867
+ event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner)
1868
+ ```
1869
+
1870
+ Topic 0 `0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700` · Emitted by: [`transferOwnership`](#registry-fn-transferownership)
1871
+
1872
+ `transferOwnership` nominated `newOwner`; the zero address means a nomination was cancelled.
1873
+
1874
+ #### <a id="registry-event-ownershiptransferred"></a>`OwnershipTransferred`
1875
+
1876
+ ```solidity
1877
+ event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)
1878
+ ```
1879
+
1880
+ Topic 0 `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` · Emitted by: [`acceptOwnership`](#registry-fn-acceptownership), [`initialize`](#registry-fn-initialize)
1881
+
1882
+ Ownership moved: from the zero address at initialization, and at each `acceptOwnership`.
1883
+
1884
+ #### <a id="registry-event-upgraded"></a>`Upgraded`
1885
+
1886
+ ```solidity
1887
+ event Upgraded(address indexed implementation)
1888
+ ```
1889
+
1890
+ Topic 0 `0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b` · Emitted by: [`upgradeToAndCall`](#registry-fn-upgradetoandcall), proxy deployment
1891
+
1892
+ The proxy now runs `implementation`. Emitted by the proxy at deployment and at every `upgradeToAndCall`. Compare the address with the deployment manifest; an implementation you have not reviewed means stop and review before sending more requests.
1893
+
1894
+ #### <a id="registry-event-initialized"></a>`Initialized`
1895
+
1896
+ ```solidity
1897
+ event Initialized(uint64 version)
1898
+ ```
1899
+
1900
+ Topic 0 `0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2` · Emitted by: [`initializeRecipeRegistry`](#registry-fn-initializereciperegistry), [`initialize`](#registry-fn-initialize)
1901
+
1902
+ `initialize` ran on the proxy (`version` 1) or `initializeRecipeRegistry` did (`version` 2). Each implementation contract also emitted it once at construction with `version` 2^64 − 1, which locks the implementation against initialization.
1903
+
1904
+ ### <a id="registry-errors"></a>Errors
1905
+
1906
+ **Epoch reads**
1907
+
1908
+ #### <a id="registry-error-invalidepoch"></a>`InvalidEpoch`
1909
+
1910
+ `error InvalidEpoch()` · Selector `0xd5b25b63` · Source: `EpochEntropy.sol` lines 66, 204, 232
1911
+
1912
+ **Raised by:** [`epochStart`](#registry-fn-epochstart), [`getEpochSelection`](#registry-fn-getepochselection), [`getEpochFallbackSelection`](#registry-fn-getepochfallbackselection), [`fallbackOpensAt`](#registry-fn-fallbackopensat), [`checkpointEpoch`](#registry-fn-checkpointepoch), [`commitEpoch`](#registry-fn-commitepoch), [`commitEpochFallback`](#registry-fn-commitepochfallback), [`scheduleCatalog`](#registry-fn-schedulecatalog).
1913
+
1914
+ Epoch 0 was passed to `epochStart`, `fallbackOpensAt`, a selection view, `checkpointEpoch` or a commit, or `scheduleCatalog` got a `fromEpoch` less than two epochs after the current one.
1915
+
1916
+ **What to do:** Epoch IDs start at 1; schedule at least two epochs ahead.
1917
+
1918
+ #### <a id="registry-error-preparationclosed"></a>`PreparationClosed`
1919
+
1920
+ `error PreparationClosed()` · Selector `0x8e2a3c7d` · Source: `EpochEntropy.sol` lines 66, 247
1921
+
1922
+ **Raised by:** [`getEpochSelection`](#registry-fn-getepochselection), [`getEpochFallbackSelection`](#registry-fn-getepochfallbackselection), [`checkpointEpoch`](#registry-fn-checkpointepoch).
1923
+
1924
+ The epoch has not started (`block.number` is below `epochStart(epochId)`), so its anchor block hash does not exist yet.
1925
+
1926
+ **What to do:** Wait for the epoch to start.
1927
+
1928
+ #### <a id="registry-error-anchorunavailable"></a>`AnchorUnavailable`
1929
+
1930
+ `error AnchorUnavailable()` · Selector `0x60776ed3` · Source: `EpochEntropy.sol` lines 66, 250
1931
+
1932
+ **Raised by:** [`getEpochSelection`](#registry-fn-getepochselection), [`getEpochFallbackSelection`](#registry-fn-getepochfallbackselection), [`checkpointEpoch`](#registry-fn-checkpointepoch), [`commitEpoch`](#registry-fn-commitepoch), [`commitEpochFallback`](#registry-fn-commitepochfallback).
1933
+
1934
+ The anchor (hash of block `epochStart - 1`) was never checkpointed and is outside the 256-block `BLOCKHASH` window.
1935
+
1936
+ **What to do:** The epoch can no longer be selected or published. Every request checkpoints its epoch's anchor, so an epoch with requests is not affected.
1937
+
1938
+ **Publication**
1939
+
1940
+ #### <a id="registry-error-onlycommitter"></a>`OnlyCommitter`
1941
+
1942
+ `error OnlyCommitter()` · Selector `0xfffe5af3` · Source: `EpochEntropy.sol` lines 67, 281
1943
+
1944
+ **Raised by:** [`commitEpoch`](#registry-fn-commitepoch), [`commitEpochFallback`](#registry-fn-commitepochfallback).
1945
+
1946
+ A commit from an address that is neither `committer()` nor an allowed backup committer.
1947
+
1948
+ **What to do:** Only the committer and backup committers publish; check `isBackupCommitter`.
1949
+
1950
+ #### <a id="registry-error-alreadycommitted"></a>`AlreadyCommitted`
1951
+
1952
+ `error AlreadyCommitted()` · Selector `0xbfec5558` · Source: `EpochEntropy.sol` lines 67, 282
1953
+
1954
+ **Raised by:** [`commitEpoch`](#registry-fn-commitepoch), [`commitEpochFallback`](#registry-fn-commitepochfallback).
1955
+
1956
+ The epoch already has a published packet.
1957
+
1958
+ **What to do:** Nothing to publish; read `getEpoch`.
1959
+
1960
+ #### <a id="registry-error-fallbacknotopen"></a>`FallbackNotOpen`
1961
+
1962
+ `error FallbackNotOpen()` · Selector `0xf8635228` · Source: `EpochEntropy.sol` lines 69, 283
1963
+
1964
+ **Raised by:** [`commitEpoch`](#registry-fn-commitepoch), [`commitEpochFallback`](#registry-fn-commitepochfallback).
1965
+
1966
+ `block.number` is below `fallbackOpensAt(epochId, attempt)`; for attempt 0, before the epoch start.
1967
+
1968
+ **What to do:** Wait for the window.
1969
+
1970
+ #### <a id="registry-error-invalidfallback"></a>`InvalidFallback`
1971
+
1972
+ `error InvalidFallback()` · Selector `0x5a93724d` · Source: `EpochEntropy.sol` lines 69, 258, 266, 277
1973
+
1974
+ **Raised by:** [`getEpochFallbackSelection`](#registry-fn-getepochfallbackselection), [`fallbackOpensAt`](#registry-fn-fallbackopensat), [`commitEpochFallback`](#registry-fn-commitepochfallback).
1975
+
1976
+ An attempt at or above `sourceCountAt(epochId)`, or attempt 0 passed to `commitEpochFallback`.
1977
+
1978
+ **What to do:** Use attempts 1 to `sourceCountAt(epochId) - 1` for fallbacks.
1979
+
1980
+ #### <a id="registry-error-invalidtime"></a>`InvalidTime`
1981
+
1982
+ `error InvalidTime()` · Selector `0x6f7eac26` · Source: `EpochEntropy.sol` lines 67, 285
1983
+
1984
+ **Raised by:** [`commitEpoch`](#registry-fn-commitepoch), [`commitEpochFallback`](#registry-fn-commitepochfallback).
1985
+
1986
+ The attestation timestamp is in the future or more than `MAX_ATTESTATION_AGE` (240 seconds) before the publication block.
1987
+
1988
+ **What to do:** A saved packet is never refreshed; its requests expire and are refunded.
1989
+
1990
+ #### <a id="registry-error-invaliddata"></a>`InvalidData`
1991
+
1992
+ `error InvalidData()` · Selector `0x5cb045db` · Source: `EpochEntropy.sol` lines 67, 286–287
1993
+
1994
+ **Raised by:** [`commitEpoch`](#registry-fn-commitepoch), [`commitEpochFallback`](#registry-fn-commitepochfallback).
1995
+
1996
+ The signed data does not match the data template of the slot's recipe exactly, which includes data longer than `MAX_DATA_BYTES` (128).
1997
+
1998
+ **What to do:** Publish only a response that matches the selected recipe's template, unmodified.
1999
+
2000
+ #### <a id="registry-error-invalidsigner"></a>`InvalidSigner`
2001
+
2002
+ `error InvalidSigner()` · Selector `0x815e1d64` · Source: `EpochEntropy.sol` lines 67, 289
2003
+
2004
+ **Raised by:** [`commitEpoch`](#registry-fn-commitepoch), [`commitEpochFallback`](#registry-fn-commitepochfallback).
2005
+
2006
+ The signature does not recover to the slot's signer in the epoch's catalog.
2007
+
2008
+ **What to do:** Use `catalogAt(epochId)` for the expected signer.
2009
+
2010
+ #### <a id="registry-error-ecdsainvalidsignature"></a>`ECDSAInvalidSignature`
2011
+
2012
+ `error ECDSAInvalidSignature()` · Selector `0xf645eedf`
2013
+
2014
+ **Raised by:** [`commitEpoch`](#registry-fn-commitepoch), [`commitEpochFallback`](#registry-fn-commitepochfallback).
2015
+
2016
+ The signature does not recover to any address (OpenZeppelin `ECDSA`).
2017
+
2018
+ **What to do:** Publish the exact signature from the response.
2019
+
2020
+ #### <a id="registry-error-ecdsainvalidsignaturelength"></a>`ECDSAInvalidSignatureLength`
2021
+
2022
+ `error ECDSAInvalidSignatureLength(uint256 length)` · Selector `0xfce698f7`
2023
+
2024
+ **Raised by:** [`commitEpoch`](#registry-fn-commitepoch), [`commitEpochFallback`](#registry-fn-commitepochfallback).
2025
+
2026
+ The signature is not 65 bytes (OpenZeppelin `ECDSA`).
2027
+
2028
+ **What to do:** Publish the exact signature from the response.
2029
+
2030
+ #### <a id="registry-error-ecdsainvalidsignatures"></a>`ECDSAInvalidSignatureS`
2031
+
2032
+ `error ECDSAInvalidSignatureS(bytes32 s)` · Selector `0xd78bce0c`
2033
+
2034
+ **Raised by:** [`commitEpoch`](#registry-fn-commitepoch), [`commitEpochFallback`](#registry-fn-commitepochfallback).
2035
+
2036
+ The signature has a high `s` value (OpenZeppelin `ECDSA`).
2037
+
2038
+ **What to do:** Publish the exact signature from the response.
2039
+
2040
+ #### <a id="registry-error-packettoolarge"></a>`PacketTooLarge`
2041
+
2042
+ `error PacketTooLarge()` · Selector `0xda85e8a5` · Source: `EpochEntropy.sol` lines 68, 298
2043
+
2044
+ **Raised by:** [`commitEpoch`](#registry-fn-commitepoch), [`commitEpochFallback`](#registry-fn-commitepochfallback).
2045
+
2046
+ The encoded packet exceeds `MAX_PACKET_BYTES` (2048).
2047
+
2048
+ **What to do:** Not reachable: the recipe and data bounds keep every packet within `MAX_PACKET_BYTES`.
2049
+
2050
+ **Recipes, administration, initialization and upgrades**
2051
+
2052
+ #### <a id="registry-error-invalidrecipe"></a>`InvalidRecipe`
2053
+
2054
+ `error InvalidRecipe()` · Selector `0x7b776f4c` · Source: `EpochEntropy.sol` lines 70, 147
2055
+
2056
+ **Raised by:** [`registerRecipe`](#registry-fn-registerrecipe).
2057
+
2058
+ `registerRecipe` got an empty canonical request or body, a canonical request over `MAX_REQUEST_BYTES` or a body over `MAX_BODY_BYTES`, or `MAX_RECIPES` recipes are already registered.
2059
+
2060
+ **What to do:** Shorten the request or body. Recipe ids are never freed.
2061
+
2062
+ #### <a id="registry-error-invalidtemplate"></a>`InvalidTemplate`
2063
+
2064
+ `error InvalidTemplate()` · Selector `0xec55b8cd` · Source: `EpochEntropy.sol` lines 70, 148
2065
+
2066
+ **Raised by:** [`registerRecipe`](#registry-fn-registerrecipe).
2067
+
2068
+ `registerRecipe` got a data template that is not well formed (README [Data templates](README.md#data-templates)).
2069
+
2070
+ **What to do:** Build the template with `encodeDataTemplate`, which names the broken rule, before registering.
2071
+
2072
+ #### <a id="registry-error-invalidconfig"></a>`InvalidConfig`
2073
+
2074
+ `error InvalidConfig()` · Selector `0x35be3ac8` · Source: `EpochEntropy.sol` lines 66, 81, 93, 101, 108, 110, 140, 196, 200
2075
+
2076
+ **Raised by:** [`getRecipe`](#registry-fn-getrecipe), [`recipeRequest`](#registry-fn-reciperequest), [`getEpochSelection`](#registry-fn-getepochselection), [`getEpochFallbackSelection`](#registry-fn-getepochfallbackselection), [`commitEpoch`](#registry-fn-commitepoch), [`commitEpochFallback`](#registry-fn-commitepochfallback), [`scheduleCatalog`](#registry-fn-schedulecatalog), [`initializeRecipeRegistry`](#registry-fn-initializereciperegistry), [`setCommitter`](#registry-fn-setcommitter), [`setBackupCommitter`](#registry-fn-setbackupcommitter), [`initialize`](#registry-fn-initialize).
2077
+
2078
+ A zero signer or committer in `initialize`; a zero address in `setCommitter`; in `setBackupCommitter` the zero address, allowing the committer, an unchanged status or a fifth backup committer; in `scheduleCatalog` an empty or oversized catalog, mismatched lengths, a repeated or unregistered recipe or a zero signer; `initializeRecipeRegistry` on a registry that already has recipes or a scheduled catalog; or a recipe id that is not registered, in `getRecipe` and `recipeRequest` or, on a registry upgraded without `initializeRecipeRegistry`, in selection and publication.
2079
+
2080
+ **What to do:** Correct the arguments; recipe ids run from 0 to `recipeCount() - 1`. A registry upgraded without the recipe step needs `initializeRecipeRegistry` from its owner.
2081
+
2082
+ #### <a id="registry-error-ownableunauthorizedaccount"></a>`OwnableUnauthorizedAccount`
2083
+
2084
+ `error OwnableUnauthorizedAccount(address account)` · Selector `0x118cdaa7`
2085
+
2086
+ **Raised by:** [`registerRecipe`](#registry-fn-registerrecipe), [`scheduleCatalog`](#registry-fn-schedulecatalog), [`initializeRecipeRegistry`](#registry-fn-initializereciperegistry), [`setCommitter`](#registry-fn-setcommitter), [`setBackupCommitter`](#registry-fn-setbackupcommitter), [`transferOwnership`](#registry-fn-transferownership), [`acceptOwnership`](#registry-fn-acceptownership), [`renounceOwnership`](#registry-fn-renounceownership), [`upgradeToAndCall`](#registry-fn-upgradetoandcall).
2087
+
2088
+ `account` is not the owner (owner-only functions) or not the pending owner (`acceptOwnership`).
2089
+
2090
+ **What to do:** Only the owner can administer or upgrade the contract; on Arc Mainnet that is the DAO treasury Safe recorded in the deployment manifest.
2091
+
2092
+ #### <a id="registry-error-ownableinvalidowner"></a>`OwnableInvalidOwner`
2093
+
2094
+ `error OwnableInvalidOwner(address owner)` · Selector `0x1e4fbdf7`
2095
+
2096
+ **Raised by:** [`initialize`](#registry-fn-initialize).
2097
+
2098
+ `initialize` was given the zero address as owner.
2099
+
2100
+ **What to do:** Deployment-time only.
2101
+
2102
+ #### <a id="registry-error-renouncedisabled"></a>`RenounceDisabled`
2103
+
2104
+ `error RenounceDisabled()` · Selector `0x89051165` · Source: `EpochEntropy.sol` lines 68, 98
2105
+
2106
+ **Raised by:** [`renounceOwnership`](#registry-fn-renounceownership).
2107
+
2108
+ The owner called `renounceOwnership`, which is disabled.
2109
+
2110
+ **What to do:** Move ownership with `transferOwnership` and `acceptOwnership`.
2111
+
2112
+ #### <a id="registry-error-invalidinitialization"></a>`InvalidInitialization`
2113
+
2114
+ `error InvalidInitialization()` · Selector `0xf92ee8a9`
2115
+
2116
+ **Raised by:** [`initializeRecipeRegistry`](#registry-fn-initializereciperegistry), [`initialize`](#registry-fn-initialize).
2117
+
2118
+ `initialize` on a proxy that is already initialized or on an implementation contract, whose initializers are disabled at construction, or `initializeRecipeRegistry` on a proxy that has already reached initializer version 2.
2119
+
2120
+ **What to do:** None: `initialize` happens once when `D20Proxy` is deployed, and `initializeRecipeRegistry` once in the recipe-registry upgrade.
2121
+
2122
+ #### <a id="registry-error-notinitializing"></a>`NotInitializing`
2123
+
2124
+ `error NotInitializing()` · Selector `0xd7e6bcf8`
2125
+
2126
+ **Raised by:** no public function (declared by OpenZeppelin `Initializable`).
2127
+
2128
+ Declared by the OpenZeppelin initializer helpers. No public function of this contract can reach it.
2129
+
2130
+ **What to do:** None.
2131
+
2132
+ #### <a id="registry-error-uupsunauthorizedcallcontext"></a>`UUPSUnauthorizedCallContext`
2133
+
2134
+ `error UUPSUnauthorizedCallContext()` · Selector `0xe07c8dba`
2135
+
2136
+ **Raised by:** [`upgradeToAndCall`](#registry-fn-upgradetoandcall), [`proxiableUUID`](#registry-fn-proxiableuuid).
2137
+
2138
+ `upgradeToAndCall` called on the implementation instead of through the proxy, or `proxiableUUID` called through the proxy.
2139
+
2140
+ **What to do:** Owner upgrade procedure only.
2141
+
2142
+ #### <a id="registry-error-uupsunsupportedproxiableuuid"></a>`UUPSUnsupportedProxiableUUID`
2143
+
2144
+ `error UUPSUnsupportedProxiableUUID(bytes32 slot)` · Selector `0xaa1d49a4`
2145
+
2146
+ **Raised by:** [`upgradeToAndCall`](#registry-fn-upgradetoandcall).
2147
+
2148
+ The new implementation reports a `proxiableUUID` other than the ERC-1967 implementation slot.
2149
+
2150
+ **What to do:** Owner upgrade procedure only.
2151
+
2152
+ #### <a id="registry-error-erc1967invalidimplementation"></a>`ERC1967InvalidImplementation`
2153
+
2154
+ `error ERC1967InvalidImplementation(address implementation)` · Selector `0x4c9c8ce3`
2155
+
2156
+ **Raised by:** [`upgradeToAndCall`](#registry-fn-upgradetoandcall).
2157
+
2158
+ The new implementation has no code or no `proxiableUUID`.
2159
+
2160
+ **What to do:** Owner upgrade procedure only.
2161
+
2162
+ #### <a id="registry-error-erc1967nonpayable"></a>`ERC1967NonPayable`
2163
+
2164
+ `error ERC1967NonPayable()` · Selector `0xb398979f`
2165
+
2166
+ **Raised by:** [`upgradeToAndCall`](#registry-fn-upgradetoandcall).
2167
+
2168
+ `upgradeToAndCall` was sent value with empty `data`.
2169
+
2170
+ **What to do:** Owner upgrade procedure only.
2171
+
2172
+ #### <a id="registry-error-addressemptycode"></a>`AddressEmptyCode`
2173
+
2174
+ `error AddressEmptyCode(address target)` · Selector `0x9996b315`
2175
+
2176
+ **Raised by:** [`upgradeToAndCall`](#registry-fn-upgradetoandcall).
2177
+
2178
+ Declared by OpenZeppelin `Address` for the delegatecall in `upgradeToAndCall`. Not reachable in practice, because the new implementation must already have code.
2179
+
2180
+ **What to do:** None.
2181
+
2182
+ #### <a id="registry-error-failedcall"></a>`FailedCall`
2183
+
2184
+ `error FailedCall()` · Selector `0xd6bda275`
2185
+
2186
+ **Raised by:** [`upgradeToAndCall`](#registry-fn-upgradetoandcall).
2187
+
2188
+ The initialization call made by `upgradeToAndCall` reverted without revert data.
2189
+
2190
+ **What to do:** Owner upgrade procedure only.