Getting tokenId after mint when inheriting from ERC721PresetMinterPauserAutoId.sol

Hi! I am writing a smart contract that inherits from ERC721PresetMinterPauserAutoId.sol.

I want to emit my own MintEvent so it's easy to discern in a web3 client after a new token has been minted. The problem with using the Transfer event is that it doesn't only get emitted after a mint.

The problem I have is when I create my own mint function, and call the parent mint function in ERC721PresetMinterPauserAutoId.sol, I do not get the current tokenId as a return value. The preset uses this mint implementation:

 function mint(address to) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have minter role to mint");

        // We cannot just use balanceOf to create the new tokenId because tokens
        // can be burned (destroyed), so we need a separate counter.
        _mint(to, _tokenIdTracker.current());

Unfortunately _tokenIdTracker is private, so I cannot access it.

What's the best way for me to get the newly minted tokenId in my caller, which looks like this:

  function myMint() public {
        mint(msg.sender); // call inherited mint function, no tokenId in ret value
        emit MyMintEvent(/* I need a tokenId to pass here */) 

I could keep my own count, but that's a problem for two reasons:

  • Managing the same state in two different locations in storage. Problematic.
  • Double the gas costs when writing to storage. Problematic.

Is there a proper way to access the latest tokenId after mint within my contract?

**** NOTE *****

After further digging, this is the cleanest solution I can come up with. I overrode the _beforeTokenTransfer hook. Is there a better way of approaching this or is this fine? The reason I ask is because this hook is triggered before balances are updated.

  function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override {
        if (from == address(0)) {
            emit MyMintEvent(tokenId, to);

        super._beforeTokenTransfer(from, to, tokenId); // Call parent hook

If the variable is private, there is no way of accessing it in a derived contract so what you can do is

  1. Modify the parent contract to make it internal instead of private
  2. Use the hooks
  3. Just filter for Transfer events where the source is address(0) since the only way an NFT is transferred from address(0) is from a mint() function call.

I would go for option 3.


Yeah, agree with STYJ, the option 3 is much better.