Skip to content

docs: add public testnet RPC guide for gas caps and rate limits - #299

Open
kutluhaneth46 wants to merge 3 commits into
circlefin:mainfrom
kutluhaneth46:docs/public-testnet-rpc-gascap-fallbacks
Open

docs: add public testnet RPC guide for gas caps and rate limits#299
kutluhaneth46 wants to merge 3 commits into
circlefin:mainfrom
kutluhaneth46:docs/public-testnet-rpc-gascap-fallbacks

Conversation

@kutluhaneth46

@kutluhaneth46 kutluhaneth46 commented Aug 31, 2026

Copy link
Copy Markdown

Summary

  • Add docs/public-testnet-rpc.md for application developers using Arc Testnet JSON-RPC
  • Document the 16,777,216 (2^24) EIP-7825 per-transaction cap (Osaka), how it differs from the node --rpc.gascap=30M default and the 30M block gas limit, and how to read gas-cap errors
  • List public fallback RPC endpoints and a viem fallback() pattern for -32011 concurrency limits
  • Cross-link from README.md, docs/running-an-arc-node.md, and BREAKING_CHANGES.md v0.7.2 gascap entry

Addresses #292 and #207.

Validation

  • Verified live: eth_getBlockByNumber latest gasLimit = 30,000,000 on rpc.testnet.arc.network
  • git diff --check passed (docs-only change)
  • Reviewed by @osr21 — EIP-7825 framing, error shapes, concurrency, viem arcTestnet (commit 2b925ec)

Test plan

Companion reference repo (same corrections): https://github.com/kutluhaneth46/arc-dev-survival-kit

Document the 16,777,216 eth_estimateGas clamp on rpc.testnet.arc.network, list fallback endpoints, and describe -32011 rate-limit handling. Addresses circlefin#292 and circlefin#207.

Co-authored-by: Cursor <cursoragent@cursor.com>
@osr21

osr21 commented Aug 31, 2026

Copy link
Copy Markdown

Thanks for writing this up — as the author of #292 this is close to home, so I re-verified the doc's claims against the repo and live endpoints at head 62fd9a1. The endpoint table, the viem sample, and the waitForTransactionReceipt caveat all check out (details below), but the doc's central framing of the 16,777,216 cap is wrong in a way that inverts its key advice — and it corrects my own #292 framing too.

The 2²⁴ cap is protocol-level (EIP-7825 via Osaka), not public-endpoint policy

  • Repo: ARC_TESTNET_HARDFORKS activates EthereumHardfork::Osaka at ForkCondition::Timestamp(1779890400) (≈2026-05-28), and crates/execution-config/src/hardforks.rs states "Zero5/Zero6 and Osaka are already activated on testnet/mainnet." EIP-7825's per-transaction cap is exactly 16,777,216.

  • Live (today): eth_estimateGas of a create whose init code is an infinite loop (0x5b600056) returns the identical error on four independently-operated infrastructuresrpc.testnet.arc.network, rpc.drpc.testnet.arc.io, rpc.blockdaemon.testnet.arc.io, rpc.quicknode.testnet.arc.io:

    {"code":-32003,"message":"out of gas: gas required exceeds: 16777216"}

    Per the peering discussion in bug: public RPC rate-limits (error -32011 / "request limit reached") break waitForTransactionReceipt, frontend approve calls, and keeper batch jobs — no official fallback documented #207, these providers run their own Arc node infrastructure (not proxies to a shared backend), so an "infrastructure policy" would have to be coordinated across all of them. An explicit "gas": 30000000 in the request is silently clamped to the same 2²⁴.

Consequences for the text:

  1. "Public testnet endpoints may apply a lower effective cap" — self-hosted v0.8.0 nodes cap identically. The effective single-transaction ceiling everywhere is min(--rpc.gascap, 16777216).
  2. "a transaction needing 17–30M gas may be valid on-chain even when the public endpoint refuses to estimate it"inverted post-Osaka: any transaction with a gas limit above 16,777,216 is invalid under EIP-7825. The 30M block gas limit means multiple transactions per block, not one large one. The advice to "retry against a node you control with a higher --rpc.gascap, or submit with an explicit gas limit" cannot work above 2²⁴.
  3. The diagnostic-table row 30000000 → Node --rpc.gascap default can no longer bind for a single-tx estimate — the 7825 cap always undercuts it.
  4. Your unchecked test-plan item ("Maintainer confirms 16M public-endpoint cap is intentional infrastructure policy") is answerable from the repo: it's the Zero5/Osaka hardfork, no maintainer confirmation needed. (Full disclosure: my Public testnet RPC clamps eth_estimateGas at 16,777,216 (2^24), below the documented 30M --rpc.gascap default and the 30M block gas limit #292 framed this as an endpoint clamp too — I've posted a correction there.)

