Lumaktaw patungo sa pangunahing content

Web3.js How to keep Websocket subscriptions alive and reconnect on failures

Smart contracts are an essential part of the Ethereum ecosystem; Smart contracts emit so-called events when something meaningful happens within the smart contract that the smart contract wants to communicate to dApps and other smart contracts.

I want to share with you some ways for keeping the Websocket connection open while listening to smart contract events. Some smart contracts don’t emit events so often, therefore, you can configure your Infura Websocket to stay alive even though the smart contract is not constantly emitting events.

Setting Up Our Project

info

This tutorial makes use of Web3.js v1.x.x. Not all functionality might work with Web3.js v4.

Please create a new folder in which we can work on our project, then install web3js using npm.

npm install web3

Ensure you have your Infura account set up and have access to your endpoint URL. Feel free to read more about getting started with Infura.

At the top of our new Javascript file, we can add the following to import the web3js library that we just installed and connect to the Infura websockets endpoint:

const Web3 = require("web3");
const web3 = new Web3("wss://mainnet.infura.io/ws/v3/<API_KEY>");

1. Avoid the Websocket connection to go idle by configuring your WebSocket with ‘keepalive’ and ‘reconnect’

We usually want to have a retry mechanism in place in case one of the smart contract we’re listening to is not emitting any events in a 1 hour time frame (Infura Websocket is closed after 1-hour idle time).

const provider = new Web3.providers.WebsocketProvider(
"wss://mainnet.infura.io/ws/v3/<YOUR_PROJECT_ID>",
{
clientConfig: {
keepalive: true,
keepaliveInterval: 60000,
},
reconnect: {
auto: true,
delay: 5000,
maxAttempts: 5,
onTimeout: false,
},
},
);

2. Avoid the Websocket connection to go idle by subscribing to ‘newBlockHeaders’

web3.eth
.subscribe("newBlockHeaders")
.on("data", (data) => {
console.log(`Received block header for block number ${data.number}.`);
})
.on("error", (error) => {
console.error(error);
console.error("An error occured on the new blocks subscription.");
})
.on("connected", (id) => {
console.log(`NewBlockHeaders subscription connected (${id})`);
});

By having our Websocket configured with a retry mechanism and listening to newBlockHeads, we will avoid having our WSS connection closed.

We’re going to track the events emitted by UniSwap Smart Contract, in particular we will take a look at the pairCreated() function.

In order to listen to specific contract events we have to get the smart contract address and the smart contract ABI from Etherscan. (https://etherscan.io/address/0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f#code).

Complete Code Overview

const Web3 = require("web3");
const factoryAbi = require("./abi.json");
const factoryAddress = "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f";
const provider = new Web3.providers.WebsocketProvider(
"wss://mainnet.infura.io/ws/v3/<YOUR_PROJECT_ID",
{
clientConfig: {
keepalive: true,
keepaliveInterval: 60000,
},
reconnect: {
auto: true,
delay: 5000,
maxAttempts: 5,
onTimeout: false,
},
},
);
provider.on("connect", () => {
console.log("Websocket connected.");
});
provider.on("close", (event) => {
console.log(event);
console.log("Websocket closed.");
});
provider.on("error", (error) => {
console.error(error);
});
const web3 = new Web3(provider);
const factoryContract = new web3.eth.Contract(factoryAbi, factoryAddress);
web3.eth
.subscribe("newBlockHeaders")
.on("data", (data) => {
console.log(`Received block header for block number ${data.number}.`);
})
.on("error", (error) => {
console.error(error);
console.error("An error occured on the new blocks subscription.");
})
.on("connected", (id) => {
console.log(`NewBlockHeaders subscription connected (${id})`);
});
factoryContract.events
.PairCreated()
.on("connected", (id) => {
console.log(`PairCreated subscription connected (${id})`);
})
.on("data", (event) => {
console.log(event);
})
.on("error", (error) => {
console.log(error);
});

I hope that you’ve enjoyed it, see you next time!