2

Suppose I have 1000 NFT's I would like to distribute. The user can see what any NFT looks like in advance, but they don't know what they'll be getting until they actually buy it. In other words, there will be a random number generated from Chainlink VRF that determines what they get. This ensures a fair distribution, so the rare NFT's can't be bought by early users.

The project that I see comes closest to this is Hashmasks, but they also had the quality that the user didn't even know what any of the artwork would look like in advance. For the purposes of my project, I don't need this property.

The main issue I see with Hashmasks is that there is no tokenURI function. They upload everything on IPFS, so it seems the only way to have a tokenURI would be to know the hashes of every NFT in advance and store them in the contract, and then append it to the IPFS url prefix, but this would be incredibly expensive.

I was wondering if there was a way I could still retain the property of having the tokenURI whilst also ensuring a random distribution.

For the random distribution part, I was thinking of getting a random number, r, from chainlink VRF, and then doing:

i = rand % maxSupply

Then I check ownerOf(i), if it's taken, then i+=1, if not taken, then assign. I was wondering, does the mere checking of ownerOf(i) take gas fees? This loop, in the worst case scenario, would take maxSupply - 1 iterations to assign a NFT; how bad could the gas fees look like?

1 Answer 1

2

Given two integers a and b which are coprime, then (a x + b) modulo n will visit all integers from 0 to n - 1 exactly once.

You can use this to "randomly" assign NFTs by choosing a fixed large prime a and populating b from VRF. Given sufficiently large a, a and b are very likely to be coprime.

You can then simply iterate through [0, x) assigning the nth NFT.

Here's a toy example written in Python to demonstrate it working:

a = 97        # Random prime
b = 12345678  # VRF
n = 10        # Total NFTs

for x in range(0, n):
    nft = ((a * x) + b) % n
    print(f"Mint {x} will be NFT {nft}")

Output:

Mint 0 will be NFT 8
Mint 1 will be NFT 5
Mint 2 will be NFT 2
Mint 3 will be NFT 9
Mint 4 will be NFT 6
Mint 5 will be NFT 3
Mint 6 will be NFT 0
Mint 7 will be NFT 7
Mint 8 will be NFT 4
Mint 9 will be NFT 1
4
  • What does the 'x' refer to here? Do we fetch a new 'b' each time? If so, then wouldn't it make more sense to just randomly pick a starting index upon contract creation? I'm trying to avoid a starting index, because then people will know what order to buy things in (eg. 514 comes before 513) I want something random, where the user has no idea what they'll get. I'm just concerned about gas fees Jun 12, 2021 at 22:44
  • x is just an index. You only fetch from VRF once. I've updated my answer to include a worked example. It's obviously possible to predict the next mint if you know both the prime and the VRF value.
    – jnic
    Jun 13, 2021 at 9:49
  • Oh I see now, thanks. The only issue with that though is that people will be able to deduce what the next NFT will be. I.e. if they want the 200th NFT they just solve for that formula. I'd prefer a function that is always random, where no one knows what they're getting until after they mint it. Which is why I proposed the method in the OP (the random number is generated every time a NFT is minted, to be clear) Jun 14, 2021 at 4:51
  • That makes sense. I think calling VRF every time might get very expensive for you (2 Link per call). You might want to look at other sources of pseudo-randomness instead (like a hash of time and block height) that might be sufficient for your purposes?
    – jnic
    Jun 15, 2021 at 11:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.