Skip to main content

WTF Ethers: 8. Contract Listeners

I've been revisiting ethers.js recently to refresh my understanding of the details and to write a simple tutorial called "WTF Ethers" for beginners.

Twitter: @0xAA_Science

Community: Website | WTF Solidity | discord | WeChat Group Application

All the code and tutorials are open-sourced on GitHub:

Note: This tutorial is based on ethers.js v6. If you are using v5, you can refer to the WTF Ethers v5.

In this lesson, we will learn how to listen to contract events and implement listening to the "Transfer" event of the USDT contract.

Refer to the ethers.js documentation for more details.

Listening to Contract Events


In ethers.js, the contract object has a contract.on method to continuously listen to contract events:

contract.on("eventName", function)

contract.on takes two parameters: the event name to listen to, which needs to be included in the contract ABI, and the function to be called when the event occurs.


The contract object also has a contract.once method to listen to a contract event just once. It takes the same parameters as contract.on:

contract.once("eventName", function)

Listening to the USDT Contract

  1. Declare the provider: Alchemy is a free ETH node provider. You need to apply for one before proceeding. You can refer to this guide to apply for the Alchemy API WTF Solidity Tutorial - Tools Part 4: Alchemy.

    import { ethers } from "ethers";
    // Prepare Alchemy API
    // You can refer to [WTF Solidity Tutorial - Tools Part 4: Alchemy]( for the setup process
    const ALCHEMY_MAINNET_URL = '';
    // Connect to the mainnet provider
    const provider = new ethers.JsonRpcProvider(ALCHEMY_MAINNET_URL);
  2. Declare the contract variables: We only care about the "Transfer" event of the USDT contract. To listen to this event, we need to include it in the ABI. If you're interested in other functions and events, you can find them on etherscan.

    // USDT contract address
    const contractAddress = '0xdac17f958d2ee523a2206206994597c13d831ec7';
    // Build the ABI for the Transfer event of USDT
    const abi = [
    "event Transfer(address indexed from, address indexed to, uint value)"
    // Generate the USDT contract object
    const contractUSDT = new ethers.Contract(contractAddress, abi, provider);
  3. Use the contract.once() function to listen to the Transfer event once and print the result.

      // Listen to the event only once
    console.log("\n1. Using contract.once(), listen to the Transfer event once");
    contractUSDT.once('Transfer', (from, to, value)=>{
    // Print the result
    `${from} -> ${to} ${ethers.formatUnits(ethers.getBigInt(value),6)}`

    Listen once

  4. Use the contract.on() function to continuously listen to the Transfer event and print the result.

      // Continuously listen to the USDT contract
    console.log("\n2. Using contract.on(), continuously listen to the Transfer event");
    contractUSDT.on('Transfer', (from, to, value)=>{
    // Print the result
    `${from} -> ${to} ${ethers.formatUnits(ethers.getBigInt(value),6)}`

    Continuous listening


In this lesson, we introduced the simplest on-chain listening features in ethers, contract.on() and contract.once(). With the methods mentioned above, you can listen to specific events of a specific contract.