Summary
The four public testnet RPC endpoints referenced in docs/running-an-arc-node.md do not serve the same JSON-RPC method set, and two of them return two different, semantically contradictory error codes for byte-identical requests. A client written against the canonical rpc.testnet.arc.io cannot use EIP-1186 state proofs at all, while the same call succeeds on rpc.drpc.testnet.arc.io.
Capability matrix
Single (non-batch) requests, 3 consecutive samples each:
| Endpoint |
eth_getProof |
eth_createAccessList |
rpc.testnet.arc.io |
-32014 / -32601 (varies) |
-32601 ×3 |
rpc.drpc.testnet.arc.io |
OK ×3 |
OK ×3 |
rpc.quicknode.testnet.arc.io |
-32601 / -32014 (varies) |
-32601 ×3 |
rpc.blockdaemon.testnet.arc.io |
OK ×3 |
-32003 "Request method filtered" ×3 |
Also unavailable on the canonical endpoint: eth_simulateV1, eth_blobBaseFee, txpool_status (all -32601). eth_getBlockReceipts and eth_maxPriorityFeePerGas work.
The part that actually breaks clients: unstable error codes
Ten consecutive identical eth_getProof calls, same hostname, ~0.8s apart:
rpc.testnet.arc.io -32014 -32601 -32601 -32014 -32014 -32014 -32014 -32014 -32014 -32014
rpc.quicknode.testnet.arc.io -32601 -32601 -32601 -32601 -32601 -32014 -32014 -32601 -32014 -32014
So rpc.testnet.arc.io answered -32014 8/10 and -32601 2/10; quicknode split 6/4 the other way. These two codes mean opposite things to a client:
-32601 "method not supported" is permanent — a well-behaved client disables the code path or falls back.
-32014 "requested data not available" reads as transient/state-related — a client retries, or retries against a different block.
Whichever branch a library takes, it takes at random, per call. Retry logic and capability detection both become nondeterministic. Any capability probe run once at startup will cache whichever answer it happened to get.
Batch semantics diverge too
Batch of three, where the middle request is eth_createAccessList:
rpc.testnet.arc.io array of 3 -> id=1:OK, id=2:-32601, id=3:OK
rpc.drpc.testnet.arc.io array of 3 -> id=1:OK, id=2:OK, id=3:OK
rpc.quicknode.testnet.arc.io array of 3 -> id=1:OK, id=2:-32601, id=3:OK
rpc.blockdaemon.testnet.arc.io TOP-LEVEL ERROR (whole batch rejected): {"code":-32003,"message":"Request method filtered"}
Blockdaemon discards the entire batch and replies with a single error object ("id": null) instead of an array. JSON-RPC 2.0 §6 specifies a batch response as an array of individual responses, so a single filtered method silently kills the two unrelated calls beside it. Confirmed it is the method and not batching in general — a batch of eth_chainId + eth_blockNumber succeeds on that endpoint, and eth_getProof on its own succeeds there too.
Where this appears to come from
grep -rIn 'getProof\|get_proof\|create_access_list' --include='*.rs' crates/ returns nothing, so these are inherited from upstream reth rather than overridden in this repo, and rpc.drpc.testnet.arc.io returns real proofs for the same address and block tag. The state is therefore available on the network; the divergence looks like per-provider gateway policy rather than a node capability limit. That also explains the intra-endpoint variation, which is consistent with load-balancing across a fleet whose members do not answer identically.
This matters for the docs specifically because docs/running-an-arc-node.md lists these endpoints together as interchangeable --follow.endpoint values (lines ~211-213 and ~883-885), which reads as "equivalent".
Suggested resolution
Any one of these would remove the ambiguity:
- Serve a consistent method set across the documented endpoints, or
- Document per-endpoint capabilities so
eth_getProof consumers know to select drpc, and
- Return a stable code for an unsupported method on a given endpoint — pick
-32601 and keep it, so capability detection and retry logic are deterministic.
The batch behaviour on blockdaemon is worth raising with that provider separately; a filtered method should produce a per-request error inside the array, not a top-level rejection.
Reproduction
curl -s -X POST -H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"eth_getProof","params":["0x3600000000000000000000000000000000000000",["0x0"],"latest"]}' \
https://rpc.testnet.arc.io
Repeat ~10 times against each host and compare. Chain id 0x4cef52 (5042002) on all four.
Disclosure: I am external to Circle — an unaffiliated community contributor with no write access to this repository. This report is advisory only; measurements are from my own probing of the public endpoints and should be reproduced before acting on them.
Summary
The four public testnet RPC endpoints referenced in
docs/running-an-arc-node.mddo not serve the same JSON-RPC method set, and two of them return two different, semantically contradictory error codes for byte-identical requests. A client written against the canonicalrpc.testnet.arc.iocannot use EIP-1186 state proofs at all, while the same call succeeds onrpc.drpc.testnet.arc.io.Capability matrix
Single (non-batch) requests, 3 consecutive samples each:
eth_getProofeth_createAccessListrpc.testnet.arc.io-32014/-32601(varies)-32601×3rpc.drpc.testnet.arc.iorpc.quicknode.testnet.arc.io-32601/-32014(varies)-32601×3rpc.blockdaemon.testnet.arc.io-32003"Request method filtered" ×3Also unavailable on the canonical endpoint:
eth_simulateV1,eth_blobBaseFee,txpool_status(all-32601).eth_getBlockReceiptsandeth_maxPriorityFeePerGaswork.The part that actually breaks clients: unstable error codes
Ten consecutive identical
eth_getProofcalls, same hostname, ~0.8s apart:So
rpc.testnet.arc.ioanswered-320148/10 and-326012/10; quicknode split 6/4 the other way. These two codes mean opposite things to a client:-32601"method not supported" is permanent — a well-behaved client disables the code path or falls back.-32014"requested data not available" reads as transient/state-related — a client retries, or retries against a different block.Whichever branch a library takes, it takes at random, per call. Retry logic and capability detection both become nondeterministic. Any capability probe run once at startup will cache whichever answer it happened to get.
Batch semantics diverge too
Batch of three, where the middle request is
eth_createAccessList:Blockdaemon discards the entire batch and replies with a single error object (
"id": null) instead of an array. JSON-RPC 2.0 §6 specifies a batch response as an array of individual responses, so a single filtered method silently kills the two unrelated calls beside it. Confirmed it is the method and not batching in general — a batch ofeth_chainId+eth_blockNumbersucceeds on that endpoint, andeth_getProofon its own succeeds there too.Where this appears to come from
grep -rIn 'getProof\|get_proof\|create_access_list' --include='*.rs' crates/returns nothing, so these are inherited from upstream reth rather than overridden in this repo, andrpc.drpc.testnet.arc.ioreturns real proofs for the same address and block tag. The state is therefore available on the network; the divergence looks like per-provider gateway policy rather than a node capability limit. That also explains the intra-endpoint variation, which is consistent with load-balancing across a fleet whose members do not answer identically.This matters for the docs specifically because
docs/running-an-arc-node.mdlists these endpoints together as interchangeable--follow.endpointvalues (lines ~211-213 and ~883-885), which reads as "equivalent".Suggested resolution
Any one of these would remove the ambiguity:
eth_getProofconsumers know to select drpc, and-32601and keep it, so capability detection and retry logic are deterministic.The batch behaviour on blockdaemon is worth raising with that provider separately; a filtered method should produce a per-request error inside the array, not a top-level rejection.
Reproduction
Repeat ~10 times against each host and compare. Chain id
0x4cef52(5042002) on all four.Disclosure: I am external to Circle — an unaffiliated community contributor with no write access to this repository. This report is advisory only; measurements are from my own probing of the public endpoints and should be reproduced before acting on them.