Mint ERC721 tokens in pseudorandom order


Due to the specifics of the project I'm working on I need a way to mint token IDs in a random fashion rather then sequentially. The requirements for randomness are not particularly strict, and block.timestamp + totalSupply() is enough for this particular case.

The problem comes as the amount of minted tokens increases. Since we need to ensure a new token ID was not minted yet, a !_exists(tokenId) lookup is required. But eventually the number of these calls becomes so big as to exhaust the gas limit.

(I know you might ask why not randomize the assets mapped to these IDs instead? But I wouldn't have to ask you this question if this would be possible — it is a strict project requirement.)

So the question is: Is there a more computationally efficient way to do this? Or a sequential lookup is the only solution which isn't going to work?

Have you considered using a hash? The likelihood if the resulting token id having been minted already is negligible.

Actually, in the setup you propose with block.timestamp + totalSupply(), why would this result in clashes? It sounds relatively unlikely.

1 Like

Thank you for the response.

Could you please elaborate on this proposal? What exactly has to be hashed?

The IDs pool is still narrow — 10000 tokens. The timestamp construct is used as an offset modulo 10000 to make a starting point for minting. You're correct, initially the clashes don't pose a problem but as the tokens pool gets progressively depleted, the consecutive ranges of owned tokens grow, and the gas usage for token availability check starts to grow pretty dramatically as well: the mint call with a single lookup uses 110889 gas, 139580 for 10 lookups, 414080 for 100 which is already prohibitively expensive to mint a single token.

I see what you mean...

Why do you need this to be random? Would it be ok for the user to submit a seed with the mint function call, so that they can validate that the id is available.

Due to the fact that collectible traits are going to be open from the start and their IDs are related to token IDs of an existing collection: randomized minting would've been the most fair for a presale phase by thwarting rarity snipers.

I've been investigating this problem further and there seems to be no practical way to achieve the desired result even by introducing additional storage variables. The easiest solution might've been an EnumerableSet populated with 10000 IDs to draw from but Solidity lacks an ability to define mapping values while declaring the variable (and populating it after the fact is going to cost >$20k just in gas fees).

So we're going to relax the requirements and move to a classic sequential IDs minting.

1 Like