NFT 与数字资产标准:ERC-721、ERC-1155 与元数据存储

深入解析 NFT 的核心标准 ERC-721 与 ERC-1155 的设计差异、元数据存储方案(IPFS/Arweave/链上)、铸造机制及主流 NFT 协议架构。

NFT(Non-Fungible Token,非同质化代币)是区块链上独一无二的数字资产标准。从数字艺术到游戏道具、从域名到房地产代币化,NFT 打开了数字所有权的新范式。本文将详解核心标准、元数据存储方案以及铸造和交易机制。

一、ERC-721:唯一的 NFT 标准

接口定义

interface IERC721 {
    // 余额与所有权
    function balanceOf(address owner) external view returns (uint256);
    function ownerOf(uint256 tokenId) external view returns (address);
    
    // 授权管理
    function approve(address to, uint256 tokenId) external;
    function getApproved(uint256 tokenId) external view returns (address);
    function setApprovalForAll(address operator, bool approved) external;
    function isApprovedForAll(address owner, address operator) external view returns (bool);
    
    // 转账
    function transferFrom(address from, address to, uint256 tokenId) external;
    function safeTransferFrom(address from, address to, uint256 tokenId) external;
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
    
    // 事件
    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);
}

💡 indexed 参数最多 3 个。Token ID 被索引意味着前端可以通过 ID 直接过滤相关事件。

safeTransferFrom 为什么重要?

function safeTransferFrom(address from, address to, uint256 tokenId) external {
    transferFrom(from, to, tokenId);
    // 检查接收方是否为合约,且是否实现了 IERC721Receiver
    require(
        to.code.length == 0 ||  // EOA
        IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, "") == 
            IERC721Receiver.onERC721Received.selector,
        "Transfer to non ERC721Receiver implementer"
    );
}

若向未实现 onERC721Received 的合约转账 NFT,资产将永久锁定。safeTransfer 强制要求接收合约正确处理 NFT。

OpenZeppelin 实现骨架

// 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 mint(address to, string memory uri) external onlyOwner returns (uint256) {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
        return tokenId;
    }
    
    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }
    
    function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
        return super.tokenURI(tokenId);
    }
}

二、ERC-1155:多代币标准

为什么需要 ERC-1155?

游戏中一个角色可能有:

  • 1 把独一无二的剑(ERC-721)
  • 100 个同样的药水(ERC-20)
  • 5 个同样的盔甲(半同质化)

为每种资产部署单独合约成本极高。ERC-1155 允许一个合约管理多种代币(每种代币有自己的 ID 和供应量)。

interface IERC1155 {
    // 查询余额
    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;
    
    // 元数据
    function uri(uint256 id) external view returns (string memory);
}

ERC-721 vs ERC-1155

特性ERC-721ERC-1155
供应量每个 Token ID = 1每个 Token ID 可任意数量
Gas 效率单代币低批量操作极优( batchTransfer )
元数据tokenURI()uri()(统一或差异化)
半同质化支持否是
适用场景艺术品、PFP、域名游戏资产、门票、会员

三、元数据存储方案

元数据 JSON 标准

{
    "name": "CryptoKitty #12345",
    "description": "A cute crypto-collectible cat",
    "image": "ipfs://QmXYZ.../image.png",
    "attributes": [
        { "trait_type": "Background", "value": "Blue" },
        { "trait_type": "Eyes", "value": "Green", "display_type": "number" },
        { "trait_type": "Level", "value": 5, "max_value": 10 }
    ],
    "animation_url": "ipfs://QmABC.../animation.glb",
    "external_url": "https://example.com/nft/12345"
}

存储方案对比

方案持久性成本去中心化速度适用场景
IPFS高(需 pin)低是(但 gateway 可审查)中等主流选择
Arweave永久一次性付费是中等高价值资产
链上永久极高完全快小数据 / 核心逻辑
中心化服务器依赖服务商低否快不推荐
Filecoin高低是较慢IPFS 的激励层

IPFS 实践

# 安装 IPFS CLI
ipfs init
ipfs daemon

# 上传文件
ipfs add image.png
# 返回: added QmXYZ... image.png

# 固定(pin)到 Pinata/Infura 防止垃圾回收
curl -X POST "https://api.pinata.cloud/pinning/pinByHash" \
  -H "Authorization: Bearer YOUR_JWT" \
  -d '{"hashToPin":"QmXYZ..."}'
// 合约中使用 IPFS 链接
function tokenURI(uint256 tokenId) public view override returns (string memory) {
    return string(abi.encodePacked("ipfs://", _tokenCIDs[tokenId]));
}

