Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
AmazingAng committed Jun 2, 2023
2 parents e560edb + fbd4ff8 commit 26896ad
Show file tree
Hide file tree
Showing 15 changed files with 330 additions and 264 deletions.
44 changes: 16 additions & 28 deletions 05_DataStorage/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,38 +45,26 @@ solidity数据存储位置有三类:`storage`,`memory`和`calldata`。不同
### 数据位置和赋值规则
在不同存储类型相互赋值时候,有时会产生独立的副本(修改新变量不会影响原变量),有时会产生引用(修改新变量会影响原变量)。规则如下:

1. `storage`(合约的状态变量)赋值给本地`storage`(函数里的)时候,会创建引用,改变新变量会影响原变量。例子:
```solidity
uint[] public x = [1,2,3]; // 状态变量:数组 x
- 赋值本质上是创建**引用**指向本体,因此修改本体或者是引用,变化可以被同步:

- `storage`(合约的状态变量)赋值给本地`storage`(函数里的)时候,会创建引用,改变新变量会影响原变量。例子:
```solidity
uint[] x = [1,2,3]; // 状态变量:数组 x

function fStorage() public{
//声明一个storage的变量 xStorage,指向x。修改xStorage也会影响x
uint[] storage xStorage = x;
xStorage[0] = 100;
}
```
**Example:**
![5-2.png](./img/5-2.png)

function fStorage() public{
//声明一个storage的变量 xStorage,指向x。修改xStorage也会影响x
uint[] storage xStorage = x;
xStorage[0] = 100;
}
```
**Example:**
![5-2.png](./img/5-2.png)

2. `storage`赋值给`memory`,会创建独立的副本,修改其中一个不会影响另一个;反之亦然。例子:
```solidity
uint[] public x = [1,2,3]; // 状态变量:数组 x
function fMemory() public view{
//声明一个Memory的变量xMemory,复制x。修改xMemory不会影响x
uint[] memory xMemory = x;
xMemory[0] = 100;
xMemory[1] = 200;
uint[] memory xMemory2 = x;
xMemory2[0] = 300;
}
```
**Example:**
![5-3.png](./img/5-3.png)

3. `memory`赋值给`memory`,会创建引用,改变新变量会影响原变量。
- `memory`赋值给`memory`,会创建引用,改变新变量会影响原变量。

4. 其他情况,变量赋值给`storage`,会创建独立的副本,修改其中一个不会影响另一个。
- 其他情况下,赋值创建的是本体的副本,即对二者之一的修改,并不会同步到另一方

## 变量的作用域
`Solidity`中变量按作用域划分有三种,分别是状态变量(state variable),局部变量(local variable)和全局变量(global variable)
Expand Down
208 changes: 109 additions & 99 deletions 39_Random/Random.sol
Original file line number Diff line number Diff line change
@@ -1,99 +1,109 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/**
* 从github和npm导入
* 导入文件存放于当前工作区的.deps目录下
*/
import "../34_ERC721/ERC721.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";

