Skip to main content
TLDR:
  • Connect the ethers v6 WebSocketProvider to a Chainstack Ethereum WSS endpoint, and pass staticNetwork so the log subscription keeps delivering.
  • Attach a Contract listener for the pool’s Swap event — the node pushes each matching log the moment the block lands onchain, with no block scanning or polling.
  • Decode amount0 and amount1 from the event: a negative amount is the token the pool paid out (what the trader received), a positive amount is the token it took in (what the trader paid).
  • This tutorial monitors the USDC/WETH 0.05% Uniswap V3 pool 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640, whose Swap topic0 is 0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67.

What you’ll build

A small Node.js script that opens one WebSocket connection to Ethereum mainnet and prints every swap on a Uniswap pool as it happens — the traded token amounts, the direction of the trade, and the transaction hash. The same pattern works for any Uniswap V3 pool or V2 pair by changing one address and the event definition. Subscribing to the pool’s Swap event is the direct way to watch trades. The node streams you only the logs you asked for, so you react the instant a swap is mined instead of pulling full blocks and filtering transactions yourself.

Prerequisites

  • Node.js 18 or later.
  • ethers v6 — install it with npm install ethers. This tutorial uses the v6 API (ethers.WebSocketProvider), which differs from v5’s ethers.providers.WebSocketProvider.
  • An Ethereum mainnet WSS endpoint from Chainstack.

How Uniswap emits swaps

Every Uniswap pool contract emits a Swap event log each time a trade settles against it. Watching that one event over a WebSocket connection is the most direct way to track trades in real time: the node pushes each matching log as soon as the block is mined, so you never poll or download whole blocks. Each event is identified by its topic0 — the keccak256 hash of the event signature. Filtering a subscription by the pool address and this topic0 tells the node to send you that pool’s swaps and nothing else. This tutorial uses the USDC/WETH 0.05% Uniswap V3 pool at 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640. Its Swap event signature and topic0 are:
  • Signature — Swap(address,address,int256,int256,uint160,uint128,int24)
  • topic0 — 0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67
Uniswap V2 pairs emit a differently shaped Swap event with its own topic0. To watch a V2 pair, see Monitor a different pool or a Uniswap V2 pair.

Get your Ethereum WebSocket endpoint

A WebSocket (WSS) endpoint keeps a single connection open so the node can push new logs to you, which is what makes real-time subscriptions possible. Get one from Chainstack:
1

Sign in to Chainstack

Log in to your Chainstack account.
2

Deploy an Ethereum node

Create a project, then deploy an Ethereum mainnet node. Global Nodes deploy instantly and are available on every plan.
3

Copy the WSS endpoint

Open the node and, on the Access and credentials tab, copy the WSS endpoint — it starts with wss://.
Keep the endpoint private and out of source control — anyone with it can send requests against your node.

Subscribe to the pool’s Swap events

The script below connects to the pool, reads the two tokens once so it can label and scale the amounts, then listens for Swap events and decodes each one. Save it as monitor.js and set WSS_ENDPOINT to your Chainstack endpoint.
monitor.js
The Swap event reports two signed amounts from the pool’s point of view. In one swap, exactly one amount is negative and the other positive — a negative amount is the token the pool paid out (what the trader received), and a positive amount is the token the pool took in (what the trader paid). The code reads token0 and token1 from the pool so it can attach the right symbol and scale each amount by that token’s decimals with ethers.formatUnits. Dividing the two scaled amounts gives the trade’s execution price. The event also carries sqrtPriceX96, liquidity, and tick, which describe the pool state after the swap. This tutorial focuses on the traded amounts.
Passing staticNetwork is what keeps the subscription alive. Without it, ethers v6 periodically re-checks the chain ID; on a load-balanced endpoint those checks can reset the connection state and the log subscription silently stops delivering events.

Run the monitor

Run the script with Node:
It prints a line per swap as trades settle on the pool. A short live run against the USDC/WETH pool produced the following — your output will show whatever swaps happen during your run:
Each line reflects a real settled trade: in the first swap the pool paid out 51,098.84 USDC and took in 29.40 WETH, so the trader sold WETH for USDC at roughly 1,738 USDC per WETH.

Keep the WebSocket connection alive

WebSocket connections drop — from idle timeouts, network blips, or node restarts. A monitor that must run unattended should recreate the provider when the socket closes. Wrap the setup in a function and reconnect on the close event:
pool.removeAllListeners() clears the handlers from the old provider before the new connection re-registers them, which avoids duplicate logging after a reconnect.

Subscribe with a raw topic filter

If you want to filter by topic0 directly — without a Contract listener, or when you only care about the raw log — subscribe with an event filter. Under the hood ethers issues an eth_subscribe for logs, and you decode each log with an Interface:
Computing swapTopic from the event signature — rather than pasting the hash — keeps the filter correct if you change the event. The value it produces is 0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67.

Monitor a different pool or a Uniswap V2 pair

To watch another Uniswap V3 pool, change POOL_ADDRESS to that pool’s contract. Every V3 pool emits the same Swap event, so the topic0 and decoding stay the same — only the token symbols and decimals differ, and the script reads those from the pool. You can find a pool’s address on a block explorer or through the Uniswap factory’s getPool(tokenA, tokenB, fee). Uniswap V2 pairs emit a different Swap event, so a V2 monitor needs a different definition and topic0:
  • Signature — Swap(address,uint256,uint256,uint256,uint256,address)
  • topic0 — 0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822
A V2 pair reports four unsigned amounts instead of two signed ones — amount0In, amount1In, amount0Out, and amount1Out. The token that flows in has a non-zero In amount, and the token that flows out has a non-zero Out amount. Swap the event string in the ABI for the V2 shape and read the four fields:

Troubleshooting

No events arrive, but the script stays connected. This is almost always the network-detection issue. Construct the provider with a static network — new ethers.WebSocketProvider(url, network, { staticNetwork: network }) — so ethers stops re-polling the chain ID and the log subscription keeps delivering. The connection drops after a while. WebSocket connections are not permanent. Recreate the provider on the socket’s close event so the monitor reconnects on its own. No swaps show up at all. Confirm the pool address and topic0 are correct, and that the pool is actually trading — a low-volume pool can sit quiet for minutes. Check the pool on a block explorer to confirm recent Swap events. The USDC/WETH 0.05% pool used here trades many times per minute. The amounts look wrong. Scale each amount by the correct token decimals — USDC uses 6, WETH uses 18 — and make sure you’re using the right event shape: Uniswap V3 emits two signed amounts, while Uniswap V2 emits four unsigned ones.

Next steps

  • Track several pools at once by creating a listener per pool on the same provider.
  • Persist each swap to a database, or push large trades to an alerting channel.
  • Compute rolling volume and price from the decoded amounts.
Learn more about the node behind this tutorial in Global Nodes.
Last modified on July 10, 2026