DEV Community

Cover image for Tutorial - play with an auction demo based on ERC721
Yongchang He
Yongchang He

Posted on

Tutorial - play with an auction demo based on ERC721

This tutorial is meant for those with a basic knowledge of Ethereum and smart contracts, who have some knowledge of Solidity.
The purpose of building this blog is to write down the detailed operation history and my memo for learning the dApps and solidity programming.
If you are also interested and want to get hands dirty, just follow these steps below and have fun!~


Smart contract overview

This contract is for selling NFT. The general idea is that users (accounts) can send their bids to the contract for winning the NFT during a bidding period. The initial ownership for this NFT is the contract owner account[0] / address(0)(assume that we deploy the contract using account[0]). The highest bidder will win the ownership of the NFT. Participants can withdraw their bids when the selling ends.

Remark: This is test version not for production use.

Overview of the contract code (EnglishAuction.sol):

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

interface IERC721 {
    function transferFrom (
        address from,
        address to,
        uint nftId
    ) external;

contract EnglishAuction {
    event Start();
    event Bid(address indexed sender, uint amount);
    event Withdraw(address indexed bidder, uint amount);
    event End(address highestBidder, uint amount);

    IERC721 public immutable nft;
    uint public immutable nftId;
    address payable public immutable seller;
    uint32 public endAt;
    bool public started;
    bool public ended;
    address public highestBidder;
    uint public highestBid;
    mapping(address => uint) public bids;

    constructor(address _nft, uint _nftId, uint _startingBid) {
        nft = IERC721(_nft);
        nftId = _nftId;
        seller = payable(msg.sender);
        highestBid = _startingBid;
    function start() external {
        require(msg.sender == seller, "not seller");
        require(!started, "started");
        started = true;
        endAt = uint32(block.timestamp + 60); // 60 seconds should be long enough for the Demo and test.
        nft.transferFrom(seller, address(this), nftId);
        emit Start();
    function bid() external payable {
        require(started, "not started");
        require(block.timestamp < endAt, "ended");
        require(msg.value > highestBid, " value < highest bid");
        if (highestBidder != address(0)) {
            bids[highestBidder] += highestBid;
        highestBid = msg.value;
        highestBidder = msg.sender;
        emit Bid(msg.sender, msg.value);
    function withdraw() external {
        uint bal = bids[msg.sender];
        bids[msg.sender] = 0;
        emit Withdraw(msg.sender, bal);
    function end() external {
        require(started, "not started");
        require(!ended, "ended!");
        require(block.timestamp >= endAt, "not ended");
        ended = true;
        if (highestBidder != address(0)) {
            nft.transferFrom(address(this), highestBidder, nftId);
        } else {
            nft.transferFrom(address(this), seller, nftId);
        emit End(highestBidder, highestBid);
Enter fullscreen mode Exit fullscreen mode

License identifier and compiler version are defined at first.
Interfaces are identified using the “interface” keyword. It contains function signatures without the function definition implementation. It can be used in a contract to call functions in another contract.
Event can be emitted in a specific function and the log can be seen in decoded output of each function call.
State variables are those whose values are permanently stored in a contract storage. Immutable state variables cannot be modified after the contract has been constructed.
A constructor is executed upon contract creation, and where we initialize the newly created NFT address, NFT ID, seller (that is contract owner account[0]) and the initial bid.
NFT ownership will be transferred from the contract owner to this contract when seller clicking "start" to execute start() function. 60 seconds is specified as the bidding period. You can always modify it as a longer time. When the bidding process started, users can send their bids with specific value by executing bid().
Function end() is used to terminate the bidding process and withdraw is to withdraw bids from this contract.

Testing process

Remix is used for contract testing.

Mint the NFT ownership to account[0] by pasting the address(0), and specifying the nftId as your lucky number:

Image description

  • 2 - Deploy the EnglishAuction.sol contract

ERC721 contract address, nftId and starting bids are needed:

Image description

  • 3 - Approval for NFT ownership transmission Contract ERC721 needs account[0] to approve that the contract EnglishAuction will be able to transfer the NFT ownership from the seller to the contract itself. contract address of EnglishAuction and nftId are needed:

Image description

  • 4 - Start the bidding process The seller can start the process by clicking start; other accounts can send bids with a specified amount of ETH:

Image description

  • 5 - Check the Bidder information and ownership

Image description

Image description

Git repository

Welcome to visit the Git repo for source code, and feel free to reach me by leaving a message below or via email found in my profile.

Thank you!

GitHub logo hyc0812 / solidity-essentials

Solidity programming baby examples...

Solidity-Baby Steps...

The best path for earning Solidity is to approach examples.

Here are baby examples. Typing one-by-one is recommended.


MyBlog explanation for using EnglishAuction and testing.




updateBalance & getBalance

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract MyLedger {
    mapping(address => uint) public balances;
    function updateBalance(uint newBal) public {
        balances[msg.sender] = newBal;
    function getBalance() public view returns(uint) {
        return balances[msg.sender];
Enter fullscreen mode Exit fullscreen mode


Inherited from Square

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/utils/Strings.sol";

contract Shape {
    uint height;
    uint width;

    constructor(uint _height, uint _width) {
        height = _height;
        width = _width;

contract Square is Shape {
    constructor(uint h, uint w) Shape(h, w) {}

    function getHeight() 
Enter fullscreen mode Exit fullscreen mode


Top comments (0)