# Upstreams > Source: https://docs.erpc.cloud/config/projects/upstreams > An upstream is one or more RPC endpoints that serve one or more EVM networks — with failsafe, rate limits, scoring, block-availability bounds, and per-method filters. > Format: machine-readable markdown export of the docs page above. > All collapsible AI sections are inlined and fully expanded. # Upstreams An upstream is a single RPC endpoint (yours or a third party's) that can serve one or more EVM networks. eRPC manages the pool: it scores upstreams in real time, picks the best one as primary, falls over to the rest on failure, and enforces per-upstream rate limits and method allow/deny lists. **You can configure:** - **Endpoint** — plain HTTPS URL, or a vendor-shorthand like `alchemy://API_KEY` (see [Providers](/config/projects/providers.llms.txt)) - **EVM details** — chain ID, state poller cadence, [block-availability bounds](#block-availability) with probes - **Method filters** — `ignoreMethods`, `allowMethods`, `autoIgnoreUnsupportedMethods` - **Failsafe** — per-upstream `timeout`, `retry`, `circuitBreaker`, `hedge`, `consensus`, with optional `matchMethod` / `matchFinality` scoping - **Rate limits** — bind a `rateLimitBudget`, optionally with `rateLimitAutoTune` that adjusts the budget based on 429 feedback - **Routing & scoring** — per-upstream `routing.scoreMultipliers` to boost/penalize this endpoint relative to others - **Transport** — JSON-RPC batching, gzip, custom headers, outbound proxy pool - **Grouping** — `group`, `vendorName` for labelling and fallback policies - **Shadow traffic** — `shadow` to mirror a fraction of requests to this upstream for comparison without affecting clients - **Per-method response validation** — `evm.integrity.eth_getBlockReceipts` correctness checks ## Minimum useful config The smallest workable upstream is just an endpoint. Everything else has sensible defaults. **Config path:** `projects > upstreams[]` **YAML — `erpc.yaml`:** ```yaml projects: - id: main upstreams: - endpoint: https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY # eRPC auto-detects the chain ID and applies default failsafe (15s timeout, # 2 retries, circuit breaker at 80% failure rate). ``` **TypeScript — `erpc.ts`:** ```typescript import { createConfig } from "@erpc-cloud/config"; export default createConfig({ projects: [{ id: "main", upstreams: [ { endpoint: "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY" }, ], }], }); ``` ## Production config — every common knob A realistic upstream with rate limits, batching, method filters, and tuned failsafe. The dimmed scaffolding shows you where each block plugs in. **Config path:** `projects > upstreams[]` **YAML — `erpc.yaml`:** ```yaml projects: - id: main rateLimiters: budgets: - id: alchemy-global rules: [{ method: "*", maxCount: 500, period: second }] upstreams: - id: my-alchemy # used in logs, metrics, selection-policy endpoint: https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY rateLimitBudget: alchemy-global # binds to budget above evm: chainId: 1 # optional, auto-detected jsonRpc: supportsBatch: true batchMaxSize: 10 batchMaxWait: 50ms enableGzip: false # compress outbound bodies; default off ignoreMethods: ["debug_*"] # never send these here allowMethods: ["eth_*", "net_*"] # if set, ALL others are blocked except listed failsafe: - matchMethod: "*" timeout: { duration: 15s } retry: maxAttempts: 3 delay: 200ms backoffFactor: 1.5 jitter: 50ms circuitBreaker: failureThresholdCount: 160 failureThresholdCapacity: 200 halfOpenAfter: 5m successThresholdCount: 3 successThresholdCapacity: 10 ``` **TypeScript — `erpc.ts`:** ```typescript import { createConfig } from "@erpc-cloud/config"; export default createConfig({ projects: [{ id: "main", rateLimiters: { budgets: [{ id: "alchemy-global", rules: [{ method: "*", maxCount: 500, period: "second" }], }], }, upstreams: [{ id: "my-alchemy", endpoint: "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY", rateLimitBudget: "alchemy-global", evm: { chainId: 1 }, jsonRpc: { supportsBatch: true, batchMaxSize: 10, batchMaxWait: "50ms", enableGzip: false, }, ignoreMethods: ["debug_*"], allowMethods: ["eth_*", "net_*"], failsafe: [{ matchMethod: "*", timeout: { duration: "15s" }, retry: { maxAttempts: 3, delay: "200ms", backoffFactor: 1.5, jitter: "50ms", }, circuitBreaker: { failureThresholdCount: 160, failureThresholdCapacity: 200, halfOpenAfter: "5m", successThresholdCount: 3, successThresholdCapacity: 10, }, }], }], }], }); ``` ## Priority & routing eRPC scores each upstream every few seconds based on error rate, latency, throttling, and block lag. The top-scoring upstream is the primary; failures rotate to the runner-up. Defaults work well in production; tune only when you have a reason. **Config path:** `projects[]` **YAML — `erpc.yaml`:** ```yaml projects: - id: main routingStrategy: score-based # or "round-robin" scoreGranularity: upstream # or "method" for per-method scoring scoreRefreshInterval: 30s scoreMetricsWindowSize: 10m scorePenaltyDecayRate: 0.95 # 0..1; higher = stabler; -1 = no smoothing scoreSwitchHysteresis: 0.10 # challenger must beat primary by this fraction scoreMinSwitchInterval: 2m # cooldown between primary switches upstreams: [ /* ... */ ] ``` **TypeScript — `erpc.ts`:** ```typescript import { createConfig } from "@erpc-cloud/config"; export default createConfig({ projects: [{ id: "main", routingStrategy: "score-based", scoreGranularity: "upstream", scoreRefreshInterval: "30s", scoreMetricsWindowSize: "10m", scorePenaltyDecayRate: 0.95, scoreSwitchHysteresis: 0.10, scoreMinSwitchInterval: "2m", upstreams: [ /* ... */ ], }], }); ``` The scoring mechanism only changes the **order** in which upstreams are tried — it doesn't disable any. To fully take an upstream offline, use the [circuit breaker](/config/failsafe/circuit-breaker.llms.txt) failsafe or `ignoreMethods: ["*"]`. ## Block availability Bound the block window an upstream can serve. The bound can be relative (`latestBlockMinus`, `earliestBlockPlus`), absolute (`exactBlock`), or probe-driven (auto-detect the earliest block where logs, calls, or traces are available). Block-availability bounds skip out-of-range upstreams before retry rather than failing-then-retrying. **Config path:** `projects > upstreams[] > evm > blockAvailability` **YAML — `erpc.yaml`:** ```yaml projects: - id: main upstreams: - id: my-archive endpoint: https://archive.example evm: blockAvailability: # Don't serve the last 64 blocks (avoid reorgs) upper: latestBlockMinus: 64 probe: blockHeader # default probe # Auto-detect earliest block where logs exist, refresh hourly lower: earliestBlockPlus: 0 probe: eventLogs updateRate: 1h ``` **TypeScript — `erpc.ts`:** ```typescript import { createConfig } from "@erpc-cloud/config"; export default createConfig({ projects: [{ id: "main", upstreams: [{ id: "my-archive", endpoint: "https://archive.example", evm: { blockAvailability: { upper: { latestBlockMinus: 64, probe: "blockHeader" }, lower: { earliestBlockPlus: 0, probe: "eventLogs", updateRate: "1h" }, }, }, }], }], }); ``` For an upstream that is a point-in-time snapshot (e.g. a frozen archive at a specific block), use `exactBlock` to pin both bounds to the same height: ```yaml upstreams: - id: snapshot-19m endpoint: https://snapshot.example evm: blockAvailability: upper: exactBlock: 19000000 # only serve requests at or below this block lower: exactBlock: 0 # serve from genesis ``` `exactBlock` is a static value — it never updates. Use it when the upstream's range will never change (snapshots, genesis-only nodes). For nodes that advance over time, prefer `latestBlockMinus` / `earliestBlockPlus` with a probe. | Probe | What it checks | |---|---| | `blockHeader` (default) | `eth_getBlockByNumber(blockHash)` returns a header. | | `eventLogs` | `eth_getLogs(blockHash)` returns ≥1 log. Useful as a lower bound for archive nodes that prune logs. | | `callState` | `eth_getBalance` returns a non-null result. Probes historical state availability. | | `traceData` | Tries `trace_block`, `debug_traceBlockByHash`, `trace_replayBlockTransactions` in order; available if any returns. | > **INFO** > Block-availability bounds are only enforced when eRPC can extract a block number from the request. For methods without an explicit block parameter (e.g. `eth_chainId`), the request goes to the upstream regardless of the bounds. `updateRate` only applies to `earliestBlockPlus` — `latestBlockMinus` always reads the live head. ## Upstream types The only `type` today is `evm` (default; covers any EVM-compatible JSON-RPC endpoint, whether self-hosted or third-party). Vendor shorthands like `alchemy://`, `drpc://`, `infura://` are handled by the [providers](/config/projects/providers.llms.txt) layer on top of `type: evm` — they're not separate types. ### Every field on `upstreams[]` | Field | Type | Notes | |---|---|---| | `id` | string | Used in logs, metrics, selection-policy eval. Auto-generated if omitted. | | `type` | string | `evm` (default and only supported value today). | | `endpoint` | string (**required**) | HTTPS URL or vendor shorthand (`alchemy://KEY`, `drpc://KEY`, `repository://URL`, etc.). | | `group` | string | Arbitrary label. Used as a metric label, and as a target in selection-policy eval functions. The literal value `"fallback"` is special — see "Groups & the fallback magic value" below. | | `vendorName` | string | Tag an upstream with a known vendor identifier so vendor-specific normalization (error code mapping, etc.) applies, even when the endpoint is a plain URL. Normally set automatically by provider shorthands (`alchemy://`, `drpc://`, etc.). Set it manually on plain-URL upstreams to opt into the same normalization — e.g. a self-hosted Erigon node behind nginx can set `vendorName: erigon` to enable Erigon-specific error parsing without using a vendor shorthand. | | `evm` | object | EVM-specific config — see "evm.* fields" below. | | `jsonRpc` | object | Transport-level config: batching, gzip, headers, proxy pool. See "jsonRpc.* fields" below. | | `ignoreMethods` | string[] | Block these methods on this upstream (matcher syntax: `eth_*`, `debug_*\|trace_*`, etc.). | | `allowMethods` | string[] | Allowlist; if set, blocks everything not listed. When `allowMethods` is set and `ignoreMethods` is not, `ignoreMethods: ["*"]` is implicit. | | `autoIgnoreUnsupportedMethods` | bool | Default `true`. When an upstream returns "method not supported", auto-add the method to `ignoreMethods` for this upstream. | | `failsafe` | array | Per-upstream failsafe policies; see [Failsafe docs](/config/failsafe.llms.txt). Supports `matchMethod` and `matchFinality` per entry. | | `rateLimitBudget` | string | Bind to a budget defined in `rateLimiters.budgets[]`. Multiple upstreams sharing a budget share the same rate-limit pool. | | `rateLimitAutoTune` | object | When a budget is bound, auto-tune is enabled by default. See "rateLimitAutoTune fields". | | `routing` | object | Per-upstream score multipliers and latency quantile. See "routing.* fields". | | `shadow` | object | Mirror a fraction of traffic to this upstream for comparison (without affecting the real response). See "Shadow upstreams" below. | ### `evm.*` fields | Field | Default | Notes | |---|---|---| | `chainId` | auto-detected via `eth_chainId` | Set explicitly to skip detection at startup. | | `statePollerInterval` | `30s` | How often to poll latest/finalized/syncing. Set to `0s` to disable polling entirely — all data will be treated as `unfinalized` or `unknown`. | | `statePollerDebounce` | dynamic | Override the polling debounce. When omitted, eRPC infers it from the observed block time. | | `skipWhenSyncing` | `false` | When `true`, route requests away from this upstream while `eth_syncing` reports it's syncing. Use for archive backfills. | | `blockAvailability` | none | See "Block availability" section above. | | `integrity.eth_getBlockReceipts` | none | Per-upstream response validation for `eth_getBlockReceipts`. See "Per-upstream integrity" below. | | `nodeType` | `archive` | **Deprecated** — use `blockAvailability` instead. Still accepted; `full` implies a 128-block availability window. | | `maxAvailableRecentBlocks` | `128` (for full nodes) | **Deprecated** — use `blockAvailability` instead. | | `getLogsAutoSplittingRangeThreshold` | none | Upstream hint for the network-level proactive splitter. The network computes the min positive threshold across selected upstreams and splits large `eth_getLogs` ranges into contiguous sub-requests of at most that size. Set to `0` or negative to disable for this upstream. | | `traceFilterAutoSplittingRangeThreshold` | none | Same as above for `trace_filter` / `arbtrace_filter`. | **Deprecated:** `getLogsMaxAllowedRange`, `getLogsMaxAllowedAddresses`, `getLogsMaxAllowedTopics`, `getLogsSplitOnError` on `upstream.evm` — these moved to the [network level](/config/projects/networks.llms.txt#evm-networks). ### `jsonRpc.*` fields | Field | Default | Notes | |---|---|---| | `supportsBatch` | `false` | Allow eRPC to batch outbound requests to this upstream. Even when `false`, clients can still send batch requests to eRPC — they'll be unrolled into individual upstream calls. | | `batchMaxSize` | `10` | Max requests per outbound batch. | | `batchMaxWait` | `50ms` | Max time to wait while filling a batch before flushing. | | `enableGzip` | `false` | Compress outbound request bodies. Most upstreams ignore this; turn on only when the vendor documents support. | | `headers` | `{}` | Extra headers on every request — typically `Authorization: Bearer ...` for private endpoints. | | `proxyPool` | none | ID of a proxy pool from the top-level `proxyPools[]` — outbound requests round-robin through that pool. | ### Method filters — interaction rules - Both `ignoreMethods` and `allowMethods` accept matcher syntax (`*` wildcard, `|` OR). - `allowMethods` takes precedence over `ignoreMethods` when both match. - Setting `allowMethods` without `ignoreMethods` implicitly adds `ignoreMethods: ["*"]` — you must explicitly set `ignoreMethods: []` to opt out of that behavior. - `autoIgnoreUnsupportedMethods` (default `true`) augments `ignoreMethods` at runtime when the upstream returns a "method not supported" error. Disable when probing experimental methods. ### Groups & the fallback magic value `group` is a free-form label, but the value `"fallback"` is special: - If any upstream in a network has `group: fallback`, eRPC auto-creates a default network-level selection policy that **prefers** non-fallback upstreams and only reaches into the fallback group when the primaries are unhealthy. - This behavior is enabled by the `ROUTING_POLICY_*` env-controlled defaults — see [selection policies](/config/projects/selection-policies.llms.txt) for the exact eval body. - Any other `group` value is purely a label and only matters if you reference it from your own selection-policy `evalFunction` or want it as a metric label. Apart from the `fallback` magic value, you can use `group` purely as a label that's referenced by your own selection-policy `evalFunction` and surfaces in Prometheus metric labels. ### `routing.*` — per-upstream score tuning ```yaml upstreams: - id: my-alchemy endpoint: https://eth-mainnet.g.alchemy.com/v2/KEY routing: scoreLatencyQuantile: 0.70 # latency percentile used for scoring (default 0.70) scoreMultipliers: # ── Selectors: which (network × method × finality) bucket these weights apply to. - network: "*" # matcher: "evm:1", "evm:*", "evm:1|evm:10" method: "*" # matcher: "eth_getLogs|eth_call", etc. finality: ["realtime", "unfinalized"] # filter — apply only to these finality buckets # ── Weights (numeric). `overall` is a final multiplier; the others scale individual penalties. overall: 1.0 # boost the final score (>1 = preferred) errorRate: 4.0 # higher = bigger penalty when error rate is bad respLatency: 8.0 # higher = bigger penalty for slow tail latency throttledRate: 3.0 blockHeadLag: 2.0 finalizationLag: 1.0 totalRequests: 1.0 # weight on observed request volume (favor warmer pools) misbehaviors: 5.0 # weight on consensus-misbehavior count ``` **How scoring works.** Each upstream is scored separately for every (network × method × finality) bucket it serves. The score is a weighted aggregate of penalty signals (error rate, latency quantile, throttling, head-lag, finalization-lag, misbehavior count, request volume); higher weight on a *penalty* dimension means that metric hurts the score more. `overall` is the final multiplier applied on top, so an upstream with `overall: 10` and slightly worse metrics still outranks one with `overall: 1` and a perfect score. **Selectors vs weights:** - `network`, `method`, `finality` are **selectors** — they decide which buckets this entry applies to. `finality` is an *array* (`["realtime", "unfinalized"]`); valid values are `finalized`, `unfinalized`, `realtime`, `unknown`. Omitting `finality` matches all buckets. - `overall`, `errorRate`, `respLatency`, `throttledRate`, `blockHeadLag`, `finalizationLag`, `totalRequests`, `misbehaviors` are **weights** (floats, default `1.0`). Setting one to `0` removes that signal from the score for the selected buckets. - **Default weights:** if no `scoreMultipliers` entry matches a given bucket (because no entry exists, or no selector matches), every dimension defaults to `1.0` and `overall` defaults to `1.0` — uniform weight across all signals, no boost. **Worked example.** Suppose upstream A has `errorRate: 8.0` and upstream B has `errorRate: 1.0`. Both observe a 5% error rate in the same window. The `errorRate` penalty contribution for A is `0.05 × 8.0 = 0.40`; for B it is `0.05 × 1.0 = 0.05`. That 0.35-point gap compounds with the other dimensions — A scores meaningfully lower and is deprioritized sooner and more aggressively than B, even though both see identical traffic. Setting `errorRate: 8.0` on a latency-sensitive bucket is a way of saying "I care much more about errors than the default here." Multipliers compose: an upstream with `overall: 10` and a perfect score will outrank one with `overall: 1` even at slightly worse metrics. To prefer a cheap upstream by default: ```yaml upstreams: - { id: cheap, endpoint: ..., routing: { scoreMultipliers: [{ overall: 10 }] } } - { id: pricey, endpoint: ..., routing: { scoreMultipliers: [{ overall: 1 }] } } ``` **Tuning tips:** - **Fast failover** — set `scoreSwitchHysteresis: -1` and `scoreMinSwitchInterval: -1` to always use the current best upstream immediately. - **Smoother scoring** — increase `scorePenaltyDecayRate` toward `0.98` so transient blips don't move the primary. - **Reactive scoring** — decrease toward `0.80` so degradation kicks in quickly. **When to tune what:** - **High `respLatency` weight** — use when tail-latency variance between providers is large and client timeout budget is tight. Raising `respLatency` to `8.0`–`16.0` makes slow p70 latency a dominant signal. - **High `misbehaviors` weight** — use when running `consensus` failsafe and you want divergent upstreams de-prioritized aggressively after they produce wrong answers. Default `1.0` is gentle; `10.0`+ will effectively exile a misbehaving upstream until its penalty decays. - **Narrow method-scoped entry** — use when an upstream is excellent for some methods but bad for others. A separate entry with a tight `method` selector applies heavier penalty only where the upstream is weak: ```yaml routing: scoreMultipliers: - method: '*' overall: 2.0 # generally preferred - method: 'eth_getLogs' respLatency: 16.0 # but penalize hard for getLogs where it's slow errorRate: 4.0 ``` ### `rateLimitAutoTune` fields ```yaml rateLimitAutoTune: enabled: true # default: true if a rateLimitBudget is bound adjustmentPeriod: 1m # window for evaluating throttled ratio errorRateThreshold: 0.1 # if throttled-rate > this, decrease increaseFactor: 1.05 # multiply budget by this when below threshold decreaseFactor: 0.9 # multiply budget by this when above threshold minBudget: 0 maxBudget: 10000 ``` The new budget applies to **every** upstream sharing that budget — auto-tune decreases on one Quicknode upstream tighten the budget for all Quicknode upstreams sharing it. ### Per-upstream integrity — `evm.integrity` Validate `eth_getBlockReceipts` responses on this upstream specifically. Useful when a vendor has known correctness issues you want to catch before the response is cached or returned. ```yaml upstreams: - id: suspect-vendor endpoint: https://... evm: integrity: eth_getBlockReceipts: enabled: true checkLogIndexStrictIncrements: true # log.index must be strictly increasing within a block checkLogsBloom: true # recalculated bloom must match the header's logsBloom ``` When a check fails, the response is treated as an upstream error — retried, scored against, and optionally exported via [consensus.misbehaviorsDestination](/config/failsafe/consensus.llms.txt#misbehaviors-destination). ### Shadow upstreams `shadow` mirrors a fraction of real traffic to this upstream **without** affecting the response sent to the client. The shadow result is compared against the primary's; mismatches are logged or exported. Useful for vendor evaluations, regression detection, and silent validation of new endpoints. ```yaml upstreams: # Primary serves traffic normally. - id: primary endpoint: https://prod-vendor.example # Shadow only — receives a 10% sample of every request the primary serves. - id: candidate endpoint: https://candidate-vendor.example shadow: enabled: true sampleRate: 0.1 # 0.0–1.0; fraction of requests to mirror ignoreFields: # diff-comparison ignore lists "*": ["blockTimestamp"] "transactions.*": ["gasPrice"] ``` Notes: - Shadow upstreams don't count in selection — they only see traffic that the primary served first. - Diffs are emitted as a metric (`erpc_upstream_shadow_diff_total`) and, when paired with a [misbehaviors destination](/config/failsafe/consensus.llms.txt#misbehaviors-destination), written out for inspection. - `ignoreFields` uses the same dot-path matcher as `consensus.ignoreFields` — `*` matches a single segment, `**` matches any depth. ### Failsafe at upstream level (matchMethod + matchFinality) `failsafe[]` accepts per-policy `matchMethod` and `matchFinality` so you can have different retry budgets for different categories of methods on the **same** upstream: ```yaml upstreams: - id: archive endpoint: https://archive.example failsafe: - matchMethod: "trace_*|debug_*" timeout: { duration: 60s } retry: { maxAttempts: 1 } # expensive — don't multiply - matchMethod: "*" matchFinality: ["realtime", "unfinalized"] timeout: { duration: 5s } retry: { maxAttempts: 3, delay: 100ms } - matchMethod: "*" matchFinality: ["finalized"] timeout: { duration: 30s } retry: { maxAttempts: 5, delay: 200ms } ``` `consensus` and `hedge` are also valid at upstream level. The full vocabulary is the same as the [network-level failsafe](/config/failsafe.llms.txt). > **INFO** > The legacy single-object form (`failsafe: { timeout: ... }`) is still accepted for backward compatibility, but the array form with `matchMethod: "*"` is the canonical shape and lets you grow into per-method tuning without rewriting. ### Defaults via `upstreamDefaults` `project.upstreamDefaults` applies before any per-upstream config — useful for proxy pools, gzip, or a baseline failsafe across every upstream in a project. ```yaml projects: - id: main upstreamDefaults: jsonRpc: proxyPool: eu-dc1-pool enableGzip: true failsafe: - matchMethod: "*" timeout: { duration: 20s } retry: { maxAttempts: 2 } upstreams: - id: a endpoint: https://a.example # inherits the defaults above - id: b endpoint: https://b.example jsonRpc: proxyPool: us-dc1-pool # per-upstream override ``` Per-upstream values **override** the defaults; arrays don't merge. ### Outbound compression eRPC supports gzip at four points: | Direction | Control | Default | |---|---|---| | Client → eRPC | Client sets `Content-Encoding: gzip`. | Always accepted. | | eRPC → Upstream | `upstreams[].jsonRpc.enableGzip` | `false` | | Upstream → eRPC | Automatic if upstream sends `Content-Encoding: gzip`. | Always accepted. | | eRPC → Client | `server.enableGzip` | `true` | ```bash # Example client → eRPC gzipped request curl -X POST \ -H "Content-Encoding: gzip" \ -H "Content-Type: application/json" \ --data-binary @<(echo '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[]}' | gzip) \ http://localhost:4000/main/evm/42161 ``` ### Custom HTTP headers ```yaml upstreams: - id: private endpoint: https://private-provider.io/v1 jsonRpc: headers: Authorization: Bearer SECRET_VALUE X-Custom-Header: HelloWorld ``` Headers are applied on every outbound request from eRPC to this upstream. ### Proxy pools A proxy pool is a named list of outbound HTTP/SOCKS proxies. When an upstream (or `upstreamDefaults`) references a pool by ID via `jsonRpc.proxyPool`, every request eRPC makes to that upstream goes through one of the pool's proxies. eRPC round-robins across proxies using an atomic counter — each successive request picks the next proxy in sequence. Useful for geographic distribution, egress IP pinning, ISP routing, or private/public splits. > **INFO** > `proxyPools` is defined at the **root** of the config, not inside a project. All projects share the same pool list. **Config path:** `proxyPools[]` **YAML — `erpc.yaml`:** ```yaml proxyPools: - id: eu-dc1-pool urls: - http://proxy111.myorg.local:3128 - https://proxy222.myorg.local:3129 - id: us-dc1-pool urls: - http://proxy333.myorg.local:3128 - socks5://user:pass@proxy444.myorg.local:1080 projects: - id: main upstreamDefaults: jsonRpc: proxyPool: eu-dc1-pool # default for all upstreams in this project upstreams: - id: us-rpc endpoint: https://us.example jsonRpc: proxyPool: us-dc1-pool # override for this upstream - id: direct-rpc endpoint: https://direct.example jsonRpc: proxyPool: '' # opt out (empty string disables the inherited default) ``` **TypeScript — `erpc.ts`:** ```typescript import { createConfig } from '@erpc-cloud/config'; export default createConfig({ proxyPools: [ { id: 'eu-dc1-pool', urls: [ 'http://proxy111.myorg.local:3128', 'https://proxy222.myorg.local:3129', ], }, { id: 'us-dc1-pool', urls: [ 'http://proxy333.myorg.local:3128', 'socks5://user:pass@proxy444.myorg.local:1080', ], }, ], projects: [{ id: 'main', upstreamDefaults: { jsonRpc: { proxyPool: 'eu-dc1-pool' }, }, upstreams: [ { id: 'us-rpc', endpoint: 'https://us.example', jsonRpc: { proxyPool: 'us-dc1-pool' }, }, { id: 'direct-rpc', endpoint: 'https://direct.example', jsonRpc: { proxyPool: '' }, }, ], }], }); ``` #### `proxyPools[]` fields | Field | Type | Notes | |---|---|---| | `id` | string (**required**) | Identifier referenced by `jsonRpc.proxyPool` on upstreams or `upstreamDefaults`. | | `urls` | string[] (**required**) | One or more proxy URLs. At least one is required. eRPC round-robins across them per request. | #### Accepted URL schemes | Scheme | Notes | |---|---| | `http://` | Plain HTTP CONNECT proxy. | | `https://` | TLS-wrapped HTTP CONNECT proxy. | | `socks5://` | SOCKS5 proxy. Credentials embedded in the URL: `socks5://user:pass@host:port`. | #### Round-robin and failover eRPC selects proxies with a lockless atomic counter — each request increments the counter and picks `counter % len(urls)`. There is no automatic failover: if the selected proxy is unreachable, the upstream request fails and normal upstream-level retry/circuit-breaker logic applies. To tolerate proxy failures, put a single reliable entry per pool or front your proxies with a load balancer. ### Deprecated fields — migration map | Old (still accepted) | New | |---|---| | `evm.nodeType: full` / `archive` | `evm.blockAvailability.upper.latestBlockMinus: 128` (or whatever your window is) | | `evm.maxAvailableRecentBlocks: 128` | `evm.blockAvailability.upper.latestBlockMinus: 128` | | `evm.getLogsMaxAllowedRange` (upstream level) | `network.evm.getLogsMaxAllowedRange` | | `evm.getLogsMaxAllowedAddresses` (upstream level) | `network.evm.getLogsMaxAllowedAddresses` | | `evm.getLogsMaxAllowedTopics` (upstream level) | `network.evm.getLogsMaxAllowedTopics` | | `evm.getLogsSplitOnError` (upstream level) | `network.evm.getLogsSplitOnError` | | `evm.getLogsMaxBlockRange` (upstream level) | `network.evm.getLogsMaxAllowedRange` | | `failsafe: { ... }` (single object) | `failsafe: [{ matchMethod: "*", ... }]` | The legacy forms still parse, so existing configs work — but new code should use the new shape. Mixing both in one config will emit a deprecation warning. ### Common pitfalls - **`allowMethods` without `ignoreMethods: []`** — `allowMethods` silently adds an implicit `ignoreMethods: ["*"]`, so an upstream with `allowMethods: ["eth_getLogs"]` will block every other method. To allow `eth_getLogs` while still serving the defaults, prefer adding to `ignoreMethods` instead. - **`group: fallback` is magic** — using this value triggers auto-creation of a default selection policy. Use any other label if you don't want that behavior. - **Compound rate-limit budgets** — `rateLimitAutoTune` decreases on one upstream tighten the budget for **every** upstream sharing it. If you need independent rate limits per upstream, give each its own budget. - **`statePollerInterval: 0s`** — disables polling entirely, so eRPC has no notion of the chain's latest/finalized state for this upstream. Every response goes into the `unknown` finality bucket; the cache layer will treat them accordingly. - **`blockAvailability.upper.updateRate`** is ignored — for `latestBlockMinus` the bound is computed on-demand from the state poller's latest head. Only `earliestBlockPlus` honors `updateRate`. > **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.