<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>polyma</title>
        <link>https://paragraph.com/@26212</link>
        <description>web2 to web3 build</description>
        <lastBuildDate>Sun, 10 May 2026 21:02:41 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>polyma</title>
            <url>https://storage.googleapis.com/papyrus_images/e68e1b729ddefa78701a01080f8377f155f47896fda431d3a8af76a3390a25d8.jpg</url>
            <link>https://paragraph.com/@26212</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Solidity极简入门:  荷兰拍卖]]></title>
            <link>https://paragraph.com/@26212/solidity</link>
            <guid>VvMiv3Xz1qfSMZ6rKwA9</guid>
            <pubDate>Fri, 05 Aug 2022 13:19:10 GMT</pubDate>
            <description><![CDATA[我最近在重新学solidity，巩固一下细节，也写一个“Solidity极简入门”，供小白们使用（编程大佬可以另找教程）， 所有代码和教程开源在github（1024个star发课程认证，2048个star发社群NFT）: github.com/AmazingAng/WTFSolidity这一讲，我将介绍荷兰拍卖，并通过简化版Azuki荷兰拍卖代码，讲解如何通过荷兰拍卖发售ERC721标准的NFT。荷兰拍卖荷兰拍卖（Dutch Auction）是一种特殊的拍卖形式。 亦称“减价拍卖”，它是指拍卖标的的竞价由高到低依次递减直到第一个竞买人应价（达到或超过底价）时击槌成交的一种拍卖。 荷兰拍卖 在币圈，很多NFT通过荷兰拍卖发售，其中包括Azuki和World of Women，其中Azuki通过荷兰拍卖筹集了超过8000枚ETH。 项目方非常喜欢这种拍卖形式，主要有两个原因荷兰拍卖的价格由最高慢慢下降，能让项目方获得最大的收入。拍卖持续较长时间（通常6小时以上），可以避免gas war。DutchAuction合约代码基于Azuki的代码简化而成。DucthAuction合约继承了...]]></description>
            <content:encoded><![CDATA[<p>我最近在重新学solidity，巩固一下细节，也写一个“Solidity极简入门”，供小白们使用（编程大佬可以另找教程），</p><p>所有代码和教程开源在github（1024个star发课程认证，2048个star发社群NFT）: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/AmazingAng/WTFSolidity">github.com/AmazingAng/WTFSolidity</a></p><hr><p>这一讲，我将介绍荷兰拍卖，并通过简化版<code>Azuki</code>荷兰拍卖代码，讲解如何通过<code>荷兰拍卖</code>发售<code>ERC721</code>标准的<code>NFT</code>。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">荷兰拍卖</h2><p>荷兰拍卖（<code>Dutch Auction</code>）是一种特殊的拍卖形式。 亦称“减价拍卖”，它是指拍卖标的的竞价由高到低依次递减直到第一个竞买人应价（达到或超过底价）时击槌成交的一种拍卖。</p><p>荷兰拍卖</p><p>在币圈，很多<code>NFT</code>通过荷兰拍卖发售，其中包括<code>Azuki</code>和<code>World of Women</code>，其中<code>Azuki</code>通过荷兰拍卖筹集了超过<code>8000</code>枚<code>ETH</code>。</p><p>项目方非常喜欢这种拍卖形式，主要有两个原因</p><ol><li><p>荷兰拍卖的价格由最高慢慢下降，能让项目方获得最大的收入。</p></li><li><p>拍卖持续较长时间（通常6小时以上），可以避免<code>gas war</code>。</p></li></ol><h2 id="h-dutchauction" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">DutchAuction合约</h2><p>代码基于<code>Azuki</code>的<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/address/0xed5af388653567af2f388e6224dc7c4b3241c544#code">代码</a>简化而成。<code>DucthAuction</code>合约继承了之前介绍的<code>ERC721</code>和<code>Ownable</code>合约：</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import &quot;@openzeppelin/contracts/access/Ownable.sol&quot;;
import &quot;https://github.com/AmazingAng/WTFSolidity/blob/main/34_ERC721/ERC721.sol&quot;;

contract DutchAuction is Ownable, ERC721 {
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.4;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/access/Ownable.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"https://github.com/AmazingAng/WTFSolidity/blob/main/34_ERC721/ERC721.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">DutchAuction</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Ownable</span>, <span class="hljs-title">ERC721</span> </span>{
</code></pre><h3 id="h-dutchauction" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">DutchAuction状态变量</h3><p>合约中一共有<code>9</code>个状态变量，其中有<code>6</code>个和拍卖相关，他们是：</p><ul><li><p><code>COLLECTOIN_SIZE</code>：NFT总量</p></li><li><p><code>AUCTION_START_PRICE</code>：荷兰拍卖起拍价，也是最高价。</p></li><li><p><code>AUCTION_END_PRICE</code>：荷兰拍卖结束价，也是地板价。</p></li><li><p><code>AUCTION_TIME</code>：拍卖持续时长。</p></li><li><p><code>AUCTION_DROP_INTERVAL</code>：每过多久时间，价格衰减一次。</p></li><li><p><code>auctionStartTime</code>：拍卖起始时间（区块链时间戳，<code>block.timestamp</code>）。</p></li></ul><pre data-type="codeBlock" text="    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 
"><code>    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> COLLECTOIN_SIZE <span class="hljs-operator">=</span> <span class="hljs-number">10000</span>; <span class="hljs-comment">// NFT总数</span>
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> AUCTION_START_PRICE <span class="hljs-operator">=</span> <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>; <span class="hljs-comment">// 起拍价</span>
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> AUCTION_END_PRICE <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.1</span> <span class="hljs-literal">ether</span>; <span class="hljs-comment">// 结束价（最低价）</span>
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> AUCTION_TIME <span class="hljs-operator">=</span> <span class="hljs-number">10</span> <span class="hljs-literal">minutes</span>; <span class="hljs-comment">// 拍卖时间，为了测试方便设为10分钟</span>
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> AUCTION_DROP_INTERVAL <span class="hljs-operator">=</span> <span class="hljs-number">1</span> <span class="hljs-literal">minutes</span>; <span class="hljs-comment">// 每过多久时间，价格衰减一次</span>
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> AUCTION_DROP_PER_STEP <span class="hljs-operator">=</span>
        (AUCTION_START_PRICE <span class="hljs-operator">-</span> AUCTION_END_PRICE) <span class="hljs-operator">/</span>
        (AUCTION_TIME <span class="hljs-operator">/</span> AUCTION_DROP_INTERVAL); <span class="hljs-comment">// 每次价格衰减步长</span>
    
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> auctionStartTime; <span class="hljs-comment">// 拍卖开始时间戳</span>
    <span class="hljs-keyword">string</span> <span class="hljs-keyword">private</span> _baseTokenURI;   <span class="hljs-comment">// metadata URI</span>
    <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">private</span> _allTokens; <span class="hljs-comment">// 记录所有存在的tokenId </span>
</code></pre><h3 id="h-dutchauction" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">DutchAuction函数</h3><p>荷兰拍卖合约中共有<code>9</code>个函数，与<code>ERC721</code>相关的函数我们这里不再重复介绍，只介绍和拍卖相关的。</p><ul><li><p>设定拍卖起始时间：我们在构造函数中会声明当前区块时间为起始时间，项目方也可以通过<code>setAuctionStartTime()</code>函数来调整：</p></li></ul><pre data-type="codeBlock" text="    constructor() ERC721(&quot;WTF Dutch Auctoin&quot;, &quot;WTF Dutch Auctoin&quot;) {
        auctionStartTime = block.timestamp;
    }

    // auctionStartTime setter函数，onlyOwner
    function setAuctionStartTime(uint32 timestamp) external onlyOwner {
        auctionStartTime = timestamp;
    }
"><code>    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) <span class="hljs-title">ERC721</span>(<span class="hljs-params"><span class="hljs-string">"WTF Dutch Auctoin"</span>, <span class="hljs-string">"WTF Dutch Auctoin"</span></span>) </span>{
        auctionStartTime <span class="hljs-operator">=</span> <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>;
    }

    <span class="hljs-comment">// auctionStartTime setter函数，onlyOwner</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setAuctionStartTime</span>(<span class="hljs-params"><span class="hljs-keyword">uint32</span> timestamp</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        auctionStartTime <span class="hljs-operator">=</span> timestamp;
    }
</code></pre><ul><li><p>获取拍卖实时价格：<code>getAuctionPrice()</code>函数通过当前区块时间以及拍卖相关的状态变量来计算实时拍卖价格。当<code>block.timestamp</code>小于起始时间，价格为最高价<code>AUCTION_START_PRICE</code>；当<code>block.timestamp</code>大于结束时间，价格为最低价<code>AUCTION_END_PRICE</code>；当<code>block.timestamp</code>处于两者之间时，则计算出当前的衰减价格。</p></li></ul><pre data-type="codeBlock" text="    // 获取拍卖实时价格
    function getAuctionPrice(uint256 _auctionStartTime)
        public
        view
        returns (uint256)
    {
        if (block.timestamp &lt; _auctionStartTime) {
        return AUCTION_START_PRICE;
        }else if (block.timestamp - _auctionStartTime &gt;= AUCTION_TIME) {
        return AUCTION_END_PRICE;
        } else {
        uint256 steps = (block.timestamp - _auctionStartTime) /
            AUCTION_DROP_INTERVAL;
        return AUCTION_START_PRICE - (steps * AUCTION_DROP_PER_STEP);
        }
    }
"><code>    <span class="hljs-comment">// 获取拍卖实时价格</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAuctionPrice</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> _auctionStartTime</span>)
        <span class="hljs-title"><span class="hljs-keyword">public</span></span>
        <span class="hljs-title"><span class="hljs-keyword">view</span></span>
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)
    </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span> <span class="hljs-operator">&#x3C;</span> _auctionStartTime) {
        <span class="hljs-keyword">return</span> AUCTION_START_PRICE;
        }<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span> <span class="hljs-operator">-</span> _auctionStartTime <span class="hljs-operator">></span><span class="hljs-operator">=</span> AUCTION_TIME) {
        <span class="hljs-keyword">return</span> AUCTION_END_PRICE;
        } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">uint256</span> steps <span class="hljs-operator">=</span> (<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span> <span class="hljs-operator">-</span> _auctionStartTime) <span class="hljs-operator">/</span>
            AUCTION_DROP_INTERVAL;
        <span class="hljs-keyword">return</span> AUCTION_START_PRICE <span class="hljs-operator">-</span> (steps <span class="hljs-operator">*</span> AUCTION_DROP_PER_STEP);
        }
    }
