乐闻世界logo
搜索文章和话题

What is an Ethereum NFT (Non-Fungible Token)? Please explain ERC-721 and ERC-1155 standards

2月21日 14:16

Ethereum NFTs (Non-Fungible Tokens) are unique digital assets on blockchain, each NFT has a unique identifier. Here's a comprehensive analysis of NFTs:

Basic Concepts of NFTs

NFT (Non-Fungible Token) means non-fungible token, each token is unique and cannot be interchanged. Unlike ERC-20 fungible tokens, NFTs represent unique assets.

ERC-721 Standard

1. ERC-721 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); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); function balanceOf(address owner) external view returns (uint256 balance); function ownerOf(uint256 tokenId) external view returns (address owner); function safeTransferFrom(address from, address to, uint256 tokenId) external; function transferFrom(address from, address to, uint256 tokenId) external; function approve(address to, uint256 tokenId) external; function getApproved(uint256 tokenId) external view returns (address operator); function setApprovalForAll(address operator, bool _approved) external; function isApprovedForAll(address owner, address operator) external view returns (bool); }

2. Metadata Extension

solidity
interface IERC721Metadata is IERC721 { function name() external view returns (string memory); function symbol() external view returns (string memory); function tokenURI(uint256 tokenId) external view returns (string memory); }

3. Complete NFT Implementation

solidity
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract MyNFT is ERC721, ERC721URIStorage, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIdCounter; constructor() ERC721("MyNFT", "MNFT") {} function safeMint(address to, string memory uri) public onlyOwner { uint256 tokenId = _tokenIdCounter.current(); _tokenIdCounter.increment(); _safeMint(to, tokenId); _setTokenURI(tokenId, uri); } function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); } function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) { return super.supportsInterface(interfaceId); } }

ERC-1155 Standard

1. Multi-Token Standard

ERC-1155 supports both fungible and non-fungible tokens, saving more Gas.

solidity
interface IERC1155 { event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values); event ApprovalForAll(address indexed account, address indexed operator, bool approved); event URI(string value, uint256 indexed id); function balanceOf(address account, uint256 id) external view returns (uint256); function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory); function setApprovalForAll(address operator, bool approved) external; function isApprovedForAll(address account, address operator) external view returns (bool); function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external; function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external; }

2. ERC-1155 Implementation

solidity
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract MyMultiToken is ERC1155, Ownable { constructor() ERC1155("") {} function mint(address account, uint256 id, uint256 amount, bytes memory data) public onlyOwner { _mint(account, id, amount, data); } function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) public onlyOwner { _mintBatch(to, ids, amounts, data); } }

NFT Metadata

1. Metadata Standard

NFT metadata is usually stored in JSON format on decentralized storage like IPFS or Arweave.

json
{ "name": "My Awesome NFT #1", "description": "This is my awesome NFT", "image": "ipfs://QmHash...", "attributes": [ { "trait_type": "Background", "value": "Blue" }, { "trait_type": "Rarity", "value": "Legendary" } ] }

2. Dynamic Metadata

