Listening to Ethereum in Real Time with web3.js: From Subscription to Reconnection
Real‑time Ethereum event listening with web3.js is faster than polling. Learn how to set up a WebSocket subscription, filter events, handle reconnections, and decide when polling is a better fit.
18 Feb 2026, 03:40 UTC

Problem: Waiting for Contract Events
When building dApps that react to token transfers, NFT sales, or governance votes, developers often rely on polling a smart‑contract’s logs. Polling is simple but introduces latency, wasted bandwidth, and a heavier load on the node. The real question is: how can we receive events instantly and reliably without constantly hitting the network?
Why Subscribe Matters
Ethereum nodes that expose WebSocket (WS) endpoints can push log entries to clients as soon as they are mined. web3.eth.subscribe('logs', …) turns a passive HTTP request into an active stream. This pattern is especially useful for:
- Real‑time dashboards that update on every transfer.
- Automated market‑making bots that need to react within milliseconds.
- Monitoring services that alert when a critical event occurs.
Setting Up a Subscription
Below is a minimal Node.js example that connects to an Infura WebSocket endpoint, filters for the Transfer event of a known ERC‑20, and logs the details. Replace the placeholder with your own key.
const Web3 = require('web3');
// 1. Create a WebSocket provider. HTTP will not work for subscriptions.
const wsProvider = new Web3.providers.WebsocketProvider('wss://mainnet.infura.io/ws/v3/YOUR_INFURA_PROJECT_ID');
const web3 = new Web3(wsProvider);
// 2. The contract address and the event signature hash.
const tokenAddress = '0xYourERC20ContractAddress';
const transferSig = web3.utils.sha3('Transfer(address,address,uint256)'); // keccak256 hash
// 3. Subscribe to logs filtered by address and first topic (event signature).
const subscription = web3.eth.subscribe('logs', {
address: tokenAddress,
topics: [transferSig] // only Transfer events
}, (error, result) => {
if (error) return console.error('Subscription error:', error);
console.log('New Transfer event:', result);
});
// 4. Basic error handling. Reconnect on network drop.
subscription.on('error', (err) => {
console.error('WS error:', err);
// Attempt to reconnect after a brief pause.
setTimeout(() => {
subscription.unsubscribe();
// Re‑establish the subscription.
subscription = web3.eth.subscribe('logs', { address: tokenAddress, topics: [transferSig] });
}, 3000);
});
// 5. Clean up on process exit.
process.on('SIGINT', () => {
subscription.unsubscribe();
wsProvider.disconnect();
process.exit();
});
Key points:
- Always use a WS provider; HTTP endpoints cannot push data.
- The first element of
topicsis the event signature hash; additional topics can filter by indexed parameters. - Provider‑specific limits exist—Infura’s free tier caps concurrent subscriptions.
Handling Reconnection
Network hiccups are inevitable. When the WS connection drops, subscription emits an error or end event, and the stream stops. A robust listener should:
- Listen for
errorandendevents. - Close the old subscription and open a new one.
- Optionally replay missed blocks (see duplicate handling).
Duplicate Events and Replay Risk
If a client disconnects and then reconnects, the node may resend logs from the last known block. To avoid processing the same log twice, store a composite key of blockNumber + logIndex for each processed event. Before handling a new log, check this key against a Redis set or a local database.
Trade‑off vs Polling
Subscriptions are lightweight when the event rate is low, but they consume a persistent WS connection and may hit provider limits. Polling, on the other hand, gives you full control over request frequency and can be cheaper if you only need to check every minute. Consider:
- High‑traffic contracts: Use batching or switch to polling after a threshold of events per minute.
- Resource constraints: A single WS connection can serve multiple event streams if you combine filters.
- Reliability: Polling guarantees you won’t miss an event if the node restarts, but at the cost of latency.
Practical Checklist
- Confirm your provider exposes a WebSocket endpoint.
- Calculate the event signature hash with
web3.utils.sha3. - Set up a reconnection loop that respects provider rate limits.
- Implement deduplication using block number and log index.
- Monitor subscription health with metrics (e.g., number of events per minute).
Takeaway
Real‑time event listening with web3.eth.subscribe turns passive polling into an active stream, reducing latency and bandwidth. By filtering with contract address and event signature, handling reconnections, and guarding against duplicates, you can build a resilient listener that scales with your dApp’s needs. If you’re in a high‑throughput scenario or on a free tier, weigh the subscription cost against polling to find the sweet spot for your use case.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.