I've been itching to try my hand at coding in web3, and what started as a brief exploration quickly became a project to launch end to end with all the bells and whistles that come with a production-grade software project.
My talented friend Brent doodled a collection of pixel art based on vices, and I decided to make an NFT collection out of it.
The main building blocks of the project are:
- Image combination and metadata generation algorithm. Along with managing pins via Pinata.
- Solidity & the smart contract
- Landing page & minting functionality
- Server for serving landing page data, metadata, the images, and managing Opensea listings
Brent provided 7 background images and 14 foregrounds, and I combined and upscaled them in Python with
PIL to get 98
The program also generates NFT metadata in the format Opensea expects it.
"name": "Metadreams # 0",
"description": "What dreams are made of",
I compute the provenance hash from the image files, which we'll use later in the smart contract.
Pinata was pretty straightforward to use, even though their UI has a lot to be desired (bulk deletes please?). Their minimal API was functional enough where I could do everything I need,
pinList, so it ended up being my choice.
By generating images in parallel to take advantage of my 16 core machine, I was able to cut the generation time down by around 10x. Python made this really easy with
pool = mp.Pool(mp.cpu_count())
Solidity & the Smart Contract
Solidity isn't as hard as it seems; it's a tiny library one can pick up in a few hours. What's difficult is anticipating and identifying all the bugs therein. Luckily, NFT contracts are very straightforward. There are plenty of popular NFT projects where you can look at their contracts on etherscan.
Note that Opensea testnet is on Rinkeby, so that's your best choice for testnet.
After completing the contract, I installed
hardhat, compiled and deployed the contract. To be able to connect to the blockchain, I signed up for Alchemy. Infura is a similar product and also a common choice. As a last step, I grabbed an Etherscan API key and used
@nomiclabs/hardhat-etherscan - this lets users validate contracts from the command line. Validating the contract is surprisingly finicky otherwise.
Landing Page & Minting
As an infra engineer, front-end engineering is IMO the hardest type of engineering because one has to know so much, work with different tools that don't necessarily work well together, and the space moves so fast. If any front-end engineers are reading this and want to share tips on how to get better, ping me on Twitter!
I used NextJS to bootstrap but did not use their Server Side Rendering, since the site is so simple. I want to optimize for serving speed by putting the whole thing up in a CDN.
The landing page is where most of my time went. Just handling all the different states that can occur when a user attempts to connect, mint, including various errors, connection issues, gas prices, etc was super tedious, and I'm sure solutions will soon arise to make this easy.
I had to learn (read: copy) a lot of CSS to make things look ok and be responsive, and styling was where I spent ~50% of all my time in this entire project 😱. I really hope there's a better way to style apps in the future.
The libraries I used here were
web3.js was easy to use and the documentation was great. I also tried
ethers.js, which has a lot of great things going for it - it's a much smaller library, the separation of
provider, but in the end, I felt the documentation wasn't strong enough, which as a newbie is a no-go.
abi generated from
hardhat compile from the previous step, it's pretty straightforward to use with
web3 and start writing code against the contract. I iterated a couple of times on the contract to get all the methods and variables I needed.
I rabbitholed into the unnecessary but satisfying task of trying to reduce my compiled code size, beginning with
3MB and reducing it down to
1.14MB and then down to
I did this by installing a custom webpack resolver to remove all repeated instances of
bn.js , removing the node dependencies for
walletconnect with minified scripts to be loaded, reducing image sizes. All of this reduces the compiled size by 10x.
What also surprised me was that I wasn't able to find a good Modal React component. I tried MUI and Bootstrap, and ended up writing my own with functionality like clicking outside the box to close. Overall, I'm surprised that in 2022 there is still so much boilerplate code to write for a basic app. Again, I'm not an expert here, ping me to share!
A server is not necessary for most NFT projects, but I wanted a bit more finesse with the metadata reveals and a layer of indirection for functionality I might add in later.
The server starts up and queries the state on the blockchain to get data on the contract and see how many NFTs are minted. It starts up a cron process to query every hour for state. The server gates reveals by serving up metadata dynamically. Every hour, based on the new NFTs that were minted, the server makes calls to Opensea's api to refresh Opensea's metadata for new reveals. It also provides basic state for the front end to query.
Express.js was a joy to use, with so many great 3rd party plugins. I was against writing server code in a scripting language for the longest time, but it's so easy to move quickly and I see the light now.
This was a fun project that helped identify a couple of interesting gaps in the ecosystem. You can check out the end product at metadreams.dev , and say hi on Twitter .
Upcoming posts include thoughts on web3, engineering management for dummies, how to hire your first engineer / engineering leader. Subscribe or check back in!
 Brent is awesome, follow him on Twitter: https://twitter.com/burnto
 Article on provenance hash https://medium.com/coinmonks/the-elegance-of-the-nft-provenance-hash-solution-823b39f99473
Bored Ape Yacht Club's contract https://etherscan.io/address/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d#code
 Metadreams NFT website https://www.metadreams.dev/
 Come say hi! https://twitter.com/emmaytang