- Connect the ethers v6
WebSocketProviderto a Chainstack Ethereum WSS endpoint, and passstaticNetworkso the log subscription keeps delivering. - Attach a
Contractlistener for the pool’sSwapevent — the node pushes each matching log the moment the block lands onchain, with no block scanning or polling. - Decode
amount0andamount1from 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 Swaptopic0is0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67.
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’sSwap 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’sethers.providers.WebSocketProvider. - An Ethereum mainnet WSS endpoint from Chainstack.
How Uniswap emits swaps
Every Uniswap pool contract emits aSwap 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
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:Sign in to Chainstack
Log in to your Chainstack account.
Deploy an Ethereum node
Create a project, then deploy an Ethereum mainnet node. Global Nodes deploy instantly and are available on every plan.
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 forSwap events and decodes each one. Save it as monitor.js and set WSS_ENDPOINT to your Chainstack endpoint.
monitor.js
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: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 theclose 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 bytopic0 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:
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, changePOOL_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
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.