簡體   English   中英

從已鑄造的 NFT 中隨機選擇令牌 ID 的正確方法

[英]Correct way of randomly selecting token Ids from already minted NFTs

我有一個包含1000 個 NFT的 NFT 項目。 作為我項目的一部分,持有 NFT 的用戶將參與抽獎。

我要實現的 function 如下:

Select x 參與抽獎的用戶數。 因此,假設從 1000 個 NFT 中鑄造了500個 NFT ,我希望500 個用戶參與抽獎,我可以選擇任意數量的中獎者。 簡而言之,500 名 NFT 持有者參與抽獎,隨機抽取 10 名 NFT 為獲勝者。

我很幸運地找到了符合我標准的智能合約。

在下面的代碼中, selectWinners()函數接受一個 uint256 noOfWinners 作為參數,然后為未來的抽獎創建一個日期,並將抽獎加 1。

selectAWinner() function 循環 x 次 (x = noOfWinners),並隨機選擇獲勝者。

rand() function 接受隨機地址和隨機數。 我相信這個 function 有助於找到隨機獲勝者。

在下面的代碼中, selectAWinner() function 生成一個介於 1 到 5000 之間的隨機令牌 id,因為該項目的 maxSupply 是 5000。

我想要的是讓 function 生成介於 1 到 500 之間的隨機令牌 id,因為從 1000 個中鑄造了 500 個 NFT。

我試過的:

rand() function 中,我嘗試使用totalSupply (NFT 鑄造的數量,即 500)而不是maxSupply 不幸的是,我遇到了錯誤。

我哪里錯了? 很感謝任何形式的幫助!

 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;
}

您可能可以通過使用模數 % 運算符來解決這個問題。

https://www.geeksforgeeks.org/solidity-operators/

生成一個隨機數,然后使用與現有 ERC721 NFT 的數量相對應的totalSupply()的模運算符。 就像是:

uint256 rand = _generateRandomness("rarity") % ERC721.totalSupply();

需要注意的是,如果不使用預言機,在 Solidity 中使用真正的隨機數是不可能的。 最著名和最值得信賴的是 ChainLink 的 RNG。

https://docs.chain.link/docs/get-a-random-number/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM