DEV Community

Cover image for Building an NFT Marketplace Smart Contract with Solidity and OpenZeppelin
Sumana
Sumana

Posted on

Building an NFT Marketplace Smart Contract with Solidity and OpenZeppelin

Building a Decentralized NFT Marketplace with Solidity

In this blog, we'll walk through the creation of a simple NFT marketplace using Solidity. This contract will allow users to list NFTs for sale, purchase NFTs, and manage ownership—all while ensuring security with the ReentrancyGuard feature to prevent certain types of attacks. Let’s break down the functionality and code.

Key Features:

  • Listing NFTs for sale by paying a small listing fee to the contract owner.
  • Buying NFTs with ether through the marketplace.
  • Security against reentrancy attacks to safeguard transactions.
  • Fetching NFTs you own or have created.

Contract Breakdown

1. Contract Setup and State Variables

The contract imports the OpenZeppelin libraries for ERC721 (NFT standard), counters for unique token IDs, and reentrancy protection.

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

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract NFTMarket is ReentrancyGuard {
    using Counters for Counters.Counter;

    // Counters to track total items and items sold
    Counters.Counter private _itemIds;
    Counters.Counter private _itemsSold;

    // Contract owner gets a listing fee for every NFT listed
    address payable owner;
    uint256 listingPrice = 0.001 ether;

    constructor() {
        owner = payable(msg.sender);
    }
Enter fullscreen mode Exit fullscreen mode
  • Counters are used to track the total number of NFTs listed and those sold.
  • The owner of the marketplace earns a listing fee on every NFT sale.

2. Struct and Mapping

A MarketItem struct stores the details of each listed NFT, including the item ID, contract address, seller, owner, price, and sale status. A mapping is used to access each MarketItem by its ID.

    struct Marketitem {
        uint itemId;
        address nftContract;
        uint256 tokenId;
        address payable seller;
        address payable owner;
        uint256 price;
        bool sold;
    }

    mapping(uint256 => Marketitem) private idToMarketItem;
Enter fullscreen mode Exit fullscreen mode

3. Listing NFTs for Sale

This function allows users to list their NFTs on the marketplace by specifying the contract address, token ID, and price. They must pay a small listing fee.

    function createMarketItem(
        address nftContract,
        uint256 tokenId,
        uint256 price
    ) public payable nonReentrant {
        require(price > 0, "Price must be at least 1 wei");
        require(msg.value == listingPrice, "Must pay listing price");

        _itemIds.increment();
        uint256 itemId = _itemIds.current();

        idToMarketItem[itemId] = Marketitem(
            itemId,
            nftContract,
            tokenId,
            payable(msg.sender),
            payable(address(0)),
            price,
            false
        );

        IERC721(nftContract).transferFrom(msg.sender, address(this), tokenId);
        emit MarketItemCreated(itemId, nftContract, tokenId, msg.sender, address(0), price, false);
    }
Enter fullscreen mode Exit fullscreen mode
  • NFTs are transferred to the marketplace contract (address(this)).
  • The marketplace owner collects a small fee, and the NFT is listed for sale.

4. Buying NFTs

This function allows users to purchase an NFT by sending the required ether. It transfers both the NFT ownership and the ether between buyer and seller.

    function createMarketSale(
        address nftContract,
        uint256 itemId
    ) public payable nonReentrant {
        uint price = idToMarketItem[itemId].price;
        uint tokenId = idToMarketItem[itemId].tokenId;
        require(msg.value == price, "Please submit the asking price");

        idToMarketItem[itemId].seller.transfer(msg.value);
        IERC721(nftContract).transferFrom(address(this), msg.sender, tokenId);

        idToMarketItem[itemId].owner = payable(msg.sender);
        idToMarketItem[itemId].sold = true;
        _itemsSold.increment();
        payable(owner).transfer(listingPrice);
    }
Enter fullscreen mode Exit fullscreen mode
  • Buyers must send the correct price to complete the sale.
  • Ownership of the NFT and ether is securely transferred.

5. Fetching NFTs

Several functions allow users to fetch NFTs that are available for sale, those they own, or ones they have created.

    function fetchMarketItems() public view returns (Marketitem[] memory) {
        uint itemCount = _itemIds.current();
        uint unsoldItemCount = _itemIds.current() - _itemsSold.current();
        uint currentIndex = 0;

        Marketitem[] memory items = new Marketitem[](unsoldItemCount);
        for (uint i = 0; i < itemCount; i++) {
            if (idToMarketItem[i + 1].owner == address(0)) {
                uint currentId = idToMarketItem[i + 1].itemId;
                Marketitem storage currentItem = idToMarketItem[currentId];
                items[currentIndex] = currentItem;
                currentIndex += 1;
            }
        }
        return items;
    }
Enter fullscreen mode Exit fullscreen mode

This function returns all unsold NFTs, which can be displayed in the marketplace UI.


Conclusion

This Solidity contract lays the foundation for a decentralized NFT marketplace. Users can securely list, buy, and manage NFTs while paying small listing fees. By leveraging OpenZeppelin libraries and reentrancy protection, we ensure security and ease of use for our marketplace.

Top comments (0)