简体   繁体   English

从已铸造的 NFT 中随机选择令牌 ID 的正确方法

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM