RPCProxy
Documentation for eth_defi.provider.rpc_proxy.RPCProxy Python class.
- class RPCProxy
Bases:
objectA running JSON-RPC failover proxy instance.
Manages the lifecycle of a background threaded HTTP server that presents a single
http://127.0.0.1:{port}endpoint while internally routing JSON-RPC requests across multiple upstream RPC providers with automatic failover, retry, and per-provider statistics collection.Why this exists
Anvil (and other tools) accept only a single
--fork-urland have no internal retry or failover logic. When the upstream RPC is slow, rate-limited, or temporarily unreachable, Anvil hangs indefinitely — causing downstream callers to timeout (e.g.eth_getTransactionCounttiming out after 90 s).This proxy sits between the consumer and multiple upstream RPCs. If one upstream fails, the proxy transparently switches to the next one and retries, all within a configurable timeout budget. On shutdown it logs per-provider statistics so you can identify flaky or slow providers.
How it works
start_rpc_proxy()allocates a free localhost port and starts aHTTPServer(withThreadingMixIn) on a daemon thread.Every incoming
POSTis forwarded to the currently-active upstream. If the upstream returns a retryable error (as determined by theFailureHandler), the proxy switches to the next upstream and retries — up toDEFAULT_RETRIEStimes.Connection-level failures (timeouts, refused connections) are always retried without consulting the failure handler.
After all retries are exhausted the proxy returns an
HTTP 502with a JSON-RPC error body so the caller can distinguish proxy-level failures from upstream errors.Calling
close()stops the server and logs a per-provider summary tologger.info().
Lifecycle
Created by
start_rpc_proxy(). The proxy runs untilclose()is called. When used withlaunch_anvil(), the lifecycle is automatic: the proxy starts before Anvil and shuts down whenclose()is called.Standalone usage
from eth_defi.provider.rpc_proxy import start_rpc_proxy # Start a proxy backed by two upstream RPCs proxy = start_rpc_proxy( [ "https://eth-mainnet.alchemyapi.io/v2/YOUR_KEY", "https://rpc.ankr.com/eth", ] ) # proxy.url is e.g. "http://127.0.0.1:23456" # Pass it to any tool that accepts a single JSON-RPC URL: # anvil --fork-url {proxy.url} # curl -X POST {proxy.url} -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' # Inspect statistics at any time for name, stats in proxy.get_stats().items(): print(f"{name}: {stats.request_count} requests, {stats.failure_count} failures") # Shut down and see final statistics in the log proxy.close()
With custom parameters
import logging from eth_defi.provider.rpc_proxy import start_rpc_proxy proxy = start_rpc_proxy( rpc_urls=[ "https://rpc-provider-a.example.com", "https://rpc-provider-b.example.com", ], timeout=15.0, # 15 s per upstream attempt retries=5, # try up to 5 times auto_switch_request_count=100, # rotate providers every 100 requests switchover_log_level=logging.WARNING, request_log_level=logging.DEBUG, # dump payloads at DEBUG level log_max_size=4096, # truncate large payloads at 4 KiB )
Automatic integration with Anvil
When
launch_anvil()receives a space-separatedfork_urlcontaining multiple RPC endpoints, it automatically starts anRPCProxyand passesproxy.urlas Anvil’s--fork-url. The proxy’s lifecycle is tied toclose():from eth_defi.provider.anvil import launch_anvil # Space-separated URLs trigger the proxy automatically launch = launch_anvil( fork_url="https://rpc-a.example.com https://rpc-b.example.com", ) # launch.proxy is the RPCProxy instance # ...run your test... launch.close() # stops Anvil, then stops the proxy and logs stats
You can also pass an
RPCProxyyou created yourself, or anRPCProxyConfigto fine-tune settings, via theproxy_multiple_upstreamparameter — seelaunch_anvil()for details.See also
RPCProxyConfig,start_rpc_proxy(),UpstreamRPCProviderStatistics.Attributes summary
nameporturlprovider_statsMethods summary
__init__(name, port, url, provider_stats, ...)close()Shut down the proxy server and log final statistics.
Return per-provider statistics, keyed by display URL.
- __init__(name, port, url, provider_stats, _server_thread, _http_server)
- Parameters
name (str) –
port (int) –
url (str) –
provider_stats (dict[str, eth_defi.provider.rpc_proxy.UpstreamRPCProviderStatistics]) –
_server_thread (threading.Thread) –
_http_server (eth_defi.provider.rpc_proxy._ThreadingHTTPServer) –
- Return type
None
- close()
Shut down the proxy server and log final statistics.
Stops accepting new requests, waits for the server thread to finish, then logs a summary of per-provider statistics at
INFOlevel.- Return type
None
- get_stats()
Return per-provider statistics, keyed by display URL.
- Returns
Dictionary mapping provider display name to its statistics.
- Return type
dict[str, eth_defi.provider.rpc_proxy.UpstreamRPCProviderStatistics]