We are excited to announce we will be launching the official Pixel Devs NFT, Tuesday 22-2-22 2:22:2 UTC!
Anyone who currently holds the Genesis "Devs for Revolution" NFT is eligible to mint the corresponding Pixel Dev. The minting price will be 12 MATIC.
The Derivatives project was one of the very first projects to be born out of the Developer DAO, with the intention to create a brand identity and provide our members with a more personalized NFT.
While you may have already come across the colorful avatars on Twitter and Discord, for the past months we've been busy building the website and underlying infrastructure that will allow DAO members to mint their own personal avatar.
Due to rising gas prices on Ethereum Mainnet we decided to go with the layer-2 chain, Polygon. Working with Polygon would mean that owning your personal avatar would be more affordable and accessible. However it also comes with an extra technical challenge to overcome: bridging the cross-chain gap from Ethereum to Polygon.
This blog post is a technical peek behind the scenes of how we solved bridging the two blockchains, which possible solutions we explored and what considerations influenced our decision making.
Setting the stage
First, a brief rundown on what we set out to build and the challenges at hand.
- The original membership of Developer DAO is a Loot-inspired NFT consisting of 8 unique traits. I'll refer to this as the "Genesis NFT."
- The Derivative NFT is a visualization of all 8 traits. Each NFT has a token ID ranging from 1-8000 which maps directly to the corresponding Genesis NFT with identical traits.
- Any wallet address holding a Genesis NFT on Ethereum Mainnet is eligible to mint the corresponding Derivative NFT on Polygon.
Now the challenge is: As the Derivative smart contract on Polygon cannot make any outbound network calls and thus connect to Ethereum itself, how do we verify whether a given wallet address holds the corresponding Genesis NFT?
Before diving into the final solution we ended up with, it would only be appropriate to mention some of the initial ideas we considered.
1. Snapshot / Merkle Tree with a Cron Job
A simple solution would be to take a snapshot of all 8000 owner addresses and write it into the Derivative smart contract. This is entirely doable although it might cost a decent amount of gas upfront. Luckily this shouldn't be too expensive on Polygon.
B: Merkle Tree
An alternative to the above is utilizing a Merkle Tree.
The difference from the first option is that instead of writing all 8000 addresses to the smart contract upfront, one only needs to store a so-called Merkle Root. This is a ‘hash’ of all the owner addresses along with the tokens they hold.
When a user wants to claim a Derivative NFT, they would need to send along some accompanying hashes from the Merkle Tree. Combining the user's wallet address with the hashes, the smart contract would be able to produce a final hash that should match the Merkle Root stored in the contract.
This article outlines the principles of using a Merkle Tree for our use case very well.
The Cron Job part
So far so good. However, both options A and B have a significant shortcoming for our use case:
They both rely on snapshots.
While we could reframe our initial project requirements to simply base it off a snapshot, we like the idea of keeping it as real-time as possible. At the time of writing this article the Developer DAO is still doing giveaways, and the Genesis NFTs are regularly changing hands. We feel it would be most fair that people purchasing NFTs on the secondary market should also be eligible for the Derivative NFTs.
Both options therefore need to be tweaked with some way to keep the data fresh. In its simplest form this could be achieved by running a Cron Job.
The Cron Job would be responsible for either syncing new owners to the contract, or updating the Merkle Root and its associated tree whenever Genesis NFTs has changed hands.
The downsides of using a Cron Job are:
- In both cases, changes cost gas. The more changes, the more gas.
- Cron Jobs can fail. When they do, it tends to happen really silently. Data would slowly drift over time, and it might take a while for anyone to notice.
- The Cron Job would need to run with a privileged private key that has write-access to the contract (more on this later).
2. Snapshot / Merkle Tree with Chainlink Keepers
Iterating on the previous solution, we could remove the need for a Cron Job by using Chainlink Keepers for the subsequent updates.
Chainlink is an oracle service which enables smart contracts to connect to the surrounding world which is not otherwise possible.
Chainlink Keepers is an automation service which provides a way to trigger contract executions when certain conditions are met. Each executed job is referred to as an "upkeep".
Besides being a more decentralized option, one possible advantage is that the code for the upkeep would reside directly in the Derivative smart contract, thus be immutable and free of private keys that could be exploited.
While no one on our team has first-hand experience using Chainlink Keepers, we believe this is a very solid approach and definitely a good option for building resilient dApps in a web3-native way.
One consideration though is cost: failing to keep the Chainlink account topped up with LINK, would stop the upkeep from running. It seems that both checkups and upkeeps incur a fee, which means it would require an ongoing cost whether or not any changes are written to the contract.
Arriving at our solution
What if we could perform the authorization check runtime outside the realm of the smart contract?
This would mean no need for up-keeping any snapshots thus no extra gas cost for writing to the smart contract. The verification would always rely on the current ownership and could never drift or lag behind from reality.
This could be achieved by hosting a stateless server or serverless function that performs the authorization for a given wallet address and then creates a signature that is passed to the smart contract.
The entire flow looks something like this:
- Client sends an authorization request to the server containing the user's wallet address and token ID that they would like to mint.
- Server verifies against the Genesis contract that the requested address is the owner of the requested token ID.
- Server creates a signature using its own private key. The signature contains the requested address, requested token ID and a minting deadline (e.g. 10 minutes).
- Client uses the signature to send a minting request to the Derivative contract.
- The Derivative contract can now verify that:
- A: The signature is legit and originates from our own server (matching the server address stored in the contract).
- B: The signature matches the values in current minting request (address and token ID).
- C: The deadline has not expired (ensuring that signatures are always freshly created).
From a business standpoint the server method checks all the boxes and furthermore has some significant advantages in terms of flexibility:
- It can be used with any EVM compatible blockchain as it simply needs to produce a verifiable signature.
- Business logic for minting can be easily tweaked and almost anything is possible. For instance we could:
- Have raffles or games to kick off the minting.
- Allow for non-DAO members to mint special "DAO supporter" NFTs.
- Most importantly, we could implement new ideas down the road without having to prepare the contract upfront. This is really the kicker as we expect future extensions and products to be built on top of the Derivatives after launch.
The only hard-coded logic in the contract at this point is the mint price. Even this too could be extended, allowing the server to enforce a mandatory fee for certain exclusive variants or extensions.
As you might have guessed by now, each of the solutions discussed above comes with their own sets of pros and cons. The authorization server is no exception.
Overall our considerations can be grouped into these categories:
- Availability & permanence
- Flexibility vs immutability
Allow me to expand on a couple of the main considerations across the three solutions we reviewed.
Availability & permanence
Several of the options we reviewed required some hosting of either server, Cron Job or both. While we'd like to depend on as few services as possible, sometimes this is just not practically possible.
Even going with a static snapshot or Chainlink Keepers, we would still need to host our web client somewhere thus introducing an inevitable point of failure.
At Developer DAO, we are fortunate to be sponsored by Vercel which provides us with reliable and easily-managed hosting.
As for permanence, our top priority is that anyone who pays for an avatar owns it for good. We ensure this by hosting the image files on IPFS. Even if it might not be possible to mint new avatars a 100 years from now, you can still access the ones you have purchased.
You might have noticed a few of the reviewed solutions mentions the use of a private key.
In the case of the authorization server, this is a direct trade-off for the flexibility it brings to the table. The smart contract has very little enforcement itself and must trust that the signature could only have come from the server having done its due diligence.
As a consequence it demands extra attention to setting up and maintaining a secure production environment, as it could otherwise compromise the integrity of the server.
Luckily this is not any new issue in the web world, and there are more than a few measures than can be taken to properly secure your application. We appreciate that this is a very individual assessment and would not always be an acceptable trade-off depending on the project.
The landscape of web3 sometimes feels a bit like the Wild West with lots of unexplored or less-documented territory. This is partially what makes makes it fun to explore, and at same time why seemingly very simple tasks can turn out quite complex to solve.
Perhaps some of our considerations can be useful should you ever find yourself with a similar need to bridge blockchains. In this case you may find yourself taking another approach than what we did. Each project has its own unique requirements and considerations to make, thus no one size fits all.
If you have found this blog post interesting and you are curious on the actual code that powers our smart contract and server, make sure to sign up for web3con.dev where I will be giving a talk on this exact subject!
In the mean time we look forward to sharing our work with you soon at https://pixel-devs.developerdao.com 🚀
If you are a Developer DAO member and wish to get involved, please feel free to introduce yourself in the #derivatives-help-wanted channel on Discord. We are actively looking for game developers and creatives who can help build out an ecosystem around the avatars.