AmazingAng / WTF-Solidity

我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用,每周更新1-3讲。Now supports English! 官网: https://wtf.academy
https://wtf.academy
Other
10.75k stars 1.89k forks source link

(39. 链上随机数) 适配于Chainlink VRF v2.5的Random.sol #738

Open REBOA opened 1 month ago

REBOA commented 1 month ago
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import "https://github.com/AmazingAng/WTFSolidity/blob/main/34_ERC721/ERC721.sol";
import "https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
import "https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";

contract Random is ERC721, VRFConsumerBaseV2Plus {
    // NFT related variables
    uint256 public totalSupply = 100; // Total supply
    uint256[100] public ids; // Array for available token IDs
    uint256 public mintCount; // Number of minted tokens

    // Chainlink VRF parameters
    address vrfCoordinator = 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B;
    bytes32 keyHash = 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae;
    uint16 requestConfirmations = 3;
    uint32 callbackGasLimit = 1_000_000;
    uint32 numWords = 1;
    uint256 public requestId;
    uint256 public subId;

    // Mapping to keep track of request IDs and mint addresses
    mapping(uint256 => address) public requestToSender;

    constructor(uint256 _subId) 
        VRFConsumerBaseV2Plus(vrfCoordinator)
        ERC721("WTF Random", "WTF")
    {
        subId = _subId;
    }

    function pickRandomUniqueId(uint256 random) private returns (uint256 tokenId) {
        uint256 len = totalSupply - mintCount++;
        require(len > 0, "All tokens have been minted");
        uint256 randomIndex = random % len;

        tokenId = ids[randomIndex] != 0 ? ids[randomIndex] : randomIndex;
        ids[randomIndex] = ids[len - 1] == 0 ? len - 1 : ids[len - 1];
        ids[len - 1] = 0;
    }

    function getRandomOnchain() public view returns (uint256) {
        bytes32 randomBytes = keccak256(abi.encodePacked(blockhash(block.number - 1), msg.sender, block.timestamp));
        return uint256(randomBytes);
    }

    function mintRandomOnchain() public {
        uint256 tokenId = pickRandomUniqueId(getRandomOnchain());
        _mint(msg.sender, tokenId);
    }

    function mintRandomVRF() public {
        requestId = s_vrfCoordinator.requestRandomWords(
            VRFV2PlusClient.RandomWordsRequest({
                keyHash: keyHash,
                subId: subId,
                requestConfirmations: requestConfirmations,
                callbackGasLimit: callbackGasLimit,
                numWords: numWords,
                extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({nativePayment: false})
                )
            })
        );
        requestToSender[requestId] = msg.sender;
    }

    function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override {
        address sender = requestToSender[requestId];
        uint256 tokenId = pickRandomUniqueId(randomWords[0]);
        _mint(sender, tokenId);
    }
}

subId 由 uint64 更改为 uint256

新合约继承 VRFConsumerBaseV2Plus,s_vrfCoordinator 在 VRFConsumerBaseV2Plus.sol 初始化为 IVRFCoordinatorV2Plus 类型的实例

s_vrfCoordinator.requestRandomWords 调用了接口 IVRFCoordinatorV2Plus.sol 的 requestRandomWords 来获取 requestId,其具体实现在合约 VRFCoordinatorV2_5.sol

Chainlink 从 VRF v2 迁移 官方文档

XdpCs commented 5 days ago

下周我有空去看一下

XdpCs commented 5 days ago

526 这两个问题 类似可以合并处理 并关闭#526这个 @AmazingAng