# Providers & vendors > Source: https://docs.erpc.cloud/config/projects/providers > One API key, every chain — declare a single provider entry and eRPC auto-generates upstreams for each network on first request, with 23 built-in vendor integrations. > Format: machine-readable markdown export of the docs page above. > All collapsible AI sections are inlined and fully expanded. # Providers & vendors One API key, every chain. Instead of wiring one upstream per (vendor, network) pair, you declare a single provider and eRPC lazy-generates concrete upstreams the moment a new network is first requested. 23 vendor integrations are built in — from general-purpose node providers to ERC-4337 bundlers to public-endpoint catalogs — and a project with no config at all gets a sensible zero-cost default automatically. **What you get** - One line to cover all chains a vendor supports — no manual network list. - Hot-path chain discovery with no I/O: static maps and lock-free remote caches keep routing decisions off the slow path. - Zero-config fallback: eRPC auto-injects `repository` + `envio` providers when no upstreams or providers are configured. ## Quick taste Illustrative, not a tuned production config — one key, every Alchemy chain: **Config path:** `projects[].upstreams[]` **YAML — `erpc.yaml`:** ```yaml projects: - id: main upstreams: - # URL shorthand — one line, all Alchemy chains endpoint: alchemy://YOUR_API_KEY ``` **TypeScript — `erpc.ts`:** ```typescript projects: [{ id: "main", upstreams: [{ // URL shorthand — one line, all Alchemy chains endpoint: "alchemy://YOUR_API_KEY", }], }] ``` ## Agent reference Copy one of these prompts into your AI agent session (Claude Code, Cursor, …) — each one points the agent at this page's machine-readable reference so it can do the work correctly: **Prompt Example #1: set up providers from scratch** ```text I want to configure eRPC providers so a single API key per vendor covers all EVM chains automatically. Add Alchemy and dRPC as providers, restrict Alchemy to the chains we actually need (mainnet, Arbitrum, Base, Polygon), and leave everything else to dRPC. Work with my existing eRPC config. Read the full reference first: https://docs.erpc.cloud/config/projects/providers.llms.txt ``` **Prompt Example #2: add per-network overrides to an existing provider** ```text My eRPC config already has providers for Alchemy and QuickNode. I need Arbitrum (evm:42161) to pre-split eth_getLogs at 1000 blocks and have a lower score multiplier for QuickNode on mainnet. Apply these as provider overrides without touching the upstreams list. Reference: https://docs.erpc.cloud/config/projects/providers.llms.txt ``` **Prompt Example #3: debug a provider not generating upstreams on cold start** ```text My eRPC instance logs ErrNetworkInitializing for evm:130 on every first request after a redeploy, even though I have a conduit provider configured. Explain why conduit returns ErrRemoteCacheCold on cold start and what I can pair it with to avoid the first-request delay. Work with my existing eRPC config. Reference: https://docs.erpc.cloud/config/projects/providers.llms.txt ``` **Prompt Example #4: route ERC-4337 bundler traffic to pimlico** ```text Add Pimlico as a provider in my eRPC config so all bundler methods (eth_sendUserOperation, pimlico_*, pm_*) route to Pimlico while general eth_* methods still hit Alchemy. Explain the method allow-list that Pimlico injects automatically. Reference: https://docs.erpc.cloud/config/projects/providers.llms.txt ``` **Prompt Example #5: split QuickNode endpoint groups by tag** ```text I have two logical QuickNode endpoint groups: one tagged "archive" and one tagged "realtime". Wire them as separate providers in my eRPC config so each group's endpoints are used for the right traffic, and explain the apiKey cache-key footgun so I don't accidentally share snapshots. Reference: https://docs.erpc.cloud/config/projects/providers.llms.txt ``` --- ### Providers & vendors — full agent reference ### How it works **Two distinct concepts.** A *vendor* (`common.Vendor` interface) is stateless integration logic: `Name()`, `OwnsUpstream(cfg)`, `SupportsNetwork(ctx, logger, settings, networkId)`, `GenerateConfigs(...)`, and `GetVendorSpecificErrorIfAny(...)`. A *provider* is a configured instance of a vendor: `ProviderConfig{Id, Vendor, Settings, OnlyNetworks, IgnoreNetworks, UpstreamIdTemplate, Overrides}`. All 23 vendors are registered in a global `VendorsRegistry` at startup; a per-project `ProvidersRegistry` resolves each `providers[].vendor` string and fails startup if the name is unknown. [`thirdparty/vendors_registry.go:L9-36`](https://github.com/erpc/erpc/blob/main/thirdparty/vendors_registry.go#L9-L36) [`thirdparty/providers_registry.go:L15-34`](https://github.com/erpc/erpc/blob/main/thirdparty/providers_registry.go#L15-L34) **Lazy upstream generation.** When a request arrives for a network with no prepared upstreams, `UpstreamsRegistry.PrepareUpstreamsForNetwork` schedules — exactly once per network via `sync.Once` — one bootstrap task per declared provider named `network//provider/`. Each task: (1) calls `Provider.SupportsNetwork`, applying `ignoreNetworks` deny-list first, then `onlyNetworks` allow-list, then delegating to the vendor; (2) calls `Provider.GenerateUpstreamConfigs`, which copies the first wildcard-matching `overrides` entry (or creates a fresh defaults-filled config), stamps `VendorName`, renders the upstream id from `upstreamIdTemplate`, and runs `os.ExpandEnv` over every generated endpoint; (3) registers the resulting configs as ordinary upstream bootstrap tasks. The caller waits up to 30 seconds (polling every 200 ms) for at least one upstream to become ready. While tasks run, clients receive the retryable `ErrNetworkInitializing`; if all tasks finish without producing any upstreams, the error becomes the retryable `ErrNetworkNotSupported`. Failed tasks auto-retry with exponential backoff (factor 1.5, 3 s → 130 s). [`upstream/registry.go:L155-293`](https://github.com/erpc/erpc/blob/main/upstream/registry.go#L155-L293) **Vendor chain-support strategies.** Vendors answer "do you support this network?" in four ways: 1. **Static in-code map** (ankr, blastapi, blockdaemon, blockpi, dwellir, infura, llama, onfinality, etherspot): instant, no I/O, works on cold start. 2. **Remote catalog with static fallback** (alchemy — 134-chain map; drpc — 150-chain map): fetches on warm paths; cold start falls back to the built-in map so the first request never blocks on I/O. 3. **Remote catalog, no fallback** (conduit, superchain, tenderly, quicknode, chainstack, repository): cold start returns the retryable sentinel `ErrRemoteCacheCold`; the initializer retries with 3 s → 130 s exponential backoff until the first fetch lands. 4. **Live `eth_chainId` probe** (envio for unknown chains, erpc, pimlico for unknown chains, routemesh, thirdweb): sends a real JSON-RPC call to the candidate endpoint with a 10 s timeout; probe clients are cached per chain id in `sync.Map`. **The request-path safety rule.** Any vendor consulting remote data on the hot path uses the shared `RemoteDataCache[T]`: reads are a single `atomic.Pointer` load — never a mutex, never I/O. Staleness triggers a single-flight async refresh in a goroutine with its own 90 s timeout, fully decoupled from the caller's context. [`thirdparty/remote_cache.go:L13-78`](https://github.com/erpc/erpc/blob/main/thirdparty/remote_cache.go#L13-L78) **URL shorthand scheme.** Any upstream whose endpoint scheme is not `http`, `https`, `grpc`, or `grpc+bds` is converted to a `ProviderConfig` at config-defaults time. The `evm+` prefix is stripped first (`evm+alchemy://` equals `alchemy://`). The original upstream config (failsafe, rate limits, method filters, etc.) is placed into `overrides: {"*": ...}` so it applies to every generated upstream. The provider id defaults to `-` (process-wide counter per vendor name). Unknown scheme is a config-load error. [`common/defaults.go:L1113-1241`](https://github.com/erpc/erpc/blob/main/common/defaults.go#L1113-L1241) **Default providers.** A project with zero upstreams, zero providers, and no CLI `--endpoint` flags automatically gets `{id:"public", vendor:"repository"}` + `{id:"envio", vendor:"envio"}` injected with all defaults, and logs a warning. Setting `providers: []` explicitly still satisfies the zero-providers condition — the injection fires unless you have at least one upstream or one CLI endpoint. [`common/defaults.go:L1113-1146`](https://github.com/erpc/erpc/blob/main/common/defaults.go#L1113-L1146) **Vendors also serve statically-configured upstreams.** At `NewUpstream`, `LookupByUpstream` finds a vendor by explicit `upstream.vendorName` (exact match, wins — if the name is unknown, `OwnsUpstream` is NOT consulted and the result is nil) or the first vendor whose `OwnsUpstream` matches the endpoint (in registration order). If no vendor matches, `vendorName` is guessed as `unknown-` from the endpoint host. The vendor's `GenerateConfigs` is invoked with `settings == nil` to apply vendor defaults (e.g. dRPC forcing `autoIgnoreUnsupportedMethods: false`, envio's method allow-list). Vendor-specific JSON-RPC error mapping fires before generic normalization: `GetVendorSpecificErrorIfAny` is called on the upstream's attached vendor first. [`architecture/evm/error_normalizer.go:L878-900`](https://github.com/erpc/erpc/blob/main/architecture/evm/error_normalizer.go#L878-L900) [`thirdparty/vendors_registry.go:L54-71`](https://github.com/erpc/erpc/blob/main/thirdparty/vendors_registry.go#L54-L71) ### Config schema #### `projects[].providers[]` — ProviderConfig | field | type | default | behavior / footguns | |---|---|---|---| | `id` | string | vendor name (`p.Id = p.Vendor`) | Unique handle; appears in `` placeholder and bootstrap task names `network//provider/`. Shorthand-converted providers get `-` (process-wide counter). No duplicate-id check — two providers with the same default id both run and may produce upstream-id collisions. [`common/defaults.go:L1474-1476`](https://github.com/erpc/erpc/blob/main/common/defaults.go#L1474-L1476) [`util/ids.go:L32-41`](https://github.com/erpc/erpc/blob/main/util/ids.go#L32-L41) | | `vendor` | string | — (required) | Must exactly match one of 23 registered `Name()` strings: `goldsky, alchemy, blastapi, conduit, drpc, dwellir, envio, etherspot, infura, pimlico, quicknode, llama, thirdweb, repository, superchain, tenderly, chainstack, onfinality, erpc, blockpi, ankr, routemesh, blockdaemon`. Unknown vendor → startup error. [`thirdparty/vendors_registry.go:L12-34`](https://github.com/erpc/erpc/blob/main/thirdparty/vendors_registry.go#L12-L34) | | `settings` | `map[string]any` (VendorSettings) | `nil` | Free-form, vendor-interpreted. Redacted in JSON/YAML marshaling (never echoed in admin dump). See per-vendor tables below. [`common/config.go:L667-700`](https://github.com/erpc/erpc/blob/main/common/config.go#L667-L700) | | `onlyNetworks` | `[]string` | `nil` | Exhaustive allow-list (`evm:` format, validated). If set, vendor's own `SupportsNetwork` is **not** called. Mutually exclusive with `ignoreNetworks` (validation error). [`thirdparty/provider.go:L42-49`](https://github.com/erpc/erpc/blob/main/thirdparty/provider.go#L42-L49) | | `ignoreNetworks` | `[]string` | `nil` | Exact-match deny-list checked **before** `onlyNetworks` and before vendor. Same `evm:` validation. Omitted from `MarshalJSON` (present in YAML marshal). [`thirdparty/provider.go:L34-40`](https://github.com/erpc/erpc/blob/main/thirdparty/provider.go#L34-L40) | | `upstreamIdTemplate` | string | `"-"` | Placeholders: ``, ``, `` (full network id e.g. `evm:1`), `` (numeric; `N/A` for non-evm networks). Validation requires non-empty. [`common/defaults.go:L1477-1479`](https://github.com/erpc/erpc/blob/main/common/defaults.go#L1477-L1479) | | `overrides` | `map[string]*UpstreamConfig` | `nil` | Keys are wildcard patterns (WildcardMatch grammar: `*`, `?`, `\|` OR, `&` AND, `!` NOT). First match from Go map iteration (nondeterministic for overlapping keys) is copied as the base config; no match → fresh `UpstreamConfig` with defaults. Pattern-match errors silently skipped. Each override validated with endpoint check skipped. [`thirdparty/provider.go:L77-101`](https://github.com/erpc/erpc/blob/main/thirdparty/provider.go#L77-L101) | #### URL shorthand derivation | shorthand | derived settings | |---|---| | `goldsky://[?tier=&secret=]` | `{secret: authority}` + `tier`/`secret` from query (host is always `edge.goldsky.com`) | | `alchemy://` | `{apiKey: key}` | | `ankr://` | `{apiKey: key}` | | `blastapi://` | `{apiKey: key}` | | `blockdaemon://` | `{apiKey: key}` | | `blockpi://` | `{apiKey: key}` | | `chainstack://[?project=®ion=&…]` | `{apiKey: key}` + all query params copied verbatim | | `conduit://` | `{apiKey: key}` | | `drpc://` | `{apiKey: key}` | | `dwellir://` | `{apiKey: key}` | | `envio://` | `{rootDomain: host}` | | `erpc://[/][?secret=]` | `{endpoint: "https://"+host+"/"+path}` + `secret` from query | | `etherspot://` | `{apiKey: key}` | | `infura://` | `{apiKey: key}` | | `llama://` | `{apiKey: key}` | | `onfinality://` | `{apiKey: key}` | | `pimlico://` | `{apiKey: key}` | | `quicknode://[?tagIds=1,2&tagLabels=a,b]` | `{apiKey: key}` + parsed `tagIds`/`tagLabels` | | `repository:///[?query]` | `{repositoryUrl: "https://"+host+"/"+path+"?"+query}` | | `routemesh:///rpc//` | `{baseURL: host, apiKey: pathParts[2]}` — path must match exactly | | `superchain://` | `{registryUrl: host+path}` | | `tenderly://` | `{apiKey: key}` | | `thirdweb://` | `{clientId: host}` | | unknown scheme | config-load error: `unsupported vendor name in vendor.settings: ` | ### Vendor catalog #### goldsky [Goldsky Edge](https://docs.goldsky.com/edge-rpc/introduction) — a multi-region public RPC service built on eRPC. One secret token covers every chain Goldsky serves; upstreams are lazy-generated per network. SupportsNetwork probes live `eth_chainId` against `https://edge.goldsky.com/{tier}/evm/{chainId}?secret={secret}` (10 s timeout) and confirms the returned chain id matches — so the secret's plan authoritatively decides chain support. A missing `secret` makes SupportsNetwork return `(false, nil)` (silent skip, no retry storm). `tier` defaults to `standard`; `endpoint` defaults to `https://edge.goldsky.com` and may be overridden for enterprise/custom deployments (an explicit path on the base URL is respected verbatim and the tier route is not injected). The secret can also be supplied out-of-band via the `X-ERPC-Secret-Token` header on a preset endpoint. Probe clients are cached per `"{url}-{chainId}"`. OwnsUpstream: `goldsky://`/`evm+goldsky://` prefix, `vendorName=="goldsky"`, or endpoint contains `edge.goldsky.com`. | key | required | default | |---|---|---| | `secret` | yes (auth fails without it) | — | | `tier` | no | `standard` | | `endpoint` | no | `https://edge.goldsky.com` | [`thirdparty/goldsky.go:L31-249`](https://github.com/erpc/erpc/blob/main/thirdparty/goldsky.go#L31-L249) #### alchemy Chain discovery: remote catalog (default `https://app-api.alchemy.com/trpc/config.getNetworkConfig`) keyed by URL via `RemoteDataCache`; **cold-start fallback** to a 134-chain built-in map; after a successful fetch the API result is merged with static defaults (API wins on conflict, static fills gaps). URL template: `https://{subdomain}.g.alchemy.com/v2/{apiKey}`. Error mapping: `-32600` + authenticated message → `ErrEndpointUnauthorized`; code `3` → EVM revert. | key | required | default | |---|---|---| | `apiKey` | yes | — | | `chainsUrl` | no | `https://app-api.alchemy.com/trpc/config.getNetworkConfig` | | `recheckInterval` | no | `24h` | OwnsUpstream: `alchemy://`/`evm+alchemy://` prefix, `vendorName=="alchemy"`, or endpoint contains `.alchemy.com`/`.alchemyapi.io`. [`thirdparty/alchemy.go:L156-326`](https://github.com/erpc/erpc/blob/main/thirdparty/alchemy.go#L156-L326) #### ankr Static map of 47 chains. URL: `https://rpc.ankr.com/{netName}/{apiKey}` (URL-escaped). OwnsUpstream: scheme prefixes or `rpc.ankr.com`. | key | required | default | |---|---|---| | `apiKey` | yes | — | [`thirdparty/ankr.go:L15-144`](https://github.com/erpc/erpc/blob/main/thirdparty/ankr.go#L15-L144) #### blastapi Static map of 79 chains. URL: `https://{netName}.blastapi.io/{apiKey}`; Avalanche appends `/ext/bc/C/rpc`. OwnsUpstream: scheme prefixes or `.blastapi.io`. | key | required | default | |---|---|---| | `apiKey` | yes | — | [`thirdparty/blastapi.go:L15-181`](https://github.com/erpc/erpc/blob/main/thirdparty/blastapi.go#L15-L181) #### blockdaemon Static map of 18 chains with heterogeneous path shapes. **Auth is always header-based**: `apiKey` is injected as `Authorization: Bearer ` on every request regardless of whether the endpoint was preset or generated — a static upstream pointing at `svc.blockdaemon.com` fails at `NewUpstream` without `apiKey`. HTTP 401 → `ErrEndpointUnauthorized`. | key | required | default | |---|---|---| | `apiKey` | yes (even with preset endpoint) | — | [`thirdparty/blockdaemon.go:L24-156`](https://github.com/erpc/erpc/blob/main/thirdparty/blockdaemon.go#L24-L156) #### blockpi Static map of 56 chains (exported `BlockPiNetworkNames`). URL: `https://{netName}.blockpi.network/v1/rpc/{escapedKey}`; chain `movement` uses `/rpc/v1/{escapedKey}/v1`. Error mapping: message containing `apikey is on another chain` → `ErrEndpointUnauthorized`. OwnsUpstream: scheme prefixes or `.blockpi.network`. | key | required | default | |---|---|---| | `apiKey` | yes | — | [`thirdparty/blockpi.go:L15-173`](https://github.com/erpc/erpc/blob/main/thirdparty/blockpi.go#L15-L173) #### chainstack Account-level node discovery: paginated GET `https://api.chainstack.com/v1/nodes/` with `Authorization: Bearer {apiKey}` and optional filter params; chain ids discovered by `eth_chainId` probe per node (concurrency-limited, semaphore 10, 10 s client timeout). Cache key includes all filter values so providers sharing an apiKey but different filters get separate snapshots. Missing `apiKey` → `(false, nil)` (silent skip). Cold cache → `ErrRemoteCacheCold`. GenerateConfigs fans out one upstream per matching running node; upstream endpoint = `https_endpoint + "/" + auth_key`; id suffix `-`. Node decode is intentionally lenient: each node is decoded individually from the paginated response and malformed nodes (type mismatches, missing fields) are skipped rather than failing the entire refresh. OwnsUpstream: scheme prefixes or `.core.chainstack.com`. | key | required | default | |---|---|---| | `apiKey` | yes | — | | `recheckInterval` | no | `1h` | | `project` | no | `""` | | `organization` | no | `""` | | `region` | no | `""` | | `provider` | no | `""` | | `type` | no | `""` | [`thirdparty/chainstack.go:L51-429`](https://github.com/erpc/erpc/blob/main/thirdparty/chainstack.go#L51-L429) #### conduit Dynamic-only discovery from `networksUrl`; no static fallback → cold start returns `ErrRemoteCacheCold`. URL: `{httpEndpoint}/{apiKey}`. OwnsUpstream: scheme prefixes or `vendorName=="conduit"` only (no domain heuristic — chains live on customer domains). Preset endpoint bypasses discovery entirely. Error mapping: `-32600` + auth/api-key message → `ErrEndpointUnauthorized`; message `"limit exceeded"`/`"capacity limit"` → `ErrEndpointCapacityExceeded`; code in range `[-32099, -32000]` → `ErrEndpointServerSideException` (note: as written the condition `code >= -32000 && code <= -32099` is never true — same impossible-range pattern as alchemy, so this branch is dead code). | key | required | default | |---|---|---| | `apiKey` | yes | — | | `networksUrl` | no | `https://api.conduit.xyz/public/network/all` | | `recheckInterval` | no | `24h` | [`thirdparty/conduit.go:L16-222`](https://github.com/erpc/erpc/blob/main/thirdparty/conduit.go#L16-L222) #### drpc Remote catalog (default `https://lb.drpc.org/networks`) with a 150-chain static fallback; after a successful fetch the fallback is **fully replaced** (no merge). **Forces `autoIgnoreUnsupportedMethods: false`** on every config (even preset endpoints) because dRPC transiently returns method-not-found responses that must stay retryable. URL: `https://lb.drpc.org/ogrpc?network={name}&dkey={apiKey}`. Catalog filters to premium EVM JSON-RPC chains only (`blockchain_type=="eth" && api_type=="jsonrpc" && has_premium`); when multiple names map to one chain id, the highest `priority` value wins. Error mapping: message `"token is invalid"` → `ErrEndpointUnauthorized`; `"ChainException: Unexpected error (code=40000)"` or `"invalid block range"` → `ErrEndpointMissingData`. | key | required | default | |---|---|---| | `apiKey` | yes | — | | `chainsUrl` | no | `https://lb.drpc.org/networks` | | `recheckInterval` | no | `24h` | [`thirdparty/drpc.go:L19-422`](https://github.com/erpc/erpc/blob/main/thirdparty/drpc.go#L19-L422) #### dwellir Static map of 71 chains → subdomain prefixes. URL: `https://{subdomain}.dwellir.com/{apiKey}`. OwnsUpstream: only `dwellir://` prefix (NOT `evm+dwellir://`) or `.dwellir.com` — the `evm+` form still works because shorthand conversion runs first. Avalanche appends `/ext/bc/C/rpc` to the URL. `SupportsNetwork` quirk: an unparseable numeric chain id part returns `(false, nil)` rather than an error (unlike every other static-map vendor which propagates the parse error). | key | required | default | |---|---|---| | `apiKey` | yes | — | [`thirdparty/dwellir.go:L15-180`](https://github.com/erpc/erpc/blob/main/thirdparty/dwellir.go#L15-L180) #### envio **Method allow-list injected on every upstream** (even preset static ones): `ignoreMethods: ["*"]` + 14 read-oriented methods (`eth_getLogs`, `eth_blockNumber`, `eth_getBlockByNumber`, etc.). SupportsNetwork: 61 known chain ids short-circuit true; unknown chains are live-probed via `eth_chainId` (10 s timeout; TLS error with `"failed to verify certificate"` → false). URL: `https://{chainId}.{rootDomain}[/{apiKey}]`. | key | required | default | |---|---|---| | `rootDomain` | no | `rpc.hypersync.xyz` | | `apiKey` | no | `""` | [`thirdparty/envio.go:L20-258`](https://github.com/erpc/erpc/blob/main/thirdparty/envio.go#L20-L258) #### erpc Chains to another eRPC instance. SupportsNetwork probes live `eth_chainId`. `parseEndpointURL` appends `/{chainId}` and optionally sets `?secret=`. Port-based scheme inference applies in `settings.endpoint`; the shorthand `erpc://host:8080` forces HTTPS. Probe clients are cached per `"{url}-{chainId}"`. | key | required | default | |---|---|---| | `endpoint` | yes | — | | `secret` | no | `""` | [`thirdparty/erpc.go:L35-265`](https://github.com/erpc/erpc/blob/main/thirdparty/erpc.go#L35-L265) #### etherspot ERC-4337 bundler (Skandha). **Method allow-list injected**: `ignoreMethods: ["*"]` + 6 bundler methods (`skandha_*`, `eth_sendUserOperation`, etc.). Static map of 15 mainnets + 11 testnets. URL shape differs by network type: mainnet `https://{networkName}-bundler.etherspot.io/`, testnet `https://testnet-rpc.etherspot.io/v1/{chainId}`; `?apikey=` appended unless apiKey is `"public"` or empty. | key | required | default | |---|---|---| | `apiKey` | yes | — | [`thirdparty/etherspot.go:L15-146`](https://github.com/erpc/erpc/blob/main/thirdparty/etherspot.go#L15-L146) #### infura Static map of 31 chains. URL: `https://{netName}.infura.io/v3/{apiKey}`. Error mapping: `-32600` auth message → unauthorized; `-32001`/`-32004` → unsupported; `-32005` → capacity exceeded. | key | required | default | |---|---|---| | `apiKey` | yes | — | [`thirdparty/infura.go:L15-163`](https://github.com/erpc/erpc/blob/main/thirdparty/infura.go#L15-L163) #### llama Static map of 6 chains (eth, arbitrum, base, binance, optimism, polygon). URL: `https://{netName}.llamarpc.com/{apiKey}`. OwnsUpstream matches `.llamarpc.com` domain only (no `llama://` scheme check — shorthand still works because conversion runs first). Cloudflare rate-limit message (`code: 1015`) → `ErrEndpointCapacityExceeded`. | key | required | default | |---|---|---| | `apiKey` | yes | — | [`thirdparty/llama.go:L14-97`](https://github.com/erpc/erpc/blob/main/thirdparty/llama.go#L14-L97) #### onfinality Static map of 25 chains. URL: `https://{netName}.api.onfinality.io/rpc?apikey={escapedKey}` (apiKey as query param). OwnsUpstream: scheme prefixes or `.api.onfinality.io`. | key | required | default | |---|---|---| | `apiKey` | yes | — | [`thirdparty/onfinality.go:L15-122`](https://github.com/erpc/erpc/blob/main/thirdparty/onfinality.go#L15-L122) #### pimlico ERC-4337 bundler/paymaster. **Method allow-list injected**: `ignoreMethods: ["*"]` + 11 bundler/paymaster methods (`pimlico_*`, `pm_*`, `eth_sendUserOperation`, etc.). SupportsNetwork: 67-chain static set short-circuits true; unknown chains probe live `eth_chainId` — **missing `apiKey` is an error** (unlike quicknode/chainstack which return `false, nil`). URL: `"public"` → `https://public.pimlico.io/v2/{chainId}/rpc`; else `https://api.pimlico.io/v2/{chainId}/rpc?apikey={apiKey}`. | key | required | default | |---|---|---| | `apiKey` | yes (error if missing, even for SupportsNetwork) | — | [`thirdparty/pimlico.go:L21-252`](https://github.com/erpc/erpc/blob/main/thirdparty/pimlico.go#L21-L252) #### quicknode Account endpoint discovery: paginated GET `https://api.quicknode.com/v0/endpoints?limit=100&offset=N` with `x-api-key` header and optional tag filters; a response-level `error` field aborts the refresh; chain ids discovered by `eth_chainId` probe per endpoint (concurrency-limited, semaphore 10, 10 s timeout). **Cache key is `apiKey` only** — two providers sharing an apiKey but different `tagIds`/`tagLabels` share one endpoint snapshot. Missing `apiKey` → `(false, nil)`. Cold cache → `ErrRemoteCacheCold`. GenerateConfigs fans out one upstream per matching endpoint; id `{upstreamId}-{endpointID}`. Full error mapping: `-32614` or (`eth_getLogs` + message `"limited to"`) → `ErrEndpointRequestTooLarge`; `-32009`/`-32007` → capacity exceeded; `-32612`/`-32613` → `ErrEndpointUnsupported`; message `"failed to parse"` → client-side, not retryable toward network; `-32010` (tx cost exceeds gas limit) → client-side but network-retryable (per-client gas caps differ); `-32602` + `"cannot unmarshal hex string"` → invalid-argument client-side, not retryable; message `"UNAUTHORIZED"` → unauthorized; code `3` → EVM revert. Note: the error handler builds a fresh local details map, so per-request `details` passed by the caller are not modified. | key | required | default | |---|---|---| | `apiKey` | yes | — | | `recheckInterval` | no | `1h` | | `tagIds` | no | `nil` | | `tagLabels` | no | `nil` | [`thirdparty/quicknode.go:L44-452`](https://github.com/erpc/erpc/blob/main/thirdparty/quicknode.go#L44-L452) #### repository Fetches `repositoryUrl` returning `{"": {"endpoints": [...]}}`. No static fallback → `ErrRemoteCacheCold` on cold start. **Defaults `autoIgnoreUnsupportedMethods: true`** — public RPCs are inconsistent in method support. GenerateConfigs fans out one upstream per http endpoint (http/https prefix only; ws/wss skipped); ids embed `RedactEndpoint(ep)` (`https#redacted=ab12c`) so dashboards can distinguish endpoints without exposing them. Each generated upstream gets deep copies of `Evm`, `JsonRpc`, `Failsafe` (per-element), `RateLimitAutoTune`, and `Tags` from the base config, and inherits `IgnoreMethods`, `AllowMethods`, and `RateLimitBudget`. OwnsUpstream: only `repository://`/`evm+repository://` scheme prefixes (no domain matching — generated upstreams have arbitrary third-party hosts). One half of the implicit default provider pair. | key | required | default | |---|---|---| | `repositoryUrl` | no | `https://evm-public-endpoints.erpc.cloud` | | `recheckInterval` | no | `1h` | [`thirdparty/repository.go:L17-232`](https://github.com/erpc/erpc/blob/main/thirdparty/repository.go#L17-L232) #### routemesh No chain catalog — SupportsNetwork always probes live `eth_chainId`. Missing `apiKey` → error (provider task retries forever). URL: `https://{baseURL}/rpc/{chainId}/{apiKey}`. Shorthand requires exact path shape `/rpc//` or config-load error. | key | required | default | |---|---|---| | `apiKey` | yes (error if missing) | — | | `baseURL` | no | `lb.routemes.sh` | [`thirdparty/routemesh.go:L20-167`](https://github.com/erpc/erpc/blob/main/thirdparty/routemesh.go#L20-L167) #### superchain OP Superchain registry. `parseSuperchainSpec` accepts: `github.com/org/repo` (defaults branch `main`, file `chainList.json`), `github.com/org/repo/branch`, `github.com/org/repo/branch/path/to/file.json`, GitHub UI blob/tree URLs (the `/blob/` or `/tree/` segment is stripped), full `http(s)://` URLs passed through, or bare `domain/path` (https prepended). GitHub specs are rewritten to raw.githubusercontent.com. Registry JSON is an array of `{chainId, rpc:[...]}`; entries with chainId ≤ 0 or empty rpc list dropped; no static fallback → `ErrRemoteCacheCold`. GenerateConfigs fans out one upstream per rpc URL with id suffix `-{index}` (e.g. `{id}-0`, `{id}-1`, …). OwnsUpstream: scheme prefixes or `vendorName=="superchain"` only (no domain matching). | key | required | default | |---|---|---| | `registryUrl` | no | `https://raw.githubusercontent.com/ethereum-optimism/superchain-registry/main/chainList.json` | | `recheckInterval` | no | `24h` | [`thirdparty/superchain.go:L16-312`](https://github.com/erpc/erpc/blob/main/thirdparty/superchain.go#L16-L312) #### tenderly Remote catalog from hardcoded `https://api.tenderly.co/api/v1/supported-networks` (not overridable via settings — there is no `chainsUrl`-style override). No static fallback → `ErrRemoteCacheCold`. Catalog decodes `chain_id` + `network_slugs`; slug preference: `node_rpc_slug` > `vnet_rpc_slug` > `explorer_slug`; entries with no usable slug or unparseable chain id are skipped. URL: `https://{slug}.gateway.tenderly.co/{apiKey}`. OwnsUpstream: scheme prefixes, `vendorName=="tenderly"`, or `.gateway.tenderly.co`. | key | required | default | |---|---|---| | `apiKey` | yes | — | | `recheckInterval` | no | `24h` | [`thirdparty/tenderly.go:L34-285`](https://github.com/erpc/erpc/blob/main/thirdparty/tenderly.go#L34-L285) #### thirdweb No chain catalog — SupportsNetwork always probes live `eth_chainId`. Missing `clientId` → error. URL: `https://{chainId}.rpc.thirdweb.com/{clientId}`. | key | required | default | |---|---|---| | `clientId` | yes (error if missing) | — | [`thirdparty/thirdweb.go:L35-141`](https://github.com/erpc/erpc/blob/main/thirdparty/thirdweb.go#L35-L141) ### Worked examples All patterns below are distilled from real production fleets; comments explain the non-obvious choices. **1. Multi-vendor setup with score de-weighting.** Production fleets use Alchemy as the premium primary and cheaper providers (dRPC, Envio) as volume fillers. The `scoreMultipliers` `overall` factor deliberately de-weights weaker vendors so the scorer favors Alchemy on latency-sensitive methods while still using them as backfill: **Config path:** `projects[].providers[]` **YAML — `erpc.yaml`:** ```yaml projects: - id: main providers: - id: alchemy vendor: alchemy settings: # premium vendor — no score penalty apiKey: \${ALCHEMY_KEY} # keep Alchemy away from chains where it adds no value or costs more ignoreNetworks: [evm:999] overrides: # catch-all: apply rate limit budget + light de-weight so fast vendors win "evm:*": rateLimitBudget: alchemy-global routing: scoreMultipliers: - network: "" method: "" # 0.2 = use as 20% of baseline score — Alchemy only wins when # others are slow, erroring, or behind on block head overall: 0.2 - id: drpc vendor: drpc settings: apiKey: \${DRPC_KEY} - id: envio vendor: envio settings: rootDomain: rpc.hypersync.xyz ``` **TypeScript — `erpc.ts`:** ```typescript import { createConfig } from "@erpc-cloud/config"; export default createConfig({ projects: [{ id: "main", providers: [ { id: "alchemy", vendor: "alchemy", settings: { apiKey: process.env.ALCHEMY_KEY }, ignoreNetworks: ["evm:999"], overrides: { "evm:*": { rateLimitBudget: "alchemy-global", routing: { scoreMultipliers: [{ network: "", method: "", overall: 0.2 }], }, }, }, }, { id: "drpc", vendor: "drpc", settings: { apiKey: process.env.DRPC_KEY }, }, { id: "envio", vendor: "envio", settings: { rootDomain: "rpc.hypersync.xyz" }, }, ], }], }); ``` **2. Per-chain getLogs splitting via provider overrides.** High-throughput chains (Arbitrum, Sei) routinely hit block-range limits when indexers issue large `eth_getLogs` queries. Production sets `getLogsAutoSplittingRangeThreshold` per chain inside the provider's `overrides` so every upstream that vendor auto-generates for that chain inherits the limit without wiring individual upstreams: **Config path:** `projects[].providers[]` **YAML — `erpc.yaml`:** ```yaml projects: - id: main providers: - id: quicknode vendor: quicknode settings: apiKey: \${QN_KEY} ignoreNetworks: [evm:1, evm:8453, evm:42161] overrides: # catch-all: slow down state polling on every QN chain (it polls fast by default) "evm:*": evm: # 10s is a safe floor; most chains don't need sub-second head tracking statePollerInterval: 10s routing: scoreMultipliers: - network: "" method: "" # 0.3 = mid-tier weight; QN is good but not the fastest in tests overall: 0.3 # Arbitrum produces enormous logs — split at 1000 blocks before upstream rejects "evm:42161": evm: getLogsAutoSplittingRangeThreshold: 1000 # Sei has aggressive range limits — split at 500 "evm:1329": evm: getLogsAutoSplittingRangeThreshold: 500 ``` **TypeScript — `erpc.ts`:** ```typescript import { createConfig } from "@erpc-cloud/config"; export default createConfig({ projects: [{ id: "main", providers: [{ id: "quicknode", vendor: "quicknode", settings: { apiKey: process.env.QN_KEY }, ignoreNetworks: ["evm:1", "evm:8453", "evm:42161"], overrides: { "evm:*": { evm: { statePollerInterval: "10s" }, routing: { scoreMultipliers: [{ network: "", method: "", overall: 0.3 }], }, }, "evm:42161": { evm: { getLogsAutoSplittingRangeThreshold: 1000 } }, "evm:1329": { evm: { getLogsAutoSplittingRangeThreshold: 500 } }, }, }], }], }); ``` **3. Provider-level method exclusions to avoid bad upstreams.** Some vendors (dwellir, blockpi) do not support `trace_filter` reliably on testnets. Production adds `ignoreMethods` per-chain inside `overrides` so auto-generated upstreams for those chains never receive trace requests that would 405-error and burn retry budget: **Config path:** `projects[].providers[]` **YAML — `erpc.yaml`:** ```yaml projects: - id: main providers: - id: dwellir vendor: dwellir settings: apiKey: \${DWELLIR_KEY} # skip chains covered by better vendors — avoids redundant SupportsNetwork probes ignoreNetworks: [evm:1, evm:137, evm:42161, evm:8453] overrides: # Sepolia and BSC testnet: trace_filter returns errors on dwellir — exclude it # so the scorer doesn't keep retrying it "evm:11155111": ignoreMethods: [trace_filter] "evm:97": ignoreMethods: [trace_filter] ``` **TypeScript — `erpc.ts`:** ```typescript import { createConfig } from "@erpc-cloud/config"; export default createConfig({ projects: [{ id: "main", providers: [{ id: "dwellir", vendor: "dwellir", settings: { apiKey: process.env.DWELLIR_KEY }, ignoreNetworks: ["evm:1", "evm:137", "evm:42161", "evm:8453"], overrides: { "evm:11155111": { ignoreMethods: ["trace_filter"] }, "evm:97": { ignoreMethods: ["trace_filter"] }, }, }], }], }); ``` **4. ERC-4337 bundler alongside general RPC.** Pimlico and etherspot auto-inject method allow-lists so only bundler methods route to them — combine with a general-purpose provider for all other methods. No manual `allowMethods` needed; the vendor injects it at `NewUpstream` time: **Config path:** `projects[].upstreams[]` **YAML — `erpc.yaml`:** ```yaml projects: - id: main upstreams: - endpoint: alchemy://\${ALCHEMY_KEY} # pimlico auto-injects: ignoreMethods: ["*"] + allowMethods: [eth_sendUserOperation, # pimlico_*, pm_*] — bundler traffic routes here automatically - endpoint: pimlico://\${PIMLICO_KEY} ``` **TypeScript — `erpc.ts`:** ```typescript import { createConfig } from "@erpc-cloud/config"; export default createConfig({ projects: [{ id: "main", upstreams: [ { endpoint: \`alchemy://\${process.env.ALCHEMY_KEY}\` }, // pimlico auto-injects ignoreMethods/allowMethods — no manual filter needed { endpoint: \`pimlico://\${process.env.PIMLICO_KEY}\` }, ], }], }); ``` **5. QuickNode with tag-based endpoint groups.** Use separate `apiKey` values per tag group — the RemoteDataCache is keyed by `apiKey` only, so two providers sharing one key with different `tagIds`/`tagLabels` both see whichever snapshot the first refresh produced: **Config path:** `projects[].providers[]` **YAML — `erpc.yaml`:** ```yaml projects: - id: main providers: # archive-grade endpoints tagged in the QuickNode dashboard as "archive" - id: quicknode-archive vendor: quicknode settings: # must be a DIFFERENT apiKey than the realtime group — # same apiKey + different tagLabels still shares one endpoint snapshot apiKey: \${QN_ARCHIVE_KEY} tagLabels: archive # fast tip-of-chain endpoints on a separate key - id: quicknode-realtime vendor: quicknode settings: apiKey: \${QN_REALTIME_KEY} tagLabels: realtime ``` **TypeScript — `erpc.ts`:** ```typescript import { createConfig } from "@erpc-cloud/config"; export default createConfig({ projects: [{ id: "main", providers: [ { id: "quicknode-archive", vendor: "quicknode", // must be a DIFFERENT apiKey than the realtime group settings: { apiKey: process.env.QN_ARCHIVE_KEY, tagLabels: "archive" }, }, { id: "quicknode-realtime", vendor: "quicknode", settings: { apiKey: process.env.QN_REALTIME_KEY, tagLabels: "realtime" }, }, ], }], }); ``` ### Best practices - Use **URL shorthands** (`alchemy://KEY`) for the common case — they are equivalent to a full `providers[]` entry with a `"*"` override and avoid boilerplate. - Use **`onlyNetworks`** to restrict expensive vendors to the chains you actually need; it also prevents the vendor's `SupportsNetwork` from being called for irrelevant chains. - Use **exact `evm:` keys** in `overrides` — never mix a catch-all `"*"` with more-specific patterns in the same map. Go map iteration is nondeterministic; overlapping keys produce different results across restarts. - For **QuickNode** with multiple logical endpoint groups, use a separate `apiKey` per group (provisioned in the QuickNode dashboard). Sharing one `apiKey` with different `tagIds`/`tagLabels` shares one endpoint snapshot due to the apiKey-only cache key. - **`recheckInterval` cannot be set from YAML or TypeScript config.** The type assertion `.(time.Duration)` fails on YAML strings and JSON numbers. Use programmatic Go config (`common.VendorSettings{"recheckInterval": 12 * time.Hour}`) if you need a custom freshness window. - Vendors with no static fallback (conduit, superchain, tenderly, quicknode, chainstack, repository) return `ErrRemoteCacheCold` on cold start. The initializer retries with backoff, but first requests for those networks will see `ErrNetworkInitializing` until the remote catalog fetches. Pair these vendors with a fallback provider that has a static map if cold-start latency matters. - **Do not use `providers: []` to "disable" auto-injection** — an explicit empty providers list still triggers default injection if upstreams is also empty. Provide at least one upstream or one provider to take full control. ### Edge cases & gotchas 1. **`recheckInterval` from YAML is silently ignored** — every vendor reads it with `.(time.Duration)`. YAML scalars decode to `string`/`int64` so the assertion fails and the vendor default applies. Only Go programmatic config can set it. [`thirdparty/repository.go:L56-59`](https://github.com/erpc/erpc/blob/main/thirdparty/repository.go#L56-L59) 2. **Cold-start contract differs by vendor**: alchemy/drpc fall back to built-in maps; conduit/superchain/tenderly/quicknode/chainstack/repository return `ErrRemoteCacheCold` and block until the first remote fetch lands. [`thirdparty/remote_cache.go:L85`](https://github.com/erpc/erpc/blob/main/thirdparty/remote_cache.go#L85) 3. **drpc replaces its fallback after a successful fetch** (no merge); alchemy merges fetched data with its static defaults (API wins on conflict, static fills gaps). [`thirdparty/drpc.go:L360-422`](https://github.com/erpc/erpc/blob/main/thirdparty/drpc.go#L360-L422) [`thirdparty/alchemy.go:L386-405`](https://github.com/erpc/erpc/blob/main/thirdparty/alchemy.go#L386-L405) 4. **Missing-apiKey behavior is inconsistent**: pimlico/routemesh/thirdweb `SupportsNetwork` return an error (provider task retries forever); quicknode/chainstack return `(false, nil)` and are silently skipped. [`thirdparty/pimlico.go:L120-123`](https://github.com/erpc/erpc/blob/main/thirdparty/pimlico.go#L120-L123) 5. **blockdaemon requires `apiKey` even with a preset endpoint** — static upstreams pointing at `svc.blockdaemon.com` fail at `NewUpstream` because `GenerateConfigs` is called with `settings=nil`. [`thirdparty/blockdaemon.go:L85-88`](https://github.com/erpc/erpc/blob/main/thirdparty/blockdaemon.go#L85-L88) 6. **Impossible range conditions in alchemy/conduit error mapping**: alchemy's `code >= -32099 && code <= -32599` can never be true; only the message-match and code-3 branches actually fire. [`thirdparty/alchemy.go:L275-326`](https://github.com/erpc/erpc/blob/main/thirdparty/alchemy.go#L275-L326) 7. **infura's Avalanche `/ext/bc/C/rpc` branch is unreachable** — map keys are `avalanche-mainnet`/`avalanche-fuji` but branch tests `ava-mainnet`/`ava-testnet`. [`thirdparty/infura.go:L90-93`](https://github.com/erpc/erpc/blob/main/thirdparty/infura.go#L90-L93) 8. **`overrides` first-match is Go map-iteration order** (nondeterministic for overlapping patterns). Pattern-match errors are silently skipped. [`thirdparty/provider.go:L81-91`](https://github.com/erpc/erpc/blob/main/thirdparty/provider.go#L81-L91) 9. **quicknode cache key is `apiKey` only** — two providers with the same apiKey but different `tagIds`/`tagLabels` share one endpoint snapshot. Chainstack includes filters in its cache key and is safe. [`thirdparty/quicknode.go:L184-205`](https://github.com/erpc/erpc/blob/main/thirdparty/quicknode.go#L184-L205) 10. **`erpc://host:8080` shorthand forces HTTPS** — `buildProviderSettings` hardcodes `https://`. Use `providers[].settings.endpoint` with an explicit URL for non-TLS targets. [`common/defaults.go:L1389-1404`](https://github.com/erpc/erpc/blob/main/common/defaults.go#L1389-L1404) 11. **`onlyNetworks`/`ignoreNetworks` are exact string matches** (not wildcards), require `evm:`, and are mutually exclusive. `ignoreNetworks` is dropped from `MarshalJSON`. [`thirdparty/provider.go:L34-49`](https://github.com/erpc/erpc/blob/main/thirdparty/provider.go#L34-L49) 12. **`os.ExpandEnv` runs on every provider-generated endpoint** — a literal `$` in an API key is substituted from the environment (often to empty). Static upstreams do not get this treatment. [`thirdparty/provider.go:L63-71`](https://github.com/erpc/erpc/blob/main/thirdparty/provider.go#L63-L71) 13. **Provider ids must be unique only by convention** — no duplicate-id check; two providers with the same default id both run and may produce upstream-id collisions. [`thirdparty/providers_registry.go:L15-34`](https://github.com/erpc/erpc/blob/main/thirdparty/providers_registry.go#L15-L34) 14. **repository upstream ids embed a redacted endpoint** (`https#redacted=ab12c` = scheme + 5-char sha256 prefix) so dashboards can distinguish public endpoints without exposing them. [`util/redact.go:L10-36`](https://github.com/erpc/erpc/blob/main/util/redact.go#L10-L36) 15. **OP-stack `"sender is over rate limit"` is the only capacity error with `retryableTowardNetwork=false`** — all OP providers proxy the same sequencer, so retrying on a different upstream hits the same limit. Expect this error to surface to clients on OP-stack chains. [`architecture/evm/error_normalizer.go:L125-139`](https://github.com/erpc/erpc/blob/main/architecture/evm/error_normalizer.go#L125-L139) 16. **Tenderly `"not found"` becomes `ErrEndpointClientSideException`** (retryable), not `ErrEndpointMissingData` — the `"Unsupported"` block in the normalizer intentionally runs after `"Not found"` to avoid misclassifying Tenderly's bare `"not found"`. [`architecture/evm/error_normalizer.go:L394-477`](https://github.com/erpc/erpc/blob/main/architecture/evm/error_normalizer.go#L394-L477) 17. **dwellir `OwnsUpstream` only matches `dwellir://` prefix** (not `evm+dwellir://`) or `.dwellir.com` domain; the `evm+` shorthand still works because shorthand conversion runs before vendor lookup. [`thirdparty/dwellir.go:L173-180`](https://github.com/erpc/erpc/blob/main/thirdparty/dwellir.go#L173-L180) 18. **Live-probe vendors dial out during routing decisions**: envio/erpc/pimlico/routemesh/thirdweb perform a real `eth_chainId` HTTP call (10 s timeout) on the provider bootstrap path. Probe clients are cached forever in `sync.Map`s keyed by chain id or `"{url}-{chainId}"`. 19. **`PrepareUpstreamsForNetwork` succeeds as soon as one upstream registers** (`minReady=1`). Slow providers continue adding upstreams in the background without blocking the first request. If all provider tasks finish with zero upstreams, there is a 100 ms grace re-check before the retryable `ErrNetworkNotSupported` is returned; if tasks are still running, the caller receives `ErrNetworkInitializing`. [`upstream/registry.go:L188-293`](https://github.com/erpc/erpc/blob/main/upstream/registry.go#L188-L293) 20. **`LookupByUpstream` with an explicit but non-existent `vendorName` returns nil** — it does not fall through to `OwnsUpstream` matching. If `upstream.vendorName` is set to an unregistered name, vendor error-mapping is silently lost. Registration order of the 23 vendors determines which vendor wins when multiple `OwnsUpstream` checks return true for the same endpoint. [`thirdparty/vendors_registry.go:L54-71`](https://github.com/erpc/erpc/blob/main/thirdparty/vendors_registry.go#L54-L71) 21. **`llama` and `routemesh` `OwnsUpstream` do not check their own scheme prefixes**: `llama.OwnsUpstream` only matches `.llamarpc.com`; `routemesh.OwnsUpstream` only matches `vendorName=="routemesh"` or endpoint containing `routemes.sh`. Both shorthands (`llama://`, `routemesh://`) work only because shorthand conversion in `common/defaults.go` runs before vendor lookup. [`thirdparty/llama.go:L95-97`](https://github.com/erpc/erpc/blob/main/thirdparty/llama.go#L95-L97) [`thirdparty/routemesh.go:L135-140`](https://github.com/erpc/erpc/blob/main/thirdparty/routemesh.go#L135-L140) 22. **dwellir `SupportsNetwork` returns `(false, nil)` for an unparseable chain id** — unlike every other static-map vendor which propagates the parse error. [`thirdparty/dwellir.go:L109-113`](https://github.com/erpc/erpc/blob/main/thirdparty/dwellir.go#L109-L113) ### Observability No provider-specific Prometheus metrics exist. The `vendor` label on all upstream-level metrics is populated from the attached vendor's `Name()`, the config's `vendorName`, or `unknown-` for unrecognized endpoints. | metric | type | key labels | when it fires | |---|---|---|---| | `erpc_upstream_request_total` | counter | `project, vendor, network, upstream, category, attempt, composite, finality, user, agent_name` | Every upstream request attempt | | `erpc_upstream_request_duration_seconds` | histogram | `project, vendor, network, upstream, category, composite, finality, user` | Upstream round-trip latency | | `erpc_upstream_request_errors_total` | counter | `project, vendor, network, upstream, category, error, severity, composite, finality, user, agent_name` | Failed upstream requests | Bootstrap task progress is visible in initializer task names: `network//provider/` (scheduling) and `network//upstream/` (per-generated upstream). [`upstream/registry.go:L423-481`](https://github.com/erpc/erpc/blob/main/upstream/registry.go#L423-L481) Trace spans: `buildProviderBootstrapTask` emits a detail span per provider task; `buildUpstreamBootstrapTask` emits one for each registered upstream. [`upstream/registry.go:L423-485`](https://github.com/erpc/erpc/blob/main/upstream/registry.go#L423-L485) Notable log messages: - warn `no providers or upstreams found in project; will use default 'public' endpoints repository` - debug `attempting to create upstream(s) from provider` / debug `provider does not support network; skipping upstream creation` / info `registering N upstream(s) from provider` - error `failed to execute provider bootstrap tasks` / info `provider bootstrap tasks executed successfully` - warn `vendor remote-data refresh failed; keeping previous snapshot` with `vendor` + `cacheKey` fields - error `panic recovered during vendor remote-data async refresh` - warn `some quicknode chain ID fetches failed; continuing with available data` / warn `failed to fetch chain IDs for some QuickNode endpoints` - warn `some chainstack chain ID fetches failed; continuing with available data` / warn `failed to fetch chain IDs for some Chainstack nodes` - info `successfully fetched dRPC network names` (with count + url) - debug `generated upstream from alchemy provider` / `generated upstream from conduit provider` / `generated upstreams from repository provider` / `generated upstreams from superchain provider` ### Source code entry points - [`thirdparty/vendors_registry.go:L9-36`](https://github.com/erpc/erpc/blob/main/thirdparty/vendors_registry.go#L9-L36) — global 23-vendor registry; `LookupByName` / `LookupByUpstream` / `SupportedVendors` - [`thirdparty/providers_registry.go:L15-34`](https://github.com/erpc/erpc/blob/main/thirdparty/providers_registry.go#L15-L34) — per-project ProviderConfig → Provider binding; unknown-vendor startup error - [`thirdparty/provider.go:L13-162`](https://github.com/erpc/erpc/blob/main/thirdparty/provider.go#L13-L162) — `SupportsNetwork` gating, `GenerateUpstreamConfigs`, id templating, env expansion - [`thirdparty/remote_cache.go:L13-78`](https://github.com/erpc/erpc/blob/main/thirdparty/remote_cache.go#L13-L78) — `RemoteDataCache[T]` algorithm; request-path safety rationale; `ErrRemoteCacheCold` - [`common/defaults.go:L1113-1241`](https://github.com/erpc/erpc/blob/main/common/defaults.go#L1113-L1241) — default-provider injection, shorthand upstream→provider conversion - [`common/defaults.go:L1243-1425`](https://github.com/erpc/erpc/blob/main/common/defaults.go#L1243-L1425) — `buildProviderSettings` per-vendor settings derivation - [`upstream/registry.go:L155-293`](https://github.com/erpc/erpc/blob/main/upstream/registry.go#L155-L293) — lazy `PrepareUpstreamsForNetwork`, provider task scheduling, readiness wait loop - [`common/validation.go:L780-815`](https://github.com/erpc/erpc/blob/main/common/validation.go#L780-L815) — ProviderConfig validation rules - [`architecture/evm/error_normalizer.go:L878-900`](https://github.com/erpc/erpc/blob/main/architecture/evm/error_normalizer.go#L878-L900) — vendor-specific error hook invocation ### Related pages - [Upstreams](/config/projects/upstreams.llms.txt) — static upstream entries; providers generate these dynamically. - [Selection policies](/config/projects/selection-policies.llms.txt) — controls which generated upstream handles each request. - [Failsafe](/config/failsafe/retry.llms.txt) — configure retry/hedge/timeout inside `overrides` per network. - [Rate limiters](/config/rate-limiters.llms.txt) — cap per-vendor request rates to stay within API budgets. - [Auth](/config/auth.llms.txt) — protect your eRPC instance when proxying to paid vendor endpoints. --- ## Navigation (machine-readable surface) - Up: [Projects](https://docs.erpc.cloud/config/projects.llms.txt) - Root index of every page: [llms.txt](https://docs.erpc.cloud/llms.txt) · everything in one file: [llms-full.txt](https://docs.erpc.cloud/llms-full.txt) ### Sibling pages - [CORS](https://docs.erpc.cloud/config/projects/cors.llms.txt) — Let your frontend talk to eRPC safely — configure which browser origins are allowed, in seconds, without blocking a single server-to-server call. - [Networks](https://docs.erpc.cloud/config/projects/networks.llms.txt) — One entry per chain — eRPC routes every request to the right upstreams, caches results, and retries failures, all without touching your code. - [Selection & scoring](https://docs.erpc.cloud/config/projects/selection-policies.llms.txt) — eRPC ranks your upstreams every 15 seconds using live health data — bad actors drop out automatically, the fastest healthy provider goes first, and re-admission is metric-driven, not timer-driven. - [Shadow upstreams](https://docs.erpc.cloud/config/projects/shadow-upstreams.llms.txt) — Dark-launch a new RPC provider by mirroring live traffic to it in the background — zero latency impact, automatic response comparison, and Prometheus counters to prove it's ready. - [Static responses](https://docs.erpc.cloud/config/projects/static-responses.llms.txt) — Return hardcoded JSON-RPC replies instantly for specific method+params pairs — no upstream contact, zero quota consumed, microsecond latency. - [Upstreams](https://docs.erpc.cloud/config/projects/upstreams.llms.txt) — Add any RPC endpoint — Alchemy, a self-hosted node, a gRPC feed — and eRPC figures out what it can serve, heals it when it breaks, and routes around it when it can't.