How to Create a Dutch Auction Smart Contract

overview

Normally, to execute any transaction between two untrusted parties, a trusted intermediary is required; smart contracts completely eliminate the need for an intermediary. In this article, we will learn how to create a Dutch auction smart contract, which will make the entire auction process trustless.
prerequisites

0_zmJJ8o3c5AByT39a

Auctions Using Smart Contracts

Auctions are platforms where goods are sold publicly through a bidding process, usually the highest bidder wins the auction and takes ownership of the item. While this is the primary form in which auctions operate, there are some additional rules for auctions.

The different types of auctions are:

English Auction (Open Rising Price): This is a well known traditional auction where the highest bidder is the winner of the auction.

Dutch Auction (Open Price Auction): This type of auction is mainly used for perishable items such as flowers, food, clothes, etc. There is a starting price, and over time, the price drops by a certain percentage, and the bidder who bids above or equal to the current price wins the auction, or when the preset duration exceeds the last bid, the bidder wins the auction.

First Price Sealed Bid Auction (Blind Auction): In this type of auction, all bidders, without knowing what the other bid, submit their bids in envelopes, which are then opened by the auctioneer and the highest bidder wins.

Second Level Sealed Bid Auction (Vickrey Auction): This is the same as the First Price Sealed Bid Auction. The only difference is that the winner is the second highest bid.

In each type of auction, the middleman is the auctioneer who conducts the auction and determines the winner for a fee, usually a percentage of the total selling price. Smart contracts can remove the need for an auctioneer and the whole process can be automated, trustless and secure as we will be doing this on the Ethereum blockchain.

What is a Dutch auction?

A Dutch auction, also known as an open descending auction, is a type of auction where the seller first sets the starting price, duration, and discount rate. Over time, the price of the item will keep decreasing until the preset duration is over. For example, let's say you want a really nice bag, but it's out of your budget. In that case, the bag will get cheaper over time; first 10% off, then 30%, then 50% until the bag is cheap enough for you to buy it. This is the concept of a Dutch auction. To make this work on Ethereum, we need to create and deploy an ERC721 contract and a Dutch auction smart contract.

Start our Ethereum node

We will deploy our contract on Ethereum's Ropsten testnet using Remix IDE and MetaMask wallet. We can use the default Ropsten network on MetaMask, but MetaMask is a very popular tool used by most Ethereum developers, so sometimes the network can get congested. To avoid this, we will create a free trial Ropsten node on QuickNode and add it as a custom provider in MetaMask.

Screenshot of Quicknode Ropsten endpoint

Copy the HTTP URL and follow the guide to add a custom provider to MetaMask .

You should also have some Ropsten test ETH in your MetaMask wallet to pay for gas. If you don't have one, you can get some from Ropsten Faucet .

Create and deploy a Dutch auction contract

Before starting the following content, it is assumed that you have read this article to understand how to create ERC721 (NFT) tokens and use Remix IDE to deploy your ERC721 token contract and mint NFT.

Now, create a new solidity file dutchAuction.sol and paste the following code into it:

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

interface IERC721 {
    function transferFrom(
        address _from,
        address _to,
        uint _nftId
    ) external;
}

contract dutchAuction {
    uint private constant DURATION = 7 days;

    IERC721 public immutable nft;
    uint public immutable nftId;

    address payable public immutable seller;
    uint public immutable startingPrice;
    uint public immutable discountRate;
    uint public immutable startAt;
    uint public immutable expiresAt;

    constructor(
        uint _startingPrice,
        uint _discountRate,
        address _nft,
        uint _nftId
    ) {
        seller = payable(msg.sender);
        startingPrice = _startingPrice;
        discountRate = _discountRate;
        startAt = block.timestamp;
        expiresAt = block.timestamp + DURATION;

        require(_startingPrice >= _discountRate * DURATION, "Starting price is too low");

        nft = IERC721(_nft);
        nftId = _nftId;
    }

    function getPrice() public view returns (uint) {
        uint timeElapsed = block.timestamp - startAt;
        uint discount = discountRate * timeElapsed;
        return startingPrice - discount;
    }

    function buy() external payable {
        require(block.timestamp < expiresAt, "This auction has ended");

        uint price = getPrice();
        require(msg.value >= price, "The amount of ETH sent is less than the price of token");

        nft.transferFrom(seller, msg.sender, nftId);
        uint refund = msg.value - price;
        if (refund > 0) {
            payable(msg.sender).transfer(refund);
        }
        selfdestruct(seller);
    }
}

Explanation of the above contract:

Line 1: Specifies the SPDX license type, which was added after Solidity ^0.6.8. These licenses can help resolve/avoid copyright issues whenever the source code of a smart contract is made available to the public. If you don't want to specify any license type, you can use the special value UNLICENSED or simply skip the entire comment (doesn't cause an error, just a warning).

Line 2: Declare the Solidity version.

