Dynamic Web3 RPC Handler
Ubiquity · 2024 · Production-grade handler that ranks RPC endpoints by latency, rotates on error via a Proxy-wrapped provider, and ships with dual CJS/ESM + types.
So what?
0 reported during my tenure failures · <=1500 ms selection
0 reported during my tenure failures<=1500 ms selection
Problem
The ecosystem needed a dynamic RPC selection system for reliable Web3 operations. Manual blacklisting caused brittleness and UI flicker. The handler had to automatically detect failing endpoints, rank healthy ones by latency, and switch seamlessly without developer or user intervention.
Approach
- Import Chainlist and maintain
extraRpcs
locally to avoid build conflicts - Probe endpoints with short, bounded timeouts; rank by measured latency; drop failures
- Wrap
JsonRpcProvider
in aProxy
that, on call error, rotates to the next best provider and retries transparently - Cache latencies in the appropriate runtime (browser:
localStorage
, Node: in-memory) with refresh cycles - Ship dual CJS/ESM outputs and
.d.ts
types for first-class TypeScript ergonomics
System diagram
flowchart LR App[Client Application] --> GetInstance[RPCHandler getInstance] GetInstance --> Race[Promise race RPC endpoints] Race --> Rank[Latency ranking] Rank --> Select[JsonRpcProvider selection] Select --> Proxy[Proxy wrapper] Proxy --> Rotate[Error handling with rotation] Rotate --> Response[Response to client]
Outcome
- Deployed across Ubiquity apps with reliable behavior and zero reported failures during my tenure
- Reduced operator toil and removed the need for manual blacklists
- Production-ready packaging: ESM + CJS + types
Proof
Code excerpt — provider proxy (from the implemented handler)
private _createProxy(provider: JsonRpcProvider): JsonRpcProvider {
return new Proxy(provider, {
get: (target, prop) => {
if (typeof (target as any)[prop] === 'function') {
return async (...args: unknown[]) => {
try {
return await (target as any)[prop].apply(target, args);
} catch (error: any) {
// Rotate to next fastest healthy RPC and retry
const nextProvider = await this.getFastestRpcProvider();
return await (nextProvider as any)[prop].apply(nextProvider, args);
}
};
}
return (target as any)[prop];
}
});
}
Example usage — selecting a fastest RPC for a given network (original article)
import { getFastestRpc } from "web3-rpc-speed-racer";
// Supply your Network ID
const rpc = await getFastestRpc(1);
// Use rpc for your web3 provider