Methods
Willow RPC supports 13 standard methods for chain data, 12 standard methods for account state and transactions, and nine Willow-specific methods. The standard methods return the same fields as a node.
| Method | Coverage |
|---|---|
eth_getLogs | Any filter: any address, any topics, or none, over any block range. See eth_getLogs. |
eth_blockNumber | The newest block Willow holds for that chain. |
eth_getBlockByNumber, eth_getBlockByHash | With transaction hashes (false) or full transactions (true). |
eth_getBlockReceipts, eth_getTransactionByHash, eth_getTransactionReceipt, eth_getTransactionByBlockNumberAndIndex, eth_getTransactionByBlockHashAndIndex | Every block and transaction Willow holds. |
eth_getBlockTransactionCountByNumber, eth_getBlockTransactionCountByHash | Every block Willow holds. |
eth_chainId, net_version | Always. |
eth_getBalance, eth_getTransactionCount, eth_getCode, eth_getStorageAt, eth_getProof, eth_call | Account state, verified against the block’s state root. eth_call on Ethereum only. On plans that include state methods. See State methods. |
eth_sendRawTransaction, eth_estimateGas, eth_gasPrice, eth_maxPriorityFeePerGas, eth_feeHistory, eth_blobBaseFee | Forwarded to a provider. On plans that include state methods. See State methods. |
willow_quoteLogsProof, willow_getLogsProof, willow_getProof, willow_getProofJob | Log proofs. See Log proof methods. |
willow_getTransactionProof, willow_getReceiptProof, willow_getBlockReceiptsProof | Transaction and receipt proofs. See Transaction and receipt proofs. |
willow_historyStatus, willow_historyCancel | Fetches of older blocks. See History. |
The browser verifier also calls
willow_lightClientBootstrap, willow_lightClientUpdates, and
willow_getBlockAnchor to verify blocks.
Every other method returns -32601:
{"error":{"code":-32601,"message":"the method eth_newFilter does not exist/is not available"},"id":6,"jsonrpc":"2.0"}This includes eth_newFilter and the other filter methods, debug and trace
methods, eth_call on the six chains other than Ethereum, and every
subscription method. Willow provides no WebSocket endpoint.
Ethereum is held from genesis. On the other chains, blocks older than those Willow holds are fetched on the first request from a paying account. A first request covering more than a few such blocks is refused while they are fetched. See History.
eth_getLogs
Section titled “eth_getLogs”The filter object behaves as it does on a standard node.
addressis optional. It is a single address or a list. An empty list matches any address.topicscontains up to four positions. Each is a value, a list of alternatives, ornullfor any value.- The block range is specified by
fromBlockandtoBlock(hexadecimal numbers or tags), or byblockHashalone. CombiningblockHashwithfromBlockortoBlockreturns-32602. An omittedfromBlockortoBlockdefaults tolatest. latestandpendingboth refer to the newest block Willow holds.earliestrefers to block 0.finalizedandsafeare described below.- Willow never returns a partial range. If it cannot serve every block in the range, the entire request is refused.
Range limits
Section titled “Range limits”Willow indexes the address and topics of every log, so a filter reads only the blocks that contain a match. The block range itself is not capped. The following limits apply:
- 10,000 logs. A call returns at most 10,000 logs. A larger result is
refused with
-32002(HTTP 200) instead of being truncated, and the message specifies a range that fits:query returned more than 10000 results. Try with this block range [0x…, 0x…].The logs of a single block are never truncated. - 2 seconds per request. The
eth_getLogsitems of one HTTP request share 2,000 ms for reading blocks. An item that exceeds it is refused with-32002, and the message specifies the range read so far:range too large: answering this filter ran past this request's 2000 ms at block … (from …); send the rest in another request. Try with this block range [0x…, 0x…].In testing, a filter on an address and an infrequent topic covered more than 100,000 older Ethereum blocks within that time. A filter with neither reads every block, and therefore reaches the 10,000-log limit within a few blocks on high-volume chains. - Blocks not yet indexed. Blocks held in full but not yet indexed, which is
uncommon, are read individually: at most
maxLogsRangeof them in one call andmaxLogsBlocksPerRequestin one HTTP request, both reported in/v1/rpc/health. Beyond either limit, the request is refused with-32002andrange too large. - Two concurrent calls. An account may have two
eth_getLogscalls reading blocks concurrently (one on Free). An additional call waits up to 50 ms for capacity, then is refused with-32000(the server is busy reading blocks for other requests), so a router can query another provider.
| Chain | maxLogsRange | maxLogsBlocksPerRequest |
|---|---|---|
| Ethereum, Base, OP Mainnet | 100 | 1,000 |
| Ink | 1,000 | 10,000 |
| Robinhood Chain | 3,000 | 30,000 |
| Unichain | 5,000 | 50,000 |
| Arbitrum One | 10,000 | 100,000 |
Logs are returned in block and log order. They are derived from each block’s
verified receipts and returned in the standard form:
address, topics, data, blockNumber, blockHash, blockTimestamp,
transactionHash, transactionIndex, logIndex, and removed (always
false).
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_getLogs","params":[{ "fromBlock":"0x18e8f2a", "toBlock":"0x18e8f2a", "address":"0x000000000004444c5dc75cb358380d2e3de08a90", "topics":["0x40e9cecb9f5f1f1c5b9c97dec2917b7ee92e57ba5563708daca94dd84ad7112f"] }]}'Response (14 logs; the first is shown, with data truncated):
{ "id": 1, "jsonrpc": "2.0", "result": [ { "address": "0x000000000004444c5dc75cb358380d2e3de08a90", "blockHash": "0xdb98a28f352acb72751ec8012c5a617acfb2d7469aadb9bc4690db6b6dd8d53f", "blockNumber": "0x18e8f2a", "blockTimestamp": "0x6ac274c3", "data": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffe38d57f2…", "logIndex": "0x2", "removed": false, "topics": [ "0x40e9cecb9f5f1f1c5b9c97dec2917b7ee92e57ba5563708daca94dd84ad7112f", "0xe500210c7ea6bfd9f69dce044b09ef384ec2b34832f132baec3b418208e3a657", "0x0000000000000000000000000000000aa232009084bd71a5797d089aa4edfad4" ], "transactionHash": "0x4560d377ceb62f875b054d83a86caabe34e7f681271f2d204ac692a472cf987c", "transactionIndex": "0x0" }, … ]}Refusal for an oversized result:
{"error":{"code":-32002,"message":"query returned more than 10000 results. Try with this block range [0x190bca0, 0x190bca6]."},"id":1,"jsonrpc":"2.0"}finalized and safe
Section titled “finalized and safe”finalizedon Ethereum is the block that the Ethereum light client (Helios) verified as finalized.finalizedon the other six chains is the newest block whose transactions were posted to Ethereum in a block that the light client verified as finalized. Willow reads the posted batches from verified Ethereum blocks, derives the chain’s blocks from them under the chain’s own rules, and serves the tag only when every block up to it matches the derived blocks.safeon Base, OP Mainnet, Unichain, and Ink is defined in the same way, but up to the newest Ethereum block the light client verified rather than the finalized one.safeis refused on Ethereum, Arbitrum One, and Robinhood Chain.- Either tag is refused with
-32000while it cannot be verified. See How Willow verifies data.
eth_blockNumber
Section titled “eth_blockNumber”Returns the newest block Willow holds for the chain. A request for a later
block is refused with -32000, so a router can fall back for the newest
blocks.
{"id":3,"jsonrpc":"2.0","result":"0x18e8f98"}Once a chain’s newest stored block is older than 15 of its block times, and at
least 10 seconds, eth_blockNumber, latest, and pending are refused with
-32000 (this store is catching up) instead of returning a stale block.
See
Chains and URLs.
eth_getBlockByNumber, eth_getBlockByHash
Section titled “eth_getBlockByNumber, eth_getBlockByHash”Both methods return the block header, reconstructed from data verified against
the block hash. With false as the second parameter, transactions is the
list of transaction hashes. With true, it contains the full transaction
objects. For eth_getBlockByNumber, the tag rules are the same as for
eth_getLogs.
Request:
{"jsonrpc":"2.0","id":2,"method":"eth_getBlockByNumber","params":["latest",false]}Response (truncated):
{ "id": 2, "jsonrpc": "2.0", "result": { "hash": "0x3ac0e76e347b2306ca05a245bd30f311ec7b7b7ee7658d757b043ed32fd86198", "number": "0x18e8f5c", "parentHash": "0xa54227b770d27c207ce1f25ef272c60aba3c2c502352a5440e694e92612f01d5", "receiptsRoot": "0x64e235b446253cabef1fb1b58cb0117fc41c090f709c708ad679326d287b53f3", "timestamp": "0x6ac2771b", "transactions": [ "0x72c61334cb42d00db2185d49161c3cc4c96a6780be184553fd6f9c75c78acf28", … ], "transactionsRoot": "0xdf3cbf1d08956d77a49b2087cf1029cd56cdf34c7e8d07a132936d625d62e784", … }}totalDifficulty is not returned, because no block commits to it.
Receipts and transactions
Section titled “Receipts and transactions”These methods return the same fields as a node, including chain-specific fields: the deposit and L1 fee fields on the OP Stack chains (Base, OP Mainnet, Unichain, and Ink), and the Nitro fields on Arbitrum One and Robinhood Chain.
eth_getBlockReceiptstakes a block number, a tag, a block hash, or{"blockHash": …}/{"blockNumber": …}(EIP-1898).eth_getTransactionByHashandeth_getTransactionReceiptreturn any transaction in a block Willow holds.eth_getTransactionByBlockNumberAndIndexandeth_getTransactionByBlockHashAndIndexreturnnullfor an index beyond the block’s last transaction.- Ethereum receipts before the Byzantium fork (block 4,370,000) contain
root, the post-state root, instead ofstatus, as committed in the block and as served by Geth. Some providers return astatuscomputed by replaying the block; the block does not commit to that value, so it cannot be verified.
A request for a transaction hash Willow does not hold is refused with -32000
instead of returning null, so a router can query a provider with more
history.
Every transaction and receipt these methods return can be
proven against its block hash.
Request:
{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt","params":["0x4560d377ceb62f875b054d83a86caabe34e7f681271f2d204ac692a472cf987c"]}Response (truncated):
{ "id": 1, "jsonrpc": "2.0", "result": { "blockHash": "0xdb98a28f352acb72751ec8012c5a617acfb2d7469aadb9bc4690db6b6dd8d53f", "blockNumber": "0x18e8f2a", "contractAddress": null, "cumulativeGasUsed": "0x3b309", "effectiveGasPrice": "0x9df1979", "from": "0x5875db54cd9ae2b2a875e09bb731772297ae9d92", "gasUsed": "0x3b309", "logs": [ … 7 logs … ], "logsBloom": "0x0000000000000000040000000000000000000000000000000000000000000000…", "status": "0x1", "to": "0x0000000aa232009084bd71a5797d089aa4edfad4", "transactionHash": "0x4560d377ceb62f875b054d83a86caabe34e7f681271f2d204ac692a472cf987c", "transactionIndex": "0x0", "type": "0x2" }}eth_chainId, net_version
Section titled “eth_chainId, net_version”{"id":1,"jsonrpc":"2.0","result":"0x1"}{"id":2,"jsonrpc":"2.0","result":"1"}Batches
Section titled “Batches”A batch is a JSON array of 1 to 100 requests. Each item is served or refused independently, and the response preserves the request order, so a refused item does not affect the others. Each item counts as one request against the rate limit and is metered separately.
[{"id":1,"jsonrpc":"2.0","result":"0x1"}, {"id":2,"jsonrpc":"2.0","result":"0x18e8f87"}, {"error":{"code":-32601,"message":"the method eth_newFilter does not exist/is not available"},"id":3,"jsonrpc":"2.0"}]The eth_getLogs items of one HTTP request share a reading budget. An item
that exceeds the remaining budget is refused with -32002, and the message
specifies a range to send in another request. The preceding items are served
normally.
A batch of more than 100 items, or an empty batch, is rejected as a whole with a single error object:
{"error":{"code":-32600,"message":"batch of 101 exceeds 100"},"id":null,"jsonrpc":"2.0"}