</code></pre><ul><li><p>用户拍卖并铸造<code>NFT</code>：用户通过调用<code>auctionMint()</code>函数，支付<code>ETH</code>参加荷兰拍卖并铸造<code>NFT</code>。该函数首先检查拍卖是否开始/铸造是否超出<code>NFT</code>总量。接着，合约通过<code>getAuctionPrice()</code>和铸造数量计算拍卖成本，并检查用户支付的<code>ETH</code>是否足够：如果足够，则将<code>NFT</code>铸造给用户，并退回超额的<code>ETH</code>；反之，则回退交易。</p></li></ul><pre data-type="codeBlock" text="    // 拍卖mint函数
    function auctionMint(uint256 quantity) external payable{
        uint256 _saleStartTime = uint256(auctionStartTime); // 建立local变量，减少gas花费
        require(
        _saleStartTime != 0 &amp;&amp; block.timestamp &gt;= _saleStartTime,
        &quot;sale has not started yet&quot;
        ); // 检查拍卖是否开始
        require(
        totalSupply() + quantity &lt;= COLLECTOIN_SIZE,
        &quot;not enough remaining reserved for auction to support desired mint amount&quot;
        ); // 检查是否超过NFT上限

        // Mint NFT
        for(uint i = 0; i &lt; quantity; i++) {
            uint mintIndex = totalSupply();
            _mint(msg.sender, mintIndex);
            _addTokenToAllTokensEnumeration(mintIndex);
        }

        uint256 totalCost = getAuctionPrice(auctionStartTime) * quantity; // 计算mint成本
        require(msg.value &gt;= totalCost, &quot;Need to send more ETH.&quot;); // 检查用户是否支付足够ETH
        // 多余ETH退款
        if (msg.value &gt; totalCost) {
            payable(msg.sender).transfer(msg.value - totalCost);
        }
    }