Error-shape precision

The quoted -32000 / gas required exceeds allowance (16777216) is one of several shapes. In the request shapes I probed today (with and without fee fields), the cap surfaces as -32003 / out of gas: gas required exceeds: 16777216; the allowance (N) string is primarily the balance-derived shape (the v0.8.0 error-text taxonomy is being tracked in #291/#297). Tooling should pattern-match both strings, and "whose intrinsic gas exceeds" should read "whose required gas exceeds" — intrinsic gas is only the calldata/creation floor.

Rate limiting: it's a concurrency limit, not a request rate

Measured in the #207/#222 threads: serialized requests at ~4/s are never rejected; 2 requests in flight lose 1; a JSON-RPC batch of N loses N−1 individually behind an HTTP 200; the header is x-ratelimit-limit: 1;w=1 (one request in flight). So the actionable guidance is: serialize on one keep-alive connection and avoid Promise.all fan-out or batching against a single endpoint — fallback across providers helps because their budgets are independent, but it doesn't fix a fan-out pattern.

Two refinements to the viem section:

  • viem's fallback() does advance past -32011 (its shouldThrow only halts on rejected/reverted transactions) — stronger than "retries on transport-level errors". Your waitForTransactionReceipt caveat is exactly right, though: the receipt poll rejects above the transport, so application-level retry is still required there.
  • -32011 is wrapped in different viem error classes depending on call path (RpcRequestError for receipt polls, but estimate/call paths can surface it as ContractFunctionRevertedError). Detection should walk the cause chain for the code/message rather than using instanceof.

Endpoints & sample verified

All 7 hosts in the table are live (eth_chainId0x4cef52 on each). Two more also answer: rpc.quicknode.testnet.arc.io and the third-party arc-testnet.drpc.org; wss://rpc.testnet.arc.io additionally supports eth_subscribe. And viem ships arcTestnet in viem/chains (verified in 2.52.2, defaulting to the .network hosts for primary/quicknode/blockdaemon), so the sample's import works out of the box — the "or your chain definition" hedge isn't needed.

One scope note: docs/ in this repo has historically been node-operator material, with app-developer content living on developers.circle.com — #297 landed the cross-link approach in BREAKING_CHANGES.md for that reason. If maintainers prefer trimming, the part that matters to operators too is the EIP-7825 correction: post-Osaka, --rpc.gascap no longer bounds single-transaction estimates the way the v0.7.2 entry implies.

Reframe 16,777,216 as protocol-level EIP-7825 (Osaka), not public-endpoint policy. Document both -32003 and -32000 error shapes, concurrency limiting (not RPS), additional endpoints, and viem arcTestnet import.

Co-authored-by: Cursor <cursoragent@cursor.com>
@kutluhaneth46

Copy link
Copy Markdown
Author

@osr21 Thanks for the thorough review — pushed 1794409 addressing all points:

  • EIP-7825 framing: 16,777,216 is now documented as the Osaka protocol cap (min(--rpc.gascap, 2²⁴) everywhere), not public-endpoint policy. Removed the inverted "17–30M may be valid on-chain" advice; 30M is block budget across multiple txs.
  • Error shapes: Both -32003 / out of gas: gas required exceeds: 16777216 and -32000 / allowance (N) are documented.
  • Concurrency limiting: Renamed section; x-ratelimit-limit: 1;w=1, serialize per connection, avoid Promise.all fan-out.
  • viem: Sample uses arcTestnet from viem/chains; notes that fallback() advances past -32011 and detection should walk the cause chain.
  • Endpoints: Added rpc.quicknode.testnet.arc.io and arc-testnet.drpc.org.
  • BREAKING_CHANGES.md: v0.7.2 cross-link updated to match the protocol-cap framing.

Companion reference repo (same corrections): https://github.com/kutluhaneth46/arc-dev-survival-kit

@osr21

osr21 commented Aug 31, 2026

Copy link
Copy Markdown

Verified 1794409 against the diff — all substantive points from my review are addressed correctly:

  • EIP-7825 documented as the protocol cap with the min(--rpc.gascap, 16_777_216) ceiling; the inverted "17–30M may be valid on-chain" advice is gone and the 30M block budget is correctly framed as multi-transaction. ✔
  • Both error families documented with the guidance to pattern-match rather than string-match one shape. ✔
  • Concurrency section now matches the measured behavior (x-ratelimit-limit: 1;w=1, serialized ~4/s clean, batch of N loses N−1, serialize-per-endpoint guidance). ✔
  • viem section: bare arcTestnet import, fallback() advancing past -32011, cause-chain detection, waitForTransactionReceipt caveat. ✔
  • Endpoint table now includes rpc.quicknode.testnet.arc.io and arc-testnet.drpc.org. ✔
  • BREAKING_CHANGES.md cross-link reframed to the protocol cap. ✔

Also a good call keeping the companion-repo link in the PR conversation rather than in the docs — this repo's docs have stayed free of third-party project links.

Three small precision nits, none blocking:

  1. "activates EIP-7825 (Osaka) via the Zero5/Zero6 hardfork" — Osaka is a distinct fork entry in ARC_TESTNET_HARDFORKS, activated at timestamp 1779890400, about 69 minutes before Zero5/Zero6 (1779894517); the chainspec pairs it with Zero5 "per convention" but they are separate activations (and Zero6 is unrelated — it gates the PQ precompiles). Suggest "at the Osaka hardfork (activated alongside Zero5)".
  2. Error-shape table, second row — the example string gas required exceeds allowance (16777216) is labeled "balance-derived allowance clamp", but when the clamp is balance-derived, N is the sender's gas budget (usually ≠ 2²⁴). The cap-valued allowance (16777216) was the shape observed on the primary endpoint earlier this summer (Public testnet RPC clamps eth_estimateGas at 16,777,216 (2^24), below the documented 30M --rpc.gascap default and the 30M block gas limit #292), i.e. a cap-side occurrence. Using a generic gas required exceeds allowance (<N>) in that row avoids implying the balance path produces the cap value.
  3. "The .arc.io hosts are the same provider set documented for node follow mode" — the operator doc names three .io hosts (primary/dRPC/Blockdaemon); rpc.quicknode.testnet.arc.io answers but isn't part of that documented set, so "overlap with" would be more accurate than "are the same".

One operational note, same as on #297: the Rust Integration Tests CI job currently fails marginally across unrelated branches (#298 — nondeterministic OOM in a 7-node test). A red run on this docs-only PR once CI is approved is almost certainly that, not your change.

Content-wise this now matches everything I can verify from the repo and live endpoints. Whether an app-developer guide lands in docs/ is the maintainers' scope call, but the corrected EIP-7825 material is relevant to operators as well — the v0.7.2 gascap guidance genuinely changed meaning post-Osaka.

Co-authored-by: Cursor <cursoragent@cursor.com>
@kutluhaneth46

Copy link
Copy Markdown
Author

@osr21 Thanks — pushed another commit addressing the three nits: Osaka vs Zero5/Zero6 wording, generic allowance error shape in the table, and overlap (not identity) for the .arc.io follow set vs QuickNode. Ready for maintainer review when you have a moment.

@kutluhaneth46

Copy link
Copy Markdown
Author

Opened a community showcase thread for the companion survival kit (external to docs, per review preference): #305 — feedback welcome on whether this belongs in developers.circle.com or stays community-hosted.

@osr21

osr21 commented Sep 1, 2026

Copy link
Copy Markdown

Verified 2b925ec — all three nits are addressed exactly:

  1. Osaka wording — now "at the Osaka hardfork (activated alongside Zero5)", which matches the chainspec pairing discussed earlier. ✔️
  2. Error-shape table — the allowance row is now the generic gas required exceeds allowance (<N>) with both derivations named, no longer implying the balance path produces the cap value. ✔️
  3. Follow-set sentence — "overlap with … (primary, DRPC, Blockdaemon); QuickNode answers on .arc.io but is not part of that documented follow set" is precisely right: running-an-arc-node.md at your head documents exactly those three --follow.endpoint hosts and no QuickNode. ✔️

Two live datapoints from today (2026-09-02) that independently support lines in the doc, for the record:

  • An eth_estimateGas with explicit "gas": 30000000 succeeded and returned a normal estimate on the primary endpoint — consistent with the doc's "silently clamped to 2²⁴" line (no error surfaces when the actual gas need fits under the cap).
  • A single eth_chainId to rpc.quicknode.testnet.arc.io came back -32011 ("request limit reached") — the shared public budget was already saturated by other traffic. Good real-world backing for the doc's advice that clients must treat -32011 as routine, even on a first call.

On the placement question: I left a full verification + placement answer on #305 (here). Short version: the copy-paste kit works best community-hosted — this repo's docs are node-operator scoped, and the durable upstream piece is this PR; developers.circle.com is the canonical surface if the content (not the kit) graduates to official app-dev docs.

From my side the doc is fully verified at 2b925ec; nothing further. Maintainer review is the remaining gate, and the earlier note stands: a red Rust Integration Tests run on this docs-only branch would almost certainly be the #298 OOM flake, not this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants