[英]Correct way of randomly selecting token Ids from already minted NFTs
I have an NFT project with 1000 NFTs .我有一个包含1000 个 NFT的 NFT 项目。 As part of my project, users who hold an NFT will be entered into a lottery.作为我项目的一部分,持有 NFT 的用户将参与抽奖。
The function I want to implement is the following:我要实现的 function 如下:
Select x number of users to participate in the lottery. Select x 参与抽奖的用户数。 So lets say 500 NFTs have been minted out of the 1000 NFTs , I want the 500 users to participate in the lottery, and I get to pick any number of winners.因此,假设从 1000 个 NFT 中铸造了500个 NFT ,我希望500 个用户参与抽奖,我可以选择任意数量的中奖者。 In short, 500 NFT holders are entered into a draw, and 10 NFTs are randomly selected as winners.简而言之,500 名 NFT 持有者参与抽奖,随机抽取 10 名 NFT 为获胜者。
I was fortunate enough to find a smart contract that match my criteria.我很幸运地找到了符合我标准的智能合约。
In the below code, the selectWinners()
functions accepts a uint256 noOfWinners as a parameter, which then creates a date for a future draw, and increments a draw by 1.在下面的代码中, selectWinners()
函数接受一个 uint256 noOfWinners 作为参数,然后为未来的抽奖创建一个日期,并将抽奖加 1。
The selectAWinner()
function is looped x number of times (x = noOfWinners), and selects random winners. selectAWinner()
function 循环 x 次 (x = noOfWinners),并随机选择获胜者。
The rand()
function accepts random address and a random number. rand()
function 接受随机地址和随机数。 I believe this function helps in finding random winners.我相信这个 function 有助于找到随机获胜者。
In the below code, the selectAWinner()
function generates a random token id between 1 to 5000, because the maxSupply of that project is 5000.在下面的代码中, selectAWinner()
function 生成一个介于 1 到 5000 之间的随机令牌 id,因为该项目的 maxSupply 是 5000。
What I want is for the function to generate random token id's between 1 to 500, because 500 NFTs have been minting out of the 1000.我想要的是让 function 生成介于 1 到 500 之间的随机令牌 id,因为从 1000 个中铸造了 500 个 NFT。
What I've tried:我试过的:
In the rand()
function, I tried to use totalSupply
(the amount of NFT minted ie 500) instead of maxSupply
.在rand()
function 中,我尝试使用totalSupply
(NFT 铸造的数量,即 500)而不是maxSupply
。 Unfortunately, I'm encountering errors.不幸的是,我遇到了错误。
Where am I going wrong?我哪里错了? Any help is very much appreciated!很感谢任何形式的帮助!
function selectWinners(uint256 noOfWinners) public onlyOwner {
require(!paused, "ERROR: Contract is paused");
require(lotteryActive, "ERROR: Lottery not active yet");
require(noOfWinners <= 50, "ERROR: Too many winners selected");
uint256 epochNow = block.timestamp;
uint256 nextLotteryDate = lotteryDates[lotteryDates.length - 1];
require(epochNow >= nextLotteryDate, "ERROR: Cannot draw yet, too early");
for (uint256 i = 0; i < noOfWinners; i++) {
selectAWinner(
0,
epochNow,
msg.sender,
nextLotteryDate,
msg.sender,
0
);
}
lotteryDates.push(epochNow + (epochDay * lotteryIntervalDays));
// increment draw
drawNumber++;
}
function selectAWinner(
uint256 it,
uint256 epochNow,
address sender,
uint256 lotteryDate,
address randomAddr,
uint256 randomNo
) internal {
// Generate random id between 1 - 5000 (corresponds to NFT id)
uint256 winningToken = rand(randomAddr, randomNo);
address winnerAddress = ERC721.ownerOf(winningToken);
uint256 lastWon = _winners[winnerAddress];
bool alreadyWon = (lastWon == lotteryDate);
Winner memory win;
if ((it < 5) && alreadyWon) {
uint256 newIt = it + 1;
return
selectAWinner(
newIt,
epochNow,
sender,
lotteryDate,
winnerAddress,
winningToken
);
} else if ((it >= 5) && alreadyWon) {
return;
} else {
win.date = lotteryDate;
win.winner = winnerAddress;
win.tokenId = winningToken;
winnerLog[drawNumber].push(win);
_winners[winnerAddress] = lotteryDate;
}
return;
}
function rand(address randomAddress, uint256 randomNo) internal view returns (uint256) {
uint256 seed = uint256(
keccak256(
abi.encodePacked(
(block.timestamp - randomNo) +
block.difficulty +
((
uint256(keccak256(abi.encodePacked(block.coinbase)))
) / (block.timestamp)) +
block.gaslimit +
((uint256(keccak256(abi.encodePacked(randomAddress)))) /
(block.timestamp)) +
block.number
)
)
);
return (seed - ((seed / maxSupply) * maxSupply)) + 1;
}
You could probably solve that by using the modulus % operator.您可能可以通过使用模数 % 运算符来解决这个问题。
https://www.geeksforgeeks.org/solidity-operators/ https://www.geeksforgeeks.org/solidity-operators/
Generate a random number and then use the modulus operator with the totalSupply()
which corresponds to the number of existing ERC721 NFT.生成一个随机数,然后使用与现有 ERC721 NFT 的数量相对应的totalSupply()
的模运算符。 something like:就像是:
uint256 rand = _generateRandomness("rarity") % ERC721.totalSupply();
A little remark would be that using truly random numbers is impossible in Solidity without using oracles.需要注意的是,如果不使用预言机,在 Solidity 中使用真正的随机数是不可能的。 The most famous and trustworthy is ChainLink's RNG.最著名和最值得信赖的是 ChainLink 的 RNG。
https://docs.chain.link/docs/get-a-random-number/ https://docs.chain.link/docs/get-a-random-number/
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.