Hardhat – How to Test Contract Event Arguments with Hardhat and Chai

eventshardhattesting

I'm currently using chai with hardhat and ethereum-waffle to test my smart contracts:

const chai = require('chai')
const hre = require('hardhat')
const { solidity } = require('ethereum-waffle')

chai.use(solidity)

I have been finding it great to use.

However I have had some difficulty testing events. I'm currently using expect( ... ).to.emit( ... ).withArgs( ... ) which works for a lot of cases but not all.

Take the following example snippet from a test:

const destroyResult = await gameContract.attack([4, 0])

// Expect the game to end with the announced winner being owner
expect(destroyResult)
  .to.emit(gameContract, 'GameEnd')
  .withArgs(undefined, owner.address)

In this example, the GameEnd event has two arguments: the timestamp of the game ending and the address of the winner (owner.address above). In my test, I only care about testing the 2nd argument (who won the game).

I tried providing undefined for the timestamp, but this is not acceptable and causes the test to fail. What I need is a way to test only the 2nd argument of the event.

It would also be acceptable for me to have a function where I have access to the event object so that I can perform any test I like. However I haven't seen any way of doing this.

Any help would be appreciated, thank you!

Best Answer

This is how I do that for the Transfer event of the ERC20 tokens:

usually :

await expect(tokenInstance.connect(<signer-account>).transfer(<addr-of-receipent>, <amount-BigNumber>))
.to.emit(tokenInstance, 'Transfer').withArgs(<addr-of-signer-account>, <addr-of-receipent>, <amount-BigNumber>);

And this is how you can get all the arguments and test all of them or any one of them:

...
const tx = await tokenInstance.connect(<signer-account>).transfer(<addr-of-receipent>, <amount-BigNumber>);
const receipt = await ethers.provider.getTransactionReceipt(tx.hash);
const interface = new ethers.utils.Interface(["event Transfer(address indexed from, address indexed to, uint256 amount)"]);
const data = receipt.logs[0].data;
const topics = receipt.logs[0].topics;
const event = interface.decodeEventLog("Transfer", data, topics);
expect(event.from).to.equal(<addr-of-signer-account>);
expect(event.to).to.equal(<addr-of-receipent>);
expect(event.amount.toString()).to.equal(<amount-BigNumber>.toString());
...

I assume here that only 1 event will be emitted and therefore I get the first element in the logs array logs[0]

Related Topic