# 聊聊ERC1155 **Published by:** [xyyme.eth](https://paragraph.com/@xyyme/) **Published on:** 2022-03-17 **URL:** https://paragraph.com/@xyyme/erc1155 ## Content 概述ERC1155 是 ERC721 之外的另一种 NFT 标准,可以理解为 ERC20 与 ERC721 的融合体。简单举几个例子理解一下:ERC20 就好像银行发行的一百元纸币,每张纸币的价值相同ERC721 就好像艺术馆里面的艺术品,每件艺术品都是独一无二的ERC1155 类似于游戏中的道具,比如一些普通道具可能有成千上万件,而一些稀有道具只有几十件。也类似于银行发行的不同面额纸币,相同面额价值相同,可以互换。不同面额价值不同,不可互换对应到代码实现中:ERC20 合约中每个 token 是同一种类型,它们的价值都是一样的ERC721 合约中每个 tokenId 都是不同类型,各自拥有不同的属性。每个 tokenId 有且只有一个ERC1155 合约中根据 id 有不同的分类,每一个 id 是一种类型。当这个 id 下有多个 token 时,就是 fungible token(类似 ERC20);当这个 id 下只有一个 token 时,就是 non-fungible token(类似ERC721)我们直接来看一下 OpenZeppelin 官方文档给出的一个例子:// contracts/GameItems.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; // 继承ERC1155标准 contract GameItems is ERC1155 { // 发行5种道具,id分别为0~5 uint256 public constant GOLD = 0; uint256 public constant SILVER = 1; uint256 public constant THORS_HAMMER = 2; uint256 public constant SWORD = 3; uint256 public constant SHIELD = 4; constructor() ERC1155("https://game.example/api/item/{id}.json") { // id不同,mint的数量不同 _mint(msg.sender, GOLD, 10**18, ""); _mint(msg.sender, SILVER, 10**27, ""); // THORS_HAMMER只mint一个,即non-fungible _mint(msg.sender, THORS_HAMMER, 1, ""); _mint(msg.sender, SWORD, 10**9, ""); _mint(msg.sender, SHIELD, 10**9, ""); } } 代码接下来看看 OpenZeppelin ERC1155 的代码。 数据结构:// id -> 用户地址 -> 数量,即用户拥有的某id token的数量 mapping(uint256 => mapping(address => uint256)) private _balances; // 授权地址信息 // owner -> 授权人 -> 是否授权 mapping(address => mapping(address => bool)) private _operatorApprovals; // uri,根据id区分,格式应该为 https://token-cdn-domain/{id}.json string private _uri; 这里需要注意一下 uri 的格式,根据 OpenZeppelin 文档:The uri can include the string {id} which clients must replace with the actual token ID, in lowercase hexadecimal (with no 0x prefix) and leading zero padded to 64 hex characters. For token ID 2 and uri https://game.example/api/item/{id}.json clients would replace {id} with 0000000000000000000000000000000000000000000000000000000000000002 to retrieve JSON at https://game.example/api/item/0000000000000000000000000000000000000000000000000000000000000002.json所有的 id,前后缀都相同,只是在 id 部分区分,需要将其补足为 64 位。同时,这个 uri 打开后的格式应该为 Json 文本,例如:{ "name": "Thor's hammer", "description": "Mjölnir, the legendary hammer of the Norse god of thunder.", "image": "https://game.example/item-id-8u5h2m.png", "strength": 20 } 在代码中看到,ERC1155 只能对所有的 token 进行授权,而不能像 ERC721 那样只对某一个 tokenId 进行授权(ERC721 也包括全部授权的方法)。 查询用户某一个 id 类型下的数量:function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { require(account != address(0), "ERC1155: balance query for the zero address"); return _balances[id][account]; } 转账:function _safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) internal virtual { require(to != address(0), "ERC1155: transfer to the zero address"); address operator = _msgSender(); _beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data); // 更新转出与转入者的数量 uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); unchecked { _balances[id][from] = fromBalance - amount; } _balances[id][to] += amount; emit TransferSingle(operator, from, to, id, amount); _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data); } 其他的方法例如 mint, burn 等其实都大同小异,没有什么难度。 在转账时,如果接收地址是合约,需要验证其实现了钩子函数接口,防止 token 永久锁死在合约中:function _doSafeTransferAcceptanceCheck( address operator, address from, address to, uint256 id, uint256 amount, bytes memory data ) private { if (to.isContract()) { // 校验合约是否实现了 onERC1155Received 方法 try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { if (response != IERC1155Receiver.onERC1155Received.selector) { revert("ERC1155: ERC1155Receiver rejected tokens"); } } catch Error(string memory reason) { revert(reason); } catch { revert("ERC1155: transfer to non ERC1155Receiver implementer"); } } } 总结ERC1155 概念与代码实现都很简单。我个人觉得 ERC1155 更适合的场景应该是在游戏中,比如对应不同数量的道具这类场景。而 ERC721 则更适合艺术品这个场景。目前在 OpenSea 上的艺术品还是以 ERC721 居多。参考ERC1155The official documentation for OpenZeppelin Libraries and Toolshttps://docs.openzeppelin.com ## Publication Information - [xyyme.eth](https://paragraph.com/@xyyme/): Publication homepage - [All Posts](https://paragraph.com/@xyyme/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@xyyme): Subscribe to updates - [Twitter](https://twitter.com/xyymeeth): Follow on Twitter