> ## Documentation Index
> Fetch the complete documentation index at: https://seilabs-docs-bridge-release-v6-6-0.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# WebSocket Connections

> Connecting to Sei via WebSocket for real-time block and event subscriptions

# WebSocket Connections

Sei supports `eth_subscribe` over WebSocket. You can subscribe to new blocks, event logs, and pending transactions using standard library WebSocket transports.

## Endpoints

| Network | WebSocket endpoint                   |
| ------- | ------------------------------------ |
| Mainnet | `wss://evm-ws.sei-apis.com`          |
| Testnet | `wss://evm-ws-testnet.sei-apis.com`  |
| Devnet  | `wss://evm-ws-arctic-1.sei-apis.com` |

## Connecting

<CodeGroup>
  ```ts viem theme={"dark"}
  import { createPublicClient, webSocket } from 'viem';
  import { sei } from 'viem/chains';

  const client = createPublicClient({
    chain: sei,
    transport: webSocket('wss://evm-ws.sei-apis.com'),
  });
  ```

  ```ts ethers theme={"dark"}
  import { ethers } from 'ethers';

  const provider = new ethers.WebSocketProvider('wss://evm-ws.sei-apis.com');
  ```
</CodeGroup>

## Watching New Blocks

<CodeGroup>
  ```ts viem theme={"dark"}
  const unwatch = client.watchBlocks({
    onBlock: (block) => {
      console.log('New block:', block.number);
    },
  });

  // Stop watching
  unwatch();
  ```

  ```ts ethers theme={"dark"}
  provider.on('block', (blockNumber) => {
    console.log('New block:', blockNumber);
  });

  // Stop watching
  provider.off('block');
  ```
</CodeGroup>

## Watching Contract Events

<CodeGroup>
  ```ts viem theme={"dark"}
  import { parseAbiItem } from 'viem';

  const unwatch = client.watchEvent({
    address: '0xContractAddress',
    event: parseAbiItem('event Transfer(address indexed from, address indexed to, uint256 value)'),
    onLogs: (logs) => {
      console.log('Transfer events:', logs);
    },
  });
  ```

  ```ts ethers theme={"dark"}
  import { ethers } from 'ethers';

  const ERC20_ABI = ['event Transfer(address indexed from, address indexed to, uint256 value)'];
  const contract = new ethers.Contract('0xContractAddress', ERC20_ABI, provider);

  contract.on('Transfer', (from, to, value, event) => {
    console.log('Transfer:', { from, to, value });
  });

  // Stop watching
  contract.off('Transfer');
  ```
</CodeGroup>

## Watching ERC-20 Transfers Across All Contracts

```ts viem theme={"dark"}
import { parseAbiItem } from 'viem';

const unwatch = client.watchEvent({
  event: parseAbiItem('event Transfer(address indexed from, address indexed to, uint256 value)'),
  onLogs: (logs) => {
    logs.forEach((log) => {
      console.log(`Transfer on ${log.address}:`, log.args);
    });
  },
});
```

## Notes

* Sei's instant finality means every block emitted over WebSocket is already final — no need to wait for additional confirmations before acting on an event.
* Pending transaction subscriptions (`newPendingTransactions`) are supported at the RPC level but Sei does not guarantee Ethereum-style pending state visibility.

## `newHeads` Under Autobahn Consensus

When a node runs under Autobahn consensus, `eth_subscribe("newHeads")` notifications are delivered from an in-process notifier that publishes committed-block headers directly, rather than from the legacy consensus event bus. Subscribers still only observe headers for fully committed blocks, but 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 surface for these fields.
* **`stateRoot`** is sourced from the finalized block's `AppHash` (the post-execution application hash), rather than from a pre-execution header field.
* **`hash`** is the Autobahn block-header hash — the same value reported as `blockHash` by `eth_getBlockByNumber` and the receipt APIs, keeping `newHeads` consistent with the rest of the EVM RPC surface.
* **`gasUsed`** is an approximation (summed from per-transaction results) to keep the notification cheap.

Because of these differences:

* Subscribers that chain-validate the head stream by linking `parentHash` values cannot rely on `newHeads` under Autobahn and need a different mechanism.
* If you need exact `gasUsed` or the omitted hash fields, fetch the block explicitly with `eth_getBlockByNumber`.
