Lesson 4.4: Non-Fungible Tokens (NFTs) 🚀

Welcome back, digital pioneers! cool

In this lesson, we’re exploring the fascinating world of Non-Fungible Tokens (NFTs). We’ll dive into the ERC721 standard, learn how to create and manage NFTs, and develop a marketplace for trading them. Ready to create your own unique digital assets? Let’s get started!

Understanding the ERC721 Standard

The ERC721 standard defines a set of rules for creating non-fungible tokens (NFTs) on the Ethereum blockchain. Unlike ERC20 tokens, each ERC721 token is unique and can’t be exchanged on a one-to-one basis with another token.

1. Key Features of ERC721:

  • Uniqueness: Each token has a unique identifier.
  • Ownership: Allows for the tracking and transfer of ownership.
  • Metadata: Can include additional data like a link to an image or description.

2. ERC721 Interface: Here’s a simplified version of the ERC721 interface:

solidity

interface IERC721 {

event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

 

function balanceOf(address owner) external view returns (uint256 balance);

function ownerOf(uint256 tokenId) external view returns (address owner);

 

function transferFrom(address from, address to, uint256 tokenId) external;

function approve(address to, uint256 tokenId) external;

}

Creating and Managing NFTs

Let’s create our own NFT contract using OpenZeppelin’s implementation of the ERC721 standard.

1. Define Your NFT Contract:

solidity

pragma solidity ^0.8.0;

import “@openzeppelin/contracts/token/ERC721/ERC721.sol”;

import “@openzeppelin/contracts/access/Ownable.sol”;

contract ArtGalleryNFT is ERC721, Ownable {

uint256 public nextTokenId;

mapping(uint256 => string) private _tokenURIs;

constructor() ERC721(“ArtGalleryNFT”, “ART”) {}

function mint(string memory tokenURI) public onlyOwner {

uint256 tokenId = nextTokenId;

_safeMint(msg.sender, tokenId);

_setTokenURI(tokenId, tokenURI);

nextTokenId++;

}

function _setTokenURI(uint256 tokenId, string memory tokenURI) internal virtual {

_tokenURIs[tokenId] = tokenURI;

}

 

function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {

return _tokenURIs[tokenId];

}

}

Explanation:

  • ERC721: Inherits the standard ERC721 functionality.
  • Ownable: Adds ownership control to the contract.
  • mint Function: Allows the contract owner to mint new tokens with a specified URI.
  • _setTokenURI and tokenURI Functions: Manage and retrieve the metadata for each token.

Developing a Marketplace for Trading NFTs

Now, here’s a fun project! Let’s create a simple marketplace contract where users can buy and sell NFTs.

1. Define Your Marketplace Contract:

solidity

pragma solidity ^0.8.0;

import “@openzeppelin/contracts/token/ERC721/IERC721.sol”;

import “@openzeppelin/contracts/access/Ownable.sol”;

contract NFTMarketplace is Ownable {

struct Listing {

address seller;

uint256 price;

}

mapping(address => mapping(uint256 => Listing)) public listings;

event TokenListed(address indexed nftContract, uint256 indexed tokenId, address indexed seller, uint256 price);

event TokenPurchased(address indexed nftContract, uint256 indexed tokenId, address indexed buyer);

function listToken(address nftContract, uint256 tokenId, uint256 price) public {

IERC721 nft = IERC721(nftContract);

require(nft.ownerOf(tokenId) == msg.sender, “Not the token owner”);

require(nft.getApproved(tokenId) == address(this), “Marketplace not approved”);

listings[nftContract][tokenId] = Listing(msg.sender, price);

emit TokenListed(nftContract, tokenId, msg.sender, price);

}

function purchaseToken(address nftContract, uint256 tokenId) public payable {

Listing memory listing = listings[nftContract][tokenId];

require(msg.value == listing.price, “Incorrect price”);

IERC721(nftContract).transferFrom(listing.seller, msg.sender, tokenId);

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

delete listings[nftContract][tokenId];

 

emit TokenPurchased(nftContract, tokenId, msg.sender);

}

}

Explanation:

  • Listing Struct: Stores the seller’s address and the token price.
  • listToken Function: Allows users to list their NFTs for sale.
  • purchaseToken Function: Enables users to purchase listed NFTs.

 

Real-World Example: Building an NFT-Based Art Gallery

Let’s put it all together and build an NFT-based art gallery where users can mint, buy, and sell unique digital artworks.

 

  1. Deploy Your NFT Contract: Deploy your ArtGalleryNFT contract and mint a few NFTs with unique URIs.
  2. Deploy Your Marketplace Contract: Deploy your NFTMarketplace contract and list the minted NFTs for sale.
  3. Interact with the Marketplace: Use the marketplace functions to list and purchase NFTs, simulating a digital art gallery where users can trade unique artworks.