ethers-rpc-pool 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sergei Karasev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,283 @@
1
+ # ethers-rpc-pool
2
+
3
+ Multi-endpoint RPC pool provider for **ethers.js** with built-in load balancing, per-endpoint concurrency limits, retry with exponential backoff, and instrumentation.
4
+
5
+ Designed for production backends and dApps that need:
6
+
7
+ - Better reliability than a single RPC endpoint
8
+ - Protection against rate limits (429) and timeouts
9
+ - Controlled concurrency per RPC
10
+ - Automatic failover between endpoints
11
+ - Observability via structured RPC events
12
+
13
+ ---
14
+
15
+ ## Why ethers-rpc-pool?
16
+
17
+ Most production apps rely on a single RPC provider. This creates:
18
+
19
+ - Single point of failure
20
+ - Hard rate limits (RPS / in-flight)
21
+ - Increased timeout risk during traffic spikes
22
+ - Cascading retry storms
23
+
24
+ `ethers-rpc-pool` solves this by introducing:
25
+
26
+ - Multi-provider routing
27
+ - Per-endpoint concurrency limiting
28
+ - Intelligent failover
29
+ - Retry with exponential backoff + jitter
30
+ - Built-in request instrumentation
31
+
32
+ ---
33
+
34
+ ## Features
35
+
36
+ - 🔀 Load balancing across multiple RPC endpoints
37
+ - 🚦 Per-endpoint concurrency limit (`inFlight`)
38
+ - 🔁 Retry with exponential backoff and jitter
39
+ - ⚡ Automatic failover on retryable errors
40
+ - 📊 Built-in request statistics
41
+ - 🧩 Drop-in replacement for `JsonRpcProvider`
42
+
43
+ ---
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ npm install ethers-rpc-pool
49
+ ```
50
+
51
+ ---
52
+
53
+ ## Quick Start
54
+
55
+ ```ts
56
+ import { RPCPoolProvider } from 'ethers-rpc-pool';
57
+
58
+ const pool = new RPCPoolProvider({
59
+ chainId: 1,
60
+ urls: ['http://rpc1.invalid', 'http://rpc2.invalid'],
61
+ perUrl: { inFlight: 1 },
62
+ retry: { attempts: 2 },
63
+ });
64
+
65
+ // Use it like a regular `JsonRpcProvider`:
66
+
67
+ const blockNumber = await pool.getBlockNumber();
68
+ const balance = await pool.getBalance('0x...');
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Configuration
74
+
75
+ ### RPCPoolProviderParams
76
+
77
+ ```ts
78
+ interface RPCPoolProviderParams {
79
+ chainId: number;
80
+ urls: string[];
81
+ perUrl: {
82
+ inFlight: number;
83
+ };
84
+ retry: {
85
+ attempts: number;
86
+ };
87
+ hooks?: {
88
+ onEvent(e: RpcEvent): void;
89
+ };
90
+ }
91
+ ```
92
+
93
+ ### Options Explained
94
+
95
+ | Option | Description |
96
+ | ----------------- | ----------------------------------------- |
97
+ | `chainId` | Target chain ID |
98
+ | `urls` | List of RPC endpoints |
99
+ | `perUrl.inFlight` | Max concurrent requests per endpoint |
100
+ | `retry.attempts` | Maximum number of unique endpoints to try |
101
+ | `hooks.onEvent` | Optional instrumentation hook |
102
+
103
+ ---
104
+
105
+ ## How It Works
106
+
107
+ ### 1. Routing
108
+
109
+ Requests are routed through an internal `Router`, which selects an available endpoint.
110
+
111
+ ### 2. Concurrency Control
112
+
113
+ Each endpoint has its own semaphore limiter:
114
+
115
+ ```ts
116
+ perUrl: {
117
+ inFlight: number;
118
+ }
119
+ ```
120
+
121
+ This prevents:
122
+
123
+ - Overloading a single RPC
124
+ - Triggering provider-side throttling
125
+ - Self-induced retry storms
126
+
127
+ ### 3. Retry Strategy
128
+
129
+ If a retryable error occurs:
130
+
131
+ - A different endpoint is selected
132
+ - Exponential backoff is applied
133
+ - Jitter is added to prevent synchronization spikes
134
+
135
+ Example retry timing:
136
+
137
+ ```
138
+ Attempt 1 → immediate
139
+ Attempt 2 → random(0..1000ms)
140
+ Attempt 3 → random(0..2000ms)
141
+ ...
142
+ ```
143
+
144
+ Retries only happen on errors considered failover-safe.
145
+
146
+ ---
147
+
148
+ ## Instrumentation & Metrics
149
+
150
+ You can subscribe to RPC lifecycle events:
151
+
152
+ ```typescript
153
+ const pool = new RPCPoolProvider({
154
+ // ...
155
+ hooks: {
156
+ onEvent(event) {
157
+ console.log(event);
158
+ },
159
+ },
160
+ });
161
+ ```
162
+
163
+ This allows integration with:
164
+
165
+ - Prometheus
166
+ - OpenTelemetry
167
+ - Custom logging pipelines
168
+
169
+ ### Access Stats Snapshot
170
+
171
+ ```ts
172
+ const stats = pool.getStats();
173
+ console.log(status.snapshot());
174
+ ```
175
+
176
+ ### Example output:
177
+ ```json
178
+ {
179
+ "total": 105,
180
+ "inFlight": 0,
181
+ "perMethodTotal": {
182
+ "eth_getBlockByNumber": 1,
183
+ "eth_gasPrice": 1,
184
+ "eth_maxPriorityFeePerGas": 1,
185
+ "eth_chainId": 1,
186
+ "eth_blockNumber": 101
187
+ },
188
+ "rateLimitedTotal": 0,
189
+ "timeoutTotal": 0,
190
+ "perProviderRateLimited": {},
191
+ "perProviderTimeout": {},
192
+ "providerCooldownUntil": {},
193
+ "perProviderInFlight": {
194
+ "rpc#1-chainId:1-https://eth.drpc.org": 0,
195
+ "rpc#2-chainId:1-https://eth1.lava.build": 0,
196
+ "rpc#3-chainId:1-https://rpc.mevblocker.io": 0,
197
+ "rpc#4-chainId:1-https://eth.blockrazor.xyz": 0,
198
+ "rpc#5-chainId:1-https://public-eth.nownodes.io": 0
199
+ },
200
+ "perProviderTotal": {
201
+ "rpc#1-chainId:1-https://eth.drpc.org": 21,
202
+ "rpc#2-chainId:1-https://eth1.lava.build": 21,
203
+ "rpc#3-chainId:1-https://rpc.mevblocker.io": 21,
204
+ "rpc#4-chainId:1-https://eth.blockrazor.xyz": 21,
205
+ "rpc#5-chainId:1-https://public-eth.nownodes.io": 21
206
+ }
207
+ }
208
+ ```
209
+
210
+ Useful for:
211
+
212
+ - Request counters
213
+ - Per-method stats
214
+ - Per-provider metrics
215
+ - Timeout tracking
216
+ - Rate limit detection
217
+
218
+ ---
219
+
220
+ ## Production Considerations
221
+
222
+ ### Recommended Settings
223
+
224
+ - `inFlight`: 1–2 depending on rpc provider limits
225
+ - `retry.attempts`: 2–3
226
+ - Use at least 2–3 independent RPC providers
227
+
228
+ ### Known Limitations
229
+
230
+ - No circuit breaker yet
231
+ - No sticky session/blockTag consistency yet
232
+ - No built-in JSON-RPC batching
233
+ - Archive/debug/trace methods depend on underlying RPC support
234
+
235
+ ---
236
+
237
+ ## When To Use
238
+
239
+ Good fit for:
240
+
241
+ - Backend services aggregating on-chain data
242
+ - dApps with moderate traffic
243
+ - Systems using free-tier RPC plans
244
+ - Environments needing failover protection
245
+
246
+ Not intended for:
247
+
248
+ - High-frequency trading systems
249
+ - Archive-heavy indexing pipelines
250
+ - Trace/debug intensive workloads
251
+
252
+ ---
253
+
254
+ ## Example Architecture
255
+
256
+ ```
257
+ ┌──────────────┐
258
+ │ Application │
259
+ └──────┬───────┘
260
+
261
+ ┌───────▼────────┐
262
+ │ RPCPoolProvider │
263
+ └───────┬────────┘
264
+ ┌───────────────┼────────────────┐
265
+ ▼ ▼ ▼
266
+ RPC Endpoint 1 RPC Endpoint 2 RPC Endpoint 3
267
+ ```
268
+
269
+ ---
270
+
271
+ ## Roadmap
272
+
273
+ - Circuit breaker + health scoring
274
+ - Sticky session / blockTag consistency
275
+ - Adaptive latency-based routing
276
+ - JSON-RPC batch support
277
+ - Singleflight request deduplication
278
+
279
+ ---
280
+
281
+ ## License
282
+
283
+ MIT