Infinite (ERC721i) — Pre-Minting 1 Million NFTs for < $2

Rob Secord
Charged Particles
Published in
9 min readJul 15, 2022

💡 We Pre-Minted 1 Million NFTs for <$2 USD. And you can too! You can find links to our demo collection on OpenSea at the end of the article.

Picasso’s Bull

Pablo Picasso, The Bull, 1945

Picasso once drew a series of Bull lithographs. The purpose was to find the “essence” of the Bull. He started with a full and well-rounded bull. As he progressed, he stripped away the parts that were unnecessary. What he was left with may only be a few lines but it is still, even at a glance, recognizable as a bull.

“Two holes — that’s the symbol for the face, enough to evoke it without representing it,” Picasso mused. “But isn’t it strange that it can be done through such simple means?…Whatever is most abstract may perhaps be the summit of reality.” — Pablo Picasso

When breaking down an NFT to find its on-chain “essence”, you’ll discover that only 2 things really matter. That a specific Token ID Exists (_exists) and that it has an Owner (ownerOf). Everything else is secondary.

When minting a million NFTs, we can predetermine these values. We don’t need to write an initial range of sequential IDs to the blockchain, nor do we need to store the exact same initial owner for each of those million IDs.

Pre-Mint vs. Batch-Mint vs. Lazy-Mint

Abstract

Minting NFTs can be expensive, really expensive! For this reason, there are a few options out there such as Batch-Minting or Lazy-Minting to help alleviate costs. But this only shifts the costs to other users or other types of transactions (like transfers).

Lazy-Minting is when the creator defines the NFTs but does not mint them all upfront. Instead, the purchaser is required to Mint the NFT at the time of purchase, and therefore also pay the expensive gas fees for minting. This method simply shifts the costs to the purchaser. I mean, who wants to pay for minting 10,000 NFTs upfront?

Batch-Minting is when the creator mints a bunch of the NFTs upfront and therefore pays for the fees of minting the batch. This has been made popular by the Azuki Team with ERC721A and their improvements to the standard. However, although ERC721A can save costs during minting, the costs are sometimes shifted to transfers. For this reason, this method is still restricted by limits to the size of batches.

Pre-Minting is when the contract auto-mints all of the NFTs upfront to the contract owner/deployer. In this way, we can Pre-Mint a Gazillion NFTs for nearly half of the cost of minting a single standard NFT. And this does not involve any shifting of costs either — the transfer function stays completely standard! In fact, our approach deviates very little from the OpenZeppelin ERC721 contracts, as most of them are still inherited. And this includes support for all of the extensions OZ provides!

This article outlines our new Infinite (ERC721i) contract and how I achieved Pre-Minting 1 Million NFTs for half the cost of a single standard NFT!

Let’s start by looking at some measurements.

Why is Azuki ERC721A Transfer so expensive? To be fair, this isn’t always the case. The measurement above is based on Batch-Minting 1000 NFTs to a single account, and then transferring a really high token ID before transferring the previous token IDs. Lower token IDs would incur less gas, as illustrated by the table above. This is how ERC721A shifts minting costs to transfer costs.

With Infinite (as with the Standard), Transfer's are completely standard. Any token ID can be transferred after Pre-Minting with consistent fees.

Technical Details

When it comes to writing state to the blockchain, we want to write as little as possible. For this reason, we want to leverage the read functions as much as possible.

When we consider pre-minting a number of tokens, we can safely make the assumption that all of those tokens belong to an “initial account” (maybe the contract deployer or owner), let’s call this account “Account A”. In this case, we don’t need to write a bunch of information to state. Instead, we can modify the read functions to return predetermined values. For example, if we pre-mint 1000 tokens with sequential IDs to “Account A”, then we know that token ID 567 belongs to “Account A” until it is transferred. We don’t need to store this initial state, we only store state when it is transferred (as per usual) and override the “ownerOf” function.