contract RandomNumber is ERC721, VRFConsumerBase{
// NFT参数
uint256 public totalSupply = 100; // 总供给
uint256[100] public ids; // 用于计算可以mint的tokenId
uint256 public mintCount; // 已mint数量, 默认值为0
// chainlink VRF参数
bytes32 internal keyHash;
uint256 internal fee;

// 记录VRF申请标识对应的mint地址
mapping(bytes32 => address) public requestToSender;
/**
* 使用chainlink VRF,构造函数需要继承 VRFConsumerBase
* 不同链参数填的不一样
* 网络: Rinkeby测试网
* Chainlink VRF Coordinator 地址: 0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B
* LINK 代币地址: 0x01BE23585060835E02B77ef475b0Cc51aA1e0709
* Key Hash: 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311
*/
constructor()
VRFConsumerBase(
0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B, // VRF Coordinator
0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK Token
)
ERC721("WTF Random", "WTF")
{
keyHash = 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311;
fee = 0.1 * 10 ** 18; // 0.1 LINK (VRF使用费,Rinkeby测试网)
}

/**
* 输入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()中
* 调用前,把LINK代币转到本合约里
*/
function mintRandomVRF() public returns (bytes32 requestId) {
// 检查合约中LINK余额
require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet");
// 调用requestRandomness获取随机数
requestId = requestRandomness(keyHash, fee);
requestToSender[requestId] = msg.sender;
return requestId;
}

/**
* VRF的回调函数,由VRF Coordinator调用
* 消耗随机数的逻辑写在本函数中
*/
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
address sender = requestToSender[requestId]; // 从requestToSender中获取minter用户地址
uint256 _tokenId = pickRandomUniqueId(randomness); // 利用VRF返回的随机数生成tokenId
_mint(sender, _tokenId);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "https://github.com/AmazingAng/WTFSolidity/blob/main/34_ERC721/ERC721.sol";
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";

contract Random is ERC721, VRFConsumerBaseV2{
// NFT相关
uint256 public totalSupply = 100; // 总供给
uint256[100] public ids; // 用于计算可供mint的tokenId
uint256 public mintCount; // 已mint数量

// chainlink VRF参数

//VRFCoordinatorV2Interface
VRFCoordinatorV2Interface COORDINATOR;

/**
* 使用chainlink VRF,构造函数需要继承 VRFConsumerBaseV2
* 不同链参数填的不一样
* 网络: Sepolia测试网
* Chainlink VRF Coordinator 地址: 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625
* LINK 代币地址: 0x01BE23585060835E02B77ef475b0Cc51aA1e0709
* 30 gwei Key Hash: 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c
* Minimum Confirmations 最小确认块数 : 3 (数字大安全性高,一般填12)
* callbackGasLimit gas限制 : 最大 2,500,000
* Maximum Random Values 一次可以得到的随机数个数 : 最大 500
*/
address vrfCoordinator = 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625;
bytes32 keyHash = 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c;
uint16 requestConfirmations = 3;
uint32 callbackGasLimit = 1_000_000;
uint32 numWords = 1;
uint64 subId;
uint256 public requestId;

// 记录VRF申请标识对应的mint地址
mapping(uint256 => address) public requestToSender;

constructor(uint64 s_subId)
VRFConsumerBaseV2(vrfCoordinator)
ERC721("WTF Random", "WTF"){
COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
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 = COORDINATOR.requestRandomWords(
keyHash,
subId,
requestConfirmations,
callbackGasLimit,
numWords
);
requestToSender[requestId] = msg.sender;
}

/**
* VRF的回调函数,由VRF Coordinator调用
* 消耗随机数的逻辑写在本函数中
*/
function fulfillRandomWords(uint256 requestId, uint256[] memory s_randomWords) internal override{
address sender = requestToSender[requestId]; // 从requestToSender中获取minter用户地址
uint256 tokenId = pickRandomUniqueId(s_randomWords[0]); // 利用VRF返回的随机数生成tokenId
_mint(sender, tokenId);
}
}
114 changes: 63 additions & 51 deletions 39_Random/RandomNumberConsumer.sol
Original file line number Diff line number Diff line change
@@ -1,51 +1,63 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";

/**
* 申请测试网的 LINK 和 ETH 的水龙头: https://faucets.chain.link/
*/

contract RandomNumberConsumer is VRFConsumerBase {

bytes32 internal keyHash; // VRF唯一标识符
uint256 internal fee; // VRF使用手续费

uint256 public randomResult; // 存储随机数

/**
* 使用chainlink VRF,构造函数需要继承 VRFConsumerBase
* 不同链参数填的不一样
* 网络: Rinkeby测试网
* Chainlink VRF Coordinator 地址: 0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B
* LINK 代币地址: 0x01BE23585060835E02B77ef475b0Cc51aA1e0709
* Key Hash: 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311
*/
constructor()
VRFConsumerBase(
0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B, // VRF Coordinator
0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK Token
)
{
keyHash = 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311;
fee = 0.1 * 10 ** 18; // 0.1 LINK (VRF使用费,Rinkeby测试网)
}

/**
* 向VRF合约申请随机数
*/
function getRandomNumber() public returns (bytes32 requestId) {
// 合约中需要有足够的LINK
require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet");
return requestRandomness(keyHash, fee);
}

/**
* VRF合约的回调函数,验证随机数有效之后会自动被调用
* 消耗随机数的逻辑写在这里
*/
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
randomResult = randomness;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";

contract RandomNumberConsumer is VRFConsumerBaseV2{

//请求随机数需要调用VRFCoordinatorV2Interface接口
VRFCoordinatorV2Interface COORDINATOR;

// 申请后的subId
uint64 subId;

//存放得到的 requestId 和 随机数
uint256 public requestId;
uint256[] public randomWords;

/**
* 使用chainlink VRF,构造函数需要继承 VRFConsumerBaseV2
* 不同链参数填的不一样
* 具体可以看:https://docs.chain.link/vrf/v2/subscription/supported-networks
* 网络: Sepolia测试网
* Chainlink VRF Coordinator 地址: 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625
* LINK 代币地址: 0x01BE23585060835E02B77ef475b0Cc51aA1e0709
* 30 gwei Key Hash: 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c
* Minimum Confirmations 最小确认块数 : 3 (数字大安全性高,一般填12)
* callbackGasLimit gas限制 : 最大 2,500,000
* Maximum Random Values 一次可以得到的随机数个数 : 最大 500
*/
address vrfCoordinator = 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625;
bytes32 keyHash = 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c;
uint16 requestConfirmations = 3;
uint32 callbackGasLimit = 200_000;
uint32 numWords = 3;

constructor(uint64 s_subId) VRFConsumerBaseV2(vrfCoordinator){
COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
subId = s_subId;
}

/**
* 向VRF合约申请随机数
*/
function requestRandomWords() external {
requestId = COORDINATOR.requestRandomWords(
keyHash,
subId,
requestConfirmations,
callbackGasLimit,
numWords
);
}

/**
* VRF合约的回调函数,验证随机数有效之后会自动被调用
* 消耗随机数的逻辑写在这里
*/
function fulfillRandomWords(uint256 requestId, uint256[] memory s_randomWords) internal override {
randomWords = s_randomWords;
}

}
Binary file added 39_Random/img/39-10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified 39_Random/img/39-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified 39_Random/img/39-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified 39_Random/img/39-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed 39_Random/img/39-5-1.png
Binary file not shown.
Binary file modified 39_Random/img/39-5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added 39_Random/img/39-6-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified 39_Random/img/39-6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified 39_Random/img/39-7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added 39_Random/img/39-8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added 39_Random/img/39-9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 26896ad

Please sign in to comment.