Skip to content

a6b8/rpcBenchmark

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Test PRs Welcome

RPC Benchmark

Validate and benchmark EVM JSON-RPC endpoints with 16 probes across HTTP and WebSocket. Measures latency, batch limits, rate limiting, throughput, WebSocket stability, and more — all from a single module.

Quickstart

git clone https://github.com/a6b8/rpc-benchmark.git
cd rpc-benchmark
npm i
import { RpcBenchmark } from 'rpc-benchmark'

const benchmark = new RpcBenchmark()

const { operational } = await benchmark.runOperational( {
    'urls': [
        { 'url': 'https://ethereum-rpc.publicnode.com', 'protocol': 'http' },
        { 'url': 'wss://ethereum-rpc.publicnode.com', 'protocol': 'ws' }
    ]
} )

Features

  • 16 Probes — 6 operational (health validation) + 10 research (performance analysis)
  • Dual Protocol — HTTP and WebSocket support with protocol-specific probes
  • Event-Driven — Progress, complete, and error events via EventEmitter
  • Parallel Execution — Configurable chunking for concurrent URL processing
  • Provider Detection — Fingerprints Alchemy, Infura, QuickNode, Ankr, PublicNode, Chainstack, dRPC
  • 26 Error Codes — Structured codes across 5 categories with descriptions and recovery guidance
  • Zero Config — Sensible defaults, works out of the box against any EVM endpoint

Table of Contents

Methods

All methods follow the same return pattern: probe results include status, data, and metrics with latencyMs, samples, and errors.

constructor()

Creates a new RpcBenchmark instance.

Method

new RpcBenchmark( { silent } )
Key Type Description Required
silent boolean Suppress console output. Default false No

Example

const benchmark = new RpcBenchmark( { 'silent': true } )

.runOperational()

Runs 6 operational probes against all provided URLs. Tests basic health: chain ID, block sync, gas price, network version, archive support, client version.

Method

.runOperational( { urls, options, chunkSize } )
Key Type Description Required
urls array of objects Endpoints to test. Each: { url: string, protocol: 'http' | 'ws' } Yes
options object Probe-specific options keyed by probe name No
chunkSize number URLs processed in parallel. Default 10 No

Example

const { operational } = await benchmark.runOperational( {
    'urls': [
        { 'url': 'https://ethereum-rpc.publicnode.com', 'protocol': 'http' },
        { 'url': 'wss://ethereum-rpc.publicnode.com', 'protocol': 'ws' }
    ],
    'options': {
        'chainId': { 'expectedChainId': 1 },
        'netVersion': { 'expectedNetVersion': '1' }
    }
} )

Returns

{
    operational: [
        {
            url: 'https://...',
            protocol: 'http',
            status: true,
            probes: {
                chainId:       { status, data, metrics },
                blockSync:     { status, data, metrics },
                gasPrice:      { status, data, metrics },
                netVersion:    { status, data, metrics },
                archive:       { status, data, metrics },
                clientVersion: { status, data, metrics }
            }
        }
    ]
}

.runResearch()

Runs all 16 probes (6 operational + 10 research). Includes latency statistics, batch limits, rate limiting detection, throughput measurement, and WebSocket stability.

Method

.runResearch( { urls, options, chunkSize } )
Key Type Description Required
urls array of objects Endpoints to test. Each: { url: string, protocol: 'http' | 'ws' } Yes
options object Probe-specific options keyed by probe name No
chunkSize number URLs processed in parallel. Default 5 No

Example

const { research, summary } = await benchmark.runResearch( {
    'urls': [ { 'url': 'https://ethereum-rpc.publicnode.com', 'protocol': 'http' } ],
    'options': {
        'latency': { 'sampleCount': 50 },
        'batchLimit': { 'maxSearch': 500 },
        'throughput': { 'durationMs': 5000 }
    }
} )

Returns

{
    research: [
        {
            url: 'https://...',
            protocol: 'http',
            status: true,
            operational: { chainId: {...}, blockSync: {...}, ... },
            research: { latency: {...}, batchLimit: {...}, ... }
        }
    ],
    summary: { total: 1, passed: 1, failed: 0 }
}

.runProbe()

Runs a single named probe against all provided URLs.

Method

.runProbe( { probeName, urls, options } )
Key Type Description Required
probeName string Name of the probe to run (see Probes) Yes
urls array of objects Endpoints to test Yes
options object Options for the specific probe No

Example

const { results, summary } = await benchmark.runProbe( {
    'probeName': 'latency',
    'urls': [ { 'url': 'https://ethereum-rpc.publicnode.com', 'protocol': 'http' } ],
    'options': { 'sampleCount': 10 }
} )

Returns

{
    results: [ { url, protocol, status, probes: { latency: { status, data, metrics } } } ],
    summary: { total: 1, passed: 1, failed: 0 }
}

.getProbeList()

Returns metadata for all available probes. Static method — no instance required.

Method

RpcBenchmark.getProbeList()

Example

const { probeList } = RpcBenchmark.getProbeList()

Returns

{
    probeList: [
        { name: 'chainId', category: 'operational', protocols: ['http','ws'], description: '...' },
        { name: 'latency', category: 'research', protocols: ['http','ws'], description: '...' },
        ...
    ]
}

Probes

Operational Probes

Health validation probes that verify an endpoint is functional and correctly configured.

