Skip to main content

WebSocket connections

Sei supports eth_subscribe over WebSocket. With the WebSocket transports in standard libraries, you can subscribe to new blocks, event logs, and pending transactions.

Endpoints

Connecting

Watching new blocks

Watching contract events

Watching ERC-20 transfers across all contracts

viem

Notes

  • Sei has instant finality, so every block emitted over WebSocket is already final. You do not need to wait for more confirmations before you act on an event.
  • Pending transaction subscriptions (newPendingTransactions) are supported at the RPC level, but Sei does not guarantee Ethereum-style pending state visibility.

Frame size and concurrency limits

The WebSocket plane shares its admission-control settings with the HTTP JSON-RPC plane through the [evm] section of app.toml. Two limits apply to every WebSocket connection:

Frame size (max_request_body_bytes)

Each inbound WebSocket frame is capped by [evm].max_request_body_bytes, the same knob that bounds HTTP request bodies. The default is 5 MiB (5242880). Frames larger than this limit cause the connection to close with WebSocket close code 1009 (message too big); no JSON-RPC error response is returned.
Earlier node releases used a hardcoded 10 MiB frame cap on the WebSocket plane. Both planes now share max_request_body_bytes with a 5 MiB default. Operators whose WebSocket clients send frames in the 5–10 MiB range (for example large eth_sendRawTransaction batches or wide filter payloads) should set max_request_body_bytes = 10485760 in app.toml before upgrading. Note that this also raises the HTTP body limit to 10 MiB.

Concurrent request budget (max_concurrent_request_bytes and ws_admission_timeout)

[evm].max_concurrent_request_bytes bounds the total size of JSON-RPC request bytes admitted for processing concurrently. The WebSocket plane and the HTTP plane each get their own independent budget, so peak in-flight request bytes process-wide can reach twice this value. When a WebSocket connection cannot be admitted immediately because the budget is exhausted, it blocks and waits for budget to free up. [evm].ws_admission_timeout bounds how long it waits (default 30s; zero or negative values use the go-ethereum default of 30s). If the wait expires, the peer receives JSON-RPC error -32005 (“timed out waiting for concurrent request-byte budget”) and the connection is closed, dropping any active subscriptions along with it.

newHeads under Autobahn consensus

When a node runs under Autobahn consensus, eth_subscribe("newHeads") notifications come from an in-process notifier, not from the legacy consensus event bus. The notifier publishes committed-block headers directly. Subscribers still observe headers only for fully committed blocks. However, the header payload differs from the legacy path in a few ways:
  • parentHash, receiptsRoot, and transactionsRoot are returned as zero hashes (0x0000…0000). The Autobahn block-execution path does not build a Tendermint-style hash chain, so there is no meaningful value to return in these fields.
  • stateRoot comes from the finalized block’s AppHash (the post-execution application hash), not from a pre-execution header field.
  • hash is the Autobahn block-header hash. It is the same value that eth_getBlockByNumber and the receipt APIs report as blockHash. This keeps newHeads consistent with the rest of the EVM RPC surface.
  • gasUsed is an approximation (summed from per-transaction results) that keeps the notification cheap.
Because of these differences:
  • Subscribers that chain-validate the head stream by linking parentHash values cannot rely on newHeads under Autobahn. They need a different mechanism.
  • If you need the exact gasUsed or the omitted hash fields, fetch the block explicitly with eth_getBlockByNumber.