Appearance
Tutorial: random-trait NFTs
Source: verity-examples/contracts/src/RandomTraitsNFT.sol (ERC-721, OpenZeppelin v5.0.2).
Mint first, reveal later. The token is minted, and paid for, before its randomness exists. The traits are written when the VRF word arrives, so a minter cannot see the traits and back out, and cannot reroll.
solidity
function mint() external payable returns (uint256 tokenId) {
tokenId = ++totalMinted;
_safeMint(msg.sender, tokenId); // exists, unrevealed
uint256 requestId = s_coordinator.requestRandomWords(keyHash, subId, confirmations, CALLBACK_GAS_LIMIT, 1);
tokenOfRequest[requestId] = tokenId;
}Independent traits from one word
solidity
uint256 roll = uint64(w) % 100; // bits 0..63 rarity
uint8 background = uint8((w >> 64) % 8); // bits 64..127
uint8 body = uint8((w >> 128) % 16); // bits 128..191
uint8 accessory = uint8((w >> 192) % 32); // bits 192..255Each trait reads its own 64-bit slice, so the traits are independent. (Computing rarity as w % 100 from the whole word would correlate it with the other traits. Our first draft of this example had exactly that bug, and the tests caught it.)
Rarity table: Common 60%, Rare 30%, Epic 9%, Legendary 1%. A test mints 400 tokens and checks the distribution.
PulseChain
Use OpenZeppelin v5.0.x for ERC-721 on PulseChain. Newer versions use the mcopy opcode, which PulseChain does not support. See PulseChain notes.