Probe Protocols Description
chainId http, ws Verifies chain identity via eth_chainId. Options: expectedChainId
blockSync http, ws Checks block sync status via eth_blockNumber. Options: maxDrift, referenceBlock
gasPrice http, ws Validates data freshness via eth_gasPrice
netVersion http, ws Confirms network identity via net_version. Options: expectedNetVersion
archive http, ws Tests archive node capability via eth_getLogs on early blocks. Options: fromBlock, toBlock
clientVersion http, ws Retrieves node software version via web3_clientVersion

Research Probes

Performance analysis probes that measure endpoint capabilities and limits.

Probe Protocols Description
latency http, ws Multi-sample latency measurement with min/max/mean/median/p95/p99/stddev. Options: sampleCount (default: 20)
batchLimit http Determines max JSON-RPC batch size via binary search. Options: maxSearch (default: 1000)
multicallStress http Finds max Multicall3 aggregate payload size. Options: maxCalls (default: 500), step (default: 10)
rateLimit http Detects rate limiting via parallel burst requests. Options: burstSize (default: 50)
throughput http Measures sustained requests per second. Options: durationMs (default: 10000)
responseSizeLimit http Determines max eth_getLogs response size by expanding block ranges. Options: startRange, maxRange
providerFingerprint http Identifies provider (Alchemy, Infura, QuickNode, Ankr, PublicNode, Chainstack, dRPC) via headers and error patterns
wsStability ws Tests long-running WebSocket connection stability with periodic pings. Options: durationMs, pingIntervalMs
wsSubscription ws Tests newHeads subscription and block delivery latency. Options: durationMs
wsConcurrency ws Determines max parallel WebSocket connections. Options: maxConnections (default: 20)

Error Codes

All errors follow the structure { code: string, message: string }. The code field uses the format CATEGORY_NAME and can be looked up via the ErrorLookup helper.

import { ErrorLookup } from 'rpc-benchmark'

// Look up a specific error
const { error } = ErrorLookup.getByCode( { 'code': 'PROBE_TIMEOUT' } )
// → { code, category, severity, recoverable, message, description }

// Get all errors for a category
const { errors } = ErrorLookup.getByCategory( { 'category': 'net' } )

// Get all recoverable errors
const { recoverable } = ErrorLookup.getRecoverable()

// Get all 26 error codes
const { codes } = ErrorLookup.getAllCodes()

INPUT

Validation errors — thrown before any probe executes.

Code Severity Message
INPUT_MISSING_URLS error The urls parameter is missing or undefined
INPUT_INVALID_URL_FORMAT error A url entry has an invalid format
INPUT_INVALID_PROTOCOL error Protocol value is not supported
INPUT_INVALID_OPTIONS error Options parameter has an invalid format
INPUT_INVALID_CHUNK_SIZE error Chunk size value is invalid
INPUT_UNKNOWN_PROBE error Probe name is not recognized

PROBE

Probe-level errors — returned in metrics.errors of individual probe results.

Code Severity Recoverable Message
PROBE_TIMEOUT warn Yes Probe execution timed out
PROBE_UNEXPECTED_RESULT warn Yes Probe received unexpected or unparseable data
PROBE_CHAIN_ID_MISMATCH error No Chain ID does not match the expected value
PROBE_BLOCK_DRIFT warn Yes Block number drift exceeds the allowed threshold
PROBE_NOT_ARCHIVE info Yes Endpoint does not support archive queries
PROBE_EXCEPTION error Yes Probe threw an unexpected exception

NET

Network-level errors — connection, DNS, TLS, and HTTP failures.

Code Severity Recoverable Message
NET_CONNECTION_REFUSED error Yes Connection to the endpoint was refused
NET_DNS_RESOLUTION_FAILED error No DNS lookup for the endpoint hostname failed
NET_TLS_ERROR error No TLS/SSL handshake failed
NET_FETCH_FAILED error Yes HTTP request failed at the network level
NET_HTTP_ERROR error Yes HTTP response returned a non-2xx status code

RPC

JSON-RPC protocol errors — invalid responses, unsupported methods, rate limits.

Code Severity Recoverable Message
RPC_INVALID_RESPONSE error Yes RPC response has an invalid or unexpected format
RPC_METHOD_NOT_FOUND warn No The requested RPC method is not supported
RPC_RATE_LIMITED warn Yes Request was rate limited by the provider
RPC_SERVER_ERROR error Yes RPC server returned an internal error

WS

WebSocket-specific errors — connection, messaging, subscriptions.

Code Severity Recoverable Message
WS_CONNECTION_FAILED error Yes WebSocket connection could not be established
WS_MESSAGE_TIMEOUT warn Yes WebSocket message response timed out
WS_UNEXPECTED_CLOSE warn Yes WebSocket connection was closed unexpectedly
WS_SUBSCRIPTION_FAILED error Yes WebSocket subscription was rejected by the provider
WS_CLOSE_FAILED info Yes WebSocket close operation failed

Events

RpcBenchmark extends EventEmitter and emits three event types:

const benchmark = new RpcBenchmark()

benchmark.on( 'progress', ( { completed, total, percent, url, protocol } ) => {
    console.log( `${percent}% (${completed}/${total})` )
} )

benchmark.on( 'complete', ( { total, results } ) => {
    console.log( `Done: ${total} URLs tested` )
} )

benchmark.on( 'error', ( { url, protocol, probe, error } ) => {
    console.log( `Error in ${probe}: ${error}` )
} )
Event Payload Description
progress { completed, total, percent, url, protocol } Emitted after each URL completes
complete { total, results } Emitted when all URLs are done
error { url, protocol, probe, error } Emitted on unhandled probe exception

Contributing

Contributions are welcome. Please open an issue or submit a pull request.

npm test                  # Run all tests
npm run test:coverage:src # Run with coverage

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors