Create Your Own NFT Marketplace with Solidity and React.js
Rajeev Sharma
Enabler | Building production-ready AI / ML products | (We’re hiring!)
Non-Fungible Tokens (NFTs) are unique digital assets that have gained popularity over the years due to their ability to represent ownership of a digital item like art, music, and more. These tokens are based on blockchain technology and are stored in a decentralized ledger. This post will teach us how to create an NFT marketplace from scratch using React.js and blockchain.
Understanding the Basics
Before building the NFT marketplace (ERC721), it's important to understand the basics of blockchain development, NFTs, and smart contracts. Smart contracts are self-executing contracts with the terms of the agreement between buyer and seller being directly written into code. Blockchain technology ensures that these contracts are immutable (non-changing), transparent, and decentralized.
Building the Blockchain
The first step in building an NFT marketplace is building the blockchain. The blockchain will be used to store the NFT contract and the marketplace contract. We will use the Hardhat framework to run a local blockchain for testing. Once the blockchain is set up, we will write the smart contracts and tests to ensure functionality.
Creating NFTs and the Marketplace
Next, we will create the NFT contract that tracks ownership and enables transfers to new addresses. We will also create the marketplace contract that interacts with the NFT contract to allow buying and selling and stores information about the seller and price. The marketplace UI will be developed using React.js, which connects to the blockchain and displays NFT and purchase information.
Minting NFTs
To create new NFTs, we will use a constructor function and the external keyword. Solidity is a statically typed language, so we will declare variables with data types. The Hardhat console can interact with smart contracts—fetching relevant details about them if needed.
// SPDX-License-Identifier: M
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
contract MyNFT is ERC721 {
using SafeMath for uint256;
mapping (uint256 => address) private _tokenOwner;
mapping (uint256 => address) private _tokenApprovals;
constructor() ERC721("MyNFT", "NFT") {}
function mint(address to, uint256 tokenId) public {
_mint(to, tokenId);
_tokenOwner[tokenId] = to;
}
function transfer(address from, address to, uint256 tokenId) public {
require(_isApprovedOrOwner(msg.sender, tokenId), "You are not authorized to transfer this NFT");
require(from == _tokenOwner[tokenId], "The current owner of this NFT is not the same as the specified address");
_transfer(from, to, tokenId);
_tokenOwner[tokenId] = to;
}I
Automated Testing
Automated testing is crucial for smart contract developers. We will use the Hardhat framework to write tests using JavaScript—and the Waffle testing framework, allowing us to compare our code's return values against expected values.
Hardhat is a popular development environment for building, testing, and debugging smart contracts on the Ethereum blockchain using JavaScript. In addition to built-in support for popular testing frameworks like Mocha and Chai, it also provides an isolated test network that can be used as a staging ground before deploying code onto production networks.
To start writing tests using Hardhat and JavaScript, follow these steps:
const { expect } = require("chai");
const { ethers } = require("hardhat");
3. Define your test cases using the Mocha testing framework. For example, you might include the following code:
领英推荐
describe("My Contract", function() {
? it("Should deploy the contract", async function() {
? ? const MyContract = await ethers.getContractFactory("MyContract");
? ? const myContract = await MyContract.deploy();
? ? await myContract.deployed();
? ? expect(await myContract.owner()).to.equal(owner.address);
? });
});
4. Run your tests using the Hardhat command line tool. For example, you might run the following command:
npx hardhat test
This will run all the test files in your "test" folder and display the results in the console.
Building the Marketplace Contract
We will build the marketplace contract to keep track of NFTs listed for sale. We will define an item struct to store data associated with each one—its name, description, and so on —and create a mapping between items (with their ID as keys)
Implementing Purchase Item Function
Next, we will implement the purchase item function on the marketplace contract. We will create a function to calculate the item's total price, including fees. We will implement the purchase item function with checks, payments to the seller, and fee account, update the item as sold, and transfer the NFT to the buyer.
function purchaseItem(uint256 _itemId) external payable {
? ? Item storage item = items[_itemId];
? ? require(item.status == ItemStatus.Available, "Item is not available");
? ? require(msg.value >= calculateTotalPrice(item.price), "Insufficient payment");
? ? address seller = item.seller;
? ? uint256 fee = calculateFee(item.price);
? ? uint256 sellerPayment = item.price - fee;
? ? // Transfer payment to seller
? ? payable(seller).transfer(sellerPayment);
? ? // Transfer fee to fee account
? ? payable(feeAccount).transfer(fee);
? ? // Mark item as sold
? ? item.status = ItemStatus.Sold;
? ? item.buyer = msg.sender;
? ? // Transfer NFT to buyer
? ? nftContract.safeTransferFrom(seller, msg.sender, item.tokenId);
}
function calculateTotalPrice(uint256 _price) internal view returns (uint256) {
? ? uint256 gasFee = tx.gasprice * GAS_USED;
? ? return _price + gasFee;
}
function calculateFee(uint256 _price) internal pure returns (uint256) {
? ? return (_price * FEE_PERCENT) / 100;
}
In this example, the purchaseItem function takes an _itemId parameter and checks that the item is available for purchase and that the buyer has sent enough payment to cover the total price, including gas fees. It then calculates the seller's payment and fee, updates the item's status to "Sold", and transfers the NFT to the buyer using the OpenZeppelin safeTransferFrom function.
The calculateTotalPrice function calculates the total price, including gas fees, based on the item's price and the current gas price multiplied by a fixed amount of gas used.
The calculateFee function calculates the fee charged as a percentage of the item's price, which is assumed to be a fixed value defined elsewhere in the contract.
Creating the Marketplace UI
Finally, we will create the NFT marketplace UI using React.js. We will set up URL links in the navbar with the Link component and create different routes for the app using the Route component from React Router DOM. We will build the marketplace UI by creating a function to load unsold items and allow users to buy them. We will also build out the create component to allow users to create and list their own NFTs for sale.
function App() {
const [items, setItems] = useState([]);
const [account, setAccount] = useState('');
const [nftContract, setNFTContract] = useState(null);
const [marketplaceContract, setMarketplaceContract] = useState(null);
useEffect(() => {
async function init() {
const accounts = await web3.eth.getAccounts();
setAccount(accounts[0]);
const nftContract = new web3.eth.Contract(NFTContract.abi, NFTContract.address);
setNFTContract(nftContract);
const marketplaceContract = new web3.eth.Contract(MarketplaceContract.abi, MarketplaceContract.address);
setMarketplaceContract(marketplaceContract);
const items = await loadItems(marketplaceContract);
setItems(items);
}
init();
}, []);
async function loadItems(marketplaceContract) {
const totalItems = await marketplaceContract.methods.getTotalItems().call();
const items = [];
for (let i = 1; i <= totalItems; i++) {
const item = await marketplaceContract.methods.getItem(i).call();
if (item.status === 'Available') {
items.push(item);
}
}
return items;
}
async function createListing(tokenId, price) {
const priceInWei = web3.utils.toWei(price, 'ether');
await nftContract.methods.approve(MarketplaceContract.address, tokenId).send({ from: account });
await marketplaceContract.methods.createItem(tokenId, priceInWei).send({ from: account });
const items = await loadItems(marketplaceContract);
setItems(items);
}
async function purchaseItem(itemId, price) {
const priceInWei = web3.utils.toWei(price, 'ether');
await marketplaceContract.methods.purchaseItem(itemId).send({ from: account, value: priceInWei });
const items = await loadItems(marketplaceContract);
setItems(items);
}
Conclusion
In conclusion, building an NFT marketplace requires a good understanding of blockchain development, NFTs, and smart contracts. Following the steps outlined in this post, you can learn to create an NFT marketplace from scratch using React.js and blockchain. Would love to know your thoughts!