Rate-limiting middleware for Koa Json Rpc. Use to limit repeated requests to APIs and/or endpoints by IP.
- On each incoming request, the
IPRateLimiterServiceconstructs a key combining the client IP and method name (e.g.,ratelimit:{ip}:{method}). - It selects a rate limit store backend (LRU or Redis) based on the
IP_RATE_LIMIT_STOREandREDIS_ENABLEDenvironment variables with the possibility to be extended and use a custom store (more info below). - The store's
incrementAndCheck(key, limit, duration)method is invoked:- LRU: maintains counts in an in-memory map and resets them after the configured duration.
- Redis: uses an atomic Lua script to
INCRand setEXPIRE, ensuring cross-pod consistency.
- If the request count exceeds the limit, the service logs a warning, increments the Prometheus counter
rpc_relay_ip_rate_limit, andshouldRateLimitreturnstrue(blocking the request). - Setting
RATE_LIMIT_DISABLED=truebypasses all rate limiting checks globally.
All rate-limiting options are exposed and can be configured from .env .
Limit tiers are total number of requests for a configurable duration per IP and endpoint.
DEFAULT_RATE_LIMIT = 200;
TIER_1_RATE_LIMIT = 100;
TIER_2_RATE_LIMIT = 800;
TIER_3_RATE_LIMIT = 1600;
LIMIT_DURATION = 60000;
RATE_LIMIT_DISABLED = false;- DEFAULT_RATE_LIMIT: - default fallback rate limit, if no other is configured. Default is to
200(200 request per IP). - TIER_1_RATE_LIMIT: - restrictive limiting tier, for expensive endpoints. Default is to
100(100 request per IP). - TIER_2_RATE_LIMIT: - moderate limiting tier, for non expensive endpoints. Default is to
800(800 request per IP). - TIER_3_RATE_LIMIT: - relaxed limiting tier. Default is to
1600(1600 request per IP). - LIMIT_DURATION: - reset limit duration. This creates a timestamp, which resets all limits, when it's reached. Default is to
60000(1 minute). - RATE_LIMIT_DISABLED: - if set to
trueno rate limiting will be performed.
You can configure which backend store to use for rate limiting via environment variables:
- IP_RATE_LIMIT_STORE: Specifies the rate limit store to use. Valid values:
- "LRU": In-memory store.
- "REDIS": Redis-based store.
- Any other custom type if you have implemented a custom store and added it to the
RateLimitStoreTypeenum. If not set, the relay will fall back to using Redis whenREDIS_ENABLED=true, otherwise it uses LRU.
- REDIS_ENABLED: If
true, enables Redis-based rate limiting whenIP_RATE_LIMIT_STOREis not explicitly set. Default:false. - REDIS_URL: The Redis connection URL (e.g.
redis://localhost:6379). Required when using Redis store.
To extend with a custom store:
-
Implement a class that implements
RateLimitStore. -
Add your custom store type string to the
RateLimitStoreTypearray inpackages/relay/src/lib/types/rateLimiter.ts. -
Start the relay with
IP_RATE_LIMIT_STORE=MyCustomStoreimport { RateLimitStore } from '@hashgraph/json-rpc-relay/dist/lib/types'; class MyCustomStore implements RateLimitStore { constructor(options: MyOptions) { /* ... */ } async incrementAndCheck(key: string, limit: number, durationMs: number): Promise<boolean> { // custom logic } }
The following table highlights each relay endpoint and the TIER associated with it as dictated by methodConfiguration.ts
| Method endpoint | Tier |
|---|---|
eth_accounts |
TIER_2_RATE_LIMIT |
eth_blockNumber |
TIER_2_RATE_LIMIT |
eth_call |
TIER_1_RATE_LIMIT |
eth_chainId |
TIER_2_RATE_LIMIT |
eth_coinbase |
TIER_2_RATE_LIMIT |
eth_blobBaseFee |
TIER_2_RATE_LIMIT |
eth_estimateGas |
TIER_2_RATE_LIMIT |
eth_feeHistory |
TIER_2_RATE_LIMIT |
eth_gasPrice |
TIER_2_RATE_LIMIT |
eth_getBalance |
TIER_2_RATE_LIMIT |
eth_getCode |
TIER_2_RATE_LIMIT |
eth_getBlockByHash |
TIER_2_RATE_LIMIT |
eth_getBlockByNumber |
TIER_2_RATE_LIMIT |
eth_getBlockTransactionCountByHash |
TIER_2_RATE_LIMIT |
eth_getBlockTransactionCountByNumber |
TIER_2_RATE_LIMIT |
eth_getFilterLogs |
TIER_2_RATE_LIMIT |
eth_getFilterChanges |
TIER_2_RATE_LIMIT |
eth_getLogs |
TIER_2_RATE_LIMIT |
eth_getStorageAt |
TIER_2_RATE_LIMIT |
eth_getTransactionByBlockHashAndIndex |
TIER_2_RATE_LIMIT |
eth_getTransactionByBlockNumberAndIndex |
TIER_2_RATE_LIMIT |
eth_getTransactionByHash |
TIER_2_RATE_LIMIT |
eth_getTransactionCount |
TIER_2_RATE_LIMIT |
eth_getTransactionReceipt |
TIER_2_RATE_LIMIT |
eth_getUncleByBlockHashAndIndex |
TIER_2_RATE_LIMIT |
eth_getUncleByBlockNumberAndIndex |
TIER_2_RATE_LIMIT |
eth_getUncleCountByBlockHash |
TIER_2_RATE_LIMIT |
eth_getUncleCountByBlockNumber |
TIER_2_RATE_LIMIT |
eth_getWork |
TIER_2_RATE_LIMIT |
eth_hashrate |
TIER_1_RATE_LIMIT |
eth_maxPriorityFeePerGas |
TIER_1_RATE_LIMIT |
eth_mining |
TIER_1_RATE_LIMIT |
eth_newBlockFilter |
TIER_2_RATE_LIMIT |
eth_newFilter |
TIER_2_RATE_LIMIT |
eth_newPendingTransactionFilter |
TIER_2_RATE_LIMIT |
eth_protocolVersion |
TIER_2_RATE_LIMIT |
eth_sendRawTransaction |
TIER_1_RATE_LIMIT |
eth_sendTransaction |
TIER_1_RATE_LIMIT |
eth_signTransaction |
TIER_1_RATE_LIMIT |
eth_sign |
TIER_1_RATE_LIMIT |
eth_submitHashrate |
TIER_1_RATE_LIMIT |
eth_submitWork |
TIER_1_RATE_LIMIT |
eth_syncing |
TIER_1_RATE_LIMIT |
engine_* (all engine methods) |
TIER_2_RATE_LIMIT |
trace_* (all engine methods) |
TIER_2_RATE_LIMIT |
debug_* (all engine methods) |
TIER_2_RATE_LIMIT |
net_listening |
TIER_3_RATE_LIMIT |
net_version |
TIER_3_RATE_LIMIT |
net_peerCount |
TIER_3_RATE_LIMIT |
web3_clientVersion |
TIER_3_RATE_LIMIT |
web3_sha3 |
TIER_3_RATE_LIMIT |
txpool_content |
TIER_2_RATE_LIMIT |
txpool_contentFrom |
TIER_2_RATE_LIMIT |
txpool_status |
TIER_2_RATE_LIMIT |