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.
We also have a Python version of this tutorial, find it here: How to set up a redundant Ethereum event listener with Python.
To address this issue, the concept of a redundant event listener emerges as a solution. By subscribing to multiple Ethereum nodes simultaneously, a redundant event listener increases the chances of receiving events even if one or more nodes fail. This approach introduces redundancy and fault tolerance, ensuring that important events are not missed and that the application remains responsive and up-to-date.
This tutorial will explore how to BUIDL a redundant Ethereum event listener using Node.js and the popular web3.js and ethers.js libraries using WSS RPC nodes. Chainstack global nodes already have redundancy and fault tolerance built in, but we can improve the robustness of this event listener by using Chainstack Trader Node. This architecture will allow your app to place event listeners in various regions worldwide, ensuring redundancy and accuracy.
Learn everything about event logs in Tracking some Bored Apes: The Ethereum event logs tutorial.
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:
Set
data structure to track unique event identifiers and prevent duplicate event processing.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.Set
to mark it as processed.By implementing this redundant event listener, the application ensures that important events, such as WETH transfers, are not missed, even in the face of node failures or connectivity issues. This increased reliability and fault tolerance can be crucial for applications that rely heavily on monitoring and reacting to Ethereum events in real-time.
To start with a JavaScript development project, you’ll need to install node.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.
For this project, it is recommended to deploy at least three RPC nodes, one Global Node, and two Trader Nodes. Remember that there is no limit on how many nodes you can use in the project, and can also mix different providers.
Once the infrastructure is set up, you can work on a new project.
First, we’ll create a new Node.js project and initialize it with a package.json
file. Open your terminal, navigate to the desired directory, and run the following command:
This will create a 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:
After installing the dependencies, create a new file called .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.
In your Node.js script, you’ll need to load the environment variables from the .env
file using the dotenv
package. At the top of your script, add the following line:
This will load the environment variables from the .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:
By following these steps, you’ll have a basic Node.js project set up with the required dependencies, and you’ll be able to store and access your WebSocket endpoints securely using environment variables in a .env
file.
Check out Web3 node.js: From zero to a full-fledged project to learn more about managing Node.js projects.
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 named index.js
and paste the following code:
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 the web3
library and configuring the dotenv
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
, and ENDPOINT_3
and stored in the endpoints
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 called seenEvents
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 the logsFilter
.
Inside the subscribeToLogs
function, a new Web3
instance is created using the provided endpoint, and the web3.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 the seenEvents
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 the endpoints
array and calls the subscribeToLogs
function for each endpoint creates multiple WebSocket subscriptions to different Ethereum nodes.
By implementing this redundant event listener, 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.
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.
This code sets up a redundant Ethereum event listener using the ethers.js
library and environment variables for storing WebSocket endpoints. Here’s what’s happening:
The code starts by importing the ethers
object from the ethers.js
library and configuring the dotenv
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
, and ENDPOINT_3
and stored in the endpoints
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 called seenEvents
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 new WebSocketProvider
instance is created using the provided endpoint, and a Contract
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 the endpoints
array and calls the subscribeToEvents
function for each endpoint creates multiple WebSocket connections and event subscriptions to different Ethereum nodes.
By implementing this redundant event listener with 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.
Building reliable and fault-tolerant systems is crucial when developing applications on the Ethereum blockchain. By implementing a redundant event listener using Node.js and libraries like web3.js or ethers.js, you can consistently ensure that your application receives important events, such as token transfers, without disruptions.
This approach mitigates the risks associated with relying on a single node. It introduces redundancy by subscribing to multiple nodes simultaneously, increasing the chances of receiving events even if some nodes fail.
The tutorial provided a step-by-step guide to setting up a project, configuring environment variables, and implementing the redundant event listener using web3.js and ethers.js. With this solution, your Ethereum-based application can remain responsive and up-to-date and provide a seamless user experience, even in node failures or connectivity issues.
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.
We also have a Python version of this tutorial, find it here: How to set up a redundant Ethereum event listener with Python.
To address this issue, the concept of a redundant event listener emerges as a solution. By subscribing to multiple Ethereum nodes simultaneously, a redundant event listener increases the chances of receiving events even if one or more nodes fail. This approach introduces redundancy and fault tolerance, ensuring that important events are not missed and that the application remains responsive and up-to-date.
This tutorial will explore how to BUIDL a redundant Ethereum event listener using Node.js and the popular web3.js and ethers.js libraries using WSS RPC nodes. Chainstack global nodes already have redundancy and fault tolerance built in, but we can improve the robustness of this event listener by using Chainstack Trader Node. This architecture will allow your app to place event listeners in various regions worldwide, ensuring redundancy and accuracy.
Learn everything about event logs in Tracking some Bored Apes: The Ethereum event logs tutorial.
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:
Set
data structure to track unique event identifiers and prevent duplicate event processing.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.Set
to mark it as processed.By implementing this redundant event listener, the application ensures that important events, such as WETH transfers, are not missed, even in the face of node failures or connectivity issues. This increased reliability and fault tolerance can be crucial for applications that rely heavily on monitoring and reacting to Ethereum events in real-time.
To start with a JavaScript development project, you’ll need to install node.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.
For this project, it is recommended to deploy at least three RPC nodes, one Global Node, and two Trader Nodes. Remember that there is no limit on how many nodes you can use in the project, and can also mix different providers.
Once the infrastructure is set up, you can work on a new project.
First, we’ll create a new Node.js project and initialize it with a package.json
file. Open your terminal, navigate to the desired directory, and run the following command:
This will create a 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:
After installing the dependencies, create a new file called .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.
In your Node.js script, you’ll need to load the environment variables from the .env
file using the dotenv
package. At the top of your script, add the following line:
This will load the environment variables from the .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:
By following these steps, you’ll have a basic Node.js project set up with the required dependencies, and you’ll be able to store and access your WebSocket endpoints securely using environment variables in a .env
file.
Check out Web3 node.js: From zero to a full-fledged project to learn more about managing Node.js projects.
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 named index.js
and paste the following code:
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 the web3
library and configuring the dotenv
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
, and ENDPOINT_3
and stored in the endpoints
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 called seenEvents
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 the logsFilter
.
Inside the subscribeToLogs
function, a new Web3
instance is created using the provided endpoint, and the web3.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 the seenEvents
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 the endpoints
array and calls the subscribeToLogs
function for each endpoint creates multiple WebSocket subscriptions to different Ethereum nodes.
By implementing this redundant event listener, 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.
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.
This code sets up a redundant Ethereum event listener using the ethers.js
library and environment variables for storing WebSocket endpoints. Here’s what’s happening:
The code starts by importing the ethers
object from the ethers.js
library and configuring the dotenv
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
, and ENDPOINT_3
and stored in the endpoints
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 called seenEvents
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 new WebSocketProvider
instance is created using the provided endpoint, and a Contract
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 the endpoints
array and calls the subscribeToEvents
function for each endpoint creates multiple WebSocket connections and event subscriptions to different Ethereum nodes.
By implementing this redundant event listener with 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.
Building reliable and fault-tolerant systems is crucial when developing applications on the Ethereum blockchain. By implementing a redundant event listener using Node.js and libraries like web3.js or ethers.js, you can consistently ensure that your application receives important events, such as token transfers, without disruptions.
This approach mitigates the risks associated with relying on a single node. It introduces redundancy by subscribing to multiple nodes simultaneously, increasing the chances of receiving events even if some nodes fail.
The tutorial provided a step-by-step guide to setting up a project, configuring environment variables, and implementing the redundant event listener using web3.js and ethers.js. With this solution, your Ethereum-based application can remain responsive and up-to-date and provide a seamless user experience, even in node failures or connectivity issues.