我最近在重新学solidity,巩固一下细节,也写一个“Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。
欢迎关注我的推特:@0xAA_Science
欢迎加入WTF科学家社区,内有加微信群方法:链接
所有代码和教程开源在github(1024个star发课程认证,2048个star发社群NFT): github.com/AmazingAng/WTFSolidity
这一讲,我将介绍荷兰拍卖,并通过简化版Azuki
荷兰拍卖代码,讲解如何通过荷兰拍卖
发售ERC721
标准的NFT
。
荷兰拍卖(Dutch Auction
)是一种特殊的拍卖形式。 亦称“减价拍卖”,它是指拍卖标的的竞价由高到低依次递减直到第一个竞买人应价(达到或超过底价)时击槌成交的一种拍卖。
在币圈,很多NFT
通过荷兰拍卖发售,其中包括Azuki
和World of Women
,其中Azuki
通过荷兰拍卖筹集了超过8000
枚ETH
。
项目方非常喜欢这种拍卖形式,主要有两个原因
gas war
。DutchAuction
合约代码基于Azuki
的代码简化而成。DucthAuction
合约继承了之前介绍的ERC721
和Ownable
合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/access/Ownable.sol";
import "https://github.com/AmazingAng/WTFSolidity/blob/main/34_ERC721/ERC721.sol";
contract DutchAuction is Ownable, ERC721 {
DutchAuction
状态变量合约中一共有9
个状态变量,其中有6
个和拍卖相关,他们是:
COLLECTOIN_SIZE
:NFT总量AUCTION_START_PRICE
:荷兰拍卖起拍价,也是最高价。AUCTION_END_PRICE
:荷兰拍卖结束价,也是地板价。AUCTION_TIME
:拍卖持续时长。AUCTION_DROP_INTERVAL
:每过多久时间,价格衰减一次。auctionStartTime
:拍卖起始时间(区块链时间戳,block.timestamp
)。 uint256 public constant COLLECTOIN_SIZE = 10000; // NFT总数
uint256 public constant AUCTION_START_PRICE = 1 ether; // 起拍价
uint256 public constant AUCTION_END_PRICE = 0.1 ether; // 结束价(最低价)
uint256 public constant AUCTION_TIME = 10 minutes; // 拍卖时间,为了测试方便设为10分钟
uint256 public constant AUCTION_DROP_INTERVAL = 1 minutes; // 每过多久时间,价格衰减一次
uint256 public constant AUCTION_DROP_PER_STEP =
(AUCTION_START_PRICE - AUCTION_END_PRICE) /
(AUCTION_TIME / AUCTION_DROP_INTERVAL); // 每次价格衰减步长
uint256 public auctionStartTime; // 拍卖开始时间戳
string private _baseTokenURI; // metadata URI
uint256[] private _allTokens; // 记录所有存在的tokenId
DutchAuction
函数荷兰拍卖合约中共有9
个函数,与ERC721
相关的函数我们这里不再重复介绍,只介绍和拍卖相关的。
setAuctionStartTime()
函数来调整: constructor() ERC721("WTF Dutch Auctoin", "WTF Dutch Auctoin") {
auctionStartTime = block.timestamp;
}
// auctionStartTime setter函数,onlyOwner
function setAuctionStartTime(uint32 timestamp) external onlyOwner {
auctionStartTime = timestamp;
}
getAuctionPrice()
函数通过当前区块时间以及拍卖相关的状态变量来计算实时拍卖价格。当block.timestamp
小于起始时间,价格为最高价AUCTION_START_PRICE
;当block.timestamp
大于结束时间,价格为最低价AUCTION_END_PRICE
;当block.timestamp
处于两者之间时,则计算出当前的衰减价格。 // 获取拍卖实时价格
function getAuctionPrice(uint256 _auctionStartTime)
public
view
returns (uint256)
{
if (block.timestamp < _auctionStartTime) {
return AUCTION_START_PRICE;
}else if (block.timestamp - _auctionStartTime >= AUCTION_TIME) {
return AUCTION_END_PRICE;
} else {
uint256 steps = (block.timestamp - _auctionStartTime) /
AUCTION_DROP_INTERVAL;
return AUCTION_START_PRICE - (steps * AUCTION_DROP_PER_STEP);
}
}
NFT
:用户通过调用auctionMint()
函数,支付ETH
参加荷兰拍卖并铸造NFT
。该函数首先检查拍卖是否开始/铸造是否超出NFT
总量。接着,合约通过getAuctionPrice()
和铸造数量计算拍卖成本,并检查用户支付的ETH
是否足够:如果足够,则将NFT
铸造给用户,并退回超额的ETH
;反之,则回退交易。 // 拍卖mint函数
function auctionMint(uint256 quantity) external payable{
uint256 _saleStartTime = uint256(auctionStartTime); // 建立local变量,减少gas花费
require(
_saleStartTime != 0 && block.timestamp >= _saleStartTime,
"sale has not started yet"
); // 检查拍卖是否开始
require(
totalSupply() + quantity <= COLLECTOIN_SIZE,
"not enough remaining reserved for auction to support desired mint amount"
); // 检查是否超过NFT上限
// Mint NFT
for(uint i = 0; i < quantity; i++) {
uint mintIndex = totalSupply();
_mint(msg.sender, mintIndex);
_addTokenToAllTokensEnumeration(mintIndex);
}
uint256 totalCost = getAuctionPrice(auctionStartTime) * quantity; // 计算mint成本
require(msg.value >= totalCost, "Need to send more ETH."); // 检查用户是否支付足够ETH
// 多余ETH退款
if (msg.value > totalCost) {
payable(msg.sender).transfer(msg.value - totalCost);
}
}
ETH
:项目方可以通过withdrawMoney()
函数提走拍卖筹集的ETH
。 // 提款函数,onlyOwner
function withdrawMoney() external onlyOwner {
(bool success, ) = msg.sender.call{value: address(this).balance}("");
require(success, "Transfer failed.");
}
这一讲,我们介绍了荷兰拍卖,并通过简化版Azuki
荷兰拍卖代码,讲解如何通过荷兰拍卖
发售ERC721
标准的NFT
。我拍卖到的最贵的NFT
是音乐家Jonathan Mann
的一首音乐NFT
,你呢?