Ethers.js: Watch events - How to avoid on load event

Ethers.js allows you to watch events on Solidity that are emitted from blockchains.

Prerequisite

Let's say you have the SimpleAuction contract that emits HighestBidIncreased event when bids happen:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.21 <0.9.0;

contract SimpleAuction {
    event HighestBidIncreased(address bidder, uint amount); // Event

    function bid() public payable {
        // ...
        emit HighestBidIncreased(msg.sender, msg.value); // Triggering event
    }
}

In Ethers.js, you can use contract.on(event, listener) to watch events as follows:

import { ethers } from 'ethers'
import contractArtifact from '../artifacts/contracts/SimpleAuction.sol/SimpleAuction.json'

const provider = new ethers.providers.Web3Provider(window.ethereum)
const { abi } = contractArtifact
const signer = provider.getSigner()
const contract = new ethers.Contract(contractAddress, abi, signer)

const filter = contract.filters.HighestBidIncreased(addressToFilter, null)

// Add an event listener here.
contract.on(filter, (bidder, amount) => {
    // This is called when `HighestBidIncreased` event is emitted in the smart contract
    console.log(`${bidder} bid ${amount} amount.`)
})

Issue with v5

If you are using Ether.js v5, there is one tricky behaviour: Event listeners are fired on page load.

https://github.com/ethers-io/ethers.js/issues/2310

This sometimes introduces unexpected behaviours or software bugs. Every time pages get loaded, the event listener fires with the same transaction payloads.

Solution

In order to avoid this, you can utilize provider.once("block") that add a listener for only a new block is detected.

provider.once("block", () => {
    contract.on(filter, (bidder, amount) => {
        console.log(`${bidder} bid ${amount} amount.`)
    })
})

If you wrap callbacks as you can see in the above example with provider.once("block"), now the event listener is only interested in new blocks.

November 02, 2022