April 12, 2022

How to Create a Dutch Auction Smart Contract

Overview

Often to perform any transaction between two untrusted parties, there is a need for a trusted middle man; smart contracts eliminate this need for a middle man altogether. In this guide, we will learn how to create a dutch auction smart contract which will make the entire auction process trustless.

Prerequisites

Auctions using smart contract

Auctions are platforms for selling goods in public through a process of bidding where usually the person with the highest bid wins the auction and gets the ownership of the item. Though this is the primary format of how an auction works, there are some other rules to an auction as well. 

The different types of auctions are:

  • English auction (Open ascending price auction): This is the traditional auction known to everyone where the highest bidder is the winner of the auction.

  • Dutch auction (Open descending price auction): This type of auction is mainly used for perishable items like flowers, food, clothes, etc. There is a start price, and the price goes down by a certain percentage as time goes and the bidder who bids more than or equal to the current price wins the auction or when a preset duration is over the last bidder who places a bid wins.

  • First-price sealed-bid auction (Blind auction): In this type of auction, all the bidders submit their bids in an envelope unaware of each other’s bids, then later the auctioneer opens the envelopes, and the highest bid wins.

  • Second-price sealed-bid auction (Vickrey auction): This is the same as a first-price sealed-bid auction. The only difference is that the winner is the second-highest bid.

In every type of auction, a middle man is an auctioneer responsible for conducting the auction and determining a winner and charges fees to conduct this, usually a percentage of the total selling price. A smart contract can eliminate the need for an auctioneer, and the entire process can be made automatic, trustless, and secure as we will perform this on the Ethereum blockchain.

What is Dutch auction?

A Dutch auction, also known as the open-descending auction, is a type of auction where the seller first sets a starting price, duration, and a discount rate. As time passes, the item's price keeps decreasing until the preset duration time has ended. For example, suppose there is a really good bag you want to get but it is out of your budget. In that case, as time goes on the bag will get cheaper; first a 10% discount, then 30%, then 50%, until the bag is cheap enough for you to purchase. This is the concept of Dutch auctions. To make this work on Ethereum we will need to create and deploy both an ERC721 contract and a dutch auction smart contract.

Booting our Ethereum node

We will deploy our contract on the Ropsten testnet of Ethereum using the Remix IDE and a MetaMask wallet. We can use the default Ropsten network on MetaMask, but MetaMask is a very popular tool used by the majority of 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 this guide on adding a custom provider to MetaMask.

You should also have some Ropsten test ETH in your MetaMask Wallet to pay for gas. If you do not, you can get some from the Ropsten faucet.

Creating and deploying the Dutch auction contract

Before moving further, it is assumed that you have already gone through this guide on how to create an ERC721 (NFT) token and deployed your ERC721 token contract using Remix IDE, and have minted an 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: Specifying SPDX license type, which is an addition after Solidity ^0.6.8. Whenever the source code of a smart contract is made available to the public, these licenses can help resolve/avoid copyright issues. If you do not wish to specify any license type, you can use a special value UNLICENSED or simply skip the whole comment (it will not result in an error, just a warning).

Line 2: Declaring the Solidity version

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

Line 12: Declaring a variable DURATION as private, which will store the time for which the auction will be active.

Line 15-16: Creating two state variables, nft and nftId, to store the NFT address and nft id of our token. Both the variables are public and immutable, which means they are viewable outside of the contract's scope and are not changeable once the contract is deployed.

Line 18-22: Creating a variable seller (it is payable, which means it can receive funds) to store the address of the seller, a variable startingPrice to store the starting price of the NFT, a variable discountRate which will store the rate that our NFT decreases over time, a variable startAt to store the starting timestamp of the auction and variable expiresAt to store the ending timestamp of the auction. All of these variables are public and immutable too.

Line 24-29: Initializing the above state variables in a constructor.

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

Line 31-34: Setting startingPrice to  _startingPrice and discountRate to  _discountRate, which will be from the input. 
- Setting startAt to block.timestamp means that the starting time will be the current timestamp, i.e., when the contract is deployed. 
- Setting expiresAt to block.timestamp + DURATION means the auction will expire when the duration is complete from the current timestamp (7 days here).

Line 36: A check to ensure that the price of the NFT is always greater than or equal to zero, along with an error message if the check fails.
      
Line 38-39: Setting nft to _nft and nftId to _nftId, from input.

Line 42-46: A function/method named getPrice() to get the current price of the NFT.
- A variable timeElapsed to store the time that has passed since the contract was deployed, calculated by subtracting the current timestamp with startAt.
- A variable discount to store the current discount rate calculated by subtracting initially set discountRate with timeElapsed.
- Returning the current price by subtracting the startingPrice with discount, so whenever getPrice() function is called, it will return the current price of the NFT.

Line 48-59: A function to buy the NFT.
- A check to ensure that the current timestamp is less than the auction expiration time expiresAt, along with an error message if the check fails.
- A variable price to store the current price of NFT, which it will get from the getPrice() method.
- A check to make sure that the amount of ETH sent by the bidder/buyer (msg.value) is always more than or equal to the current price of the NFT, along with an error message if the check fails.
- Transferring nft using the transferFrom function of IERC721. NFT will be identified with the help of the nftId and will be transferred from the seller to the current msg.sender (who is the person interacting with the contract at the moment).
- A variable refund to store any excess amount of ETH left after the buyer bought the NFT, is calculated by subtracting the amount of ETH sent by the buyer (msg.value) with the current price of the NFT, which is price.
- An if condition to check if the value of in refund variable is zero, the contract will send that value back to the buyer.
- Deleting the contract using the selfdestruct function and transferring the ETH to the seller.

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


