Wednesday, December 18, 2024

Simple NFT Marketplace Code Explained: A Detailed Breakdown of Minting, Listing, and Buying NFTs

 In this post, I created a simple NFT marketplace contract. The program listing follows, and I will explain each line to help basic learners understand what it does.

Here is the complete program listing:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract SimpleNFTMarketplace is ERC721, Ownable {
    uint256 public nextTokenId;
    mapping(uint256 => uint256) public prices;
    mapping(uint256 => bool) public forSale;

    constructor() ERC721('SimpleNFT', 'SNFT') {}

    function mint(address to) external onlyOwner {
        _safeMint(to, nextTokenId);
        nextTokenId++;
    }

    function listForSale(uint256 tokenId, uint256 price) external {
        require(ownerOf(tokenId) == msg.sender, "You do not own this token");
        require(price > 0, "Price must be greater than zero");
        prices[tokenId] = price;
        forSale[tokenId] = true;
    }

    function buy(uint256 tokenId) external payable {
        require(forSale[tokenId], "Token is not for sale");
        require(msg.value >= prices[tokenId], "Insufficient funds sent");

        address seller = ownerOf(tokenId);
        _transfer(seller, msg.sender, tokenId);

        payable(seller).transfer(msg.value);

        forSale[tokenId] = false;
        prices[tokenId] = 0;
    }

    function _baseURI() internal view virtual override returns (string memory) {
        return "https://api.example.com/metadata/";
    }
}

License and Solidity Version

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

  • License Identifier: // SPDX-License-Identifier: MIT specifies that this contract uses the MIT license, making it open source and freely usable by others.
  • Solidity Version: pragma solidity ^0.8.0; ensures that the contract is compiled with Solidity version 0.8.0 or later, but not with versions that are not backward-compatible.

Imports

1
2
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";


  • ERC721: This is an import from the OpenZeppelin library, which provides a standard implementation of the ERC-721 non-fungible token standard.
  • Ownable: This is another import from OpenZeppelin, which provides basic access control where there is an owner account that can be granted exclusive access to specific functions.

Contract Definition

1
contract SimpleNFTMarketplace is ERC721, Ownable {

  • Inheritance: The contract SimpleNFTMarketplace inherits from ERC721 and Ownable, meaning it has all the functionalities of a standard ERC-721 token and basic ownership functionalities.

State Variables

1
2
3
uint256 public nextTokenId;
mapping(uint256 => uint256) public prices;
mapping(uint256 => bool) public forSale;

  • nextTokenId: This variable keeps track of the next token ID to be minted.
  • prices: This mapping associates each token ID with a price in wei (the smallest unit of Ether).
  • forSale: This mapping keeps track of whether a token is listed for sale or not.

Constructor

1
constructor() ERC721('SimpleNFT', 'SNFT') {}

  • Constructor: The constructor initializes the contract. It calls the constructor of the ERC721 contract with the name "SimpleNFT" and the symbol "SNFT".

Mint Function

1
2
3
4
function mint(address to) external onlyOwner {
    _safeMint(to, nextTokenId);
    nextTokenId++;
}

  • mint: This function allows the contract owner to mint new NFTs.
    • onlyOwner: This modifier ensures that only the owner of the contract can call this function.
    • _safeMint: This is an internal function from the ERC721 contract that safely mints a new token and assigns it to the specified address.
    • Increment Token ID: After minting a new token, nextTokenId is incremented to ensure the next token has a unique ID.

List for Sale Function

1
2
3
4
5
6
function listForSale(uint256 tokenId, uint256 price) external {
    require(ownerOf(tokenId) == msg.sender, "You do not own this token");
    require(price > 0, "Price must be greater than zero");
    prices[tokenId] = price;
    forSale[tokenId] = true;
}

  •  listForSale: This function allows the owner of an NFT to list it for sale.

  • Check Ownership: require(ownerOf(tokenId) == msg.sender, "You do not own this token"); ensures that only the owner of the token can list it for sale.
  • Check Price: require(price > 0, "Price must be greater than zero"); ensures that the sale price is positive.
  • Set Price and Sale Status: The price and sale status of the token are updated in the prices and forSale mappings, respectively.

 Buy Function

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function buy(uint256 tokenId) external payable {
    require(forSale[tokenId], "Token is not for sale");
    require(msg.value >= prices[tokenId], "Insufficient funds sent");

    address seller = ownerOf(tokenId);
    _transfer(seller, msg.sender, tokenId);

    payable(seller).transfer(msg.value);

    forSale[tokenId] = false;
    prices[tokenId] = 0;
}

  • buy: This function allows a user to purchase an NFT that is listed for sale.
    • Check Sale Status: require(forSale[tokenId], "Token is not for sale"); ensures the token is actually listed for sale.
    • Check Funds: require(msg.value >= prices[tokenId], "Insufficient funds sent"); ensures the buyer has sent enough Ether to cover the price.
    • Transfer Token: _transfer(seller, msg.sender, tokenId); transfers the token from the seller to the buyer.
    • Transfer Payment: payable(seller).transfer(msg.value); transfers the payment to the seller.
    • Update Sale Status: The sale status and price of the token are reset.

Base URI Function

1
2
3
function _baseURI() internal view virtual override returns (string memory) {
    return "https://api.example.com/metadata/";
}

  • _baseURI: This function returns the base URI for the token metadata. It overrides the _baseURI function from the ERC721 contract.
    • Metadata URI: https://api.example.com/metadata/ is the base URI where the metadata for the tokens is hosted. This should be replaced with your actual metadata URL.

Summary

  • Minting NFTs: The contract owner can mint new NFTs.
  • Listing NFTs for Sale: NFT owners can list their tokens for sale by setting a price.
  • Buying NFTs: Users can buy listed NFTs by sending sufficient Ether to the contract, which then transfers the token and payment.
  • Metadata Management: The base URI for the token metadata can be customized to point to the appropriate metadata location.

This contract forms the basis for a simple NFT marketplace where users can mint, list, and buy NFTs. It can be extended with more features such as auctions, royalties, or advanced metadata handling based on your requirements.

No comments:

Post a Comment