NFT(非同质化代币)介绍
NFT(非同质化代币)介绍
NFT(Non-Fungible Token,非同质化代币)是一种特殊的区块链代币,每一个都是独一无二的,不可互换。
什么是 NFT
同质化 vs 非同质化
同质化代币(Fungible Token):
- 每个代币完全相同,可以互换
- 例如:1 ETH = 1 ETH,1 USDC = 1 USDC
- 就像纸币,一张 100 元和另一张 100 元没有区别
非同质化代币(Non-Fungible Token):
- 每个代币都是独一无二的
- 不可互换,不能等量替换
- 就像画作,蒙娜丽莎和向日葵各有其独特价值
NFT 的核心特性
- 唯一性:每个 NFT 有唯一的 tokenId
- 所有权可验证:区块链上公开记录所有权
- 不可伪造:加密技术保证真实性
- 可编程:支持版税、解锁内容等复杂逻辑
ERC721 标准
ERC721 是以太坊 NFT 的核心标准,定义了所有 NFT 合约必须实现的接口:
interface IERC721 { // 查询某地址持有的 NFT 数量 function balanceOf(address owner) external view returns (uint256);
// 查询某个 tokenId 的所有者 function ownerOf(uint256 tokenId) external view returns (address);
// 将 NFT 从 from 转移到 to function safeTransferFrom(address from, address to, uint256 tokenId) external; function transferFrom(address from, address to, uint256 tokenId) external;
// 授权某地址操作特定 NFT function approve(address to, uint256 tokenId) external;
// 授权某地址操作所有 NFT function setApprovalForAll(address operator, bool approved) external;
// 查询某 tokenId 的授权地址 function getApproved(uint256 tokenId) external view returns (address);
// 查询是否授权给某操作者 function isApprovedForAll(address owner, address operator) external view returns (bool);
// 关键事件 event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);}ERC721Metadata 扩展
interface IERC721Metadata { function name() external view returns (string memory); function symbol() external view returns (string memory);
// 返回 NFT 的元数据链接(通常是 IPFS 链接) function tokenURI(uint256 tokenId) external view returns (string memory);}ERC1155(多代币标准)
ERC1155 是改进版本,支持在同一合约中同时管理同质化和非同质化代币,更适合游戏场景:
// 一次批量转移多种代币function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, // 代币 ID 列表 uint256[] calldata amounts, // 对应数量列表 bytes calldata data) external;NFT 元数据
NFT 的元数据通常按 OpenSea 标准格式存储在 IPFS 上:
{ "name": "Cool Ape #1234", "description": "一只独特的猿猴,来自 Cool Apes 系列", "image": "ipfs://QmXxx.../1234.png", "external_url": "https://coolapes.io/1234", "attributes": [ { "trait_type": "背景", "value": "蓝色" }, { "trait_type": "眼睛", "value": "激光眼" }, { "trait_type": "帽子", "value": "派对帽", "rarity": "1%" }, { "display_type": "number", "trait_type": "战斗力", "value": 9527 } ]}上传到 IPFS
const { NFTStorage, File } = require('nft.storage');const fs = require('fs');
const client = new NFTStorage({ token: process.env.NFT_STORAGE_KEY });
async function uploadNFT(imagePath, metadata) { const imageData = fs.readFileSync(imagePath);
const nft = await client.store({ name: metadata.name, description: metadata.description, image: new File([imageData], 'image.png', { type: 'image/png' }), attributes: metadata.attributes });
console.log('IPFS CID:', nft.ipnft); console.log('元数据链接:', nft.url); return nft.url;}创建 NFT 合约
基础 NFT 合约
// SPDX-License-Identifier: MITpragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";import "@openzeppelin/contracts/access/Ownable.sol";import "@openzeppelin/contracts/utils/Counters.sol";
contract MyNFT is ERC721URIStorage, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIds;
uint256 public constant MAX_SUPPLY = 10000; uint256 public constant MINT_PRICE = 0.05 ether; string public baseTokenURI;
bool public saleActive = false;
event NFTMinted(address indexed to, uint256 indexed tokenId);
constructor(string memory _baseURI, address initialOwner) ERC721("My NFT Collection", "MNFT") Ownable(initialOwner) { baseTokenURI = _baseURI; }
// 开关公售 function toggleSale() external onlyOwner { saleActive = !saleActive; }
// 公开铸造 function mint(uint256 quantity) external payable { require(saleActive, "公售未开启"); require(quantity > 0 && quantity <= 10, "每次最多铸造10个"); require(_tokenIds.current() + quantity <= MAX_SUPPLY, "超过最大供应量"); require(msg.value >= MINT_PRICE * quantity, "ETH不足");
for (uint256 i = 0; i < quantity; i++) { _tokenIds.increment(); uint256 newId = _tokenIds.current(); _safeMint(msg.sender, newId); emit NFTMinted(msg.sender, newId); } }
// Owner 免费铸造(赠品/团队保留) function ownerMint(address to, uint256 quantity) external onlyOwner { require(_tokenIds.current() + quantity <= MAX_SUPPLY, "超过最大供应量"); for (uint256 i = 0; i < quantity; i++) { _tokenIds.increment(); _safeMint(to, _tokenIds.current()); } }
// 返回 tokenURI(元数据链接) function tokenURI(uint256 tokenId) public view override returns (string memory) { require(_exists(tokenId), "Token不存在"); return string(abi.encodePacked(baseTokenURI, Strings.toString(tokenId), ".json")); }
function totalSupply() public view returns (uint256) { return _tokenIds.current(); }
// 提款 function withdraw() external onlyOwner { payable(owner()).transfer(address(this).balance); }}带白名单的 NFT
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
contract WhitelistNFT is ERC721URIStorage, Ownable { bytes32 public merkleRoot; // Merkle 树根,用于验证白名单 mapping(address => bool) public whitelistClaimed;
// 设置白名单 Merkle Root function setMerkleRoot(bytes32 _root) external onlyOwner { merkleRoot = _root; }
// 白名单铸造 function whitelistMint(bytes32[] calldata proof) external { require(!whitelistClaimed[msg.sender], "已经铸造过");
// 验证 Merkle 证明 bytes32 leaf = keccak256(abi.encodePacked(msg.sender)); require(MerkleProof.verify(proof, merkleRoot, leaf), "无效的白名单证明");
whitelistClaimed[msg.sender] = true; // 铸造逻辑... }}带版税的 NFT(ERC2981)
import "@openzeppelin/contracts/token/common/ERC2981.sol";
contract RoyaltyNFT is ERC721URIStorage, ERC2981, Ownable { constructor(address initialOwner) ERC721("Royalty NFT", "RNFT") Ownable(initialOwner) { // 设置 5% 版税,给 owner _setDefaultRoyalty(initialOwner, 500); // 500 = 5% }
// ERC2981 版税查询(市场调用此接口支付版税) function royaltyInfo(uint256 tokenId, uint256 salePrice) public view override returns (address receiver, uint256 royaltyAmount) { // 每次二次销售,creator 获得 salePrice 的 5% return super.royaltyInfo(tokenId, salePrice); }}NFT 市场生态
主流 NFT 市场
| 市场 | 特点 | 费用 |
|---|---|---|
| OpenSea | 最大 NFT 市场,支持多链 | 2.5% |
| Blur | 专为交易者设计,聚合器 | 0.5% |
| Magic Eden | 多链市场,Solana 起家 | 2% |
| Foundation | 艺术家友好,策展质量高 | 5% |
| LooksRare | 代币激励交易 | 2% |
使用 OpenSea API
const { OpenSeaSDK, Chain } = require('opensea-js');const { ethers } = require('ethers');
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
const sdk = new OpenSeaSDK(wallet, { chain: Chain.Mainnet, apiKey: process.env.OPENSEA_API_KEY,});
// 查询 NFT 信息const asset = await sdk.api.getNFT( '0xContractAddress', '1234' // tokenId);
// 创建购买挂单const offer = await sdk.createBuyOrder({ asset: { tokenId: '1234', tokenAddress: '0xContractAddress', }, accountAddress: wallet.address, startAmount: 0.1, // 0.1 ETH});与 NFT 交互的前端代码
// React + wagmi 示例import { useReadContract, useWriteContract } from 'wagmi';import { erc721Abi } from 'viem';
const NFT_ADDRESS = '0xYourNFTAddress';
function NFTViewer({ tokenId }: { tokenId: bigint }) { const { data: owner } = useReadContract({ address: NFT_ADDRESS, abi: erc721Abi, functionName: 'ownerOf', args: [tokenId], });
const { data: tokenURI } = useReadContract({ address: NFT_ADDRESS, abi: erc721Abi, functionName: 'tokenURI', args: [tokenId], });
return ( <div> <p>Token #{tokenId.toString()}</p> <p>所有者: {owner}</p> <p>元数据: {tokenURI}</p> </div> );}
function MintButton() { const { writeContract, isPending } = useWriteContract();
return ( <button onClick={() => writeContract({ address: NFT_ADDRESS, abi: [...], // 你的 NFT ABI functionName: 'mint', args: [1n], // 铸造 1 个 value: BigInt(0.05e18), // 0.05 ETH })} disabled={isPending} > {isPending ? '铸造中...' : '铸造 NFT'} </button> );}NFT 应用场景
数字艺术
艺术家可以直接向全球买家销售数字作品,并通过版税机制从二次销售中持续获益。
游戏道具
游戏内物品作为 NFT,玩家真正拥有并可自由交易:
// 游戏道具 NFTcontract GameItem is ERC1155 { uint256 public constant SWORD = 1; uint256 public constant SHIELD = 2; uint256 public constant POTION = 3;
// 批量铸造装备 function craftItems(address player, uint256[] memory ids, uint256[] memory amounts) external { _mintBatch(player, ids, amounts, ""); }}音乐版权
音乐 NFT 允许粉丝购买版权份额,自动分配流媒体收益。
会员 & 访问权
NFT 作为会员证明,持有者享有特殊权益:
contract MembershipNFT is ERC721 { mapping(uint256 => MembershipTier) public tierOf;
enum MembershipTier { Bronze, Silver, Gold }
modifier onlyMember() { require(balanceOf(msg.sender) > 0, "必须持有会员NFT"); _; }
function accessPremiumContent() external onlyMember view returns (string memory) { return "这是独家内容..."; }}现实资产代币化(RWA)
房产、艺术品、股权等现实资产的链上表示。
NFT 市场趋势
- 数字艺术:Beeple 以 6900 万美元出售 NFT 画作
- PFP(头像)项目:CryptoPunks、BAYC 等蓝筹 NFT
- 链游(GameFi):Axie Infinity、Gods Unchained
- 音乐 NFT:Royal.io 等平台
- SBT(灵魂绑定代币):不可转让的身份/成就证明
总结
NFT 已经成为 Web3 生态的重要组成部分:
- 技术基础:ERC721/ERC1155 标准,存储在区块链上的所有权记录
- 元数据:通常存储在 IPFS,遵循 OpenSea 元数据标准
- 应用场景:数字艺术、游戏、音乐、会员、RWA 等
- 市场生态:OpenSea、Blur 等交易市场
进一步学习:
- ERC721 开发指南 - 深入学习 NFT 合约开发
- Foundry 教程 - 使用 Foundry 测试 NFT 合约