Self-hosted service
erpc/erpc avatar
erpc/erpc

eRPC: a fault-tolerant RPC proxy with a re-org aware cache in front of your chain providers

eRPC — fault-tolerant evm rpc proxy

795 stars133 forksGoApache-2.0

At a glance

What is it?
eRPC is a Go proxy that sits between your application and a set of EVM or Solana RPC upstreams and handling failover, retries, circuit breakers, hedged requests, rate limits and a permanent cache that understands chain re-organisations. It serves both EVM chains and SVM clusters, and the config is TypeScript, JavaScript or YAML.
Who is it for?
eRPC fits a team making many read-heavy RPC calls across several providers and self-hosted nodes, where upstream flakiness and per-provider rate limits are the operational pain. It does not fit a team with a single reliable provider, and it adds a stateful cache and a consensus layer that you then have to operate.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the proxy does and where it sits

eRPC is a fault-tolerant RPC proxy with re-org-aware permanent caching, serving both EVM chains and SVM, meaning Solana, clusters. It sits in front of all your upstreams, managed providers and your own nodes, behind a single endpoint, and handles failover, retries, caching and multi-chain routing automatically. It is built for read-heavy workloads such as data indexing and high-load frontends.

The URL scheme shows the multi-chain design. A request to a path like /main/evm/42161 addresses the main project, the EVM chain with chain id 42161, and the Solana equivalent is /main/svm/mainnet-beta. SVM networks identify themselves as svm:<cluster>, for example svm:mainnet-beta, svm:devnet and svm:testnet, or svm:<chain>:<cluster> for Solana forks and variants such as Fogo and Eclipse, so several SVM chains can be hosted side by side without network-id or cache-key collisions.

The quick start has three ways to run it. Through npx:

bash
npx start-erpc

Or with Docker:

bash
docker run -p 4000:4000 ghcr.io/erpc/erpc

Or as a Railway template. A request against it is a normal JSON-RPC call:

bash
curl 'http://localhost:4000/main/evm/42161' \
--header 'Content-Type: application/json' \
--data '{
    "method": "eth_getBlockByNumber",
    "params": ["latest", false],
    "id": 9199,
    "jsonrpc": "2.0"
}'

That setup gives immediate access to 2,000 or more EVM chains and 4,000 or more public free EVM RPC endpoints, with the SVM networks configured explicitly.

The Solana side is not just the EVM side renamed

The README says the same failsafe, routing and consensus machinery EVM uses applies to SVM, with Solana-specific behaviour wired in automatically, and then lists what that behaviour is. The differences are substantial enough to matter if you run both.

Commitment-aware finality is the first. A network-level svm.commitment is injected per method at the param position Solana expects, so every upstream answers at the same level. Cache finality follows Solana's rules rather than EVM's: commitment: finalized is the latest rooted slot, a head that advances roughly every 400 milliseconds, so only slot-pinned reads such as getBlock and getTransaction at a finalized commitment are cached as immutable, while moving-head state reads such as getBalance and getAccountInfo stay realtime.

That distinction is the whole point of the SVM cache block, configured under database.svmJsonRpcCache with the same schema as evmJsonRpcCache, and both may share one backend. SVM cache keys are case-preserving, because base58 public keys and signatures are case-sensitive.

Write guards are the third difference and the one with a direct operational consequence. sendTransaction, sendRawTransaction and requestAirdrop are never auto-dispatched to a second upstream by retry, hedge or probe mirroring, which is the opposite of EVM, where eth_sendRawTransaction is hedgeable.

There is also a per-upstream slot poller that tracks processed and finalized slots plus the blockstore-ingestion watermark, so an upstream that fails getHealth or ingests shreds without replaying them is cordoned out of routing until it recovers. And native error passthrough means Solana JSON-RPC codes and error.data reach the client unchanged, notably the -32002 SendTransactionPreflightFailure with its RpcSimulateTransactionResult, so the solana web3.js and solana kit libraries keep working.

