Beyond NFT Provenance
A standardized practice set by Bored Ape Yacht Club is being taken to a whole new level…
There are 10,069 Particlon NFTs from 4 different planets: Hydroluna, Ignisis, Terrasonia, and Bonded. Each planet has one god-tier Particlon, which furthermore adds significance to the reveal on April 14th. As a blockchain developer, I wanted to reassure both the rest of the team and the holders that no one is able to abuse their power and participate in what is known as “insider trading”.
I received the final artwork in five folders totaling more than 40GB, four folders containing ordinary Particlons and one containing the four god-tiers. Each image also had some initial token ID used by the artists to separate the Particlons, but that became irrelevant as you’ll see now.
The next step I had to do was shuffle them into an initial permutation that wouldn’t change anymore, from which they would be shuffled once again by the Fisher-Yates algorithm using randomness that would be supplied by Chainlink VRF.
I decided it would be a good idea to order the images in ascending order by their sha256 hashes. This firstly seemed like a good source of pseudorandomness to run some tests, but more importantly, it allowed me to create a “natural” constant order that can be easily reconstructed at any point in time without requiring any previous knowledge. It eliminates a piece of information and makes the whole process I’m about to describe a bit easier.
The next step was to concentrate all the hashes into one and calculate the hash of that string — that hash is known as a provenance hash.
In our case, it’s not the final provenance hash, but merely a starting point in the validation process; hence I will call it the pre-provenance hash.
The pre-provenance hash for Particlons, deployed on Ethereum Mainnet (0xCd2BA94E435e536dC48648EAb2f4F1db257BC64C) is:
By swapping any images, or changing just one bit in any image — the provenance hash is going to change in an unpredictable (but still deterministic) way, and it will be obvious to a validator that someone has tampered with it in some way.
Most projects will usually end it here and consider their work done — either by adding an offset ID that is calculated from the block the contract is mined in, or in some similar way. However, this fundamentally doesn’t solve the “insider” knowledge problem, it simply translates all the token IDs by a random amount, but keeps their relative order. The question arises, how can we then shuffle the tokens without knowing in advance the final permutation?
Chainlink has entered the chat…
A new NFT order
There is a very simple and elegant algorithm called the Fisher-Yates shuffle. We first pick a random element from a list containing N elements, remove it from the original list and write it as the first one in the new list.
Then we pick another random element from the first list, now containing N-1 elements, and write it as the next element in the new list. We repeat the whole process until the original list is empty. Note that the last element doesn’t have to be randomly chosen, because it will be the only one left, and can be simply written at the end of the newly ordered list. The total amount of random calls needed for our project is 10,068.
We can now construct a system that doesn’t use predetermined (pseudo) randomness, but rather generates it from a verifiable source not before it’s actually minted. But, how can we design a smart contract in a way that it’s impossible to generate all the random numbers in advance, which would again beat the whole purpose of such a system?
Architecting a cross-chain solution
The smart contracts will use Chainlink V1 on Polygon to save on gas and LINK tokens since we don’t really have any benefits from using Ethereum Mainnet other than satisfying Vitalik.
Personally, I find it even cooler to do the validation using Chainlink on Polygon, not because it’s a cheaper solution, but because it gives me so many more ideas for the future and opens the door to cross-chain integrations…
This all sounds very cool in theory, but in practice, it can be quite a task to orchestrate together. There are a total of 3 APIs needed, two of which we have to maintain on our own, but only temporarily.
The first API is a public one — Etherscan API.
Thanks to the innovative tech at Chainlink that allows HTTP GET requests, Etherscan API can be used in the smart contract itself to prevent obtaining more random numbers (reveals) than the current minted supply is.
If you click on the link above, under “result” you should see the current total supply of Particlons.
We can therefore easily add a logical limitation that will prevent requesting randomness from Chainlink in case the number of random numbers would exceed the total supply.
The second API is the periodic supply checker which calls the deployed contract on Polygon to produce more randomness when needed.
It should be noted that anyone can actually call this function, in order to prevent any unnecessary dependencies.
The third API is the metadata server — it is actually only temporary and is needed for as long as there are unminted tokens. This API gets the latest random number from the smart contract and runs the next Fisher-Yates iteration since that part can be done off-chain. There is no need to run it on-chain since we are using a pre-provenance hash (encoded in the contract), meaning anyone will be able to verify it on their computers.
When all supply is minted, we are going to move to a more permanent and decentralized solution, probably something like arweave.
There will also be an immutable provenance string encoded in the contract that can only be set by the owner after all have been minted, as a proof of record that everything was done with integrity.
It is important that the pre-provenance hash is encoded as a public function anyone can call in the contract right after deployment. This is so that it won’t be possible to retroactively go and manipulate the image data and somehow after the randomness is known, brute-force the images into desired token ids by altering the image bits in ways the human eye cannot even perceive.