Skip to Content
EVMWebSockets

WebSockets & Subscriptions

Real-time event streams via WebSocket subscriptions (eth_subscribe) for newHeads, logs, and newPendingTransactions.

Configuration Limits

ParameterDefaultDescription
max_subscriptions_new_head10,000Maximum concurrent newHeads subscriptions per node
Subscription buffer10 blocksInternal buffer per subscription; slow consumers trigger disconnect
Connection capacity100Maximum 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.

Subscription Types

newHeadsStreams canonical block headers. Default cap: max_subscriptions_new_head = 5000. Use for lightweight UI updates.
logsFilters logs by address/topics across live blocks. Obeys max_blocks_for_log and inherits Cosmos rate limits.
newPendingTransactionsSurface pending transactions from the mempool. Sensitive to mempool.cache_size pressure; avoid on public endpoints.

Connection Management

Replay & Gap Handling

When a WebSocket disconnects:

  1. Track last processed block (store block.number and block.hash)
  2. On reconnect, fetch current head via HTTP eth_blockNumber
  3. Backfill gaps using eth_getLogs with block ranges ≤ 2,000 blocks (respects MaxBlocksForLog)
  4. Deduplicate by (transactionHash, logIndex) to handle overlaps
  5. 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 newHeads subscriptions
  • 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_traceTransaction calls to background workers

Troubleshooting

ErrorCauseFix
no new subscription can be createdNode newHeads limit (10,000) reachedCheck node config for max_subscriptions_new_head; reuse existing subscriptions or request limit increase.
Subscription closed unexpectedlyConsumer not draining buffer fast enoughIncrease processing speed or buffer size; slow consumers trigger auto-close.
Missing blocks after reconnectGap in stream during disconnectBackfill using eth_getLogs with last processed block as fromBlock.
Connection drops frequentlyNetwork instability or missing heartbeatsImplement ping/pong heartbeat every 30s; reconnect with exponential backoff.

References

Last updated on