5月28日 04:09

以太坊 NFT 的 ERC-721 和 ERC-1155 有什么区别?

以太坊 NFT(非同质化代币)是部署在以太坊区块链上的独特数字资产,每个代币绑定一个全局唯一的 tokenId,不可互换也不可分割。以太坊上有两个主流 NFT 标准:ERC-721 和 ERC-1155。ERC-721 一个合约只管理一种 NFT,每次只能转移一个代币;ERC-1155 一个合约可以同时管理同质化和非同质化代币,支持批量转账,Gas 成本节省约 70%。选型依据:单一艺术品收藏用 ERC-721,游戏道具、多类型资产体系用 ERC-1155。

追问

ERC-721 和 ERC-1155 有什么区别?

维度ERC-721ERC-1155
代币类型仅非同质化同质化 + 非同质化 + 半同质化
批量转账不支持,一次转一个支持 safeBatchTransferFrom
合约关系一个合约 = 一个 NFT 集合一个合约可管理多种代币
Gas 成本高(每笔 6-10 万 gas)低(批量操作省约 70%)
互转能力NFT 之间不可互转FT 和 NFT 可互转
市场兼容OpenSea 等全平台支持主流市场均已支持

早期项目(CryptoPunks 除外,它甚至不符合任何标准)几乎都用 ERC-721,因为 OpenSea 等市场初期只兼容 721。现在主流 NFT 市场对两个标准都已支持,Enjin、Decentraland 等游戏类项目早已转向 ERC-1155。

ERC-721 的核心接口有哪些?为什么这么设计?

四个核心函数:ownerOf(tokenId) 查所有者、balanceOf(owner) 查持有数量、transferFrom / safeTransferFrom 转移所有权、approve / setApprovalForAll 授权。

设计逻辑:每个 tokenId 全局唯一且绑定一个 owner,这就是"非同质化"的来源——不存在两个 tokenId 相同的代币。safeTransferFromtransferFrom 多了一步:调用接收方的 onERC721Received 回调,确认对方能处理 NFT。没有这步,代币可能被转进一个没有提取函数的合约,永远锁死。历史上因为漏用 safe 版本导致资产损失的案例不少。

什么时候用 ERC-721,什么时候用 ERC-1155?

单一类型资产、1/1 艺术品、收藏品——ERC-721 更简单直接,生态工具链也更成熟。多类型资产体系(游戏装备分武器/防具/消耗品、赛事门票分 VIP/普通/团体)——ERC-1155 一份合约搞定,省 Gas 又省部署成本。

踩坑经验:ERC-1155 的 tokenId 语义完全由开发者自定义,合约 A 的 tokenId=1 代表武器,合约 B 的 tokenId=1 可能代表门票。跨合约交互时必须检查 tokenId 的上下文含义,否则会转错资产。

NFT 元数据存在哪里?tokenURI 返回什么?

tokenURI 返回一个指向 JSON 文件的 URI,JSON 包含 namedescriptionimageattributes 等字段。存储方式两种:

  • 链下存储(主流):IPFS 或 Arweave,tokenURI 返回 ipfs://QmHash... 格式。优点是 Gas 低,缺点是依赖外部存储可用性。
  • 链上存储:直接在合约里存 base64 编码的 JSON,tokenURI 返回 data:application/json;base64,...。优点是元数据永不丢失,缺点是 Gas 成本极高,只适合小体量项目。

面试加分点:提一下 ERC-2981 版税标准——它定义了 royaltyInfo() 接口,让市场自动计算创作者版税,OpenSea 和 LooksRare 都已支持。

ERC-1155 的批量操作是怎么省 Gas 的?

ERC-721 转 10 个 NFT = 10 笔独立交易,每笔执行完整的 transfer 逻辑。ERC-1155 的 safeBatchTransferFrom 在一笔交易里转多个代币,共享一笔基础 Gas(约 21000),每个代币只附加少量存储读写开销。批量铸造 mintBatch 同理,一次调用铸造多种代币,省掉多次合约调用的固定开销。

实测数据:批量转 10 个代币,ERC-721 约 60-100 万 Gas,ERC-1155 约 8-15 万 Gas,差距 5-10 倍。这就是为什么游戏类项目几乎都用 ERC-1155——玩家一次性装备/卸下多件道具是高频操作。

写段代码

ERC-721 铸造关键片段:

solidity
function safeMint(address to, string memory uri) public onlyOwner { uint256 tokenId = _tokenIdCounter.current(); _tokenIdCounter.increment(); _safeMint(to, tokenId); _setTokenURI(tokenId, uri); }

ERC-1155 批量铸造:

solidity
function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) public onlyOwner { _mintBatch(to, ids, amounts, data); }
标签:以太坊