ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

WTF Solidity 极简教程:第 40 讲 ERC1155 多代币标准与 BAYC1155 实战

2026/9/14 23:26:52 拓冰建站 浏览量
WTF Solidity 极简教程:第 40 讲 ERC1155 多代币标准与 BAYC1155 实战 WTF Solidity 极简教程第 40 讲 ERC1155 多代币标准与 BAYC1155 实战【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity导读本文基于 WTF-Solidity 仓库第 40 讲的内容系统讲解以太坊EIP-1155提出的多代币标准ERC1155一个合约如何同时容纳同质化与非同质化的多种代币以及它的接口、事件、函数、接收合约与完整实现。文中还给出了仓库中的实战案例BAYC1155——一个包含 10,000 种代币、元数据与 BAYC 完全一致的免费铸造合约并演示了在 Remix 中的部署与铸造全流程。读完本文你将掌握ERC1155的核心设计、IERC1155/IERC1155Receiver接口的每一个函数与事件并能基于仓库源码独立实现一个多代币合约。EIP1155为什么需要多代币标准ERC20与ERC721标准都有一个共同特点一个合约只对应一种代币。设想在以太坊上做一款类似《魔兽世界》的大型游戏每一件装备都需要部署一个合约数千件装备就意味着数千个合约部署与管理工作极其繁琐。为此Ethereum EIP1155 提出了多代币标准ERC1155允许一个合约包含多种同质化与非同质化代币。ERC1155在 GameFi 领域被广泛使用Decentraland、The Sandbox 等知名链游均采用该标准。简单类比之前介绍的 ERC721在ERC721中每个tokenId是唯一标识一个tokenId只对应一个代币在ERC1155中每个种类的代币用id作为唯一标识一个id对应一种代币。这样多种代币可以在同一个合约中异构管理且每种代币都有一个 URLuri用于存放元数据类似于ERC721中的tokenURI。下面是ERC1155的元数据接口合约IERC1155MetadataURI完整源码见 IERC1155MetadataURI.sol/** * dev 可选的 ERC1155 接口加入 uri() 函数查询元数据 */ interface IERC1155MetadataURI is IERC1155 { /** * dev 返回第 id 种类代币的 URI */ function uri(uint256 id) external view returns (string memory); }如何判断ERC1155中某一种代币是同质化还是非同质化规则很简单某个id对应代币的总量为 1则为非同质化代币类似ERC721某个id对应代币的总量大于 1则为同质化代币因为这些代币共享同一个id类似ERC20。IERC1155接口合约IERC1155接口合约抽象了EIP1155实现所需的功能包含4 个事件与6 个函数。与ERC721不同由于ERC1155包含多种代币它实现了批量转账与批量余额查询可同时对多种代币进行操作。完整源码见 IERC1155.sol仓库中英文版为 Languages/en/40_ERC1155_en/IERC1155.sol。// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import ../34_ERC721_en/IERC165.sol; /** * dev ERC1155 标准接口合约实现 EIP1155 的功能 * 详见https://eips.ethereum.org/EIPS/eip-1155[EIP]. */ interface IERC1155 is IERC165 { /** * dev 单类代币转账事件 * 当 value 个 id 种类代币被 operator 从 from 转账到 to 时释放 */ event TransferSingle( address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value ); /** * dev 多类代币转账事件 * ids 和 values 为转账的代币种类和数量数组 */ event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); /** * dev 批量授权事件 * 当 account 将所有代币授权给 operator 时释放 */ event ApprovalForAll( address indexed account, address indexed operator, bool approved ); /** * dev 当 id 种类代币的 URI 发生变化时释放value 为新的 URI */ event URI(string value, uint256 indexed id); /** * dev 持仓查询返回 account 拥有的 id 种类代币的持仓量 */ function balanceOf(address account, uint256 id) external view returns (uint256); /** * dev 批量持仓查询accounts 和 ids 数组长度需相等 */ function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory); /** * dev 批量授权将调用者的代币授权给 operator 地址释放 {ApprovalForAll} 事件 */ function setApprovalForAll(address operator, bool approved) external; /** * dev 批量授权查询如果授权地址 operator 被 account 授权则返回 true */ function isApprovedForAll(address account, address operator) external view returns (bool); /** * dev 安全转账将 amount 单位 id 种类代币从 from 转账给 to释放 {TransferSingle} 事件 * 要求 * - 如果调用者不是 from 地址而是授权地址则需要得到 from 的授权 * - from 地址必须有足够持仓 * - 如果接收方是合约需要实现 IERC1155Receiver 的 onERC1155Received 方法并返回相应值 */ function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes calldata data ) external; /** * dev 批量安全转账释放 {TransferBatch} 事件 * 要求 * - ids 和 amounts 长度相等 * - 如果接收方是合约需要实现 IERC1155Receiver 的 onERC1155BatchReceived 方法并返回相应值 */ function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data ) external; }注仓库根目录下的 40_ERC1155/IERC1155.sol 使用^0.8.34编译指令并直接import ../34_ERC721/IERC165.sol英文教程版 Languages/en/40_ERC1155_en/IERC1155.sol 则从34_ERC721_en目录导入IERC165二者接口定义完全一致。IERC1155的 4 个事件TransferSingle事件单类代币转账时释放记录operator、from、to、id与value。TransferBatch事件多类代币转账时释放ids与values为代币种类和数量数组。ApprovalForAll事件批量授权时释放记录授权人account、被授权人operator与授权状态approved。URI事件元数据地址uri变化时释放value为新 URIid为对应代币种类。IERC1155的 6 个函数balanceOf(account, id)查询account对id种类代币的持仓量。balanceOfBatch(accounts, ids)批量查询持仓量accounts与ids数组长度必须相等返回持仓量数组。setApprovalForAll(operator, approved)调用者将全部代币授权给operator即全量授权。isApprovedForAll(account, operator)查询operator是否被account授权。safeTransferFrom(from, to, id, amount, data)单类代币安全转账。若to是合约必须实现onERC1155Received()函数。safeBatchTransferFrom(from, to, ids, amounts, data)多类代币安全转账amounts与ids是长度相等的数组。若to是合约必须实现onERC1155BatchReceived()函数。ERC1155接收合约与ERC721类似为了防止代币被转入黑洞合约ERC1155要求代币接收合约继承IERC1155Receiver并实现两个接收函数完整源码见 IERC1155Receiver.solonERC1155Received()单类代币转账的接收函数必须返回选择器0xf23a6e61即bytes4(keccak256(onERC1155Received(address,address,uint256,uint256,bytes)))。onERC1155BatchReceived()多类代币转账的接收函数必须返回选择器0xbc197c81即bytes4(keccak256(onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)))才能通过safeBatchTransferFrom接受ERC1155安全批量转账。// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import ../34_ERC721_en/IERC165.sol; /** * dev ERC1155 接收合约要接受 ERC1155 的安全转账需要实现这个合约 */ interface IERC1155Receiver is IERC165 { /** * dev 接受 ERC1155 安全转账 safeTransferFrom * 需要返回 0xf23a6e61 或 bytes4(keccak256(onERC1155Received(address,address,uint256,uint256,bytes))) */ function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns (bytes4); /** * dev 接受 ERC1155 批量安全转账 safeBatchTransferFrom * 需要返回 0xbc197c81 或 bytes4(keccak256(onERC1155BatchReceived(address,address,uint256[],uint256[],bytes))) */ function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns (bytes4); }主合约ERC1155的实现主合约 ERC1155.sol 实现了IERC1155接口合约规定的函数并额外提供了单类/多类代币的铸造与销毁函数。ERC1155的 4 个状态变量name代币名称。symbol代币代号。_balances代币持仓映射记录地址account对id种类代币的持仓量balances结构为mapping(uint256 mapping(address uint256))。_operatorApprovals批量授权映射记录持有人地址对另一地址的授权情况结构为mapping(address mapping(address bool))。ERC1155的 16 个函数函数作用构造函数初始化状态变量name和symbolsupportsInterface()实现ERC165标准声明合约支持的接口供其他合约查询balanceOf()实现IERC1155的balanceOf()与ERC721不同需要同时传入查询地址account和代币idbalanceOfBatch()实现balanceOfBatch()批量查询代币持仓setApprovalForAll()实现setApprovalForAll()批量授权并释放ApprovalForAll事件isApprovedForAll()实现isApprovedForAll()批量查询授权信息safeTransferFrom()实现safeTransferFrom()单类代币安全转账并释放TransferSingle事件与ERC721不同除from、to、id外还需要转账数量amountsafeBatchTransferFrom()实现safeBatchTransferFrom()多类代币安全转账并释放TransferBatch事件_mint()单类代币铸造函数_mintBatch()多类代币铸造函数_burn()单类代币销毁函数_burnBatch()多类代币销毁函数_doSafeTransferAcceptanceCheck()单类转账安全检查由safeTransferFrom()调用确保接收方为合约时实现了onERC1155Received()_doSafeBatchTransferAcceptanceCheck()多类转账安全检查由safeBatchTransferFrom()调用确保接收方为合约时实现了onERC1155BatchReceived()uri()返回id种类代币的元数据 URL类似ERC721的tokenURIbaseURI()返回baseURIuri即baseURI拼接id可由开发者重写核心实现要点解析1. 批量余额查询balanceOfBatch()先校验accounts与ids数组长度相等否则revert(ERC1155: accounts and ids length mismatch)随后用 for 循环逐对调用balanceOf()填充返回数组。2. 安全转账safeTransferFrom()核心校验链为调用者身份 → 零地址 → 余额充足三连判调用者必须是持有人或被授权人from operator || isApprovedForAll(from, operator)to不能是零地址_balances[id][from] amount余额必须充足。余额扣减使用unchecked块防止无意义溢出检查最后释放TransferSingle事件并执行安全接收检查。safeBatchTransferFrom()则是同样的逻辑在 for 循环中处理多组id/amount最后释放TransferBatch事件。3. 铸造与销毁_mint()将amount记入to地址的_balances[id][to]以from address(0)释放TransferSingle事件_burn()反向操作扣减余额并以to address(0)释放事件。批量版本_mintBatch()/_burnBatch()通过 for 循环批量更新并释放TransferBatch事件。全部四个函数均为internal对外暴露铸造/销毁入口由子合约自行封装。4. 安全接收检查这是ERC1155防黑洞合约的关键。_doSafeTransferAcceptanceCheck()先通过to.code.length 0判断接收方是否为合约若是则以try/catch调用onERC1155Received()并要求返回值等于函数选择器IERC1155Receiver.onERC1155Received.selector即0xf23a6e61否则revert ERC1155InvalidReceiver(to)仓库源码 40_ERC1155/ERC1155.sol 定义了错误类型error ERC1155InvalidReceiver(address receiver)并给出空 reason 的兜底处理。批量检查_doSafeBatchTransferAcceptanceCheck()逻辑相同只是改为校验onERC1155BatchReceived的选择器0xbc197c81。5. 元数据uri()uri(id)返回_baseURI()拼接id.toString()其中toString来自 34_ERC721/String.sol 中的Strings库英文版从34_ERC721_en导入。默认_baseURI()返回空字符串需要子合约重写。6. 接口声明supportsInterface()ERC1155同时继承IERC165、IERC1155、IERC1155MetadataURIsupportsInterface()逐一比对三个接口的type(...).interfaceId其他合约可通过 34_ERC721/IERC165.sol 中的supportsInterface()查询其能力。实战把 BAYC 改造成ERC1155——BAYC1155仓库在 40_ERC1155/BAYC1155.sol英文版 Languages/en/40_ERC1155_en/BAYC1155.sol中提供了一个实战合约BAYC1155对无聊猿 BAYC 稍作修改改为遵循ERC1155标准并支持免费铸造。其关键设计是重写_baseURI()使BAYC1155的uri与 BAYC 的tokenURI相同从而让BAYC1155的元数据与无聊猿完全一致。// SPDX-License-Identifier: MIT // by 0xAA pragma solidity ^0.8.21; import ./ERC1155.sol; contract BAYC1155 is ERC1155 { uint256 constant MAX_ID 10000; // 构造函数 constructor() ERC1155(BAYC1155, BAYC1155) {} // BAYC 的 baseURI 为 ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/ function _baseURI() internal pure override returns (string memory) { return ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/; } // 铸造函数 function mint(address to, uint256 id, uint256 amount) external { // id 不能超过 10,000 require(id MAX_ID, id overflow); _mint(to, id, amount, ); } // 批量铸造函数 function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts) external { // id 不能超过 10,000 for (uint256 i 0; i ids.length; i) { require(ids[i] MAX_ID, id overflow); } _mintBatch(to, ids, amounts, ); } }BAYC1155的要点常量MAX_ID 10000限定了代币种类id的范围0到9999共 10,000 种mint与mintBatch均校验id MAX_ID超限即revert(id overflow)_baseURI()返回 BAYC 官方 IPFS 地址配合基类uri()中baseURI id.toString()的拼接逻辑uri(0)、uri(1)……即对应 BAYC 编号 0、1…… 的元数据铸造入口对外公开任何人可免费铸造任意种类的任意数量——这正演示了ERC1155中同一种类数量为 1 则是 NFT、数量大于 1 则是 FT的灵活性。注意仓库根目录源码 40_ERC1155/BAYC1155.sol 使用pragma solidity ^0.8.34教程正文中的示例为^0.8.21二者逻辑一致编译时以仓库实际文件为准。Remix 实战演示以下操作均在 Remix IDE 中进行若需本地体验可通过git clone拉取本仓库后使用 Foundry/Remix 打开 40_ERC1155/BAYC1155.sol。1. 部署BAYC1155合约在 Remix 编译BAYC1155.sol后于 DEPLOY RUN TRANSACTIONS 面板选择合约并点击Deploy即可完成部署合约继承的mint、mintBatch等函数会一并出现在交互面板中。2. 查看元数据URI部署后调用uri(id)函数输入某个id如0即可查看到由 BAYC 的 IPFS baseURI 与id拼接而成的元数据地址验证与 BAYC 元数据一致。3.mint单类铸造并查看持仓变化在mint区域输入账户地址、id与数量后点击mint按钮铸造。如果数量为1该id即非同质化代币如果数量大于1则该id是同质化代币。在balanceOf区域输入账户地址与id即可查看对应持仓数量验证铸造结果。4. 批量mint并查看持仓变化在mintBatch区域输入ids数组与对应铸造数量数组两个数组长度必须相同否则触发ERC1155: ids and amounts length mismatch。输入多个id与其对应数量例如[1,2,3,4]与[99,9,1,1]后执行批量铸造。随后在balanceOfBatch中输入账户地址数组与ids数组即可一次性查看多个种类的持仓变化。5. 转账与接收合约校验在safeBatchTransferFrom或safeTransferFrom区域从已持有代币的地址向新地址转账输入ids与对应amounts数组长度必须相等。目标地址可以是普通地址也可以是合约地址若目标为普通地址转账直接成功若目标为合约地址合约会调用_doSafeBatchTransferAcceptanceCheck()验证其是否实现了onERC1155Received()/onERC1155BatchReceived()并返回正确选择器未实现则revert从而防止代币被打入黑洞合约。转账后可在balanceOf/balanceOfBatch中查看接收地址持仓变化。小结本讲学习了以太坊EIP1155提出的ERC1155多代币标准它允许一个合约包含多种同质化或非同质化代币通过id对应一种代币、数量为 1 即 NFT、大于 1 即 FT的统一抽象显著降低了多资产场景尤其是 GameFi的合约部署与管理成本。同时我们基于仓库源码创建了 BAYC 的ERC1155改造版BAYC1155——包含 10,000 种、元数据与 BAYC 完全一致、支持免费铸造的代币合约。目前ERC1155主要应用于 GameFi随着元宇宙技术的发展这一标准有望获得更广泛的应用。【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考