Skip to content

Commit 18915f9

Browse files
committed
refactor: ethers v6 testing by sepolica
1 parent 438e1e3 commit 18915f9

15 files changed

+179
-83
lines changed

basic/15-nft-blindbox-chainlink-vrf/README-CN.md

+27-6
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,25 @@ VRF 为链上安全可验证随机数, 用于安全的生成随机数, 具体可
88
在 .env 中放入的私钥,格式为 "PRIVATE_KEY=xxxx", 然后代码自动从中读取
99

1010
- 获取 test Link
11-
每次去 ChainLink 请求 VRF 随机数时, 都需要消耗 Link 币, 所以在测试前需要申请 Link 测试币. 以 Goerli 测试网为例, 前往 [Request testnet LINK](https://faucets.chain.link/goerli?_ga=2.35440098.2104755910.1637393798-1377742816.1635817935) , 然后 "Netwrok" 选择 "Ethereum Goerli", "Testnet account address" 输入 .env 文件中 PRIVATE_KEY 对应的账户地址
12-
![](./images/chainlink.png)
13-
<center><img src="https://github.com/Dapp-Learning-DAO/Dapp-Learning-Arsenal/blob/main/images/basic/15-nft-blindbox-chainlink-vrf/chainlink.png?raw=true" /></center>
11+
每次去 ChainLink 请求 VRF 随机数时, 都需要消耗 Link 币, 所以在测试前需要申请 Link 测试币. 以 Sepolia 测试网为例, 前往 [Request testnet LINK](https://faucets.chain.link/sepolia) , 然后 "Netwrok" 选择 "Ethereum Sepolia", "Connect wallet" 链接小狐狸,点击"Send Request" 获取25 Test Link
12+
<center><img src="./imgs/Ethereum-Sepolia.png?raw=true" /></center>
13+
<center><img src="./imgs/GetLink.png?raw=true" /></center>
1414

1515
- 安装依赖
1616
```
1717
npm install
18+
19+
Node版本:v20.11.0
1820
```
1921

2022
- 创建 ChainLink SubscriptionID
2123
登陆 [ChainLink VRF 测试网](https://vrf.chain.link/?_ga=2.225785050.1950508783.1645630272-1230768383.1643005305) , 点击 "Create Subscription" 创建 SubscriptionID , 之后可以在 "My Subscriptions" 中看到创建的 SubscriptionID
22-
<center><img src="https://github.com/Dapp-Learning-DAO/Dapp-Learning-Arsenal/blob/main/images/basic/14-chainlink-price-feed/ChainLinkVRF.png?raw=true" /></center>
24+
<center><img src="./imgs/CreateSubscription.png?raw=true" /></center>
2325

2426

2527
- 保存 SubscriptionID
2628
将上一步创建的 SubscriptionID 保存到 .env 文件中
27-
<center><img src="https://github.com/Dapp-Learning-DAO/Dapp-Learning-Arsenal/blob/main/images/basic/14-chainlink-price-feed/SubscriptionID.png?raw=true" /></center>
29+
2830

2931
```sh
3032
## .env
@@ -40,20 +42,39 @@ PRIVATE_KEY=xxxxxxxxxxxxxxxx
4042
INFURA_ID=yyyyyyyy
4143
```
4244

45+
- 编译合约
46+
```
47+
npx hardhat compile
48+
49+
```
50+
51+
4352
- 部署测试合约
4453
```
4554
npx hardhat run scripts/deploy.js --network sepolia
4655
```
56+
对于不同测试网络(非sepolia),请参考[Chain Link: supported-networks](https://docs.chain.link/vrf/v2/subscription/supported-networks),在./contracts/RandomNumberVRF.sol 中修改vrfCoordinator,link,keyHash的值
57+
58+
- 添加Consumer
59+
```
60+
点击进入已经创建好的Subscription,点击“Add Consumer"将上一步骤 deploy 返回合约地址填入, 填入完毕后需要稍等 10s, 此时再刷新页面,可以看到出现配置的consumer地址
61+
62+
```
63+
<center><img src="./imgs/SubscriptionDetail.png?raw=true" /></center>
64+
<center><img src="./imgs/AddConsumer.png?raw=true" /></center>
4765

4866
- 获取随机数
4967
```
5068
npx hardhat run scripts/random-number-vrf.js --network sepolia
5169
```
5270

53-
- 生成随机 Character
71+
requestRandomWords请求发送后,ChainLink回调fulfillRandomWords生成随机数需要一定时间,防止Main 程序运行结束,因此需要设置循环检测是否生成了 RequestFulfilled 事件。
72+
73+
- 随机数获重新获取
5474
```
5575
npx hardhat run scripts/transaction.js --network sepolia
5676
```
77+
RandomWords 随机数生成之后,通过合约传入。./scripts/deployment.json 中保存的 requestID,再次获取之前获取的随机数。可以自定义随机数使用场景,本例只作为调用参考。
5778

5879
## 参考链接
5980
github 样例代码: https://github.com/PatrickAlphaC/dungeons-and-dragons-nft

basic/15-nft-blindbox-chainlink-vrf/README.md

+26-6
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,25 @@ This sample code demonstrates how to use ChainLink for NFT blind box design.
88
The private key put in **.env** in the format "PRIVATE_KEY= XXXX ", from which the code automatically reads.
99

1010
- Get Link test coins
11-
Every time you go to ChainLink to request VRF random number, you need to consume Link coins. Therefore, you need to apply for Link test coins before the test. For the Goerli test network, go to [Request testnet LINK](https://faucets.chain.link/goerli?_ga=2.35440098.2104755910.1637393798-1377742816.1635817935) , Then "Netwrok" selects "Ethereum Goerli" and "Testnet Account Address "enters the account address corresponding to PRIVATE_KEY in the **.env** file
12-
![](./images/chainlink.png)
13-
<center><img src="https://github.com/Dapp-Learning-DAO/Dapp-Learning-Arsenal/blob/main/images/basic/15-nft-blindbox-chainlink-vrf/chainlink.png?raw=true" /></center>
11+
Every time you go to ChainLink to request VRF random number, you need to consume Link coins. Therefore, you need to apply for Link test coins before the test. For the Goerli test network, go to [Request testnet LINK](https://faucets.chain.link/goerli?_ga=2.35440098.2104755910.1637393798-1377742816.1635817935) , Then "Netwrok" selects "Ethereum Sepolia" , click "Connect wallet" connect with MeatMask", click "Send Request" for geting 25 Test Link
12+
<center><img src="./imgs/Ethereum-Sepolia.png?raw=true" /></center>
13+
<center><img src="./imgs/GetLink.png?raw=true" /></center>
1414

1515
- Install Dependencies
1616
```
1717
npm install
18+
19+
Node Version:v20.11.0
1820
```
1921

2022
- Create ChainLink SubscriptionID
2123
Login [ChainLink VRF Test network](https://vrf.chain.link/?_ga=2.225785050.1950508783.1645630272-1230768383.1643005305) , Click on "Create Subscription" to Create a SubscriptionID and you can see the created SubscriptionID under "My Subscriptions"
22-
<center><img src="https://github.com/Dapp-Learning-DAO/Dapp-Learning-Arsenal/blob/main/images/basic/14-chainlink-price-feed/ChainLinkVRF.png?raw=true" /></center>
24+
<center><img src="./imgs/CreateSubscription.png?raw=true" /></center>
2325

2426

2527
- Save SubscriptionID
2628
Save the SubscriptionID created in the previous step to **.env**
27-
<center><img src="https://github.com/Dapp-Learning-DAO/Dapp-Learning-Arsenal/blob/main/images/basic/14-chainlink-price-feed/SubscriptionID.png?raw=true" /></center>
29+
<center><img src="./imgs/CreateSubscription.png?raw=true" /></center>
2830

2931
```sh
3032
## .env
@@ -40,20 +42,38 @@ PRIVATE_KEY=xxxxxxxxxxxxxxxx
4042
INFURA_ID=yyyyyyyy
4143
```
4244

45+
- Compile contract
46+
```
47+
npx hardhat compile
48+
```
49+
50+
4351
- Deployment test contract
4452
```
4553
npx hardhat run scripts/deploy.js --network sepolia
4654
```
55+
For different test networks (non-sepolia), please refer to [Chain Link: supported-networks](https://docs.chain.link/vrf/v2/subscription/supported-networks), in ./contracts/RandomNumberVRF.sol Modify the values ​​of vrfCoordinator, link, keyHash
56+
57+
- Add Consumer
58+
```
59+
Click to enter the already created Subscription, click "Add Consumer" to fill in the contract address returned by deploy in the previous step. After filling in, you need to wait for 10 seconds. At this time, refresh the page and you can see the configured consumer address.
60+
61+
```
62+
<center><img src="./imgs/SubscriptionDetail.png?raw=true" /></center>
63+
<center><img src="./imgs/AddConsumer.png?raw=true" /></center>
4764

4865
- Get random number
4966
```
5067
npx hardhat run scripts/random-number-vrf.js --network sepolia
5168
```
69+
After the requestRandomWords request is sent, it takes a certain amount of time for the ChainLink callback fulfillRandomWords to generate random numbers, which prevents the Main program from ending. Therefore, it is necessary to set up a loop to detect whether the RequestFulfilled event is generated.
5270

53-
- Generate random Character
71+
- Get randomword again
5472
```
5573
npx hardhat run scripts/transaction.js --network sepolia
5674
```
75+
RandomWords After the random number is generated, it is passed in through the contract. The requestID saved in ./scripts/deployment.json is used to obtain the random number obtained before. You can customize random number usage scenarios. This example is only used as a calling reference.
76+
5777

5878
## Refer to the link
5979
Github sample code: https://github.com/PatrickAlphaC/dungeons-and-dragons-nft

basic/15-nft-blindbox-chainlink-vrf/contracts/RandomNumberVRF.sol

+60-28
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,46 @@
22
// An example of a consumer contract that relies on a subscription for funding.
33
pragma solidity >=0.8.0;
44

5-
import '@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol';
5+
66
import '@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol';
77
import '@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol';
8+
import '@chainlink/contracts/src/v0.8/ConfirmedOwner.sol';
9+
10+
contract RandomNumberVRF is VRFConsumerBaseV2, ConfirmedOwner {
11+
event RequestSent(uint256 requestId, uint32 numWords);
12+
event RequestFulfilled(uint256 requestId, uint256[] randomWords);
13+
14+
struct RequestStatus {
15+
bool fulfilled; // whether the request has been successfully fulfilled
16+
bool exists; // whether a requestId exists
17+
uint256[] randomWords;
18+
}
19+
mapping(uint256 => RequestStatus)
20+
public s_requests; /* requestId --> requestStatus */
821

9-
contract RandomNumberVRF is VRFConsumerBaseV2 {
10-
event FulfillRandomness(uint256,uint256[]);
11-
event RequestId(address,uint256);
1222

1323
VRFCoordinatorV2Interface COORDINATOR;
14-
LinkTokenInterface LINKTOKEN;
24+
1525

1626
// Your subscription ID.
1727
uint64 s_subscriptionId;
1828

19-
// Goerli coordinator. For other networks,
29+
// past requests Id.
30+
uint256[] public requestIds;
31+
uint256 public lastRequestId;
32+
33+
// sepolia coordinator. For other networks,
2034
// see https://docs.chain.link/docs/vrf-contracts/#configurations
21-
address vrfCoordinator = 0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D;
35+
address vrfCoordinator = 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625;
2236

23-
// Goerli LINK token contract. For other networks,
37+
// sepolia LINK token contract. For other networks,
2438
// see https://docs.chain.link/docs/vrf-contracts/#configurations
25-
address link = 0x326C977E6efc84E512bB9C30f76E30c160eD06FB;
39+
address link = 0x779877A7B0D9E8603169DdbD7836e478b4624789;
2640

2741
// The gas lane to use, which specifies the maximum gas price to bump to.
2842
// For a list of available gas lanes on each network,
2943
// see https://docs.chain.link/docs/vrf-contracts/#configurations
30-
bytes32 keyHash = 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15;
44+
bytes32 keyHash = 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c;
3145

3246
// Depends on the number of requested values that you want sent to the
3347
// fulfillRandomWords() function. Storing each word costs about 20,000 gas,
@@ -44,34 +58,52 @@ contract RandomNumberVRF is VRFConsumerBaseV2 {
4458
// Cannot exceed VRFCoordinatorV2.MAX_NUM_WORDS.
4559
uint32 numWords = 2;
4660

47-
uint256[] public s_randomWords;
48-
uint256 public s_requestId;
49-
address s_owner;
50-
51-
constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator) {
61+
constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator)
62+
ConfirmedOwner(msg.sender)
63+
{
5264
COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
53-
LINKTOKEN = LinkTokenInterface(link);
54-
s_owner = msg.sender;
65+
5566
s_subscriptionId = subscriptionId;
5667
}
5768

5869
// Assumes the subscription is funded sufficiently.
59-
function requestRandomWords() external onlyOwner {
70+
function requestRandomWords() external
71+
onlyOwner
72+
returns (uint256 requestId)
73+
{
6074
// Will revert if subscription is not set and funded.
61-
s_requestId = COORDINATOR.requestRandomWords(keyHash, s_subscriptionId, requestConfirmations, callbackGasLimit, numWords);
62-
emit RequestId(msg.sender,s_requestId);
75+
requestId = COORDINATOR.requestRandomWords(
76+
keyHash,
77+
s_subscriptionId,
78+
requestConfirmations,
79+
callbackGasLimit,
80+
numWords);
81+
s_requests[requestId] = RequestStatus({
82+
randomWords: new uint256[](0),
83+
exists: true,
84+
fulfilled: false
85+
});
86+
requestIds.push(requestId);
87+
lastRequestId = requestId;
88+
emit RequestSent(requestId, numWords);
89+
return requestId;
6390
}
6491

6592
function fulfillRandomWords(
66-
uint256, /* requestId */
67-
uint256[] memory randomWords
93+
uint256 _requestId, /* requestId */
94+
uint256[] memory _randomWords
6895
) internal override {
69-
s_randomWords = randomWords;
70-
emit FulfillRandomness(s_requestId,s_randomWords);
71-
}
96+
require(s_requests[_requestId].exists, "request not found");
97+
s_requests[_requestId].fulfilled = true;
98+
s_requests[_requestId].randomWords = _randomWords;
99+
emit RequestFulfilled(_requestId, _randomWords);
100+
}
72101

73-
modifier onlyOwner() {
74-
require(msg.sender == s_owner);
75-
_;
102+
function getRequestStatus(
103+
uint256 _requestId
104+
) external view returns (bool fulfilled, uint256[] memory randomWords) {
105+
require(s_requests[_requestId].exists, "request not found");
106+
RequestStatus memory request = s_requests[_requestId];
107+
return (request.fulfilled, request.randomWords);
76108
}
77109
}

basic/15-nft-blindbox-chainlink-vrf/hardhat.config.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
require("@nomicfoundation/hardhat-toolbox");
22
require('dotenv').config();
3+
require("@nomicfoundation/hardhat-verify");
34

45
/** @type import('hardhat/config').HardhatUserConfig */
6+
7+
task("accounts", "Prints the list of accounts", async () => {
8+
const accounts = await ethers.getSigners();
9+
10+
for (const account of accounts) {
11+
console.log(account.address);
12+
}
13+
});
514
function mnemonic() {
615

716
return process.env.PRIVATE_KEY
@@ -36,6 +45,7 @@ module.exports = {
3645
sepolia: {
3746
url: 'https://sepolia.infura.io/v3/' + process.env.INFURA_ID, //<---- CONFIG YOUR INFURA ID IN .ENV! (or it won't work)
3847
accounts: [mnemonic()],
48+
chainId: 11155111,
3949
},
4050
mainnet: {
4151
url: "https://mainnet.infura.io/v3/" + process.env.INFURA_ID, //<---- YOUR INFURA ID! (or it won't work)
@@ -45,6 +55,6 @@ module.exports = {
4555
},
4656
},
4757
etherscan: {
48-
apiKey: "1324"
58+
apiKey: process.env.APIKEY
4959
}
5060
};
Loading
Loading
Loading
Loading
Loading
Loading
Loading

basic/15-nft-blindbox-chainlink-vrf/package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
"author": "",
1111
"license": "ISC",
1212
"dependencies": {
13-
"@chainlink/contracts": "^0.4.0",
13+
"@chainlink/contracts": "^0.4.2",
14+
"@nomiclabs/hardhat-etherscan": "^3.1.8",
1415
"@openzeppelin/contracts": "^4.5.0",
1516
"dotenv": "^16.4.5",
16-
"hardhat": "^2.22.2"
17+
"hardhat": "^2.22.2",
18+
"hardhat-contract-sizer": "^2.10.0"
1719
},
1820
"devDependencies": {
1921
"@nomicfoundation/hardhat-toolbox": "^5.0.0"

basic/15-nft-blindbox-chainlink-vrf/scripts/deploy.js

+7-5
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@ const { saveDeployment } = require('./utils');
55

66
async function main() {
77
// We get the contract to deploy
8-
const Dnd = await ethers.getContractFactory("DungeonsAndDragonsCharacter");
8+
const RnvContractFactory = await ethers.getContractFactory("RandomNumberVRF");
99

10-
const dnd = await Dnd.deploy(process.env.SubscriptionId, "http://81.69.8.95/WaterMarginJson/");
10+
const rnv = await RnvContractFactory.deploy(process.env.SubscriptionId);
11+
12+
console.log("deploying....");
1113

12-
await dnd.waitForDeployment()
14+
await rnv.waitForDeployment()
1315

14-
console.log("dnd deployed to:", dnd.target);
16+
console.log("rnv deployed to:", rnv.target);
1517

1618
// save contract address to file
1719
saveDeployment({
18-
dndAddress: dnd.target,
20+
rnvAddress: rnv.target,
1921
});
2022

2123
}

0 commit comments

Comments
 (0)