# https://docs.erpc.cloud llms.txt ## eRPC Overview [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Quick start # Introducing eRPC eRPC is a fault-tolerant EVM RPC proxy and permanent caching solution. It is built with read-heavy use-cases in mind such as data indexing and high-load frontend usage. End User If data is already cached 5 RPS 500 RPS 0 RPS eRPC RPC Request Indexers Services k8s, docker, etc **Storage** Weak Upstream A Strong Upstream B Offline Upstream C Monthly, daily rate-limits Archive/Full node auto-routing Auto-split+batch for getLogs limits # Quick start ### Create configuration [Permalink for this section](https://docs.erpc.cloud/\#create-configuration) Create your [`erpc.yaml`](https://docs.erpc.cloud/config/example) configuration file based on the `erpc.dist.yaml` file: ``` cp erpc.dist.yaml erpc.yaml code erpc.yaml ``` See [a complete config example](https://docs.erpc.cloud/config/example) for inspiration. ### Run with Docker [Permalink for this section](https://docs.erpc.cloud/\#run-with-docker) Use the Docker image: ``` docker run -v $(pwd)/erpc.yaml:/erpc.yaml -p 4000:4000 -p 4001:4001 ghcr.io/erpc/erpc:latest ``` ### Test the setup [Permalink for this section](https://docs.erpc.cloud/\#test-the-setup) Send your first request: ``` curl --location 'http://localhost:4000/main/evm/42161' \ --header 'Content-Type: application/json' \ --data '{ "method": "eth_getBlockByNumber", "params": [\ "0x1203319",\ false\ ], "id": 9199, "jsonrpc": "2.0" }' ``` ### Setup monitoring (optional) [Permalink for this section](https://docs.erpc.cloud/\#setup-monitoring-optional) Bring up monitoring stack (Prometheus, Grafana) using docker-compose: ``` # clone the repo if you haven't git clone https://github.com/erpc/erpc.git cd erpc # bring up the monitoring stack docker-compose up -d ``` ### Access Grafana [Permalink for this section](https://docs.erpc.cloud/\#access-grafana) Open Grafana at [http://localhost:3000 (opens in a new tab)](http://localhost:3000/) and login with the following credentials: - username: `admin` - password: `admin` ### Monitor metrics [Permalink for this section](https://docs.erpc.cloud/\#monitor-metrics) Send more requests and watch the metrics being collected and visualized in Grafana. ![eRPC Grafana Dashboard](https://docs.erpc.cloud/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmonitoring-example-erpc.2cb040a1.png&w=3840&q=75) [Why eRPC?](https://docs.erpc.cloud/why "Why eRPC?") ## Benefits of eRPC [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Why eRPC? # Why eRPC? These are the main reasons eRPC was built: - To **reduce overall costs** of RPC usage and egress traffic, by local caching. - To provide a **fault-tolerant** and **reliable source** for RPC consumers in case of one or more provider outages. - To give a global **observability** about your RPC usage both within internal teams and projects, and towards the upstream RPC 3rd party companies. # Features - [Score](https://docs.erpc.cloud/config/projects/upstreams#priority--selection-mechanism) multiple upstreams by tracking their response time, error rate, blockchain sync status, etc. - [Failsafe](https://docs.erpc.cloud/config/failsafe) policies to help with intermittent issues and increase general resiliency (retries, hedging, circuit breakers). - Offer self-imposed [rate limitting](https://docs.erpc.cloud/config/rate-limiters) per project, or network, or upstream to avoid abuse, and unintentional DDoS. - Prometheus metrics collection and Grafana dashboards to [monitor](https://docs.erpc.cloud/operation/monitoring) costs, usage and health of your RPC endpoints. - [Caching](https://docs.erpc.cloud/config/database/evm-json-rpc-cache) and multiplexing (auto-merging identical requests) to reduce redundant RPC calls and costs. - [Integrity](https://docs.erpc.cloud/config/failsafe/integrity) module helps increase data quality on certain methods such as eth\_getLogs or eth\_getBlockByNumber. # Use-cases For the start the main focus of eRPC will be on read-heavy use-cases such as: ### Frontend of dApps [Permalink for this section](https://docs.erpc.cloud/why\#frontend-of-dapps) Often many requests made from dApps can be multiplexed into a single RPC call, or use cache to reduce costs. Here are real production case studies where eRPC serves 8k RPS and 1B requests / month: - 🚀 [Moonwell: How eRPC slashed RPC calls by 67% (opens in a new tab)](https://erpc.cloud/case-studies/moonwell) - 🚀 [Chronicle: How eRPC reduced RPC cost by 45% (opens in a new tab)](https://erpc.cloud/case-studies/chronicle) ### Data indexing using Ponder, Graph, Envio, Flair, etc. [Permalink for this section](https://docs.erpc.cloud/why\#data-indexing-using-ponder-graph-envio-flair-etc) When using any of the web3 indexing tools you will be making lots of requests towards your RPC provider, especially during re-backfills for historical data. eRPC can help in two main areas: - Cache already made RPC calls (eth\_getLogs, eth\_call, eth\_getBlockByNumber, etc) - Rate-limit upstream pressure towards RPC nodes to avoid fatal error ### Resilient load balancer for self-hosted RPC nodes [Permalink for this section](https://docs.erpc.cloud/why\#resilient-load-balancer-for-self-hosted-rpc-nodes) For app-specific chains and rollups, or projects who prefer to self-host RPC nodes for performance reasons, it makes sense to use eRPC as the entry-point load-balancer. Compared to more traditional LB solutions (ALB, K8S Services, etc) eRPC will provide EVM-focused features like: - EVM-aware healthchecks (e.g. how many blocks behind) - EVM-aware fallbacks (e.g. if a 4xx is because of missing block, try another upstream) - EVM-aware method filters (e.g. certain methods to go to node A and other methods to go to node B) [Quick start](https://docs.erpc.cloud/ "Quick start") [Free & Public RPCs](https://docs.erpc.cloud/free "Free & Public RPCs") ## eRPC FAQ Guide [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) FAQ ## Frequently Asked Questions [Permalink for this section](https://docs.erpc.cloud/faq\#frequently-asked-questions) ### How to set env variables? [Permalink for this section](https://docs.erpc.cloud/faq\#how-to-set-env-variables) To use env variables in [erpc.yaml](https://docs.erpc.cloud/config/example), follow these steps: 1. **Set env variables**: Define the env variables in your system or shell before running your application: ``` export ETHEREUM_RPC_URL=https://mainnet.infura.io/v3/YOUR_INFURA_KEY ``` 2. **Use placeholders in config**: Add placeholders in your config file where you want the env variables to be used: yamltypescript erpc.yaml ``` upstreams: - endpoint: ${ETHEREUM_RPC_URL} ``` ### How to disable caching? [Permalink for this section](https://docs.erpc.cloud/faq\#how-to-disable-caching) To disable caching, set [`evmJsonRpcCache`](https://docs.erpc.cloud/config/database/evm-json-rpc-cache) to `null` in your configuration: yamltypescript erpc.yaml ``` database: evmJsonRpcCache: ~ ``` ### How do I set up CORS for frontend usage? [Permalink for this section](https://docs.erpc.cloud/faq\#how-do-i-set-up-cors-for-frontend-usage) If you’re deploying eRPC on Railway (or elsewhere) and plan to call it directly from a web application in the browser, you must enable CORS in your [erpc.yaml](https://docs.erpc.cloud/config/projects/cors#config). Below is a minimal example that allows requests from a specific origin or any origin: erpc.yaml ``` projects: - id: main cors: allowedOrigins: # If you want to allow all origins, use "*" which means any frontend can make calls to your erpc instance - "https://myapp.com" allowedMethods: - "GET" - "POST" - "OPTIONS" allowedHeaders: - "Content-Type" allowCredentials: false maxAge: 300 ``` [Free & Public RPCs](https://docs.erpc.cloud/free "Free & Public RPCs") [erpc.yaml/ts](https://docs.erpc.cloud/config/example "erpc.yaml/ts") ## Free RPC Endpoints [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Free & Public RPCs ## Free & Public RPC Endpoints [Permalink for this section](https://docs.erpc.cloud/free\#free--public-rpc-endpoints) Get immediate access to 2,000+ chains and 4,000+ public free EVM RPC endpoints: 1. Run an eRPC instance: NPMDocker ``` npx start-erpc ``` You can also deploy it to `Railway`: [![Deploy on Railway](https://railway.app/button.svg) (opens in a new tab)](https://railway.com/template/10iW1q?referralCode=PpPFJd) 2. Send requests to the eRPC instance based on chainId: ``` curl 'http://localhost:4000/evm/42161' \ --header 'Content-Type: application/json' \ --data '{ "method": "eth_getBlockByNumber", "params": [\ "latest",\ false\ ], "id": 9199, "jsonrpc": "2.0" }' ``` 3. 🚀 Profit! ![../public/assets/romulus.gif](https://docs.erpc.cloud/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fromulus.7f2e7f9c.gif&w=1080&q=75) ### How it works? [Permalink for this section](https://docs.erpc.cloud/free\#how-it-works) When running eRPC without a configuration file, it will use a basic configuration using the special [repository](https://docs.erpc.cloud/config/projects/providers#repository) provider. This provider automatically fetches (every 1 hour) RPC public endpoints from [https://evm-public-endpoints.erpc.cloud (opens in a new tab)](https://evm-public-endpoints.erpc.cloud/) which is a combination of [Chainlist (opens in a new tab)](https://chainlist.org/), [ChainID.Network (opens in a new tab)](https://chainid.network/), and [Viem (opens in a new tab)](https://viem.sh/) public RPC endpoints. ### Next steps [Permalink for this section](https://docs.erpc.cloud/free\#next-steps) This setup is recommended for development and testing purposes. For production environments, we recommend [extending eRPC config (opens in a new tab)](https://docs.erpc.cloud/config/example) with dedicated premium providers and advanced failover configs. [Why eRPC?](https://docs.erpc.cloud/why "Why eRPC?") [FAQ](https://docs.erpc.cloud/faq "FAQ") ## eRPC URL Operations [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Operation URL # URL eRPC supports several URL patterns for making requests, with options for both single-chain and multi-chain endpoints. ## Single-chain requests [Permalink for this section](https://docs.erpc.cloud/operation/url\#single-chain-requests) ### Standard URL pattern [Permalink for this section](https://docs.erpc.cloud/operation/url\#standard-url-pattern) When making requests only for a single chain, you can use this URL structure: https:///// ##### `` [Permalink for this section](https://docs.erpc.cloud/operation/url\#your-erpc-hostname) Depends on your deployment setup, for example in local development (using `make run`) it will be `localhost:4000`. ##### `` [Permalink for this section](https://docs.erpc.cloud/operation/url\#project-id) Target project ID you configured in [erpc.yaml](https://docs.erpc.cloud/config/example), for example "main" or "frontend", "backend", etc. ##### `` [Permalink for this section](https://docs.erpc.cloud/operation/url\#network-architecture) Target network architecture you configured in [erpc.yaml](https://docs.erpc.cloud/config/example), for example `evm`. ##### `` [Permalink for this section](https://docs.erpc.cloud/operation/url\#chain-id) Target chain ID that one or more upstreams support, for example "1" or `42161`. ``` # A cURL example of sending a request to a project named "main" and Ethereum mainnet chain: curl --location 'http://localhost:4000/main/evm/1' \ --header 'Content-Type: application/json' \ --data '{ "method": "eth_getBlockByNumber", "params": [\ "0x1203319",\ false\ ], "id": 9199, "jsonrpc": "2.0" }' ``` ### Domain aliasing [Permalink for this section](https://docs.erpc.cloud/operation/url\#domain-aliasing) If configured with domain aliasing, you can have predefined project and network values: erpc.yaml ``` server: # ... aliasing: rules: - matchDomain: "*" # (OPTIONAL) Pattern to match Host header, defaults to `*` (all domains) serveProject: "main" # (OPTIONAL) Project ID to serve for matched domains serveArchitecture: "evm" # (OPTIONAL) Network architecture (e.g., "evm") serveChain: "1" # (OPTIONAL) Chain ID (e.g., "1" for Ethereum mainnet) ``` #### Configuration examples [Permalink for this section](https://docs.erpc.cloud/operation/url\#configuration-examples) - **No aliasing** \- full URL is required ``` aliasing: ~ ``` ``` https://api.myservice.com/main/evm/1 ``` - **Project only** ``` server: aliasing: rules: - matchDomain: "api.myservice.com" serveProject: "main" ``` ``` https://api.myservice.com/evm/1 ``` - **Project and architecture** ``` server: aliasing: rules: - matchDomain: "evm.myservice.com" serveProject: "main" serveArchitecture: "evm" ``` ``` https://evm.myservice.com/1 ``` - **Full aliasing** ``` server: aliasing: rules: - matchDomain: "eth.myservice.com" serveProject: "main" serveArchitecture: "evm" serveChain: "1" ``` ``` https://eth.myservice.com ``` #### Multiple rules example [Permalink for this section](https://docs.erpc.cloud/operation/url\#multiple-rules-example) You can define multiple rules to handle different domains: ``` server: aliasing: rules: # Ethereum Mainnet specific endpoint - matchDomain: "eth.myservice.com" serveProject: "main" serveArchitecture: "evm" serveChain: "1" # Arbitrum specific endpoint - matchDomain: "arbitrum.myservice.com" serveProject: "main" serveArchitecture: "evm" serveChain: "42161" # Generic EVM endpoint (requires chain ID in URL) - matchDomain: "evm.myservice.com" serveProject: "main" serveArchitecture: "evm" # Project-specific endpoint (requires architecture and chain in URL) - matchDomain: "api.myservice.com" serveProject: "main" ``` Alias domains are matched with `Host` header using [matcher syntax](https://docs.erpc.cloud/config/matcher) ## Multi-chain requests [Permalink for this section](https://docs.erpc.cloud/operation/url\#multi-chain-requests) When making requests for multiple chains, you can use the project endpoint only and must include "networkId" within the request body: https:/// ``` # A cURL example of sending a request to a project named "main" and Ethereum mainnet chain: curl --location 'http://localhost:4000/main' \ --header 'Content-Type: application/json' \ --data '{ "networkId": "evm:1", "method": "eth_getBlockByNumber", "params": [\ "0x1203319",\ false\ ], "id": 9199, "jsonrpc": "2.0" }' ``` ## Batch requests [Permalink for this section](https://docs.erpc.cloud/operation/url\#batch-requests) You can batch multiple calls across any number of networks, in a single request. Read more about it in [Batch requests](https://docs.erpc.cloud/operation/batch) page. [Cloud](https://docs.erpc.cloud/deployment/cloud "Cloud") [Healthcheck](https://docs.erpc.cloud/operation/healthcheck "Healthcheck") ## eRPC Production Guidelines [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Operation Production # Production guidelines Here are some recommendations for running eRPC in production. ## Memory usage [Permalink for this section](https://docs.erpc.cloud/operation/production\#memory-usage) Biggest memory usage contributor in eRPC is size of responses of your requests. For example, for common requests such as `eth_getBlockByNumber` or `eth_getTransactionReceipt` the size (<1MB) will be relatively smaller than `debug_traceTransaction` (which could potentially be up to 50MB). When using eRPC in Kubernetes for example your might see occesional `OOMKilled` errors which is most often because of high RPS of large request/responses. In majority of use-cases eRPC uses around 256MB of memory (and 1vCPU). To find the ideal memory limit based on your use-case start with a high limit first (e.g. 16GB) and route your production traffic (either shadow or real) to see what is the usage based on your request patterns. For more control you can configure Go's garbage collection with the following env variables (e.g. when facing OOM Killed errors on Kubernetes): ``` # This flag controls when GC kicks in, for example when memory is increased by 30% try to run GC: export GOGC=30 # This flag instructs Go to do a GC when memory goes over the 2GiB limit. # IMPORTANT: if this value is too low, it might cause high GC frequency, # which in turn might impact the performance without giving much memory benefits. export GOMEMLIMIT=2GiB ``` ## Failsafe policies [Permalink for this section](https://docs.erpc.cloud/operation/production\#failsafe-policies) Make sure to configure [retry policy](https://docs.erpc.cloud/config/failsafe#retry-policy) on both network-level and upstream-level. - Network-level retry configuration is useful to try other upstreams if one has an issue. Even when you only have 1 upstream, network-level retry is still useful. Recommendation is to configure `maxCount` to be equal to the number of upstreams. - Upstream-level retry configuration covers intermittent issues with a specific upstream. It is recommended to set at least 2 and at most 5 as `maxCount`. [Timeout policy](https://docs.erpc.cloud/config/failsafe#timeout-policy) depends on the expected response time for your use-case, for example when using "trace" methods on EVM chains, providers might take up to 10 seconds to respond. Therefore a low timeout might ultimately always fail. If you are not using heavy methods such as trace or large getLogs, you can use `3s` as a default timeout. [Hedge policy](https://docs.erpc.cloud/config/failsafe#hedge-policy) is **highly-recommended** if you prefer "fast response as soon as possible". For example setting `500ms` as "delay" will make sure if upstream A did not respond under 500 milliseconds, simultaneously another request to upstream B will be fired, and eRPC will respond back as soon as any of them comes back with result faster. Note: since more requests are sent, it might incur higher costs to achieve the "fast response" goal. ## Caching database [Permalink for this section](https://docs.erpc.cloud/operation/production\#caching-database) Storing cached RPC responses requires high storage for read-heavy use-cases such as indexing 100m blocks on Arbitrum. eRPC is designed to be robust towards cache database issues, so even if database is completely down it will not impact the RPC availability. As described in [Database](https://docs.erpc.cloud/config/database) section depending on your requirements choose the right type. You can start with Redis which is easiest to setup, and if amount of cached data is larger than available memory you can switch to PostgreSQL. Using [eRPC cloud](https://docs.erpc.cloud/deployment/cloud) solution will be most cost-efficient in terms of caching storage costs, as we'll be able to break the costs over many projects. ## Horizontal scaling [Permalink for this section](https://docs.erpc.cloud/operation/production\#horizontal-scaling) When running multiple eRPC instances (e.g., in a Kubernetes deployment with multiple replicas), it's recommended to enable shared state with Redis to ensure proper synchronization between instances. The [shared state feature](https://docs.erpc.cloud/config/database/shared-state) allows your eRPC instances to share critical blockchain information such as latest and finalized block numbers, which reduces redundant upstream requests and improves integrity checks. Even if Redis becomes temporarily unavailable, eRPC will continue serving requests by falling back to local state tracking. This might cause a slight increase in upstream requests as each instance will need to poll for latest/finalized blocks independently, but the impact is minimal and service availability is maintained. The shared state feature requires minimal storage (less than 1MB per upstream) while significantly improving coordination between instances. For high-traffic deployments with multiple replicas, this pattern is strongly recommended. ## Explicitly configure Chain ID [Permalink for this section](https://docs.erpc.cloud/operation/production\#explicitly-configure-chain-id) Even though eRPC can automatically detect the chain ID, it's recommended to explicitly configure the chain ID in the project configuration. This ensures faster startup time and more resilient rollouts. There are mainly 2 places to configure the chain ID: - `networks.*.evm.chainId` under [Networks](https://docs.erpc.cloud/config/projects/networks) section - `upstreams.*.evm.chainId` under [Upstreams](https://docs.erpc.cloud/config/projects/upstreams) section ## Healthcheck [Permalink for this section](https://docs.erpc.cloud/operation/production\#healthcheck) For a zero-downtime smooth rollout, configure [Healthcheck](https://docs.erpc.cloud/operation/healthcheck) in your orchestration platform (e.g. kubernetes). #### Example: Cilium/Envoy and zero-downtime deployments [Permalink for this section](https://docs.erpc.cloud/operation/production\#example-ciliumenvoy-and-zero-downtime-deployments) When using Cilium with Envoy (either Ingress or Gateway-API) we observed that keeping `waitBeforeShutdown` and `waitAfterShutdown` **both** at 30s (together with a readiness probe that fails in ≤ 10 s) eliminates `connection reset / refused` errors during rolling updates: ``` server: waitBeforeShutdown: 30s # pod is in draining mode waitAfterShutdown: 30s # process stays alive until Envoy finishes ``` Shorter values let Envoy reuse a connection after the listener is gone or try to reach a pod that has already exited. Use these numbers as a safe starting point and adjust to match your own probe intervals. ## Custom response headers [Permalink for this section](https://docs.erpc.cloud/operation/production\#custom-response-headers) You can add custom headers to all HTTP responses using `server.responseHeaders`. This is useful for exposing instance metadata (region, machine ID, pod name) directly in responses for debugging. Values support environment variable expansion using `${VAR}` syntax. Headers with empty values (after expansion) are automatically omitted. ``` server: responseHeaders: X-ERPC-Region: ${FLY_REGION} # Fly.io region X-ERPC-Machine: ${FLY_MACHINE_ID} # Fly.io machine ID # Or for Kubernetes: # X-ERPC-Pod: ${HOSTNAME} ``` This allows quick identification of which instance handled a request without checking traces: ``` HTTP/1.1 200 OK X-ERPC-Version: main X-ERPC-Region: sin X-ERPC-Machine: 4d891234ab ``` For deeper debugging, combine this with [custom trace attributes](https://docs.erpc.cloud/operation/tracing#custom-resource-attributes) to get full observability in your tracing backend. [Directives](https://docs.erpc.cloud/operation/directives "Directives") [Monitoring](https://docs.erpc.cloud/operation/monitoring "Monitoring") ## eRPC Failsafe Policies [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Config Failsafe # Failsafe Failsafe policies help with intermittent issues and increase resiliency. They can be configured at both [Network](https://docs.erpc.cloud/config/projects/networks) and [Upstream](https://docs.erpc.cloud/config/projects/upstreams) levels, with support for **per-method** configuration. ## Available policies [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#available-policies) - [`timeout:`](https://docs.erpc.cloud/config/failsafe#timeout-policy) prevents requests from hanging indefinitely - [`retry:`](https://docs.erpc.cloud/config/failsafe#retry-policy) recovers from transient failures - [`hedge:`](https://docs.erpc.cloud/config/failsafe#hedge-policy) runs parallel requests when upstreams are slow - [`circuitBreaker:`](https://docs.erpc.cloud/config/failsafe#circuitbreaker-policy) temporarily removes failing upstreams - [`consensus:`](https://docs.erpc.cloud/config/failsafe/consensus) verifies multiple upstreams agree on results - [Integrity](https://docs.erpc.cloud/config/failsafe/integrity) increases data quality for specific methods ## Per-method configuration [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#per-method-configuration) Failsafe policies can optionally be configured per-method using `matchMethod` and `matchFinality` fields. This allows fine-tuned behavior for different RPC methods and different block finality states. - `matchMethod`: Pattern to match RPC methods (a [matcher](https://docs.erpc.cloud/config/matchers) supports wildcards `*` and OR operator `|`) - `matchFinality`: Array of finality states to match When multiple failsafe configs are defined, they are evaluated in order and the first matching config is used. ### Finality States [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#finality-states) The `matchFinality` field can match against these data finality states: - **`finalized`**: Data from blocks that are confirmed as finalized and safe from reorgs. This is determined by comparing the block number with the upstream's finalized block. - Example methods: `eth_getBlockByNumber` (for old blocks), `eth_getLogs` (for finalized ranges) - Use case: Can have relaxed failsafe policies since data won't change - **`unfinalized`**: Data from recent blocks that could still be reorganized. Also includes any data from pending blocks. - Example methods: `eth_getBlockByNumber("latest")`, `eth_call` with recent blocks - Use case: May need more aggressive retries and shorter timeouts - **`realtime`**: Data that changes frequently, typically with every new block. - Example methods: `eth_blockNumber`, `eth_gasPrice`, `eth_maxPriorityFeePerGas`, `net_peerCount` - Use case: Often needs fast timeouts and may benefit from hedging - **`unknown`**: When the block number cannot be determined from the request/response. - Example methods: `eth_getTransactionByHash`, `trace_transaction`, `debug_traceTransaction` - Use case: Data is typically immutable once included, but block context is unknown yamltypescript erpc.yaml ``` projects: - id: main upstreams: - id: my-upstream failsafe: # Default policy for all methods - matchMethod: "*" # matches any method (default if omitted) timeout: duration: 30s retry: maxAttempts: 3 # Fast timeout for simple queries - matchMethod: "eth_getBlock*|eth_getTransaction*" timeout: duration: 5s retry: maxAttempts: 2 delay: 100ms # Longer timeout for heavy trace methods - matchMethod: "trace_*|debug_*" timeout: duration: 60s retry: maxAttempts: 1 # expensive operations, minimize retries # Different policy for finalized vs unfinalized data - matchMethod: "eth_call|eth_estimateGas" matchFinality: ["unfinalized", "realtime"] timeout: duration: 10s retry: maxAttempts: 5 # unfinalized data changes frequently, retry more ``` ## `timeout` policy [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#timeout-policy) Sets a timeout for requests. Network-level timeout applies to the entire request lifecycle (including retries), while upstream-level timeout applies to each individual attempt. yamltypescript erpc.yaml ``` projects: - id: main networks: - architecture: evm evm: chainId: 42161 failsafe: - matchMethod: "*" timeout: duration: 30s # Total time including all retries upstreams: - id: blastapi-chain-42161 failsafe: - matchMethod: "*" timeout: duration: 15s # Per-attempt timeout ``` ## `retry` policy [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#retry-policy) Automatically retries failed requests with configurable backoff strategies. #### Retryable Errors [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#retryable-errors) - `5xx` server errors (intermittent issues) - `408` request timeout - `429` rate limit exceeded - Empty responses for certain methods (e.g., `eth_getLogs` when node is lagging) #### Non-Retryable Errors [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#non-retryable-errors) - `4xx` client errors (invalid requests) - Unsupported method errors yamltypescript erpc.yaml ``` projects: - id: main upstreams: - id: my-upstream failsafe: - matchMethod: "*" retry: maxAttempts: 3 # Total attempts (initial + 2 retries) delay: 1000ms # Initial delay between retries backoffMaxDelay: 10s # Maximum delay after backoff backoffFactor: 0.3 # Exponential backoff multiplier jitter: 500ms # Random jitter (0-500ms) to prevent thundering herd ``` ### Empty responses [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#empty-responses) Retry feature is useful to handle empty responses when a node is lagging behind or for some other reason returns an unexpected empty response. - **What counts as empty**: Results like `null`, `[]`, `""`, `{}`, `0x`, `"0x"`, hex strings that are all zeros (e.g., `0x000...0`), and method-specific empties (e.g., empty logs). Internally we detect these directly from response bytes. - **Where retries apply**: Only at the **network level** when the request has `retryEmpty` enabled (via directive defaults or request headers/params). Upstream-level retry does not retry on empties. - **Default ignore list (`retry.emptyResultIgnore`)**: Methods to NEVER retry when the response is empty (e.g., `eth_getLogs`, `eth_call`). Configure to override defaults. - **Block availability check**: For EVM, when empty and the upstream is not syncing, we try to extract the block number and check upstream availability. If the upstream can serve that block but still returned empty, we do not retry. - **Availability confidence (`retry.emptyResultConfidence`)**: - `finalizedBlock`: If the target block is finalized (at or below finalized), empty responses are treated as valid (no retry). If the target block is after finalized, we will retry. - `blockHead`: If the target block is at or below the node's latest head, empty responses are treated as valid (no retry). If the target block is ahead of the head, we will retry. - **Syncing nodes**: If an upstream is syncing and returns empty, it is treated unfavorably and skipped for the remainder of the request. - **Per-request de-dup**: Upstreams that returned empty for a request are skipped on subsequent rotations for that same request. - **Cap empty retries**: `retry.emptyResultMaxAttempts` caps total attempts specifically for empty-result retries (default equals `retry.maxAttempts`). - **Non-empty wins**: If any non-empty response was seen, it is preserved and can be returned even if later attempts fail. In consensus, when configured, non-empty results are preferred. - **Writes are never retried**: Write methods (e.g., `eth_send*`) are not retried. yamltypescript erpc.yaml ``` projects: - id: main networks: - architecture: evm evm: chainId: 1 directiveDefaults: retryEmpty: true # enable empty-result retries at network level failsafe: - matchMethod: "*" retry: maxAttempts: 4 # total attempts (initial + retries) emptyResultIgnore: ["eth_getLogs", "eth_call"] # Never retry these methods when result is empty emptyResultConfidence: finalizedBlock # treat finalized empties as valid emptyResultMaxAttempts: 2 # cap attempts for empty-result retries only ``` ## `hedge` policy [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#hedge-policy) Starts parallel requests when an upstream is slow to respond. Highly recommended at network level for optimal performance. **Quantile-based hedging** (recommended) uses response time statistics to determine optimal hedge timing, while **fixed-delay hedging** uses a static delay. yamltypescript erpc.yaml ``` projects: - id: main networks: - architecture: evm evm: chainId: 1 failsafe: - matchMethod: "*" hedge: # Quantile-based (recommended): hedge after p99 response time quantile: 0.99 minDelay: 100ms # Minimum wait before hedging maxDelay: 2s # Maximum wait before hedging maxCount: 1 # Max parallel hedged requests # Alternative: Fixed-delay hedging # delay: 500ms # maxCount: 1 ``` Monitor effectiveness via Prometheus metrics: - `erpc_network_hedged_request_total` \- total hedged requests - `erpc_network_hedge_discards_total` \- wasted hedges (original responded first) ## `circuitBreaker` policy [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#circuitbreaker-policy) Temporarily removes consistently failing upstreams to allow recovery time. Circuit breaker states: - **Closed**: Normal operation, upstream is healthy - **Open**: Upstream is failing, temporarily removed from rotation - **Half-open**: Testing if upstream has recovered with limited traffic yamltypescript erpc.yaml ``` projects: - id: main upstreams: - id: my-upstream failsafe: - matchMethod: "*" circuitBreaker: # Open circuit when 80% (160/200) of recent requests fail failureThresholdCount: 160 failureThresholdCapacity: 200 halfOpenAfter: 60s # Try recovery after 1 minute # Close circuit when 80% (8/10) succeed in half-open state successThresholdCount: 8 successThresholdCapacity: 10 ``` ## Real-World Examples [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#real-world-examples) ### High-Performance DeFi Configuration [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#high-performance-defi-configuration) yamltypescript erpc.yaml ``` projects: - id: defi-prod networks: - architecture: evm evm: chainId: 1 failsafe: # Aggressive hedging for all methods - matchMethod: "*" hedge: quantile: 0.9 # p90 latency minDelay: 50ms maxCount: 2 # Up to 2 parallel hedges timeout: duration: 10s upstreams: - id: primary-node failsafe: # Price feeds need fast response - matchMethod: "eth_call" matchFinality: ["latest"] timeout: duration: 1s retry: maxAttempts: 1 # No time for retries # Block data can be slower but must succeed - matchMethod: "eth_getBlock*" timeout: duration: 5s retry: maxAttempts: 5 delay: 100ms ``` ### Finality-Based Configuration [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#finality-based-configuration) yamltypescript erpc.yaml ``` failsafe: # Finalized data: relaxed policies - matchMethod: "*" matchFinality: ["finalized"] timeout: duration: 30s retry: maxAttempts: 5 backoffFactor: 2 # Unfinalized data: aggressive timeouts - matchMethod: "*" matchFinality: ["unfinalized"] timeout: duration: 5s retry: maxAttempts: 2 delay: 100ms # Realtime data: fast with hedging - matchMethod: "*" matchFinality: ["realtime"] timeout: duration: 2s hedge: delay: 500ms maxCount: 1 # Unknown finality: moderate settings - matchMethod: "*" matchFinality: ["unknown"] timeout: duration: 15s retry: maxAttempts: 3 ``` ### Indexer Configuration [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#indexer-configuration) yamltypescript erpc.yaml ``` projects: - id: indexer upstreams: - id: archive-node failsafe: # Bulk log queries need long timeouts - matchMethod: "eth_getLogs" timeout: duration: 120s retry: maxAttempts: 3 backoffFactor: 2 # Trace methods are expensive but critical - matchMethod: "trace_*|arbtrace_*" timeout: duration: 180s retry: maxAttempts: 2 circuitBreaker: failureThresholdCount: 10 # More tolerant for slow methods failureThresholdCapacity: 20 halfOpenAfter: 5m ``` ## Disabling Policies [Permalink for this section](https://docs.erpc.cloud/config/failsafe\#disabling-policies) To disable any policy, set it to `null` or `~` (YAML): yamltypescript erpc.yaml ``` failsafe: - matchMethod: "*" hedge: ~ # Disable hedging circuitBreaker: ~ # Disable circuit breaker ``` [CORS](https://docs.erpc.cloud/config/projects/cors "CORS")Circuit breaker ## eRPC Admin Operations [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Operation Admin ## Admin endpoint [Permalink for this section](https://docs.erpc.cloud/operation/admin\#admin-endpoint) Administrative operations are available through: ``` https:///admin ``` Admin endpoints require authentication configured under root `admin` section: erpc.yaml ``` admin: auth: strategies: - type: secret secret: value: server: # ... projects: # ... ``` ### Available admin methods [Permalink for this section](https://docs.erpc.cloud/operation/admin\#available-admin-methods) #### erpc\_taxonomy [Permalink for this section](https://docs.erpc.cloud/operation/admin\#erpc_taxonomy) Returns a taxonomy of projects, networks, and upstreams configured in the system. **Example request:** ``` curl --location 'http://localhost:4000/admin?secret=' \ # OR as a header: # --header 'X-ERPC-Secret-Token: ' \ --header 'Content-Type: application/json' \ --data '{ "method": "erpc_taxonomy", "id": 1, "jsonrpc": "2.0" }' ``` **Example Response:** ``` { "jsonrpc": "2.0", "result": { "projects": [\ {\ "id": "frontend",\ "networks": [\ {\ "id": "evm:1",\ "upstreams": [\ {\ "id": "blastapi-test"\ },\ {\ "id": "my-alchemy"\ }\ ]\ }\ ]\ }\ ] } } ``` #### erpc\_project [Permalink for this section](https://docs.erpc.cloud/operation/admin\#erpc_project) Returns detailed configuration and upstream scoring/health information for a specific project. **Example request:** ``` curl --location 'http://localhost:4000/admin?secret=' \ # OR as a header: # --header 'X-ERPC-Secret-Token: ' \ --header 'Content-Type: application/json' \ --data '{ "method": "erpc_project", "params": ["main"], "id": 1, "jsonrpc": "2.0" }' ``` **Example response:** ``` { "jsonrpc": "2.0", "id": 1, "result": { "config": { "id": "frontend", "cors": { /* ... */ }, "upstreams": [\ {\ "id": "blastapi-test",\ "endpoint": "blastapi#redacted=e6401",\ "type": "evm",\ "ignoreMethods": [\ "*"\ ],\ "allowMethods": [\ "eth_blockNumber"\ ],\ },\ // ...\ ], "networks": [\ {\ "architecture": "evm",\ "evm": {\ "chainId": 1,\ "fallbackFinalityDepth": 1024\ },\ "rateLimitBudget": "my-network-budget",\ // ...\ }\ ], "rateLimitBudget": "my-project-budget", // ... }, "health": { "upstreams": [\ {\ "id": "blastapi#redacted=e6401",\ "metrics": {\ "evm:1|eth_blockNumber": {\ "errorsTotal": 0,\ "remoteRateLimitedTotal": 0,\ "blockHeadLag": 0,\ "finalizationLag": 0,\ "cordoned": false,\ "latencySecs": {\ "p90": 0.110877458\ },\ "selfRateLimitedTotal": 0,\ "requestsTotal": 1,\ "lastCordonedReason": null\ },\ "*|eth_blockNumber": {\ "blockHeadLag": 0,\ "cordoned": false,\ "lastCordonedReason": null,\ "selfRateLimitedTotal": 0,\ "errorsTotal": 0,\ "remoteRateLimitedTotal": 0,\ "requestsTotal": 1,\ "finalizationLag": 0,\ "latencySecs": {\ "p90": 0.110877458\ }\ },\ "evm:1|*": {\ "blockHeadLag": 0,\ "finalizationLag": 0,\ "cordoned": false,\ "lastCordonedReason": null,\ "latencySecs": {\ "p90": 0.110877458\ },\ "errorsTotal": 0,\ "remoteRateLimitedTotal": 0,\ "selfRateLimitedTotal": 0,\ "requestsTotal": 1\ },\ "*|*": {\ "blockHeadLag": 0,\ "finalizationLag": 0,\ "latencySecs": {\ "p90": 0.110877458\ },\ "selfRateLimitedTotal": 0,\ "remoteRateLimitedTotal": 0,\ "requestsTotal": 1,\ "errorsTotal": 0,\ "cordoned": false,\ "lastCordonedReason": null\ }\ },\ "activeNetworks": [\ "evm:1"\ ]\ },\ // ...\ ], "sortedUpstreams": { "evm:1": { "*": [\ "my-alchemy",\ "blastapi-test"\ ], "eth_blockNumber": [\ "my-alchemy",\ "blastapi-test"\ ] }, "*": { "*": [\ "blastapi-test",\ "my-alchemy"\ ], "eth_blockNumber": [\ "my-alchemy",\ "blastapi-test"\ ] } }, "upstreamScores": { "blastapi-test": { "evm:1": { "eth_blockNumber": 14, "*": 14 }, "*": { "*": 15.41420133288338, "eth_blockNumber": 14 } }, "my-alchemy": { "evm:1": { "*": 19, "eth_blockNumber": 19 }, "*": { "eth_blockNumber": 19, "*": 14 } } } } } } ``` [Tracing](https://docs.erpc.cloud/operation/tracing "Tracing") ## eRPC Healthcheck [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Operation Healthcheck # Healthcheck eRPC has a built-in `/healthcheck` endpoint that can be used to check the health of the service within Kubernetes, Railway, etc. ## Config [Permalink for this section](https://docs.erpc.cloud/operation/healthcheck\#config) You can configure healthcheck on top-level in `erpc.yaml` file: ``` logLevel: debug server: # ... # (OPTIONAL) For zero-downtime deployments, wait before shutting down the server. # During this period active requests are still being processed but no new requests are accepted. # Because readiness /healthcheck endpoint will start returning unhealthy after receiving SIGTERM, # so that Kubernetes (or any other orchestrator) removes the old pod from list of available endpoints. # # You usually need two separate delays: # waitBeforeShutdown – after the pod receives SIGTERM it is marked **NotReady** (via healthcheck) but # the listener keeps running for this duration. Existing # requests can finish, new ones are rejected. Set it to at # least (readinessProbe.periodSeconds × readinessProbe.failureThreshold) + 1s. # waitAfterShutdown – once the HTTP server is fully stopped we keep the process # alive for this duration so load-balancers (Envoy, kube-proxy…) # can gracefully close any still-open TCP connections. waitBeforeShutdown: 30s waitAfterShutdown: 30s # ... healthCheck: # (OPTIONAL) Mode can be "simple" (just returns OK/ERROR) or "verbose" (returns detailed JSON) mode: verbose # (OPTIONAL) Default evaluation strategy to use when one isn't specified in the request # See the "Evaluation Strategies" section below for options... defaultEval: "any:initializedUpstreams" # (OPTIONAL) Authentication for the healthcheck endpoint auth: strategies: - type: secret secret: value: - type: network network: # To allow requests coming from the same host (localhost, 127.0.0.1, ::1) allowLocalhost: true # To allow requests coming from private networks allowedCIDRs: - "10.0.0.0/8" - "172.16.0.0/12" - "192.168.0.0/16" ``` It is recommended to use healthcheck endpoint for **readiness probe only**. For liveness probe use TCP healthcheck on the port specified in `server.httpPort` (4000 by default). ### Readiness and Liveness [Permalink for this section](https://docs.erpc.cloud/operation/healthcheck\#readiness-and-liveness) For zero-downtime deployments and general health tracking, configure readiness and liveness probes in your orchestrator's deployment configuration. For example in Kubernetes: ``` # Allow up to 1 minute to startup if there are too many upstreams or they are slow. startupProbe: httpGet: path: /healthcheck port: 4000 initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 6 # Readiness fails after 20 seconds max, after receiving SIGTERM. # Great for zero-downtime deployments, and good enough for general health tracking. readinessProbe: httpGet: path: /healthcheck port: 4000 initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 5 failureThreshold: 2 successThreshold: 1 # Liveness checks if http server is running, otherwise it means eRPC itself is dead. livenessProbe: tcpSocket: port: 4000 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 1 failureThreshold: 3 successThreshold: 1 ``` ## Evaluation Strategies [Permalink for this section](https://docs.erpc.cloud/operation/healthcheck\#evaluation-strategies) The healthcheck endpoint supports different strategies to evaluate the health of your upstreams. You can specify the strategy in the configuration or by adding `?eval=strategy_name` to the URL. Available strategies: | Strategy | Description | | --- | --- | | `any:initializedUpstreams` | Returns healthy if any upstreams are initialized (default) | | `all:activeUpstreams` | Returns healthy if all configured upstreams are initialized AND not cordoned | | `any:errorRateBelow90` | Returns healthy if any upstream has an error rate below 90% | | `all:errorRateBelow90` | Returns healthy if all upstreams have an error rate below 90% | | `any:errorRateBelow100` | Returns healthy if any upstream has an error rate below 100% | | `all:errorRateBelow100` | Returns healthy if all upstreams have an error rate below 100% | | `any:evm:eth_chainId` | Returns healthy if any EVM upstream reports the expected chain ID | | `all:evm:eth_chainId` | Returns healthy if all EVM upstreams report the expected chain ID | - Error rate is read from [score tracking](https://docs.erpc.cloud/config/projects/upstreams#priority--selection-mechanism) component of each Upstream and it is a fast memory-access operation. - The `eth_chainId` evals will send an actual request to the upstreams (in parallel), thus ensure proper timeout is set for the healthcheck (e.g. on Kubernetes readinessProbe.timeoutSeconds). - The `all:activeUpstreams` is an aggressive strategy that checks both initialization status and cordon status of ALL configured upstreams. An upstream is "cordoned" when [selection policy](https://docs.erpc.cloud/operation/config/projects/selection-policies) exclude it from the list. ## Endpoints [Permalink for this section](https://docs.erpc.cloud/operation/healthcheck\#endpoints) ### Global healthcheck [Permalink for this section](https://docs.erpc.cloud/operation/healthcheck\#global-healthcheck) Check the health of all projects and their upstreams: ``` curl http://localhost:4000/healthcheck -v # < HTTP/1.1 200 OK # OK ``` The global healthcheck checks all active projects and all upstreams. For example even if 1 upstream (on any network) is healthy the `any:initializedUpstreams` strategy will return healthy. ### Project-specific healthcheck [Permalink for this section](https://docs.erpc.cloud/operation/healthcheck\#project-specific-healthcheck) Check the health of a specific project and network: ``` curl http://localhost:4000/main/evm/1/healthcheck -v # OR http://localhost:4000/main/evm/1 # < HTTP/1.1 200 OK # OK ``` For project-specific healthchecks, only the upstreams for the specified network are checked. ### Using a custom evaluation strategy [Permalink for this section](https://docs.erpc.cloud/operation/healthcheck\#using-a-custom-evaluation-strategy) You can specify which evaluation strategy to use via the query parameter: ``` # Check if any upstream has an error rate below 90% curl http://localhost:4000/healthcheck?eval=any:errorRateBelow90 # Check if all EVM upstreams report the correct chain ID curl http://localhost:4000/main/evm/1/healthcheck?eval=all:evm:eth_chainId ``` The evaluation strategy can be specified in the [configuration](https://docs.erpc.cloud/operation/healthcheck#config) as well, as shown above. ## Response Modes [Permalink for this section](https://docs.erpc.cloud/operation/healthcheck\#response-modes) ### Simple Mode (default) [Permalink for this section](https://docs.erpc.cloud/operation/healthcheck\#simple-mode-default) In simple mode, the healthcheck returns a plain text "OK" with a 200 status code if healthy, or an error JSON with a non-200 status code if unhealthy. ``` curl http://localhost:4000/healthcheck # OK ``` ### Verbose Mode [Permalink for this section](https://docs.erpc.cloud/operation/healthcheck\#verbose-mode) In verbose mode, the healthcheck returns a detailed JSON response with information about the status of each project and upstream: ``` curl http://localhost:4000/healthcheck # { # "status": "OK", # "message": "all systems operational", # "details": { # "main": { # "status": "OK", # "message": "3 / 3 upstreams have low error rates", # "config": { # "networks": 2, # "upstreams": 3, # "providers": 1 # }, # "upstreams": { # "alchemy-mainnet": { # "network": "evm:1", # "metrics": { ... }, # "status": "OK" # }, # ... # } # } # } # } ``` ## Authentication [Permalink for this section](https://docs.erpc.cloud/operation/healthcheck\#authentication) If you've configured authentication for the healthcheck endpoint, you'll need to include the appropriate credentials: ``` # Using a token in a query parameter curl "http://localhost:4000/healthcheck?secret=CHANGE_ME" # ... OR using a token in a header curl http://localhost:4000/healthcheck -H "X-ERPC-Secret-Token: CHANGE_ME" ``` ## Aliasing healthcheck [Permalink for this section](https://docs.erpc.cloud/operation/healthcheck\#aliasing-healthcheck) If you have configured domain aliasing, you can append the `/healthcheck` to the URL: ``` # When aliasing is NOT used: curl http://rpc.example.com/main/evm/42161/healthcheck -v # When only project is aliased: curl http://rpc.example.com/evm/42161/healthcheck -v # When only project and network architecture is aliased: curl http://evm-rpc.example.com/42161/healthcheck -v # When all project, network architecture and chain are aliased: curl http://eth-evm-rpc.example.com/healthcheck -v ``` [URL](https://docs.erpc.cloud/operation/url "URL") [Batching](https://docs.erpc.cloud/operation/batch "Batching") ## eRPC Kubernetes Deployment [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Deployment Kubernetes # Kubernetes installation eRPC can be deployed on Kubernetes using the following manifests. These examples provide a basic setup that you can customize based on your needs. ### Configuration [Permalink for this section](https://docs.erpc.cloud/deployment/kubernetes\#configuration) First, create a ConfigMap and a Secret for your eRPC configuration: ``` apiVersion: v1 kind: ConfigMap metadata: name: erpc-config data: erpc.yaml: | logLevel: debug projects: - id: main upstreams: - endpoint: alchemy://${ALCHEMY_API_KEY} - endpoint: blastapi://${BLASTAPI_API_KEY} - endpoint: https://mynode-chain-1.svc.cluster.local --- apiVersion: v1 kind: Secret metadata: name: erpc-secrets type: Opaque stringData: ALCHEMY_API_KEY: your-alchemy-key-here BLASTAPI_API_KEY: your-blastapi-key-here ``` ### Deployment [Permalink for this section](https://docs.erpc.cloud/deployment/kubernetes\#deployment) Deploy eRPC with the following configuration: ``` apiVersion: apps/v1 kind: Deployment metadata: name: erpc labels: app: erpc spec: replicas: 1 selector: matchLabels: app: erpc template: metadata: labels: app: erpc spec: containers: - name: erpc image: ghcr.io/erpc/erpc:latest resources: # CPU limits removed as they can cause throttling issues requests: memory: "256Mi" limits: memory: "2Gi" env: - name: GOGC value: "40" - name: GOMEMLIMIT value: "1900MiB" envFrom: - secretRef: name: erpc-secrets ports: - containerPort: 4000 name: http - containerPort: 4001 name: metrics volumeMounts: - name: config mountPath: /erpc.yaml subPath: erpc.yaml startupProbe: httpGet: path: /healthcheck port: 4000 initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 6 readinessProbe: httpGet: path: /healthcheck port: 4000 initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 5 failureThreshold: 2 successThreshold: 1 livenessProbe: tcpSocket: port: 4000 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 1 failureThreshold: 3 successThreshold: 1 volumes: - name: config configMap: name: erpc-config # This must be same or greater than server.maxTimeout in erpc.yaml terminationGracePeriodSeconds: 180 ``` ### Service [Permalink for this section](https://docs.erpc.cloud/deployment/kubernetes\#service) Expose eRPC using a Service: ``` apiVersion: v1 kind: Service metadata: name: erpc labels: app: erpc spec: ports: - port: 4000 name: http targetPort: 4000 - port: 4001 name: metrics targetPort: 4001 selector: app: erpc ``` ### Horizontal Pod Autoscaling [Permalink for this section](https://docs.erpc.cloud/deployment/kubernetes\#horizontal-pod-autoscaling) Configure automatic scaling based on CPU and memory usage: ``` apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: erpc spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: erpc minReplicas: 1 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 80 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80 ``` ### Installation [Permalink for this section](https://docs.erpc.cloud/deployment/kubernetes\#installation) Apply the manifests using kubectl: ``` # Apply the manifests kubectl apply -f erpc-configmap.yaml kubectl apply -f erpc-secret.yaml kubectl apply -f erpc-deployment.yaml kubectl apply -f erpc-service.yaml kubectl apply -f erpc-hpa.yaml # Verify the deployment kubectl get pods kubectl get services kubectl get hpa ``` The eRPC service will be available within your cluster at `erpc:4000` for HTTP traffic and `erpc:4001` for metrics. [Railway](https://docs.erpc.cloud/deployment/railway "Railway") [Cloud](https://docs.erpc.cloud/deployment/cloud "Cloud") ## Deploy eRPC on Railway [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Deployment Railway # Railway installation [Railway (opens in a new tab)](https://railway.app/) provides a quick and easy way to deploy eRPC. To get started, please ensure that you have signed up or logged in to Railway and connected your GitHub account. ### Deploy [Permalink for this section](https://docs.erpc.cloud/deployment/railway\#deploy) Click the `Deploy on Railway` button below to get started. This will take you to our eRPC template, which includes proxy and monitoring services. [![Deploy on Railway](https://railway.app/button.svg) (opens in a new tab)](https://railway.app/template/10iW1q) This template comes with a default [erpc.yaml](https://docs.erpc.cloud/config/projects/providers#repository) configuration that will give you access to 2,000+ chains and 4,000+ public free EVM RPC endpoints. ### Config customizaiton [Permalink for this section](https://docs.erpc.cloud/deployment/railway\#config-customizaiton) ⚠️ **Frontend / browser usage?** If you plan to consume eRPC from a frontend (e.g browser), be sure to set up the appropriate CORS headers in your [erpc.yaml](https://docs.erpc.cloud/config/projects/cors#config) configuration. If you need further [customization](https://docs.erpc.cloud/config/example#full-config-example), you can fork the [template's repository (opens in a new tab)](https://github.com/erpc/railway-deployment). E.g. you can create an `erpc.yaml` file in your forked repository to add your own premium RPC endpoints, caching, customised network or upstream level failsafe configs, etc. After forking and adjustments, you can either connect your forked repository to your existing deployment or create a new service linked to this forked repository. ![image](https://i.imgur.com/xZQudNq.png) ### Usage in your services [Permalink for this section](https://docs.erpc.cloud/deployment/railway\#usage-in-your-services) If your backend services (like indexers or MEV bots) are on the same Railway project as eRPC, you can reduce cost and overhead by using private networking (`.railway.internal`) to connect: ``` const result = await fetch("https://my-erpc.railway.internal/main/evm/1", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ method: "eth_getBlockByNumber", params: ["0x1203319", false], id: 9199, jsonrpc: "2.0", }), }); ``` If you need external access or your services are hosted elsewhere, use the public URL found under `Settings > Networking > Public Networking` in your eRPC service: ![image](https://i.imgur.com/WRezSaK.png) ### Monitoring [Permalink for this section](https://docs.erpc.cloud/deployment/railway\#monitoring) After sending more requests, click on `monitoring` service and find your Grafana url under `Settings > Networking > Public Networking` You can login with the following credentials: - username: `admin` - password: `admin` ![image](https://i.imgur.com/sOpBuXe.png) Send more requests and watch the metrics being collected and visualized in Grafana. ![image](https://i.imgur.com/2aOA960.png) [Docker](https://docs.erpc.cloud/deployment/docker "Docker") [Kubernetes](https://docs.erpc.cloud/deployment/kubernetes "Kubernetes") ## eRPC Request Directives [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Operation Directives # Directives To instruct eRPC behavior on a per-request basis, you can provide directive "Headers" based on actual use-case: - [Retry empty responses](https://docs.erpc.cloud/operation/directives#retry-empty-responses) - [Retry pending transactions](https://docs.erpc.cloud/operation/directives#retry-pending-transactions) - [Skip cache read](https://docs.erpc.cloud/operation/directives#skip-cache-read) - [Use specific upstream(s)](https://docs.erpc.cloud/operation/directives#use-specific-upstreams) - [Validation directives](https://docs.erpc.cloud/config/failsafe/integrity#validations-directives) — Control response validation (bloom filters, receipts, logs) ## Retry empty responses [Permalink for this section](https://docs.erpc.cloud/operation/directives\#retry-empty-responses) By default all empty-ish responses will be retried, and only if all upstreams return the same empty response, then client will receive the empty response. Emptyish means any of these: - Response is `[]` empty array for example for eth\_getLogs - Response is `null` or `{}` empty object for example for eth\_getTransactionReceipt - Response is `""` or `0x` empty hashed byte, for example for certain eth\_call responses To explicitly disable this behavior for certain requests, you can use either: - Header `X-ERPC-Retry-Empty: false` - Or query parameter `?retry-empty=false` Empty-response retry behavior only applies when dealing with unfinalized data (recent blocks). For blocks in far past, empty responses are treated as final and won't be retried. For example when you're requesting eth\_getTransactionReceipt of mostly reecent transactions and prefer to immeditely get an empty response and handle it on your client side: ``` curl --location 'http://localhost:4000/main/evm/42161' \ --header 'Content-Type: application/json' \ --header 'X-ERPC-Retry-Empty: false' \ --data '{ "method": "eth_getTransactionReceipt", "params": [\ "0xe014f359cb3988f9944cd8003aac58812730383041993fdf762efcee21172d15",\ ], "id": 9199, "jsonrpc": "2.0" }' # OR curl --location 'http://localhost:4000/main/evm/42161?retry-empty=false' # ... ``` You can set this directive on network-wide configuration so that it applies to all requests: erpc.yaml ``` projects: - id: main # To apply to all networks in this project: networkDefaults: directiveDefaults: retryEmpty: false # (default: true) # For a specific network: networks: - type: evm evm: chainId: 137 directiveDefaults: retryEmpty: false # (default: true) ``` ## Retry pending transactions [Permalink for this section](https://docs.erpc.cloud/operation/directives\#retry-pending-transactions) By default requests towards pending transactions will be retried until tx is included (blockNumber is not `null`), and fail if even after all retries blockNumber is still null. This behavior is applied to these methods: - eth\_getTransactionByHash - eth\_getTransactionByBlockHashAndIndex - eth\_getTransactionByBlockNumberAndIndex - eth\_getTransactionReceipt To disable this behavior, you can use either: - Header `X-ERPC-Retry-Pending: false` - Or query parameter `?retry-pending=false` For example if you're intentionally looking to query data of pending transactions (e.g. MEV bot) and prefer to immeditely get the pending tx data: ``` curl --location 'http://localhost:4000/main/evm/42161' \ --header 'Content-Type: application/json' \ --header 'X-ERPC-Retry-Pending: false' \ --data '{ "method": "eth_getTransactionReceipt", "params": [\ "0xe014f359cb3988f9944cd8003aac58812730383041993fdf762efcee21172d15",\ ], "id": 9199, "jsonrpc": "2.0" }' # OR curl --location 'http://localhost:4000/main/evm/42161?retry-pending=false' # ... ``` Pending transactions (with blockNumber of `null`) are not stored in [cache](https://docs.erpc.cloud/config/database/evm-json-rpc-cache) because they are not guaranteed to be included in any block. You can set this directive on network-wide configuration so that it applies to all requests: erpc.yaml ``` projects: - id: main # To apply to all networks in this project: networkDefaults: directiveDefaults: retryPending: false # (default: true) # For a specific network: networks: - type: evm evm: chainId: 137 directiveDefaults: retryPending: false # (default: true) ``` ## Skip cache read [Permalink for this section](https://docs.erpc.cloud/operation/directives\#skip-cache-read) To instruct eRPC to skip 'reading' responses from cache, and make actual calls to upstreams. This directive is "false" by default, which means cache will be used. Useful when you need to force-refresh some data or override an already cached response. - Header `X-ERPC-Skip-Cache-Read: true` - Or query parameter `?skip-cache-read=true` ``` curl --location 'http://localhost:4000/main/evm/42161' \ --header 'Content-Type: application/json' \ --header 'X-ERPC-Skip-Cache-Read: true' \ --data '{ "method": "eth_getTransactionReceipt", "params": [\ "0xe014f359cb3988f9944cd8003aac58812730383041993fdf762efcee21172d15",\ ], "id": 9199, "jsonrpc": "2.0" }' # OR curl --location 'http://localhost:4000/main/evm/42161?skip-cache-read=true' # ... ``` > The new response will still be subject to caching as per usual. ## Use specific upstream(s) [Permalink for this section](https://docs.erpc.cloud/operation/directives\#use-specific-upstreams) When sending requests to eRPC you can instruct to use only one specific upstream (or multiple via wildcard match) using: - Header `X-ERPC-Use-Upstream: ` - Or query parameter `?use-upstream=` This will skip over any upstream that does not match the value you've provided. You can use `*` as wildcard character to match a group of upstreams. e.g. "priv-\*" will match any upstream IDs starting with "priv-" For example if you want to make sure that request is sent to a specific upstream: ``` curl --location 'http://localhost:4000/main/evm/42161' \ --header 'Content-Type: application/json' \ --header 'X-ERPC-Use-Upstream: up123' \ --data '{ "method": "eth_getTransactionReceipt", "params": [\ "0xe014f359cb3988f9944cd8003aac58812730383041993fdf762efcee21172d15",\ ], "id": 9199, "jsonrpc": "2.0" }' # OR curl --location 'http://localhost:4000/main/evm/42161?use-upstream=up123' # ... ``` ## Validation directives [Permalink for this section](https://docs.erpc.cloud/operation/directives\#validation-directives) For high-integrity use-cases (such as indexing) where data accuracy is critical, eRPC provides validation directives that check response structure and consistency. When validation fails, the response is rejected and retry/consensus policies automatically try other upstreams. Examples of what you can validate: - **Bloom filter consistency** — Ensure `logsBloom` matches actual logs in receipts - **Receipt structure** — Validate transaction indices, log indices, hash uniqueness - **Field formats** — Check header field lengths, transaction fields, log address/topic lengths - and many more... ``` # Enable bloom validation for a single request curl 'http://localhost:4000/main/evm/1?validate-logs-bloom-match=true' \ --header 'Content-Type: application/json' \ --data '{"method": "eth_getBlockReceipts", "params": ["0x123"], "id": 1, "jsonrpc": "2.0"}' ``` See [Integrity → Validation Directives](https://docs.erpc.cloud/config/failsafe/integrity#validations-directives) for the full list of available directives and configuration options. [Batching](https://docs.erpc.cloud/operation/batch "Batching") [Production](https://docs.erpc.cloud/operation/production "Production") ## eRPC Batching Guide [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Operation Batching # Batch requests eRPC automatically batches requests towards upstreams which support it. Additionally you can send batched requests (an array of multiple requests) to eRPC itself. 👋 Most often json-rpc batching for EVM is [anti-pattern (opens in a new tab)](https://www.quicknode.com/guides/quicknode-products/apis/guide-to-efficient-rpc-requests#avoid-batching-multiple-rpc-requests), as it increases resource consumption without significant benefits: - All requests will be as slow as the slowest request inside the batch. - JSON handling will be more expensive causing memory spikes and OOM errors. - Handling partial failures will be burdensome for the client (status code is always 200 OK). - Many 3rd-party providers (Alchemy, Infura, etc) charge based on number of method calls, not actual requests. - When running eRPC in private network locally close to your services, overhead of many single requests is negligible. ### How it works? [Permalink for this section](https://docs.erpc.cloud/operation/batch\#how-it-works) - When an upstream is configured to support batching, eRPC will accumulate as many requests as possible for that upstream, even if you send many single requests. - Batching mechanism respects other aspects of eRPC such as allowed/ignored methods, rate limits, supported/unsupported methods, therefore one huge batch request might be split into smaller ones depending on the most efficient distribution among upstreams. - Requests will be handled separately (or in mini-batches) and at the end results will be merged back together. - Response status code will always be `200 OK` because there might be a mix of successful and failed requests. - At the moment self-imposed rate limiters work as-if these requests are sent individually (Ping our engineers if this becomes an issue). Even if you send many single requests to eRPC they might be batched together if the upstream supports it. This minimizes the need to actually batch the requests on client-side, unless "network traffic" is a concern. In this scenario auto-batching mechanism is transparent to you. ## Upstream config [Permalink for this section](https://docs.erpc.cloud/operation/batch\#upstream-config) You can explicitly enable batching for an upstream as follows: yamltypescript erpc.yaml ``` # ... projects: - id: main upstreams: - id: blastapi-chain-42161 # ... endpoint: https://arbitrum-one.blastapi.io/xxxxxx jsonRpc: # When enabled eRPC will wait for a specified amount of time to batch as many requests as possible. supportsBatch: true # The maximum amount of time to wait to collect requests for a batch. batchMaxWait: 100ms # The maximum amount of requests in a single batch, which is usually enforced by the provider. batchMaxSize: 100 ``` For certain known providers (Alchemy, Infura, etc) batching is enabled by default. ### Example single chain [Permalink for this section](https://docs.erpc.cloud/operation/batch\#example-single-chain) When all the requests are for the same chain, you can send them to the URL that includes chain id. ``` curl --location 'http://localhost:4000/main/evm/1' \ --header 'Content-Type: application/json' \ --data '[\ {\ "method": "eth_getBlockByNumber",\ "params": [\ "0x1203318888888888",\ false\ ],\ "id": 8888,\ "jsonrpc": "2.0"\ },\ {\ "method": "eth_getBlockByNumber",\ "params": [\ "0x1203319",\ false\ ],\ "id": 9999,\ "jsonrpc": "2.0"\ }\ ]' ``` ### Example multi-chain [Permalink for this section](https://docs.erpc.cloud/operation/batch\#example-multi-chain) You can provide "networkId" within each request to specify which chain it is for by sending the request to project endpoint: ``` curl --location 'http://localhost:4000/main' \ --header 'Content-Type: application/json' \ --data '[\ {\ "networkId": "evm:1",\ "method": "eth_getBlockByNumber",\ "params": [\ "0x1203888",\ false\ ],\ "id": 888,\ "jsonrpc": "2.0"\ },\ {\ "networkId": "evm:42161",\ "method": "eth_getBlockByNumber",\ "params": [\ "0x1203999",\ false\ ],\ "id": 999,\ "jsonrpc": "2.0"\ }\ ]' ``` #### Roadmap [Permalink for this section](https://docs.erpc.cloud/operation/batch\#roadmap) On some doc pages we like to share our ideas for related future implementations, feel free to open a PR if you're up for a challenge: - [ ] Auto-batch multiple `eth_call`s for evm upstreams using multicall3 contracts if available on that chain. [Healthcheck](https://docs.erpc.cloud/operation/healthcheck "Healthcheck") [Directives](https://docs.erpc.cloud/operation/directives "Directives") ## eRPC Monitoring Guide [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Operation Monitoring # Monitoring and metrics Network-level and upstream-level metrics are available via [Prometheus (opens in a new tab)](https://prometheus.io/) and [Grafana (opens in a new tab)](https://grafana.com/). To enable metrics via config: yamltypescript erpc.yaml ``` # ... metrics: enabled: true listenV4: true hostV4: "0.0.0.0" listenV6: false hostV6: "[::]" port: 4001 errorLabelMode: "verbose" # Optional: "verbose" (default) or "compact" histogramBuckets: "0.01,0.1,0.5,1,5,10,60,300" # Optional: custom histogram buckets ``` ### Reducing Metrics Cardinality [Permalink for this section](https://docs.erpc.cloud/operation/monitoring\#reducing-metrics-cardinality) eRPC provides two configuration options to help reduce metrics cardinality, which can significantly decrease the storage requirements and query performance of your monitoring system. #### Error Label Mode [Permalink for this section](https://docs.erpc.cloud/operation/monitoring\#error-label-mode) The `errorLabelMode` setting controls how detailed error information is included in metrics labels: - `verbose`: Uses the full error message as labels (default for backward compatibility) - `compact`: Uses only the error type as labels, reducing cardinality significantly yamltypescript erpc.yaml ``` metrics: errorLabelMode: "compact" # "verbose" or "compact" ``` #### Histogram Buckets [Permalink for this section](https://docs.erpc.cloud/operation/monitoring\#histogram-buckets) You can customize histogram buckets to reduce cardinality and focus on relevant latency ranges: yamltypescript erpc.yaml ``` metrics: histogramBuckets: "0.01,0.1,0.5,1,5,10,60,300" ``` Setting fewer buckets or focusing on relevant latency ranges can significantly reduce the number of time series stored in your monitoring system. Refer to [erpc/docker-compose.yml (opens in a new tab)](https://github.com/erpc/erpc/blob/main/docker-compose.yml#L4-L17) and [erpc/monitoring (opens in a new tab)](https://github.com/erpc/erpc/tree/main/monitoring) for ready-made templates to bring up montoring. ### Available metrics [Permalink for this section](https://docs.erpc.cloud/operation/monitoring\#available-metrics) To get full list of available metrics check the source code of [erpc/health/metrics.go (opens in a new tab)](https://github.com/erpc/erpc/blob/main/health/metrics.go). ![eRPC Grafana Dashboard](https://docs.erpc.cloud/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmonitoring-example-erpc.2cb040a1.png&w=3840&q=75) Here is a list of some of the most important metrics: | Metric | Type | Description | | --- | --- | --- | | erpc\_upstream\_request\_total | Counter | Total number of actual requests to upstreams. | | erpc\_upstream\_request\_duration\_seconds | Histogram | Duration of requests to upstreams. | | erpc\_upstream\_request\_errors\_total | Counter | Total number of errors for requests to upstreams. | | erpc\_upstream\_request\_self\_rate\_limited\_total | Counter | Total number of self-imposed rate limited requests before sending to upstreams. | | erpc\_upstream\_request\_remote\_rate\_limited\_total | Counter | Total number of remote rate limited requests by upstreams. | | erpc\_upstream\_request\_skipped\_total | Counter | Total number of requests skipped by upstreams. | | erpc\_upstream\_request\_missing\_data\_error\_total | Counter | Total number of requests where upstream is missing data or not synced yet. | | erpc\_upstream\_request\_empty\_response\_total | Counter | Total number of empty responses from upstreams. | | erpc\_upstream\_block\_head\_lag | Gauge | Total number of blocks (head) behind the most up-to-date upstream. | | erpc\_upstream\_finalization\_lag | Gauge | Total number of finalized blocks behind the most up-to-date upstream. | | erpc\_upstream\_score\_overall | Gauge | Overall score of upstreams. | | erpc\_upstream\_latest\_block\_number | Gauge | Latest block number of upstreams. | | erpc\_upstream\_finalized\_block\_number | Gauge | Finalized block number of upstreams. | | erpc\_network\_latest\_block\_timestamp\_distance\_seconds | Gauge | Distance in seconds between the latest block timestamp and current time for a network. | | erpc\_upstream\_cordoned | Gauge | Whether upstream is excluded from routing by selection policy. (0=uncordoned or 1=cordoned) | | erpc\_upstream\_stale\_latest\_block\_total | Counter | Total number of times an upstream returned a stale latest block number (vs others). | | erpc\_upstream\_stale\_finalized\_block\_total | Counter | Total number of times an upstream returned a stale finalized block number (vs others). | | erpc\_upstream\_evm\_get\_logs\_stale\_upper\_bound\_total | Counter | Total number of times eth\_getLogs was skipped due to upstream latest block being less than requested toBlock. | | erpc\_upstream\_evm\_get\_logs\_stale\_lower\_bound\_total | Counter | Total number of times eth\_getLogs was skipped due to fromBlock being less than upstream's available block range. | | erpc\_upstream\_evm\_get\_logs\_range\_exceeded\_auto\_splitting\_threshold\_total | Counter | Total number of times eth\_getLogs request exceeded the block range threshold and needed splitting (based on upstream config for "upstream.evm.getLogsAutoSplittingRangeThreshold"). | | erpc\_upstream\_evm\_get\_logs\_forced\_splits\_total | Counter | Total number of eth\_getLogs request splits by dimension (block\_range, addresses, topics), due to a complain/error from upstream (e.g. "Returned too many results use a smaller block range"). | | erpc\_upstream\_evm\_get\_logs\_split\_success\_total | Counter | Total number of successful split eth\_getLogs sub-requests. | | erpc\_upstream\_evm\_get\_logs\_split\_failure\_total | Counter | Total number of failed split eth\_getLogs sub-requests. | | erpc\_upstream\_latest\_block\_polled\_total | Counter | Total number of times the latest block was pro-actively polled from an upstream. | | erpc\_upstream\_finalized\_block\_polled\_total | Counter | Total number of times the finalized block was pro-actively polled from an upstream. | | erpc\_network\_request\_received\_total | Counter | Total number of requests received by the network. | | erpc\_network\_multiplexed\_request\_total | Counter | Total number of multiplexed requests received by the network. | | erpc\_network\_failed\_request\_total | Counter | Total number of failed requests received by the network. | | erpc\_network\_request\_self\_rate\_limited\_total | Counter | Total number of self-imposed rate limited requests before sending to upstreams. | | erpc\_network\_successful\_request\_total | Counter | Total number of successful requests received by the network. | | erpc\_network\_cache\_hits\_total | Counter | Total number of cache hits for requests received by the network. | | erpc\_network\_cache\_misses\_total | Counter | Total number of cache misses for requests received by the network. | | erpc\_network\_request\_duration\_seconds | Histogram | Duration of requests received by the network. | | erpc\_project\_request\_self\_rate\_limited\_total | Counter | Total number of self-imposed rate limited requests towards the project. | | erpc\_rate\_limiter\_budget\_max\_count | Gauge | Maximum number of requests allowed per second for a rate limiter budget | | erpc\_auth\_request\_self\_rate\_limited\_total | Counter | Total number of self-imposed rate limited requests due to auth config for a project. | | erpc\_cache\_set\_success\_total | Counter | Total number of cache set operations. | | erpc\_cache\_set\_error\_total | Counter | Total number of cache set errors. | | erpc\_cache\_set\_skipped\_total | Counter | Total number of cache set skips. | | erpc\_cache\_get\_success\_hit\_total | Counter | Total number of cache get hits. | | erpc\_cache\_get\_success\_miss\_total | Counter | Total number of cache get misses. | | erpc\_cache\_get\_error\_total | Counter | Total number of cache get errors. | | erpc\_cache\_get\_skipped\_total | Counter | Total number of cache get skips (i.e. no matching policy found). | | erpc\_cors\_requests\_total | Counter | Total number of CORS requests received. | | erpc\_cors\_preflight\_requests\_total | Counter | Total number of CORS preflight requests received. | | erpc\_cors\_disallowed\_origin\_total | Counter | Total number of CORS requests from disallowed origins. | #### PromQL examples [Permalink for this section](https://docs.erpc.cloud/operation/monitoring\#promql-examples) ``` # Request rate per second by network over last 5 minutes sum(rate(erpc_network_request_received_total{}[5m])) by (network) # Total daily requests by project and network sum(increase(erpc_network_request_received_total{}[24h])) by (project, network) # Top 5 project and networks by request volume topk(5, sum(rate(erpc_network_request_received_total{}[5m])) by (project, network)) # Error rate percentage by network and upstream 100 * sum(rate(erpc_upstream_request_errors_total{}[5m])) by (network, upstream) / sum(rate(erpc_upstream_request_total{}[5m])) by (network, upstream) # Top error types in the last hour topk(10, sum(increase(erpc_upstream_request_errors_total{}[1h])) by (error)) # Missing data errors by network and upstream sum(rate(erpc_upstream_request_missing_data_error_total{}[5m])) by (network, upstream) # 95th percentile request duration by network histogram_quantile(0.95, sum(rate(erpc_network_request_duration_seconds_bucket{}[5m])) by (le,network)) # Average request duration for eth_call methods sum(rate(erpc_upstream_request_duration_seconds_sum{category="eth_call"}[5m])) by (network, upstream) / sum(rate(erpc_upstream_request_duration_seconds_count{category="eth_call"}[5m])) by (network, upstream) # Identify slow upstreams (avg duration > 500ms) sum(rate(erpc_upstream_request_duration_seconds_sum{}[5m])) by (network, upstream) / sum(rate(erpc_upstream_request_duration_seconds_count{}[5m])) by (network, upstream) > 0.5 # Cache hit ratio by network sum(rate(erpc_network_cache_hits_total{}[5m])) by (network) / ( sum(rate(erpc_network_cache_hits_total{}[5m])) by (network) + sum(rate(erpc_network_cache_misses_total{}[5m])) by (network) ) # Cache miss rate for eth_getBlockByNumber rate(erpc_network_cache_misses_total{category="eth_getBlockByNumber"}[5m]) # Self rate-limited requests by project and network sum(rate(erpc_network_request_self_rate_limited_total{}[5m])) by (project,network) # Authentication rate limiting by strategy sum(rate(erpc_auth_request_self_rate_limited_total{strategy="jwt"}[5m])) by (project) # Remote rate limiting from upstreams sum(rate(erpc_upstream_request_remote_rate_limited_total{}[5m])) by (upstream) # Block lag by network and upstream max(erpc_upstream_block_head_lag) by (network,upstream) # Finalization lag alert (lag > 5 blocks) max(erpc_upstream_finalization_lag) by (network) > 5 # Block height difference between upstreams max(erpc_upstream_latest_block_number) by (network) - min(erpc_upstream_latest_block_number) by (network) # Overall upstream health score avg(erpc_upstream_score_overall) by (network, upstream) # CORS issues by origin sum(rate(erpc_cors_disallowed_origin_total{}[5m])) by (project, origin) # Network block timestamp distance (how far behind is the latest block) # All sources erpc_network_latest_block_timestamp_distance_seconds{network=~"${network:regex}"} # Only from EVM state poller (internal polling) erpc_network_latest_block_timestamp_distance_seconds{network=~"${network:regex}",origin="evm_state_poller"} # Only from network responses (what clients receive, including cached responses) erpc_network_latest_block_timestamp_distance_seconds{network=~"${network:regex}",origin="network_response"} # Alert if block timestamp is too far behind (> 30 seconds) erpc_network_latest_block_timestamp_distance_seconds > 30 ``` [Production](https://docs.erpc.cloud/operation/production "Production") [Tracing](https://docs.erpc.cloud/operation/tracing "Tracing") ## eRPC Configuration Examples [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Config erpc.yaml/ts # Complete config example This example config demonstrates all features of eRPC in one place. For more explanation of each section, refer to dedicated pages: - [Database](https://docs.erpc.cloud/config/database/drivers): to configure caching and database. - [Projects](https://docs.erpc.cloud/config/projects): to define multiple projects with different rate limit budgets. - [Networks](https://docs.erpc.cloud/config/projects/networks): to configure failsafe policies for each network. - [Upstreams](https://docs.erpc.cloud/config/projects/upstreams): to configure upstreams with failsafe policies, rate limiters, allowed/rejected methods, etc. - [Rate limiters](https://docs.erpc.cloud/config/rate-limiters): to configure various self-imposed budgets to prevent pressure on upstreams. - [Failsafe](https://docs.erpc.cloud/config/failsafe): explains different policies such as retry, timeout, and hedges, used for networks and upstreams. By default `erpc` binary will look for `./erpc.ts`, `./erpc.yaml`, `./erpc.yml` files in the current directory. You can change this path by passing an argument to the binary: yamltypescript ``` $ erpc /path/to/your/erpc.yaml ``` ### Minimal config example [Permalink for this section](https://docs.erpc.cloud/config/example\#minimal-config-example) eRPC will auto-detect or use sane defaults for various configs such as retries, timeouts, circuit-breaker, hedges, node architecture etc. **eRPC is Multi-chain** A single instance of eRPC can server multiple projects (frontend, indexer, etc) and multiple chains. yamltypescript erpc.yaml ``` logLevel: debug projects: - id: main upstreams: # Put all your RPC endpoints for any network here. # You don't need to define architecture (e.g. evm) or chain id (e.g. 42161) # as they will be detected automatically by eRPC. - endpoint: https://xxxxx.matic.quiknode.pro/xxxxxxxxxx/ - endpoint: drpc://XXX_MY_DRPC.ORG_API_KEY_XXX # Add all supported chains of drpc.org - endpoint: blastapi://XXX_MY_BLASTAPI.IO_API_KEY_XXX # Add all supported chains of blastapi.io - endpoint: alchemy://XXX_MY_ALCHEMY.COM_API_KEY_XXX # Add all supported chains of alchemy.com - endpoint: envio://rpc.hypersync.xyz # Add all supported methods and chains of envio.dev HyperRPC ``` ### Full config example [Permalink for this section](https://docs.erpc.cloud/config/example\#full-config-example) To have more control over the configuration, you can use the example below. yamltypescript erpc.yaml ``` # Log level helps in debugging or error detection: # - debug: information down to actual request and responses, and decisions about rate-liming etc. # - info: usually prints happy paths and might print 1 log per request indicating of success or failure. # - warn: these problems do not cause end-user problems, but might indicate degredataion or an issue such as cache databse being down. # - error: these are problems that have end-user impact, such as misconfigurations. logLevel: warn # The main server for eRPC to listen for requests. server: listenV4: true httpHostV4: "0.0.0.0" httpPortV4: 4000 # listenV6: false # httpHostV6: "[::]" # httpPortV6: 5000 maxTimeout: 30s readTimeout: 10s writeTimeout: 20s enableGzip: true waitBeforeShutdown: 30s waitAfterShutdown: 30s tls: enabled: false certFile: "/path/to/cert.pem" keyFile: "/path/to/key.pem" caFile: "/path/to/ca.pem" # Optional, for client cert verification insecureSkipVerify: false # Optional, defaults to false # Optional Prometheus metrics server metrics: enabled: true listenV4: true hostV4: "0.0.0.0" listenV6: false hostV6: "[::]" port: 4001 # There are various use-cases of database in erpc, such as caching, dynamic configs, rate limit persistence, etc. database: # `evmJsonRpcCache` defines the destination for caching JSON-RPC cals towards any EVM architecture upstream. # This database is non-blocking on critical path, and is used as best-effort. # Make sure the storage requirements meet your usage, for example caching 70m blocks + 10m txs + 10m traces on Arbitrum needs 200GB of storage. evmJsonRpcCache: # Refer to "Database" section for more details. # Note that table, schema and indexes will be created automatically if they don't exist. connectors: - id: memory-cache driver: memory memory: maxItems: 100000 - id: postgres-cache driver: postgresql postgresql: connectionUri: >- postgres://YOUR_USERNAME_HERE:YOUR_PASSWORD_HERE@your.postgres.hostname.here.com:5432/your_database_name table: rpc_cache policies: - network: "*" method: "*" finality: finalized connector: memory-cache ttl: 0 - network: "*" method: "*" finality: unfinalized connector: memory-cache maxItemSize: 1MB # optional max size of item to store via this policy ttl: 5s - network: "*" method: "*" finality: unknown connector: memory-cache ttl: 5s - network: "*" # supports * as wildcard and | as OR operator method: "eth_getLogs|trace_*" # supports * as wildcard and | as OR operator finality: finalized connector: postgres-cache ttl: 0 - network: "evm:42161|evm:10" # supports * as wildcard and | as OR operator method: "arbtrace_*" # supports * as wildcard and | as OR operator finality: finalized connector: postgres-cache ttl: 86400s # Each project is a collection of networks and upstreams. # For example "backend", "indexer", "frontend", and you want to use only 1 project you can name it "main" # The main purpose of multiple projects is different failsafe policies (more aggressive and costly, or less costly and more error-prone) projects: - id: main # Optionally you can define a self-imposed rate limite budget for each project # This is useful if you want to limit the number of requests per second or daily allowance. rateLimitBudget: frontend-budget # This array configures network-specific (a.k.a chain-specific) features. # For each network "architecture" and corresponding network id (e.g. evm.chainId) is required. # Remember defining networks is OPTIONAL, so only provide these only if you want to override defaults. networks: - architecture: evm evm: chainId: 1 # Refer to "Failsafe" section for more details. # On network-level "timeout" is applied for the whole lifecycle of the request (including however many retries) failsafe: - matchMethod: "*" # Default policy for all methods timeout: duration: 30s retry: maxAttempts: 3 delay: 0ms # Defining a "hedge" is highly-recommended on network-level because if upstream A is being slow for # a specific request, it can start a new parallel hedged request to upstream B, for whichever responds faster. hedge: delay: 500ms maxCount: 1 circuitBreaker: failureThresholdCount: 160 # 80% error rate failureThresholdCapacity: 200 halfOpenAfter: 5m successThresholdCount: 3 successThresholdCapacity: 3 - architecture: evm evm: chainId: 42161 failsafe: - matchMethod: "*" timeout: duration: 30s retry: maxAttempts: 3 delay: 0ms hedge: delay: 500ms maxCount: 1 # Each upstream supports 1 or more networks (chains) upstreams: - id: blastapi-chain-42161 type: evm endpoint: https://arbitrum-one.blastapi.io/xxxxxxx-xxxxxx-xxxxxxx # Defines which budget to use when hadnling requests of this upstream. rateLimitBudget: global-blast # chainId is optional and will be detected from the endpoint (eth_chainId) but it is recommended to set it explicitly, for faster initialization. evm: chainId: 42161 # Which methods must never be sent to this upstream: ignoreMethods: - "alchemy_*" - "eth_traceTransaction" # Refer to "Failsafe" section for more details: failsafe: - matchMethod: "*" # Default policy for all methods timeout: duration: 15s retry: maxAttempts: 2 delay: 500ms backoffMaxDelay: 3s backoffFactor: 1.2 jitter: 0ms - id: blastapi-chain-1 type: evm endpoint: https://eth-mainnet.blastapi.io/xxxxxxx-xxxxxx-xxxxxxx rateLimitBudget: global-blast evm: chainId: 1 failsafe: - matchMethod: "*" timeout: duration: 15s retry: maxAttempts: 2 delay: 500ms backoffMaxDelay: 3s backoffFactor: 1.2 jitter: 0ms - id: quiknode-chain-42161 type: evm endpoint: https://xxxxxx-xxxxxx.arbitrum-mainnet.quiknode.pro/xxxxxxxxxxxxxxxxxxxxxxxx/ rateLimitBudget: global-quicknode # You can enable auto-ignoring unsupported methods, instead of defining them explicitly. # NOTE: some providers (e.g. dRPC) are not consistent with "unsupported method" responses, # so this feature might mark methods as unsupported that are actually supported! autoIgnoreUnsupportedMethods: true # To allow auto-batching requests towards the upstream, use these settings. # Remember if "supportsBatch" is false, you still can send batch requests to eRPC # but they will be sent to upstream as individual requests. jsonRpc: supportsBatch: true batchMaxSize: 10 batchMaxWait: 100ms evm: chainId: 42161 failsafe: - matchMethod: "*" timeout: duration: 15s retry: maxAttempts: 2 delay: 500ms backoffMaxDelay: 3s backoffFactor: 1.2 jitter: 0ms # "id" is a unique identifier to distinguish in logs and metrics. - id: alchemy-multi-chain-example # For certain known providers (such as Alchemy) you use a custom protocol name # which allows a single upstream to import "all chains" supported by that provider. # Note that these chains are hard-coded in the repo, so if they support a new chain eRPC must be updated. endpoint: alchemy://XXXX_YOUR_ALCHEMY_API_KEY_HERE_XXXX rateLimitBudget: global failsafe: - matchMethod: "*" timeout: duration: 15s retry: maxAttempts: 2 delay: 500ms backoffMaxDelay: 3s backoffFactor: 1.2 jitter: 0ms # Rate limiter allows you to create "shared" budgets for upstreams. # For example upstream A and B can use the same budget, which means both of them together must not exceed the defined limits. rateLimiters: budgets: - id: default-budget rules: - method: "*" maxCount: 10000 period: 1s waitTime: 100ms # Allow waiting up to 100ms for capacity to free up (Default is 0 meaning immediate error) - id: global-blast rules: - method: "*" maxCount: 1000 period: 1s - id: global-quicknode rules: - method: "*" maxCount: 300 period: 1s - id: frontend-budget rules: - method: "*" maxCount: 500 period: 1s ``` [FAQ](https://docs.erpc.cloud/faq "FAQ") [Projects](https://docs.erpc.cloud/config/projects "Projects") ## eRPC Authentication Config [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Config Auth # Authentication Each project can have one or more authentication strategies enabled. When any authentication strategy is defined all requests towards the project must comply with at least one of the strategies. ## Config [Permalink for this section](https://docs.erpc.cloud/config/auth\#config) The appropriate strategy will be activated based on request payload. For example if "token" is present as a query string then "secret" strategy will be activated. These are currently supported strategies: - [`secret`](https://docs.erpc.cloud/config/auth#secret) - [`network`](https://docs.erpc.cloud/config/auth#network) - [`jwt`](https://docs.erpc.cloud/config/auth#jwt) - [`siwe`](https://docs.erpc.cloud/config/auth#siwe) yamltypescript erpc.yaml ``` logLevel: debug projects: - id: frontend auth: strategies: # Define a simple secret token for authentication of this project: - type: secret rateLimitBudget: free-tier secret: value: "some-random-secret-value" # Define another secret token, that can also be used, but with higher rate limit: - type: secret rateLimitBudget: premium secret: value: "some-other-random-secret-value" upstreams: # ... rateLimiters: # ... ``` #### Method filtering [Permalink for this section](https://docs.erpc.cloud/config/auth\#method-filtering) You can allow or disallow certain methods when a client is authenticated by a specific strategy. For example you can limit types of method available for a certain IP (or token), or define multiple secret tokens with different allowed methods. `allowMethods` takes precedence over `ignoreMethods`. For example if you only want to allow eth\_getLogs for a certain IP, you can: Both allowMethods and ignoreMethods support wildcard `*` anywhere in the method name. yamltypescript erpc.yaml ``` projects: - id: main auth: strategies: - type: secret ignoreMethods: - eth_getLogs - alchemy_* allowMethods: - alchemy_getAssetTransfers # ... upstreams: # ... rateLimiters: # ... ``` #### Rate limiter [Permalink for this section](https://docs.erpc.cloud/config/auth\#rate-limiter) For each strategy item defined for a project you can enforce a separate rate limit budget. For example to limit users providing secret A to 100 requests per second, and users providing secret B to 1000 requests per second. ⚠️ At the moment, rate limit budgets apply across all clients authenticated by a specific strategy, and **NOT** per user. For example in sample below, no matter how many actual clients use the premium secret token, all of them **together** cannot exceed 1000 requests per second. yamltypescript erpc.yaml ``` projects: - id: main auth: strategies: - type: secret rateLimitBudget: free-tier # ... - type: jwt rateLimitBudget: premium # ... upstreams: # ... rateLimiters: budgets: - id: low-tier rules: - method: '*' maxCount: 10 period: 1s - id: premium rules: - method: '*' maxCount: 1000 period: 1s ``` ## `secret` strategy [Permalink for this section](https://docs.erpc.cloud/config/auth\#secret-strategy) A simple strategy that allows you to define a secret value that will be checked against a `token` provided via query string, or via `X-ERPC-Secret-Token` header. This strategy is mainly recommended for backend to backend communication. Exposing this token on your frontend allows users to impersonate the requests from anywhere. If you still want to use this strategy on frontend, make sure [CORS configuration](https://docs.erpc.cloud/config/projects/cors) are defined to reduce the potential abuse. yamltypescript erpc.yaml ``` projects: - id: main auth: strategies: - type: secret ignoreMethods: - eth_getLogs - alchemy_* allowMethods: - alchemy_getAssetTransfers rateLimitBudget: premium secret: id: "custom-id-for-metrics" value: "some-random-secret-value" # To use env vars: ${MY_SECRET_VALUE} upstreams: # ... rateLimiters: budgets: - id: premium rules: - method: '*' maxCount: 1000 period: 1s ``` The client must provide this value either via a query string parameter: ``` curl -X POST https://localhost:4000/main/evm/42161?secret=some-random-secret-value \ # ... ``` Or via a header: ``` curl -X POST https://localhost:4000 \ -H "X-ERPC-Secret-Token: some-random-secret-value" # ... ``` ## `network` strategy [Permalink for this section](https://docs.erpc.cloud/config/auth\#network-strategy) To prevent requests based on IP address of the client, use `network` strategy: yamltypescript erpc.yaml ``` projects: - id: main auth: strategies: - type: network network: # To allow requests coming from the same host (localhost, 127.0.0.1, ::1) allowLocalhost: true # To allow requests coming from the specific IPs allowedIPs: - "89.123.123.123" # To allow requests coming from the specific CIDR ranges allowedCIDRs: - "78.13.0.0/16" # When requests carry X-Forwarded-For header, you can define trusted proxies # that are allowed to override the client's IP address. # # These will evaluate X-Forwarded-For value from the left to the right, # and will use the first IP address that is not in the trustedProxies list. # # Example 1: # X-Forwarded-For: 192.168.1.123, 22.22.22.22, 33.33.33.33 # trustedProxies: # - "192.168.1.123" # \_____ Detected client IP: 22.22.22.22 # # Example 2: # X-Forwarded-For: 11.11.11.11, 22.22.22.22, 33.33.33.33 # trustedProxies: # - "192.168.1.123" # \_____ Detected client IP: 11.11.11.11 trustedProxies: - "192.168.1.123" upstreams: # ... rateLimiters: # ... ``` ## `jwt` strategy [Permalink for this section](https://docs.erpc.cloud/config/auth\#jwt-strategy) Use [JWT (opens in a new tab)](https://jwt.io/) strategy to only allow requests carrying a JWT token signed by you or a trusted party. The main requirement for this strategy is public key(s) that you trust. For frontend dApps this strategy is the **most recommended** because it allows control over how many users can hit your RPC endpoint and the "expiration" prevents users from abusing the RPC by copying the jwt token in multiple places. If you already use a JWT for your frontend, you can use the same token for eRPC, only providing the proper public key(s). This strategy respects the JWT token's expiration (`exp` claim) and will reject the request if token has expired. yamltypescript erpc.yaml ``` projects: - id: main auth: strategies: - type: jwt jwt: # At least one public key must be provided, you can either provide the public key PEM as plain value, # or provide a path to the file containing the public key. # # The for each verification key you can use their "kid" (e.g. rsa-kid-1) as a key, and provide the PEM as a value. verificationKeys: "rsa-kid-1": "file:///Users/aram/www/0xflair/erpc/test/aux/public_key.pem" "rsa-kid-2": "${MY_RSA_KEY_2_PEM}" # Optional list of issuers that are allowed, if token has a different "iss" claim it will be rejected. allowedIssuers: - "https://erpc.web3-project.xyz" # Optional list of audiences that are allowed, if token has a different "aud" claim it will be rejected. allowedAudiences: - "https://frontend.web3-project.xyz" # Optional list of algorithms that are allowed, if token has a different "alg" header it will be rejected. allowedAlgorithms: - "RS256" - "HS256" # Optional list of claims that are required to be present in the token, otherwise the token will be rejected. requiredClaims: - "sub" - "role" upstreams: # ... rateLimiters: # ... ``` ## `siwe` strategy [Permalink for this section](https://docs.erpc.cloud/config/auth\#siwe-strategy) Many frontend dApps already use [Sign-in with Ethereum (opens in a new tab)](https://eips.ethereum.org/EIPS/eip-4361) (SIWE) to authenticate wallets. You can use `siwe` strategy to allow requests from your dApp by providing the signature and signed message: Message (which includes your statement, domain, expiration, etc) must be provided as base64 encoded string. yamltypescript erpc.yaml ``` projects: - id: main auth: strategies: - type: siwe siwe: # A list of domains from which SIWE messages are allowed to be signed. allowedDomains: - "my-web3-project.xyz" upstreams: # ... rateLimiters: # ... ``` Message and signature can be provided via query string parameters: ``` curl -X POST https://localhost:4000/main/evm/42161?message=my_message_base64_ecnoded&signature=0x123456 \ # ... ``` or via `X-ERPC-SIWE-Message` and `X-ERPC-SIWE-Signature` headers: ``` curl -X POST https://localhost:4000 \ -H "X-ERPC-SIWE-Message: my_message_base64_ecnoded" -H "X-ERPC-SIWE-Signature: 0x123456" # ... ``` #### Roadmap [Permalink for this section](https://docs.erpc.cloud/config/auth\#roadmap) On some doc pages we like to share our ideas for related future implementations, feel free to open a PR if you're up for a challenge: - [ ] Allow defining rate-limits per user (vs across all users), for more granular control over usage. [Shared State](https://docs.erpc.cloud/config/database/shared-state "Shared State") [Rate limiters](https://docs.erpc.cloud/config/rate-limiters "Rate limiters") ## OpenTelemetry Tracing [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Operation Tracing # OpenTelemetry Tracing eRPC includes support for distributed tracing using OpenTelemetry. This allows you to track requests as they flow through the system, identify performance bottlenecks, and debug issues in production environments. ## Config [Permalink for this section](https://docs.erpc.cloud/operation/tracing\#config) To enable tracing, add the following to your `erpc.yaml` configuration: ``` tracing: enabled: true endpoint: "localhost:4317" # OTLP endpoint (Jaeger, Tempo, etc.) protocol: "grpc" # "grpc" or "http" sampleRate: 0.1 # Sample 10% of requests detailed: true # Include detailed tracing information tls: enabled: false # Enable TLS for secure connections # certFile: "/path/to/cert.pem" # keyFile: "/path/to/key.pem" # caFile: "/path/to/ca.pem" server: # ... projects: # ... ``` ### Detailed tracing [Permalink for this section](https://docs.erpc.cloud/operation/tracing\#detailed-tracing) When `tracing.detailed` is set to true, eRPC will include detailed tracing information in the traces. This includes: - Internal operations and mutex locks (useful to debug long requests that are not waiting for any I/O) - High-cardinality attributes (e.g. request json-rpc IDs, request params, actual cache keys used, etc.) Remember that detailed tracing can significantly increase the volume of traces, so use it judiciously. ### Custom resource attributes [Permalink for this section](https://docs.erpc.cloud/operation/tracing\#custom-resource-attributes) You can add custom attributes to all traces from an eRPC instance using `resourceAttributes`. This is useful for adding deployment-specific metadata like region, machine ID, or pod name. Values support environment variable expansion using `${VAR}` syntax. Attributes with empty values (after expansion) are automatically omitted. ``` tracing: enabled: true endpoint: "localhost:4317" protocol: "grpc" sampleRate: 0.1 # Custom attributes added to all traces from this instance resourceAttributes: fly.region: ${FLY_REGION} # Fly.io region fly.machine_id: ${FLY_MACHINE_ID} # Fly.io machine ID # Or for Kubernetes: # k8s.pod_name: ${HOSTNAME} # k8s.node_name: ${NODE_NAME} ``` This allows you to filter and group traces by region/machine in your tracing backend (Jaeger, Tempo, etc.) to debug region-specific issues. ## Using with Jaeger [Permalink for this section](https://docs.erpc.cloud/operation/tracing\#using-with-jaeger) The included [`docker-compose.yml` (opens in a new tab)](https://github.com/erpc/erpc/blob/main/docker-compose.yml) file contains a Jaeger service for visualizing traces. To use it: 1. Start the Jaeger container: ``` docker-compose up jaeger ``` 2. Configure eRPC to send traces to Jaeger: ``` tracing: enabled: true endpoint: "localhost:4317" protocol: "grpc" sampleRate: 1.0 # Sample all requests during development detailed: true ``` 3. Access the Jaeger UI at [http://localhost:16686 (opens in a new tab)](http://localhost:16686/) ## Traced components [Permalink for this section](https://docs.erpc.cloud/operation/tracing\#traced-components) The following components are instrumented with tracing: - HTTP server request handling - Network-level (chain) forwarding - Upstream-level request forwarding - Cache operations (get/set) - Failsafe executor operations (hedges, retries) - HTTP client requests to upstreams - Rate limiters - And more... If you noticed a missing component from tracing, free free to open an [issue or PR (opens in a new tab)](https://github.com/erpc/erpc/issues/new)! [Monitoring](https://docs.erpc.cloud/operation/monitoring "Monitoring") [Admin](https://docs.erpc.cloud/operation/admin "Admin") ## eRPC Docker Deployment [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Deployment Docker # Docker installation eRPC provides official Docker images that can be used to quickly deploy the service. Follow these steps to get started: ### Create configuration [Permalink for this section](https://docs.erpc.cloud/deployment/docker\#create-configuration) Create your `erpc.yaml` configuration file. You can start with the minimal example: yamltypescript ``` logLevel: debug projects: - id: main upstreams: - endpoint: alchemy://XXX_MY_ALCHEMY_API_KEY_XXX - endpoint: blastapi://XXX_MY_BLASTAPI_API_KEY_XXX ``` See the [complete config example](https://docs.erpc.cloud/config/example) for all available options and detailed explanations. ### Run eRPC container [Permalink for this section](https://docs.erpc.cloud/deployment/docker\#run-erpc-container) Run the Docker container, mounting your configuration file: ``` docker run -v $(pwd)/erpc.yaml:/erpc.yaml \ -p 4000:4000 -p 4001:4001 \ ghcr.io/erpc/erpc:latest ``` ### Test the deployment [Permalink for this section](https://docs.erpc.cloud/deployment/docker\#test-the-deployment) Send a test request to verify the setup: ``` curl --location 'http://localhost:4000/main/evm/1' \ --header 'Content-Type: application/json' \ --data '{ "method": "eth_getBlockByNumber", "params": ["0x1203319", false], "id": 1, "jsonrpc": "2.0" }' ``` ### Setup monitoring (optional) [Permalink for this section](https://docs.erpc.cloud/deployment/docker\#setup-monitoring-optional) For production deployments, we recommend setting up monitoring with Prometheus and Grafana. You can use our docker-compose setup: ``` # Clone the repo if you haven't git clone https://github.com/erpc/erpc.git cd erpc # Start the monitoring stack docker-compose up -d ``` See the [monitoring guide](https://docs.erpc.cloud/operation/monitoring) for more details on metrics and dashboards. ## Docker compose [Permalink for this section](https://docs.erpc.cloud/deployment/docker\#docker-compose) For production deployments, you might want to use docker-compose to manage eRPC along with its monitoring stack. Here's a basic example: ``` version: '3.8' services: erpc: image: ghcr.io/erpc/erpc:latest ports: - "4000:4000" - "4001:4001" volumes: - ./erpc.yaml:/erpc.yaml restart: unless-stopped ``` ## Installing custom NPM modules [Permalink for this section](https://docs.erpc.cloud/deployment/docker\#installing-custom-npm-modules) When using TypeScript configuration with additional NPM dependencies beyond `@erpc-cloud/config`, you'll need to make these dependencies available inside the Docker container. There are two approaches to achieve this: ### Option 1: Building a custom image [Permalink for this section](https://docs.erpc.cloud/deployment/docker\#option-1-building-a-custom-image) Create a custom Dockerfile that includes your dependencies: ``` FROM debian:12 COPY package.json pnpm-lock.yaml / # COPY package.json package-lock.json / # For npm # COPY package.json yarn.lock / # For yarn RUN pnpm install # RUN npm install # For npm # RUN yarn install # For yarn FROM ghcr.io/erpc/erpc:latest COPY --from=0 /node_modules /node_modules ``` Build and run your custom image: ``` docker build -t erpc-custom -f Dockerfile.custom . docker run -v $(pwd)/erpc.ts:/erpc.ts \ -p 4000:4000 -p 4001:4001 \ erpc-custom ``` ### Option 2: Mounting host dependencies [Permalink for this section](https://docs.erpc.cloud/deployment/docker\#option-2-mounting-host-dependencies) Alternatively, you can mount your local `package.json` and `node_modules` directly: ``` docker run \ -v $(pwd)/package.json:/package.json \ -v $(pwd)/node_modules:/node_modules \ -v $(pwd)/erpc.ts:/erpc.ts \ -p 4000:4000 -p 4001:4001 \ ghcr.io/erpc/erpc:latest ``` For docker-compose, add the volumes to your service configuration: ``` version: '3.8' services: erpc: image: ghcr.io/erpc/erpc:latest ports: - "4000:4000" - "4001:4001" volumes: - ./erpc.ts:/erpc.ts - ./package.json:/package.json - ./node_modules:/node_modules restart: unless-stopped ``` If you're only using the `@erpc-cloud/config` package, you don't need these additional steps. The base image already includes this package. [Matcher syntax](https://docs.erpc.cloud/config/matcher "Matcher syntax") [Railway](https://docs.erpc.cloud/deployment/railway "Railway") ## Matcher Syntax Overview [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Config Matcher syntax # Matcher syntax Certain configurations accept a matcher syntax with some basic operations designed for blockchain json-rpc request/response values. Matchers are used in the following configurations: - [Upstream](https://docs.erpc.cloud/config/projects/upstreams)'s allowMethods or ignoreMethods. - [Cache policy](https://docs.erpc.cloud/config/database/evm-json-rpc-cache#cache-policies) patterns for network/method/params. - [Aliasing](https://docs.erpc.cloud/operation/url#domain-aliasing) rules for `Host` header. - [Failsafe](https://docs.erpc.cloud/config/failsafe)`matchMethod` patterns. ## Examples [Permalink for this section](https://docs.erpc.cloud/config/matcher\#examples) ``` // Match one exact method OR any eth_ method arbtrace_transaction | eth_* // Match all methods expect those prefixed with alchemy_* !alchemy_* // Match a number between 1 and 100 >=1 & <=100 // Match some block tags AND any numeric blocks higher than 1000 (also with hex example) (latest | safe | finalized) | >=1000 (latest | safe | finalized) | >=0x4096 // Match only numeric values (e.g. ignore block tags) 0x* ``` ## Operations [Permalink for this section](https://docs.erpc.cloud/config/matcher\#operations) ### Wildcards [Permalink for this section](https://docs.erpc.cloud/config/matcher\#wildcards) - `*` \- matches any number of characters - `` \- matches an empty string ### Logical Operations [Permalink for this section](https://docs.erpc.cloud/config/matcher\#logical-operations) - `|` \- OR operation - `&` \- AND operation - `!` \- NOT operation - `()` \- grouping/nesting operations ### Numeric Comparisons (for hex values) [Permalink for this section](https://docs.erpc.cloud/config/matcher\#numeric-comparisons-for-hex-values) - `>` \- greater than - `<` \- less than - `>=` \- greater than or equal - `<=` \- less than or equal - `=` \- equal to [Rate limiters](https://docs.erpc.cloud/config/rate-limiters "Rate limiters") [Docker](https://docs.erpc.cloud/deployment/docker "Docker") ## Cloud Deployment Options [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Deployment Cloud # Hosted cloud To avoid DevOps overhead, and optimal caching storage costs you can request a hosted cloud solution in your preferred infrastrcutre region. Available regions include but not limited to: - `EU` close to AWS's eu-central-1 or Hetzner's EU region - `US` close to AWS's us-east-1 or Hetzner's US region ### Pricing [Permalink for this section](https://docs.erpc.cloud/deployment/cloud\#pricing) | Feature | Unit cost / month | | --- | --- | | Compute instance(s) | $50 per 2vCPU+4GB RAM | | Cache data storage | $0.3 per 1GB | As an example LiFi project on Arbitrum chain caches 10m transactions and traces, 200m blocks, which results in ~400GB of storage: - 1 x instance = $50 / mo - 400 GB x cached data = $100 / mo Ping our engineers to [bring up a cloud instance (opens in a new tab)](https://t.me/erpc_cloud) in few minutes. [Kubernetes](https://docs.erpc.cloud/deployment/kubernetes "Kubernetes") [URL](https://docs.erpc.cloud/operation/url "URL") ## eRPC Project Configuration [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Config Projects # Projects A single instance of eRPC can be used for various projects, any number of chains, and any number of upstreams. You can have separate `backend`, `indexer` and `frontend` projects, so that you control self-imposed rate-limits, or supported methods. This allows you to decide different **"cost"** vs **"reliability"** strategies for each project. ## Config [Permalink for this section](https://docs.erpc.cloud/config/projects\#config) The `projects:` array is the top-most configuration, and it is required to have at least 1 project. Each project has the following properties: - `id:` a unique identifier used in logs and metrics. - [`rateLimitBudget:`](https://docs.erpc.cloud/config/rate-limiters) a budget for the total number of requests that this project is allowed to serve. - [`networks:`](https://docs.erpc.cloud/config/projects/networks) an array of custom configuration for one or more of the supported networks. - [`networkDefaults:`](https://docs.erpc.cloud/config/projects/networks#config-defaults) default configuration for all networks in this project. - [`upstreams:`](https://docs.erpc.cloud/config/projects/upstreams) an array of all upstreams to use in this project. - [`upstreamDefaults:`](https://docs.erpc.cloud/config/projects/upstreams#config-defaults) default configuration for all upstreams in this project. #### Example [Permalink for this section](https://docs.erpc.cloud/config/projects\#example) Refer to [`erpc.yaml`](https://docs.erpc.cloud/config/example) and "projects" section. [erpc.yaml/ts](https://docs.erpc.cloud/config/example "erpc.yaml/ts") [Networks](https://docs.erpc.cloud/config/projects/networks "Networks") ## eRPC Network Configuration [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Config [Projects](https://docs.erpc.cloud/config/projects) Networks # Networks A network represents a chain (e.g., evm, solana, etc), and it is a logical grouping of upstreams. [Upstreams](https://docs.erpc.cloud/config/projects/upstreams) are configured separately, and on the first request to a network, the eRPC will automatically find any upstream that support that network. ## Config [Permalink for this section](https://docs.erpc.cloud/config/projects/networks\#config) You can optionally configure each network as follows: yamltypescript erpc.yaml ``` projects: - id: main # (OPTIONAL) This array configures network-specific (a.k.a chain-specific) features. # For each network "architecture" and corresponding network id (e.g. evm.chainId) is required. # You don't need to define networks as they will be automatically detected from configured endpoints (lazy-loaded). # Only provide network list if you want to customize features such as failsafe policies, rate limit budget, finality depth, etc. networks: - architecture: evm # (OPTIONAL) When "evm" is used, "chainId" is required, so that rate limit budget or failsafe policies are properly applied. evm: # (REQUIRED) chainId is required when "evm" architecture is used. chainId: 1 # (OPTIONAL) fallbackFinalityDepth is optional and allows to manually set a finality depth in case upstream does not support eth_getBlockByNumber(finalized). # In case this fallback is used, finalized block will be 'LatestBlock - fallbackFinalityDepth'. # Defining this fallback helps with increasing cache-hit rate and reducing redundant 'retry' attempts on empty responses, as we know which data is finalized. # DEFAULT: auto-detect - via eth_getBlockByNumber(finalized). fallbackFinalityDepth: 1024 # (OPTIONAL) Enable idempotent transaction broadcasting for eth_sendRawTransaction. # When true (default), duplicate transaction errors are converted to success responses, # allowing safe use of retry/hedge policies with transaction sending. idempotentTransactionBroadcast: true # (OPTIONAL) A friendly alias for this network. This allows you to reference the network using the alias # instead of the architecture/chainId format. For example, instead of using /main/evm/1, you can use /main/ethereum. # The alias must contain only alphanumeric characters, dash, or underscore. alias: ethereum # (OPTIONAL) Refer to "Selection Policy" section for more details. # Here are default values used for selectionPolicy if not explicitly defined: selectionPolicy: # Every 1 minute evaluate which upstreams must be included, # based on the arbitrary logic (e.g., <90% error rate and <10 block lag): evalInterval: 1m # To isolate selection evaluation and result to each "method" separately change this flag to true evalPerMethod: false # Freeform TypeScript-based logic to select upstreams to be included by returning them: evalFunction: | (upstreams, method) => { const defaults = upstreams.filter(u => u.config.group !== 'fallback') const fallbacks = upstreams.filter(u => u.config.group === 'fallback') // Maximum allowed error rate. const maxErrorRate = parseFloat(process.env.ROUTING_POLICY_MAX_ERROR_RATE || '0.7') // Maximum allowed block head lag. const maxBlockHeadLag = parseFloat(process.env.ROUTING_POLICY_MAX_BLOCK_HEAD_LAG || '10') // Minimum number of healthy upstreams that must be included in default group. const minHealthyThreshold = parseInt(process.env.ROUTING_POLICY_MIN_HEALTHY_THRESHOLD || '1') // Filter upstreams that are healthy based on error rate and block head lag. const healthyOnes = defaults.filter( u => u.metrics.errorRate < maxErrorRate && u.metrics.blockHeadLag < maxBlockHeadLag ) // If there are enough healthy upstreams, return them. if (healthyOnes.length >= minHealthyThreshold) { return healthyOnes } // If there are fallbacks defined, try to use them if (fallbacks.length > 0) { // Apply same health filtering as default rpcs let healthyFallbacks = fallbacks.filter( u => u.metrics.errorRate < maxErrorRate && u.metrics.blockHeadLag < maxBlockHeadLag ) // If there are healthy fallbacks use them if (healthyFallbacks.length > 0) { return healthyFallbacks } } // The reason all upstreams are returned is to be less harsh and still consider default nodes (in case they have intermittent issues) // Order of upstreams does not matter as that will be decided by the upstream scoring mechanism return upstreams } # When an upstream is excluded, you can give it a chance on a regular basis # to handle a certain number of sample requests again, so that metrics are refreshed. # For example, to see if error rate is improving after 5 minutes, or still too high. # This is conceptually similar to how a circuit-breaker works in a "half-open" state. # Resampling is not always needed because the "evm state poller" component will still make # requests for the "latest" block, which still updates errorRate. resampleExcluded: false resampleInterval: 5m resampleCount: 10 # (OPTIONAL) A network-level rate limit budget applied to all requests despite upstreams own rate-limits. # For example even if upstreams can handle 1000 RPS, and network-level is limited to 100 RPS, # the request will be rate-limited to 100 RPS. rateLimitBudget: my-limiter-budget # (OPTIONAL) Refer to "Failsafe" section for more details. # Here are default values used for networks if not explicitly defined: failsafe: timeout: # On network-level "timeout" is applied for the whole lifecycle of the request (including however many retries happens on upstream) duration: 30s retry: # It is recommended to set a retry policy on network-level to make sure if one upstream is rate-limited, # the request will be retried on another upstream. Most often you don't need to set a delay. maxAttempts: 3 delay: 0ms # Defining a "hedge" is highly-recommended on network-level because if upstream A is being slow for # a specific request, it can start a new parallel hedged request to upstream B, for whichever responds faster. hedge: delay: 200ms maxCount: 3 upstreams: # Refer to "Upstreams" section to learn how to configure upstreams. # ... # ... ``` ### Defaults and lazy-loading [Permalink for this section](https://docs.erpc.cloud/config/projects/networks\#defaults-and-lazy-loading) Networks are lazy-loaded on first request for a network (if not explicitly defined in config). You can configure "networkDefaults" to set default values for all networks (both static or lazy-loaded): yaml erpc.yaml ``` projects: - id: main networkDefaults: # (OPTIONAL) A network-level rate limit budget applied to all requests despite upstreams own rate-limits. # For example even if upstreams can handle 1000 RPS, and network-level is limited to 100 RPS, # the request will be rate-limited to 100 RPS. # Defaults to no rate limit budget. rateLimitBudget: "my-default-budget" # (OPTIONAL) Refer to "Failsafe" section for more details. # https://docs.erpc.cloud/config/failsafe # If a network has its own failsafe defined, it will not take any of policies from networkDefaults. # i.e. if network has only "timeout" policy, it will NOT get hedge/retry from networkDefaults (those will be disabled). failsafe: timeout: duration: "30s" hedge: delay: "200ms" maxCount: 3 retry: maxAttempts: 3 delay: "0ms" # (OPTIONAL) Refer to "Selection Policy" section for more details about default values. # https://docs.erpc.cloud/config/projects/selection-policies#config selectionPolicy: #... # (OPTIONAL) Default directives to apply to all requests for this network. # These can be overridden by request-specific directives via HTTP headers or query parameters. # See https://docs.erpc.cloud/operation/directives for more details about each directive. directiveDefaults: retryEmpty: true # OPTIONAL (default: true) retryPending: false # OPTIONAL (default: false) skipCacheRead: false # OPTIONAL (default: false) useUpstream: "alchemy-*|localnode-*" # OPTIONAL (default: *) # (OPTIONAL) List of customizations per network if needed can be defined as usual: # For each static network, first networkDefaults will be applied (deep object merge), # then network-specific overrides can be applied. networks: # ... ``` If a network has its own `failsafe:` defined, it will not take any of policies from networkDefaults. e.g. if a network only has "timeout" policy, it will **NOT** get hedge/retry from networkDefaults (those will be disabled). ## `evm` Networks [Permalink for this section](https://docs.erpc.cloud/config/projects/networks\#evm-networks) This type of network are generic EVM-based chains that support JSON-RPC protocol. ### Integrity Configuration [Permalink for this section](https://docs.erpc.cloud/config/projects/networks\#integrity-configuration) yamltypescript erpc.yaml ``` projects: - id: main networks: - architecture: evm evm: chainId: 1 integrity: # Track highest block across upstreams for "latest" and "finalized" tags enforceHighestBlock: true # default: true # Validate eth_getLogs block range availability on upstreams enforceGetLogsBlockRange: true # default: true # Convert null responses to errors for eth_getBlockByNumber tagged blocks ("pending", "latest", etc.) # Numeric blocks (0x1234) always error when null regardless of this setting # Set to false to allow null responses for eth_getBlockByNumber tagged blocks (e.g. for zkSync) enforceNonNullTaggedBlocks: true # default: true ``` ### `eth_getLogs` [Permalink for this section](https://docs.erpc.cloud/config/projects/networks\#eth_getlogs) Network-level controls manage validation, proactive splitting, and error-driven splitting for eth\_getLogs. Requests may be split into smaller sub-requests and merged transparently. - **Validation & availability**: `integrity.enforceGetLogsBlockRange` validates range and asserts the upstream has data for `fromBlock..toBlock`. - **Hard limits**: `getLogsMaxAllowedRange`, `getLogsMaxAllowedAddresses`, `getLogsMaxAllowedTopics` reject oversized requests early. - **Proactive splitting**: If requested range exceeds an effective threshold, the network splits the request into contiguous ranges. The effective threshold is the minimum positive `upstream.evm.getLogsAutoSplittingRangeThreshold` across selected upstreams, capped by `getLogsMaxAllowedRange`. - **Split on error**: If an upstream complains about large requests (including provider-specific 413-like errors), the network retries by splitting (first range, then addresses, then topics\[0\] OR-list) and merges results. - **Concurrency**: `getLogsSplitConcurrency` limits parallel sub-requests during splitting. Relationship to upstream config: - Upstream-level `evm.getLogsAutoSplittingRangeThreshold` is a hint used by the network to compute the effective proactive split size. All other getLogs controls are defined at the network level. yamltypescript erpc.yaml ``` projects: - id: main networks: - architecture: evm evm: chainId: 1 # Validate requested block range and ensure upstream has data for both ends. # Enabled by default; set to false to skip availability checks. integrity: enforceGetLogsBlockRange: true # Hard limits that reject the request up front (413-style errors): getLogsMaxAllowedRange: 10000 # Max number of blocks (inclusive) getLogsMaxAllowedAddresses: 10000 # Max length when 'address' is an array getLogsMaxAllowedTopics: 10000 # Max OR-count when topics[0] is an array # When providers return "too many results"/large-range errors, split and retry automatically. getLogsSplitOnError: true # Parallelism for split sub-requests (applies to proactive and error-driven splits). getLogsSplitConcurrency: 16 # Upstream hint used to compute proactive split size (network takes the min positive across selected upstreams) upstreams: - id: my-upstream endpoint: https://mainnet.example.com evm: # 0 or negative disables hint for this upstream getLogsAutoSplittingRangeThreshold: 5000 ``` Splitting preserves order and merges results server-side. Address count is the length of the `address` array (if present). Topic count considers only `topics[0]` when it is an OR-list. ### `eth_sendRawTransaction` [Permalink for this section](https://docs.erpc.cloud/config/projects/networks\#eth_sendrawtransaction) eRPC provides **idempotent transaction broadcasting** for `eth_sendRawTransaction`, enabling safe use of retry and hedge policies with transaction sending. **How it works:** - When an upstream returns "already known" or similar duplicate transaction errors, eRPC converts it to a success response with the transaction hash - For "nonce too low" errors, eRPC verifies if the exact transaction exists on-chain before returning success - This allows failsafe policies (retry, hedge) to work safely—if a transaction is broadcast to multiple upstreams or retried, duplicate errors are handled gracefully **Enabled by default.** To disable: yamltypescript erpc.yaml ``` projects: - id: main networks: - architecture: evm evm: chainId: 1 # Disable idempotent transaction broadcast (default: true) idempotentTransactionBroadcast: false ``` When enabled, `eth_sendRawTransaction` can safely use retry and hedge policies. The transaction hash is deterministically computed from the signed transaction, so duplicate detection works across any upstream. ### `eth_getTransactionCount` [Permalink for this section](https://docs.erpc.cloud/config/projects/networks\#eth_gettransactioncount) When querying nonce values across multiple upstreams (e.g., using consensus), you may want to return the **highest** nonce rather than the most common one. This prevents issues where stale nonces from lagging nodes cause transaction failures. Use `preferHighestValueFor` in the consensus policy to return the highest numeric value: yamltypescript erpc.yaml ``` projects: - id: main networks: - architecture: evm evm: chainId: 1 failsafe: - matchMethod: eth_getTransactionCount consensus: maxParticipants: 3 # Query 3 upstreams in parallel agreementThreshold: 1 # Return highest nonce (typically the most recent) preferHighestValueFor: eth_getTransactionCount: - result # Compare the direct result value (hex nonce) ``` The `preferHighestValueFor` map supports: - **Direct result**: Use `"result"` for methods returning a simple value (like `eth_getTransactionCount` returning `"0x5"`) - **Nested fields**: Use field names for object results (e.g., `["nonce", "blockNumber"]` for `eth_getTransactionByHash`) - **Tie-breakers**: Multiple fields are compared in order—first field is primary, subsequent fields break ties ⚠️ **How `maxParticipants` and `agreementThreshold` behave:** When `preferHighestValueFor` is configured for a method: - **`maxParticipants`**: All configured upstreams are queried in parallel. Set this to the number of upstreams you want to compare (recommended: 2-3). - **`agreementThreshold`**: Minimum number of upstreams that must agree on a value for it to qualify. Among qualifying values, the highest wins. **Recommendation:** Use `agreementThreshold: 1`. The highest nonce typically represents the most recently mined transaction, which is the correct value to use. Lagging nodes may return stale (lower) nonces, and requiring agreement would incorrectly prefer the stale value. Only use `agreementThreshold: 2` or higher if you have specific concerns about compromised upstreams returning artificially high nonces. When `preferHighestValueFor` is configured for a method, it takes precedence over normal hash-based consensus. Error responses are ignored; only valid numeric responses are compared. ## Name aliasing [Permalink for this section](https://docs.erpc.cloud/config/projects/networks\#name-aliasing) You can define friendly aliases for your networks instead of the /architecture/chainId format. For example, instead of using `/main/evm/1`, you can use `/main/ethereum`: ``` networks: - architecture: evm evm: chainId: 1 alias: ethereum - architecture: evm evm: chainId: 42161 alias: arbitrum - architecture: evm evm: chainId: 137 alias: polygon ``` ``` POST http://localhost:4000/main/ethereum POST http://localhost:4000/main/arbitrum POST http://localhost:4000/main/polygon ``` - Aliases are only applicable to statically defined networks in your configuration. The alias must contain only alphanumeric characters, dash, or underscore. [Projects](https://docs.erpc.cloud/config/projects "Projects") [Upstreams](https://docs.erpc.cloud/config/projects/upstreams "Upstreams") ## Consensus Configuration Overview [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) # Consensus The `consensus` policy sends the same request to multiple upstreams and returns the result only when enough of them agree. This improves correctness, detects misbehaving nodes, and provides deterministic behavior during faults. ⚠️ Consensus can only be configured at **network level** since it requires multiple upstreams to compare results. ## Configuration [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#configuration) yamltypescript erpc.yaml ``` projects: - id: main networks: - architecture: evm evm: chainId: 42161 failsafe: - matchMethod: "*" # Configure per-method thresholds if needed consensus: maxParticipants: 4 agreementThreshold: 2 disputeBehavior: returnError # acceptMostCommonValidResult | preferBlockHeadLeader | onlyBlockHeadLeader lowParticipantsBehavior: acceptMostCommonValidResult # returnError | preferBlockHeadLeader | onlyBlockHeadLeader preferNonEmpty: true preferLargerResponses: true ignoreFields: eth_getBlockByNumber: ["timestamp"] misbehaviorsDestination: type: file # 'file' | 's3' path: /var/log/erpc/misbehaviors # absolute directory for file, or s3://bucket/prefix for S3 filePattern: "{timestampMs}-{method}-{networkId}" # default for file; S3 default adds -{instanceId} # s3: # region: us-west-2 # maxRecords: 100 # maxSize: 1048576 # 1MB # flushInterval: 60s # contentType: application/jsonl # credentials: # mode: env # 'env' | 'file' | 'secret' # # for mode 'file': # # credentialsFile: ~/.aws/credentials # # profile: default # # for mode 'secret': # # accessKeyID: AKIA... # # secretAccessKey: ... punishMisbehavior: # (optional) To exclude bad upstreams from consensus for a while disputeThreshold: 10 disputeWindow: 10m sitOutPenalty: 30m ``` #### A real-world example of `ignoreFields` [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#a-real-world-example-of-ignorefields) The following fields are often safe to ignore, but you should verify them for your specific use case: ``` ignoreFields: eth_getLogs: - "*.blockTimestamp" eth_getTransactionReceipt: - "blockTimestamp" - "logs.*.blockTimestamp" - "l1Fee" - "l1GasPrice" - "l1GasUsed" - "gasUsedForL1" - "timeboosted" - "l1BlockNumber" eth_getBlockByHash: - "requestsHash" - "transactions.*.gasPrice" - "transactions.*.accessList" - "transactions.*.chainId" - "transactions.*.l1Fee" - "transactions.*.yParity" - "transactions.*.isSystemTx" - "transactions.*.depositReceiptVersion" eth_getBlockByNumber: - "requestsHash" - "transactions.*.gasPrice" - "transactions.*.accessList" - "transactions.*.chainId" - "transactions.*.l1Fee" - "transactions.*.yParity" - "transactions.*.isSystemTx" - "transactions.*.depositReceiptVersion" eth_getBlockReceipts: - "*.blockTimestamp" - "*.l1Fee" - "*.l1GasPrice" - "*.l1GasUsed" - "*.logs.*.blockTimestamp" ``` ## Key options [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#key-options) ### `maxParticipants` [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#maxparticipants) Number of upstreams to query in each consensus round. The policy selects the first N healthy upstreams based on their scores. ### `agreementThreshold` [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#agreementthreshold) Minimum number of identical responses needed to reach consensus. For example, with `maxParticipants: 3` and `agreementThreshold: 2`, at least 2 upstreams must return the same result. ### disputeBehavior [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#disputebehavior) When upstreams disagree (no group meets threshold): - `acceptMostCommonValidResult`: Use preferences and select the best valid result among groups that meet threshold. If none meet threshold, returns dispute. - `returnError`: Always return a dispute error in disagreement scenarios. - `preferBlockHeadLeader`: If the block head leader has a non-error result, return it; otherwise fall back to `acceptMostCommonValidResult` logic. - `onlyBlockHeadLeader`: Return the leader’s non-error result if available; otherwise dispute. ## Behavior options [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#behavior-options) ### preferNonEmpty [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#prefernonempty) Prioritize meaningful data over empty, and empty over errors. Applies with `acceptMostCommonValidResult`: - Above threshold: If both a non-empty and a consensus-valid error group meet threshold, pick the best non-empty (by count, then size). - Below threshold: With exactly one non-empty and at least one empty, pick the non-empty. - Prevents short-circuiting to empty/consensus-error when a non-empty may still arrive. ### preferLargerResponses [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#preferlargerresponses) Prefer larger non-empty results: - Below threshold (AcceptMostCommon): choose the largest non-empty. - Above threshold with multiple valid groups: choose the largest non-empty. - If a smaller non-empty meets threshold but a larger non-empty exists: - `acceptMostCommonValidResult`: choose the largest - `returnError`: dispute (don’t accept the smaller) ### ignoreFields [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#ignorefields) Per-method fields ignored when computing canonical hashes (useful for timestamps etc.). ### lowParticipantsBehavior [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#lowparticipantsbehavior) When fewer than `agreementThreshold` valid responses are available: - `acceptMostCommonValidResult`: Apply preferences to pick a valid result; still respects threshold semantics. - `returnError`: Return a low-participants error. - `preferBlockHeadLeader`: If the block head leader has a non-error result, return it; otherwise fall back to `acceptMostCommonValidResult`. - `onlyBlockHeadLeader`: If the leader has a non-error result, return it; if the leader only has an error, return that error; otherwise return a low-participants error. **Block Head Leader**: The upstream reporting the highest block number. This is determined by each upstream's state poller and ensures you're getting data from the most synchronized node. ## How it works [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#how-it-works) 1. Send the request to up to `maxParticipants` (if less upstreams it continues with the available ones); group identical results/errors. 2. If any valid group meets `agreementThreshold`, it wins 3. If no winner, apply behaviors: - Low participants → `lowParticipantsBehavior` - Otherwise → `disputeBehavior` 4. Preferences (non-empty, larger responses) may override selection in specific contexts (see above). 5. Ties without preferences → dispute. 6. All upstreams return identical error → return that error; otherwise return low-participants error. ## Misbehavior tracking [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#misbehavior-tracking) ### `punishMisbehavior` [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#punishmisbehavior) Temporarily removes upstreams that consistently disagree with the consensus: - **`disputeThreshold`**: Number of disputes before punishment (e.g., 3 strikes) - **`disputeWindow`**: Time window for counting disputes (e.g., 10m) - **`sitOutPenalty`**: How long the upstream is cordoned (e.g., 30m) ### `misbehaviorsDestination` [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#misbehaviorsdestination) Append full misbehavior events (JSONL) to a destination. Each line contains the full JSON-RPC request, all participant responses or errors, the analysis summary, the winner, and the policy snapshot. No truncation is applied. - **type**: `file` \| `s3` - **path**: - For `file`: absolute directory path; files are created using `filePattern`. - For `s3`: `s3://bucket/prefix` where files are uploaded using `filePattern`. - **filePattern**placeholders: - `{dateByHour}`: UTC hour (`YYYY-MM-DD-HH`) - `{dateByDay}`: UTC day (`YYYY-MM-DD`) - `{method}`: JSON-RPC method - `{networkId}`: network id with `:` replaced by `_` - `{instanceId}`: unique instance ID (auto from env/pod/hostname or generated) - `{timestampMs}`: UTC timestamp in milliseconds (useful to avoid key collisions on S3) - Defaults: `{timestampMs}-{method}-{networkId}.jsonl` - **s3** (when type=`s3`): - `region`, `maxRecords`, `maxSize` (bytes), `flushInterval`, `contentType` - `credentials.mode`: `env` \| `file` \| `secret` (\+ required fields per mode) Notes: - File writes use atomic append; use external rotation for large files. - S3 uploads are buffered and flushed by size, count, or time. ## Performance [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#performance) Consensus increases costs and latency since it waits for multiple responses. Use it selectively for critical workloads and specific methods rather than all requests. ## Full flow diagram [Permalink for this section](https://docs.erpc.cloud/config/failsafe/consensus\#full-flow-diagram) No Yes Yes OnlyBlockHeadLeader Yes No Yes No AcceptMostCommon Yes No Yes No Yes No ReturnError No No Yes Yes No Yes No No Yes Yes No No Yes Yes Yes Yes No No No Yes No No Yes Yes No No Yes Yes No No Yes No Yes AcceptMostCommon ReturnError No Yes No Yes ConsensusError NonEmpty or Empty No Yes Yes No Start: collected responses Any responses? Error: LowParticipants - no responses available Group responses and compute counts, sizes, validParticipants Low participants? LowParticipantsBehavior OnlyBHL - low participants Leader non-error exists? Return leader result Leader has only error? Return leader error Error: LowParticipants - not enough participants LowParticipants + AcceptMostCommon Any NonEmpty exists? Return best NonEmpty by count then size Any Empty exists? Return best Empty Any ConsensusError exists? Return best ConsensusError Error: LowParticipants Error: LowParticipants Dispute/normal path DisputeBehavior == PreferBlockHeadLeader Proceed Any valid group meets threshold? Leader non-error exists? Return leader result PreferLargerResponses AND DisputeBehavior==AcceptMostCommon Proceed Best count < threshold? Return largest NonEmpty PreferNonEmpty AND AcceptMostCommon context Proceed Best-by-count >= threshold? Best type is Empty or ConsensusError? Any NonEmpty exists? Return best NonEmpty Below threshold: one NonEmpty and some Empty? Return best NonEmpty No preference active Proceed Tie at/above threshold among non-error groups? Error: Dispute PreferLargerResponses Proceed >1 valid group >= threshold? Return largest NonEmpty PreferLargerResponses Proceed Best-by-count is NonEmpty >= threshold AND larger NonEmpty exists? DisputeBehavior Return largest NonEmpty Error: Dispute Any valid group meets threshold? Multiple valid groups below threshold? Error: Dispute Proceed Winner type Return error Return result validParticipants == 0 Fallback Best is InfraError AND meets threshold? Return infra error Error: LowParticipants Short-circuit: ConsensusError >= threshold -> return error unless PreferNonEmpty+AcceptMostCommon Short-circuit: NonEmpty winner unassailable lead -> return result [Why eRPC?](https://docs.erpc.cloud/why "Why eRPC?") ## EVM Providers Overview [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Config [Projects](https://docs.erpc.cloud/config/projects) Providers ## Providers [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#providers) Providers make it easy to add well-known third-parties RPC endpoints quickly. Here are the supported providers: - [`repository`](https://docs.erpc.cloud/config/projects/providers#repository) A special provider to automatically add "public" RPC endpoints for 2,000+ EVM chains. - [`erpc`](https://docs.erpc.cloud/config/projects/providers#erpc) Accepts erpc.cloud endpoint and automatically adds all their EVM chains. - [`alchemy`](https://docs.erpc.cloud/config/projects/providers#alchemy) Accepts alchemy.com api key and automatically adds all their EVM chains. - [`drpc`](https://docs.erpc.cloud/config/projects/providers#drpc) Accepts drpc.org api key and automatically adds all their EVM chains. - [`blastapi`](https://docs.erpc.cloud/config/projects/providers#blastapi) Accepts blastapi.io api key and automatically adds all their EVM chains. - [`thirdweb`](https://docs.erpc.cloud/config/projects/providers#thirdweb) Accepts thirdweb.com client-id and automatically adds all their EVM chains. - [`infura`](https://docs.erpc.cloud/config/projects/providers#infura) Accepts infura.io api key and automatically adds all their EVM chains. - [`envio`](https://docs.erpc.cloud/config/projects/providers#envio) Accepts envio.dev rpc endpoint and automatically adds all chains by HyperRPC. - [`pimlico`](https://docs.erpc.cloud/config/projects/providers#pimlico) Accepts pimlico.io rpc endpoint for account-abstraction (ERC-4337) support. - [`etherspot`](https://docs.erpc.cloud/config/projects/providers#etherspot) Accepts etherspot.io rpc endpoint for account-abstraction (ERC-4337) support. - [`dwellir`](https://docs.erpc.cloud/config/projects/providers#dwellir) Accepts dwellir.com api key and automatically adds all their EVM chains. - [`conduit`](https://docs.erpc.cloud/config/projects/providers#conduit) Accepts conduit.xyz api key and automatically adds all their EVM chains. - [`superchain`](https://docs.erpc.cloud/config/projects/providers#superchain) Accepts [superchain registry (opens in a new tab)](https://github.com/ethereum-optimism/superchain-registry/blob/main/chainList.json) and automatically adds all chains from it. - [`chainstack`](https://docs.erpc.cloud/config/projects/providers#chainstack) Accepts chainstack.com api key and automatically adds all their EVM chains. - [`onfinality`](https://docs.erpc.cloud/config/projects/providers#onfinality) Accepts onfinality.io api key and automatically adds all their EVM chains. - [`tenderly`](https://docs.erpc.cloud/config/projects/providers#tenderly) Accepts tenderly.co api key and automatically adds all their EVM chains. - [`blockpi`](https://docs.erpc.cloud/config/projects/providers#blockpi) Accepts blockpi.io api key and automatically adds all their EVM chains. - [`ankr`](https://docs.erpc.cloud/config/projects/providers#ankr) Accepts ankr.com api key and automatically adds all their EVM chains. - [`quicknode`](https://docs.erpc.cloud/config/projects/providers#quicknode) Accepts quicknode.com api key and automatically adds all their EVM chains. - [`routemesh`](https://docs.erpc.cloud/config/projects/providers#routemesh) Accepts routemesh.io api key and automatically adds all their EVM chains. eRPC supports **any EVM-compatible** JSON-RPC endpoint when using [`evm` type](https://docs.erpc.cloud/config/projects/upstreams). ## Simple endpoints [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#simple-endpoints) #### `repository` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#repository) This special provider read a remote repository (a simple JSON file) that contains a list of RPC endpoints for any EVM chain. This allows automatic and lazy-loading of EVM chains on "first request": ⚠️ eRPC design aims to be robust towards any number of endpoints in terms of failures or response times, but it is recommended to test before you use this provider in production. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: repository://evm-public-endpoints.erpc.cloud ``` eRPC team regularly updates an IPFS file containing 4,000+ public endpoints from [chainlist.org (opens in a new tab)](https://chainlist.org/), [chainid.network (opens in a new tab)](https://chainid.network/) and [viem library (opens in a new tab)](https://viem.sh/), which is pointed to by [https://evm-public-endpoints.erpc.cloud (opens in a new tab)](https://evm-public-endpoints.erpc.cloud/) domain. #### `alchemy` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#alchemy) Built for [Alchemy (opens in a new tab)](https://alchemy.com/) 3rd-party provider to make it easier to import "all supported evm chains" with just an API-KEY. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: alchemy://YOUR_ALCHEMY_API_KEY # ... ``` #### `drpc` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#drpc) Built for [dRPC (opens in a new tab)](https://drpc.org/) 3rd-party provider to make it easier to import "all supported evm chains" with just an API-KEY. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: drpc://YOUR_DRPC_API_KEY # ... ``` #### `erpc` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#erpc) Built for [eRPC Cloud (opens in a new tab)](https://erpc.cloud/) endpoints to make it easier to connect to eRPC-hosted RPC services. You don't have to pass chainId as that will be automatically detected based on the request you send. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: # With project and architecture specified - endpoint: erpc://xxx.aws.erpc.cloud/project/evm # With authentication secret (optional) - endpoint: erpc://xxx.aws.erpc.cloud/project/evm?secret=xxxxx ``` #### `blastapi` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#blastapi) Built for [BlastAPI (opens in a new tab)](https://blastapi.io/) 3rd-party provider to make it easier to import "all supported evm chains" with just an API-KEY. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: blastapi://YOUR_BLASTAPI_API_KEY # ... ``` #### `infura` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#infura) Built for [Infura (opens in a new tab)](https://www.infura.io/) 3rd-party provider to make it easier to import "all supported evm chains" with just an API-KEY. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: infura://YOUR_INFURA_API_KEY # ... ``` #### `thirdweb` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#thirdweb) Built for [Thirdweb (opens in a new tab)](https://thirdweb.com/chainlist) 3rd-party provider to make it easier to import "all supported evm chains" with just a CLIENT-ID. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: thirdweb://YOUR_THIRDWEB_CLIENT_ID # ... ``` For production traffic consult with Thirdweb team about the chains you are goin to use and amount of traffic you expect to handle. #### `envio` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#envio) Envio [HyperRPC (opens in a new tab)](https://docs.envio.dev/docs/HyperSync/hyperrpc-supported-networks) service provides a higher-performance alternative for certain read methods. When handling requests if a [method is supported by HyperRPC (opens in a new tab)](https://docs.envio.dev/docs/HyperSync/overview-hyperrpc), then this upstream may be used. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: envio://rpc.hypersync.xyz # ... ``` For indexing use-cases it is recommended to this upstream. This will automatically add all supported EVM chains by HyperRPC. #### `pimlico` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#pimlico) [Pimlico (opens in a new tab)](https://pimlico.io/) adds account-abstraction (ERC-4337) support to your eRPC instance. With this upstream added when a AA-related request arrives it'll be forwarded to Pimlico, which allows you to use the same RPC endpoint for both usual eth\_\* methods along with ERC-4337 methods. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: pimlico://public # Or provide your API-KEY as: # endpoint: pimlico://xxxxxmy-api-key # ... ``` #### `etherspot` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#etherspot) [Etherspot (opens in a new tab)](https://etherspot.io/) adds account-abstraction (ERC-4337) support to your eRPC instance. With this upstream added when a AA-related request arrives it'll be forwarded to Etherspot, which allows you to use the same RPC endpoint for both usual eth\_\* methods along with ERC-4337 methods. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: etherspot://public # Or provide your API-KEY as: # endpoint: etherspot://xxxxxmy-api-key # ... ``` #### `dwellir` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#dwellir) Built for [Dwellir (opens in a new tab)](https://www.dwellir.com/) 3rd-party provider to make it easier to import their supported EVM chains with just an API-KEY. You can obtain an API key by registering at [dashboard.dwellir.com/register (opens in a new tab)](https://dashboard.dwellir.com/register). yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: dwellir://YOUR_DWELLIR_API_KEY # Optional: Limit to specific chains if needed # onlyNetworks: # - evm:1 # Ethereum Mainnet # - evm:137 # Polygon Mainnet # ... ``` #### `conduit` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#conduit) Built for [Conduit (opens in a new tab)](https://conduit.xyz/) rollup platform to make it easier to import all their rollup EVM chains with just an API key. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: conduit://YOUR_CONDUIT_API_KEY # ... ``` #### `superchain` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#superchain) This provider accepts superchain registry json file (e.g [github.com/ethereum-optimism/superchain-registry/main/chainList.json (opens in a new tab)](https://github.com/ethereum-optimism/superchain-registry/blob/main/chainList.json)) and automatically adds all chains from it. **Note**: If you are using a github URL, you can simply use the shorthand of `superchain://github.com/org/repo//chainList.json`. if your url includes `blob`, t will be automatically stripped. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: superchain://github.com/ethereum-optimism/superchain-registry/main/chainList.json # ... ``` #### `tenderly` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#tenderly) Built for [Tenderly (opens in a new tab)](https://tenderly.co/) 3rd-party provider to make it easier to import "all supported evm chains" with just an API key. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: tenderly://YOUR_TENDERLY_API_KEY # ... ``` For production traffic consult with Tenderly team about the chains you are going to use and amount of traffic you expect to handle. #### `chainstack` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#chainstack) Built for [Chainstack (opens in a new tab)](https://chainstack.com/) 3rd-party provider to make it easier to import "all supported evm chains" with just an API key. See also Chainstack docs: [Using eRPC with Chainstack: Quickstart (opens in a new tab)](https://docs.chainstack.com/docs/using-erpc-with-chainstack-quickstart). This key must be created using [Platform API key (opens in a new tab)](https://docs.chainstack.com/reference/platform-api-getting-started) settings page. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: # Simple usage with just API key - endpoint: chainstack://YOUR_CHAINSTACK_PLATFORM_API_KEY # ... # With query parameters for filtering - endpoint: chainstack://YOUR_CHAINSTACK_PLATFORM_API_KEY?project=PROJECT_ID&organization=ORG_ID®ion=us-east-1&provider=aws&type=dedicated # ... ``` Chainstack supports a wide range of EVM-compatible networks. For production traffic, ensure your Chainstack subscription plan supports the expected load and number of networks you plan to use. **Supported filter parameters:** - `project`: Filter by project ID - `organization`: Filter by organization ID - `region`: Filter by region (e.g., `asia-southeast1`, `ap-southeast-1`, `us-west-2`, `us-east-1`, `uksouth`, `eu3`) - `provider`: Filter by cloud provider (e.g., `aws`, `azure`, `gcloud`, `vzo`) - `type`: Filter by node type (e.g., `shared`, `dedicated`) #### `onfinality` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#onfinality) Built for [Onfinality (opens in a new tab)](https://onfinality.io/) 3rd-party provider to make it easier to import "all supported evm chains" with just an API key. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: onfinality://YOUR_ONFINALITY_API_KEY # ... ``` #### `blockpi` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#blockpi) Built for [BlockPi (opens in a new tab)](https://blockpi.io/) 3rd-party provider to make it easier to import "all supported evm chains" with just an API key. You can [contact BlockPi team (opens in a new tab)](https://docs.blockpi.io/supports/contact-us) to get a global API key for all your evm chains. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: blockpi://YOUR_BLOCKPI_API_KEY # ... ``` #### `ankr` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#ankr) Built for [Ankr (opens in a new tab)](https://www.ankr.com/) 3rd-party provider to make it easier to import "all supported evm chains" with just an API key. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: ankr://YOUR_ANKR_API_KEY # ... ``` #### `quicknode` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#quicknode) Built for [QuickNode (opens in a new tab)](https://www.quicknode.com/) 3rd-party provider to make it easier to import "all supported evm chains" with just an API key. You can create an API key from your [QuickNode dashboard (opens in a new tab)](https://dashboard.quicknode.com/api-keys). yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - endpoint: quicknode://YOUR_QUICKNODE_API_KEY # ... ``` #### `routemesh` [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#routemesh) Built for [Routemesh (opens in a new tab)](https://routemesh.io/) 3rd-party provider to make it easier to import "all supported evm chains" with just an API key. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... providers: - vendor: routemesh settings: baseURL: lb.routemes.sh # (optional) Defaults to lb.routemes.sh apiKey: YOUR_ROUTEMESH_API_KEY ``` ## Advanced config [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#advanced-config) You can use dedicated `providers:` config to customize per-network configurations (e.g. different config for Alchemy eth-mainnet vs Alchemy polygon) as follows: yamltypescript erpc.yaml ``` # ... projects: - id: main # ... providers: - id: alchemy-prod # (optional) Unique ID that will be prefixed to the dynamically generated upstream ID vendor: alchemy # (REQUIRED) Defines the provider type settings: # (optional) Provider-specific settings apiKey: xxxxx onlyNetworks: # (optional) If you want to limit the lazy-loaded networks (instead of loading all supported chains) - evm:1 - evm:137 ignoreNetworks: # (optional) If you want to exclude specific networks from this provider - evm:56 - evm:43114 # (optional) If you want to customize the dynamically generated upstream ID upstreamIdTemplate: "-" # (optional) Customize upstream configs for specific networks: # - The key must be a networkId, and it supports matcher syntax (https://docs.erpc.cloud/config/matcher). # - The value is a typical upstream config (https://docs.erpc.cloud/config/projects/upstreams#config). overrides: "evm:1": rateLimitBudget: # ... jsonRpc: # ... ignoreMethods: # ... allowMethods: # ... failsafe: # ... "evm:*": failsafe: # ... "evm:123|evm:10": failsafe: # ... ``` #### Vendor settings reference [Permalink for this section](https://docs.erpc.cloud/config/projects/providers\#vendor-settings-reference) Here is a reference of all the settings you can use for each vendor: erpc.yaml ``` # ... providers: - vendor: alchemy settings: apiKey: xxxxx - vendor: blastapi settings: apiKey: xxxxx - vendor: drpc settings: apiKey: xxxxx - vendor: envio settings: rootDomain: rpc.hypersync.xyz - vendor: erpc settings: endpoint: xxx.aws.erpc.cloud/project/evm secret: xxxxx # Optional authentication secret - vendor: etherspot settings: apiKey: xxxxx - vendor: infura settings: apiKey: xxxxx - vendor: llama settings: apiKey: xxxxx - vendor: pimlico settings: apiKey: xxxxx # can be "public" or your API-KEY - vendor: thirdweb settings: clientId: xxxxx - vendor: repository settings: repositoryUrl: https://evm-public-endpoints.erpc.cloud recheckInterval: 1h # (optional) How often to recheck the repository for newly added RPC endpoints (default: 1h) - vendor: dwellir settings: apiKey: xxxxx - vendor: conduit settings: apiKey: xxxxx networksUrl: https://api.conduit.xyz/public/network/all # (optional) Endpoint to fetch all supported networks recheckInterval: 24h # (optional) How often to recheck the API for newly added networks (default: 24h) - vendor: superchain settings: registryUrl: "github.com/ethereum-optimism/superchain-registry/main/chainList.json" recheckInterval: 24h # (optional) How often to recheck the registry for newly added chains (default: 24h) - vendor: tenderly settings: apiKey: xxxxx # Your Tenderly API key - vendor: chainstack settings: apiKey: xxxxx # Your Chainstack API key recheckInterval: 1h # (optional) How often to recheck the API for newly added networks (default: 1h) project: xxxxx # (optional) Filter by project ID organization: xxxxx # (optional) Filter by organization ID region: us-east-1 # (optional) Filter by region (asia-southeast1, ap-southeast-1, us-west-2, us-east-1, uksouth, eu3) provider: aws # (optional) Filter by cloud provider (aws, azure, gcloud, vzo) type: dedicated # (optional) Filter by node type (shared, dedicated) - vendor: onfinality settings: apiKey: xxxxx # Your OnFinality API key - vendor: blockpi settings: apiKey: xxxxx # Your BlockPi API key - vendor: ankr settings: apiKey: xxxxx # Your Ankr API key - vendor: quicknode settings: apiKey: xxxxx # Your QuickNode API key recheckInterval: 1h # (optional) How often to recheck the API for newly added networks (default: 1h) - vendor: routemesh settings: baseURL: lb.routemes.sh # (optional) Defaults to lb.routemes.sh apiKey: xxxxx # Your Routemesh API key ``` [Upstreams](https://docs.erpc.cloud/config/projects/upstreams "Upstreams") [Selection policies](https://docs.erpc.cloud/config/projects/selection-policies "Selection policies") ## Rate Limiters Configuration [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Config Rate limiters # Rate limiters Use self-imposed rate limits to protect upstreams and your infrastructure. Define one or more "budgets" and assign them to project, network, upstream, or via authentication (per-user) overrides. Budgets are evaluated locally (in-process) using Envoy's ratelimit algorithm with either a Redis-backed shared store or a local memory store. ### Config [Permalink for this section](https://docs.erpc.cloud/config/rate-limiters\#config) yamltypescript erpc.yaml ``` # ... projects: - id: main # ... # A project can have a budget that applies to all requests (any network or upstream) # Useful to prevent a project (e.g. frontend, or indexer) to send too much requests. rateLimitBudget: frontend # ... # Each upstream can have its own budget upstreams: - id: blastapi-chain-42161 type: evm endpoint: https://arbitrum-one.blastapi.io/xxxxxxx-xxxxxx-xxxxxxx rateLimitBudget: global-blast # ... - id: blastapi-chain-1 type: evm endpoint: https://eth-mainnet.blastapi.io/xxxxxxx-xxxxxx-xxxxxxx rateLimitBudget: global-blast # ... - id: quiknode-chain-42161 type: evm endpoint: https://xxxxxx-xxxxxx.arbitrum-mainnet.quiknode.pro/xxxxxxxxxxxxxxxxxxxxxxxx/ rateLimitBudget: global-quicknode # ... # Rate limiter allows you to create "shared" budgets for upstreams. # For example upstream A and B can use the same budget, which means both of them together must not exceed the defined limits. rateLimiters: # Store is REQUIRED. Choose between redis (distributed) or memory (local-only) store: driver: redis # "redis" | "memory" redis: # required when driver=redis uri: redis://localhost:6379 username: "" # optional # tls, pool, etc are supported via standard redis connector fields budgets: - id: frontend rules: - method: 'eth_trace*' # narrowest rule on top maxCount: 5 period: second perIP: true - method: '*' # wildcard supported; checked per request method maxCount: 20 # allowed count per period period: second # one of: second, minute, hour, day, week, month, year perIP: true - id: global-blast rules: # You can limit which methods apply to this rule e.g. eth_getLogs or eth_* or * (all methods). - method: '*' maxCount: 1000 period: second - method: '*' maxCount: 5000000 period: day - id: global-quicknode rules: - method: '*' maxCount: 300 period: second - method: '*' maxCount: 1000000 period: day ``` ## Auto-tuner [Permalink for this section](https://docs.erpc.cloud/config/rate-limiters\#auto-tuner) The auto-tuner feature allows dynamic adjustment of rate limits based on the upstream's performance. It's particularly useful in the following scenarios: 1. When you're unsure about the actual RPS limit imposed by the provider. 2. When you need to update the limits dynamically based on the provider's current capacity. The auto-tuner is enabled by default when an upstream has any rate limit budget defined. Here's an example configuration with explanations: yamltypescript ``` upstreams: - id: example-upstream type: evm endpoint: https://example-endpoint.com rateLimitBudget: example-budget rateLimitAutoTune: enabled: true # Enable auto-tuning (default: true) adjustmentPeriod: "1m" # How often to adjust the rate limit (default: "1m") errorRateThreshold: 0.1 # Maximum acceptable error rate (default: 0.1) increaseFactor: 1.05 # Factor to increase the limit by (default: 1.05) decreaseFactor: 0.9 # Factor to decrease the limit by (default: 0.9) minBudget: 1 # Minimum rate limit (default: 0) maxBudget: 10000 # Maximum rate limit (default: 10000) ``` It's recommended to set `minBudget` to at least 1. This ensures that some requests are always routed to the upstream, allowing the auto-tuner to re-adjust if the provider can handle more requests. The auto-tuner works by monitoring the "rate limited" (e.g. 429 status code) error rate of requests to the upstream. If the 'rate-limited' error rate is below the `errorRateThreshold`, it gradually increases the rate limit by the `increaseFactor`. If the 'rate-limited' error rate exceeds the threshold, it quickly decreases the rate limit by the `decreaseFactor`. By default, the auto-tuner is enabled with the following configuration: yamltypescript ``` rateLimitAutoTune: enabled: true adjustmentPeriod: "1m" errorRateThreshold: 0.1 increaseFactor: 1.05 decreaseFactor: 0.9 minBudget: 0 maxBudget: 10000 ``` You can override these defaults by specifying the desired values in your configuration. ### Metrics [Permalink for this section](https://docs.erpc.cloud/config/rate-limiters\#metrics) The following metrics are available for rate limiter budgets: - `erpc_rate_limiter_budget_max_count` with labels `budget` and `method` This metrics shows how maxCount is adjusted over time if auto-tuning is enabled. ### Where budgets can be applied [Permalink for this section](https://docs.erpc.cloud/config/rate-limiters\#where-budgets-can-be-applied) - Project: `project.rateLimitBudget` applies to all requests within the project, across all networks and upstreams. - Network defaults: `networkDefaults.rateLimitBudget` provides a default for all networks unless overridden per network. - Network: `network.rateLimitBudget` applies to requests routed through that network. - Upstream: `upstream.rateLimitBudget` applies to requests forwarded to that specific upstream (checked right before sending). - Auth strategy and per-user override: - Each auth strategy can impose an additional budget before request handling: - Secret: `auth.strategies[].secret.rateLimitBudget` (static) - JWT: claim-based override. Default claim name `rlm`, configurable via `auth.strategies[].jwt.rateLimitBudgetClaimName`. If present, sets `user.rateLimitBudget` for that request. - Database: a `rateLimitBudget` column in your record sets `user.rateLimitBudget`. - SIWE: `auth.strategies[].siwe.rateLimitBudget` (static) - Network auth: `auth.strategies[].network.rateLimitBudget` (static) Budgets are composable: eRPC evaluates each applied layer independently (auth → project → network → upstream). If any layer returns over-limit, the request is rejected with a dedicated error indicating the layer, budget, and rule. ### Rule scopes and descriptors [Permalink for this section](https://docs.erpc.cloud/config/rate-limiters\#rule-scopes-and-descriptors) Rules can be evaluated with additional descriptors to partition rate usage: - `perIP: true` → adds the client IP to the descriptor. Requires eRPC to determine `req.ClientIP()` (via trusted proxy headers or remote addr). - `perUser: true` → adds the authenticated user ID. Requires an auth strategy to set `user.id`. - `perNetwork: true` → adds the network ID being accessed. Descriptor behavior: - Scopes only affect partitioning of counters; they do not change `maxCount`. - Combining scopes increases cardinality (e.g., perUser+perNetwork isolates usage per user per network). - If a scoped value is missing (e.g., `perUser: true` but unauthenticated), the rule will error for that request. Example rule with scopes: ``` rateLimiters: budgets: - id: free-trial-package rules: - method: '*' maxCount: 10 period: second perUser: true # counts per authenticated user id perNetwork: false # overall limit for each user - method: '*' maxCount: 20000 period: day perUser: true # counts per authenticated user id perNetwork: true # further partition per network ``` ### Store backends [Permalink for this section](https://docs.erpc.cloud/config/rate-limiters\#store-backends) - Redis (recommended for multi-instance deployments): - Strongly consistent counting across replicas. - Configure via `rateLimiters.store.driver: redis` and `rateLimiters.store.redis` (URI, TLS, pool size, etc.). - `nearLimitRatio` (default 0.8) controls when "near limit" is reported internally. - `cacheKeyPrefix` (default `erpc_rl_`) prefixes all ratelimit keys. - `redis.getTimeout` (default `5s`) maximum time to wait for Redis rate limit check. If exceeded, request is allowed (fail-open). Set to `0` to disable timeout. - Memory (single-process only): - Fast, in-memory counters. Not shared across processes. - Good for development or single-node setups. ### Matching and wildcards [Permalink for this section](https://docs.erpc.cloud/config/rate-limiters\#matching-and-wildcards) - `rules[].method` supports wildcards (`*`). Exact match or wildcard match triggers the rule. - Multiple rules can match a method; all matching rules are evaluated and any over-limit denies the request. ### Evaluation order [Permalink for this section](https://docs.erpc.cloud/config/rate-limiters\#evaluation-order) For each request, eRPC may evaluate up to four layers (if configured): 1. Auth layer (if the applied strategy defines a budget or the user provides an override via JWT/DB) 2. Project layer 3. Network layer 4. Upstream layer Each layer selects matching rules by `method` and checks counters via the configured store. The first layer that is over-limit stops processing and returns an error specific to that layer. ### Errors and status codes [Permalink for this section](https://docs.erpc.cloud/config/rate-limiters\#errors-and-status-codes) - Auth layer: `ErrAuthRateLimitRuleExceeded` - Project layer: `ErrProjectRateLimitRuleExceeded` - Network layer: `ErrNetworkRateLimitRuleExceeded` - Upstream layer: `ErrUpstreamRateLimitRuleExceeded` Each error includes the `budget` ID and the `rule` (e.g., `method:eth_getLogs`) to aid debugging. ### Defaults and validation [Permalink for this section](https://docs.erpc.cloud/config/rate-limiters\#defaults-and-validation) - `rateLimiters.store` is required. Allowed drivers: `redis`, `memory`. - Budgets must define at least one `rules` entry. - `period` must be one of: `second`, `minute`, `hour`, `day`, `week`, `month`, `year`. Legacy durations like `1s` are accepted and mapped. - Upstream `rateLimitAutoTune` defaults on when a budget is set; you can tune `enabled`, `adjustmentPeriod`, `errorRateThreshold`, `increaseFactor`, `decreaseFactor`, `minBudget`, `maxBudget`. - JWT budget claim defaults to `rlm` and can be changed via `auth.strategies[].jwt.rateLimitBudgetClaimName`. ### Metrics [Permalink for this section](https://docs.erpc.cloud/config/rate-limiters\#metrics-1) - `erpc_rate_limiter_budget_max_count{budget,method,scope}`: current configured/auto-tuned max per rule. - `erpc_rate_limit_requests_total{budget,category,user,network}`: local checks performed. - `erpc_rate_limit_within_limit_total{budget,category,user,network}`: allowed by local limiter. - `erpc_rate_limit_over_limit_total{budget,category,user,network}`: blocked by local limiter. - `erpc_auth_request_self_rate_limited_total{project,strategy,category}`: auth layer over-limits. - `erpc_project_request_self_rate_limited_total{project,category}`: project layer over-limits. ### Operational notes [Permalink for this section](https://docs.erpc.cloud/config/rate-limiters\#operational-notes) - Redis store is preferred for horizontal scaling; memory store is per-process only. - Scopes that rely on request context (IP, user) require correct upstream proxy headers and a successful auth step. - Wildcard-heavy rule sets are supported; prefer a small set of broad rules for performance and clarity. - Auto-tuner adjusts only rules that match the method and have accumulated enough samples; set `minBudget >= 1` so traffic never stops entirely. [Auth](https://docs.erpc.cloud/config/auth "Auth") [Matcher syntax](https://docs.erpc.cloud/config/matcher "Matcher syntax") ## eRPC Database Drivers [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Config Database Drivers ## Drivers [Permalink for this section](https://docs.erpc.cloud/config/database/drivers\#drivers) Depending on your use-case storage and performance requirements, you can use different drivers. ### Memory [Permalink for this section](https://docs.erpc.cloud/config/database/drivers\#memory) Mainly useful when you want fast access for limited amount of cached data. Use this driver for high-frequency RPC calls. yamltypescript erpc.yaml ``` database: evmJsonRpcCache: connectors: - id: memory-cache driver: memory memory: maxItems: 10000 maxTotalSize: "1GB" # For debugging purposes, you can enable metrics collection (expect 10% performance hit) emitMetrics: false policies: - network: "*" method: "*" finality: finalized connector: memory-cache ``` ### Redis [Permalink for this section](https://docs.erpc.cloud/config/database/drivers\#redis) Redis is useful when you need to store cached data temporarily with **eviction policy** (e.g. certain amount of memory). yamltypescript erpc.yaml ``` database: evmJsonRpcCache: connectors: - id: redis-cache driver: redis redis: # Connection URI (Required) # Format: redis://[username]:[password]@[host][:port][/database][?dial_timeout=value1&read_timeout=value2&write_timeout=value3&pool_size=value4] # Example: redis://:some-secret@global-shared-states-redis-master.redis.svc.cluster.local:6379/?pool_size=10 uri: "redis://username:password@host:port/db?dial_timeout=5s&read_timeout=1s&write_timeout=2s&pool_size=10" tls: enabled: false # or "true" if redis is configured with TLS certFile: /path/to/client.crt # Optional keyFile: /path/to/client.key # Optional caFile: /path/to/ca.crt # Optional policies: - network: "*" method: "*" finality: finalized connector: redis-cache ``` #### TLS options [Permalink for this section](https://docs.erpc.cloud/config/database/drivers\#tls-options) When your Redis endpoint already uses **rediss://** (for example, Railway or Upstash), TLS is negotiated automatically and you can omit the entire `tls:` block. Add the `tls:` section only when you need **mutual‑TLS** (client certificate/key) or your server uses a **private CA**: ``` redis: uri: rediss://user:pass@redis.internal:6380/0 tls: enabled: true certFile: /secrets/redis-client.crt # sent to server (mTLS) keyFile: /secrets/redis-client.key caFile: /secrets/redis-rootCA.pem # trust this CA instead of system roots ``` #### Redis URI Format [Permalink for this section](https://docs.erpc.cloud/config/database/drivers\#redis-uri-format) The Redis URI format follows this pattern: ``` redis://[[username]:[password]@][host][:port][/database][?dial_timeout=value1&read_timeout=value2&write_timeout=value3&pool_size=value4] ``` Examples: - `redis://localhost:6379/0` \- Basic connection to localhost - `redis://user:pass@redis.example.com:6379/0` \- Connection with authentication - `redis://:password@redis.example.com:6379/0` \- Connection with password only (no username) - `redis://redis.example.com:6379` \- Connection without database number (defaults to 0) - `redis://redis.example.com:6379/0?dial_timeout=5s&read_timeout=1s&write_timeout=2s&pool_size=10` \- Connection with timeouts and pool size - `redis://global-shared-states-redis-master.redis.svc.cluster.local:6379` \- Kubernetes service deployed via [Bitnami charts (opens in a new tab)](https://github.com/bitnami/charts/tree/master/bitnami/redis) You can include these parameters directly in the URI: - `dial_timeout` \- Timeout for initial connection (e.g., `5s`) - `read_timeout` \- Timeout for read operations (e.g., `1s`) - `write_timeout` \- Timeout for write operations (e.g., `2s`) - `pool_size` \- Connection pool size (e.g., `10`) #### Configuration Notes [Permalink for this section](https://docs.erpc.cloud/config/database/drivers\#configuration-notes) ``` maxmemory 2000mb maxmemory-policy allkeys-lru ``` ### PostgreSQL [Permalink for this section](https://docs.erpc.cloud/config/database/drivers\#postgresql) Useful when you need to store cached data permanently without TTL i.e. forever. You don't need to create the table, the driver will automatically create the table and requried indexes. yamltypescript erpc.yaml ``` database: evmJsonRpcCache: connectors: - id: postgres-cache driver: postgresql postgresql: connectionUri: >- postgres://YOUR_USERNAME_HERE:YOUR_PASSWORD_HERE@your.postgres.hostname.here.com:5432/your_database_name table: rpc_cache initTimeout: 5s getTimeout: 1s setTimeout: 2s policies: - network: "*" method: "*" finality: finalized connector: postgres-cache ``` ### DynamoDB [Permalink for this section](https://docs.erpc.cloud/config/database/drivers\#dynamodb) When you need to have scalable (compared to Postgres) permanent caching and are happy with the costs. yamltypescript erpc.yaml ``` database: evmJsonRpcCache: connectors: - id: dynamodb-cache driver: dynamodb dynamodb: table: erpc_json_rpc_cache region: eu-west-1 initTimeout: 5s getTimeout: 1s setTimeout: 2s endpoint: https://dynamodb.eu-west-1.amazonaws.com # Optional # Auth is optional if you are running within AWS. auth: mode: secret # file, or env accessKeyId: YOUR_ACCESS_KEY_ID # Only if mode is secret secretAccessKey: YOUR_SECRET_ACCESS_KEY # Only if mode is secret profile: xxxxx # Only if mode is file credentialsFile: xxxx # Only if mode is file policies: - network: "*" method: "*" finality: finalized connector: dynamodb-cache ``` #### IAM Permissions [Permalink for this section](https://docs.erpc.cloud/config/database/drivers\#iam-permissions) Make sure the IAM role/user has the necessary permissions to create and/or access the DynamoDB table: - Table Management: - `dynamodb:CreateTable` \- For creating the table if it doesn't exist - `dynamodb:DescribeTable` \- For checking table existence and configuration - `dynamodb:UpdateTable` \- For adding the global secondary index - `dynamodb:UpdateTimeToLive` \- For configuring TTL attributes - Data Operations: - `dynamodb:PutItem` \- For storing data and creating locks - `dynamodb:GetItem` \- For retrieving data (certain cache queries such as eth\_getBlockByNumber with a hex block number) - `dynamodb:Query` \- For querying with the reverse index (certain cache queries such as eth\_getBlockReceipts by blockHash) - `dynamodb:DeleteItem` \- For removing locks - `dynamodb:UpdateItem` \- For counter operations with conditions You can create the table and the reverse GSI index manually and avoid "Table management" permissions: - Table name: `erpc_json_rpc_cache` - Reverse GSI index name: `idx_requestKey_groupKey` with primary key `requestKey` and sort key `groupKey` and projection type `ALL` Timeout [EVM Cache](https://docs.erpc.cloud/config/database/evm-json-rpc-cache "EVM Cache") ## eRPC Integrity Module [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) # Integrity RPC nodes may return stale or inconsistent data. eRPC's integrity module ensures you always get the most recent and valid blockchain data through: 1. **Block tracking** — Monitors highest known block across all upstreams, ensures `eth_blockNumber` and `eth_getBlockByNumber(latest/finalized)` return the freshest data. 2. **Range enforcement** — For `eth_getLogs`, ensures the requested block range is available on the chosen upstream. 3. **Response validation** — Validates response structure and consistency (bloom filters, receipts, logs). See [Validations](https://docs.erpc.cloud/config/failsafe/integrity#validations). Combine with [retry](https://docs.erpc.cloud/config/failsafe/retry) and [consensus](https://docs.erpc.cloud/config/failsafe/consensus) policies for automatic failover when integrity checks fail. ## Config [Permalink for this section](https://docs.erpc.cloud/config/failsafe/integrity\#config) ``` projects: - id: main networks: - architecture: evm evm: chainId: 1 # Block tracking: ensure highest known block is returned integrity: enforceHighestBlock: true # Replace stale block numbers with highest known enforceGetLogsBlockRange: true # Skip upstreams that don't have requested range # State poller settings (how block tracking works) upstreamDefaults: evm: statePollerInterval: 30s # How often to poll latest/finalized block statePollerDebounce: 5s # Min interval between polls (ideally ≤ block time) ``` ## Block Tracking [Permalink for this section](https://docs.erpc.cloud/config/failsafe/integrity\#block-tracking) eRPC runs a background state poller for each upstream to track latest/finalized blocks. When `enforceHighestBlock: true`: - **`eth_blockNumber`**: If response is older than highest known, returns highest known instead. - **`eth_getBlockByNumber(latest/finalized)`**: If response is stale, retries on other upstreams. The poller also updates proactively when `eth_blockNumber` or `eth_getBlockByNumber(latest)` returns a higher block than currently tracked. **Metrics**: `erpc_upstream_stale_latest_block_total`, `erpc_upstream_stale_finalized_block_total` ## Range Enforcement for `eth_getLogs` [Permalink for this section](https://docs.erpc.cloud/config/failsafe/integrity\#range-enforcement-for-eth_getlogs) When `enforceGetLogsBlockRange: true`, eRPC checks that the upstream has the requested block range before sending the request: 1. If `toBlock` \> upstream's latest block → skip to next upstream (after forcing a fresh poll if stale) 2. If `fromBlock` < upstream's available range (based on `maxAvailableRecentBlocks` config) → skip to next upstream **Large range handling**: eRPC can auto-split large ranges based on [`getLogsAutoSplittingRangeThreshold`](https://docs.erpc.cloud/config/projects/upstreams#eth_getlogs-max-range-automatic-splitting) or when upstream returns "range too large" errors. **Metrics**: `erpc_upstream_evm_get_logs_stale_upper_bound_total`, `erpc_upstream_evm_get_logs_stale_lower_bound_total`, `erpc_upstream_evm_get_logs_forced_splits_total` ## Validations Directives [Permalink for this section](https://docs.erpc.cloud/config/failsafe/integrity\#validations-directives) Response validation directives are ideal for **high-integrity use-cases** (such as indexing) where you need guaranteed data accuracy. When a validation fails, eRPC treats it as an upstream error — the response is rejected and retry/consensus policies automatically try other upstreams until valid data is found. **How it works with failsafe policies:** ``` Request → Upstream A returns receipts with missing logs (logsBloom doesn't match actual logs) → Validation fails → Response rejected (not cached, not returned) → Retry policy kicks in → Try Upstream B → Upstream B returns complete receipts with matching bloom → Success! ``` With **retry**: Each validation failure triggers the next retry attempt. Configure `maxAttempts` high enough to cover your upstream pool. With **consensus**: Invalid responses are excluded from consensus voting. Only valid responses participate, so even if 2/3 upstreams return bad data, the 1 valid response wins. With **hedge + consensus + retry** (recommended for indexers): Hedge spawns parallel requests, consensus compares valid responses, retry handles cases where all initial attempts fail validation. Set via **config** (applies to all requests), **HTTP headers**, or **query parameters**: | Directive | Header | Query | Description | | --- | --- | --- | --- | | `validateLogsBloomEmptiness` | `X-ERPC-Validate-Logs-Bloom-Emptiness` | `validate-logs-bloom-emptiness` | Bloom/logs consistency: logs exist ↔ bloom non-zero | | `validateLogsBloomMatch` | `X-ERPC-Validate-Logs-Bloom-Match` | `validate-logs-bloom-match` | Recalculate bloom from logs and verify match | | `enforceLogIndexStrictIncrements` | `X-ERPC-Enforce-Log-Index-Strict-Increments` | `enforce-log-index-strict-increments` | Log indices must increment by 1 across receipts | | `validateTxHashUniqueness` | `X-ERPC-Validate-Tx-Hash-Uniqueness` | `validate-tx-hash-uniqueness` | No duplicate transaction hashes in receipts | | `validateTransactionIndex` | `X-ERPC-Validate-Transaction-Index` | `validate-transaction-index` | Receipt indices must be sequential (0, 1, 2...) | | `validateHeaderFieldLengths` | `X-ERPC-Validate-Header-Field-Lengths` | `validate-header-field-lengths` | Block header field byte lengths | | `validateTransactionFields` | `X-ERPC-Validate-Transaction-Fields` | `validate-transaction-fields` | Transaction field formats | | `validateTransactionBlockInfo` | `X-ERPC-Validate-Transaction-Block-Info` | `validate-transaction-block-info` | Tx block hash/number matches block | | `validateLogFields` | `X-ERPC-Validate-Log-Fields` | `validate-log-fields` | Log address/topic lengths | | `receiptsCountExact` | `X-ERPC-Receipts-Count-Exact` | `receipts-count-exact` | Receipts array must have exactly N items | | `receiptsCountAtLeast` | `X-ERPC-Receipts-Count-At-Least` | `receipts-count-at-least` | Receipts array must have at least N items | | `validationExpectedBlockHash` | `X-ERPC-Validation-Expected-Block-Hash` | `validation-expected-block-hash` | All receipts must have this block hash | | `validationExpectedBlockNumber` | `X-ERPC-Validation-Expected-Block-Number` | `validation-expected-block-number` | All receipts must have this block number | ``` projects: - id: main networks: - architecture: evm evm: chainId: 1 directiveDefaults: # Response validations are DISABLED by default (to avoid JSON parsing overhead). # Enable specific ones as needed for high-integrity use-cases: validateLogsBloomEmptiness: true validateLogsBloomMatch: true enforceLogIndexStrictIncrements: true # etc. # Recommended for indexers: hedge + consensus + retry # Invalid responses are rejected, valid ones are compared, retries if all fail failsafe: - matchMethods: "eth_getBlockReceipts" hedge: maxCount: 3 # Spawn up to 3 parallel requests delay: 100ms consensus: maxParticipants: 3 agreementThreshold: 2 # Accept if 2+ agree (invalid ones excluded) retry: maxAttempts: 5 # Keep trying until valid data found ``` [Why eRPC?](https://docs.erpc.cloud/why "Why eRPC?") ## EVM Upstream Config [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Config [Projects](https://docs.erpc.cloud/config/projects) Upstreams # Upstreams An upstream is defined to handle 1 or more networks (a.k.a. chains). There are currently these types of upstreams: - [`evm`](https://docs.erpc.cloud/config/projects/upstreams#evm-json-rpc) A generic EVM-compatible JSON-RPC endpoint. This is the default and most-used type. eRPC supports **any EVM-compatible** JSON-RPC endpoint when using `evm` type. Specialized types like "alchemy" are built for well-known providers to make it easier to import "all supported evm chains" with just an API-KEY. ## Config [Permalink for this section](https://docs.erpc.cloud/config/projects/upstreams\#config) yamltypescript erpc.yaml ``` # ... projects: - id: main # ... # Each upstream supports 1 or more networks (i.e. evm chains) upstreams: # (REQUIRED) Endpoint URL supports http(s) scheme along with custom schemes like "alchemy://" defined below in this docs. - endpoint: https://arbitrum-one.blastapi.io/xxxxxxx-xxxxxx-xxxxxxx # Each upstream can have an arbitrary group name which is used in metrics, as well as # useful when writing an eval function in selectionPolicy below. # Use "fallback" group to let eRPC automatically create a "default" selection policy on the network level # and then fallback to this group if the default one doesn't have enough healthy upstreams. group: fallback # (OPTIONAL) Upstream ID is optional and can be used to identify the upstream in logs/metrics. id: blastapi-chain-42161 # (OPTIONAL) Configurations for EVM-compatible upstreams. evm: # (OPTIONAL) chainId is optional and will be detected from the endpoint (eth_chainId), # but it is recommended to set it explicitly, for faster initialization. # DEFAULT: auto-detected. chainId: 42161 # (OPTIONAL) statePollerInterval used to periodically fetch the latest/finalized/sync states. # DEFAULT: 30s. statePollerInterval: 30s # (OPTIONAL) statePollerDebounce prevents too many Polls for latest/finalized block numbers during integrity checks. # This ideally must be close (or lower than) the block time of the chain, but not too low to avoid thundering herd (e.g <1s is too low). # DEFAULT: 5s (or equal to block time if the chainId is a known chain) statePollerDebounce: 5s # (OPTIONAL) nodeType is optional and you can manually set it to "full" or "archive". # DEFAULT: archive nodeType: full # (OPTIONAL) maxAvailableRecentBlocks limits the maximum number of recent blocks to be served by this upstream. # DEFAULT: 128 (for "full" nodes). maxAvailableRecentBlocks: 128 # (OPTIONAL) getLogsAutoSplittingRangeThreshold is an upstream hint used by the network-level # proactive splitter. The network computes the min positive threshold across selected upstreams # and splits large ranges into contiguous sub-requests of at most that size. # Set to 0 or a negative value to disable for this upstream. getLogsAutoSplittingRangeThreshold: 10000 # (OPTIONAL) Defines which budget to use when hadnling requests of this upstream (e.g. to limit total RPS) # Since budgets can be applied to multiple upstreams they all consume from the same budget. # For example "global-blast" below can be applied to all chains supported by BlastAPI, # to ensure you're not hitting them more than your account allows. # DEFAULT: - no budget applied. rateLimitBudget: global-blast # (OPTIONAL) Rate limit budget can be automatically adjusted based on the "rate-limited" error rate, # received from upstream. Auto-tuning is enabled by default with values below. # This is useful to automatically increase the budget if an upstream is capable of handling more requests, # and decrease the budget if upstream is degraded. # Every "adjustmentPeriod" total number of requests vs rate-limited will be calculated, # if the value (0 to 1) is above "errorRateThreshold" then budget will be decreased by "decreaseFactor", # if the value is below "errorRateThreshold" then budget will be increased by "increaseFactor". # Note that the new budget will be applied to any upstream using this budget (e.g. Quicknode budget decreases). # DEFAULT: if any budget is defined, auto-tuning is enabled with these values: rateLimitAutoTune: enabled: true adjustmentPeriod: 1m errorRateThreshold: 0.1 increaseFactor: 1.05 decreaseFactor: 0.9 minBudget: 0 maxBudget: 10_000 jsonRpc: # (OPTIONAL) To allow auto-batching requests towards the upstream. # Remember even if "supportsBatch" is false, you still can send batch requests to eRPC # but they will be sent to upstream as individual requests. supportsBatch: true batchMaxSize: 10 batchMaxWait: 50ms # (OPTIONAL) Headers to send along with every outbound JSON-RPC request. # This is especially useful for upstreams that require a static Bearer token for authentication. headers: Authorization: "Bearer 1234567890" # (OPTIONAL) Which methods must never be sent to this upstream. # For example this can be used to avoid archive calls (traces) to full nodes ignoreMethods: - "eth_traceTransaction" - "alchemy_*" # (OPTIONAL) Explicitly allowed methods will take precedence over ignoreMethods. # For example if you only want eth_getLogs to be served, set ignore methods to "*" and allowMethods to "eth_getLogs". allowMethods: - "eth_getLogs" # (OPTIONAL) By default a dynamic mechanism automatically adds "Unsupported" methods to ignoreMethods, # based on errors returned by the upstream. Set this to false to disable this behavior. # Default: true autoIgnoreUnsupportedMethods: true # (OPTIONAL) Refer to "Failsafe" docs section for more details. # Here is "default" configuration if not explicitly set: failsafe: timeout: duration: 15s retry: maxAttempts: 2 delay: 1000ms backoffMaxDelay: 10s backoffFactor: 0.3 jitter: 500ms circuitBreaker: # Open circuit after 80% of requests so far have failed (160 out of 200 last requests) failureThresholdCount: 160 failureThresholdCapacity: 200 # Wait 5 minutes before trying again halfOpenAfter: 5m # Close circuit after 3 successful requests (3 out of 10) successThresholdCount: 3 successThresholdCapacity: 10 ``` ### Config defaults [Permalink for this section](https://docs.erpc.cloud/config/projects/upstreams\#config-defaults) The `project.upstreamDefaults` configuration allows you to set default values for all [`upstreams`](https://docs.erpc.cloud/config/projects/upstreams) in a project. These defaults are applied before any upstream-specific configurations: erpc.yaml ``` projects: - id: main upstreams: # ... example above ^ upstreamDefaults: # Default group for all upstreams group: "default" # Default JSON-RPC settings jsonRpc: supportsBatch: true batchMaxSize: 10 batchMaxWait: "50ms" # Default failsafe policies failsafe: timeout: duration: "15s" retry: maxAttempts: 3 delay: "300ms" jitter: "100ms" backoffMaxDelay: "5s" backoffFactor: 1.5 circuitBreaker: failureThresholdCount: 160 failureThresholdCapacity: 200 halfOpenAfter: "5m" successThresholdCount: 3 successThresholdCapacity: 3 # Default method filters ignoreMethods: - "eth_traceTransaction" - "alchemy_*" allowMethods: - "eth_getLogs" ``` Default values are only applied if the upstream doesn't have those values explicitly set. This allows you to have consistent configuration across all upstreams while still maintaining the ability to override specific values when needed. Defaults are merged on the first-level only (and not a deep merge). i.e. If an upstream has its own `failsafe:` defined, it will not take any of policies from upstreamDefaults. e.g. if an upstream.failsafe only has "timeout" policy, it will **NOT** get retry/circuitBreaker from upstreamDefaults (those will be disabled). ## Priority & selection mechanism [Permalink for this section](https://docs.erpc.cloud/config/projects/upstreams\#priority--selection-mechanism) eRPC evaluates each upstream's performance using key metrics to decide the most suitable upstream for each request. These metrics include: - **Total request failures**: Prioritizes upstreams with lower failure rates. - **Rate-limited requests**: Gives preference to upstreams with fewer rate-limited requests. - **P90 request latency**: Prioritizes upstreams with lower latency. - **Total requests served**: Favors upstreams that have served fewer requests to balance load. - **Block head lag**: Prefers upstreams with lower lag compared to the best-performing upstream. - **Finalization lag**: Prioritizes upstreams with lower finalization lag. Each upstream receives a **score** based on these metrics, calculated per method (e.g., `eth_blockNumber`, `eth_getLogs`) over a configurable time window (`scoreMetricsWindowSize`, default 30 minutes). Adjust the window size in `erpc.yaml` as shown: yamltypescript erpc.yaml ``` projects: # ... - id: main # ... scoreMetricsWindowSize: 1h # ... ``` The scoring mechanism only affects the order in which upstreams are tried. To fully disable an unreliable upstream, use the [Circuit Breaker](https://docs.erpc.cloud/config/failsafe#circuitbreaker-policy) failsafe policy at the upstream level. ### Customizing scores & priorities [Permalink for this section](https://docs.erpc.cloud/config/projects/upstreams\#customizing-scores--priorities) Upstreams are ranked by score, controlling selection order. You can adjust this ranking by setting multipliers at different levels: overall, per network or method, or for specific metrics (e.g., error rate, block lag). For every value the higher the value, the more important it is. For example respLatency: 8 means you care about this more than errorRate: 4 yamltypescript erpc.yaml ``` upstreams: # ... - id: my-alchemy # ... routing: # (OPTIONAL) The quantile to use for latency scoring. e.g. "70%" means to ignore 30% of the slowest requests. # DEFAULT: 0.70 scoreLatencyQuantile: 0.70 # (OPTIONAL) The score multipliers to give upstreams different priority. scoreMultipliers: - network: '*' # Network(s) to apply these multipliers to (default: all networks) method: '*' # Method(s) where you want to apply these multipliers (default: all methods) # method: 'eth_*|alchemy_*' means apply these multipliers to all methods starting with "eth_" or "alchemy_" finality: # Finality states for this multiplier set (default: all finality states) - finalized # Can be: "finalized", "unfinalized", "realtime", or "unknown" - unfinalized # (OPTIONAL) Adjusts the overall score scale. # DEFAULT: 1.0 overall: 1.0 # (OPTIONAL) Default multiplier values: respLatency: 8.0 # Penalize higher latency by increasing this value (according to scoreLatencyQuantile). errorRate: 4.0 # Penalize higher error rates by increasing this value. throttledRate: 3.0 # Penalize higher throttled requests by increasing this value. blockHeadLag: 2.0 # Penalize nodes lagging in block head updates by increasing this value. totalRequests: 1.0 # Give more weight to upstreams with fewer requests. finalizationLag: 1.0 # Penalize nodes lagging in finalization by increasing this value. ``` Example: To prioritize a less expensive (but slower) upstream, adjust the `overall` score multiplier as follows: yamltypescript erpc.yaml ``` upstreams: # ... - id: my-cheap-node # ... routing: scoreMultipliers: - overall: 10 - id: my-expensive-node # ... routing: scoreMultipliers: - overall: 1 ``` A higher score means the upstream is tried first. If errors occur, other upstreams are attempted. ## Upstream types [Permalink for this section](https://docs.erpc.cloud/config/projects/upstreams\#upstream-types) ### `evm` [Permalink for this section](https://docs.erpc.cloud/config/projects/upstreams\#evm) These are generic well-known EVM-compatible JSON-RPC endpoints. This is the default and most-used type. They can be your own self-hosted nodes, or remote 3rd-party provider nodes. yamltypescript erpc.yaml ``` # ... projects: - id: main # ... upstreams: - id: my-infura type: evm endpoint: https://mainnet.infura.io/v3/YOUR_INFURA_KEY # (OPTIONAL) Configurations for EVM-compatible upstreams. evm: # (OPTIONAL) chainId is optional and will be detected from the endpoint (eth_chainId), # but it is recommended to set it explicitly, for faster initialization. # DEFAULT: auto-detected. chainId: 42161 # (OPTIONAL) statePollerInterval used to periodically fetch the latest/finalized/sync states. # To disable state polling set this value to 0, which means no regular calls to RPC for latest/finalized/sync states. # The consequence of this is all data will be considered "unfinalized" or "unknown" despite their block numbers (and where if theye're actually finalized or not). # DEFAULT: 30s. statePollerInterval: 30s # (OPTIONAL) statePollerDebounce prevents too many Polls for latest/finalized block numbers during integrity checks. # This ideally must be close (or lower than) the block time of the chain, but not too low to avoid thundering herd (e.g <1s is too low). # DEFAULT: 5s (or equal to block time if the chainId is a known chain) statePollerDebounce: 5s # (OPTIONAL) nodeType is optional and you can manually set it to "full" or "archive". # DEFAULT: archive nodeType: full # (OPTIONAL) maxAvailableRecentBlocks limits the maximum number of recent blocks to be served by this upstream. # DEFAULT: 128 (for "full" nodes). maxAvailableRecentBlocks: 128 # (OPTIONAL) getLogsAutoSplittingRangeThreshold is an upstream hint used by the network-level # proactive splitter. The network computes the min positive threshold across selected upstreams # and splits large ranges into contiguous sub-requests of at most that size. # Set to 0 or a negative value to disable for this upstream. getLogsAutoSplittingRangeThreshold: 10000 # ... ``` getLogs limits, splitting on error, and enforcement are now configured at the **network** level. See [EVM Networks](https://docs.erpc.cloud/config/projects/networks#evm-networks) → _eth\_getLogs_. ## Block availability [Permalink for this section](https://docs.erpc.cloud/config/projects/upstreams\#block-availability) Define the block window each EVM upstream can serve. You can bound by the chain's earliest or latest block and use different probes to detect real availability on that upstream. This feature is optional and primarily helps reduce redundant calls and latency. eRPC already maintains correctness by automatically failing over to other healthy upstreams when one node lacks the data. Block availability simply helps skip over such nodes faster without trying them first. For many setups, method filters are a cheaper and simpler way to control upstream data availability; consider using `ignoreMethods`/`allowMethods` first. See [Config → method filters](https://docs.erpc.cloud/config/projects/upstreams#config). yamltypescript erpc.yaml ``` projects: - id: main upstreams: - id: my-evm endpoint: https://mainnet.example evm: # Limit the highest block this upstream serves to latest-64 (helps avoid reorgs) blockAvailability: upper: latestBlockMinus: 64 # latest - 64 is the upper bound probe: blockHeader # default probe; can be omitted # updateRate is ignored for latestBlockMinus (bound computed on-demand from evmStatePoller's latest block) # Auto-detect the earliest block where logs exist on this upstream, # and start serving from there (refresh hourly to follow pruning). lower: earliestBlockPlus: 0 # earliestDetected(eventLogs) + 0 probe: eventLogs # require >=1 log in the block updateRate: 1h # re-evaluate earliest periodically # Example 2: fixed window (serve blocks 17,000,000..latest-128) - id: fixed-window endpoint: https://another evm: blockAvailability: lower: exactBlock: 17000000 # hard lower bound probe: blockHeader updateRate: 0s upper: latestBlockMinus: 128 # rolling upper bound (always uses current latest) probe: blockHeader # updateRate is ignored for latestBlockMinus (bound computed on-demand from evmStatePoller's latest block) # Example 3: traces-aware lower bound (only serve blocks that have traces) - id: traces endpoint: https://traces.example evm: blockAvailability: lower: earliestBlockPlus: 0 # earliestDetected(traceData) probe: traceData # tries multiple trace/debug methods updateRate: 24h # re-check daily in case of pruning ``` - probe values: `blockHeader` (default), `eventLogs`, `callState`, `traceData` - lower/upper bounds: choose one of `exactBlock`, `earliestBlockPlus`, `latestBlockMinus` - updateRate: only applies to `earliestBlockPlus` bounds. 0 freezes the computed bound; >0 periodically re-evaluates it. For `latestBlockMinus`, updateRate is ignored since bounds are computed on-demand using the evmStatePoller's latest block Notes on probes: - eventLogs: considered available only if querying the block by `blockHash` returns at least 1 log. - callState: checks historical state via `eth_getBalance`; any non-null result counts as available. - traceData: tries multiple engines in order: `trace_block`, `debug_traceBlockByHash`, `trace_replayBlockTransactions`; available if any returns a non-empty result. ### When block availability is enforced? [Permalink for this section](https://docs.erpc.cloud/config/projects/upstreams\#when-block-availability-is-enforced) Block availability bounds are only enforced when eRPC can extract a block number from the request. If the block number cannot be determined (e.g., certain method calls without explicit block parameters), the request will be forwarded to the upstream regardless of the configured bounds. This ensures availability checks don't block requests where block context is unavailable. When a probe is unsupported on an upstream (e.g. method ignored/unsupported), eRPC skips that probe for availability decisions. Prefer `blockHeader` or choose a probe the upstream supports. **About updateRate:** The `updateRate` field only applies to `earliestBlockPlus` bounds. For `latestBlockMinus`, it is ignored because bounds are computed on-demand using the continuously-updated latest block value maintained by eRPC's state poller. This ensures `latestBlockMinus` bounds always reflect the current latest block without needing a separate update schedule. ## Compression [Permalink for this section](https://docs.erpc.cloud/config/projects/upstreams\#compression) eRPC supports gzip compression at multiple points in the request/response cycle: 1. **Client → eRPC**: Clients can send gzipped requests by setting `Content-Encoding: gzip` header ``` # Example of sending gzipped request to eRPC 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 ``` 2. **eRPC → Upstream**: Configurable per upstream to send gzipped requests (disabled by default) yamltypescript erpc.yaml ``` upstreams: - id: my-infura jsonRpc: enableGzip: false # gzip when sending requests to this upstream (disabled by default) ``` 3. **Upstream → eRPC**: Automatically handles gzipped responses from upstreams when they send `Content-Encoding: gzip` 4. **eRPC → Client**: Automatically enabled when clients send `Accept-Encoding: gzip` header (can be disabled in server config) yamltypescript erpc.yaml ``` server: enableGzip: true # gzip compression for responses to clients (enabled by default) ``` Using gzip can reduce ingress/egress bandwidth costs, and in certain cases (e.g. large RPC requests) it can improve performance. ## Custom HTTP Headers [Permalink for this section](https://docs.erpc.cloud/config/projects/upstreams\#custom-http-headers) You can send additional headers (e.g. `Authorization`) along with every outbound JSON-RPC request to an upstream by specifying `jsonRpc.headers` in the config. This is especially useful for upstreams that require a static Bearer token for authentication. yamltypescript erpc.yaml ``` upstreams: - id: my-private-upstream endpoint: https://private-provider.io/v1 jsonRpc: # (OPTIONAL) Send additional headers to this upstream on every request # e.g. Authorization bearer token, custom X-Header, etc. headers: Authorization: "Bearer SECRET_VALUE_123" X-Custom-Header: "HelloWorld" ``` ## Client proxy pools [Permalink for this section](https://docs.erpc.cloud/config/projects/upstreams\#client-proxy-pools) You define proxies for outgoing traffic from eRPC to upstreams. Proxy Pools enable centralized management of http(s)/socks5 proxies with round-robin load balancing across multiple upstreams. This is particularly useful for routing requests through different proxy servers based on geographic location or specific requirements (e.g., public vs private RPC endpoints). yamltypescript erpc.yaml ``` # Define proxy pools at the root level 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://proxy444.myorg.local:3129 projects: - id: main # Option 1: Apply proxy pool to all upstreams upstreamDefaults: jsonRpc: proxyPool: eu-dc1-pool # Option 2: Apply proxy pools selectively to specific upstreams upstreams: - id: public-rpc-1 endpoint: https://public-rpc-1.example.com jsonRpc: proxyPool: eu-dc1-pool - id: public-rpc-2 endpoint: https://public-rpc-2.example.com jsonRpc: proxyPool: us-dc1-pool # This upstream won't use a proxy since it has no proxyPool specified - id: private-rpc-1 endpoint: https://private-rpc-1.example.com ``` You can use `upstreamDefaults` to apply a proxy pool to all upstreams, or configure them individually. Individual upstream configurations will override the defaults. [Networks](https://docs.erpc.cloud/config/projects/networks "Networks") [Providers](https://docs.erpc.cloud/config/projects/providers "Providers") ## CORS Configuration Guide [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Config [Projects](https://docs.erpc.cloud/config/projects) CORS # Cross-Origin Resource Sharing (CORS) When using eRPC directly from the browser (i.e., frontend), you might need to enable Cross-Origin Resource Sharing (CORS) so that only your domains are allowed to access eRPC endpoints. ## Config [Permalink for this section](https://docs.erpc.cloud/config/projects/cors\#config) Here's an example of how to configure CORS in your `erpc.yaml` file: yamltypescript erpc.yaml ``` projects: - id: main cors: # List of allowed origins. Use ["*"] to allow any origin allowedOrigins: - "https://example.com" - "https://*.example.com" # HTTP methods allowed for CORS requests allowedMethods: - "GET" - "POST" - "OPTIONS" # Headers allowed in actual requests allowedHeaders: - "Content-Type" - "Authorization" # Headers exposed to the browser exposedHeaders: - "X-Request-ID" # Whether the browser should include credentials with requests allowCredentials: true # How long (in seconds) browsers should cache preflight request results maxAge: 3600 upstreams: # ... rateLimiters: # ... ``` #### `allowedOrigins` [Permalink for this section](https://docs.erpc.cloud/config/projects/cors\#allowedorigins) - Type: array of strings - Description: Specifies which origins are allowed to make requests to your eRPC endpoint. - Example: `["https://example.com", "https://*.example.com"]` - Use `["*"]` to allow any origin (not recommended for production) #### `allowedMethods` [Permalink for this section](https://docs.erpc.cloud/config/projects/cors\#allowedmethods) - Type: array of strings - Description: HTTP methods that are allowed when accessing the resource. - Example: `["GET", "POST", "OPTIONS"]` #### `allowedHeaders` [Permalink for this section](https://docs.erpc.cloud/config/projects/cors\#allowedheaders) - Type: array of strings - Description: Headers that are allowed in actual requests. - Example: `["Content-Type", "Authorization"]` #### `exposedHeaders` [Permalink for this section](https://docs.erpc.cloud/config/projects/cors\#exposedheaders) - Type: array of strings - Description: Headers that browsers are allowed to access. - Example: `["X-Request-ID"]` #### `allowCredentials` [Permalink for this section](https://docs.erpc.cloud/config/projects/cors\#allowcredentials) - Type: boolean - Description: Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates. - Example: `true` #### `maxAge` [Permalink for this section](https://docs.erpc.cloud/config/projects/cors\#maxage) - Type: integer - Description: Indicates how long (in seconds) the results of a preflight request can be cached. - Example: `3600` (1 hour) ## Behavior for Disallowed Origins [Permalink for this section](https://docs.erpc.cloud/config/projects/cors\#behavior-for-disallowed-origins) eRPC handles disallowed origins in a standards-compliant way: - eRPC does not forcibly block requests from origins that are not in your `allowedOrigins`. Instead, it simply omits the CORS headers in those cases. - **Browser-based clients** that strictly enforce CORS will automatically block these requests (due to missing CORS headers) - **Non-browser clients** (like curl, Postman, or certain Chrome extensions) typically don't enforce CORS and can still receive valid responses even without CORS headers This approach follows the [W3C CORS recommendation (opens in a new tab)](https://www.w3.org/TR/cors/#cross-origin-requests), which treats the server's CORS headers as an "opt-in" rather than a hard firewall. Since the Origin header is easily spoofed, relying on it for strict blocking is not recommended. ## Examples [Permalink for this section](https://docs.erpc.cloud/config/projects/cors\#examples) ### Basic Web Application [Permalink for this section](https://docs.erpc.cloud/config/projects/cors\#basic-web-application) For a basic web application where you want to allow requests only from your main domain: yamltypescript erpc.yaml ``` cors: allowedOrigins: - "https://myapp.com" allowedMethods: - "GET" - "POST" allowedHeaders: - "Content-Type" allowCredentials: false maxAge: 300 ``` ### Multiple Subdomains [Permalink for this section](https://docs.erpc.cloud/config/projects/cors\#multiple-subdomains) If your application spans multiple subdomains: yamltypescript erpc.yaml ``` cors: allowedOrigins: - "https://*.myapp.com" allowedMethods: - "GET" - "POST" - "PUT" - "DELETE" allowedHeaders: - "Content-Type" - "Authorization" exposedHeaders: - "X-Request-ID" allowCredentials: true maxAge: 3600 ``` ### Development Environment [Permalink for this section](https://docs.erpc.cloud/config/projects/cors\#development-environment) For a development environment where you need more permissive settings: yamltypescript erpc.yaml ``` cors: allowedOrigins: - "http://localhost:3000" - "http://127.0.0.1:3000" allowedMethods: - "GET" - "POST" - "PUT" - "DELETE" - "OPTIONS" allowedHeaders: - "*" allowCredentials: true maxAge: 86400 ``` [Selection policies](https://docs.erpc.cloud/config/projects/selection-policies "Selection policies") [Failsafe](https://docs.erpc.cloud/config/failsafe "Failsafe") ## Selection Policies Overview [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Config [Projects](https://docs.erpc.cloud/config/projects) Selection policies ### Selection policies [Permalink for this section](https://docs.erpc.cloud/config/projects/selection-policies\#selection-policies) **Selection policy** allows you to influence how upstreams are selected to serve traffic (or not). A selection policy is defined at the network level and is responsible for returning a list of upstreams that must remain active. The primary purpose of a selection policy is to define acceptable performance metrics and/or required conditions for selecting an upstream node. Selection policies can be configured to run per-method and network or per-network only. ⚠️ Selection policies are not executed per request, instead they run on an interval much like a healthcheck and update the available upstreams. #### Default fallback policy [Permalink for this section](https://docs.erpc.cloud/config/projects/selection-policies\#default-fallback-policy) By default a built-in selection policy is activated if **at least one upstream** is assigned to the "fallback" group. This default policy incorporates basic logic for error rates and block lag, which can be tuned via theese environment variables - `ROUTING_POLICY_MAX_ERROR_RATE` (Default: `0.7`): Maximum allowed error rate. - `ROUTING_POLICY_MAX_BLOCK_HEAD_LAG` (Default: `10`): Maximum allowed block head lag. - `ROUTING_POLICY_MIN_HEALTHY_THRESHOLD` (Default: "1"): Minimum number of healthy upstreams that must be included in default group. These environment variables allow you to adjust the default logic without rewriting the policy function. #### Use cases [Permalink for this section](https://docs.erpc.cloud/config/projects/selection-policies\#use-cases) - **Block Lag:** Disable upstreams that are lagging behind more than a specified number of blocks until they resync. - **Error Rate:** Exclude upstreams exceeding a certain error rate and periodically check their status. - **Cost-Efficiency:** Prioritize "cheap" nodes and fallback to "fast" nodes only is all cheap nodes are down. ##### Looking to influence selection ordering? [Permalink for this section](https://docs.erpc.cloud/config/projects/selection-policies\#looking-to-influence-selection-ordering) If you only want to change ordering of upstreams (not entirely exclude them) check out [Scoring multipliers](https://docs.erpc.cloud/config/projects/upstreams#customizing-scores--priorities) docs. Remember selection policy will NOT influence the ordering of upstreams. #### Config [Permalink for this section](https://docs.erpc.cloud/config/projects/selection-policies\#config) yamltypescript erpc.yaml ``` projects: - id: main upstreams: - endpoint: cheap-1.com - endpoint: cheap-2.com - endpoint: fast-1.com # Each upstream can have an arbitrary group name which is used in metrics, as well as # useful when writing an eval function in selectionPolicy below. group: fallback - endpoint: fast-2.com group: fallback networks: - architecture: evm evm: chainId: 1 # Determines when to include or exclude upstreams depending on their health and performance selectionPolicy: # Every 1 minute evaluate which upstreams must be included, # based on the arbitrary logic (e.g., <90% error rate and <10 block lag): evalInterval: 1m # Freeform TypeScript-based logic to select upstreams to be included by returning them: evalFunction: | (upstreams, method) => { const defaults = upstreams.filter(u => u.config.group !== 'fallback') const fallbacks = upstreams.filter(u => u.config.group === 'fallback') // Maximum allowed error rate. const maxErrorRate = parseFloat(process.env.ROUTING_POLICY_MAX_ERROR_RATE || '0.7') // Maximum allowed block head lag. const maxBlockHeadLag = parseFloat(process.env.ROUTING_POLICY_MAX_BLOCK_HEAD_LAG || '10') // Minimum number of healthy upstreams that must be included in default group. const minHealthyThreshold = parseInt(process.env.ROUTING_POLICY_MIN_HEALTHY_THRESHOLD || '1') // Filter upstreams that are healthy based on error rate and block head lag. const healthyOnes = defaults.filter( u => u.metrics.errorRate < maxErrorRate && u.metrics.blockHeadLag < maxBlockHeadLag ) // If there are enough healthy upstreams, return them. if (healthyOnes.length >= minHealthyThreshold) { return healthyOnes } // The reason all upstreams are returned is to be less harsh and still consider default nodes (in case they have intermittent issues) // Order of upstreams does not matter as that will be decided by the upstream scoring mechanism return upstreams } # To isolate selection evaluation and result to each "method" separately change this flag to true evalPerMethod: false # When an upstream is excluded, you can give it a chance on a regular basis # to handle a certain number of sample requests again, so that metrics are refreshed. # For example, to see if error rate is improving after 5 minutes, or still too high. # This is conceptually similar to how a circuit-breaker works in a "half-open" state. # Resampling is not always needed because the "evm state poller" component will still make # requests for the "latest" block, which still updates errorRate. resampleExcluded: false resampleInterval: 5m resampleCount: 100 ``` #### `evalFunction` parameters [Permalink for this section](https://docs.erpc.cloud/config/projects/selection-policies\#evalfunction-parameters) `upstreams` and `method` are available as variables in the `evalFunction`. types.d.ts ``` // Current upstream export type Upstream = { id: string; config: UpstreamConfig; metrics: UpstreamMetrics; }; // Upstream configuration export type UpstreamConfig = { // Upstream ID is optional and can be used to identify the upstream in logs/metrics. id: string; // Each upstream can have an arbitrary group name which is used in metrics, as well as // useful when writing an eval function in selectionPolicy below. // Use "fallback" group to let eRPC automatically create a "default" selection policy on the network level // and then fallback to this group if the default one doesn't have enough healthy upstreams. group: string; // Endpoint URL supports http(s) scheme along with custom schemes like "alchemy://" defined below in this docs. endpoint: string; }; // Upstream metrics export type UpstreamMetrics = { // p90 rate of errors of last X minutes (X is based on `project.scoreMetricsWindowSize`) errorRate: number; // total errors of this upstream errorsTotal: number; // total requests served by this upstream requestsTotal: number; // Throttled rate of this upstream. throttledRate: number; // p90 response time in seconds for this upstream. p90ResponseSeconds: number; // p95 response time in seconds for this upstream. p95ResponseSeconds: number; // p99 response time in seconds for this upstream. p99ResponseSeconds: number; // Block head lag in seconds for this upstream. blockHeadLag: number; // Finalization lag in seconds for this upstream. finalizationLag: number; }; // Method is either `*` (all methods) or a specific method name. export type Method = '*' | string; ``` [Providers](https://docs.erpc.cloud/config/projects/providers "Providers") [CORS](https://docs.erpc.cloud/config/projects/cors "CORS") ## Shared State Configuration [If you like eRPC, give it a star on GitHub ⭐️](https://github.com/erpc/erpc) Config Database Shared State # `sharedState` The `sharedState` feature enables multiple eRPC instances to share critical state information across a cluster. This is especially useful for horizontal scaling deployments where having a shared view of blockchain state improves efficiency and reduces unnecessary upstream requests. Key benefits: - **Reduced upstream load**: Instances share latest and finalized block info, eliminating redundant polling. - **Enhanced integrity checks**: More accurate integrity checks for operations like `eth_getLogs` by using shared latest block number. ## Config [Permalink for this section](https://docs.erpc.cloud/config/database/shared-state\#config) yamltypescript erpc.yaml ``` database: sharedState: # Unique identifier for a group of eRPC instances that should share state # Recommended if you have multiple separate eRPC clusters # Default: "erpc-default" clusterKey: "my-cluster-1" # Storage backend configuration # Local "memory" is used by default connector: # Storage driver: memory, redis, postgresql (memory is default) driver: redis # Redis-specific configuration redis: # Example: redis://:some-secret@global-shared-states-redis-master.redis.svc.cluster.local:6379/?pool_size=10 uri: "redis://username:password@host:port/db?dial_timeout=5s&read_timeout=1s&write_timeout=2s&pool_size=10" # Network I/O timeout for backing store operations (get/set/publish) # Recommended: 3s fallbackTimeout: 3s # TTL for distributed locks in the backing store. # Recommended: 2s (keep short; foreground path is best‑effort) lockTtl: 2s # Foreground latency budgets (best‑effort) # Recommended: lockMaxWait=100ms, updateMaxWait=50ms lockMaxWait: 100ms # max time to try acquiring the lock before proceeding locally updateMaxWait: 50ms # max time to run refresh function before returning current value ``` ⚠️ Setting a unique `clusterKey` is critical if you have multiple eRPC deployments (e.g., different clusters in Kubernetes). This ensures each cluster maintains its own isolated shared state. If not specified, it defaults to "erpc-default". ### Recommendation [Permalink for this section](https://docs.erpc.cloud/config/database/shared-state\#recommendation) We recommend using Redis as the shared state connector for production deployments: yamltypescript erpc.yaml ``` database: sharedState: connector: driver: redis redis: # Example: redis://:some-secret@global-shared-states-redis-master.redis.svc.cluster.local:6379/?pool_size=10 uri: "redis://username:password@host:port/db?dial_timeout=5s&read_timeout=1s&write_timeout=2s&pool_size=10" ``` Redis is the recommended connector for shared state as it provides fast synchronization between instances. The total storage needed is typically less than 1MB per upstream. For more information on available connectors and their configuration options, see the [Drivers](https://docs.erpc.cloud/config/database/drivers) documentation. [EVM Cache](https://docs.erpc.cloud/config/database/evm-json-rpc-cache "EVM Cache") [Auth](https://docs.erpc.cloud/config/auth "Auth")