ethers-io / ethers.js

Complete Ethereum library and wallet implementation in JavaScript.
https://ethers.org/
MIT License
7.97k stars 1.85k forks source link

Duplicate Event Triggers on the same Event Filter #3207

Open maharshi365 opened 2 years ago

maharshi365 commented 2 years ago

Ethers Version

5.6.9

Search Terms

Event, Listener

Describe the Problem

I am trying to make a tool that listens to various events via a JSON RPC provider. I have a use case where I may want to listen to the same event, but have different listeners (each with a different effect) trigger. While attempting to set this system up I noticed that I kept on receiving duplicate events being triggered. How do I make it so that I only get 1 trigger per listener.

I need to dynamically create the listener function to provide context for each listener so I have a higher order function that returns the listener function.

When an event is triggered I would get the following output: value1 0x1aaa3a62e75f29c634cb634b8968a78a16e54246b45990a38d1c106b3730c4a8 value2 0x1aaa3a62e75f29c634cb634b8968a78a16e54246b45990a38d1c106b3730c4a8 value3 0x1aaa3a62e75f29c634cb634b8968a78a16e54246b45990a38d1c106b3730c4a8 value1 0x1aaa3a62e75f29c634cb634b8968a78a16e54246b45990a38d1c106b3730c4a8 value2 0x1aaa3a62e75f29c634cb634b8968a78a16e54246b45990a38d1c106b3730c4a8 value3 0x1aaa3a62e75f29c634cb634b8968a78a16e54246b45990a38d1c106b3730c4a8 value1 0x1aaa3a62e75f29c634cb634b8968a78a16e54246b45990a38d1c106b3730c4a8 value2 0x1aaa3a62e75f29c634cb634b8968a78a16e54246b45990a38d1c106b3730c4a8 value3 0x1aaa3a62e75f29c634cb634b8968a78a16e54246b45990a38d1c106b3730c4a8

Code Snippet

import { ethers } from 'ethers';

const higherOrder = (ctx) => {
  let processor = (data) => {
    console.log(ctx, data.transactionHash);
  };

  processor.ctxVal = ctx;

  return processor;
};

let provider = new ethers.providers.JsonRpcProvider(<URL>);

let contract = new ethers.Contract(<addr>, abi);
let filter = contract.filters.integer();

provider.on(filter, higherOrder('value1'));
provider.on(filter, higherOrder('value2'));
provider.on(filter, higherOrder('value3'));

Contract ABI

[
  {
    anonymous: false,
    inputs: [
      {
        indexed: false,
        internalType: 'uint8',
        name: '_int1',
        type: 'uint8',
      },
      {
        indexed: false,
        internalType: 'uint16',
        name: '_int2',
        type: 'uint16',
      },
      {
        indexed: false,
        internalType: 'uint32',
        name: '_int3',
        type: 'uint32',
      },
      {
        indexed: false,
        internalType: 'uint64',
        name: '_int4',
        type: 'uint64',
      },
      {
        indexed: false,
        internalType: 'uint128',
        name: '_int5',
        type: 'uint128',
      },
      {
        indexed: false,
        internalType: 'uint256',
        name: '_int6',
        type: 'uint256',
      },
    ],
    name: 'integer',
    type: 'event',
  },
]

Errors

No response

Environment

node.js (v12 or newer)

Environment (Other)

No response

zemse commented 2 years ago

I tried to reproduce but the logs are once per event. What backend are you using? It could be an edge case for some network. Is this your custom contract, or are you also seeing this issue for publically used contracts like USDC Transfer events? Can you provide me the exact contract address, backend provider, and more information to help me reproduce this issue quickly?

maharshi365 commented 2 years ago

Hey @zemse. Here are the details. I tried with USDC transfer events and still have the same issue.

const higherOrder = (ctx) => {
  let processor = (data) => {
    console.log(
      `${ctx}::${data.blockNumber}::${data.transactionHash}::${data.transactionIndex}::${data.logIndex}`
    );
  };

  processor.ctxVal = ctx;

  return processor;
};

let provider = new ethers.providers.JsonRpcProvider(<PROVIDER_URL>);

let abi = [
  {
    anonymous: false,
    inputs: [
      {
        indexed: true,
        internalType: 'address',
        name: 'from',
        type: 'address',
      },
      {
        indexed: true,
        internalType: 'address',
        name: 'to',
        type: 'address',
      },
      {
        indexed: false,
        internalType: 'uint256',
        name: 'value',
        type: 'uint256',
      },
    ],
    name: 'Transfer',
    type: 'event',
  },
];

let addr = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';

let contract = new ethers.Contract(addr, abi);
let filter = contract.filters.Transfer();

provider.on(filter, higherOrder('value1'));
provider.on(filter, higherOrder('value2'));
provider.on(filter, higherOrder('value3'));

Below is a sample output

value1::15340939::0x59a85ee38f4ea21d17bbb45c1731aba244afd401c87efe2c135dba41b89882c8::1::4
value2::15340939::0x59a85ee38f4ea21d17bbb45c1731aba244afd401c87efe2c135dba41b89882c8::1::4
value3::15340939::0x59a85ee38f4ea21d17bbb45c1731aba244afd401c87efe2c135dba41b89882c8::1::4

I am using ethers version 5.6.9. The provider is an alchemy endpoint. The contract in my initial post is custom, but the same issue happens on USDC transfer events on the Ethereum Mainnet.

"ethers": "^5.6.9",
zemse commented 2 years ago

If you have three listeners created for the same filter, it is expected the callback to be triggered three times right? The output makes sense to me. What output are you expecting?