WTF Solidity极简入门: 35. 荷兰拍卖
这一讲,我将介绍荷兰拍卖,并通过简化版Azuki
荷兰拍卖代码,讲解如何通过荷兰拍卖
发售ERC721
标准的NFT
。
荷兰拍卖
荷兰拍卖(Dutch Auction
)是一种特殊的拍卖形式。 亦称“减价拍卖”,它是指拍卖标的的竞价由高到低依次递减直到第一个竞买人应价(达到或超过底价)时击槌成交的一种拍卖。
在币圈,很多NFT
通过荷兰拍卖发售,其中包括Azuki
和World of Women
,其中Azuki
通过荷兰拍卖筹集了超过8000
枚ETH
。
项目方非常喜欢这种拍卖形式,主要有两个原因
荷兰拍卖的价格由最高慢慢下降,能让项目方获得最大的收入。
拍卖持续较长时间(通常6小时以上),可以避免
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 URIuint256[] private _allTokens; // 记录所有存在的tokenId
DutchAuction
函数
荷兰拍卖合约中共有9
个函数,与ERC721
相关的函数我们这里不再重复介绍,只介绍和拍卖相关的函数。
- 设定拍卖起始时间:我们在构造函数中会声明当前区块时间为起始时间,项目方也可以通过
setAuctionStartTime()
函数来调整:
constructor() ERC721("WTF Dutch Auctoin", "WTF Dutch Auctoin") {auctionStartTime = block.timestamp;}// auctionStartTime setter函数,onlyOwnerfunction setAuctionStartTime(uint32 timestamp) external onlyOwner {auctionStartTime = timestamp;}
- 获取拍卖实时价格:
getAuctionPrice()
函数通过当前区块时间以及拍卖相关的状态变量来计算实时拍卖价格。
当block.timestamp
小于起始时间,价格为最高价AUCTION_START_PRICE
;
当block.timestamp
大于结束时间,价格为最低价AUCTION_END_PRICE
;
当block.timestamp
处于两者之间时,则计算出当前的衰减价格。
// 获取拍卖实时价格function getAuctionPrice()publicviewreturns (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上限uint256 totalCost = getAuctionPrice() * quantity; // 计算mint成本require(msg.value >= totalCost, "Need to send more ETH."); // 检查用户是否支付足够ETH// Mint NFTfor(uint256 i = 0; i < quantity; i++) {uint256 mintIndex = totalSupply();_mint(msg.sender, mintIndex);_addTokenToAllTokensEnumeration(mintIndex);}// 多余ETH退款if (msg.value > totalCost) {payable(msg.sender).transfer(msg.value - totalCost); //注意一下这里是否有重入的风险}}
- 项目方取出筹集的
ETH
:项目方可以通过withdrawMoney()
函数提走拍卖筹集的ETH
。
// 提款函数,onlyOwnerfunction withdrawMoney() external onlyOwner {(bool success, ) = msg.sender.call{value: address(this).balance}(""); // call函数的调用方式详见第22讲require(success, "Transfer failed.");}
Remix演示
合约部署:首先,部署
DutchAuction.sol
合约,并通过setAuctionStartTime()
函数设置拍卖起始时间。 本例采用的起始时间为,2022年7月12日 1点30分,对应的utc时间为1658338200。实验时可以在工具网站(比如这里)自行查询对应时间。荷兰拍卖:随后,可以通过
getAuctionPrice()
函数获取到当前的拍卖价格。可以观察到,拍卖开始前的价格为起拍价 AUCTION_START_PRICE
随着拍卖进行,拍卖价格在逐渐降低,直到降低至地板价 AUCTION_END_PRICE
后不再变化。Mint操作:通过
auctionMin()
函数,完成mint,可以看见本例中,由于时间已经超过拍卖时间,因此仅耗费了地板价
就完成了拍卖。提取
ETH
:直接通过withdrawMoney()
函数,便能将筹集到的ETH
通过call()
发送到合约创建者的地址。
总结
这一讲,我们介绍了荷兰拍卖,并通过简化版Azuki
荷兰拍卖代码,讲解如何通过荷兰拍卖
发售ERC721
标准的NFT
。我拍卖到的最贵的NFT
是音乐家Jonathan Mann
的一首音乐NFT
,你呢?
WTF Solidity极简入门: 35. 荷兰拍卖相关推荐
- WTF Solidity极简入门: 39链上随机数
本文是按照以下教程编写的,在BNB测试网上运行失败,发现了ChainLink帮助文档中的参数错误,修正后才运行成功.TESTNET Binance (BNB) Blockchain Explorer ...
- Docker极简入门
原 Docker极简入门 2018年05月22日 20:25:12 阅读数:44 一.Docker概述 Docker通过一个包括应用程序运行时所需的一切的可执行镜像启动容器,包括配置有代码.运行时.库 ...
- 芋道 Apollo 极简入门
点击上方"芋道源码",选择"设为星标" 做积极的人,而不是积极废人! 源码精品专栏 原创 | Java 2020 超神之路,很肝~ 中文详细注释的开源项目 RP ...
- .Net Core in Docker极简入门(下篇)
点击上方蓝字"小黑在哪里"关注我吧 Docker-Compose 代码修改 yml file up & down 镜像仓库 前言 上一篇[.Net Core in Dock ...
- Nginx 极简入门教程
Nginx 极简入门教程 基本介绍 Nginx 是一个高性能的 HTTP 和反向代理 web 服务器,同时也提供了 IMAP/POP3/SMTP服务. Nginx 是由伊戈尔·赛索耶夫为俄罗斯访问量第 ...
- Python极简入门教程
前言 为了方便各位小白能轻松入门Python,同时加深自己对Python的理解,所以创造了"Python极简入门教程",希望能帮到大家,若有错误请多指正,谢谢.极简入门教程代表着不 ...
- SkyWalking 极简入门
1. 概述 1.1 概念 SkyWalking 是什么? FROM http://skywalking.apache.org/ 分布式系统的应用程序性能监视工具,专为微服务.云原生架构和基于容器(Do ...
- Seata 极简入门
1. 概述 Seata 是阿里开源的一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务. 1.1 四种事务模式 Seata 目标打造一站式的分布事务的解决方案,最终会提供四种事务 ...
- RL极简入门:从MDP、DP MC TC到Q函数、策略学习、PPO
前言 22年底/23年初ChatGPT大火,在写ChatGPT通俗笔记的过程中,发现ChatGPT背后技术涉及到了RL/RLHF,于是又深入研究RL,研究RL的过程中又发现里面的数学公式相比ML/DL ...
最新文章
- excel为什么只显示一行_十年如一日的工作,才总结出来的这些Excel技巧
- axure 下拉多选 元件_Axure教程:下拉多选列表集合(多选下拉列表+单选下拉列表+分级下拉列表)...
- 线性代数笔记:Frobenius 范数
- hdu-4045 Machine scheduling
- elk 搜索 语法_ELK学习笔记7| Lucence 的搜索语法
- Win10还原被Windows Defender隔离的文件
- Java 中Thread的sleep、join、yield方法解析
- Linux通过RPM方式指定软件安装目录
- 游戏卡牌半小时拍出8700万天价,法院紧急叫停!
- 2017-2018-1 20155204 《信息安全系统设计基础》第七周学习总结
- Xstream 学习地址
- box-sizing 的作用
- BizTalk Server 2010 - 使用 WCF Service [ 中篇 ]
- 2022年智能电梯行业研究报告
- 安装软件时“应用程序无法启动,因为应用程序的并行配置不正确......”
- wrk服务器性能测试
- 计算机试图启动宏,打开电子表格时,将提示禁用或启用宏的解决方案
- c语言中函数已有主体,error C2084 函数已有主体(Function already has a body)解决方案...
- Zabbix 3.0 监控Web [七]
- 离散数学 代数系统思维导图