On-Chain Smart Contract Design of OnChainBirds

OnChainBirds
5 min readSep 26, 2022

When I think about great art and rarity design Moonbirds is one of the first that comes to my mind. Once Moonbirds went Public Domain, I knew I had to try to recreate the birds, while also doing my own unique spin on the way we mint NFTs on-chain. My obsession with on-chain smart contracts runs deep because they make the minting experience more fun, fair and don’t rely on a centralized entity, which means you can truly own your NFT forever. I found that I could allow a lot more generative complexity on-chain than we’ve ever seen before while also keeping the minting gas cost low and predictable. My goal was to create a fully born on-chain collectible that is indistinguishable from something crafted off-chain. OnChainBirds are a whole new collection of never seen before birds that were fully random generated on the blockchain at mint.

Interested to know what it means for a NFT to be stored on-chain? Here’s a great video explanation and showcase by BigWise:

In-Depth Breakdown of our Smart Contract

The following methodologies are the reason why our smart contract is unlike any other on-chain smart contract. I heavily relied on branchless programming, which is pretty much never talked about in regards to Smart Contract Development, but very useful for the specific use case of on-chain random generation. You can find the full source code here.

What does it mean to be born on-chain and why on-mint random generation? Your NFT can either be off-chain, on-chain but pre-determined or randomized on-chain. Randomizing it on-chain is the most beautiful solution, because the minting experience should feel like opening a pack of Pokémon cards. Instant and fair. On-mint random generation is the most direct and fun way to achieve this while keeping deployment costs low. Knowing that the birds are randomized on-chain and that there is no centralized server involved means that we can assume that the mint is provably fair, while also keeping track of that on the Blockchain. This is the only reliable way to ensure that nothing gets tampered with or leaked which means no one can snipe rares pre-reveal.

Encoding/decoding the art and deployment costs: We encoded all the traits of Moonbirds to 91kb by storing only the relevant parts of the image, encoding it with RLE and using a color palette. For example, the array of pixels: R R R R G G (R is red, G is green) is encoded as: (R, 4) (G, 2). At around 7 gwei gas fee deploying the contract and uploading the pixel data has cost us roughly 700$ (0.57 ETH). Storage can be compressed and optimized further, but with the current gas prices it wasn’t necessary. The decoding as seen in the video above is optimized by connecting consecutive (same colored) pixels into one SVG rectangle element. We also support any amount of transparent colors being blended. The only limitation one could face is the Opensea API not being able to query too complex SVGs. Our pixel data and algorithm only hit roughly 25% of that limitation, so there is still a lot of room to do even more complex art.

Random generation done from memory instead of storage: Memory in Solidity is a temporary and cheap place to store and read data whereas Storage holds data permanently but is expensive to use. Until now other on-chain projects kept storing the probability data for their on-mint randomization in storage, which is very inefficient and the reason why their random generation cannot be as complex. The probability tree being stored in memory is a clever way to keep the gas consumption from exploding. Minting an OnChainBird only costs between 100,000–170,000 gwei gas total despite being generated from the largest on-chain probability tree ever. The gas fees are not much higher than minting an off-chain NFT despite the heavy probability calculation. Doing our generation from storage would have cost over 410,000 gwei in gas for each mint (= 2.5x more gas). It may seem insignificant for a single mint, but collectively speaking more ETH was kept in our holder’s wallets instead of paying the network and that can only be a good thing. This means that the total gas fee paid to mint out the collection was reduced from roughly 40,000$ to 16,000$ (30 to 12 ETH).

Consistent Gas Consumption: Keeping the gas fees consistent was the most difficult requirement to implement, because as the generative complexity grows to the level of Moonbirds, it gets more and more difficult to keep the gas consumption for each mint the same. Especially with mint trackers getting more popular which can copy mint transactions and with people who prefer to mint from Etherscan; this is very important. Without a solution there will be frequent ‘out of gas’ errors and they will have to pay for the failed transaction fee anyway. This can also offset the probability distribution which is an even larger concern. Gas consumption is often overlooked and a lot of on-mint random generated projects have had issues with that in the past or are simple enough that it isn’t a problem yet. My solution was to use branchless programming, which is why you might see a lot of bitwise operations throughout the code.

The Probability Tree and Clone Avoidance: All traits are randomly selected from the largest probability tree ever stored on-chain. The tree being stored in memory created the issue that the contract size started exploding above the maximum size limit of 24kb. To keep the contract size low enough and gas consumption efficient we decided to split the DNA generation into 2 stages. The DNA of each bird gets generated at minting time and avoids most of the duplicates that get generated, but not all. Basic conflicts between traits were removed from the probability tree and simply handled by the DNA read function. This resulted in a few duplicates (around 90 in the whole 10,000 collection) that were scanned by a server and automatically rerolled as fast as possible. We were barely able to fit the contract into the maximum contract size limit, but because of that it was possible.

Why Not Split The Contract? Keeping the contract as one, makes it easier for people to verify the source code. Furthermore it also saves minting gas because most of the contract size was consumed by minting related functions and splitting them would have been inefficient.

Legendary Random Generation: Rolling for a legendary while keeping gas consumption consistent was also pretty difficult. We solved it by using the LegendaryId in each birds DNA as a counter that is copied from the last bird that was minted. Legendaries are rolled during the background selection and if drawn the LegendaryId gets increased. They use a mix of random and predefined traits that are derived from the Id during the DNA read function.

Conclusion

OnChainBirds is the first NFT project to create a random generation directly on-chain — with an amount of traits this high and the complexity of the probability tree. To achieve this efficiently we are using fairly new features of Solidity (YUL IR Pipeline) and branchless programming. There’s still a lot more that can be explored when it comes to innovation in the web3 space and we’ll make sure to keep pushing the envelope of what’s possible. But being fully on-chain is a nice way to start and hopefully it becomes the new standard.

Feel free to join our discord or dm me if you’d like to discuss this topic. Here you can find the source code of our smart contract and our socials:

Source Code | OpenSea | Website & FAQ | Discord | Twitter

--

--