I have a Solidity contract that inherits from ERC721PresetMinterPauserAutoId

I want to write a test that verifies that a Transfer event has been emitted by the contract after the public mint(to) function has been invoked. Here's what my tests look like so far:

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("MyContract -- Contract Tests", function () {
    let admin, addr1, addr2;
    let myContract;
    let minterRole = ethers.utils.id("MINTER_ROLE");

    beforeEach(async () => {
        ([admin, addr1, addr2] = await ethers.getSigners());
        const MyContract = await ethers.getContractFactory('MyContract');
        myContract = await MyContract.deploy();

    // Test passes
    it("Should not allow account without MINTER_ROLE to mint", async function() {
        await expect(myContract.connect(addr1).mint(addr1.address))
                .to.be.revertedWith('ERC721PresetMinterPauserAutoId: must have minter role to mint');


    it("Should allow account with MINTER_ROLE to mint", async function() {
        await myContract.connect(admin).grantRole(minterRole, addr1.address);
        expect(await myContract.hasRole(minterRole, addr1.address)).to.equal(true);

        await expect(myContract.connect(addr1).mint(addr1.address))
            .to.emit(myContract, 'Transfer')
            .withArgs(address(0), addr1.address, 1); // <-- this part is broken

Obviously address(0) is undefined. I looked at the source code for the underlying _mint function and it looks like the following:

function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);

So I have a few questions.

  • How do I capture the arguments captured for the emitted event in my test so I can debug the output?
  • What is the correct way to test that the arguments emitted by the event? I'm hard coding the token counter to 1 but that's because I haven't found an API yet that tells me the total number of tokens minted. Also, I'm not entirely sure how to test for address(0).

5 Answers 5


This example shows how to work with events in Hardhat in general, and it includes a test that illustrates some scenarios.

The tl;dr is that, given a contract like this one:

contract EventEmitter {
  event MyEventWithData(uint, string);

  function emitMyEventWithData(uint x, string calldata s) public {
    emit MyEventWithData(x, s);

You can check it in a test like this (notice that this relies on hardhat-waffle):

  it("Should emit MyEventWithData", async function () {
    const EventEmitter = await ethers.getContractFactory("EventEmitter");
    const eventEmitter = await EventEmitter.deploy();
    await eventEmitter.deployed();

    await expect(eventEmitter.emitMyEventWithData(42, "foo"))
      .to.emit(eventEmitter, "MyEventWithData")
      .withArgs(42, "foo");
  • Is there a way to dump the args that the event has emitted in the test? Commented Sep 16, 2021 at 15:50
  • 2
    Check the scripts/getEventsFromTx.js file in the linked example Commented Sep 17, 2021 at 12:01

You can follow an example like this:

const tx = await eventEmitter.emitBothEvents(42, "foo");

  const receipt = await tx.wait()

  for (const event of receipt.events) {
    console.log(`Event ${event.event} with args ${event.args}`);

You can pass a function as the argument to capture the event arguments.

let capturedValue
const captureValue = (value) => {
    capturedValue = value
    return true
await expect(eventEmitter.emitBothEvents(arg1, arg2))
            .to.emit(myContract, 'Transfer')
            .withArgs(captureValue, otherArg1, otherArg2)
console.log("CapturedValue:", capturedValue)

You can do this by converting the balance to a Bignumber.

await expect(myContract.connect(addr1).mint(addr1.address))
.to.emit(myContract, 'Transfer')
.withArgs(address(0), addr1.address, ethers.BigNumber.from("1")); // <-- this part is broken

Zero address is represented by Ethers.js constant ethers.constants.AddressZero. As you mention using address(0) does not work, since it does not really represent anything in your code. In solidity, address(0) represents a zero value of type address; in Java Script, it represents calling function address(0) and passing it a zero, which doesn't make any sense in your case. In your case, address(0) needs to be replaced with either ethers.constants.AddressZero, which is a string of 20 bytes (40 nibbles) of zeros, or an actual string of zeros such as 0x0000000000000000000000000000000000000000

The _mint(address to, uint256 tokenId) function you provided is not the same function that you're calling. The _mint function takes two arguments, mint function that you end up calling in your test takes only one. Without knowing what your mint function is, and what tokenId is expected to be returned by the Transfer event you are testing, it would be impossible to provide you with the exact solution.

However, it looks like, in your code, you are trying to assert that the arguments emitted by the Transfer event are zero address, to address, and 1 (token id, I'm assuming). Let's assume that we know that tokenId emitted by that event should equal 1; then you could do the following:


const expectedTokenId = 1;
const zeroAddress = ethers.constants.AddressZero;

await expect(myContract.connect(addr1).mint(addr1.address))
            .to.emit(myContract, 'Transfer')
            .withArgs(zeroAddress, addr1.address, expectedTokenId);


.withArgs() takes expected event arguments and compares those to whatever is returned by the actual event to assert them to be equal to pass the test.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.