Skip to content

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.

MethodCoverage
eth_getLogsAny filter: any address, any topics, or none, over any block range. See eth_getLogs.
eth_blockNumberThe newest block Willow holds for that chain.
eth_getBlockByNumber, eth_getBlockByHashWith transaction hashes (false) or full transactions (true).
eth_getBlockReceipts, eth_getTransactionByHash, eth_getTransactionReceipt, eth_getTransactionByBlockNumberAndIndex, eth_getTransactionByBlockHashAndIndexEvery block and transaction Willow holds.
eth_getBlockTransactionCountByNumber, eth_getBlockTransactionCountByHashEvery block Willow holds.
eth_chainId, net_versionAlways.
eth_getBalance, eth_getTransactionCount, eth_getCode, eth_getStorageAt, eth_getProof, eth_callAccount 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_blobBaseFeeForwarded to a provider. On plans that include state methods. See State methods.
willow_quoteLogsProof, willow_getLogsProof, willow_getProof, willow_getProofJobLog proofs. See Log proof methods.
willow_getTransactionProof, willow_getReceiptProof, willow_getBlockReceiptsProofTransaction and receipt proofs. See Transaction and receipt proofs.
willow_historyStatus, willow_historyCancelFetches 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.

The filter object behaves as it does on a standard node.

  • address is optional. It is a single address or a list. An empty list matches any address.
  • topics contains up to four positions. Each is a value, a list of alternatives, or null for any value.
  • The block range is specified by fromBlock and toBlock (hexadecimal numbers or tags), or by blockHash alone. Combining blockHash with fromBlock or toBlock returns -32602. An omitted fromBlock or toBlock defaults to latest.
  • latest and pending both refer to the newest block Willow holds. earliest refers to block 0. finalized and safe are described below.
  • Willow never returns a partial range. If it cannot serve every block in the range, the entire request is refused.

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_getLogs items 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 maxLogsRange of them in one call and maxLogsBlocksPerRequest in one HTTP request, both reported in /v1/rpc/health. Beyond either limit, the request is refused with -32002 and range too large.
  • Two concurrent calls. An account may have two eth_getLogs calls 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.
ChainmaxLogsRangemaxLogsBlocksPerRequest
Ethereum, Base, OP Mainnet1001,000
Ink1,00010,000
Robinhood Chain3,00030,000
Unichain5,00050,000
Arbitrum One10,000100,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:

Terminal window
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 on Ethereum is the block that the Ethereum light client (Helios) verified as finalized.
  • finalized on 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.
  • safe on 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. safe is refused on Ethereum, Arbitrum One, and Robinhood Chain.
  • Either tag is refused with -32000 while it cannot be verified. See How Willow verifies data.

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.

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.

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_getBlockReceipts takes a block number, a tag, a block hash, or {"blockHash": …} / {"blockNumber": …} (EIP-1898).
  • eth_getTransactionByHash and eth_getTransactionReceipt return any transaction in a block Willow holds.
  • eth_getTransactionByBlockNumberAndIndex and eth_getTransactionByBlockHashAndIndex return null for 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 of status, as committed in the block and as served by Geth. Some providers return a status computed 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"
}
}
{"id":1,"jsonrpc":"2.0","result":"0x1"}
{"id":2,"jsonrpc":"2.0","result":"1"}

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"}