From 62fd9a1dd2e4d4780117f525b947f6de49dc752f Mon Sep 17 00:00:00 2001 From: airdropzamani Date: Tue, 1 Sep 2026 01:19:45 +0300 Subject: [PATCH 1/3] docs: add public testnet RPC guide for gas caps and rate limits Document the 16,777,216 eth_estimateGas clamp on rpc.testnet.arc.network, list fallback endpoints, and describe -32011 rate-limit handling. Addresses #292 and #207. Co-authored-by: Cursor --- BREAKING_CHANGES.md | 1 + README.md | 1 + docs/public-testnet-rpc.md | 109 ++++++++++++++++++++++++++++++++++++ docs/running-an-arc-node.md | 3 + 4 files changed, 114 insertions(+) create mode 100644 docs/public-testnet-rpc.md diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index 81f831ca..e8a938a1 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -55,6 +55,7 @@ No breaking changes in this release. - Old (`v0.7.1`): `--rpc.gascap` default `50000000` (Reth stock default). - New (`v0.7.2`): `--rpc.gascap` default `30000000`. - `eth_call` and `eth_estimateGas` requests that need more than 30M gas now fail with a gas-cap error. Pass `--rpc.gascap 50000000` (or higher) to restore the previous budget. Operators who never set the flag and do not rely on calls above 30M gas are unaffected. + - Application developers using public RPC endpoints should read [docs/public-testnet-rpc.md](docs/public-testnet-rpc.md) — public infrastructure may apply a lower effective cap than this node default. - **[CLI] `arc-node-execution`: replay-unprotected (pre-EIP-155) transactions are rejected over JSON-RPC by default.** - Old (`v0.7.1`): pre-EIP-155 (replay-unprotected) transactions were accepted over JSON-RPC. diff --git a/README.md b/README.md index 77c91008..e0645e41 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Arc is an open EVM-compatible layer 1 built on [Malachite](https://github.com/ci - šŸš€ **[Execution](crates/node/README.md)** - Execution binary and configuration - šŸ—³ļø **[Consensus](crates/malachite-app/README.md)** - Consensus binary and configuration +- 🌐 **[Public Testnet RPC](docs/public-testnet-rpc.md)** - Endpoint list, gas-estimation limits, and rate-limit fallbacks - More: see Arc [developer docs](https://docs.arc.io/arc/concepts/welcome-to-arc) for guides, APIs, and specs ## Install and Run a Node diff --git a/docs/public-testnet-rpc.md b/docs/public-testnet-rpc.md new file mode 100644 index 00000000..97277dce --- /dev/null +++ b/docs/public-testnet-rpc.md @@ -0,0 +1,109 @@ +# Arc Testnet public RPC + +Reference for application developers using the Arc Testnet JSON-RPC endpoints. +For node-operator follow/forwarder configuration, see [Running an Arc Node](running-an-arc-node.md). + +## Endpoints + +Arc Testnet (chain ID `5042002`) exposes multiple public HTTPS endpoints. +Distribute traffic across them to reduce rate-limit errors (see [Rate limiting](#rate-limiting)). + +| URL | Notes | +| --- | --- | +| `https://rpc.testnet.arc.network` | Primary public endpoint | +| `https://rpc.testnet.arc.io` | Alternate public endpoint | +| `https://rpc.drpc.testnet.arc.network` | DRPC provider | +| `https://rpc.drpc.testnet.arc.io` | DRPC provider (alternate host) | +| `https://rpc.blockdaemon.testnet.arc.network` | Blockdaemon provider | +| `https://rpc.blockdaemon.testnet.arc.io` | Blockdaemon provider (alternate host) | +| `https://rpc.quicknode.testnet.arc.network` | QuickNode provider | + +These URLs are community-facing read/write endpoints. +The `.arc.io` hosts are the same provider set documented for node follow mode in +[Running an Arc Node](running-an-arc-node.md); the `.arc.network` hosts are the +URLs most sample apps and wallets default to. + +## Gas estimation limits + +### Node default vs public endpoints + +Self-hosted `arc-node-execution` defaults to `--rpc.gascap=30000000` (30M gas). +That cap applies to `eth_call`, `eth_estimateGas`, and related simulation paths. +It is a **node-local RPC budget**, not the protocol block gas limit. + +Public testnet endpoints may apply a **lower effective cap**. +As of 2026-08, `https://rpc.testnet.arc.network` rejects `eth_estimateGas` requests +whose intrinsic gas exceeds **16,777,216 (2²⁓)** with: + +```json +{"code":-32000,"message":"gas required exceeds allowance (16777216)"} +``` + +The parenthesized limit is the key diagnostic: + +| Observed `` | Likely cause | +| --- | --- | +| `16777216` | Public-endpoint or EIP-7825 per-transaction gas cap | +| `30000000` | Node `--rpc.gascap` default | +| Other value | Compare against the operator's configured `--rpc.gascap` | + +The block gas limit on Arc Testnet is 30M (`eth_getBlockByNumber("latest").gasLimit`), +so a transaction needing 17–30M gas may be valid on-chain even when the public +endpoint refuses to estimate it. + +**Do not treat an `eth_estimateGas` failure as proof that a transaction is +impossible on-chain.** Retry against a node you control with a higher +`--rpc.gascap`, or submit with an explicit `gas` limit when you have validated the +budget another way. + +The same `gas required exceeds allowance ()` string is also returned when +the sender's balance-derived gas allowance is exhausted — compare `` against +the configured cap to disambiguate. See [BREAKING_CHANGES.md](../BREAKING_CHANGES.md#v072). + +### EIP-7825 per-transaction cap + +Arc Testnet enforces the EIP-7825 (Osaka) per-transaction gas limit of +16,777,216 (2²⁓). This protocol cap is independent of the RPC gas cap and applies +whether you estimate through a public endpoint or a self-hosted node. + +## Rate limiting + +Public endpoints enforce per-connection request rate limits. +When exceeded, the JSON-RPC error is: + +```json +{"code":-32011,"message":"request limit reached"} +``` + +Depending on the client layer, this surfaces as: + +- viem `RpcRequestError` with `shortMessage: "RPC Request failed."` +- MetaMask toasts that look like a contract revert (`"Request is being rate limited"`) +- Partial failures inside JSON-RPC batch responses + +`viem`'s `fallback()` transport retries on transport-level errors across providers, +which reduces how often `-32011` fires for read calls: + +```ts +import { createPublicClient, fallback, http } from "viem"; +import { arcTestnet } from "viem/chains"; // or your chain definition + +const RPC_URLS = [ + "https://rpc.testnet.arc.network", + "https://rpc.drpc.testnet.arc.network", + "https://rpc.blockdaemon.testnet.arc.network", + "https://rpc.quicknode.testnet.arc.network", +]; + +export const publicClient = createPublicClient({ + chain: arcTestnet, + transport: fallback(RPC_URLS.map((url) => http(url))), +}); +``` + +`waitForTransactionReceipt` still aborts on the first `-32011` poll unless you wrap +it with application-level retry — a rate-limited receipt poll does not mean the +transaction failed on-chain. + +For production workloads, run your own node or use a dedicated RPC provider with +higher rate limits rather than relying on the shared public endpoints alone. diff --git a/docs/running-an-arc-node.md b/docs/running-an-arc-node.md index 58e7e5b3..a19731e8 100644 --- a/docs/running-an-arc-node.md +++ b/docs/running-an-arc-node.md @@ -225,6 +225,9 @@ companion execution layer. The consensus layer operates in the **follow** mode. We provide three endpoints from which the node retrieves finalized blocks. +Application developers using public JSON-RPC (gas caps, rate limits, fallback URLs) +should see [Public Testnet RPC](public-testnet-rpc.md). + ### Verify operation After starting both the consensus and execution layer, wait about 30 seconds. From 1794409d2bf4d8a0f8f51fc3e07c5818a9876c08 Mon Sep 17 00:00:00 2001 From: airdropzamani Date: Tue, 1 Sep 2026 02:10:47 +0300 Subject: [PATCH 2/3] docs: correct EIP-7825 gas cap framing per review 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 --- BREAKING_CHANGES.md | 2 +- docs/public-testnet-rpc.md | 100 +++++++++++++++++++++---------------- 2 files changed, 57 insertions(+), 45 deletions(-) diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index e8a938a1..15388e91 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -55,7 +55,7 @@ No breaking changes in this release. - Old (`v0.7.1`): `--rpc.gascap` default `50000000` (Reth stock default). - New (`v0.7.2`): `--rpc.gascap` default `30000000`. - `eth_call` and `eth_estimateGas` requests that need more than 30M gas now fail with a gas-cap error. Pass `--rpc.gascap 50000000` (or higher) to restore the previous budget. Operators who never set the flag and do not rely on calls above 30M gas are unaffected. - - Application developers using public RPC endpoints should read [docs/public-testnet-rpc.md](docs/public-testnet-rpc.md) — public infrastructure may apply a lower effective cap than this node default. + - Application developers should read [docs/public-testnet-rpc.md](docs/public-testnet-rpc.md) — post-Osaka (EIP-7825) the per-transaction gas limit is 16,777,216 (2²⁓), which undercuts this default for single-transaction estimates. - **[CLI] `arc-node-execution`: replay-unprotected (pre-EIP-155) transactions are rejected over JSON-RPC by default.** - Old (`v0.7.1`): pre-EIP-155 (replay-unprotected) transactions were accepted over JSON-RPC. diff --git a/docs/public-testnet-rpc.md b/docs/public-testnet-rpc.md index 97277dce..e7de7e69 100644 --- a/docs/public-testnet-rpc.md +++ b/docs/public-testnet-rpc.md @@ -6,70 +6,75 @@ For node-operator follow/forwarder configuration, see [Running an Arc Node](runn ## Endpoints Arc Testnet (chain ID `5042002`) exposes multiple public HTTPS endpoints. -Distribute traffic across them to reduce rate-limit errors (see [Rate limiting](#rate-limiting)). +Spread traffic across providers and serialize concurrent requests per endpoint +(see [Concurrency limiting](#concurrency-limiting)). | URL | Notes | | --- | --- | | `https://rpc.testnet.arc.network` | Primary public endpoint | -| `https://rpc.testnet.arc.io` | Alternate public endpoint | +| `https://rpc.testnet.arc.io` | Alternate public endpoint; `wss://` supports `eth_subscribe` | | `https://rpc.drpc.testnet.arc.network` | DRPC provider | | `https://rpc.drpc.testnet.arc.io` | DRPC provider (alternate host) | | `https://rpc.blockdaemon.testnet.arc.network` | Blockdaemon provider | | `https://rpc.blockdaemon.testnet.arc.io` | Blockdaemon provider (alternate host) | | `https://rpc.quicknode.testnet.arc.network` | QuickNode provider | +| `https://rpc.quicknode.testnet.arc.io` | QuickNode provider (alternate host) | +| `https://arc-testnet.drpc.org` | Third-party DRPC endpoint | -These URLs are community-facing read/write endpoints. The `.arc.io` hosts are the same provider set documented for node follow mode in [Running an Arc Node](running-an-arc-node.md); the `.arc.network` hosts are the URLs most sample apps and wallets default to. ## Gas estimation limits -### Node default vs public endpoints +### EIP-7825 per-transaction cap (protocol) -Self-hosted `arc-node-execution` defaults to `--rpc.gascap=30000000` (30M gas). -That cap applies to `eth_call`, `eth_estimateGas`, and related simulation paths. -It is a **node-local RPC budget**, not the protocol block gas limit. +Arc Testnet activates EIP-7825 (Osaka) via the Zero5/Zero6 hardfork. The +**per-transaction gas limit is 16,777,216 (2²⁓)** on every node — public RPC, +self-hosted, and third-party infrastructure alike. -Public testnet endpoints may apply a **lower effective cap**. -As of 2026-08, `https://rpc.testnet.arc.network` rejects `eth_estimateGas` requests -whose intrinsic gas exceeds **16,777,216 (2²⁓)** with: +The effective ceiling for a single `eth_estimateGas` / `eth_call` is therefore: -```json -{"code":-32000,"message":"gas required exceeds allowance (16777216)"} +```text +min(--rpc.gascap, 16_777_216) ``` -The parenthesized limit is the key diagnostic: +Self-hosted `arc-node-execution` defaults to `--rpc.gascap=30000000` (30M), but +post-Osaka the protocol cap always undercuts it for a single transaction. Raising +`--rpc.gascap` alone cannot make a transaction above 2²⁓ gas valid. -| Observed `` | Likely cause | -| --- | --- | -| `16777216` | Public-endpoint or EIP-7825 per-transaction gas cap | -| `30000000` | Node `--rpc.gascap` default | -| Other value | Compare against the operator's configured `--rpc.gascap` | +The block gas limit on Arc Testnet is 30M (`eth_getBlockByNumber("latest").gasLimit`). +That budget is shared across **multiple transactions per block**, not one +large transaction. -The block gas limit on Arc Testnet is 30M (`eth_getBlockByNumber("latest").gasLimit`), -so a transaction needing 17–30M gas may be valid on-chain even when the public -endpoint refuses to estimate it. +### Error shapes -**Do not treat an `eth_estimateGas` failure as proof that a transaction is -impossible on-chain.** Retry against a node you control with a higher -`--rpc.gascap`, or submit with an explicit `gas` limit when you have validated the -budget another way. +Cap and budget failures surface under several JSON-RPC error texts. Tooling +should pattern-match both families rather than a single string: -The same `gas required exceeds allowance ()` string is also returned when -the sender's balance-derived gas allowance is exhausted — compare `` against -the configured cap to disambiguate. See [BREAKING_CHANGES.md](../BREAKING_CHANGES.md#v072). +| Shape | Typical code | When | +| --- | --- | --- | +| `out of gas: gas required exceeds: 16777216` | `-32003` | Gas budget exceeds EIP-7825 cap (common on cap probes) | +| `gas required exceeds allowance (16777216)` | `-32000` | Balance-derived allowance clamp (also seen on some paths) | +| Other `out of gas` / halt variants | varies | Cap/limit failures on older reth lineages | -### EIP-7825 per-transaction cap +An explicit `"gas": 30000000` in the request is silently clamped to 2²⁓ when the +protocol cap applies. -Arc Testnet enforces the EIP-7825 (Osaka) per-transaction gas limit of -16,777,216 (2²⁓). This protocol cap is independent of the RPC gas cap and applies -whether you estimate through a public endpoint or a self-hosted node. +**Do not treat an `eth_estimateGas` failure above 2²⁓ as proof that a higher gas +limit would succeed on-chain** — it would not. For failures below the protocol +cap, compare the parenthesized limit against `--rpc.gascap` and the sender's +balance-derived allowance. See [BREAKING_CHANGES.md](../BREAKING_CHANGES.md#v072). -## Rate limiting +## Concurrency limiting -Public endpoints enforce per-connection request rate limits. -When exceeded, the JSON-RPC error is: +Public endpoints enforce a **per-connection concurrency limit**, not a requests-per-second +rate. Observed behavior: + +- Serialized requests (~4/s on one connection) are not rejected. +- Two requests in flight on the same connection: one may receive `-32011`. +- JSON-RPC batches of *N* entries may return `-32011` on *Nāˆ’1* items behind HTTP 200. +- Response header: `x-ratelimit-limit: 1;w=1` (one request in flight). ```json {"code":-32011,"message":"request limit reached"} @@ -77,16 +82,23 @@ When exceeded, the JSON-RPC error is: Depending on the client layer, this surfaces as: -- viem `RpcRequestError` with `shortMessage: "RPC Request failed."` +- viem `RpcRequestError` on receipt polls (`shortMessage: "RPC Request failed."`) +- `ContractFunctionRevertedError` on some estimate/call paths - MetaMask toasts that look like a contract revert (`"Request is being rate limited"`) -- Partial failures inside JSON-RPC batch responses -`viem`'s `fallback()` transport retries on transport-level errors across providers, -which reduces how often `-32011` fires for read calls: +**Actionable guidance:** serialize RPC calls per endpoint (avoid `Promise.all` +fan-out on one URL), prefer keep-alive on a single connection, and use +`fallback()` across independent providers — each provider's concurrency budget +is separate. + +`viem`'s `fallback()` advances past `-32011` on most call paths (its `shouldThrow` +only halts on rejected/reverted transactions). Detection code should walk the +error `cause` chain for code `-32011` or the message rather than relying on a +single `instanceof`. ```ts import { createPublicClient, fallback, http } from "viem"; -import { arcTestnet } from "viem/chains"; // or your chain definition +import { arcTestnet } from "viem/chains"; const RPC_URLS = [ "https://rpc.testnet.arc.network", @@ -101,9 +113,9 @@ export const publicClient = createPublicClient({ }); ``` -`waitForTransactionReceipt` still aborts on the first `-32011` poll unless you wrap -it with application-level retry — a rate-limited receipt poll does not mean the +`waitForTransactionReceipt` still aborts on the first `-32011` poll unless wrapped +with application-level retry — a rate-limited receipt poll does not mean the transaction failed on-chain. -For production workloads, run your own node or use a dedicated RPC provider with -higher rate limits rather than relying on the shared public endpoints alone. +For production workloads, run your own node or use a dedicated RPC provider rather +than relying on the shared public endpoints alone. From 2b925ec9fbd002dd495fb9f6f2038b2bf41a6b58 Mon Sep 17 00:00:00 2001 From: airdropzamani Date: Wed, 2 Sep 2026 00:54:16 +0300 Subject: [PATCH 3/3] docs: address osr21 precision nits on EIP-7825 and endpoints Co-authored-by: Cursor --- docs/public-testnet-rpc.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/public-testnet-rpc.md b/docs/public-testnet-rpc.md index e7de7e69..61a06d0f 100644 --- a/docs/public-testnet-rpc.md +++ b/docs/public-testnet-rpc.md @@ -21,17 +21,18 @@ Spread traffic across providers and serialize concurrent requests per endpoint | `https://rpc.quicknode.testnet.arc.io` | QuickNode provider (alternate host) | | `https://arc-testnet.drpc.org` | Third-party DRPC endpoint | -The `.arc.io` hosts are the same provider set documented for node follow mode in -[Running an Arc Node](running-an-arc-node.md); the `.arc.network` hosts are the -URLs most sample apps and wallets default to. +The `.arc.io` hosts overlap with the provider set documented for node follow mode in +[Running an Arc Node](running-an-arc-node.md) (primary, DRPC, Blockdaemon); QuickNode +answers on `.arc.io` but is not part of that documented follow set. The `.arc.network` +hosts are the URLs most sample apps and wallets default to. ## Gas estimation limits ### EIP-7825 per-transaction cap (protocol) -Arc Testnet activates EIP-7825 (Osaka) via the Zero5/Zero6 hardfork. The -**per-transaction gas limit is 16,777,216 (2²⁓)** on every node — public RPC, -self-hosted, and third-party infrastructure alike. +Arc Testnet activates EIP-7825 (Osaka) at the Osaka hardfork (activated alongside +Zero5). The **per-transaction gas limit is 16,777,216 (2²⁓)** on every node — +public RPC, self-hosted, and third-party infrastructure alike. The effective ceiling for a single `eth_estimateGas` / `eth_call` is therefore: @@ -55,7 +56,7 @@ should pattern-match both families rather than a single string: | Shape | Typical code | When | | --- | --- | --- | | `out of gas: gas required exceeds: 16777216` | `-32003` | Gas budget exceeds EIP-7825 cap (common on cap probes) | -| `gas required exceeds allowance (16777216)` | `-32000` | Balance-derived allowance clamp (also seen on some paths) | +| `gas required exceeds allowance ()` | `-32000` | Allowance clamp (balance-derived `N`, or cap-valued `N` on some paths) | | Other `out of gas` / halt variants | varies | Cap/limit failures on older reth lineages | An explicit `"gas": 30000000` in the request is silently clamped to 2²⁓ when the