- Monitoring Ethereum events is crucial for accurate data and a smooth user experience.
- Subscribing to multiple nodes in parallel prevents missed events during node downtime, ensuring reliability.
- Using web3.js or ethers.js with environment variables streamlines setup and protects your endpoints.
- Redundant event listeners are key for mission-critical DApps, providing robust real-time updates.
Main article
When building on Ethereum, reliably monitoring and reacting to events is important. Events like token transfers often trigger downstream actions or application updates. Missing important events can lead to data inconsistencies, incorrect user balances, and poor user experience. While Ethereum nodes provide access to the blockchain and its events, relying on a single node for event listening can be risky. Nodes can experience downtime, connectivity issues, or other failures, resulting in missed events and application functionality disruptions. This challenge is amplified in mission-critical applications or those handling high-value transactions, where missing events can have severe consequences.Python version
We also have a Python version of this tutorial, find it here: How to set up a redundant Ethereum event listener with Python.Learn everything about event logs in Tracking some Bored Apes: The Ethereum event logs tutorial.
Project overview
This tutorial shows you how to build a redundant Ethereum event listener using Node.js, web3.js, and ethers.js. This application aims to ensure reliable and fault-tolerant monitoring of events on the Ethereum blockchain, specifically the transfer events of the Wrapped Ether (WETH) contract. The application achieves redundancy by establishing multiple WebSocket connections to Ethereum RPC nodes, using Chainstack global and regional infrastructure. By subscribing to multiple nodes simultaneously, the application increases its chances of receiving events even if one or more nodes experience downtime or connectivity issues. The logic behind the application is as follows:- It defines an array of WebSocket endpoints from various Ethereum node providers.
- It creates a filter object that specifies the contract address of the WETH contract and the topic (event signature) for the “Transfer” event, which is the event we want to listen for.
- It initializes a
Set
data structure to track unique event identifiers and prevent duplicate event processing. - The application defines a function called
subscribeToLogs
that takes an endpoint as input, creates a new Web3 instance with that endpoint, and sets up a WebSocket subscription to listen for logs (events) matching the defined filter. - When a new event is received, the function checks if the event identifier (a combination of the transaction hash and log index in web3.js and transaction hash and block in ethers) has been seen before. If not, it logs the event data to the console and adds the event identifier to the
Set
to mark it as processed. - The function also handles subscription errors by logging them to the console.
Prerequisites
To start with a JavaScript development project, you’ll need to installnode.js
, a powerful JavaScript runtime environment that enables developers to run JavaScript code outside a web browser. For this project, it’s recommended to use at least version 18. You can download it from their website.
With node.js
installed, you’re ready to start using JavaScript. Now, you can set up your nodes.
Keep in mind that you need at least a Chainstack paid plan to deploy multiple nodes. Check the Chainstack pricing page for a coupon.
Create a new JavaScript project
First, we’ll create a new Node.js project and initialize it with apackage.json
file. Open your terminal, navigate to the desired directory, and run the following command:
package.json
file with default settings.
Next, we’ll install the required dependencies, which are the web3.js
, ethers.js
and the dotenv
package for loading environment variables from a .env
file:
.env
in the root directory of your project. This file will store your environment variables, including the WebSocket endpoints for the Ethereum nodes. In the .env
file, you can define the endpoints like this:
Rememeber that we are using WSS endpoints.
.env
file using the dotenv
package. At the top of your script, add the following line:
.env
file and make them accessible in your script using process.env
.
Now, instead of hardcoding the endpoints in your script, you can read them from the environment variables:
.env
file.
Check out Web3 node.js: From zero to a full-fledged project to learn more about managing Node.js projects.
Web3.js code
This section will guide you through this project using web3.js, and the following section will go over ethers.js. This way, you have options based on your favorite library. The project is already set up, so let’s create a new file namedindex.js
and paste the following code:
Web3 code explanation
This code sets up a redundant Ethereum event listener using the Web3.js library and environment variables for storing WebSocket endpoints. Here’s what’s happening:-
The code starts by importing the
Web3
object from theweb3
library and configuring thedotenv
package to load environment variables from a.env
file. -
The WebSocket endpoints for various Ethereum nodes are read from the environment variables
ENDPOINT_1
,ENDPOINT_2
, andENDPOINT_3
and stored in theendpoints
array. -
The
logsFilter
object is defined, which specifies the contract address of the Wrapped Ether (WETH) contract and the topic (event signature) for the “Transfer” event. This filter will be used to subscribe to only the desired events. -
A
Set
data structure calledseenEvents
is initialized to track unique event identifiers and prevent duplicate event processing. -
The
subscribeToLogs
async function is defined, which takes an endpoint as input and sets up a WebSocket subscription to listen for logs (events) matching thelogsFilter
. -
Inside the
subscribeToLogs
function, a newWeb3
instance is created using the provided endpoint, and theweb3.eth.subscribe("logs", logsFilter)
method is called to create a subscription. -
When a new event is received through the subscription, the
subscription.on("data", ...)
callback is triggered. The callback generates a unique event identifier using the transaction hash and log index and checks if this identifier has been seen before in theseenEvents
Set. -
If the event identifier is new, it is added to the
seenEvents
Set and the event data is logged to the console along with the endpoint from which the event was received. -
The
subscription.on("error", ...)
callback is set up to handle any errors during the subscription process, logging the error into the console. -
Finally, the
endpoints.forEach(subscribeToLogs)
line iterates over theendpoints
array and calls thesubscribeToLogs
function for each endpoint creates multiple WebSocket subscriptions to different Ethereum nodes.
Ethers.js code
Here, we’ll cover the ethers.js version of this project; you can create an entire new Node.js project or just make a new JavaScript file, then paste this code:Ethers.js now supports
ChainstackProvider
, learn more about it by reading ethers ChainstackProvider Documentation.Ethers.js code breakdown
This code sets up a redundant Ethereum event listener using theethers.js
library and environment variables for storing WebSocket endpoints. Here’s what’s happening:
-
The code starts by importing the
ethers
object from theethers.js
library and configuring thedotenv
package to load environment variables from a.env
file. -
The WebSocket endpoints for various Ethereum nodes are read from the environment variables
ENDPOINT_1
,ENDPOINT_2
, andENDPOINT_3
and stored in theendpoints
array. - The contract address of the Wrapped Ether (WETH) contract and its contract ABI (Application Binary Interface) are defined as constants.
-
A
Set
data structure calledseenEvents
is initialized to track unique event identifiers and prevent duplicate event processing. -
The
subscribeToEvents
async function is defined, which takes an endpoint as input and sets up an event subscription for the WETH contract’s “Transfer” event. -
Inside the
subscribeToEvents
function, a newWebSocketProvider
instance is created using the provided endpoint, and aContract
instance is created using the contract address, ABI, and provider. -
The
contract.on("Transfer", ...)
method is used to subscribe to the “Transfer” event of the WETH contract. - When a new “Transfer” event is received, the callback function is triggered, generating a unique event identifier using the transaction hash and block number.
-
If the event identifier is new, it is added to the
seenEvents
Set, and the event details (sender, recipient, and amount transferred) are logged to the console along with the endpoint from which the event was received. -
The
provider.on("error", ...)
callback is set up to handle any errors during the WebSocket connection, logging the error to the console. -
Finally, the
endpoints.forEach(subscribeToEvents)
line iterates over theendpoints
array and calls thesubscribeToEvents
function for each endpoint creates multiple WebSocket connections and event subscriptions to different Ethereum nodes.
ethers.js
, the application can monitor WETH transfer events reliably and consistently, even if one or more Ethereum nodes experience downtime or connectivity issues. Using environment variables to store endpoints makes managing and updating endpoint configurations easier without modifying the code directly.