To pull this off, there are really only a few changes that we need to make to the standard;

  • We need to use Sequential Token IDs. We want to pre-mint a range, and they should naturally be in order.
  • We will rewrite the ERC721Enumerable contract to exclude the double-accounting around “allTokens”. Since we are pre-minting, we already know all tokens.
  • We will override the ownerOf and _exists functions from the base ERC721 contract.
  • We will modify the base ERC721 contract to change the scope of _owners & _balances from private to internal. Since we are overriding ownerOf we will also need to change the scope resolution for all ownerOf calls. (change ERC721.ownerOf to simply ownerOf).
  • We will include an address for the Pre-Mint Receiver.
  • We will include an integer for Max-Supply in order to Pre-Mint that amount.
  • We will add a really simple PreMint function!

For a full example, you will find a link to the source code at the end of the article.

Let’s take a look at the ownerOf function. In the ERC-721 standard, it returns the address of a given tokenId’s owner. However, if this function returns address 0x0 then we can safely assume that the token belongs to the “initial account” or Pre-Mint Receiver.

function ownerOf(uint256 tokenId) 
public
view
override(IERC721, ERC721)
returns (address)
{
// Anything beyond the Pre-Minted supply will use the standard "ownerOf"
if (tokenId > _maxSupply) {
return super.ownerOf(tokenId);
}
// Since we have Pre-Minted the Max-Supply to the "owner" account, we know:
// - if the "_owners" mapping has not been assigned, then the owner is the Pre-Mint owner.
// - after the NFT is transferred, the "_owners" mapping will be updated with the new owner.
address owner_ = _owners[tokenId];
if (owner_ == address(0)) {
owner_ = _preMintReceiver;
}
return owner_;
}

Now let’s take a look at the _exists function in the ERC-721 standard. It returns true if the given tokenId has a valid owner. Since we know how many have been pre-minted, we can safely return true for that range of token IDs.

function _exists(uint256 tokenId)
internal
view
override(ERC721)
returns (bool)
{
// Anything beyond the Pre-Minted supply will use the standard "_exists"
if (tokenId > _maxSupply) {
return super._exists(tokenId);
}
// We know the Max-Supply has been Pre-Minted
return (tokenId > 0 && tokenId <= _maxSupply);
}

Finally, let’s take a look at the Pre-Mint function. First, we store the Pre-Mint Receiver and the Max-Supply. Then we update the Pre-Mint Receivers balance. And then a touch of magic: We emit the ConsecutiveTransfer event as defined by EIP-2309. This event allows us to signal a transfer event for all of the newly minted NFTs at once. We can safely avoid firing 1 Million Transfer events!

The ConsecutiveTransfer event is currently supported by OpenSea and LooksRare among other platforms, and more are adopting it every day.

// Let's Pre-Mint a Gazillion NFTs!!  (wait, 2^^256-1 equals what again?)
function preMint() external onlyOwner {
// Set vars defined in ERC721EnumerablePreMint.sol
_preMintReceiver = owner();
_balances[owner()] = _maxSupply;
// Emit the Consecutive Transfer Event
emit ConsecutiveTransfer(1, _maxSupply, address(0), owner());
}

Now you can Pre-Mint your entire 20,000 NFT collection for your project and sell them on OpenSea by simply transferring them when purchased. You could even use the OpenSea JavaScript SDK to automate the sale of them all!

💡 No more Lazy-Minting fees. No more High Transfer fees. No more Minting fees period! Pre-Mint them all for half the cost of minting a single NFT and be done with it!

Pros and Cons

The pros of this method are quite obvious — super-cheap minting of NFTs! But there’s more! The Pre-Mint method sticks very closely to adopted standards, leveraging the popular OpenZeppelin framework as much as possible. I have changed very little, and therefore it is quite easy to adapt to your own projects. You can even continue to use OZ extensions!

After the pre-mint, everything else about the standard stays the same. This is why the Transfer method is able to remain standard. The only thing I really changed is the very first owner and how I account for the token supply.

