Skip to content

Commit

Permalink
Merge pull request #489 from yeecai/main
Browse files Browse the repository at this point in the history
feat: udpate en/38-39
  • Loading branch information
AmazingAng committed May 1, 2023
2 parents 8096658 + 0a2d712 commit efa3b8c
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 173 deletions.
133 changes: 78 additions & 55 deletions Languages/en/38_NFTSwap_en/NFTSwap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,98 +5,121 @@ import "../34_ERC721/IERC721.sol";
import "../34_ERC721/IERC721Receiver.sol";
import "../34_ERC721/WTFApe.sol";

contract NFTSwap is IERC721Receiver{
event List(address indexed seller, address indexed nftAddr, uint256 indexed tokenId, uint256 price);
event Purchase(address indexed buyer, address indexed nftAddr, uint256 indexed tokenId, uint256 price);
event Revoke(address indexed seller, address indexed nftAddr, uint256 indexed tokenId);
event Update(address indexed seller, address indexed nftAddr, uint256 indexed tokenId, uint256 newPrice);

// 定义order结构体
struct Order{
contract NFTSwap is IERC721Receiver {
event List(
address indexed seller,
address indexed nftAddr,
uint256 indexed tokenId,
uint256 price
);
event Purchase(
address indexed buyer,
address indexed nftAddr,
uint256 indexed tokenId,
uint256 price
);
event Revoke(
address indexed seller,
address indexed nftAddr,
uint256 indexed tokenId
);
event Update(
address indexed seller,
address indexed nftAddr,
uint256 indexed tokenId,
uint256 newPrice
);

// define the order structure
struct Order {
address owner;
uint256 price;
uint256 price;
}
// NFT Order映射
// NFT Order mapping
mapping(address => mapping(uint256 => Order)) public nftList;

fallback() external payable{}
fallback() external payable {}

// 挂单: 卖家上架NFT,合约地址为_nftAddr,tokenId为_tokenId,价格_price为以太坊(单位是wei)
function list(address _nftAddr, uint256 _tokenId, uint256 _price) public{
IERC721 _nft = IERC721(_nftAddr); // 声明IERC721接口合约变量
require(_nft.getApproved(_tokenId) == address(this), "Need Approval"); // 合约得到授权
require(_price > 0); // 价格大于0
// Pending order: The seller puts NFT on the shelf, the contract address is _nftAddr, the tokenId is _tokenId, and the price _price is Ethereum (the unit is wei)
function list(address _nftAddr, uint256 _tokenId, uint256 _price) public {
IERC721 _nft = IERC721(_nftAddr); // Declare IERC721 interface contract variables
require(_nft.getApproved(_tokenId) == address(this), "Need Approval"); // The contract is authorized
require(_price > 0); // price is greater than 0

Order storage _order = nftList[_nftAddr][_tokenId]; //设置NF持有人和价格
Order storage _order = nftList[_nftAddr][_tokenId]; //Set NF holder and price
_order.owner = msg.sender;
_order.price = _price;
// 将NFT转账到合约
// Transfer NFT to contract
_nft.safeTransferFrom(msg.sender, address(this), _tokenId);

// 释放List事件
// Release the List event
emit List(msg.sender, _nftAddr, _tokenId, _price);
}

// 购买: 买家购买NFT,合约为_nftAddr,tokenId为_tokenId,调用函数时要附带ETH
function purchase(address _nftAddr, uint256 _tokenId) payable public {
Order storage _order = nftList[_nftAddr][_tokenId]; // 取得Order
require(_order.price > 0, "Invalid Price"); // NFT价格大于0
require(msg.value >= _order.price, "Increase price"); // 购买价格大于标价
// 声明IERC721接口合约变量
// Purchase: The buyer purchases NFT, the contract is _nftAddr, the tokenId is _tokenId, and ETH is required when calling the function
function purchase(address _nftAddr, uint256 _tokenId) public payable {
Order storage _order = nftList[_nftAddr][_tokenId]; // get Order
require(_order.price > 0, "Invalid Price"); // NFT price is greater than 0
require(msg.value >= _order.price, "Increase price"); // The purchase price is greater than the list price
// Declare IERC721 interface contract variables
IERC721 _nft = IERC721(_nftAddr);
require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT在合约中
require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT is in the contract

// 将NFT转给买家
// Transfer NFT to buyer
_nft.safeTransferFrom(address(this), msg.sender, _tokenId);
// 将ETH转给卖家,多余ETH给买家退款
// Transfer ETH to the seller, and refund the excess ETH to the buyer
payable(_order.owner).transfer(_order.price);
payable(msg.sender).transfer(msg.value-_order.price);
payable(msg.sender).transfer(msg.value - _order.price);

delete nftList[_nftAddr][_tokenId]; // 删除order
delete nftList[_nftAddr][_tokenId]; // delete order

// 释放Purchase事件
// Release the Purchase event
emit Purchase(msg.sender, _nftAddr, _tokenId, msg.value);
}

// 撤单: 卖家取消挂单
// Cancellation: The seller cancels the pending order
function revoke(address _nftAddr, uint256 _tokenId) public {
Order storage _order = nftList[_nftAddr][_tokenId]; // 取得Order
require(_order.owner == msg.sender, "Not Owner"); // 必须由持有人发起
// 声明IERC721接口合约变量
Order storage _order = nftList[_nftAddr][_tokenId]; // get Order
require(_order.owner == msg.sender, "Not Owner"); // must be initiated by the owner
// Declare IERC721 interface contract variables
IERC721 _nft = IERC721(_nftAddr);
require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT在合约中
// 将NFT转给卖家
require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT is in the contract

// Transfer NFT to seller
_nft.safeTransferFrom(address(this), msg.sender, _tokenId);
delete nftList[_nftAddr][_tokenId]; // 删除order
// 释放Revoke事件
delete nftList[_nftAddr][_tokenId]; // delete order

// Release the Revoke event
emit Revoke(msg.sender, _nftAddr, _tokenId);
}

// 调整价格: 卖家调整挂单价格
function update(address _nftAddr, uint256 _tokenId, uint256 _newPrice) public {
require(_newPrice > 0, "Invalid Price"); // NFT价格大于0
Order storage _order = nftList[_nftAddr][_tokenId]; // 取得Order
require(_order.owner == msg.sender, "Not Owner"); // 必须由持有人发起
// 声明IERC721接口合约变量
// Adjust price: The seller adjusts the pending order price
function update(
address _nftAddr,
uint256 _tokenId,
uint256 _newPrice
) public {
require(_newPrice > 0, "Invalid Price"); // NFT price is greater than 0
Order storage _order = nftList[_nftAddr][_tokenId]; // get Order
require(_order.owner == msg.sender, "Not Owner"); // must be initiated by the owner
// Declare IERC721 interface contract variables
IERC721 _nft = IERC721(_nftAddr);
require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT在合约中
// 调整NFT价格
require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT is in the contract

// Adjust NFT price
_order.price = _newPrice;
// 释放Update事件

// Release the Update event
emit Update(msg.sender, _nftAddr, _tokenId, _newPrice);
}
// 实现{IERC721Receiver}的onERC721Received,能够接收ERC721代币

// Implement onERC721Received of {IERC721Receiver}, able to receive ERC721 tokens
function onERC721Received(
address operator,
address from,
uint tokenId,
bytes calldata data
) external override returns (bytes4){
) external override returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}
}
4 changes: 2 additions & 2 deletions Languages/en/38_NFTSwap_en/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ The contract includes four events corresponding to the actions of listing (list)
An `NFT` order is abstracted as the `Order` structure, which contains information about the listing price (`price`) and the owner (`owner`). The `nftList` mapping records the `NFT` series (contract address) and `tokenId` information that the order corresponds to.

```solidity
// 定义order结构体
// Define the order structure
struct Order{
address owner;
uint256 price;
}
// NFT Order映射
// NFT Order mapping
mapping(address => mapping(uint256 => Order)) public nftList;
```

Expand Down
123 changes: 69 additions & 54 deletions Languages/en/39_Random_en/Random.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,98 +2,113 @@
pragma solidity ^0.8.4;

/**
* 从github和npm导入
* 导入文件存放于当前工作区的.deps目录下
*/
* import from github and npm
* Import files are stored in the .deps directory of the current workspace
*/
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参数
contract RandomNumber is ERC721, VRFConsumerBase {
// NFT parameters
uint256 public totalSupply = 100; // total supply
uint256[100] public ids; // used to calculate tokenId that can be mint
uint256 public mintCount; // the number of mint, the default value is 0
// chainlink VRF parameters
bytes32 internal keyHash;
uint256 internal fee;

// 记录VRF申请标识对应的mint地址
// Record the mint address corresponding to the VRF application ID
mapping(bytes32 => address) public requestToSender;

/**
* 使用chainlink VRF,构造函数需要继承 VRFConsumerBase
* 不同链参数填的不一样
* 网络: Rinkeby测试网
* Chainlink VRF Coordinator 地址: 0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B
* LINK 代币地址: 0x01BE23585060835E02B77ef475b0Cc51aA1e0709
* To use chainlink VRF, the constructor needs to inherit VRFConsumerBase
* The parameters of different chains are filled differently
* Network: Rinkeby test network
* Chainlink VRF Coordinator address: 0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B
* LINK token address: 0x01BE23585060835E02B77ef475b0Cc51aA1e0709
* Key Hash: 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311
*/
constructor()
constructor()
VRFConsumerBase(
0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B, // VRF Coordinator
0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK Token
0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK Token
)
ERC721("WTF Random", "WTF")
{
keyHash = 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311;
fee = 0.1 * 10 ** 18; // 0.1 LINK (VRF使用费,Rinkeby测试网)
fee = 0.1 * 10 ** 18; // 0.1 LINK (VRF usage fee, Rinkeby test network)
}

/**
* 输入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
/**
* Input a uint256 number and return a tokenId that can be mint
*/
function pickRandomUniqueId(
uint256 random
) private returns (uint256 tokenId) {
// Calculate the subtraction first, then calculate ++, pay attention to the difference between (a++, ++a)
uint256 len = totalSupply - mintCount++; // mint quantity
require(len > 0, "mint close"); // all tokenIds are mint finished
uint256 randomIndex = random % len; // get the random number on the chain

// Take the modulus of the random number to get the tokenId as an array subscript, and record the value as len-1 at the same time. If the value obtained by taking the modulus already exists, then tokenId takes the value of the array subscript
tokenId = ids[randomIndex] != 0 ? ids[randomIndex] : randomIndex; // get tokenId
ids[randomIndex] = ids[len - 1] == 0 ? len - 1 : ids[len - 1]; // update ids list
ids[len - 1] = 0; // delete the last element, can return gas
}

/**
* 链上伪随机数生成
* keccak256(abi.encodePacked()中填上一些链上的全局变量/自定义变量
* 返回时转换成uint256类型
*/
function getRandomOnchain() public view returns(uint256){
/**
* On-chain pseudo-random number generation
* keccak256(abi.encodePacked() fill in some global variables/custom variables on the chain
* Convert to uint256 type when returning
*/
function getRandomOnchain() public view returns (uint256) {
/*
* 本例链上随机只依赖区块哈希,调用者地址,和区块时间,
* 想提高随机性可以再增加一些属性比如nonce等,但是不能根本上解决安全问题
* In this case, randomness on the chain only depends on block hash, caller address, and block time,
* If you want to improve the randomness, you can add some attributes such as nonce, etc., but it cannot fundamentally solve the security problem
*/
bytes32 randomBytes = keccak256(abi.encodePacked(blockhash(block.number-1), msg.sender, block.timestamp));
bytes32 randomBytes = keccak256(
abi.encodePacked(
blockhash(block.number - 1),
msg.sender,
block.timestamp
)
);
return uint256(randomBytes);
}

// 利用链上伪随机数铸造NFT
// Use the pseudo-random number on the chain to cast NFT
function mintRandomOnchain() public {
uint256 _tokenId = pickRandomUniqueId(getRandomOnchain()); // 利用链上随机数生成tokenId
uint256 _tokenId = pickRandomUniqueId(getRandomOnchain()); // Use the random number on the chain to generate tokenId
_mint(msg.sender, _tokenId);
}

/**
* 调用VRF获取随机数,并mintNFT
* 要调用requestRandomness()函数获取,消耗随机数的逻辑写在VRF的回调函数fulfillRandomness()中
* 调用前,把LINK代币转到本合约里
/**
* Call VRF to get random number and mintNFT
* To call the requestRandomness() function to obtain, the logic of consuming random numbers is written in the VRF callback function fulfillRandomness()
* Before calling, transfer LINK tokens to this contract
*/
function mintRandomVRF() public returns (bytes32 requestId) {
// 检查合约中LINK余额
require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet");
// 调用requestRandomness获取随机数
// Check the LINK balance in the contract
require(
LINK.balanceOf(address(this)) >= fee,
"Not enough LINK - fill contract with faucet"
);
// Call requestRandomness to get a random number
requestId = requestRandomness(keyHash, fee);
requestToSender[requestId] = msg.sender;
return requestId;
}

/**
* VRF的回调函数,由VRF Coordinator调用
* 消耗随机数的逻辑写在本函数中
* VRF callback function, called by VRF Coordinator
* The logic of consuming random numbers is written in this function
*/
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
address sender = requestToSender[requestId]; // 从requestToSender中获取minter用户地址
uint256 _tokenId = pickRandomUniqueId(randomness); // 利用VRF返回的随机数生成tokenId
function fulfillRandomness(
bytes32 requestId,
uint256 randomness
) internal override {
address sender = requestToSender[requestId]; // Get minter user address from requestToSender
uint256 _tokenId = pickRandomUniqueId(randomness); // Use the random number returned by VRF to generate tokenId
_mint(sender, _tokenId);
}
}
Loading

0 comments on commit efa3b8c

Please sign in to comment.