Solidity&Foundry 荷兰拍卖

荷兰式拍卖:

solidityproject/DutchAuction at master · XuHugo/solidityproject · GitHub

荷兰式拍卖,也称为公开降序拍卖,是一种拍卖类型,卖方首先设置起始价格、持续时间和折扣率。随着时间的推移,物品的价格会不断下降,直到预设的持续时间结束。例如,假设您想要一个无聊猿,但超出了您的预算。在那种情况下,随着时间的推移,这个无聊猿会变得越来越便宜;首先是 10% 的折扣,然后是 30%,然后是 50%,直到无聊猿便宜到足以让您购买为止。这就是荷兰式拍卖的概念。

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

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

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

代码分析

nft:要拍卖NFT的地址;seller:被拍卖nft的所有者;id:被拍卖nft的id;

AUCTIONTIME:拍卖持续时间;

startPrice:nft的起始价格;endPrice:nft最低价格,结束价格;

dropInterval:多长时间降低一次价格; dropStep:每次降低多少价格;

auctionStartTime:拍卖的起始时间;

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

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/interfaces/IERC721.sol";

contract DutchAuction is Ownable {
    IERC721 public immutable nft;
    address public immutable seller;
    uint256 private constant AUCTIONTIME = 10 minutes; // acution time

    uint256 public immutable startPrice; // max price
    uint256 public immutable endPrice; // min price

    uint256 public immutable dropInterval; // how long will change price
    uint256 public immutable dropStep; //

    uint256 public auctionStartTime; //
    uint256 public id; //NFT id

    // constructor
    constructor(
        uint256 _startPrice,
        uint256 _endPrice,
        uint256 _dropInterval,
        address _nft,
        uint256 _id,
        address _seller
    ) Ownable(msg.sender) {
        require(
            _startPrice > _endPrice,
            "start price must bigger than end price!"
        );
        startPrice = _startPrice;
        endPrice = _endPrice;
        nft = IERC721(_nft);
        auctionStartTime = block.timestamp;
        id = _id;
        seller = _seller;
        dropInterval = _dropInterval;
        dropStep = (_startPrice - _endPrice) / (AUCTIONTIME / _dropInterval);
    }

   

初始化函数,dropStep是需要计算出来的,其实就是用起始价格差除以总共的时间段数;



    // acution fun
    function auction() external payable {
        uint256 _saleStartTime = uint256(auctionStartTime); // new local varible, save gas
        require(
            _saleStartTime != 0 && block.timestamp >= _saleStartTime,
            "sale has not started yet"
        ); // check auction time, if staring

        uint256 price = getPrice(); // calc price
        require(msg.value >= price, "Need to send more ETH.."); // check balances

        // fransfer NFT
        //nft._mint(msg.sender, id);
        nft.safeTransferFrom(seller, msg.sender, id);

        //  refund eth
        if (msg.value > price) {
            payable(msg.sender).transfer(msg.value - price); //catch reentry
        }
        payable(seller).transfer(price);
    }


拍卖函数,获取当前的价格,判断拍卖人是否有足够的余额支付nft;如果有,则转移nft;然后返回拍卖人剩余的钱,以及给nft原来的owner发送拍卖所得;

    // get current price
    function getPrice() public view returns (uint256) {
        if (block.timestamp < auctionStartTime) {
            return startPrice;
        } else if (block.timestamp - auctionStartTime >= AUCTIONTIME) {
            return endPrice;
        } else {
            uint256 steps = (block.timestamp - auctionStartTime) / dropInterval;
            return startPrice - (steps * dropStep);
        }
    }

获取当前的nft价格,如果还没有开始拍卖,就返回最大值;如果结束了,就返回地板价;如果处于拍卖中,就计算一下当前的价格;公式也比较简单,就不做介绍了;

foundry测试

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.21;

import "forge-std/Test.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {DutchAuction} from "../src/DutchAuction.sol";

contract DutchAuctionTest is Test {
    DutchAuction public dauction;
    NFT public nft;
    address sellr = vm.addr(1);
    address bob = vm.addr(2);
    uint256 blockTime;

    function setUp() public {
        nft = new NFT();
        dauction = new DutchAuction(
            100 ether,
            10 ether,
            1 minutes,
            address(nft),
            1,
            sellr
        );
        //check blocktime
        blockTime = dauction.auctionStartTime();
        console.log("blockTime:", blockTime);
        //set 1 of nft of owner
        nft.mint(sellr, 1);
        vm.prank(sellr);
        nft.approve(address(dauction), 1);
    }

    function test_getPrice() public {
        //set blocktime
        vm.warp(121);
        uint256 price = dauction.getPrice();
        assertEq(price, 82 ether);
    }

    function test_auction() public {
        vm.deal(bob, 100 ether);
        //set blocktime
        vm.warp(121);
        vm.prank(bob);
        dauction.auction{value: 90 ether}();
        assertEq(sellr.balance, 82 ether);
        assertEq(nft.ownerOf(1), address(bob));
    }
}

contract NFT is ERC721 {
    constructor() ERC721("DA", "DA") {}

    function mint(address to, uint256 tokenId) public {
        _mint(to, tokenId);
    }
}

NFT

这里需要自己提前写一个NFT的合约,我们直接使用openzeppelin来完成;

setUp

测试的时候,前置工作;分别创建一个NFT和DutchAuction合约;同时给sellr mint一个di为1的NFT;然后使用approve给dauction赋予id为1的NFT的转移权限,这样方便拍卖成功的时候,dauction帮忙转移NFT;

test_getPrice

测试获取价格,这里需要注意的就是,改变区块时间;

test_auction

拍卖,注意给拍卖人——bob足够的钱,以及修改区块时间;

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0xweb3q

有钱的捧个钱场,没钱的捧个人场

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值