💡 链上只需存储固定的 IPFS CID(Qm...),实际内容通过 IPFS 网关(如 https://ipfs.io/ipfs/Qm...)或自己的节点获取。

四、铸造(Mint)机制

常见铸造模式

模式描述Gas 成本代表项目
Owner Mint项目方铸造后分发给用户项目方承担大多数 PFP
用户 Mint用户支付 Gas 自行铸造用户承担大多数生成艺术
Lazy Mint用户购买时代币才上链买家承担OpenSea shared store
Airdrop项目方主动分发项目方承担社区空投
Free Claim满足条件的用户免费铸造用户承担 Gas社区奖励

可验证随机性(链上生成)

对于生成艺术 NFT,需要链上随机决定属性:

import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";

contract RandomNFT is VRFConsumerBaseV2 {
    function requestRandomWords() external returns (uint256 requestId) {
        return COORDINATOR.requestRandomWords(
            keyHash, subscriptionId, requestConfirmations, callbackGasLimit, numWords
        );
    }
    
    function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
        uint256 tokenId = requestIdToTokenId[requestId];
        // 使用随机数生成属性
        uint256 random = randomWords[0];
        attributes[tokenId] = Attribute({
            background: random % 10,
            eyes: (random / 10) % 10,
            ...
        });
    }
}

⚠️ 绝不要使用 block.timestamp、block.number 或 blockhash 作为随机源——矿工/验证人可操纵。

五、Soulbound Tokens(SBT)与可编程 NFT

SBT 的概念与设计

Soulbound Token(灵魂绑定代币)由 Vitalik Buterin 等人提出,核心特征是不可转让——一旦铸造到某个地址,便永久绑定,无法交易或转移。SBT 适用于身份证明、学历证书、成就徽章、社区成员资格等场景,构建去中心化社会(DeSoc)的信任基础。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

abstract contract Soulbound is ERC721 {
    error SoulboundTransferNotAllowed();
    
    function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) 
        internal virtual override {
        super._beforeTokenTransfer(from, to, tokenId, batchSize);
        // 仅允许铸造(from == address(0))和销毁(to == address(0))
        if (from != address(0) && to != address(0)) {
            revert SoulboundTransferNotAllowed();
        }
    }
}

⚠️ SBT 的实现需注意:如果被授权的合约(如质押合约)需要临时持有 NFT,传统 SBT 会阻断其功能性。更灵活的方案是限时委托(delegation)或灵魂绑定不可转让但可委托的混合模型。

可编程 NFT 的扩展方向

方向实现机制典型标准
动态元数据链上/链外数据驱动 tokenURI 变化ERC-721 + 预言机
可租赁 NFT分离所有权和使用权ERC-4907 (租赁)
碎片化将单个 NFT 拆分为可交易的 ERC-20 份额ERC-1155 / 自定义
跨链流通通过锁定-铸造机制跨链转移LayerZero / Axelar
可组合性NFT 包含其他 NFT 或 ERC-20EIP-5606 / EIP-6059

六、NFT 市场与版税

版税机制(EIP-2981)

interface IERC2981 {
    function royaltyInfo(uint256 tokenId, uint256 salePrice) 
        external view returns (address receiver, uint256 royaltyAmount);
}

// 实现示例:固定 5% 版税
function royaltyInfo(uint256, uint256 salePrice) external view 
    returns (address receiver, uint256 royaltyAmount) {
    return (artistAddress, salePrice * 500 / 10000);  // 5%
}

⚠️ 注意:OpenSea、Blur 等市场已逐步转向可选版税模式,EIP-2981 的强制执行能力在减弱。

主要市场对比

市场特点版税策略
OpenSea最大综合市场可选版税,最低 0.5%
Blur专业交易者零平台费、可选版税
Magic EdenSolana/多链可选
Foundation高端艺术强制版税 10%
Zora创作者优先低费用、模块化

Gas 优化策略

批量铸造场景下 Gas 成本是项目方和用户的核心痛点:

  1. 批量操作(Batch Mint):ERC-1155 天然支持 mintBatch,一次性铸造多种代币仅需单次交易。
  2. 压缩元数据:链上存储 JSON 的 base64 编码版本,减少 SSTORE 操作数。
  3. 克隆工厂模式(Minimal Proxy / Beacon Proxy):使用 EIP-1167 克隆合约降低部署成本,每个 NFT 系列无需完整部署逻辑合约。
  4. 优化存储布局:将 uri 从字符串改为 bytes32 映射(SHA256 hash),仅在需要时通过链外解析。
  5. 利用预编译合约:批量验签 ecrecover 比循环调用单签便宜一个数量级,适合白名单铸造场景。
优化手段典型节省适用场景
批量铸造(ERC-1155)50-70%游戏道具、门票
克隆工厂部署90%+多系列 NFT
Merkle Tree 白名单60%+大规模白名单
Lazy Minting100%(铸造侧)市场平台
Calldata 优化20-30%所有场景

六、本章小结

NFT 标准从 ERC-721 的唯一性到 ERC-1155 的高效批量管理,为数字资产提供了坚实的技术基础。元数据存储是 NFT 持久性的关键——IPFS 是当前的主流选择,而 Arweave 则为高价值资产提供了永久存储方案。在开发 NFT 项目时,随机性来源的安全性、Gas 优化(特别是 ERC-1155 的批量操作)以及市场兼容性都是需要重点考虑的因素。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「blockchain」更多文章

  1. DePIN 去中心化物理基础设施网络
  2. DeFi 衍生品:期权、永续合约与合成资产
  3. 智能合约形式化验证:Certora 与 K 框架