# Networks > Source: https://docs.erpc.cloud/config/projects/networks > A network is a chain (`evm:1`, `evm:42161`, …) and how eRPC serves it — failsafe, selection, integrity, static responses, aliasing. > Format: machine-readable markdown export of the docs page above. > All collapsible AI sections are inlined and fully expanded. # Networks A network is a logical grouping of upstreams that serve one chain. eRPC discovers networks **lazily** — on first request to a network, every configured upstream that supports that chain is enrolled automatically. You only need to enumerate `networks[]` when you want to customize behavior (failsafe, selection policy, rate-limit budget, finality semantics, integrity checks, static responses, alias). **You can configure:** - **Chain identity** — `architecture: evm` + `evm.chainId`; optionally a friendly `alias` (e.g. `ethereum` instead of `evm/1`) - **Finality semantics** — `fallbackFinalityDepth`, dynamic block-time multipliers, `fallbackStatePollerDebounce` - **Failsafe** — `timeout`, `retry`, `hedge`, `consensus`, with `matchMethod` / `matchFinality` scoping - **Selection policy** — JS eval function to decide which upstreams handle which method - **Rate limits** — bind a `rateLimitBudget` enforced before any upstream is contacted - **Request directives defaults** — turn on `retryEmpty`, set a default `useUpstream`, etc. - **eth_getLogs and trace_filter** — proactive splitting + split-on-error + hard limits - **eth_sendRawTransaction** — idempotent broadcasting for safe retry/hedge - **Static responses** — canned answers for `(method, params)` pairs that no real upstream can serve - **Method classification overrides** — extend or replace the default cacheable-method table per network ## Minimum useful config Networks are lazy-loaded. The smallest explicit network is just `architecture` + `evm.chainId` plus whatever feature you want to override. **Config path:** `projects > networks[]` **YAML — `erpc.yaml`:** ```yaml projects: - id: main networks: - architecture: evm evm: chainId: 1 alias: ethereum # friendly URL: /main/ethereum instead of /main/evm/1 ``` **TypeScript — `erpc.ts`:** ```typescript import { createConfig } from "@erpc-cloud/config"; export default createConfig({ projects: [{ id: "main", networks: [{ architecture: "evm", evm: { chainId: 1 }, alias: "ethereum", }], }], }); ``` > **INFO** > The single-object `failsafe:` form (one object instead of an array) is accepted as shorthand for an array with one entry. The array form with `matchMethod: "*"` is what you want as soon as you need per-method scoping. ## Production config — network-level failsafe + selection + rate limit A realistic mainnet entry with a network-wide failsafe (covers retries across upstreams when any one is rate-limited), a hedge to short-circuit slow tails, and a network-level rate-limit budget. **Config path:** `projects > networks[]` **YAML — `erpc.yaml`:** ```yaml projects: - id: main networks: - architecture: evm evm: chainId: 1 rateLimitBudget: mainnet-network # enforced before any upstream is contacted failsafe: - matchMethod: "*" timeout: # Network timeout covers the full request lifecycle (every retry/hedge across upstreams) duration: 30s retry: maxAttempts: 3 delay: 0ms # 0 = no wait; immediately fail over to the next upstream hedge: # If the primary takes longer than 'delay', race a second copy on another upstream. delay: 200ms maxCount: 3 directiveDefaults: retryEmpty: true # retry empty responses unless the method is in retry.emptyResultAccept useUpstream: "alchemy-*|localnode-*" # default upstream filter; can be overridden per-request ``` **TypeScript — `erpc.ts`:** ```typescript import { createConfig } from "@erpc-cloud/config"; export default createConfig({ projects: [{ id: "main", networks: [{ architecture: "evm", evm: { chainId: 1 }, rateLimitBudget: "mainnet-network", failsafe: [{ matchMethod: "*", timeout: { duration: "30s" }, retry: { maxAttempts: 3, delay: "0ms" }, hedge: { delay: "200ms", maxCount: 3 }, }], directiveDefaults: { retryEmpty: true, useUpstream: "alchemy-*|localnode-*", }, }], }], }); ``` ## Lazy-load defaults Networks not listed under `networks[]` still work — they're discovered on first request and inherit from `networkDefaults`. Use this to apply baseline failsafe and directives across every chain in the project. **Config path:** `projects > networkDefaults` **YAML — `erpc.yaml`:** ```yaml projects: - id: main networkDefaults: # Applies to every network in this project — both statically listed and lazy-loaded. rateLimitBudget: my-default-budget failsafe: - matchMethod: "*" timeout: { duration: 30s } retry: { maxAttempts: 3, delay: 0ms } hedge: { delay: 200ms, maxCount: 3 } directiveDefaults: retryEmpty: true retryPending: false skipCacheRead: false # false | true | wildcard pattern (e.g. "memory*") networks: # ...network-specific overrides go here; deep-merged on top of networkDefaults ``` **TypeScript — `erpc.ts`:** ```typescript import { createConfig } from "@erpc-cloud/config"; export default createConfig({ projects: [{ id: "main", networkDefaults: { rateLimitBudget: "my-default-budget", failsafe: [{ matchMethod: "*", timeout: { duration: "30s" }, retry: { maxAttempts: 3, delay: "0ms" }, hedge: { delay: "200ms", maxCount: 3 }, }], directiveDefaults: { retryEmpty: true, retryPending: false, skipCacheRead: false, }, }, networks: [], }], }); ``` > **WARNING** > If a network has its own `failsafe:` defined, **none** of `networkDefaults.failsafe` is merged in — defaults are wholesale replaced, not deep-merged for that field. Same for `selectionPolicy`. Other top-level fields (`rateLimitBudget`, `directiveDefaults`) are deep-merged. `multiplexing` is also settable under `networkDefaults` and behaves like any other scalar field: a per-network `multiplexing` value wins; if absent, the default applies to every network in the project. ## Name aliasing Use a friendly alias instead of the `architecture/chainId` URL segment. Aliases are only available for statically-defined networks (lazy-loaded networks don't have one). ```yaml projects: - id: main networks: - { architecture: evm, evm: { chainId: 1 }, alias: ethereum } - { architecture: evm, evm: { chainId: 42161 }, alias: arbitrum } - { architecture: evm, evm: { chainId: 137 }, alias: polygon } ``` ```bash POST http://localhost:4000/main/ethereum POST http://localhost:4000/main/arbitrum POST http://localhost:4000/main/polygon ``` Aliases must contain only alphanumeric characters, dashes, and underscores. ## Static responses For chains that deviate from common client assumptions in ways no real upstream can serve. Example: a chain whose genesis block is at height `1` instead of `0` has no valid answer for `eth_getBlockByNumber("0x0", false)`. `staticResponses[]` returns a canned response for matching `(method, params)` pairs — no upstream is contacted. ```yaml projects: - id: main networks: - architecture: evm evm: chainId: 999 staticResponses: # Synthetic genesis block - method: eth_getBlockByNumber params: ["0x0", false] response: result: number: "0x0" hash: "0x0000000000000000000000000000000000000000000000000000000000000000" parentHash: "0x0000000000000000000000000000000000000000000000000000000000000000" # ...remaining block fields # Or return a JSON-RPC error instead of a result - method: some_unsupported_method params: [] response: error: code: -32601 message: "Method not found" data: # optional, attached as response.error.data hint: "use eth_call instead" ``` - `method` must match exactly. - `params` is matched by deep equality. Keys may be in any order; numbers compare by value (`1` matches `1.0`); hex strings compare literally (`"0x0"` ≠ `"0x00"`). - First-match-wins, in declaration order. - Exactly one of `response.result` / `response.error` must be set. - Matched requests skip cache, multiplexer, and upstream selection entirely. Hits are exported as `erpc_network_static_response_served_total`. - Internal state pollers (latest/finalized block lookups) still go to upstreams — they're not intercepted. ### Every field on `networks[]` | Field | Type | Notes | |---|---|---| | `architecture` | string (**required**) | `evm` (only supported value today). | | `evm` | object | EVM-specific config — see "evm.* fields". | | `alias` | string | Friendly URL segment for this network (`ethereum`, `arbitrum`). Only applies to static networks. Allowed characters: alphanumeric, `-`, `_`. | | `failsafe` | array | Per-network failsafe policies. Wraps the full request lifecycle (including any upstream-level retries). Accepts `matchMethod` and `matchFinality` per entry. | | `selectionPolicy` | object | `evalFunc` (JS, run on `evalInterval`) returning the ordered list of upstreams to use. Order IS the routing decision — position 0 = primary, missing = excluded. See "selectionPolicy fields". | | `directiveDefaults` | object | Default request directives applied if the request doesn't override them via header/query. | | `rateLimitBudget` | string | Bind to a budget from `rateLimiters.budgets[]`. Enforced **before** any upstream is contacted. | | `methods` | object | Per-network override of cacheable-method classification — see "Per-network method overrides". | | `multiplexing` | bool | Per-network override for the in-flight request deduplication. Default `true` (inherits global). When `false`, identical concurrent requests each hit upstreams independently. | | `staticResponses` | array | Canned `(method, params)` → response mappings — see "Static responses" above. | ### `evm.*` fields | Field | Default | Notes | |---|---|---| | `chainId` | required when `architecture: evm` | The chain's EIP-155 chain ID. | | `fallbackFinalityDepth` | auto-detected via `eth_getBlockByNumber("finalized")` | Used when an upstream doesn't expose the `finalized` tag. Finalized block = `latest - fallbackFinalityDepth`. Higher values are safer (more reorg-resistant) at the cost of cache hit rate. | | `fallbackStatePollerDebounce` | `5s` | Static debounce used for block polling until enough blocks have been observed to compute a dynamic block time. | | `dynamicBlockTimeDebounceMultiplier` | `0.7` | Multiplier on the observed block time to derive the dynamic polling debounce. `0.7` means polling at 70% of the block time (more frequent than chain tick) so the latest-block pointer stays fresh. Lower = more aggressive polling (fresher data, more upstream load); higher = gentler (less load, slightly more latency on tip-following). Fast chains like Arbitrum or BNB benefit from lower values (e.g. `0.5`); slow chains like Ethereum L1 can use higher (e.g. `0.9`). | | `blockUnavailableDelayMultiplier` | `0.8` | Multiplier on the observed block time used as the retry delay when ALL upstreams returned "block not available." Falls back to the static `retry.blockUnavailableDelay` until block time is known. Lower = shorter wait between retries (faster recovery, more polling pressure); higher = longer wait (less pressure, more latency). Fast chains benefit from lower values; slow chains can tolerate higher. | | `maxRetryableBlockDistance` | 128 | Cap on how far ahead of any upstream's known head a request can target before retries stop being attempted. When a request asks for a block that is beyond every upstream's latest tip, eRPC would otherwise retry indefinitely while upstreams catch up. Setting `maxRetryableBlockDistance` limits how far ahead is still considered "catching up" — once the requested block exceeds the nearest upstream's tip by more than this value, eRPC fails fast with a missing-data error instead of looping. Increase for very slow-syncing chains; decrease to surface indexing gaps faster. | | `idempotentTransactionBroadcast` | `true` | When `true`, duplicate-transaction errors on `eth_sendRawTransaction` are converted to successful responses by re-computing the tx hash. Lets retry/hedge be safe for transaction broadcast. | | `markEmptyAsErrorMethods` | none | List of methods where an empty response should be treated as an error (and thus retried/rotated). For example, `["eth_getTransactionReceipt"]` on a chain where empty receipts indicate the tx is missing rather than pending. | | `getLogsMaxAllowedRange` | none | Hard limit on `eth_getLogs` block range. Requests beyond this are rejected with a 413-style error before being sent. | | `getLogsMaxAllowedAddresses` | none | Hard limit on the length of the `address` array in `eth_getLogs`. | | `getLogsMaxAllowedTopics` | none | Hard limit on `topics[0]` OR-list length. | | `getLogsSplitOnError` | `true` | When `true` and an upstream returns "too many results" / 413-style errors, retry by splitting the range, then the addresses, then `topics[0]`. Results merged server-side. | | `getLogsSplitConcurrency` | `16` | Parallelism cap for split sub-requests. | | `traceFilterSplitOnError` | `false` | Same idea as `getLogsSplitOnError` for `trace_filter` / `arbtrace_filter`. | | `traceFilterSplitConcurrency` | `10` | Parallelism cap for trace-filter splits. | | `enforceBlockAvailability` | nil (= `true`) | Network-level toggle for block-availability enforcement. `nil` or `true` means upstreams are skipped when the requested block falls outside their `evm.blockAvailability` bounds. Set `false` to globally disable the filter for this network — upstreams will be tried regardless of their declared availability bounds. For per-method control, override `enforceBlockAvailability` inside `methods.definitions.`. | ### Per-network method overrides — `methods.*` By default, eRPC uses a built-in cacheable-method table (see [evmJsonRpcCache → default methods](/config/database/evm-json-rpc-cache.llms.txt#default-cacheable-methods)). You can extend or replace it per network: ```yaml networks: - architecture: evm evm: { chainId: 999 } methods: # When true (default), per-network entries augment the global defaults. # Set false to ENTIRELY REPLACE the defaults with the definitions below. preserveDefaultMethods: true definitions: # Add a chain-specific RPC method that's not in the default table custom_specialQuery: finalized: true # Override an existing default: don't translate `latest` tag to a number for this method eth_blockNumber: translateLatestTag: false ``` Each entry in `definitions` is a `CacheMethodConfig` with the following fields: | Field | Type | Default | Notes | |---|---|---|---| | `finalized` | bool | `false` | When `true`, responses are treated as finalized data — cached indefinitely under the `finalized` finality state. Use for immutable point-lookups (`eth_getBlockByHash`, `eth_getTransactionByHash` for a confirmed tx). | | `realtime` | bool | `false` | When `true`, this method observes live mempool state. Realtime responses are not cached. | | `stateful` | bool | `false` | When `true`, the response depends on caller-specific state. Disables multiplexing — each caller gets its own upstream call. Use for custom RPC methods that tie results to connection-level session context. | | `reqRefs` | `[][]string` | — | JSON-path segments pointing to block-reference fields in the **request** params. Used for finality classification, cache key construction, and block-availability filtering. Example: `[[1]]` means second param is the block number. | | `respRefs` | `[][]string` | — | JSON-path segments pointing to block-reference fields in the **response** result. Used to extract the canonical block number from the response object (e.g. `[["blockNumber"]]` for a transaction). | | `translateLatestTag` | bool | `true` | When `true`, the `latest` tag in this method's request is rewritten to a concrete hex block number before caching. Set `false` when `latest` should be preserved as-is in the cache key. | | `translateFinalizedTag` | bool | `true` | Same as `translateLatestTag` but for the `finalized` tag. | | `enforceBlockAvailability` | bool | `true` | Per-method override of the network-level `evm.enforceBlockAvailability`. Set `false` to skip block-availability filtering for this method only — useful for methods without a meaningful block parameter that would otherwise be filtered. | **When to use `stateful: true`.** Mark a method stateful when its result depends on caller-supplied session state — for example, a custom RPC method `tenant_query` that takes a session token as a parameter and returns data scoped to that caller. Without `stateful: true`, eRPC can multiplex different callers' requests through the same upstream connection, which can yield cross-tenant cache hits or wrong results if the upstream ties its response to the connection's session context. Setting `stateful: true` disables request multiplexing for that method. ```yaml networks: - architecture: evm evm: { chainId: 999 } methods: definitions: tenant_query: stateful: true # each caller gets a dedicated upstream connection ``` ### `selectionPolicy` fields ```yaml selectionPolicy: evalInterval: 15s # how often to re-evaluate eligibility (default 15s) evalTimeout: 100ms # per-tick eval deadline (must be < evalInterval; prior cache retained on timeout) evalScope: network # 'network' (default) | 'network-method' | 'network-finality' | 'network-method-finality' evalFunc: | (upstreams, ctx) => upstreams .removeCordoned() .excludeIf(all(samplesAbove(10), errorRateAbove(0.7))) .whenEmpty(() => upstreams) .preferTag('!tier:fallback', { minHealthy: 1, fallback: 'tier:fallback' }) .sortByScore(PREFER_FASTEST) .stickyPrimary({ hysteresis: 0.30, minSwitchInterval: '30s' }) .probeExcluded({ sampleRate: 0.1, minSamples: 10, maxConcurrent: 4, timeout: '10s' }) ``` `evalFunc` returns an **ordered** `Upstream[]`: position 0 is the primary, the rest are retry order, anything missing is excluded. Use the chainable stdlib (`removeCordoned`, `excludeIf`, `whenEmpty`, `preferTag`, `sortByScore`, `stickyPrimary`, `probeExcluded`, …) — see [Selection policy](/config/projects/selection-policies.llms.txt) for the full DSL + every method. Default policy: omitting `selectionPolicy` (or `evalFunc`) applies a production-hardened chain-agnostic default — `removeCordoned` + `excludeIf` filters (error / throttle rates each gated on ≥ 10 samples, p70 latency deviation gated on ≥ 20 samples, block-head lag) + tier-based fallback via `preferTag('!tier:fallback')` + `sortByScore(PREFER_FASTEST)` + sticky primary + `probeExcluded` shadow-mirroring (gives excluded upstreams a chance to heal via background traffic). See the [full default-policy chain](/config/projects/selection-policies.llms.txt#the-default-policy) for thresholds and rationale. Investigations are exposed via OTLP tracing and the `erpc_selection_*` Prometheus metric family. ### `directiveDefaults` — request directives at network level These apply to every request on this network unless the client explicitly overrides them via HTTP header (`X-ERPC-…`) or query param. | Directive | Default | Notes | |---|---|---| | `retryEmpty` | `true` | Retry empty responses (unless method is in `retry.emptyResultAccept`). | | `retryPending` | `false` | Treat pending-block responses as retryable. | | `skipCacheRead` | `false` | `false` = read from every cache; `true` = skip ALL caches; **string** = wildcard pattern matching connector IDs to skip (e.g. `"memory*"` skips all in-memory cache connectors while still reading from Redis/Postgres). | | `useUpstream` | none | Matcher: only consider upstreams whose `id` matches (`alchemy-*\|localnode-*`). | | `skipInterpolation` | `false` | Skip block-tag → concrete-number rewriting in request params and cache keys entirely. Use only when your client intentionally sends symbolic tags and you want them preserved verbatim through the cache layer. Rarely needed. | | `enforceHighestBlock` | `true` | Track highest block seen across upstreams; serve `latest`/`finalized` consistently. | | `enforceGetLogsBlockRange` | `true` | Validate `eth_getLogs` block range against upstream availability. | | `enforceNonNullTaggedBlocks` | `true` | Convert null responses to errors for `eth_getBlockByNumber("latest"\|"pending"\|...)`. Numeric block requests are always errors when null regardless. Set `false` for chains like zkSync that legitimately return null for some tags. | | `validateTransactionsRoot`, `validateTransactionFields`, `validateTransactionBlockInfo`, `validateHeaderFieldLengths` | `false` | Cross-validate transaction-root, per-tx fields, block-info, and header field lengths. Expensive but catches malformed responses. | | `validateLogFields`, `validateLogsBloomEmptiness`, `validateLogsBloomMatch` | `false` | Log-level validations. `validateLogsBloomMatch` recomputes the bloom filter from logs — most expensive of the three. | | `enforceLogIndexStrictIncrements`, `validateTxHashUniqueness`, `validateTransactionIndex` | `false` | Receipt-level validations. | | `validateReceiptTransactionMatch`, `validateContractCreation` | `false` | Cross-validate receipt vs transaction. Requires ground-truth transactions in library mode. | | `receiptsCountExact` | none | Exact expected receipt count for a block response. When set, eRPC rejects responses whose receipt list length doesn't match exactly. Use in conjunction with a known block to catch missing-receipt bugs on specific upstreams. | | `receiptsCountAtLeast` | none | Minimum expected receipt count. Less strict than `receiptsCountExact` — rejects only if the upstream returns fewer receipts than this threshold. | | `validationExpectedBlockHash` | none | Expected block hash (hex string) for the response block. Rejects any response whose `hash` field doesn't match. Useful in library mode when the caller knows the canonical hash and wants to catch equivocating upstreams. | | `validationExpectedBlockNumber` | none | Expected block number (integer). Rejects responses whose `number` field doesn't match. Use alongside `validationExpectedBlockHash` for full block-identity validation. | ### `eth_getLogs` — splitting and limits | Mechanism | Setting | Purpose | |---|---|---| | Validation | `directiveDefaults.enforceGetLogsBlockRange` (default `true`) | Reject if range exceeds the chosen upstream's known availability. | | Hard limits | `evm.getLogsMaxAllowedRange` / `getLogsMaxAllowedAddresses` / `getLogsMaxAllowedTopics` | Reject oversized requests upfront with a 413-style error. | | Proactive splitting | `upstream.evm.getLogsAutoSplittingRangeThreshold` (per upstream) | Network takes the min positive across selected upstreams and splits into contiguous ranges of at most that size. | | Split on error | `evm.getLogsSplitOnError` (default `true`) | Retry by bisecting on range, then addresses, then `topics[0]` if an upstream returns "too many results". | | Concurrency | `evm.getLogsSplitConcurrency` (default `16`) | Parallelism for split sub-requests. | Splitting preserves order. Address count is the length of the `address` array (if present). Topic count considers only `topics[0]` when it's an OR-list. ### `trace_filter` and `arbtrace_filter` — splitting Same pattern as `eth_getLogs`, but **opt-in** (disabled by default): - **Proactive splitting**: set `upstream.evm.traceFilterAutoSplittingRangeThreshold` to a positive value. Pick something below the upstream's per-response result cap for typical trace density. - **Split on error**: set `network.evm.traceFilterSplitOnError: true`. Bisects block range, then `fromAddress`, then `toAddress`. - **Concurrency**: `network.evm.traceFilterSplitConcurrency` (default `10`). Sub-ranges and address-list halves are disjoint by construction — no server-side deduplication. Order is preserved by sub-request index. ### `eth_sendRawTransaction` — idempotent broadcasting Enabled by default via `evm.idempotentTransactionBroadcast: true`. When set: - "Already known" / duplicate-transaction errors are converted to success responses (the tx hash is recomputed from the signed payload). - "Nonce too low" errors are verified against on-chain state — if the tx exists, return success. This makes retry and hedge policies safe for transaction broadcast — duplicate broadcasts to multiple upstreams don't surface as errors to the client. To disable (e.g. on chains where you want to see the duplicate-broadcast errors): ```yaml networks: - architecture: evm evm: chainId: 1 idempotentTransactionBroadcast: false ``` ### `eth_getTransactionCount` — return highest nonce, not most-common When fanning nonce queries across multiple upstreams, you usually want the **highest** value, not the most-agreed value (lagging upstreams will report stale lower nonces). ```yaml networks: - architecture: evm evm: { chainId: 1 } failsafe: - matchMethod: eth_getTransactionCount consensus: maxParticipants: 3 # query 3 upstreams in parallel agreementThreshold: 1 # only need 1 valid response; pick the highest preferHighestValueFor: eth_getTransactionCount: - result # the result IS the nonce (hex string) ``` The `preferHighestValueFor` map keys are method names; values are arrays of JSON field paths to compare: - `"result"` — compare the response's direct result value (works for `eth_getTransactionCount` which returns `"0x5"` directly). - Field name(s) — for object results (e.g. `["nonce", "blockNumber"]` for `eth_getTransactionByHash` where you want highest nonce, breaking ties by highest blockNumber). - Multiple fields are compared in declaration order; first decides, later ones break ties. **When `preferHighestValueFor` is set:** - `maxParticipants` — set to the number of upstreams you want to compare (2-3 is typical). - `agreementThreshold` — recommended `1`. The highest nonce typically reflects the most recently mined tx; requiring agreement would prefer the stale value. Use `≥2` only when you fear compromised upstreams returning artificially-high nonces. `preferHighestValueFor` takes precedence over normal hash-based consensus for the matched method. Error responses are ignored; only valid numeric responses are compared. ### `markEmptyAsErrorMethods` Treat empty responses on specific methods as errors (so they're retried and the upstream is scored down): ```yaml networks: - architecture: evm evm: chainId: 1 markEmptyAsErrorMethods: - eth_getTransactionReceipt # empty here means tx missing, not pending ``` By default, empty responses on most methods are valid data (caching them is safe). Use this only when an empty value indicates the upstream lacks the data. ### Static-response error variant The `staticResponses` example in the human section shows result-style. For error-style: ```yaml staticResponses: - method: some_unsupported_method params: [] response: error: code: -32601 # JSON-RPC standard error codes; -32601 = method not found message: "Method not found" data: # optional, embedded as response.error.data hint: "use eth_call instead" ``` The same `(method, params)` matching rules apply. Use this for methods you want clients to immediately know are unsupported on this chain without round-tripping to an upstream. ### Multiplexing override By default, identical concurrent requests on the same network are deduplicated — a single upstream call is made, and the result is shared with all callers. To disable for one network (e.g. to force every probe through to upstream during latency testing): ```yaml networks: - architecture: evm evm: { chainId: 1 } multiplexing: false ``` `true` is the default and almost always what you want — disabling triples upstream RPS during traffic spikes for no benefit in production. ### Common pitfalls - **`failsafe` replaces, doesn't deep-merge `networkDefaults.failsafe`** — if `networks[].failsafe` is set, the network entirely overrides defaults for that field. Same for `selectionPolicy`. Other fields like `rateLimitBudget` and `directiveDefaults` ARE deep-merged. - **`matchFinality: ["latest"]`** — there is no `latest` finality state. Valid values are `finalized`, `unfinalized`, `realtime`, `unknown`. Invalid values silently never match. - **Network timeout vs upstream timeout** — the network `timeout.duration` covers the **full** request lifecycle including every upstream retry. The upstream's own `timeout` only bounds one attempt. Set the network timeout generously (≥ upstream timeout × maxAttempts). - **`fallbackFinalityDepth: 1024` blocks finality detection** — if you set this and the upstream actually supports `eth_getBlockByNumber("finalized")`, eRPC still uses the dynamic value. The fallback only kicks in if the upstream doesn't. - **Static responses bypass everything** — including auth's rate-limit budgets. Don't put privileged data in a static response. - **`alias: eth/1` is invalid** — only alphanumeric, dash, and underscore allowed. > **TIP** > Append `.llms.txt` to this URL (or use the **AI** link above) to fetch the entire expanded reference as plain markdown for an AI assistant.