WebSockets & Subscriptions
Real-time event streams via WebSocket subscriptions (eth_subscribe) for newHeads, logs, and newPendingTransactions.
Configuration Limits
| Parameter | Default | Description |
|---|---|---|
max_subscriptions_new_head | 10,000 | Maximum concurrent newHeads subscriptions per node |
| Subscription buffer | 10 blocks | Internal buffer per subscription; slow consumers trigger disconnect |
| Connection capacity | 100 | Maximum subscriptions per connection (all types combined) |
⚠️
Nodes enforce
max_subscriptions_new_head globally. When the limit is reached, new eth_subscribe('newHeads') calls return no new subscription can be created.Supported Subscription Types
newHeads
Streams new block headers as they’re produced (~400ms on Sei).
// Function: eth_subscribe('newHeads')
import WebSocket from 'ws';
const ws = new WebSocket('wss://evm-rpc.sei-apis.com');
ws.on('open', () => {
ws.send(
JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'eth_subscribe',
params: ['newHeads']
})
);
});
ws.on('message', (data) => {
const msg = JSON.parse(data.toString());
if (msg.method === 'eth_subscription') {
console.log('New block:', msg.params.result.number);
}
});logs
Streams logs matching filter criteria (address, topics).
// Function: eth_subscribe('logs', filter)
ws.send(
JSON.stringify({
jsonrpc: '2.0',
id: 2,
method: 'eth_subscribe',
params: [
'logs',
{
address: '0xYourContract...',
topics: ['0xYourEventSignature...']
}
]
})
);newPendingTransactions
Streams transaction hashes as they enter the mempool.
// Function: eth_subscribe('newPendingTransactions')
ws.send(
JSON.stringify({
jsonrpc: '2.0',
id: 3,
method: 'eth_subscribe',
params: ['newPendingTransactions']
})
);Connection Management
Heartbeats:
- Send ping frames every 30–60 seconds
- Missing pongs indicate stale connection; reconnect immediately
Backoff on reconnect:
- Use exponential backoff with jitter (start 1s, max 30s)
- After reconnect, fetch latest block via HTTP before resubscribing to establish checkpoint
Subscription cleanup:
- Always call
eth_unsubscribebefore closing connection - Node auto-cleans subscriptions on disconnect, but explicit cleanup is best practice
Replay & Gap Handling
When a WebSocket disconnects:
- Track last processed block (store
block.numberandblock.hash) - On reconnect, fetch current head via HTTP
eth_blockNumber - Backfill gaps using
eth_getLogswith block ranges ≤ 2,000 blocks (respectsMaxBlocksForLog) - Deduplicate by
(transactionHash, logIndex)to handle overlaps - Resume subscription from current head
// Gap backfill pattern
async function backfillGap(fromBlock: number, toBlock: number) {
const logs = await httpProvider.send('eth_getLogs', [
{
fromBlock: `0x${fromBlock.toString(16)}`,
toBlock: `0x${toBlock.toString(16)}`,
address: contractAddress
}
]);
// Deduplicate and process
const seen = new Set();
for (const log of logs) {
const key = `${log.transactionHash}-${log.logIndex}`;
if (!seen.has(key)) {
seen.add(key);
processLog(log);
}
}
}Best Practices
- One subscription per topic - Fan out internally rather than creating duplicate
newHeadssubscriptions - Monitor buffer health - Track dropped subscriptions (channel closure) as a signal of slow consumption
- Hybrid approach - Use WebSocket for real-time updates, HTTP for historical queries and backfills
- Avoid trace/sim during WS handling - Offload heavy
debug_traceTransactioncalls to background workers
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
no new subscription can be created | Node newHeads limit (10,000) reached | Check node config for max_subscriptions_new_head; reuse existing subscriptions or request limit increase. |
Subscription closed unexpectedly | Consumer not draining buffer fast enough | Increase processing speed or buffer size; slow consumers trigger auto-close. |
Missing blocks after reconnect | Gap in stream during disconnect | Backfill using eth_getLogs with last processed block as fromBlock. |
Connection drops frequently | Network instability or missing heartbeats | Implement ping/pong heartbeat every 30s; reconnect with exponential backoff. |
References
- WebSocket implementation: github.com/sei-protocol/sei-chain/evmrpc/subscribe.go
- Configuration: EVM RPC Config
Last updated on