"><code>    <span class="hljs-comment">// 拍卖mint函数</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">auctionMint</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> quantity</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span></span>{
        <span class="hljs-keyword">uint256</span> _saleStartTime <span class="hljs-operator">=</span> <span class="hljs-keyword">uint256</span>(auctionStartTime); <span class="hljs-comment">// 建立local变量，减少gas花费</span>
        <span class="hljs-built_in">require</span>(
        _saleStartTime <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span> <span class="hljs-operator">></span><span class="hljs-operator">=</span> _saleStartTime,
        <span class="hljs-string">"sale has not started yet"</span>
        ); <span class="hljs-comment">// 检查拍卖是否开始</span>
        <span class="hljs-built_in">require</span>(
        totalSupply() <span class="hljs-operator">+</span> quantity <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> COLLECTOIN_SIZE,
        <span class="hljs-string">"not enough remaining reserved for auction to support desired mint amount"</span>
        ); <span class="hljs-comment">// 检查是否超过NFT上限</span>

        <span class="hljs-comment">// Mint NFT</span>
        <span class="hljs-keyword">for</span>(<span class="hljs-keyword">uint</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> quantity; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            <span class="hljs-keyword">uint</span> mintIndex <span class="hljs-operator">=</span> totalSupply();
            _mint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, mintIndex);
            _addTokenToAllTokensEnumeration(mintIndex);
        }

        <span class="hljs-keyword">uint256</span> totalCost <span class="hljs-operator">=</span> getAuctionPrice(auctionStartTime) <span class="hljs-operator">*</span> quantity; <span class="hljs-comment">// 计算mint成本</span>
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span> <span class="hljs-operator">></span><span class="hljs-operator">=</span> totalCost, <span class="hljs-string">"Need to send more ETH."</span>); <span class="hljs-comment">// 检查用户是否支付足够ETH</span>
        <span class="hljs-comment">// 多余ETH退款</span>
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span> <span class="hljs-operator">></span> totalCost) {
            <span class="hljs-keyword">payable</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>).<span class="hljs-built_in">transfer</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span> <span class="hljs-operator">-</span> totalCost);
        }
    }