solidity
contract DynamicNFT is ERC721 { struct TokenMetadata { string name; uint256 level; uint256 experience; } mapping(uint256 => TokenMetadata) public tokenMetadata; function gainExperience(uint256 tokenId, uint256 exp) public { require(ownerOf(tokenId) == msg.sender, "Not owner"); tokenMetadata[tokenId].experience += exp; // Upgrade logic if (tokenMetadata[tokenId].experience >= getRequiredExp(tokenMetadata[tokenId].level)) { tokenMetadata[tokenId].level++; } } function getRequiredExp(uint256 level) public pure returns (uint256) { return level * 100; } }

NFT Use Cases

1. Digital Art

solidity
contract DigitalArt is ERC721 { struct Artwork { address artist; string metadataURI; uint256 price; } mapping(uint256 => Artwork) public artworks; uint256 public artworkCount; function createArtwork(string memory metadataURI, uint256 price) public { uint256 tokenId = artworkCount; _safeMint(msg.sender, tokenId); artworks[tokenId] = Artwork({ artist: msg.sender, metadataURI: metadataURI, price: price }); artworkCount++; } function buyArtwork(uint256 tokenId) public payable { Artwork storage artwork = artworks[tokenId]; require(msg.value >= artwork.price, "Insufficient payment"); payable(artwork.artist).transfer(msg.value); _transfer(artwork.artist, msg.sender, tokenId); } }

2. Game Items

solidity
contract GameItems is ERC1155 { struct Item { string name; uint256 power; uint256 rarity; } mapping(uint256 => Item) public items; constructor() { items[1] = Item("Sword", 100, 1); items[2] = Item("Shield", 80, 1); items[3] = Item("Legendary Sword", 200, 3); } function craftItem(uint256 itemId) public { require(items[itemId].rarity > 0, "Invalid item"); _mint(msg.sender, itemId, 1, ""); } function equipItem(uint256 itemId) public { require(balanceOf(msg.sender, itemId) > 0, "Don't own item"); // Equipment logic } }

3. Tickets and Identity

solidity
contract EventTickets is ERC721 { struct Ticket { string eventName; uint256 eventDate; address attendee; bool used; } mapping(uint256 => Ticket) public tickets; function mintTicket(address to, uint256 tokenId, string memory eventName, uint256 eventDate) public { _safeMint(to, tokenId); tickets[tokenId] = Ticket({ eventName: eventName, eventDate: eventDate, attendee: to, used: false }); } function useTicket(uint256 tokenId) public { require(ownerOf(tokenId) == msg.sender, "Not owner"); require(!tickets[tokenId].used, "Already used"); require(block.timestamp < tickets[tokenId].eventDate, "Event expired"); tickets[tokenId].used = true; } }

NFT Marketplace

1. Simple Marketplace

solidity
contract NFTMarketplace { struct Listing { address seller; uint256 price; bool active; } IERC721 public nft; mapping(uint256 => Listing) public listings; event Listed(uint256 indexed tokenId, address indexed seller, uint256 price); event Sold(uint256 indexed tokenId, address indexed seller, address indexed buyer, uint256 price); constructor(address _nft) { nft = IERC721(_nft); } function listItem(uint256 tokenId, uint256 price) public { require(nft.ownerOf(tokenId) == msg.sender, "Not owner"); nft.transferFrom(msg.sender, address(this), tokenId); listings[tokenId] = Listing({ seller: msg.sender, price: price, active: true }); emit Listed(tokenId, msg.sender, price); } function buyItem(uint256 tokenId) public payable { Listing storage listing = listings[tokenId]; require(listing.active, "Not listed"); require(msg.value >= listing.price, "Insufficient payment"); listing.active = false; nft.transferFrom(address(this), msg.sender, tokenId); payable(listing.seller).transfer(msg.value); emit Sold(tokenId, listing.seller, msg.sender, msg.value); } }

2. Auction

solidity
contract NFTAuction { struct Auction { address seller; uint256 startPrice; uint256 highestBid; address highestBidder; uint256 endTime; bool ended; } IERC721 public nft; mapping(uint256 => Auction) public auctions; function createAuction(uint256 tokenId, uint256 startPrice, uint256 duration) public { require(nft.ownerOf(tokenId) == msg.sender, "Not owner"); nft.transferFrom(msg.sender, address(this), tokenId); auctions[tokenId] = Auction({ seller: msg.sender, startPrice: startPrice, highestBid: startPrice, highestBidder: address(0), endTime: block.timestamp + duration, ended: false }); } function bid(uint256 tokenId) public payable { Auction storage auction = auctions[tokenId]; require(!auction.ended, "Auction ended"); require(block.timestamp < auction.endTime, "Auction expired"); require(msg.value > auction.highestBid, "Bid too low"); if (auction.highestBidder != address(0)) { payable(auction.highestBidder).transfer(auction.highestBid); } auction.highestBid = msg.value; auction.highestBidder = msg.sender; } function endAuction(uint256 tokenId) public { Auction storage auction = auctions[tokenId]; require(!auction.ended, "Already ended"); require(block.timestamp >= auction.endTime, "Auction not ended"); auction.ended = true; nft.transferFrom(address(this), auction.highestBidder, tokenId); payable(auction.seller).transfer(auction.highestBid); } }

NFT Security

1. Prevent Duplicate Minting

solidity
contract SecureNFT is ERC721 { mapping(uint256 => bool) public mintedTokenIds; function safeMint(address to, uint256 tokenId) public onlyOwner { require(!mintedTokenIds[tokenId], "Already minted"); mintedTokenIds[tokenId] = true; _safeMint(to, tokenId); } }

2. Approval Security

solidity
contract SafeNFT is ERC721 { mapping(address => bool) public trustedContracts; function setTrustedContract(address contractAddress, bool trusted) public onlyOwner { trustedContracts[contractAddress] = trusted; } function safeApprove(address to, uint256 tokenId) public override { require(trustedContracts[to] || to == address(0), "Untrusted contract"); super.approve(to, tokenId); } }

Best Practices

  1. Use OpenZeppelin Library: Avoid reinventing the wheel
  2. Metadata Storage: Use decentralized storage like IPFS
  3. Gas Optimization: Use ERC-1155 for batch operations
  4. Security Audit: Conduct audits before deployment
  5. User Experience: Provide clear metadata and previews
  6. Copyright Protection: Clearly define NFT copyright and usage terms

NFTs are changing the concept of digital asset ownership, bringing new possibilities to art, gaming, collectibles, and other fields.

标签:以太坊