Caching, routing, limits and consensus

The key feature list covers eleven areas, and the first two are the reason to run a proxy at all.

Retries, failover, circuit breakers and hedged requests mean every call is routed to the fastest healthy upstream, automatically. The re-org-aware permanent cache serves reads from the cache, stays consistent across chain re-orgs and eliminates redundant upstream calls, and there is a hosted gRPC cache by Goldsky for plugging in a read-only cache of indexed chain data to serve historical reads without hitting your upstreams.

Automatic method routing removes the need to track which provider supports which eth_* or Solana method, which is a maintenance problem that grows quietly with every provider you add.

Configurable rate limits set hourly or daily rate limits and compute-unit budgets per upstream to control usage and cost. Selection policies influence which upstreams serve traffic, for example cheap-first until error-rate or block-lag thresholds are crossed. Consensus and data integrity compare results across upstreams, enforce consensus and penalise nodes that disagree or return stale data. Unified error handling gives consistent, normalised error codes and messages across every provider. Flexible authentication covers basic auth, secrets, JWT and SIWE. Smart batching aggregates multiple RPC or contract calls into one. And observability tracks throughput, errors and latency per upstream from a single Grafana dashboard.

The case studies section links two named results: Moonwell on slashing RPC calls by 67 percent and Chronicle on reducing RPC cost by 45 percent.

Configuration as code and the validate command

The configuration can be TypeScript, JavaScript or YAML, and the repository ships examples of each shape with names that describe their purpose: erpc.dist.ts, erpc.dist.yaml and erpc.svm.example.yaml, plus a separate Design-Multi-Chain-Solana document with a v0.5 revision alongside it.

There is a CLI command for checking a configuration before it goes anywhere near production:

bash
erpc validate erpc.y

It validates a configuration file and reports any errors, warnings or notices, and the README describes it as useful in CI pipelines to catch misconfigurations before deployment. That is the sort of command that is worth more than the proxy itself if you write your config in TypeScript, because a proxy with a bad upstream definition fails in ways that are hard to read from the error alone.

The repository layout is organised around the subsystems, with directories named after them: upstream for the provider clients, failsafe for retries and hedging, consensus for the cross-upstream comparison, auth for the authentication modes, data for the cache, clients, cmd, common, health, indexer, internal, kube, monitoring, plans, scylla, telemetry, test, thirdparty and typescript. There is a specs directory and an architecture directory, and a Dockerfile.fake alongside the main Dockerfile, which is a hint that a fake upstream exists for testing the proxy without spending provider quota.

The TypeScript side is a pnpm workspace with the config package published as @erpc-cloud/config, esbuild for the build, and a Biome configuration for formatting. The package manager is pinned to pnpm 10.28.2, and the Dockerfile explains why in a comment: an unpinned install pulls the newest pnpm, which re-executes itself to the pinned version and fails on alpine.

The infrastructure the compose file expects

The docker-compose.yml in the repository is mostly commented out, which is itself informative about what is optional and what is expected. The erpc service block is commented out in full, along with the volumes mapping an erpc.yaml file.

What is active is the monitoring stack. The monitoring service builds from a local directory and publishes port 3000 for Grafana and 9090 for Prometheus, with a SERVICE_ENDPOINT pointing at host.docker.internal and SERVICE_PORT 4001, which is the metrics port of the eRPC process. The Grafana configuration and dashboards are mounted from the monitoring directory, and Prometheus and Grafana both get named volumes for their data. A redis service runs on 6379 with a restart policy.

The commented blocks are the alternatives a user might want. There is a jaeger block with the collector OTLP enabled and a filter processor with a one second maximum duration, exposing the OTLP gRPC and HTTP ports. There is a postgresql block on 5432, and a scylla block using scylladb/scylla 5.2.18 with the alternator port and write isolation set to unsafe_rmw, which is what makes Scylla usable as a cache backend here.