</code></pre><ul><li><p>项目方取出筹集的<code>ETH</code>：项目方可以通过<code>withdrawMoney()</code>函数提走拍卖筹集的<code>ETH</code>。</p></li></ul><pre data-type="codeBlock" text="    // 提款函数，onlyOwner
    function withdrawMoney() external onlyOwner {
        (bool success, ) = msg.sender.call{value: address(this).balance}(&quot;&quot;);
        require(success, &quot;Transfer failed.&quot;);
    }
"><code>    <span class="hljs-comment">// 提款函数，onlyOwner</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdrawMoney</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        (<span class="hljs-keyword">bool</span> success, ) <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>.<span class="hljs-built_in">call</span>{<span class="hljs-built_in">value</span>: <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>}(<span class="hljs-string">""</span>);
        <span class="hljs-built_in">require</span>(success, <span class="hljs-string">"Transfer failed."</span>);
    }
</code></pre><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">总结</h2><p>这一讲，我们介绍了荷兰拍卖，并通过简化版<code>Azuki</code>荷兰拍卖代码，讲解如何通过<code>荷兰拍卖</code>发售<code>ERC721</code>标准的<code>NFT</code>。我拍卖到的最贵的<code>NFT</code>是音乐家<code>Jonathan Mann</code>的一首音乐<code>NFT</code>，你呢？</p>]]></content:encoded>
            <author>26212@newsletter.paragraph.com (polyma)</author>
        </item>
    </channel>
</rss>