The cons of this method are quite simple — I had to slightly modify the OpenZeppelin standard ERC721 contracts in a few places. This breaks away from the security that is inherent in using their battle-tested contracts. Perhaps an audit of this new method is in order.

Next, I assumed Sequential Token IDs and a Max-Supply. Now, most NFT projects use sequential IDs anyway, so this isn’t too big of a deal. As for Max-Supply, some projects might want to mint more later. In this case, I propose pre-minting extra! If you want 10k now and 5k more later on, and perhaps even 5k more after that, then pre-mint 50k and restrict the sales. There’s a lot you can do with some logic!

The only other downside is the use of the not-so-standard ConsecutiveTransfer event. This event is only used once during the Pre-Mint phase, and afterward, the standard Transfer event is used. However, as more and more platforms adopt this event and it becomes more of a standard, this point will be moot. As previously mentioned, it is already supported on the popular OpenSea and LooksRare marketplaces.

Noteworthy:

During our testing on the Rinkeby testnet, I noticed that OpenSea doesn’t like to index very far past 20,000 NFTs (can’t blame them, I minted a Gazillion!). However, once the token is Transfer'd to another account (say, after purchase), everything is standard and the NFTs get registered everywhere!

Want to see?

Charged Particles & Taggr have joined forces to launch a very special NFT project and we will of course be leveraging our Infinite ERC721i Pre-Mint contracts to provide the lowest-fee NFT drop possible! Stay tuned for more news and announcements! Exciting things ahead!

For now, you can test out these Shiny New NFTs by minting a few for yourself and discovering what may be the cheapest NFTs on the market! Grab 1 or Score 50 — only transfer fees, no minting fees!

Grab some Peppermint Rorschach NFTs!

OpenSea & Etherscan

OpenSea Collection: https://opensea.io/collection/peppermint-rorschach-genesis-1mm

Etherscan Mint TX: https://etherscan.io/tx/0xcbbe7583cd48b019dc2bcff3cd10526029afe966fed6695ef023315f233ebe2d

Source-code

Find the full source code on GitHub:

https://github.com/Charged-Particles/erc721i

MIT License

Next Steps

ERC-1155

Porting the Pre-Mint over to the ERC-1155 contracts.

Upgradeability

Add support for Upgradeable Contracts

Solmate

Add version using Solmate’s ERC721 contracts

Max-Supply

Eliminate the Max-Supply and bring back the “allTokens” array.

Feedback

Feedback is always welcome! Find us on Discord, Twitter, Telegram, or just simply comment on this post! We would love to hear from you!

Conclusion

By breaking down the “essence” of what an NFT is on-chain, we can greatly reduce the amount of state required to account for an initial amount and an initial owner.

By leveraging better events, we can greatly reduce the amount of gas costs incurred by users.

💡 No more Lazy-Minting fees. No more High Transfer fees. No more Minting fees period! Pre-Mint them all for half the cost of minting a single NFT and be done with it!

About Charged Particles

Charged Particles is a protocol that allows users to deposit ERC-20, ERC-721, and ERC-1155 tokens into NFTs. A scarce NFT (e.g. Art, Collectible, Virtual Real Estate, In-Game Item, etc.) can now be transformed into a basket holding a number of other tokens. The Principal amount can be time-locked inside the NFT, and through integration with Aave’s aTokens, the programmable yield from these DeFi yield-generating assets is just a few clicks away.

Endless applications of DeFi NFTs!

“Genergizable” PFPs | ✅ DAO Capsules | ✅ Vesting Capsules | ✅ Escrow Capsules | ✅ NFT Trust Accounts | ✅ Gift Baskets | ✅ Improved NFT Index Funds | ✅ Financially Appreciating Artworks | ✅ Nested Bundles of Artworks | ✅ Redemption Tickets | ✅ Creative Crowd-funding Campaigns | ✅ Company Token Promos

Stay in touch with Charged Particles

Website | Docs | Telegram | Twitter | Instagram | Discord | LinkedIn | YouTube | Reddit | TikTok

--

--