Error subscribing to events using web3.js and Infura API

51 views Asked by At

I'm developing a small web3 app using Ethereum Sepolia testnet, nodejs v21, web3js v4 and Infura API. I have the following smart contrcat deployed, that emits events:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract testContract {
    address public admin;
    bool public paused = false;

    event Paused(bool isPaused);

    modifier onlyAdmin() {
        require(msg.sender == admin, "Only admin can call this function");
        _;
    }

    constructor() {
        admin = msg.sender;
    }

    function togglePause() external onlyAdmin {
        paused = !paused;
        emit Paused(paused);
    }
}

I can intercat normally with the contract and I can see the events using Etherscan or a similar tool. Then I Have the node js program, that basically only tries to subscribe to the events:

const path = require('path');
const dotenvPath = path.resolve(__dirname, 'file.env');
require('dotenv').config({ path: path.resolve(__dirname, 'file.env') });

const { Web3 } = require('web3');
const mqtt = require('mqtt');

const rpcURL = process.env.RPC_URL; // e.g., "https://sepolia.infura.io/v3/your_infura_project_id"
const contractAddress = process.env.CONTRACT_ADDRESS; // e.g., "0xYourContractAddressHere"
const mqttBrokerUrl = process.env.MQTT_BROKER_URL || 'mqtt://localhost';

const provider = new Web3.providers.WebsocketProvider(rpcURL, {
  clientConfig: {
    keepalive: true,
    keepaliveInterval: 60000 // 60 seconds
  },
  reconnect: {
    auto: true,
    delay: 5000, // 5 seconds
    maxAttempts: 5,
    onTimeout: false
  }
});

const web3 = new Web3(provider);
const abi = [Here goes the ABI];
const contract = new web3.eth.Contract(abi, contractAddress);
const client = mqtt.connect(mqttBrokerUrl);

client.on('connect', () => {
    console.log('MQTT client connected');
});

client.on('error', (error) => {
    console.error('MQTT client error:', error);
    process.exit(1); // Exit in case of MQTT connection error
});

provider.on('connect', () => {
    console.log('WebSocket client connected');
    subscribeToContractEvents().catch(console.error);
});

async function subscribeToContractEvents() {
    try {
        await contract.events.Paused({
            fromBlock: 'latest'
        })
        .on('data', (event) => {
            console.log("Event received:", event);
        })
        .on('error', console.error);
    } catch (error) {
        console.error('Error subscribing to events:', error);
    }
}

provider.on('error', (error) => {
  console.error('WebSocket error:', error);
});

provider.on('end', (error) => {
  console.log('WebSocket connection ended:', error);
});

When I run the program i get the following error log when tries to subscribe to the event:

MQTT client connected
WebSocket client connected
Error subscribing to events: TypeError: Cannot read properties of undefined (reading 'on')
    at subscribeToContractEvents (/home/rpi-blockchain/node/test1.js:60:9)
    at EventEmitter.<anonymous> (/home/rpi-blockchain/node/test1.js:49:5)
    at EventEmitter.emit (node:events:519:28)
    at /home/rpi-blockchain/node/node_modules/web3-utils/lib/commonjs/web3_eip1193_provider.js:90:44
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

I have been around this for hours and can't figure why I keep getting this error. I can even log the contract.events.Paused object that results in: [Function (anonymous)]

2

There are 2 answers

1
Petr Hejda On

You need to use a WSS (websocket) RPC provider - not HTTPS - in order to subscribe to the event filters.

Your code correctly expects a WSS provider

Web3.providers.WebsocketProvider(rpcURL

but the commented out part suggests that you're passing a HTTPS (not WSS) provider in your RPC_URL variable.

// e.g., "https://sepolia.infura.io/v3/your_infura_project_id"
0
Jong On

I found this solution for event subscribing using web3.js and infura: https://docs.infura.io/tutorials/ethereum/track-erc-20-token-transfers?_ga=2.123496139.1944717594.1710351176-1575997909.1710351175#5-subscribe-to-contract-events

5. Subscribe to contract events You can subscribe to the events that token contracts emit, allowing you to track every new token transfer as it occurs.

Add the following filter to the script, which tells the web3.eth.subscribe function in web3.js which events to track:

let options = {
 topics: [web3.utils.sha3("Transfer(address,address,uint256)")],
};

Then, initiate the subscription by passing along the filter:

let subscription = await web3.eth.subscribe("logs", options);

INFO In step 3, you wrap the whole script in an async function main(), because top level await is not allowed except in recent JavaScript versions.

You can also add the following lines to the script to see whether the subscription started successfully or if any errors occurred:

  subscription.on("error", (err) => {
    throw err;
  });
  subscription.on("connected", (nr) =>
    console.log("Subscription on ERC-20 started with ID %s", nr),
  );