Now, go to the deploy tab, select ‘Injected Web3’ under the ‘ENVIRONMENT’ option, also make sure you see ‘Ropsten (3) network’ written below it; if not, select the QuickNode Ropsten node which we added earlier and select dutchAuction from the drop-down ‘CONTRACT’ option then click on the small arrow beside the deploy button and fill in the following details.

  1. Starting price of the NFT in gwei, 10,000,000 gwei here.
  2. The discount rate at which the price of NFT will decrease every second since the contract is deployed until seven days or until the price is greater than zero, whichever is earliest. 1 here.
  3. Address of the NFT contract which we deployed earlier.
  4. ID of our NFT.


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

Performing the auction

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


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

  1. Click on the ‘getPrice’ button to get the current price of the NFT.
  2. Copy the value and scroll up and paste it in the value field right above where we selected the contract name while deploying our contract.
  3. Now scroll down back to the deployed dutchAuction contract and click on buy.




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

Also, the contract is now destructed. It will not return any output:

Conclusion

If you made it here, congratulations; you are on your way to becoming a Solidity expert. In this guide, we learned about auctions with smart contracts and writing and deploying a smart contract on solidity to perform Dutch auction.

Subscribe to our newsletter for more articles and guides on Ethereum. If you have any feedback, feel free to reach out to us via Twitter. You can always chat with us on our Discord community server, featuring some of the coolest developers you’ll ever meet :)
Ready to try QuickNode? Start your 7-day free trial

Related articles 14

Apr 12, 2022 How to Deploy a Smart Contract on MATIC/Polygon

Ethereum is a very lovely blockchain to work with, but recently, heavy traffic and many people building on it have resulted in the chain being a bit congested. Layer 2 solutions solve this issue by extending Ethereum's scalability. Polygon (FKA MATIC) is one such...

Continue reading
Apr 12, 2022 How to build an Ethereum URL shortener dApp

dApps (decentralized applications) are an integral part of the Ethereum development ecosystem. There are thousands of dApps already residing on the Ethereum blockchain. In this guide, we will learn how to build an Ethereum dApp that...

Continue reading
Jan 31, 2022 How to Create a BEP20 Token

BEP20 is the Binance Smart Chain equivalent to the popular ERC20 specification for tokens on the Ethereum network. The difference is the BEP20 Token isn't run on the Ethereum Network, but the Binance Smart Chain network. Despite this difference, because BSC (Binance Smart...

Continue reading
Dec 27, 2021 An overview of how smart contracts work on Ethereum

Smart contracts are the first-class citizens in the Ethereum blockchain; they are a critical part of Ethereum development. In this guide, we’ll see an overview of how smart contracts work on the Ethereum blockchain. This will include how to set up an Ethereum IDE and then...

Continue reading
Mar 7, 2022 How to create and deploy an ERC20 token

Ethereum network’s launch in 2015 created a lot of buzz in the developer community and sprouted a lot of tokens on the network. Initially there weren’t any templates or guidelines for token development. This resulted in a variety of tokens quite different from each other....

Continue reading
Dec 27, 2021 How to create your own Oracle with an Ethereum smart contract

Ethereum smart contracts are the most valuable features of the Ethereum blockchain network; The development of applications driven by smart contracts on the Ethereum blockchain has skyrocketed recently. These smart-contracts are sandboxed and can’t access the data...

Continue reading
Dec 27, 2021 What is an ABI?

While interacting with a smart contract ABI is one of the essential components. In this guide, let us understand what the ABI of smart contracts is.

Continue reading
Dec 27, 2021 How to call another smart contract from your solidity code

Smart contracts are the most popular feature of the Ethereum network. Smart contracts have functions that sometimes need to be accessed by other smart contracts. Before understanding smart contracts' interactions, let's brush up on our basics about smart...

Continue reading
Dec 27, 2021 How to create and deploy an ERC-721 (NFT)

Digital collectibles compatible with the ERC-721 standard have become very popular since the launch of Cryptokitties and have moved forward towards mass adoption in recent months. This guide will cover creating and deploying our...

Continue reading
Dec 27, 2021 How to write an Ethereum smart contract using Solidity

This article is intended for developers new to Ethereum development. In this article, we will talk about Solidity and smart contracts, What they are and what role they actually play in the ethereum development with the end goal of writing a smart contract using...

Continue reading
Mar 14, 2022 How to Create and Deploy an ERC-1155 NFT

ERC1155 has emerged as a gold standard to create NFTs; every major marketplace lists new tokens as an ERC1155 standard. In this guide, we will learn about the ERC1155 token standard and how to create an ERC1155 token.What we will...

Continue reading
Jan 31, 2022 Como crear un Token BEP20

BEP20 es en Binance Smart Chain el equivalente al popular token ERC20 de la red de Ethereum. La diferencia es que el token BEP20 no corre en la red de Ethereum sino en la red de Binance Smart Chain. Más allá de esta diferencia, como BSC (Binance Smart Chain) implementa la...

Continue reading
Feb 12, 2022 How to Create a Smart Contract Factory in Solidity using Hardhat

Hello reader! Today we are going to be leveling up your Solidity skills. We are going to walk you through how to implement the factory contract pattern. Do not worry if you have not heard this term before; all will be explained to you by the end of the...

Continue reading
Apr 12, 2022 How to Create and Deploy a Factory ERC-1155 Contract on Polygon using Truffle

With the increasing popularity of NFTs, Polygon, a fast-growing blockchain, has become the go-to choice for many users and developers. Compared to Ethereum, Polygon allows users and developers to interact with blockchains at a more affordable level. For these reasons,...

Continue reading