Get Yellowstone gRPC streaming for $49/month! Access real-time Solana blockchain data.    Learn more
  • Pricing
  • Docs

Ethereum: How to monitor and react to events reliably in JavaScript?

Created Jun 18, 2024 Updated Jun 19, 2024
Yh5baeaaaaalaaaaaabaaeaaaibraa7 logo

Reliably monitoring and reacting to blockchain events is crucial for maintaining data consistency and ensuring a smooth user experience, while building on Ethereum. Relying on a single node can be risky due to potential downtime or connectivity issues.

A redundant event listener addresses this by subscribing to multiple Ethereum nodes, ensuring that important events are not missed. Here are its core advantages:

  • Increased reliability: By subscribing to multiple nodes, you reduce the risk of missing events due to node failures.
  • Enhanced fault tolerance: The system remains functional even if some nodes experience issues.
  • Improved performance: Lower latency and higher transaction capacity.

This guide will show you how to build a redundant event listener using Node.js, web3.js, and ethers.js on atop robust Chainstack infrastructure to ensure fault tolerance and reliability.

How to build a redundant Ethereum event listener in JavaScript?

This tutorial guides you through building a redundant Ethereum event listener using Node.js with web3.js and ethers.js libraries, leveraging global and regional Chainstack infrastructure. This setup ensures reliable and fault-tolerant monitoring of Wrapped Ether (WETH) transfer events.

Prerequisites

  1. Node.js: Install Node.js (version 18 or higher recommended).
  2. Chainstack account: Sign up on Chainstack, deploy nodes, and get their access credentials.
  3. Dependencies: Install required packages: npm init -y npm install web3 ethers dotenv
  4. Environment variables: Create a .env file to store your WebSocket endpoints:
ENDPOINT_1=YOUR_CHAINSTACK_GLOBAL_NODE
ENDPOINT_2=YOUR_CHAINSTACK_REGIONAL_NODE
ENDPOINT_3=YOUR_CHAINSTACK_REGIONAL_NODE

How to set up the redundant event listener JavaScript code?

The following DApp ensures redundancy by establishing multiple WebSocket connections to Ethereum RPC nodes using our global and regional infrastructure. By subscribing to multiple nodes simultaneously, the DApp increases its chances of receiving events even if some nodes experience downtime or connectivity issues. Here’s how the logic works:

  1. Connect to WebSocket endpoints: Define an array of WebSocket endpoints from various Ethereum node providers.
  2. Set up an event filter: Create a filter object specifying the WETH contract address and the “Transfer” event signature.
  3. Implement unique event tracking: Initialize a Set data structure to track unique event identifiers and prevent duplicate event processing.
  4. Define the subscription function: Define a function, subscribeToLogs, that:
    • Takes an endpoint as input.
    • Creates a new Web3 instance with that endpoint.
    • Sets up a WebSocket subscription to listen for logs matching the defined filter.
  5. Handle events effectively:
    • When a new event is received, check if the event identifier (transaction hash and log index for web3.js; transaction hash and block for ethers) has been seen before.
    • If the event is new, log the event data to the console and add the event identifier to the Set to mark it as processed.
  6. Handle errors with ease: Log any subscription errors to the console.

By implementing this redundant event listener, the application ensures that critical events, such as WETH transfers, are not missed, even if node failures or connectivity issues occur. This increased reliability and fault tolerance are essential for applications that rely heavily on real-time monitoring and reacting to Ethereum events.

How to set up the redundant event listener script with Web3JS?

Create a file named index.js and add the following code:

const { Web3 } = require("web3");
require('dotenv').config();
const endpoints = [
  process.env.ENDPOINT_1,
  process.env.ENDPOINT_2,
  process.env.ENDPOINT_3,
];
const logsFilter = {
  address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH contract address
  topics: [
    "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
  ],
};
const seenEvents = new Set();
async function subscribeToLogs(endpoint) {
  const web3 = new Web3(endpoint);
  try {
    const subscription = await web3.eth.subscribe("logs", logsFilter);
    console.log(`Subscription created with ID: ${subscription.id}`);
    subscription.on("data", (log) => {
      const eventId = `${log.transactionHash}-${log.logIndex}`;
      if (!seenEvents.has(eventId)) {
        seenEvents.add(eventId);
        console.log(`Event received first from ${endpoint.slice(0, 33)}:`, log);
      }
    });
    subscription.on("error", (error) => {
      console.error(`Error when subscribing to logs from ${endpoint}:`, error);
    });
  } catch (error) {
    console.error(`Error setting up subscription from ${endpoint}:`, error);
  }
}
endpoints.forEach((endpoint) => {
  subscribeToLogs(endpoint);
});

How to set up the redundant event listener script with ethersJS?

Alternatively, create a new file and add the following code:

const { ethers } = require("ethers");
require('dotenv').config();
const endpoints = [
  process.env.ENDPOINT_1,
  process.env.ENDPOINT_2,
  process.env.ENDPOINT_3,
];
const contractAddress = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; // WETH contract address
const contractABI = [
  "event Transfer(address indexed from, address indexed to, uint amount)",
];
const seenEvents = new Set();
async function subscribeToEvents(endpoint) {
  const provider = new ethers.WebSocketProvider(endpoint);
  const contract = new ethers.Contract(contractAddress, contractABI, provider);
  contract.on("Transfer", (from, to, amount, event) => {
    const eventId = `${event.log.transactionHash}-${event.log.blockNumber}`;
    if (!seenEvents.has(eventId)) {
      seenEvents.add(eventId);
      console.log(`Event received first from ${endpoint.slice(0, 33)}:`);
      console.log(
        `Transfer from ${from} to ${to} of ${ethers.formatEther(amount.toString())} ETH`
      );
    }
  });
  provider.on("error", (error) => {
    console.error(`WebSocket error from ${endpoint}:`, error);
  });
}
endpoints.forEach((endpoint) => {
  subscribeToEvents(endpoint);
});

Bringing it all together

Building reliable and fault-tolerant systems is essential for Ethereum blockchain applications. Implementing a redundant event listener using Node.js with web3.js or ethers.js libraries ensures your application consistently receives important events, like token transfers, without interruptions.

This strategy reduces the risks of depending on a single node by subscribing to multiple nodes simultaneously. This redundancy ensures events are captured even if some nodes fail.

This tutorial offered a step-by-step guide to setting up a project, configuring environment variables, and creating a redundant event listener. With this setup, your Ethereum-based application will remain responsive, up-to-date, and provide a seamless user experience, even in the face of node failures or connectivity issues.

Power-boost your project on Chainstack

Have you already explored what you can achieve with Chainstack? Get started for free today.

SHARE THIS ARTICLE
Chainstack MegaETH support

Chainstack introduces MegaETH support

Chainstack now supports MegaETH, a high-performance Ethereum layer 2. Deploy Global Elastic or Dedicated nodes to build scalable EVM-compatible applications.

T9c0d9l8p U0a2lha30nl 07cf70c046c6 512 150x150 logo
Alex Usachev
Mar 6
Hyperliquid HIPs

Hyperliquid HIPs Explained: HIP-1 to HIP-4

Hyperliquid HIPs are modular protocol upgrades that extend HyperCore. This guide explains HIP-1 to HIP-4, architecture, and how builders integrate new market types.

Logo Chainstack 150x150 logo
Developer Hub Guest
Apr 8
Customer Stories

IguVerse

Balancing the heavy network load of breakneck social gaming interactions on-chain with an adaptive BNB setup.

ChartEx

Achieving production-grade reliability for blockchain queries saves time, money, and hustle.

GET protocol

Handling large transaction volumes in minting NFT tickets for large-scale events.