It is great to see a supportive community that could potentially help me out. I hope my question (as a beginner blockchain dev) is not too basic.

I have the following mint() function (users can call it, so I am implementing lazy minting) that uses OpenZeppelin's _safemint() in my ERC721 contract:

function mint(uint256 _mintAmount) public payable {
   uint256 supply = totalSupply();
   require(_mintAmount > 0);
   require(_mintAmount <= maxMintAmount);
   require(supply + _mintAmount <= maxSupply);

   if (msg.sender != owner()) {
     require(msg.value >= cost * _mintAmount);

   for (uint256 i = 1; i <= _mintAmount; i++) {
     _safeMint(msg.sender, supply + i);
     //the following line is actually a bit more sophisticated in my actual code and serves an 
     //actual purpose, I have simplified it here to show that a boolean gets pushed to an array 


The specific collection that I am building requires that:

  • users mint in multiples of 4 or doesn't mint any NFTs at all
  • each user who calls (transacts with) the mint function above gets consecutive tokenIDs - e.g. 1,2,3,4 or 5,6,7,8.

A user should never:

  • get non-consecutive tokenIDs, e.g. 1,2,3,5

  • mint some number other than 0 and a multiple of 4, for example - 2 NFTs - tokenIDs 1 and 2, for example

  • Mint four consecutive tokenIDs with a beginning tokenID that is not 1, 5, 9, 13, etc. - e.g. tokenIDs 3,4,5 6. This would happen if for example someone before that only minted tokenIDs 1 and 2.

As far as I understand Ethereum transactions are atomic - either the whole transaction executes or none of it. So because of my "require(_mintAmount%4==0)" I am guessing that users will always be minting in multiples of 4 or 0 NFTs (please, correct me if I am wrong). However, I am concerned that:

  • the mint() function could be executing concurrently for many users and mint 4 non-consecutive tokenIDs for each user; on one hand I read that the EVM is single-threaded, so only one person could be in mint() at a time, on the other hand - don't reentrancy attacks exist exactly because it is possible to enter a function before it has stopped executing a previous call to it? I must be missing something (again, I am a beginner)
  • there is another way that I am unaware of (an "unknown unknown" kind of problem) that the function will exhibit one of the unwanted behaviors listed above, for example mint only two tokenIDs during some call - then this will not only be an issue for this user, but for all users, and the whole project will be ruined. The project depends on every user getting exactly 4 consecutive IDs, exactly from tokenID 1.

Do you think this function will satisfy the behavior that I am looking for? If not, can you think of possible fixes? Thank you all!

1 Answer 1


It looks like the function should work as expected. Since the Blockchain is single-thread there will never be a concurrent execution of this function (as transactions go one after another). If you need to be sure some unforseen case will not break your contract, add assure(supply % 4 == 0) at the end of the function. If for some reason function finished with the incorrect amount minted, it will revert all the changes.

  • 1
    Thank you. I thought that was the case but I wasn't 100% sure. Great idea with the assure check at the end as well. After I posted, I read on how reentrancy attacks work and realized that they need an external call to work & don't break single-threadedness, and as far as I see the code in _safemint(), it just updates internal arrays. So now everything seems much more clear. Thank you.
    – iloop
    Commented Feb 1, 2022 at 22:31
  • What is assure? Do you mean assert? Further, the supply variable doesn't change so why wouldn't you use assert(totalSupply() == supply + _mintAmount) ? Commented Nov 24, 2022 at 21:32

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.