> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stablenet.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Listen to On-Chain Events

> Subscribe to real-time StableNet events using WebSocket and query historical logs with eth_getLogs.

> Stream new blocks and contract events from StableNet using WebSocket subscriptions and log queries.

## What you'll learn

By the end of this tutorial, you'll be able to:

* Subscribe to new block headers via WebSocket
* Subscribe to contract events matching a topic filter
* Query historical logs for a contract using `eth_getLogs`

## Prerequisites

* Node.js ≥ 18
* `npm install ethers` (v6)
* A deployed contract that emits events (e.g., the `WKRCPayment` contract from [Deploy with Foundry](/en/v0.3/build/tutorials/deploy-with-foundry))

## Connect via WebSocket

`eth_subscribe` requires a persistent WebSocket connection. HTTP endpoints do not support subscriptions.

```typescript theme={null}
import { ethers } from "ethers";

const wsProvider = new ethers.WebSocketProvider(
  "wss://api.test.stablenet.network/ws"
);
```

## Subscribe to new block headers

Each new block emits a header event. Use this to track chain progress or trigger polling logic.

```typescript theme={null}
wsProvider.on("block", async (blockNumber) => {
  const block = await wsProvider.getBlock(blockNumber);
  console.log(`Block ${block.number} — hash: ${block.hash}`);
});
```

```text theme={null}
Block 1234567 — hash: 0xabc...
Block 1234568 — hash: 0xdef...
```

## Subscribe to contract events

To receive events from a specific contract, pass the contract's ABI and address to an `ethers.Contract` instance. The example below listens for `Payment` events from the `WKRCPayment` contract.

```typescript theme={null}
const CONTRACT_ADDRESS = "0xYourDeployedContract";
const ABI = [
  "event Payment(address indexed from, address indexed to, uint256 amount)"
];

const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, wsProvider);

contract.on("Payment", (from, to, amount, event) => {
  console.log(`Payment from ${from} to ${to}: ${ethers.formatEther(amount)} WKRC`);
  console.log(`  tx: ${event.log.transactionHash}`);
});
```

```text theme={null}
Payment from 0xSender to 0xRecipient: 1.0 WKRC
  tx: 0xabc...
```

## Filter events by topic

To receive events only from a specific sender, add a topic filter. EVM topic filters use `null` as a wildcard.

```typescript theme={null}
// Listen only for payments sent by a specific address
const filter = contract.filters.Payment("0xSpecificSender");
contract.on(filter, (from, to, amount) => {
  console.log(`Filtered payment: ${ethers.formatEther(amount)} WKRC`);
});
```

## Query historical logs with eth\_getLogs

Use `eth_getLogs` to query past events without a persistent connection. Specify a block range and optional topic filters.

```typescript theme={null}
const httpProvider = new ethers.JsonRpcProvider(
  "https://api.test.stablenet.network"
);

const logs = await httpProvider.getLogs({
  address: CONTRACT_ADDRESS,
  fromBlock: 1200000,
  toBlock: "latest",
  topics: [
    ethers.id("Payment(address,address,uint256)")  // keccak256 of event signature
  ],
});

for (const log of logs) {
  const parsed = contract.interface.parseLog(log);
  console.log(
    `Block ${log.blockNumber}: ${ethers.formatEther(parsed.args.amount)} WKRC`
  );
}
```

```text theme={null}
Block 1234500: 1.0 WKRC
Block 1234512: 0.5 WKRC
```

## Clean up subscriptions

Remove event listeners when you no longer need them to avoid memory leaks.

```typescript theme={null}
// Remove a specific listener
contract.off("Payment", listenerFunction);

// Remove all listeners for an event
contract.removeAllListeners("Payment");

// Close the WebSocket connection
await wsProvider.destroy();
```

## Topic matching rules

Topics map to indexed event parameters. Multiple values in one position are OR conditions; positions without a value are wildcards.

| Position               | Meaning                                |
| ---------------------- | -------------------------------------- |
| `topics[0]`            | Event signature hash (always required) |
| `topics[1]`            | First indexed parameter (`from`)       |
| `topics[2]`            | Second indexed parameter (`to`)        |
| `null` at any position | Matches any value                      |

A maximum of 4 topic positions is supported.

## Next steps

<CardGroup cols={2}>
  <Card title="Deploy with Foundry" href="/en/v0.3/build/tutorials/deploy-with-foundry">Deploy a contract that emits events.</Card>
  <Card title="RPC API Reference" href="/en/v0.3/reference/rpc-api">Full reference for eth\_subscribe, eth\_getLogs, and filter methods.</Card>
  <Card title="Read On-Chain Data" href="/en/v0.3/read-onchain-data">Query balances, storage slots, and contract state.</Card>
  <Card title="Fee Delegation" href="/en/v0.3/build/tutorials/fee-delegation">Let a separate account pay gas for your users.</Card>
</CardGroup>
