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);
}
- 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;
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);
}
- 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);
}
- 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;
}
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)