So the supported backends for the cache are Redis, PostgreSQL and Scylla, and the tracing story is OpenTelemetry rather than a bespoke one, which is why the Go dependencies include the otel packages, the Envoy ratelimit package for the rate limiting.

Where eRPC is the wrong choice

The first limitation is that most teams do not need this. If you have one reliable provider or one self-hosted node, a proxy adds a process, a cache, a configuration language and a failure mode, and the honest answer to the problem is more provider redundancy rather than a smarter layer in front of it. The README itself frames the default setup as ideal for development and testing, and recommends extending the configuration with dedicated premium providers and advanced failover settings for production.

The second is the state. A permanent cache is a database that must be sized, backed up and understood, and the re-org awareness is the feature that makes it worth having and also the feature that makes it subtle to operate. The chain-specific rules mean the right configuration for an EVM chain is not the right configuration for a Solana cluster, and a mistake in the SVM cache settings produces stale reads that look like chain behaviour.

The third is the surface area of the consensus and integrity machinery. Comparing results across upstreams, enforcing consensus and penalising nodes that disagree is genuinely useful against a lying or lagging provider, and it also means the proxy is now making judgements about which upstream is right. A single upstream that everyone else agrees with is a strange position to be in, and the thresholds for when a node is penalised are configuration you own.

The comparison with a plain load balancer or with the retry logic in your own client library is the useful one. eRPC is worth its weight when you are running an indexer that makes thousands of read calls a second across several providers, where upstream rate limits and occasional downtime become the dominant operational problem. Below that scale, the machinery exceeds the need and a simpler setup is the better engineering decision.

Editorial conclusion

eRPC fits a team making many read-heavy RPC calls across several providers and self-hosted nodes, where upstream flakiness and per-provider rate limits are the operational pain. It does not fit a team with a single reliable provider, and it adds a stateful cache and a consensus layer that you then have to operate. Before adopting it, run the validate command against your configuration in CI, decide which upstreams are allowed to serve writes, since the write guards differ between EVM and Solana, and read the SVM commitment documentation carefully because the cache finality rules there are not the EVM rules.

Frequently asked questions

What is eRPC and what does it do?

It is a fault-tolerant RPC proxy written in Go that sits in front of your EVM or Solana upstreams behind a single endpoint, handling failover, retries, circuit breakers, caching and multi-chain routing automatically. It is aimed at read-heavy workloads such as data indexing and high-load frontends.

How do I run eRPC and send it a request?

Run it with `npx start-erpc` or `docker run -p 4000:4000 ghcr.io/erpc/erpc`, then post a normal JSON-RPC call to a path that names the project and the chain, for example `http://localhost:4000/main/evm/42161` for an EVM chain or `http://localhost:4000/main/svm/mainnet-beta` for a Solana cluster.

How does the caching differ between EVM and Solana in eRPC?

Cache finality follows Solana's rules rather than EVM's. Commitment finalized is the latest rooted slot, so only slot-pinned reads such as getBlock and getTransaction at that commitment are cached as immutable, while moving-head state reads such as getBalance and getAccountInfo stay realtime. SVM caching is configured under database.svmJsonRpcCache and its keys are case-preserving because base58 keys and signatures are case-sensitive.

Can eRPC retry a write to a second upstream?

Not on Solana. sendTransaction, sendRawTransaction and requestAirdrop are never auto-dispatched to a second upstream by retry, hedge or probe mirroring. On EVM the opposite holds, since eth_sendRawTransaction is hedgeable, so the write guard behaviour differs between the two families.

What configuration formats does eRPC accept and how do I check it?

Configuration can be TypeScript, JavaScript or YAML, with examples in the repository as erpc.dist.ts, erpc.dist.yaml and erpc.svm.example.yaml. The `erpc validate` command validates a config file and reports errors, warnings or notices, which the README suggests using in CI to catch misconfigurations before deployment.

Official sources

  1. erpc/erpc on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/erpc-erpc.svg)](https://hysenlabs.com/projects/erpc-erpc)

Was this useful?