I want to deploy an NFT that at mint time, selects at random one of an enum of several metadata choices. Obviously I can't do this on a static mint site because you could exploit the contract by passing your own integer via a script directly to the contract. I could make an IPFS folder of pre-generated metadata that is named incrementally as in: 1.json, 2.json, 3.json, etc... I could make the metadata contents a random assortment of a few metadata choices. If I had 3 files 1.json, 2.json, and 3.json, I could make the metadata: 1.json 2.json 2.json 1.json 3.json 2.json 3.json 1.json 1.json 2.json 3.json 2.json 1.json 1.json 1.json 1.json 3.json 2.json 3.json 2.json 2.json 2.json 1.json, and so on... But this isn't ideal because an attacker could just view the metadata files, decide which one the attacker likes and wait for that integer to be next in line for minting. Chainlink VRF is probably the only thing that can actually generate a true random value at mint time that is somewhat hard to attack. But I can't find a recent enough tutorial on how to mint an NFT this way. Is there any tutorial that shows how to mint an NFT that selects the metadata at random at run time? Whether it uses Chainlink or something else?
1 Answer
Yes, you are correct that Chainlink VRF
is a good solution to generate a random number
at mint time
for your NFT, and it can help to prevent any potential exploitation of the contract.
Here's example code I got from somewhere, worked for me
pragma solidity ^0.8.0;
import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
contract MyNFT is VRFConsumerBase {
uint256 public tokenIdCounter;
enum MetadataChoices { Choice1, Choice2, Choice3 }
bytes32 internal keyHash;
uint256 internal fee;
uint256 public randomResult;
mapping(bytes32 => address) public requestIdToSender;
mapping(bytes32 => uint256) public requestIdToTokenId;
mapping(uint256 => string) public tokenIdToMetadata;
event requestedRandomness(bytes32 requestId);
constructor(address vrfCoordinator, address link, bytes32 _keyHash, uint256 _fee)
VRFConsumerBase(vrfCoordinator, link) {
keyHash = _keyHash;
fee = _fee;
metadataChoices.push(MetadataChoices.Choice1);
metadataChoices.push(MetadataChoices.Choice2);
metadataChoices.push(MetadataChoices.Choice3);
}
function requestRandomNumber(uint256 tokenId) external returns (bytes32) {
require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK to fulfill request");
bytes32 requestId = requestRandomness(keyHash, fee);
requestIdToSender[requestId] = msg.sender;
requestIdToTokenId[requestId] = tokenId;
emit requestedRandomness(requestId);
return requestId;
}
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
address sender = requestIdToSender[requestId];
uint256 tokenId = requestIdToTokenId[requestId];
uint256 index = randomness % metadataChoices.length;
tokenIdToMetadata[tokenId] = _metadataForChoice(metadataChoices[index]);
randomResult = randomness;
_safeMint(sender, tokenId);
}
}
-
Awesome thank you for the answer! I will spin this up on a testnet to double check that the code works. Commented Apr 5, 2023 at 6:25
-
1
-
I'm currently getting this error imgur.com/a/DhqegWC. Was
metadataChoices
supposed to be declared earlier in the contract? Commented Apr 7, 2023 at 23:35