Building an NFT Marketplace from Scratch Using Solidity
Tags: webdev, beginners, devops, blockchain
Published: 2023-03-07 11:21:52

The rise of non-fungible tokens (NFTs) has opened up a whole new world of digital ownership and trading. NFTs are unique and cannot be replicated, making them valuable and in demand. As a result, many developers are now creating NFT marketplaces to facilitate the buying and selling of these unique assets. In this article, we will explore how to build an NFT marketplace from scratch using Solidity, the programming language used to develop smart contracts on the Ethereum blockchain.

## Step 1: Understanding the Requirements

Before we start coding, we need to understand the requirements for the NFT marketplace. The following are some of the key requirements:

- The ability for users to list NFTs for sale
- The ability for users to purchase NFTs from the marketplace
- The ability for sellers to withdraw their NFTs from the marketplace
- The ability for the marketplace to charge a commission on sales
- User roles and permissions, such as admins, sellers, and buyers.

## Step 2: Setting Up the Development Environment
To develop the NFT marketplace, we will need to have the following tools:

- Solidity compiler
- Remix IDE
- Ganache (or any other local Ethereum blockchain)
- Metamask (or any other Ethereum wallet).
- Once we have these tools set up, we can start writing the code.

## Step 3: Defining the Smart Contract

We will start by defining the smart contract that will power the NFT marketplace. The smart contract will handle the logic for buying, selling, and withdrawing NFTs from the marketplace.


```
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

contract NFTMarketplace is IERC721Receiver {
   using SafeMath for uint256;

   enum UserRole {Admin, Seller, Buyer}

   address public marketplaceAddress;
   uint256 public marketplaceFee;

   mapping(address => UserRole) public userRoles;
   mapping(uint256 => NFTListing) public listings;

   struct NFTListing {
      address seller;
      uint256 price;
   }

   constructor(address _marketplaceAddress, uint256 _marketplaceFee) {
      marketplaceAddress = _marketplaceAddress;
      marketplaceFee = _marketplaceFee;
      setUserRole(msg.sender, UserRole.Admin);
   }

   function setUserRole(address user, UserRole role) public virtual onlyAdmin {
      userRoles[user] = role;
   }

   function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external pure override returns (bytes4) {
      return this.onERC721Received.selector;
   }
}
```

## Step 4: Importing the Required Libraries and Interfaces

We will import the required libraries and interfaces for the smart contract. This includes the IERC721 and IERC721Receiver interfaces from the OpenZeppelin library.

```
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

```
Step 5: Defining the Marketplace Configuration

We will define the marketplace configuration, including the address of the marketplace and the fee percentage charged by the marketplace on each sale.

```
address public marketplaceAddress;
uint256 public marketplaceFee;

```

## Step 6: Defining the NFT Listing

We will define the NFT listing function that allows users to list their NFTs for sale on the marketplace. The function takes the NFT contract address, the token ID, and the sale price as inputs. It then adds the NFT to the listings mapping with the seller's address and the sale price.

```
function listNFT(address nftAddress, uint256 tokenId, uint256 price) public {
   require(IERC721(nftAddress).ownerOf(tokenId) == msg.sender, "You do not own this NFT");
   require(userRoles[msg.sender] == UserRole.Seller, "You do not have permission to list NFTs");

   IERC721(nftAddress).safeTransferFrom(msg.sender, address(this), tokenId);
   listings[tokenId] = NFTListing({
      seller: msg.sender,
      price: price
   });
}

```

## Step 7: Defining the Buy NFT Function

We will define the buy NFT function that allows users to purchase NFTs from the marketplace. The function takes the NFT contract address and the token ID as inputs. It then checks if the NFT is listed for sale and if the buyer has enough funds to make the purchase. If the conditions are met, the function transfers the NFT to the buyer and pays the seller the sale price minus the marketplace fee.

```
function buyNFT(address nftAddress, uint256 tokenId) public payable {
   require(listings[tokenId].seller != address(0), "NFT is not listed for sale");
   require(listings[tokenId].price == msg.value, "Incorrect payment amount");

   address seller = listings[tokenId].seller;
   uint256 salePrice = listings[tokenId].price;
   uint256 feeAmount = salePrice.mul(marketplaceFee).div(100);

   payable(marketplaceAddress).transfer(feeAmount);
   payable(seller).transfer(salePrice.sub(feeAmount));
   IERC721(nftAddress).safeTransferFrom(address(this), msg.sender, tokenId);
}

```

## Step 8: Defining the Withdraw NFT Function

We will define the withdraw NFT function that allows sellers to withdraw their NFTs from the marketplace. The function takes the NFT contract address and the token ID as inputs. It then checks if the seller owns the NFT and if it is currently listed for sale. If the conditions are met, the function transfers the NFT back to the seller.

```
function withdrawNFT(address nftAddress, uint256 tokenId) public {
   require(listings[tokenId].seller == msg.sender, "You do not own this NFT or it is not listed for sale");

   delete listings[tokenId];
   IERC721(nftAddress).safeTransferFrom(address(this), msg.sender, tokenId);
}

```

## Step 9: Defining User Roles and Permissions

We will define user roles and permissions for the NFT marketplace. There are three user roles: admin, seller, and buyer. The admin role has permission to set user roles, while sellers and buyers have permission to list and purchase NFTs, respectively.

```
enum UserRole {Admin, Seller, Buyer}

mapping(address => UserRole) public userRoles;

function setUserRole(address user, UserRole role) public virtual onlyAdmin {
   userRoles[user] = role;
}

modifier onlyAdmin() {
   require(userRoles[msg.sender] == UserRole.Admin, "Only admin can perform this action");
   _;
}

```

## Step 10: Deploying the Smart Contract

We can now deploy the smart contract to the Ethereum blockchain using Remix IDE and Ganache. Once the contract is deployed, we can interact with it using Metamask or any other Ethereum wallet.

## Conclusion

Building an NFT marketplace from scratch using Solidity requires a good understanding of the Ethereum blockchain and smart contractdevelopment. By following the steps outlined in this tutorial, you can create a functional NFT marketplace where users can buy and sell NFTs securely and transparently.

However, it is important to note that [building an NFT marketplace](https://www.inoru.com/nft-marketplace-development) is just the first step. To attract buyers and sellers to your marketplace, you need to market it effectively and provide value-added services such as NFT curation, dispute resolution, and community engagement.

Moreover, you should also consider the legal and regulatory aspects of running an NFT marketplace. Depending on the jurisdiction, you may need to comply with laws related to intellectual property, securities, anti-money laundering, and consumer protection.

Therefore, it is important to do your research and seek legal advice before launching an NFT marketplace. By doing so, you can ensure that your marketplace is compliant, trustworthy, and sustainable in the long run