Lines 4-10: Extend the IERC721 contract interface to use the ERC721 methods in this contract, we will need them to work with our NFT.

Line 12: Declare the variable DURATION as private, which will store the duration of the auction.

Lines 15-16: Create two state variables nft and nftId to store the NFT address and nft id of our token. Both variables are public and immutable, meaning they are visible outside the scope of the contract and cannot be changed once the contract is deployed.

Lines 18-22: Create a variable seller (it is payable, meaning it can receive funds) to store the seller's address, a variable startingPrice to store the starting price of the NFT, and a variable discountRate to store our NFT The rate of decrease over time, the variable startAt stores the start timestamp of the auction, and the variable expiresAt stores the end timestamp of the auction. All these variables are public and immutable.

Lines 24-29: Initialize the above state variables in the constructor.

Line 30: Set the seller as the deployer of the contract.

Lines 31-34: set startingPrice to _startingPrice and discountRate to **_discountRate, **this will come from input.

  • Setting startAt to block.timestamp means that the start time will be the current timestamp, the time the contract was deployed.
  • Setting expiresAt to block.timestamp + DURATION means the auction will expire when the duration of the current timestamp (here 7 days) is complete.

Line 36: Check to make sure the NFT's price is always greater than or equal to zero, and display an error message if the check fails.

Lines 38-39: Set nft to **_nft** and nftId to **_nftId** from input.

Lines 42-46: A function/method called **getPrice()** to get the current price of the NFT.

  • The variable timeElapsed is used to store the time elapsed since the contract was deployed, calculated by subtracting the current timestamp from startAt .
  • The variable discount stores the variable discount of the current discount rate , which is calculated by subtracting the initially set discountRate from timeElapsed .
  • Returns the current price by subtracting startPrice from discount , so whenever the getPrice() function is called, it will return the current price of the NFT.

Lines 48-59: Function to buy NFT.

  • Checks to make sure the current timestamp is less than the auction expiration time **expiresAt, **If the check fails, an error message will be displayed.
  • The variable price is used to store the current price of the NFT, which will be obtained from the **getPrice()** method.
  • Checks to make sure the amount of ETH ( msg.value ) sent by the bidder/buyer is always greater than or equal to the current price of the NFT, and displays an error message if the check fails.
  • Use the transferFrom function of IERC721 to transfer nft. The NFT will be identified with the help of nftId and will be transferred from the seller to the current msg.sender (i.e. the person currently interacting with the contract).
  • The variable refund is used to store any excess ETH amount remaining after the buyer purchases the NFT. It is calculated by subtracting the amount of ETH sent by the buyer ( msg.value ) from the current price of the NFT, ie price .
  • Checks if the value of the refund variable is zero, if so the contract will send that value back to the buyer.
  • Use the selfdestruct function to delete the contract and transfer the ETH to the seller.

Compile the contract; if you get errors during compilation, make sure to select the correct solidity compiler version.

https://img.chengxuka.com

Now, go to the deploy tab, select "Injected Web3" under the "ENVIRONMENT" option, also make sure you see "Ropsten (3) network" under it; if not, select the QuickNode Ropsten node we added earlier, and from Select dutchAuction in the dropdown "CONTRACT" option, then click the small arrow next to the Deploy button and fill in the details below.

  1. The starting price of NFT is in gwei , which is 10,000,000 gwei here.
  2. The discount rate by which the NFT price drops per second since the contract was deployed until 7 days or until the price is greater than zero, whichever is earlier. Fill in 1 here.
  3. The address of the NFT contract we deployed earlier.
  4. The ID of our NFT.

https://img.chengxuka.com

Now click the "transact button" and approve the transaction from MetaMask. Once the transaction is approved, our Dutch auction contract is deployed.

Executing the auction

Now that our Dutch auction contract is deployed, let's see it in action. The first step here is to approve the dutchAcution contract to be able to transfer the NFT. Open the deployed NFT contract and expand the approve function, in the first field paste the address of the dutchAuction contract and the NFT id of the token we are selling.

https://img.chengxuka.com

Now, switch to another account in MetaMask, then expand the deployed dutchAuction contract and perform the following steps:

  1. Click the "getPrice" button to get the current price of the NFT.
  2. Copy that value and scroll up and paste it into the value field just above the contract name we chose when deploying the contract.
  3. Scroll down back to the deployed dutchAuction contract and click Buy.

https://img.chengxuka.com

https://img.chengxuka.com

https://img.chengxuka.com

Once the transaction is complete, the ownership of the NFT is transferred and the Dutch auction is complete.

Also, the contract is now broken. It doesn't return any output:

https://img.chengxuka.com

in conclusion

If you made it, congratulations; you're well on your way to becoming a Solidity professional. In this article, we took a look at auctions using smart contracts, and writing and deploying smart contracts on Solidity to perform Dutch auctions.

Guess you like

Origin blog.csdn.net/hanru723/article/details/125801739