Skip to content

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

@REBOA

Description

@REBOA
// 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 迁移 官方文档

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions