This project is a Go-based proxy server designed to distribute Ethereum JSON-RPC requests across multiple RPC endpoints. It acts as a smart load balancer, enhancing reliability and potentially reducing costs by intelligently routing requests.
The Ethereum RPC Pool serves as a central point for all your Ethereum JSON-RPC requests. Instead of directly connecting to a single RPC provider, you send your requests to this proxy. It then forwards these requests to one of several configured RPC endpoints, distributing the load and providing resilience against single-point failures.
Key functionalities include:
- Load Balancing: Distributes incoming JSON-RPC
POST
requests across a list of configured RPC endpoints. - RPC Health Monitoring: Continuously monitors the health and performance of each configured RPC endpoint by periodically fetching the latest block number and measuring response times.
- Intelligent Routing: Currently uses a round-robin algorithm to select the next available RPC. (Future enhancements could include least-latency routing based on collected performance metrics).
- Response Caching: Caches responses for common and frequently requested methods like
eth_blockNumber
andeth_getBlockByNumber
to reduce redundant calls to upstream RPCs and improve response times. - Error Handling: Gracefully handles errors from upstream RPCs and returns them in the standard Ethereum RPC error format.
- Configurable: Easily configured via environment variables for RPC endpoints and server port.
- Containerized: Provided with a
Dockerfile
for easy deployment in containerized environments.
- Initialization: Upon startup, the server reads a comma-separated list of RPC URLs from the
RPC_LIST
environment variable. It initializes the status for each RPC, marking them as offline initially. - Background Monitoring: A background goroutine periodically sends
eth_blockNumber
requests to all configured RPCs. For each RPC, it records:- Whether the RPC is
Online
orOffline
. - The
ResponseTime
(latency) of theeth_blockNumber
request. - The
BlockNumber
returned by the RPC. - A
Timestamp
of the last successful check. This data is stored in an in-memory cache (rpcStatusCache
).
- Whether the RPC is
- Request Handling (
RPCHandler
):- When a client sends a
POST
request to the proxy, theRPCHandler
first checks if the request can be served from the internal cache (e.g.,eth_blockNumber
oreth_getBlockByNumber
). - If the request is cacheable and the data is fresh, the cached response is returned immediately.
- If not cacheable or cache is stale, the handler selects an RPC endpoint using the round-robin algorithm.
- The request is then proxied to the selected upstream RPC.
- The response from the upstream RPC is returned to the client.
- When a client sends a
- Status Endpoint (
/status
): A dedicated/status
endpoint provides a JSON overview of the health and performance of all configured RPCs, including their online status, last fetched block number, and response time in milliseconds.
- Go 1.19 or later
- Docker (for containerized deployment)
RPC_LIST
: A comma-separated list of RPC endpoints (e.g.,http://rpc1.example.com,http://rpc2.example.com
). Required.PORT
: The port on which the server will listen (default is8080
).BLOCK_NUMBER_FETCH_INTERVAL_SECONDS
: Interval in seconds for background RPC health checks (default is10
).
-
Clone the repository:
git clone https://github.com/itxtoledo/ethereum-rpc-pool.git cd ethereum-rpc-pool
-
Set the environment variables (you can use a
.env
file or export them directly):export RPC_LIST="https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID,https://eth-mainnet.alchemyapi.io/v2/YOUR_ALCHEMY_API_KEY" export PORT=8080 export BLOCK_NUMBER_FETCH_INTERVAL_SECONDS=5
-
Run the application:
go run .
-
Build the Docker image:
docker build -t ethereum-rpc-pool .
-
Run the Docker container:
docker run -d -p 8080:8080 -e RPC_LIST="https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID,https://eth-mainnet.alchemyapi.io/v2/YOUR_ALCHEMY_API_KEY" -e PORT=8080 ethereum-rpc-pool
- Click the "Deploy on Railway" button above
- Set the required environment variable
RPC_LIST
with your comma-separated RPC endpoints - Deploy and your Ethereum RPC Pool will be live
Once the server is running, you can send standard Ethereum JSON-RPC POST
requests to its address.
Example POST request (fetching the latest block number):
curl -X POST http://localhost:8080/ -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
You can check the real-time status and performance metrics of each configured RPC by sending a GET
request to the /status
endpoint.
curl http://localhost:8080/status
This will return a JSON object with the status of each RPC, including its online status, the latest block number it reported, and its response time in milliseconds.
The project is organized into several key directories and files:
main.go
: The application's entry point. It handles environment variable loading, initializes RPC status, sets up HTTP routes (/
for RPC proxying and/status
for health checks), and starts the HTTP server.handlers/
: Contains the HTTP handler functions for the RPC proxy and status endpoint.rpc.go
: Implements the core logic for proxying JSON-RPC requests, including cache handling foreth_blockNumber
andeth_getBlockByNumber
, and forwarding requests to upstream RPCs.cache.go
: Manages the in-memory cache for RPC status (online/offline, response time, block number) and block data. It provides functions to set and retrieve RPC and block cache information.error.go
: Provides utility functions for sending standardized JSON-RPC error responses.
utils/
: Contains utility functions used across the project.round_robin.go
: Implements the round-robin algorithm for selecting the next RPC endpoint from the configured list.
.github/workflows/on_pr.yaml
: GitHub Actions workflow configuration for Continuous Integration (CI), running tests and linting on Pull Requests.Dockerfile
: Defines the multi-stage Docker build process for creating a lightweight container image of the application..env.example
: An example file demonstrating the required environment variables.go.mod
&go.sum
: Go module definition and dependency checksums.LICENSE
: The project's license (MIT License).
Contributions are welcome! Please feel free to open issues or submit pull requests.
- Fork the repository.
- Create a new branch (
git checkout -b feature-branch
). - Commit your changes (
git commit -am 'Add some feature'
). - Push to the branch (
git push origin feature-branch
). - Create a new Pull Request.
This project is licensed under the MIT License. See the LICENSE file for details.