-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Expand file tree
/
Copy pathRandom.sol
More file actions
115 lines (102 loc) · 5.15 KB
/
Copy pathRandom.sol
File metadata and controls
115 lines (102 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import "https://github.com/AmazingAng/WTF-Solidity/blob/main/34_ERC721/ERC721.sol";
import {VRFConsumerBaseV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
import {VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
contract Random is ERC721, VRFConsumerBaseV2Plus{
// NFT相关
uint256 public totalSupply = 100; // 总供给
uint256[100] public ids; // 用于计算可供mint的tokenId
uint256 public mintCount; // 已mint数量
// chainlink VRF参数
/**
* 使用chainlink VRF,构造函数需要继承 VRFConsumerBaseV2Plus
* 不同链参数填的不一样
* 网络: Sepolia测试网
* Chainlink VRF Coordinator 地址: 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B
* LINK 代币地址: 0x779877a7b0d9e8603169ddbd7836e478b4624789
* 30 gwei Key Hash: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae
* Minimum Confirmations 最小确认块数 : 3 (数字大安全性高,一般填12)
* callbackGasLimit gas限制 : 最大 2,500,000
* Maximum Random Values 一次可以得到的随机数个数 : 最大 500
*/
address vrfCoordinator = 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B;
bytes32 keyHash = 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae;
uint16 requestConfirmations = 3;
uint32 callbackGasLimit = 1_000_000;
uint32 numWords = 1;
//订阅ID类型已从VRF V2中的uint64变为VRF V2.5中的uint256
uint256 subId;
uint256 public requestId;
// 记录VRF申请标识对应的mint地址
mapping(uint256 => address) public requestToSender;
constructor(uint256 s_subId)
VRFConsumerBaseV2Plus(vrfCoordinator)
ERC721("WTF Random", "WTF"){
subId = s_subId;
}
/**
* 输入uint256数字,返回一个可以mint的tokenId
*/
function pickRandomUniqueId(uint256 random) private returns (uint256 tokenId) {
//先计算减法,再计算++, 关注(a++,++a)区别
uint256 len = totalSupply - mintCount++; // 可mint数量
require(len > 0, "mint close"); // 所有tokenId被mint完了
uint256 randomIndex = random % len; // 获取链上随机数
//随机数取模,得到tokenId,作为数组下标,同时记录value为len-1,如果取模得到的值已存在,则tokenId取该数组下标的value
tokenId = ids[randomIndex] != 0 ? ids[randomIndex] : randomIndex; // 获取tokenId
ids[randomIndex] = ids[len - 1] == 0 ? len - 1 : ids[len - 1]; // 更新ids 列表
ids[len - 1] = 0; // 删除最后一个元素,能返还gas
}
/**
* 链上伪随机数生成
* keccak256(abi.encodePacked()中填上一些链上的全局变量/自定义变量
* 返回时转换成uint256类型
*/
function getRandomOnchain() public view returns(uint256){
/*
* 本例链上随机只依赖区块哈希,调用者地址,和区块时间,
* 想提高随机性可以再增加一些属性比如nonce等,但是不能根本上解决安全问题
*/
bytes32 randomBytes = keccak256(abi.encodePacked(blockhash(block.number-1), msg.sender, block.timestamp));
return uint256(randomBytes);
}
// 利用链上伪随机数铸造NFT
function mintRandomOnchain() public {
uint256 _tokenId = pickRandomUniqueId(getRandomOnchain()); // 利用链上随机数生成tokenId
_mint(msg.sender, _tokenId);
}
/**
* 调用VRF获取随机数,并mintNFT
* 要调用requestRandomness()函数获取,消耗随机数的逻辑写在VRF的回调函数fulfillRandomness()中
* 调用前,需要在Subscriptions中fund足够的Link
*/
function mintRandomVRF() public {
// 调用requestRandomness获取随机数
requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest(
{
keyHash:keyHash,
subId:subId,
requestConfirmations: requestConfirmations,
callbackGasLimit: callbackGasLimit,
numWords: numWords,
extraArgs: VRFV2PlusClient._argsToBytes(
//此为是否指定原生代币如ETH等,来支付VRF请求的费用,当为false表示使用LINK代币支付
VRFV2PlusClient.ExtraArgsV1({nativePayment: false})
)
}
)
);
requestToSender[requestId] = msg.sender;
}
/**
* VRF的回调函数,由VRF Coordinator调用
* 消耗随机数的逻辑写在本函数中
*/
function fulfillRandomWords(uint256 _requestId, uint256[] calldata s_randomWords) internal override{
address sender = requestToSender[_requestId]; // 从requestToSender中获取minter用户地址
uint256 tokenId = pickRandomUniqueId(s_randomWords[0]); // 利用VRF返回的随机数生成tokenId
_mint(sender, tokenId);
}
}