NFT premint on deployment a possibility?


On a few occasions I've seen mentions (not on this forum, probably SO) of the following feature: during the contract deployment it is possible to mint all the ERC-721 tokens to a specific address with no additional gas fee. From the technical standpoint, I cannot see how this can be achieved considering the fact we need to write the ownership information to the blockchain storage. So, I'm curious to know whether this is actually a possibility or a misinformation.

It depends on how many tokens by "all" tokens. What a mint really does is 1) update the ownership of a token and 2) update the balance of owner. If there are 50 or even 100 tokens, it is totally possible to mint all in the constructor with a for loop to assign the ownership and update the balance after the for loop. If a lot more tokens are to be minted, say 10000 tokens, I don't think it's possible to mint all in the deployment stage, unless it is not using a standard ERC721 library and having highly customized minting functions. For example, to get the owner of a token with a valid ID, say less than 10000, that is non-existent, then return that specific address, etc. In that case, a token is not actually minted, hence no additional gas fee. This is not anything standard and I'm just guessing.

    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);

Thank you for your response. Actually it gave me a somewhat related idea: what if the contract considers any unminted (or better say not transferred yet) token as owned by the creator address? It could've been possible to implement by overriding the ownerOf in order to return the creator's address for unminted token calls rather than emitting an error (it's clearly not the only thing that needs to be done but the necessary first step). However when I tried to prototype the idea it quickly turned out that numerous internal methods of the OpenZeppelin library specifically call the class method ERC721.ownerOf, effectively making the override irrelevant. It gets slightly off-topic but what was the reason behind this specific way of implementing these calls?

Actually, the functions calling ERC721.ownerOf does require the logic of ownerOf being implemented correctly. For example, in transferFrom, the address from must be aligned with the owner of tokenId, there is no way to work around that. By calling ERC721.ownerOf, the right logic is enforced.

However, ownerOf can still be overridden perhaps for the exact reasons that you were thinking, allowing non-existent tokens to have non-zero owners.

1 Like

I think most of the ERC-721 functions could be "virtualized" so that if there is no owner in the _owners map, a token is considered to be owned by an initial owner. Then you would just need to set a fixed totalSupply value in the constructor and assume that tokenId is always equal to tokenIndex, and you would have the desired amount of preminted tokens.

The biggest problem in this logic is the tokenOfOwnerByIndex() function. Once some tokens are transferred away from the initial owner, there will be "holes" in the initial range. It will then be non-trivial to implement tokenOfOwnerByIndex() for the initial owner so that it would enumerate all the remaining preminted tokens.

Thinking about it, I got an idea of using a for loop to implement tokenOfOwnerByIndex and skipping indexes that appear in the _owners mapping. To get the owned item # 100, the loop would go from 0 to 100, notice that items 69 and 76 are owned by someone else, and increment the index to 102, thus returning tokenId 102. I wonder if that is all that is needed for this to work..