【从0学习Solidity】35. 荷兰拍卖

【从0学习Solidity】35. 荷兰拍卖

在这里插入图片描述

  • 博主简介:不写代码没饭吃,一名全栈领域的创作者,专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构,分享一些项目实战经验以及前沿技术的见解。
  • 关注我们的主页,探索全栈开发,期待与您一起在移动开发的世界中,不断进步和创造!
  • 本文收录于 不写代码没饭吃 的学习汇报系列,大家有兴趣的可以看一看。
  • 欢迎访问我们的微信公众号:不写代码没饭吃,获取更多精彩内容、实用技巧、行业资讯等。您关注的是我们前进的动力!

这一讲,我将介绍荷兰拍卖,并通过简化版Azuki荷兰拍卖代码,讲解如何通过荷兰拍卖发售ERC721标准的NFT

荷兰拍卖

荷兰拍卖(Dutch Auction)是一种特殊的拍卖形式。 亦称“减价拍卖”,它是指拍卖标的的竞价由高到低依次递减直到第一个竞买人应价(达到或超过底价)时击槌成交的一种拍卖。

35-1.png

在币圈,很多NFT通过荷兰拍卖发售,其中包括AzukiWorld of Women,其中Azuki通过荷兰拍卖筹集了超过8000ETH

项目方非常喜欢这种拍卖形式,主要有两个原因

  1. 荷兰拍卖的价格由最高慢慢下降,能让项目方获得最大的收入。

  2. 拍卖持续较长时间(通常6小时以上),可以避免gas war

DutchAuction合约

代码基于Azuki的代码简化而成。DucthAuction合约继承了之前介绍的ERC721Ownable合约:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/access/Ownable.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()
        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上限

        uint256 totalCost = getAuctionPrice() * quantity; // 计算mint成本
        require(msg.value >= totalCost, "Need to send more ETH."); // 检查用户是否支付足够ETH
        
        // Mint NFT
        for(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
    // 提款函数,onlyOwner
    function withdrawMoney() external onlyOwner {
        (bool success, ) = msg.sender.call{value: address(this).balance}(""); // call函数的调用方式详见第22讲
        require(success, "Transfer failed.");
    }

Remix演示

  1. 合约部署:首先,部署DutchAuction.sol合约,并通过setAuctionStartTime()函数设置拍卖起始时间。
    本例采用的起始时间为,2022年7月12日 1点30分,对应的utc时间为1658338200。实验时可以在工具网站(比如这里)自行查询对应时间。
    35-2.png

  2. 荷兰拍卖:随后,可以通过getAuctionPrice()函数获取到当前的拍卖价格。可以观察到,拍卖开始前的价格为起拍价 AUCTION_START_PRICE随着拍卖进行,拍卖价格在逐渐降低,直到降低至地板价 AUCTION_END_PRICE后不再变化。
    35-3.png

  3. Mint操作:通过auctionMin()函数,完成mint,可以看见本例中,由于时间已经超过拍卖时间,因此仅耗费了地板价就完成了拍卖。
    35-4.png

  4. 提取ETH:直接通过withdrawMoney()函数,便能将筹集到的ETH通过call()发送到合约创建者的地址。

总结

这一讲,我们介绍了荷兰拍卖,并通过简化版Azuki荷兰拍卖代码,讲解如何通过荷兰拍卖发售ERC721标准的NFT。我拍卖到的最贵的NFT是音乐家Jonathan Mann的一首音乐NFT,你呢?

在这里插入图片描述

如果这份博客对大家有帮助,希望各位给作者一个免费的点赞?作为鼓励,并评论收藏一下⭐,谢谢大家!!!
制作不易,如果大家有什么疑问或给作者的意见,欢迎评论区留言。文章来源地址https://www.uudwc.com/A/4rnYB/

原文地址:https://blog.csdn.net/weixin_46626339/article/details/132687895

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

h
上一篇 2023年09月29日 21:06
计算机类软件方向适合参加的比赛
下一篇 2023年09月29日 22:36