ethers-rpc-pool 1.1.4 → 3.2.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/README.md +336 -89
- package/dist/index.cjs +282 -143
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -71
- package/dist/index.d.ts +57 -71
- package/dist/index.js +282 -143
- package/dist/index.js.map +1 -1
- package/package.json +13 -3
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/RpcPoolProvider.ts","../src/Stats.ts","../src/utils.ts","../src/InstrumentedProvider.ts","../src/Semaphore.ts","../src/RpsLimiter.ts","../src/Router.ts"],"sourcesContent":["export { RPCPoolProvider, RPCPoolProviderParams } from './RpcPoolProvider';\nexport type { RpcEvent } from './utils';\nexport type { RpcStatsSnapshot } from './Stats';\n","import { FetchRequest, JsonRpcProvider, Network, Networkish } from 'ethers';\nimport { Stats } from './Stats';\nimport { Endpoint, isRpcLogicalError, RpcEvent, shouldFailover } from './utils';\nimport {\n InstrumentedJsonRpcProvider,\n InstrumentedJsonRpcProviderOptions,\n} from './InstrumentedProvider';\nimport { Router } from './Router';\n\ninterface RPCPoolProviderOptions extends Partial<InstrumentedJsonRpcProviderOptions> {\n url: string | FetchRequest;\n network?: Networkish;\n}\n\nexport interface RPCPoolProviderParams {\n network: Networkish;\n rpc: RPCPoolProviderOptions[];\n defaultRpcOptions: { inFlight: number; timeout?: number; rps?: number; rpsBurst?: number };\n retry: { attempts: number };\n hooks?: {\n onEvent(e: RpcEvent): void;\n };\n}\n\n// TODO\n// -- circuit breaker + health checks\n// -- sticky “session”\n\nexport class RPCPoolProvider extends JsonRpcProvider {\n readonly router: Router;\n readonly params: RPCPoolProviderParams;\n readonly stats: Stats;\n\n constructor(params: RPCPoolProviderParams) {\n const network = Network.from(params.network);\n super('http://localhost', network, { staticNetwork: network });\n\n this.params = params;\n\n this.stats = new Stats();\n\n const endpoints: Endpoint[] = this.params.rpc.map((options, i) => {\n const url = typeof options.url === 'string' ? options.url : options.url.url;\n const providerId = `rpc#${i + 1}-chainId:${this.params.network}-${url}`;\n\n const provider = new InstrumentedJsonRpcProvider(options.url, this.params.network, {\n providerId,\n stats: this.stats,\n ...this.params.defaultRpcOptions,\n ...options,\n onEvent: this.params.hooks?.onEvent,\n });\n\n return { providerId, url, provider };\n });\n\n this.router = new Router(endpoints, this.stats);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async send(method: string, params: any): Promise<any> {\n const tried = new Set<string>();\n const maxAttempts = this.params.retry.attempts;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n let endpoint = this.router.pick();\n\n // first, try to pick an endpoint that hasn't been tried yet\n if (tried.size < this.router.size()) {\n let retries = 0;\n\n while (tried.has(endpoint.providerId) && retries < this.router.size()) {\n endpoint = this.router.pick();\n retries++;\n }\n }\n\n tried.add(endpoint.providerId);\n\n const startedAt = Date.now();\n try {\n return await endpoint.provider.send(method, params);\n } catch (e: any) {\n if (isRpcLogicalError(e)) {\n this.emitRpcLogicalError(endpoint, method, startedAt, e);\n }\n if (!shouldFailover(e)) throw e;\n if (attempt === maxAttempts - 1) throw e;\n\n await this.sleepWithBackoff(attempt);\n }\n }\n\n throw new Error('No RPC available');\n }\n\n private async sleepWithBackoff(attempt: number): Promise<void> {\n const baseDelay = Math.min(1000 * 2 ** attempt, 5000);\n const jitter = Math.random() * baseDelay;\n\n await new Promise((resolve) => setTimeout(resolve, jitter));\n }\n\n private emitRpcLogicalError(ep: Endpoint, method: string, startedAt: number, error: any): void {\n const endedAt = Date.now();\n\n this.stats.bumpRpcError(ep.providerId, method);\n\n this.params.hooks?.onEvent?.({\n type: 'error',\n chainId: BigInt(Network.from(this.params.network).chainId),\n providerId: ep.providerId,\n method,\n startedAt,\n endedAt,\n ms: endedAt - startedAt,\n isRateLimit: false,\n isTimeout: false,\n status: undefined,\n code: error?.code,\n message: String(error?.message || error),\n errorKind: 'rpc',\n });\n }\n\n getStats(): Stats {\n return this.stats;\n }\n}\n","export interface RpcStatsSnapshot {\n total: number;\n inFlight: number;\n perMethodTotal: Record<string, number>;\n rateLimitedTotal: number;\n perProviderRateLimited: Record<string, number>;\n timeoutTotal: number;\n perProviderTimeout: Record<string, number>;\n perProviderTotal: Record<string, number>;\n providerCooldownUntil: Record<string, number>;\n perProviderInFlight: Record<string, number>;\n perProviderError: Record<string, number>;\n\n rpcErrorTotal: number;\n perProviderRpcError: Record<string, Record<string, number>>;\n perMethodRpcError: Record<string, number>;\n perProviderMethod: Record<string, Record<string, number>>;\n}\n\nexport class Stats {\n private _total = 0;\n private _inFlight = 0;\n\n private _perMethod: Record<string, number> = {};\n private _perProviderMethod: Record<string, Record<string, number>> = {};\n\n private _rateLimitedTotal = 0;\n private _timeoutTotal = 0;\n private _rpcErrorTotal = 0;\n\n private _perProviderInFlight: Record<string, number> = {};\n private _perProviderTotal: Record<string, number> = {};\n private _perProviderTimeout: Record<string, number> = {};\n private _perProviderRateLimited: Record<string, number> = {};\n private _perProviderError: Record<string, number> = {};\n private _perProviderRpcError: Record<string, Record<string, number>> = {};\n private _perMethodRpcError: Record<string, number> = {};\n\n private _providerCooldownUntil: Record<string, number> = {};\n\n private _bump(map: Record<string, number>, key: string) {\n map[key] = (map[key] || 0) + 1;\n }\n\n private _decrease(map: Record<string, number>, key: string) {\n map[key] = Math.max((map[key] || 0) - 1, 0);\n }\n\n private _bumpTotal() {\n this._total++;\n }\n\n private _bumpInFlight() {\n this._inFlight++;\n }\n\n private _bumpRateLimitedTotal() {\n this._rateLimitedTotal++;\n }\n\n private _bumpTimeoutTotal() {\n this._timeoutTotal++;\n }\n\n private _bumpRpcErrorTotal() {\n this._rpcErrorTotal++;\n }\n\n bumpInFlightPerProvider(id: string) {\n this._bumpInFlight();\n this._bump(this._perProviderInFlight, id);\n }\n\n decreaseInFlightPerProvider(id: string) {\n this.decreaseInFlight();\n this._decrease(this._perProviderInFlight, id);\n }\n\n decreaseInFlight() {\n this._inFlight = Math.max(this._inFlight - 1, 0);\n }\n\n bumpPerMethod(id: string, method: string) {\n this._bump(this._perMethod, method);\n if (!this._perProviderMethod[id]) {\n this._perProviderMethod[id] = {};\n }\n this._bump(this._perProviderMethod[id], method);\n }\n\n bumpRateLimitedPerProvider(id: string) {\n this._bumpRateLimitedTotal();\n this._bump(this._perProviderRateLimited, id);\n }\n\n bumpTimeoutPerProvider(id: string) {\n this._bumpTimeoutTotal();\n this._bump(this._perProviderTimeout, id);\n }\n\n bumpProviderTotal(id: string) {\n this._bumpTotal();\n this._perProviderTotal[id] = (this._perProviderTotal[id] || 0) + 1;\n }\n\n bumpServerErrorPerProvider(id: string) {\n this._bump(this._perProviderError, id);\n }\n\n bumpRpcError(providerId: string, method: string) {\n this._bumpRpcErrorTotal();\n\n if (!this._perProviderRpcError[providerId]) {\n this._perProviderRpcError[providerId] = {};\n }\n this._bump(this._perProviderRpcError[providerId], method);\n this._bump(this._perMethodRpcError, method);\n }\n\n timeoutRatio(id: string) {\n const t = this._perProviderTimeout[id] || 0;\n const n = this._perProviderTotal[id] || 0;\n return n ? t / n : 0;\n }\n\n isInCooldown(id: string) {\n return (this._providerCooldownUntil[id] || 0) > Date.now();\n }\n\n setCooldown(id: string, ms: number) {\n this._providerCooldownUntil[id] = Date.now() + ms;\n }\n\n snapshot(): Readonly<RpcStatsSnapshot> {\n return {\n total: this._total,\n inFlight: this._inFlight,\n perMethodTotal: { ...this._perMethod },\n rateLimitedTotal: this._rateLimitedTotal,\n timeoutTotal: this._timeoutTotal,\n rpcErrorTotal: this._rpcErrorTotal,\n perProviderMethod: { ...this._perProviderMethod },\n perProviderInFlight: { ...this._perProviderInFlight },\n perProviderRateLimited: { ...this._perProviderRateLimited },\n perProviderTimeout: { ...this._perProviderTimeout },\n perProviderError: { ...this._perProviderError },\n perProviderRpcError: { ...this._perProviderRpcError },\n perMethodRpcError: { ...this._perMethodRpcError },\n perProviderTotal: { ...this._perProviderTotal },\n providerCooldownUntil: { ...this._providerCooldownUntil },\n };\n }\n}\n","import { InstrumentedJsonRpcProvider } from './InstrumentedProvider';\n\nexport interface Endpoint {\n providerId: string;\n url: string;\n provider: InstrumentedJsonRpcProvider;\n}\n\nexport type RpcEvent =\n | {\n type: 'request';\n chainId: bigint;\n providerId: string;\n method: string;\n startedAt: number;\n }\n | {\n type: 'response';\n chainId: bigint;\n providerId: string;\n method: string;\n startedAt: number;\n endedAt: number;\n ms: number;\n }\n | {\n type: 'error';\n chainId: bigint;\n providerId: string;\n method: string;\n startedAt: number;\n endedAt: number;\n ms: number;\n isRateLimit: boolean;\n isTimeout: boolean;\n status?: number;\n code?: string;\n message: string;\n errorKind?: 'transport' | 'rpc';\n };\n\nexport function getHttpStatus(e: any): number | undefined {\n return (\n e?.status ??\n e?.response?.status ??\n e?.response?.statusCode ??\n e?.error?.status ??\n e?.error?.response?.status ??\n e?.body?.statusCode // sometimes present\n );\n}\n\nexport function isRateLimitError(e: any): boolean {\n const status = getHttpStatus(e);\n if (status === 429 || status === 402) return true;\n\n const msg = String(e?.message || e);\n if (/error code:\\s*1015/i.test(msg)) return true; // Cloudflare\n return /rate limit|too many requests|429|quota|throttl/i.test(msg);\n}\n\nexport function isServerError(e: any): boolean {\n const status = getHttpStatus(e);\n return status !== undefined && status >= 500;\n}\n\nexport function getRetryAfterMs(e: any): number | null {\n const ra =\n e?.response?.headers?.get?.('retry-after') ??\n e?.response?.headers?.['retry-after'] ??\n e?.headers?.['retry-after'];\n const n = Number(ra);\n return Number.isFinite(n) ? n * 1000 : null;\n}\n\nexport function isTimeoutError(e: any): boolean {\n // ethers v5\n if (e?.code === 'TIMEOUT') return true;\n\n const status = getHttpStatus(e);\n // some RPCs / proxies return 504 on timeout\n if (status === 504) return true;\n\n const msg = String(e?.message || e);\n\n // node-fetch / undici / axios / nginx / generic\n return /timeout|timed out|ETIMEDOUT|ESOCKETTIMEDOUT|ECONNABORTED|504 Gateway/i.test(msg);\n}\n\nexport function isTransportError(e: any): boolean {\n return isTimeoutError(e) || isRateLimitError(e) || isServerError(e);\n}\n\n/**\n * JSON-RPC / application-level error:\n * the RPC responded, but the call itself failed logically.\n *\n * Examples:\n * - getLogs block range too large\n * - invalid params\n * - execution reverted\n * - method not supported\n */\nexport function isRpcLogicalError(e: any): boolean {\n if (isTransportError(e)) return false;\n\n // ethers often preserves code/message for JSON-RPC errors\n if (e?.error?.code !== undefined) return true;\n if (e?.code === 'CALL_EXCEPTION') return true;\n if (e?.code === 'UNKNOWN_ERROR' && e?.error) return true;\n\n const msg = String(e?.message || e);\n\n return /execution reverted|invalid params|method not found|method not supported|block range|too many blocks|getLogs/i.test(\n msg,\n );\n}\n\nexport function shouldFailover(e: any): boolean {\n const to = isTimeoutError(e);\n const rl = isRateLimitError(e);\n const se = isServerError(e);\n\n // failover on timeouts and rate limits, but not on logical errors (e.g. invalid params)\n return to || rl || se;\n}\n","import {\n JsonRpcProvider,\n Network,\n JsonRpcPayload,\n FetchRequest,\n JsonRpcApiProviderOptions,\n Networkish,\n} from 'ethers';\nimport { Semaphore } from './Semaphore';\nimport { Stats } from './Stats';\nimport {\n getHttpStatus,\n getRetryAfterMs,\n isRateLimitError,\n isServerError,\n isTimeoutError,\n RpcEvent,\n} from './utils';\nimport { RpsLimiter } from './RpsLimiter';\n\nexport interface InstrumentedJsonRpcProviderOptions extends JsonRpcApiProviderOptions {\n providerId: string;\n stats: Stats;\n inFlight?: number;\n timeout?: number;\n rps?: number;\n rpsBurst?: number;\n onEvent?: (e: RpcEvent) => void;\n}\n/**\n * Instrumented JsonRpcProvider.\n * Tracks requests, inFlight count, rate limits, and per-method / per-provider metrics.\n */\nexport class InstrumentedJsonRpcProvider extends JsonRpcProvider {\n readonly providerId: string;\n readonly chainId: bigint;\n readonly options: InstrumentedJsonRpcProviderOptions;\n\n readonly inFlightLimiter: Semaphore;\n readonly rpsLimiter: RpsLimiter;\n readonly stats: Stats;\n readonly fetchRequest: FetchRequest;\n\n private lastCooldownMs: number = 0;\n\n constructor(\n url: string | FetchRequest,\n network: Networkish,\n options: InstrumentedJsonRpcProviderOptions,\n ) {\n let fetchRequest: FetchRequest;\n\n if (typeof url == 'string') {\n fetchRequest = new FetchRequest(url);\n } else {\n fetchRequest = url;\n }\n\n fetchRequest.timeout = options.timeout || 10_000;\n\n const _network = Network.from(network);\n super(fetchRequest, _network, { staticNetwork: true, ...options });\n this.fetchRequest = fetchRequest;\n this.providerId = options.providerId;\n this.chainId = _network.chainId;\n this.options = options;\n\n const { rps = 10, rpsBurst, inFlight = 1 } = options;\n\n this.inFlightLimiter = new Semaphore(inFlight);\n this.rpsLimiter = new RpsLimiter(rps, rpsBurst || rps);\n this.stats = options.stats;\n }\n\n override async _send(payload: JsonRpcPayload | JsonRpcPayload[]): Promise<any> {\n await this.rpsLimiter.take(1);\n\n const release = this.inFlightLimiter ? await this.inFlightLimiter.acquire() : undefined;\n\n try {\n return await this._sendInstrumented(payload);\n } finally {\n release?.();\n }\n }\n\n isAvailable(count = 1): boolean {\n return this.rpsLimiter.isAvailable(count) && this.inFlightLimiter.isAvailable();\n }\n\n // ethers v5 calls send(method, params)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private async _sendInstrumented(payload: JsonRpcPayload | JsonRpcPayload[]): Promise<any> {\n const startedAt = Date.now();\n const payloads = Array.isArray(payload) ? payload : [payload];\n\n this.stats.bumpInFlightPerProvider(this.providerId);\n this.stats.bumpProviderTotal(this.providerId);\n\n for (const p of payloads) {\n this.stats.bumpPerMethod(this.providerId, p.method);\n this.options.onEvent?.({\n type: 'request',\n chainId: this.chainId,\n providerId: this.providerId,\n method: p.method,\n startedAt,\n });\n }\n\n try {\n const res = await super._send(payload);\n\n const endedAt = Date.now();\n\n for (const p of payloads) {\n this.options.onEvent?.({\n type: 'response',\n chainId: this.chainId,\n providerId: this.providerId,\n method: p.method,\n startedAt,\n endedAt,\n ms: endedAt - startedAt,\n });\n }\n\n this.lastCooldownMs = 0;\n\n return res;\n } catch (e: any) {\n const endedAt = Date.now();\n const rl = isRateLimitError(e);\n if (rl) {\n this.stats.bumpRateLimitedPerProvider(this.providerId);\n const cooldownMs = 10_000;\n const raMs = getRetryAfterMs(e) ?? cooldownMs;\n this.stats.setCooldown(this.providerId, raMs);\n }\n\n const isTimeout = isTimeoutError(e);\n if (isTimeout && !rl) {\n this.stats.bumpTimeoutPerProvider(this.providerId);\n\n const n = this.stats.snapshot().perProviderTotal[this.providerId] || 0;\n const ratio = this.stats.timeoutRatio(this.providerId);\n\n // thresholds: do not ban on a single timeout, only after enough data\n if (n >= 50 && ratio >= 0.2) {\n const cooldownMs = ratio >= 0.5 ? 600_000 : 60_000;\n const raMs = getRetryAfterMs(e) ?? cooldownMs;\n this.lastCooldownMs = raMs;\n this.stats.setCooldown(this.providerId, raMs + Math.floor(Math.random() * 1000));\n }\n }\n\n const isError = isServerError(e);\n if (isError && !rl && !isTimeout) {\n this.stats.bumpServerErrorPerProvider(this.providerId);\n const cooldownMs = (this.lastCooldownMs * 2 || 10_000) + Math.floor(Math.random() * 1000);\n this.lastCooldownMs = cooldownMs;\n this.stats.setCooldown(this.providerId, cooldownMs);\n }\n\n for (const p of payloads) {\n this.options.onEvent?.({\n type: 'error',\n chainId: this.chainId,\n providerId: this.providerId,\n method: p.method,\n startedAt,\n endedAt,\n ms: endedAt - startedAt,\n isRateLimit: rl,\n isTimeout: isTimeout,\n status: getHttpStatus(e),\n code: e?.code,\n message: String(e?.message || e),\n errorKind: 'transport',\n });\n }\n\n throw e;\n } finally {\n this.stats.decreaseInFlightPerProvider(this.providerId);\n }\n }\n}\n","export class Semaphore {\n private inUse = 0;\n private queue: Array<() => void> = [];\n\n constructor(private readonly max: number) {\n if (!Number.isFinite(max) || max <= 0) {\n throw new Error(`Semaphore max must be a positive number, got: ${max}`);\n }\n }\n\n isAvailable(): boolean {\n return this.inUse < this.max;\n }\n\n async acquire(): Promise<() => void> {\n if (this.inUse < this.max) {\n this.inUse++;\n let released = false;\n return () => {\n if (released) return;\n released = true;\n this.release();\n };\n }\n\n return new Promise<() => void>((resolve) => {\n this.queue.push(() => {\n this.inUse++;\n let released = false;\n resolve(() => {\n if (released) return;\n released = true;\n this.release();\n });\n });\n });\n }\n\n private release() {\n this.inUse = Math.max(0, this.inUse - 1);\n const next = this.queue.shift();\n if (next) next();\n }\n}\n","export class RpsLimiter {\n // Current number of tokens in the bucket (can be fractional)\n private tokens: number;\n\n // Time of last token refill, in ms\n private lastRefill = Date.now();\n\n constructor(\n // rps: how many tokens we add per second\n private readonly rps: number,\n // burst: maximum bucket capacity.\n // Default: >=1 and approximately equal to rps (to allow a small burst)\n private readonly burst: number = Math.max(1, Math.ceil(rps)),\n ) {\n // At start, the bucket is full: can make burst requests immediately\n this.tokens = burst;\n }\n\n // Refill tokens according to elapsed time\n private refill(now: number) {\n // rps<=0 means \"limit is disabled\"\n if (this.rps <= 0) return;\n\n const elapsed = now - this.lastRefill; // ms since last refill\n if (elapsed <= 0) return;\n\n // How many tokens to add:\n // elapsed/1000 = seconds, multiply by rps\n const add = (elapsed / 1000) * this.rps;\n\n // Add tokens, but don't exceed burst (bucket capacity)\n this.tokens = Math.min(this.burst, this.tokens + add);\n\n // Remember that we refilled tokens at time now\n this.lastRefill = now;\n }\n\n isAvailable(count = 1): boolean {\n if (!this.rps || this.rps <= 0) return true;\n\n const now = Date.now();\n this.refill(now);\n\n return this.tokens >= count;\n }\n\n // Take count tokens (usually 1 request = 1 token).\n // If not enough tokens — wait and try again.\n async take(count = 1): Promise<void> {\n if (!this.rps || this.rps <= 0) return;\n\n while (true) {\n const now = Date.now();\n\n // Before attempting — refill tokens\n this.refill(now);\n\n // If enough tokens — \"pay\" for the request and exit\n if (this.tokens >= count) {\n this.tokens -= count;\n return;\n }\n\n // Not enough tokens: calculate how long to wait\n const need = count - this.tokens;\n\n // How many ms needed to accumulate need tokens:\n // need / rps = seconds, *1000 = ms\n const waitMs = Math.ceil((need / this.rps) * 1000);\n\n // Wait in chunks (not all waitMs at once), to:\n // - not sleep too long if time/state changed\n // - be more resilient to timer drift\n await new Promise((r) => setTimeout(r, Math.min(waitMs, 50)));\n }\n }\n}\n","import { Endpoint } from './utils';\nimport { Stats } from './Stats';\n\nexport class Router {\n private rr = 0;\n\n constructor(\n private readonly endpoints: Endpoint[],\n private readonly stats: Stats,\n ) {}\n\n size(): number {\n return this.endpoints.length;\n }\n\n pick(): Endpoint {\n const n = this.endpoints.length;\n for (let k = 0; k < n; k++) {\n const i = ((this.rr++ % n) + n) % n;\n const ep = this.endpoints[i];\n\n if (!this.stats.isInCooldown(ep.providerId) && ep.provider.isAvailable(1)) return ep;\n }\n // if all are in cooldown, return the next one in round-robin order\n return this.endpoints[((this.rr++ % n) + n) % n];\n }\n}\n"],"mappings":"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","names":["import_ethers"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/RpcPoolProvider.ts","../src/Stats.ts","../src/utils.ts","../src/InstrumentedProvider.ts","../src/Semaphore.ts","../src/RpsLimiter.ts","../src/Router.ts","../src/CooldownManager.ts"],"sourcesContent":["export { RPCPoolProvider, RPCPoolProviderParams, RPCPoolProviderOptions } from './RpcPoolProvider';\nexport type { RpcEvent } from './utils';\nexport type { RpcStatsSnapshot } from './Stats';\nexport type { CircuitState } from './CooldownManager';\n","import { FetchRequest, JsonRpcProvider, Network, Networkish } from 'ethers';\nimport { Stats, RpcStatsSnapshot } from './Stats';\nimport { Endpoint, isRpcLogicalError, RpcEvent, shouldFailover } from './utils';\nimport {\n InstrumentedJsonRpcProvider,\n InstrumentedJsonRpcProviderOptions,\n} from './InstrumentedProvider';\nimport { Router } from './Router';\nimport { CooldownManager } from './CooldownManager';\n\nexport interface RPCPoolProviderOptions extends Partial<InstrumentedJsonRpcProviderOptions> {\n url: string | FetchRequest;\n network?: Networkish;\n priority?: number;\n}\n\nexport interface RPCPoolProviderParams {\n network: Networkish;\n rpc: RPCPoolProviderOptions[];\n defaultRpcOptions: { inFlight: number; timeout?: number; rps?: number; rpsBurst?: number };\n retry: { attempts: number };\n hooks?: {\n onEvent(e: RpcEvent): void;\n };\n healthProbe?: {\n intervalMs?: number;\n };\n}\n\nexport class RPCPoolProvider extends JsonRpcProvider {\n readonly router: Router;\n readonly params: RPCPoolProviderParams;\n private readonly _stats: Stats;\n private readonly _cooldown: CooldownManager;\n private readonly _endpoints: ReadonlyArray<{ id: string; provider: InstrumentedJsonRpcProvider }>;\n private _probeInterval: ReturnType<typeof setInterval> | undefined;\n\n constructor(params: RPCPoolProviderParams) {\n const network = Network.from(params.network);\n super('http://localhost', network, { staticNetwork: network });\n\n this.params = params;\n this._stats = new Stats();\n this._cooldown = new CooldownManager();\n\n const endpoints: Array<{ id: string; provider: InstrumentedJsonRpcProvider }> = [];\n\n const routerInputs = this.params.rpc.map((options, i) => {\n const { priority, network: _n, ...providerOptions } = options;\n const url = typeof options.url === 'string' ? options.url : options.url.url;\n const providerId = `rpc#${i + 1}-chainId:${this.params.network}-${url}`;\n\n const provider = new InstrumentedJsonRpcProvider(options.url, this.params.network, {\n providerId,\n ...this.params.defaultRpcOptions,\n ...providerOptions,\n onEvent: (e) => this._handleTransportEvent(e),\n });\n\n endpoints.push({ id: providerId, provider });\n\n const endpoint: Endpoint = { providerId, url, provider };\n return { endpoint, priority: priority ?? 0 };\n });\n\n this._endpoints = endpoints;\n this.router = new Router(routerInputs, this._cooldown);\n\n if (params.healthProbe !== undefined) {\n const intervalMs = params.healthProbe.intervalMs ?? 15_000;\n this._probeInterval = setInterval(() => this._runHealthProbe(), intervalMs);\n (this._probeInterval as NodeJS.Timeout).unref?.();\n }\n }\n\n destroy(): void {\n if (this._probeInterval !== undefined) {\n clearInterval(this._probeInterval);\n this._probeInterval = undefined;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async send(method: string, params: any): Promise<any> {\n if (this.router.size() === 0) throw new Error('No RPC available');\n\n const tried = new Set<string>();\n const maxAttempts = this.params.retry.attempts;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n let endpoint = this.router.pick();\n\n if (tried.size < this.router.size()) {\n let retries = 0;\n\n while (tried.has(endpoint.providerId) && retries < this.router.totalSlots()) {\n endpoint = this.router.pick();\n retries++;\n }\n }\n\n tried.add(endpoint.providerId);\n\n const startedAt = Date.now();\n try {\n return await endpoint.provider.send(method, params);\n } catch (e: any) {\n if (isRpcLogicalError(e)) {\n this._emitRpcLogicalError(endpoint, method, startedAt, e);\n }\n if (!shouldFailover(e)) throw e;\n if (attempt === maxAttempts - 1) throw e;\n\n await this._sleepWithBackoff(attempt);\n }\n }\n\n throw new Error('No RPC available');\n }\n\n getSnapshot(): Readonly<RpcStatsSnapshot> {\n return {\n ...this._stats.snapshot(),\n providerCooldownUntil: this._cooldown.cooldownSnapshot(),\n providerCircuitState: this._cooldown.circuitStateSnapshot(),\n perProviderLatencyEwma: this.router.ewmaSnapshot(),\n };\n }\n\n // Returns a provider pinned to a single RPC node selected right now.\n // Use when consecutive calls must see consistent chain state — e.g. read\n // eth_getBalance on block N, then eth_call on the same block N. Without\n // pinning, each call may route to a different node that lags behind.\n pinnedProvider(): InstrumentedJsonRpcProvider {\n return this.router.pick().provider;\n }\n\n private _runHealthProbe(): void {\n const states = this._cooldown.circuitStateSnapshot();\n const cooldowns = this._cooldown.cooldownSnapshot();\n const now = Date.now();\n\n for (const { id, provider } of this._endpoints) {\n if (states[id] !== 'open') continue;\n const cooldownUntil = cooldowns[id];\n if (cooldownUntil !== undefined && cooldownUntil > now) continue;\n // isInCooldown atomically claims the probe slot (open→half-open side effect)\n if (!this._cooldown.isInCooldown(id)) {\n provider.send('eth_blockNumber', []).catch(() => {});\n }\n }\n }\n\n private _handleTransportEvent(e: RpcEvent): void {\n this._stats.onEvent(e);\n this._cooldown.onEvent(e);\n if (e.type === 'response' || e.type === 'error') {\n this.router.recordLatency(e.providerId, e.ms);\n }\n this.params.hooks?.onEvent?.(e);\n }\n\n private async _sleepWithBackoff(attempt: number): Promise<void> {\n const baseDelay = Math.min(1000 * 2 ** attempt, 5000);\n const jitter = Math.random() * baseDelay;\n\n await new Promise((resolve) => setTimeout(resolve, jitter));\n }\n\n private _emitRpcLogicalError(ep: Endpoint, method: string, startedAt: number, error: any): void {\n const endedAt = Date.now();\n\n this._stats.bumpRpcError(ep.providerId, method);\n\n this.params.hooks?.onEvent?.({\n type: 'error',\n chainId: BigInt(Network.from(this.params.network).chainId),\n providerId: ep.providerId,\n method,\n startedAt,\n endedAt,\n ms: endedAt - startedAt,\n isRateLimit: false,\n isTimeout: false,\n isNetworkError: false,\n status: undefined,\n code: error?.code,\n message: String(error?.message || error),\n errorKind: 'rpc',\n });\n }\n}\n","import { RpcEvent } from './utils';\nimport { CircuitState } from './CooldownManager';\n\nexport interface RpcStatsSnapshot {\n total: number;\n inFlight: number;\n perMethodTotal: Record<string, number>;\n rateLimitedTotal: number;\n perProviderRateLimited: Record<string, number>;\n timeoutTotal: number;\n perProviderTimeout: Record<string, number>;\n serverErrorTotal: number;\n perProviderTotal: Record<string, number>;\n providerCooldownUntil: Record<string, number>;\n providerCircuitState: Record<string, CircuitState>;\n perProviderLatencyEwma: Record<string, number>;\n perProviderInFlight: Record<string, number>;\n perProviderError: Record<string, number>;\n rpcErrorTotal: number;\n perProviderRpcError: Record<string, Record<string, number>>;\n perMethodRpcError: Record<string, number>;\n perProviderMethod: Record<string, Record<string, number>>;\n}\n\ntype MetricsSnapshot = Omit<\n RpcStatsSnapshot,\n 'providerCooldownUntil' | 'providerCircuitState' | 'perProviderLatencyEwma'\n>;\n\nexport class Stats {\n private _total = 0;\n private _inFlight = 0;\n\n private _perMethod: Record<string, number> = {};\n private _perProviderMethod: Record<string, Record<string, number>> = {};\n\n private _rateLimitedTotal = 0;\n private _timeoutTotal = 0;\n private _serverErrorTotal = 0;\n private _rpcErrorTotal = 0;\n\n private _perProviderInFlight: Record<string, number> = {};\n private _perProviderTotal: Record<string, number> = {};\n private _perProviderTimeout: Record<string, number> = {};\n private _perProviderRateLimited: Record<string, number> = {};\n private _perProviderError: Record<string, number> = {};\n private _perProviderRpcError: Record<string, Record<string, number>> = {};\n private _perMethodRpcError: Record<string, number> = {};\n\n private _bump(map: Record<string, number>, key: string) {\n map[key] = (map[key] || 0) + 1;\n }\n\n private _decrease(map: Record<string, number>, key: string) {\n map[key] = Math.max((map[key] || 0) - 1, 0);\n }\n\n onEvent(e: RpcEvent): void {\n const id = e.providerId;\n\n if (e.type === 'request') {\n this._inFlight++;\n this._bump(this._perProviderInFlight, id);\n this._total++;\n this._perProviderTotal[id] = (this._perProviderTotal[id] || 0) + 1;\n this._bump(this._perMethod, e.method);\n if (!this._perProviderMethod[id]) this._perProviderMethod[id] = {};\n this._bump(this._perProviderMethod[id], e.method);\n return;\n }\n\n this._inFlight = Math.max(this._inFlight - 1, 0);\n this._decrease(this._perProviderInFlight, id);\n\n if (e.type === 'error') {\n if (e.isRateLimit) {\n this._rateLimitedTotal++;\n this._bump(this._perProviderRateLimited, id);\n } else if (e.isTimeout) {\n this._timeoutTotal++;\n this._bump(this._perProviderTimeout, id);\n } else if (e.status !== undefined && e.status >= 500) {\n this._serverErrorTotal++;\n this._bump(this._perProviderError, id);\n }\n }\n }\n\n bumpRpcError(providerId: string, method: string): void {\n this._rpcErrorTotal++;\n if (!this._perProviderRpcError[providerId]) {\n this._perProviderRpcError[providerId] = {};\n }\n this._bump(this._perProviderRpcError[providerId], method);\n this._bump(this._perMethodRpcError, method);\n }\n\n removeProvider(id: string): void {\n delete this._perProviderInFlight[id];\n delete this._perProviderTotal[id];\n delete this._perProviderTimeout[id];\n delete this._perProviderRateLimited[id];\n delete this._perProviderError[id];\n delete this._perProviderRpcError[id];\n delete this._perProviderMethod[id];\n }\n\n snapshot(): Readonly<MetricsSnapshot> {\n return {\n total: this._total,\n inFlight: this._inFlight,\n perMethodTotal: { ...this._perMethod },\n rateLimitedTotal: this._rateLimitedTotal,\n timeoutTotal: this._timeoutTotal,\n serverErrorTotal: this._serverErrorTotal,\n rpcErrorTotal: this._rpcErrorTotal,\n perProviderMethod: Object.fromEntries(\n Object.entries(this._perProviderMethod).map(([k, v]) => [k, { ...v }]),\n ),\n perProviderInFlight: { ...this._perProviderInFlight },\n perProviderRateLimited: { ...this._perProviderRateLimited },\n perProviderTimeout: { ...this._perProviderTimeout },\n perProviderError: { ...this._perProviderError },\n perProviderRpcError: Object.fromEntries(\n Object.entries(this._perProviderRpcError).map(([k, v]) => [k, { ...v }]),\n ),\n perMethodRpcError: { ...this._perMethodRpcError },\n perProviderTotal: { ...this._perProviderTotal },\n };\n }\n}\n","import { InstrumentedJsonRpcProvider } from './InstrumentedProvider';\n\nexport interface Endpoint {\n providerId: string;\n url: string;\n provider: InstrumentedJsonRpcProvider;\n}\n\nexport type RpcEvent =\n | {\n type: 'request';\n chainId: bigint;\n providerId: string;\n method: string;\n startedAt: number;\n }\n | {\n type: 'response';\n chainId: bigint;\n providerId: string;\n method: string;\n startedAt: number;\n endedAt: number;\n ms: number;\n }\n | {\n type: 'error';\n chainId: bigint;\n providerId: string;\n method: string;\n startedAt: number;\n endedAt: number;\n ms: number;\n isRateLimit: boolean;\n isTimeout: boolean;\n isNetworkError: boolean;\n status?: number;\n retryAfterMs?: number;\n code?: string;\n message: string;\n errorKind?: 'transport' | 'rpc';\n };\n\nexport function getHttpStatus(e: any): number | undefined {\n return (\n e?.status ??\n e?.response?.status ??\n e?.response?.statusCode ??\n e?.error?.status ??\n e?.error?.response?.status ??\n e?.body?.statusCode // sometimes present\n );\n}\n\nexport function isRateLimitError(e: any): boolean {\n const status = getHttpStatus(e);\n if (status === 429 || status === 402) return true;\n\n const msg = String(e?.message || e);\n if (/error code:\\s*1015/i.test(msg)) return true; // Cloudflare\n return /rate limit|too many requests|429|quota|throttl/i.test(msg);\n}\n\nexport function isServerError(e: any): boolean {\n const status = getHttpStatus(e);\n return status !== undefined && status >= 500;\n}\n\nexport function getRetryAfterMs(e: any): number | null {\n const ra =\n e?.response?.headers?.get?.('retry-after') ??\n e?.response?.headers?.['retry-after'] ??\n e?.headers?.['retry-after'];\n const n = Number(ra);\n return Number.isFinite(n) ? n * 1000 : null;\n}\n\nexport function isTimeoutError(e: any): boolean {\n // ethers v5\n if (e?.code === 'TIMEOUT') return true;\n\n const status = getHttpStatus(e);\n // some RPCs / proxies return 504 on timeout\n if (status === 504) return true;\n\n const msg = String(e?.message || e);\n\n // node-fetch / undici / axios / nginx / generic\n return /timeout|timed out|ETIMEDOUT|ESOCKETTIMEDOUT|ECONNABORTED|504 Gateway/i.test(msg);\n}\n\nconst NETWORK_ERROR_CODES = new Set(['ECONNREFUSED', 'ECONNRESET', 'ENOTFOUND', 'EAI_AGAIN']);\n\nexport function isNetworkError(e: any): boolean {\n return NETWORK_ERROR_CODES.has(e?.code);\n}\n\nexport function isTransportError(e: any): boolean {\n return isTimeoutError(e) || isRateLimitError(e) || isServerError(e) || isNetworkError(e);\n}\n\n/**\n * JSON-RPC / application-level error:\n * the RPC responded, but the call itself failed logically.\n *\n * Examples:\n * - getLogs block range too large\n * - invalid params\n * - execution reverted\n * - method not supported\n */\nexport function isRpcLogicalError(e: any): boolean {\n if (isTransportError(e)) return false;\n\n // ethers often preserves code/message for JSON-RPC errors\n if (e?.error?.code !== undefined) return true;\n if (e?.code === 'CALL_EXCEPTION') return true;\n if (e?.code === 'UNKNOWN_ERROR' && e?.error) return true;\n\n const msg = String(e?.message || e);\n\n return /execution reverted|invalid params|method not found|method not supported|block range|too many blocks|getLogs/i.test(\n msg,\n );\n}\n\nexport function shouldFailover(e: any): boolean {\n return isTimeoutError(e) || isRateLimitError(e) || isServerError(e) || isNetworkError(e);\n}\n","import {\n JsonRpcProvider,\n Network,\n JsonRpcPayload,\n FetchRequest,\n JsonRpcApiProviderOptions,\n Networkish,\n} from 'ethers';\nimport { Semaphore } from './Semaphore';\nimport {\n getHttpStatus,\n getRetryAfterMs,\n isNetworkError,\n isRateLimitError,\n isTimeoutError,\n RpcEvent,\n} from './utils';\nimport { RpsLimiter } from './RpsLimiter';\n\nexport interface InstrumentedJsonRpcProviderOptions extends JsonRpcApiProviderOptions {\n providerId: string;\n inFlight?: number;\n timeout?: number;\n rps?: number;\n rpsBurst?: number;\n onEvent?: (e: RpcEvent) => void;\n}\n\nexport class InstrumentedJsonRpcProvider extends JsonRpcProvider {\n readonly providerId: string;\n readonly chainId: bigint;\n readonly options: InstrumentedJsonRpcProviderOptions;\n\n readonly inFlightLimiter: Semaphore;\n readonly rpsLimiter: RpsLimiter;\n readonly fetchRequest: FetchRequest;\n\n constructor(\n url: string | FetchRequest,\n network: Networkish,\n options: InstrumentedJsonRpcProviderOptions,\n ) {\n let fetchRequest: FetchRequest;\n\n if (typeof url == 'string') {\n fetchRequest = new FetchRequest(url);\n } else {\n fetchRequest = url;\n }\n\n fetchRequest.timeout = options.timeout || 10_000;\n\n const _network = Network.from(network);\n super(fetchRequest, _network, { staticNetwork: true, ...options });\n this.fetchRequest = fetchRequest;\n this.providerId = options.providerId;\n this.chainId = _network.chainId;\n this.options = options;\n\n const { rps = 10, rpsBurst, inFlight = 1 } = options;\n\n this.inFlightLimiter = new Semaphore(inFlight);\n this.rpsLimiter = new RpsLimiter(rps, rpsBurst || rps);\n }\n\n override async _send(payload: JsonRpcPayload | JsonRpcPayload[]): Promise<any> {\n await this.rpsLimiter.take(1);\n\n const release = await this.inFlightLimiter.acquire();\n\n try {\n return await this._sendInstrumented(payload);\n } finally {\n release();\n }\n }\n\n isAvailable(count = 1): boolean {\n return this.rpsLimiter.isAvailable(count) && this.inFlightLimiter.isAvailable();\n }\n\n private async _sendInstrumented(payload: JsonRpcPayload | JsonRpcPayload[]): Promise<any> {\n const startedAt = Date.now();\n const payloads = Array.isArray(payload) ? payload : [payload];\n\n for (const p of payloads) {\n this.options.onEvent?.({\n type: 'request',\n chainId: this.chainId,\n providerId: this.providerId,\n method: p.method,\n startedAt,\n });\n }\n\n try {\n const res = await super._send(payload);\n\n const endedAt = Date.now();\n\n for (const p of payloads) {\n this.options.onEvent?.({\n type: 'response',\n chainId: this.chainId,\n providerId: this.providerId,\n method: p.method,\n startedAt,\n endedAt,\n ms: endedAt - startedAt,\n });\n }\n\n return res;\n } catch (e: any) {\n const endedAt = Date.now();\n const rl = isRateLimitError(e);\n const isTimeout = isTimeoutError(e);\n const ne = isNetworkError(e);\n const retryAfterMs = getRetryAfterMs(e) ?? undefined;\n\n for (const p of payloads) {\n this.options.onEvent?.({\n type: 'error',\n chainId: this.chainId,\n providerId: this.providerId,\n method: p.method,\n startedAt,\n endedAt,\n ms: endedAt - startedAt,\n isRateLimit: rl,\n isTimeout,\n isNetworkError: ne,\n status: getHttpStatus(e),\n retryAfterMs,\n code: e?.code,\n message: String(e?.message || e),\n errorKind: 'transport',\n });\n }\n\n throw e;\n }\n }\n}\n","export class Semaphore {\n private inUse = 0;\n private queue: Array<() => void> = [];\n\n constructor(private readonly max: number) {\n if (!Number.isFinite(max) || max <= 0) {\n throw new Error(`Semaphore max must be a positive number, got: ${max}`);\n }\n }\n\n isAvailable(): boolean {\n return this.inUse < this.max;\n }\n\n async acquire(): Promise<() => void> {\n if (this.inUse < this.max) {\n this.inUse++;\n let released = false;\n return () => {\n if (released) return;\n released = true;\n this.release();\n };\n }\n\n return new Promise<() => void>((resolve) => {\n this.queue.push(() => {\n this.inUse++;\n let released = false;\n resolve(() => {\n if (released) return;\n released = true;\n this.release();\n });\n });\n });\n }\n\n private release() {\n this.inUse = Math.max(0, this.inUse - 1);\n const next = this.queue.shift();\n if (next) next();\n }\n}\n","export class RpsLimiter {\n // Current number of tokens in the bucket (can be fractional)\n private tokens: number;\n\n // Time of last token refill, in ms\n private lastRefill = Date.now();\n\n constructor(\n // rps: how many tokens we add per second\n private readonly rps: number,\n // burst: maximum bucket capacity.\n // Default: >=1 and approximately equal to rps (to allow a small burst)\n private readonly burst: number = Math.max(1, Math.ceil(rps)),\n ) {\n // At start, the bucket is full: can make burst requests immediately\n this.tokens = burst;\n }\n\n // Refill tokens according to elapsed time\n private refill(now: number) {\n // rps<=0 means \"limit is disabled\"\n if (this.rps <= 0) return;\n\n const elapsed = now - this.lastRefill; // ms since last refill\n if (elapsed <= 0) return;\n\n // How many tokens to add:\n // elapsed/1000 = seconds, multiply by rps\n const add = (elapsed / 1000) * this.rps;\n\n // Add tokens, but don't exceed burst (bucket capacity)\n this.tokens = Math.min(this.burst, this.tokens + add);\n\n // Remember that we refilled tokens at time now\n this.lastRefill = now;\n }\n\n isAvailable(count = 1): boolean {\n if (!this.rps || this.rps <= 0) return true;\n\n const now = Date.now();\n this.refill(now);\n\n return this.tokens >= count;\n }\n\n // Take count tokens (usually 1 request = 1 token).\n // If not enough tokens — enqueue exactly one setTimeout for the precise wake-up time.\n // tokens may go negative (debt); each caller's wait time is derived from that debt.\n async take(count = 1): Promise<void> {\n if (!this.rps || this.rps <= 0) return;\n\n const now = Date.now();\n this.refill(now);\n this.tokens -= count;\n\n if (this.tokens >= 0) return;\n\n // tokens is negative: we owe (-tokens) tokens that will accumulate at rps tokens/sec\n const waitMs = Math.ceil((-this.tokens / this.rps) * 1000);\n return new Promise((resolve) => setTimeout(resolve, waitMs));\n }\n}\n","import { Endpoint } from './utils';\nimport { IAvailabilityChecker } from './CooldownManager';\n\nconst EWMA_ALPHA = 0.2;\n\ntype RouterEntry = { endpoint: Endpoint };\ntype PriorityGroup = { entries: RouterEntry[]; rr: number };\n\nexport interface RouterEndpointInput {\n endpoint: Endpoint;\n priority: number;\n}\n\nexport class Router {\n private readonly _groups: PriorityGroup[];\n private readonly _size: number;\n private readonly _ewma = new Map<string, number>();\n\n constructor(\n inputs: RouterEndpointInput[],\n private readonly availability: IAvailabilityChecker,\n ) {\n const byPriority = new Map<number, RouterEntry[]>();\n for (const { endpoint, priority } of inputs) {\n if (!byPriority.has(priority)) byPriority.set(priority, []);\n byPriority.get(priority)!.push({ endpoint });\n }\n\n this._groups = [...byPriority.entries()]\n .sort(([a], [b]) => b - a)\n .map(([, entries]) => ({ entries, rr: 0 }));\n\n this._size = inputs.length;\n }\n\n size(): number {\n return this._size;\n }\n\n totalSlots(): number {\n return this._size;\n }\n\n recordLatency(providerId: string, ms: number): void {\n const prev = this._ewma.get(providerId);\n this._ewma.set(providerId, prev === undefined ? ms : EWMA_ALPHA * ms + (1 - EWMA_ALPHA) * prev);\n }\n\n ewmaSnapshot(): Record<string, number> {\n return Object.fromEntries(this._ewma);\n }\n\n pick(): Endpoint {\n for (const group of this._groups) {\n const ep = this._pickFromGroup(group);\n if (ep !== null) return ep;\n }\n // all endpoints unavailable: round-robin within highest-priority group\n const g = this._groups[0];\n const entry = g.entries[g.rr % g.entries.length];\n g.rr++;\n return entry.endpoint;\n }\n\n private _pickFromGroup(group: PriorityGroup): Endpoint | null {\n const available = group.entries.filter(\n (e) =>\n !this.availability.isInCooldown(e.endpoint.providerId) &&\n e.endpoint.provider.isAvailable(1),\n );\n\n if (available.length === 0) return null;\n if (available.length === 1) return available[0].endpoint;\n\n // P2C: pick 2 random candidates, return the one with lower EWMA latency.\n // Unsampled endpoints have EWMA=0 so they get explored first.\n const i = Math.floor(Math.random() * available.length);\n let j = Math.floor(Math.random() * (available.length - 1));\n if (j >= i) j++;\n\n const a = available[i].endpoint;\n const b = available[j].endpoint;\n const ewmaA = this._ewma.get(a.providerId) ?? 0;\n const ewmaB = this._ewma.get(b.providerId) ?? 0;\n\n return ewmaA <= ewmaB ? a : b;\n }\n}\n","import { RpcEvent } from './utils';\n\nexport interface IAvailabilityChecker {\n isInCooldown(id: string): boolean;\n}\n\nexport type CircuitState = 'closed' | 'open' | 'half-open';\n\nexport class CooldownManager implements IAvailabilityChecker {\n private readonly _cooldownUntil: Record<string, number> = {};\n private readonly _lastCooldownMs: Record<string, number> = {};\n private readonly _perProviderTotal: Record<string, number> = {};\n private readonly _perProviderTimeout: Record<string, number> = {};\n private readonly _circuitState: Record<string, CircuitState> = {};\n private readonly _probeInFlight = new Set<string>();\n\n // atomically claims the probe slot when entering half-open\n isInCooldown(id: string): boolean {\n const state = this._circuitState[id];\n\n if (!state) return false;\n\n if (state === 'half-open') {\n /* v8 ignore start */\n if (!this._probeInFlight.has(id)) {\n // unreachable: open→half-open transition always claims the probe slot atomically\n this._probeInFlight.add(id);\n return false;\n }\n /* v8 ignore stop */\n return true;\n }\n\n const until = this._cooldownUntil[id];\n if (until === undefined || until <= Date.now()) {\n delete this._cooldownUntil[id];\n this._circuitState[id] = 'half-open';\n this._probeInFlight.add(id);\n return false;\n }\n\n return true;\n }\n\n private _openCircuit(id: string, ms: number): void {\n this._cooldownUntil[id] = Date.now() + ms;\n this._circuitState[id] = 'open';\n this._probeInFlight.delete(id);\n }\n\n private _openWithBackoff(id: string): void {\n const prev = this._lastCooldownMs[id] || 0;\n const ms = (prev ? prev * 2 : 10_000) + Math.floor(Math.random() * 1000);\n this._lastCooldownMs[id] = ms;\n this._openCircuit(id, ms);\n }\n\n cooldownSnapshot(): Record<string, number> {\n return { ...this._cooldownUntil };\n }\n\n circuitStateSnapshot(): Record<string, CircuitState> {\n const result: Record<string, CircuitState> = {};\n for (const [id, state] of Object.entries(this._circuitState)) {\n result[id] = state;\n }\n return result;\n }\n\n removeProvider(id: string): void {\n delete this._cooldownUntil[id];\n delete this._lastCooldownMs[id];\n delete this._perProviderTotal[id];\n delete this._perProviderTimeout[id];\n delete this._circuitState[id];\n this._probeInFlight.delete(id);\n }\n\n onEvent(e: RpcEvent): void {\n const id = e.providerId;\n\n if (e.type === 'request') {\n this._perProviderTotal[id] = (this._perProviderTotal[id] || 0) + 1;\n return;\n }\n\n if (e.type === 'response') {\n if (this._circuitState[id] === 'half-open') {\n delete this._circuitState[id];\n this._probeInFlight.delete(id);\n }\n this._lastCooldownMs[id] = 0;\n return;\n }\n\n if (this._circuitState[id] === 'half-open') {\n if (e.isRateLimit) {\n this._openCircuit(id, e.retryAfterMs ?? 10_000);\n } else {\n this._openWithBackoff(id);\n }\n return;\n }\n\n if (e.isRateLimit) {\n this._openCircuit(id, e.retryAfterMs ?? 10_000);\n return;\n }\n\n if (e.isTimeout) {\n this._perProviderTimeout[id] = (this._perProviderTimeout[id] || 0) + 1;\n const n = this._perProviderTotal[id] || 0;\n const t = this._perProviderTimeout[id];\n const ratio = n ? t / n : 0;\n if (n >= 50 && ratio >= 0.2) {\n const baseCooldown = ratio >= 0.5 ? 600_000 : 60_000;\n const ms = (e.retryAfterMs ?? baseCooldown) + Math.floor(Math.random() * 1000);\n this._lastCooldownMs[id] = ms;\n this._openCircuit(id, ms);\n }\n return;\n }\n\n if ((e.status !== undefined && e.status >= 500) || e.isNetworkError) {\n this._openWithBackoff(id);\n }\n }\n}\n"],"mappings":"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","names":["import_ethers"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,60 +1,5 @@
|
|
|
1
1
|
import { JsonRpcProvider, JsonRpcApiProviderOptions, FetchRequest, Networkish, JsonRpcPayload } from 'ethers';
|
|
2
2
|
|
|
3
|
-
interface RpcStatsSnapshot {
|
|
4
|
-
total: number;
|
|
5
|
-
inFlight: number;
|
|
6
|
-
perMethodTotal: Record<string, number>;
|
|
7
|
-
rateLimitedTotal: number;
|
|
8
|
-
perProviderRateLimited: Record<string, number>;
|
|
9
|
-
timeoutTotal: number;
|
|
10
|
-
perProviderTimeout: Record<string, number>;
|
|
11
|
-
perProviderTotal: Record<string, number>;
|
|
12
|
-
providerCooldownUntil: Record<string, number>;
|
|
13
|
-
perProviderInFlight: Record<string, number>;
|
|
14
|
-
perProviderError: Record<string, number>;
|
|
15
|
-
rpcErrorTotal: number;
|
|
16
|
-
perProviderRpcError: Record<string, Record<string, number>>;
|
|
17
|
-
perMethodRpcError: Record<string, number>;
|
|
18
|
-
perProviderMethod: Record<string, Record<string, number>>;
|
|
19
|
-
}
|
|
20
|
-
declare class Stats {
|
|
21
|
-
private _total;
|
|
22
|
-
private _inFlight;
|
|
23
|
-
private _perMethod;
|
|
24
|
-
private _perProviderMethod;
|
|
25
|
-
private _rateLimitedTotal;
|
|
26
|
-
private _timeoutTotal;
|
|
27
|
-
private _rpcErrorTotal;
|
|
28
|
-
private _perProviderInFlight;
|
|
29
|
-
private _perProviderTotal;
|
|
30
|
-
private _perProviderTimeout;
|
|
31
|
-
private _perProviderRateLimited;
|
|
32
|
-
private _perProviderError;
|
|
33
|
-
private _perProviderRpcError;
|
|
34
|
-
private _perMethodRpcError;
|
|
35
|
-
private _providerCooldownUntil;
|
|
36
|
-
private _bump;
|
|
37
|
-
private _decrease;
|
|
38
|
-
private _bumpTotal;
|
|
39
|
-
private _bumpInFlight;
|
|
40
|
-
private _bumpRateLimitedTotal;
|
|
41
|
-
private _bumpTimeoutTotal;
|
|
42
|
-
private _bumpRpcErrorTotal;
|
|
43
|
-
bumpInFlightPerProvider(id: string): void;
|
|
44
|
-
decreaseInFlightPerProvider(id: string): void;
|
|
45
|
-
decreaseInFlight(): void;
|
|
46
|
-
bumpPerMethod(id: string, method: string): void;
|
|
47
|
-
bumpRateLimitedPerProvider(id: string): void;
|
|
48
|
-
bumpTimeoutPerProvider(id: string): void;
|
|
49
|
-
bumpProviderTotal(id: string): void;
|
|
50
|
-
bumpServerErrorPerProvider(id: string): void;
|
|
51
|
-
bumpRpcError(providerId: string, method: string): void;
|
|
52
|
-
timeoutRatio(id: string): number;
|
|
53
|
-
isInCooldown(id: string): boolean;
|
|
54
|
-
setCooldown(id: string, ms: number): void;
|
|
55
|
-
snapshot(): Readonly<RpcStatsSnapshot>;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
3
|
declare class Semaphore {
|
|
59
4
|
private readonly max;
|
|
60
5
|
private inUse;
|
|
@@ -78,26 +23,19 @@ declare class RpsLimiter {
|
|
|
78
23
|
|
|
79
24
|
interface InstrumentedJsonRpcProviderOptions extends JsonRpcApiProviderOptions {
|
|
80
25
|
providerId: string;
|
|
81
|
-
stats: Stats;
|
|
82
26
|
inFlight?: number;
|
|
83
27
|
timeout?: number;
|
|
84
28
|
rps?: number;
|
|
85
29
|
rpsBurst?: number;
|
|
86
30
|
onEvent?: (e: RpcEvent) => void;
|
|
87
31
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Instrumented JsonRpcProvider.
|
|
90
|
-
* Tracks requests, inFlight count, rate limits, and per-method / per-provider metrics.
|
|
91
|
-
*/
|
|
92
32
|
declare class InstrumentedJsonRpcProvider extends JsonRpcProvider {
|
|
93
33
|
readonly providerId: string;
|
|
94
34
|
readonly chainId: bigint;
|
|
95
35
|
readonly options: InstrumentedJsonRpcProviderOptions;
|
|
96
36
|
readonly inFlightLimiter: Semaphore;
|
|
97
37
|
readonly rpsLimiter: RpsLimiter;
|
|
98
|
-
readonly stats: Stats;
|
|
99
38
|
readonly fetchRequest: FetchRequest;
|
|
100
|
-
private lastCooldownMs;
|
|
101
39
|
constructor(url: string | FetchRequest, network: Networkish, options: InstrumentedJsonRpcProviderOptions);
|
|
102
40
|
_send(payload: JsonRpcPayload | JsonRpcPayload[]): Promise<any>;
|
|
103
41
|
isAvailable(count?: number): boolean;
|
|
@@ -133,24 +71,62 @@ type RpcEvent = {
|
|
|
133
71
|
ms: number;
|
|
134
72
|
isRateLimit: boolean;
|
|
135
73
|
isTimeout: boolean;
|
|
74
|
+
isNetworkError: boolean;
|
|
136
75
|
status?: number;
|
|
76
|
+
retryAfterMs?: number;
|
|
137
77
|
code?: string;
|
|
138
78
|
message: string;
|
|
139
79
|
errorKind?: 'transport' | 'rpc';
|
|
140
80
|
};
|
|
141
81
|
|
|
82
|
+
interface IAvailabilityChecker {
|
|
83
|
+
isInCooldown(id: string): boolean;
|
|
84
|
+
}
|
|
85
|
+
type CircuitState = 'closed' | 'open' | 'half-open';
|
|
86
|
+
|
|
87
|
+
interface RpcStatsSnapshot {
|
|
88
|
+
total: number;
|
|
89
|
+
inFlight: number;
|
|
90
|
+
perMethodTotal: Record<string, number>;
|
|
91
|
+
rateLimitedTotal: number;
|
|
92
|
+
perProviderRateLimited: Record<string, number>;
|
|
93
|
+
timeoutTotal: number;
|
|
94
|
+
perProviderTimeout: Record<string, number>;
|
|
95
|
+
serverErrorTotal: number;
|
|
96
|
+
perProviderTotal: Record<string, number>;
|
|
97
|
+
providerCooldownUntil: Record<string, number>;
|
|
98
|
+
providerCircuitState: Record<string, CircuitState>;
|
|
99
|
+
perProviderLatencyEwma: Record<string, number>;
|
|
100
|
+
perProviderInFlight: Record<string, number>;
|
|
101
|
+
perProviderError: Record<string, number>;
|
|
102
|
+
rpcErrorTotal: number;
|
|
103
|
+
perProviderRpcError: Record<string, Record<string, number>>;
|
|
104
|
+
perMethodRpcError: Record<string, number>;
|
|
105
|
+
perProviderMethod: Record<string, Record<string, number>>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
interface RouterEndpointInput {
|
|
109
|
+
endpoint: Endpoint;
|
|
110
|
+
priority: number;
|
|
111
|
+
}
|
|
142
112
|
declare class Router {
|
|
143
|
-
private readonly
|
|
144
|
-
private readonly
|
|
145
|
-
private
|
|
146
|
-
|
|
113
|
+
private readonly availability;
|
|
114
|
+
private readonly _groups;
|
|
115
|
+
private readonly _size;
|
|
116
|
+
private readonly _ewma;
|
|
117
|
+
constructor(inputs: RouterEndpointInput[], availability: IAvailabilityChecker);
|
|
147
118
|
size(): number;
|
|
119
|
+
totalSlots(): number;
|
|
120
|
+
recordLatency(providerId: string, ms: number): void;
|
|
121
|
+
ewmaSnapshot(): Record<string, number>;
|
|
148
122
|
pick(): Endpoint;
|
|
123
|
+
private _pickFromGroup;
|
|
149
124
|
}
|
|
150
125
|
|
|
151
126
|
interface RPCPoolProviderOptions extends Partial<InstrumentedJsonRpcProviderOptions> {
|
|
152
127
|
url: string | FetchRequest;
|
|
153
128
|
network?: Networkish;
|
|
129
|
+
priority?: number;
|
|
154
130
|
}
|
|
155
131
|
interface RPCPoolProviderParams {
|
|
156
132
|
network: Networkish;
|
|
@@ -167,16 +143,26 @@ interface RPCPoolProviderParams {
|
|
|
167
143
|
hooks?: {
|
|
168
144
|
onEvent(e: RpcEvent): void;
|
|
169
145
|
};
|
|
146
|
+
healthProbe?: {
|
|
147
|
+
intervalMs?: number;
|
|
148
|
+
};
|
|
170
149
|
}
|
|
171
150
|
declare class RPCPoolProvider extends JsonRpcProvider {
|
|
172
151
|
readonly router: Router;
|
|
173
152
|
readonly params: RPCPoolProviderParams;
|
|
174
|
-
readonly
|
|
153
|
+
private readonly _stats;
|
|
154
|
+
private readonly _cooldown;
|
|
155
|
+
private readonly _endpoints;
|
|
156
|
+
private _probeInterval;
|
|
175
157
|
constructor(params: RPCPoolProviderParams);
|
|
158
|
+
destroy(): void;
|
|
176
159
|
send(method: string, params: any): Promise<any>;
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
160
|
+
getSnapshot(): Readonly<RpcStatsSnapshot>;
|
|
161
|
+
pinnedProvider(): InstrumentedJsonRpcProvider;
|
|
162
|
+
private _runHealthProbe;
|
|
163
|
+
private _handleTransportEvent;
|
|
164
|
+
private _sleepWithBackoff;
|
|
165
|
+
private _emitRpcLogicalError;
|
|
180
166
|
}
|
|
181
167
|
|
|
182
|
-
export { RPCPoolProvider, type RPCPoolProviderParams, type RpcEvent, type RpcStatsSnapshot };
|
|
168
|
+
export { type CircuitState, RPCPoolProvider, type RPCPoolProviderOptions, type RPCPoolProviderParams, type RpcEvent, type RpcStatsSnapshot };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,60 +1,5 @@
|
|
|
1
1
|
import { JsonRpcProvider, JsonRpcApiProviderOptions, FetchRequest, Networkish, JsonRpcPayload } from 'ethers';
|
|
2
2
|
|
|
3
|
-
interface RpcStatsSnapshot {
|
|
4
|
-
total: number;
|
|
5
|
-
inFlight: number;
|
|
6
|
-
perMethodTotal: Record<string, number>;
|
|
7
|
-
rateLimitedTotal: number;
|
|
8
|
-
perProviderRateLimited: Record<string, number>;
|
|
9
|
-
timeoutTotal: number;
|
|
10
|
-
perProviderTimeout: Record<string, number>;
|
|
11
|
-
perProviderTotal: Record<string, number>;
|
|
12
|
-
providerCooldownUntil: Record<string, number>;
|
|
13
|
-
perProviderInFlight: Record<string, number>;
|
|
14
|
-
perProviderError: Record<string, number>;
|
|
15
|
-
rpcErrorTotal: number;
|
|
16
|
-
perProviderRpcError: Record<string, Record<string, number>>;
|
|
17
|
-
perMethodRpcError: Record<string, number>;
|
|
18
|
-
perProviderMethod: Record<string, Record<string, number>>;
|
|
19
|
-
}
|
|
20
|
-
declare class Stats {
|
|
21
|
-
private _total;
|
|
22
|
-
private _inFlight;
|
|
23
|
-
private _perMethod;
|
|
24
|
-
private _perProviderMethod;
|
|
25
|
-
private _rateLimitedTotal;
|
|
26
|
-
private _timeoutTotal;
|
|
27
|
-
private _rpcErrorTotal;
|
|
28
|
-
private _perProviderInFlight;
|
|
29
|
-
private _perProviderTotal;
|
|
30
|
-
private _perProviderTimeout;
|
|
31
|
-
private _perProviderRateLimited;
|
|
32
|
-
private _perProviderError;
|
|
33
|
-
private _perProviderRpcError;
|
|
34
|
-
private _perMethodRpcError;
|
|
35
|
-
private _providerCooldownUntil;
|
|
36
|
-
private _bump;
|
|
37
|
-
private _decrease;
|
|
38
|
-
private _bumpTotal;
|
|
39
|
-
private _bumpInFlight;
|
|
40
|
-
private _bumpRateLimitedTotal;
|
|
41
|
-
private _bumpTimeoutTotal;
|
|
42
|
-
private _bumpRpcErrorTotal;
|
|
43
|
-
bumpInFlightPerProvider(id: string): void;
|
|
44
|
-
decreaseInFlightPerProvider(id: string): void;
|
|
45
|
-
decreaseInFlight(): void;
|
|
46
|
-
bumpPerMethod(id: string, method: string): void;
|
|
47
|
-
bumpRateLimitedPerProvider(id: string): void;
|
|
48
|
-
bumpTimeoutPerProvider(id: string): void;
|
|
49
|
-
bumpProviderTotal(id: string): void;
|
|
50
|
-
bumpServerErrorPerProvider(id: string): void;
|
|
51
|
-
bumpRpcError(providerId: string, method: string): void;
|
|
52
|
-
timeoutRatio(id: string): number;
|
|
53
|
-
isInCooldown(id: string): boolean;
|
|
54
|
-
setCooldown(id: string, ms: number): void;
|
|
55
|
-
snapshot(): Readonly<RpcStatsSnapshot>;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
3
|
declare class Semaphore {
|
|
59
4
|
private readonly max;
|
|
60
5
|
private inUse;
|
|
@@ -78,26 +23,19 @@ declare class RpsLimiter {
|
|
|
78
23
|
|
|
79
24
|
interface InstrumentedJsonRpcProviderOptions extends JsonRpcApiProviderOptions {
|
|
80
25
|
providerId: string;
|
|
81
|
-
stats: Stats;
|
|
82
26
|
inFlight?: number;
|
|
83
27
|
timeout?: number;
|
|
84
28
|
rps?: number;
|
|
85
29
|
rpsBurst?: number;
|
|
86
30
|
onEvent?: (e: RpcEvent) => void;
|
|
87
31
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Instrumented JsonRpcProvider.
|
|
90
|
-
* Tracks requests, inFlight count, rate limits, and per-method / per-provider metrics.
|
|
91
|
-
*/
|
|
92
32
|
declare class InstrumentedJsonRpcProvider extends JsonRpcProvider {
|
|
93
33
|
readonly providerId: string;
|
|
94
34
|
readonly chainId: bigint;
|
|
95
35
|
readonly options: InstrumentedJsonRpcProviderOptions;
|
|
96
36
|
readonly inFlightLimiter: Semaphore;
|
|
97
37
|
readonly rpsLimiter: RpsLimiter;
|
|
98
|
-
readonly stats: Stats;
|
|
99
38
|
readonly fetchRequest: FetchRequest;
|
|
100
|
-
private lastCooldownMs;
|
|
101
39
|
constructor(url: string | FetchRequest, network: Networkish, options: InstrumentedJsonRpcProviderOptions);
|
|
102
40
|
_send(payload: JsonRpcPayload | JsonRpcPayload[]): Promise<any>;
|
|
103
41
|
isAvailable(count?: number): boolean;
|
|
@@ -133,24 +71,62 @@ type RpcEvent = {
|
|
|
133
71
|
ms: number;
|
|
134
72
|
isRateLimit: boolean;
|
|
135
73
|
isTimeout: boolean;
|
|
74
|
+
isNetworkError: boolean;
|
|
136
75
|
status?: number;
|
|
76
|
+
retryAfterMs?: number;
|
|
137
77
|
code?: string;
|
|
138
78
|
message: string;
|
|
139
79
|
errorKind?: 'transport' | 'rpc';
|
|
140
80
|
};
|
|
141
81
|
|
|
82
|
+
interface IAvailabilityChecker {
|
|
83
|
+
isInCooldown(id: string): boolean;
|
|
84
|
+
}
|
|
85
|
+
type CircuitState = 'closed' | 'open' | 'half-open';
|
|
86
|
+
|
|
87
|
+
interface RpcStatsSnapshot {
|
|
88
|
+
total: number;
|
|
89
|
+
inFlight: number;
|
|
90
|
+
perMethodTotal: Record<string, number>;
|
|
91
|
+
rateLimitedTotal: number;
|
|
92
|
+
perProviderRateLimited: Record<string, number>;
|
|
93
|
+
timeoutTotal: number;
|
|
94
|
+
perProviderTimeout: Record<string, number>;
|
|
95
|
+
serverErrorTotal: number;
|
|
96
|
+
perProviderTotal: Record<string, number>;
|
|
97
|
+
providerCooldownUntil: Record<string, number>;
|
|
98
|
+
providerCircuitState: Record<string, CircuitState>;
|
|
99
|
+
perProviderLatencyEwma: Record<string, number>;
|
|
100
|
+
perProviderInFlight: Record<string, number>;
|
|
101
|
+
perProviderError: Record<string, number>;
|
|
102
|
+
rpcErrorTotal: number;
|
|
103
|
+
perProviderRpcError: Record<string, Record<string, number>>;
|
|
104
|
+
perMethodRpcError: Record<string, number>;
|
|
105
|
+
perProviderMethod: Record<string, Record<string, number>>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
interface RouterEndpointInput {
|
|
109
|
+
endpoint: Endpoint;
|
|
110
|
+
priority: number;
|
|
111
|
+
}
|
|
142
112
|
declare class Router {
|
|
143
|
-
private readonly
|
|
144
|
-
private readonly
|
|
145
|
-
private
|
|
146
|
-
|
|
113
|
+
private readonly availability;
|
|
114
|
+
private readonly _groups;
|
|
115
|
+
private readonly _size;
|
|
116
|
+
private readonly _ewma;
|
|
117
|
+
constructor(inputs: RouterEndpointInput[], availability: IAvailabilityChecker);
|
|
147
118
|
size(): number;
|
|
119
|
+
totalSlots(): number;
|
|
120
|
+
recordLatency(providerId: string, ms: number): void;
|
|
121
|
+
ewmaSnapshot(): Record<string, number>;
|
|
148
122
|
pick(): Endpoint;
|
|
123
|
+
private _pickFromGroup;
|
|
149
124
|
}
|
|
150
125
|
|
|
151
126
|
interface RPCPoolProviderOptions extends Partial<InstrumentedJsonRpcProviderOptions> {
|
|
152
127
|
url: string | FetchRequest;
|
|
153
128
|
network?: Networkish;
|
|
129
|
+
priority?: number;
|
|
154
130
|
}
|
|
155
131
|
interface RPCPoolProviderParams {
|
|
156
132
|
network: Networkish;
|
|
@@ -167,16 +143,26 @@ interface RPCPoolProviderParams {
|
|
|
167
143
|
hooks?: {
|
|
168
144
|
onEvent(e: RpcEvent): void;
|
|
169
145
|
};
|
|
146
|
+
healthProbe?: {
|
|
147
|
+
intervalMs?: number;
|
|
148
|
+
};
|
|
170
149
|
}
|
|
171
150
|
declare class RPCPoolProvider extends JsonRpcProvider {
|
|
172
151
|
readonly router: Router;
|
|
173
152
|
readonly params: RPCPoolProviderParams;
|
|
174
|
-
readonly
|
|
153
|
+
private readonly _stats;
|
|
154
|
+
private readonly _cooldown;
|
|
155
|
+
private readonly _endpoints;
|
|
156
|
+
private _probeInterval;
|
|
175
157
|
constructor(params: RPCPoolProviderParams);
|
|
158
|
+
destroy(): void;
|
|
176
159
|
send(method: string, params: any): Promise<any>;
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
160
|
+
getSnapshot(): Readonly<RpcStatsSnapshot>;
|
|
161
|
+
pinnedProvider(): InstrumentedJsonRpcProvider;
|
|
162
|
+
private _runHealthProbe;
|
|
163
|
+
private _handleTransportEvent;
|
|
164
|
+
private _sleepWithBackoff;
|
|
165
|
+
private _emitRpcLogicalError;
|
|
180
166
|
}
|
|
181
167
|
|
|
182
|
-
export { RPCPoolProvider, type RPCPoolProviderParams, type RpcEvent, type RpcStatsSnapshot };
|
|
168
|
+
export { type CircuitState, RPCPoolProvider, type RPCPoolProviderOptions, type RPCPoolProviderParams, type RpcEvent, type RpcStatsSnapshot };
|