A month or so ago, I had to do a few NFT migrations. I migrated an NFT collection of over 3000 NFTs by creating another NFT contract with a built-in airdrop function and passed in array arguments.
The result was that, I spent around 11 ETH in order to airdrop 3000 NFTs to holders. At an average cost of around 30,000 gas per NFT.
For a normal NFT minting contract, 30,000 gas per NFT is excellent. Assuming 100 GWEI gas and ETH price at 3,000 USD, that is only 9 USD for a mint!
However, when you are the initiator of the transaction and are spending 3,000 x 9 USD, the costs add up quite a lot and totaled me to over 30,000 USD (as gas was a bit over 100) for this airdrop. It was a painful learning experience and I definitely did not want to go through it again.
The First Half of Phantom Minting
However, I had one remaining migration to make, and that was on my own project Message to Martians.
Faced with the fact that I would have to pay another 11 ETH + gas in order to migrate another collection plus a bit more time to think, I thought to myself: there has to be a more efficient way than this, thus I created a new way of doing migrations — through Phantom Minting.
For the migration, there was a certain circumstance that played in my favor:
- I was able to lock the transfers of the old collection
Thus, equipped with this knowledge, I had a playing field to come up with some mechanism on.
On a migration, I assumed that:
- If the owner mapping of the new contract does not exist (belongs to address(0x0), but the token ID is within the range of the totalSupply of the token, this means that the token must exist, and belong to the owner of the token in the previous contract which is locked.
Thus, I crafted a custom ownerOf logic to reflect exactly so.
This function says that, if the tokenId_ is equals or less than 3259 (the total supply of the previous contract) and the internal _ownerOf[tokenId_] mapping does not exist, then, the owner must be the owner of the token in the previous contract. Thus, read from the previous contract and return the ownerOf.
However, if the _ownerOf[tokenId_] mapping does exist, return the internal storage value instead.
The Second Half of Phantom Minting
After being able to create pointers to the correct owner in your modified ownerOf function, you will quickly find out that the NFT itself doesn’t exist, even though the ownerOf returns the correct owner, and the contract returns the exact information that a traditional ERC721 would. That is because, Etherscan and the likes of OpenSea etc. actually use another metric to figure out NFT mints — events!
An event Transfer(from, to, tokenId); is emitted from an ERC721 contract. If the address from is 0x0, and to is a wallet address, then, Etherscan and the likes accepts this as a “Mint” of an NFT.
So, it was simple to do the second part of the Phantom Minting — emit Transfer events on any tokens you want to “Mint”!
I wrote an onlyOwner initialize() function which takes in an array of tokenIds_ and owners_ and then Phantom Mints the NFTs (airdrops) them to their respective owners.
Because the ownerOf logic is as planned, after the NFTs appeared into people’s wallets, they were able to interact it with the other functions in the contract because it returned them as the correct owner, even though no owner was written into SSTORE! This is because of the modified ownerOf logic reading from the previous contract, with a solid logic that is able to determine the correct owner at all times.
And there you have it! A innovative system I call Phantom Minting that allows NFTs to be minted without actually writing to SSTORE.
With this simple logic using a modified ownerOf function, I was able to save thousands of dollars (around 10 ETH actually!) and only spent 1 ETH to migrate Message to Martians contract.
Since then, a few innovative contracts has been made that leverage the same type of Phantom Minting logic:
- ERC721A from Azuki’s NFT which used Phantom Minting logic into a minting contract instead of a migration contract to allow very low gas batch minting of NFTs.
- Cyber Turtles which uses Phantom Minting logic and Single Contract staking in order to generate sTokens (Proof of Stake) tokens to prove staking as well as able to use it for ownership verification and such through a standard ownerOf interface. (This allows something like collab.land to read from it easily)
- More to come? :)
Thanks for reading!