State methods
Willow RPC serves account state on all seven chains. State is read from a
provider’s EIP-1186 proof and
verified against the state root of a block that Willow verified, before it is
returned. On Ethereum, eth_call is executed by Willow over state verified in
the same way. Methods whose results no block commits to, such as sending a
transaction or estimating gas, are forwarded to a provider and returned as the
provider answers them.
State methods are set per plan. Of the self-serve plans, Pay as you go,
Builder, and Scale include them, and Free does not. A custom plan includes them
only if its agreement does. A key on a plan that does not include state methods
receives -32601 for every state method, including the forwarded methods, so a
router sends the request to another provider. /v1/prices
reports each plan’s state_methods. See Plans.
| Method | Chains | How it is answered | Units |
|---|---|---|---|
eth_getBalance, eth_getTransactionCount, eth_getCode, eth_getStorageAt, eth_getProof | All seven | Verified against the block’s state root | 5 |
eth_call | Ethereum | Executed by Willow over verified state | 25 |
eth_sendRawTransaction, eth_estimateGas, eth_gasPrice, eth_maxPriorityFeePerGas, eth_feeHistory, eth_blobBaseFee | All seven | Forwarded to a provider, not verified | 3 |
A request that requires more than its listed units’ worth of provider requests costs 3 units per provider request instead. See Units.
eth_call on the other six chains returns -32601, as do state overrides on
any chain. See eth_call.
On Robinhood Chain, reads are refused with -32000 while Willow does not
receive the chain’s sequencer feed (see
How Willow verifies data). The
forwarded methods are served.
Verification
Section titled “Verification”Willow requests the account, and any storage slots, as an eth_getProof
response from a provider. It verifies the account proof and each storage proof
against the state root in the header of the requested block, and verifies the
returned code against the account’s code hash. A provider whose proof does not
verify is not used, and the next provider is asked. If no provider returns a
proof that verifies, the request is refused with -32000, so a router can send
it to another provider.
The header is one Willow verified before serving it:
- On Ethereum, state is read only at blocks the light client (Helios) has confirmed. See Blocks.
- On the other six chains, state is read at blocks the sequencer signed, the same blocks Willow serves. See How Willow verifies data.
A value that does not exist is proven absent: the balance, nonce, and storage
of an account with no entry in the state are returned as 0x0, and its code
as 0x.
Blocks
Section titled “Blocks”The block parameter accepts a hexadecimal number, a tag (latest, pending,
earliest, finalized, or safe), a block hash, or an EIP-1898 object
({"blockHash": …} or {"blockNumber": …}). An omitted block parameter means
latest. finalized and safe are served and refused as for the other
methods (see finalized and safe), so
safe is refused on Ethereum, Arbitrum One, and Robinhood Chain.
latestandpendingon Ethereum refer to the newest block the light client has confirmed, which can be behind the newest block thateth_blockNumberreturns. If that block is more than 180 seconds old, the request is refused with-32000instead of being answered from an old block.- A block number on Ethereum later than the newest confirmed block is
refused with
-32000(not yet confirmed by the light client). - On the other six chains,
latestandpendingrefer to the newest block Willow holds, under the same freshness rule as the other methods. - Older blocks are answered when a provider holds their state. Willow asks
archive providers for older blocks; a block whose state no provider returns
is refused with
-32000. On the six chains other than Ethereum, a block older than those Willow holds is refused as described in History.
Pending nonce
Section titled “Pending nonce”eth_getTransactionCount with pending returns the provider’s pending nonce,
which includes transactions in the provider’s mempool and therefore cannot be
proven. Willow bounds it by the proven nonce at the newest confirmed block: the
result is never below the proven nonce and never more than 64 above it.
eth_getBalance, eth_getTransactionCount, eth_getCode, and
eth_getStorageAt return the same values as a node. eth_getProof returns the
verified proof in the standard form (address, balance, codeHash, nonce,
storageHash, accountProof, and storageProof), with at most 256 storage
keys per request.
Request:
curl -s https://api.willow.tech/v1/rpc/1/{YOUR_KEY} \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"eth_getBalance","params":["0x00000000219ab540356cbb839cbe05303d7705fa","latest"]}'Response:
{"id":1,"jsonrpc":"2.0","result":"0x…"}eth_call
Section titled “eth_call”eth_call is served on Ethereum. Willow executes the call in its own EVM at
the requested block. Every account, storage slot, and contract code the call
reads is proven against the block’s state root before the call returns. A
provider’s access list is used only to request the likely state in advance;
state it omits is requested when the execution reaches it, and is verified in
the same way.
The call object accepts the standard fields: from, to, gas, gasPrice,
maxFeePerGas, maxPriorityFeePerGas, value, data (or input),
accessList, and chainId.
| Result | Code | Message |
|---|---|---|
| Returned | result holds the return data. | |
| Reverted | 3 | execution reverted, with the decoded reason when the data is an Error(string) or Panic(uint256). error.data holds the revert data. |
| Halted | -32000 | out of gas, or VM execution error: followed by the reason. |
These results are final: another provider executing the same call at the same block returns the same result, so a router should return them to the client rather than fall back.
Limits
Section titled “Limits”- State per call. A call may read at most 256 accounts and 8,192 storage
slots. A call that reads more is refused with
-32000(the call reads more state than one call may). - Time. A request is answered within 20 seconds. A call whose state cannot
be proven within that time is refused with
-32000(proving the state it reads took longer than this request may). - Overrides. State overrides and block overrides (the third and fourth
parameters) are refused with
-32601(eth_call with state or block overrides is not available here), since the result would not describe the block’s state. - Blobs and authorizations. A call object with
blobVersionedHashesor anauthorizationList(EIP-7702) is refused with-32000.
Forwarded methods
Section titled “Forwarded methods”eth_sendRawTransaction, eth_estimateGas, eth_gasPrice,
eth_maxPriorityFeePerGas, eth_feeHistory, and eth_blobBaseFee are sent to
a provider, and the provider’s answer is returned unchanged, including its
errors. No block commits to these values, so they are not verified.
eth_sendRawTransaction returns the hash of the submitted transaction as
computed from its bytes, not as reported by the provider. If a provider
reports the transaction as already known, the transaction is treated as sent.
Concurrency
Section titled “Concurrency”Each chain has separate capacity for reads, eth_call, sent transactions, and
the other forwarded methods:
| Lane | At once per chain | At once per key |
|---|---|---|
Reads (eth_getBalance … eth_getProof) | 64 | 16 |
eth_call | 8 | 4 |
eth_sendRawTransaction | 16 | 4 |
| Other forwarded methods | 64 | 16 |
A key’s requests beyond its share wait their turn. When the lane itself is
full, a request waits up to 2 seconds and is then refused with -32000
(this server is busy, try again in a moment), so a router can send it to
another provider.
Each kind of state method has a flat price in units, regardless of the size of the response. A request that required more provider requests than its price covers costs 3 units per provider request instead.
| Kind | Units |
|---|---|
| Forwarded method | 3 |
| Read | 5 |
eth_call | 25 |
- Billed: a result, a revert, a halted call, and an error returned by a provider for a forwarded method.
- Billed per provider request (3 units each): a call refused because it reads too much state or ran out of time, since the provider requests were made.
- Not billed: a refusal because the server is busy, because no provider returned a proof that verifies, or because the request was invalid.
Errors
Section titled “Errors”| Code | Meaning | Remedy |
|---|---|---|
3 | The call reverted. error.data holds the revert data. | Return to the client. Do not fall back. |
-32000, message without “not found” | The call halted (out of gas, VM execution error: …). | Return to the client. Do not fall back. |
-32000, message with “not found” | The block is not yet confirmed or is too old, no provider returned a proof that verifies, the call reads too much state or ran out of time, or the server is busy. | Fall back to another provider. |
-32601 | eth_call on a chain other than Ethereum, eth_call with overrides, or a key on a plan that does not include state methods. | Send to another provider. |
-32602 | A malformed address, slot, or call object, or more than 256 keys in eth_getProof. | Correct the request. |
| Provider’s code | An error returned by the provider for a forwarded method, such as a rejected transaction. | Handle as the provider’s error. |
Examples:
{"error":{"code":-32000,"message":"not found in this store: block 23650010 is not yet confirmed by the light client"},"id":1,"jsonrpc":"2.0"}{"error":{"code":-32000,"message":"not found in this store: state reads: this server is busy, try again in a moment"},"id":2,"jsonrpc":"2.0"}{"error":{"code":-32601,"message":"eth_call with state or block overrides is not available here"},"id":3,"jsonrpc":"2.0"}