Solidity Event Filtering – Solving ‘Always Returns Topics’ Query with Ethers.js

ethers.jseventshardhatjavascriptsolidity

I have an event in my contract:

event CreatedUnfinishedRandomSVG(uint256 indexed tokenId, address nftOwner, uint256[] randomWords);

In my JS script, I am trying to run some code only if the event that matches this tokenId has been emitted. This is how I am filtering for this event (hardhat, ethers, contract is called RandomSvg):

// Get contract object so we can get address
  let randomSVGObject = await get("RandomSVG");
  let randomSvgAddress = randomSVGObject.address;

  // Get the RandomSVG contract factory
  const randomSvgFactory = await ethers.getContractFactory("RandomSVG");
  const randomSvgInterface = randomSvgFactory.interface;

  // Get signer
  const accounts = await hre.ethers.getSigners();
  const signer = accounts[0];

  // Create a new randomSVG Contract instance
  const randomSvg = new ethers.Contract(
    randomSvgAddress,
    randomSvgInterface,
    signer
  );

.... call function on RandomSvg that emits the below event....

// Filter for event
const events = randomSvg.filters.CreatedUnfinishedRandomSVG(tokenId);

Above, tokenId is taken from an event in the transaction receipt. The value of this is always 1, and I have checked that there are never any more tokenId's in the contract (eg 2, 3, 4 etc, the code that creates the tokenId is only called once).

When I run this and print events, I get the following output:

{"address":"0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9","topics":["0x26cb4155602d63931f31f118192ad576866bf5df054834c85516a4273134bd1e","0x0000000000000000000000000000000000000000000000000000000000000001"]}

From my understanding, the first element in topics array is the hash of the event signature (https://docs.ethers.io/v5/concepts/events/#events-solidity). The following elements are the indexed parameters, so in this case, it is the tokenId which is equal to 1. 0x0000000000000000000000000000000000000000000000000000000000000001 is equal to 1, so this makes sense.

However, if I run this in my JS script:

const events = randomSvg.filters.CreatedUnfinishedRandomSVG(9999);

I get this:

{"address":"0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9","topics":["0x26cb4155602d63931f31f118192ad576866bf5df054834c85516a4273134bd1e","0x000000000000000000000000000000000000000000000000000000000000270f"]}

If we convert the first element of topics (0x000000000000000000000000000000000000000000000000000000000000270f) to decimal number, we get 9999. This makes no sense to me…

My JS code stated that I wanted to filter CreatedUnfinishedRandomSVG events where the tokenId is equal to 9999. Since there are no CreatedUnfinishedRandomSVG where tokenId is equal to 9999, surely this should return null?

This makes it impossible to determine if an event with a specific indexed value exists, because no matter what tokenId you pass in to filter on, you always get a result???

What is the reasoning for this and why is this happening?
How would I determine if an event with a specific value has been emitted?

Additional info:

If I run:

const events = randomSvg.filters.CreatedUnfinishedRandomSVG();

I get:

{"address":"0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9","topics":["0x26cb4155602d63931f31f118192ad576866bf5df054834c85516a4273134bd1e"]}

Which is only the topic hash…

Best Answer

With this:

// Filter for event
const events = randomSvg.filters.CreatedUnfinishedRandomSVG(tokenId);

you aren't yet filtering events, but only making the filter (which is the object you've seen).

To filter for actual live events from the blockchain:

const filter = randomSvg.filters.CreatedUnfinishedRandomSVG(tokenId);
provider.on(filter, (log, event) => {
    console.log(log)
    // ...
})

Instead to show the events from a receipt:

const tx = ...   // our transaction
const receipt = await tx.wait();
for (let i=0; i<receipt.logs.length; i++) {
        if (receipt.logs[i].address == filter.address) {
            if (receipt.logs[i].topics[0] == filter.topics[0]) {
                console.log(receipt.logs[i])
            }
        }
    }

I don't think there's a method to filter these, but nothing you can't do by nesting some ifs!

Related Topic