I've read and written simple smart contracts in the past yet I'm relatively new to getting random data in a transaction, which seems to be harder than I've thought, due to the nature of determinism of EVM.

I'm working on a NFT project where there will be several rarity categories of NFTs. The rarity needs to be randomly assigned on mint. Let's keep things simple and for the sake of example assume there will be a total supply of 100 items and 5 of them will be rare.

I've researched this a bit and found the following:

  • Commit and reveal is NOT an option as it's not a lottery. I need everything to be set and frozen on mint.
  • I can't get input from the user as a random: even if I provide a nice UX with random generation at JS, client side random generation is obviously insecure.
  • I can't simply give up on-chain randomness and generate a random rarity on my side:
    1. If I put the NFT data into IPFS, they will be accessible in advance for each NFT, obviously not an option.
    2. If I point metadata URI to some HTTP server that I control, users will need to fully trust me on a fair distribution, which I don't want. I need things to be trustless.
  • I'm not sure about external oracles such as Provable or Chainlink, they seem to use callbacks for random generation, don't know exactly how to proceed that way. It also costs, I'd prefer a free one, though if it's the only option I could pay for them.
  • I've also checked the contract code of LOOT at https://etherscan.io/address/0xff9c1b15b16263c61d017ee9f65c50e4ae0113d7#code. It seems to have a random function defined like this:
    function random(string memory input) internal pure returns (uint256) {
        return uint256(keccak256(abi.encodePacked(input)));

It seems to take KECCAK-256 on the input, which is itself just concetenated+packed form of some hardcoded string and token number. I don't think it's safe to from an attacker to get a rare mint at all, but correct me if I'm wrong.

What would be the best way to proceed? I know generating randoms have been asked before, but many of the questions are about either lotteries (that can be revealed in the future) or collections with things being equally rare but just random. Is going with something similar to Loot's contract safe, or is going 2-process with an oracle or commit/reveal the only good way to go?

  • People normally go either Chainlink random oracle way or pseudo randomness using the block number, timestamp etc. Jan 21 at 22:50
  • @TarasFilatov I'd actually love to go pseudo-random as the project isn't of high value and unlikely to hold any financial value at least in the mint period (after mint is over it won't matter anyway) so miners rejecting transaction isn't an issue in practice for this project. However I'm concerned with an attacker finding a way to submit transactions in a way to get the rare NFTs. Jan 22 at 10:47
  • In such case maybe you can break it into two steps. At first step they Reserve the tokens and get the rarity numbers that are temporary and will be translated/mapped later into real numbers. At the second step when they Claim you unveil the mapping that has been previously hidden in a data structure of another contract, on IPFS etc. Jan 22 at 16:38
  • Hmmm. I really don't want to go the two-step process, also the reason I don't want to go commit-reveal path too. But yeah maybe I'll go something like that if I can't do it reliably with Chainlink or something similar. Jan 22 at 17:01
  • It is not possible to do an NFT drop that is: Fair, Random, Immediate, and Decentralized ("no FRID") Feb 7 at 23:28

1 Answer 1


I've ended up doing a two-phase mint:

  • At first phase the tokens are minted and their block number is stored on chain, but their value/rarity isn't determined.

  • After waiting at least 1 block but not more than 256 blocks, a second call is made to calculate the randomness based on the block hash of the next block after the block that the NFT was first minted.

The downside is that it's two-phase; user needs two transactions each costing some gas and it needs to be done within 256 blocks otherwise the blockhash of N+1 will be zero. (which I disincentivized by defaulting that case to the lowest-rarity (most common) tier in my project)

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.