Skip to content

ERC1155

ERC1155是一个新颖的合约,它从之前的标准中获取精华,旨在创建一个与可替代性不管,燃料高效的token合约。

提示

ERC1155 从 ERC20、ERC721 和 ERC777 中汲取灵感。如果您不熟悉这些标准,请先查看他们的指南。

多Token标准

ERC1155的一个显著特点是,它使用一个合约,可以包含多种token代币。这就是它的balanceOf函数与ERC20和ERC777不同的原因:它有一个附加的id参数,来标识你要查询的token的balanceOf余额。
这和ERC721有点类似,但在ERC721中,tokenid是没有余额这个概念的:每个代币都是不可替代的,只有存在或不存在两种状态。ERC721的balanceOf函数是表示一个账户有多少不同的token,而不是每个token拥有多少。另一方面,ERC1155中的账户,每个tokenid对应着完全不同的余额,不可替代的token是通过实现对其中某个token的铸造来完成的。
这个方式,对于某个项目中需要包含多种token的情况,可以节约大量的gas。我们不需要再为每个token类型编写一个单独的合约,一个合约就可以保存所有的token状态,降低了部署花费和系统的复杂性。

批量操作

由于所有状态都保存在单个合约中,因此可以非常有效地在单个交易中对多个代币进行操作。标准中提供了两个函数:balanceOfBatchsafeBatchTransferFrom,它们可以批量查询余额,批量发送多个token,这样做非常简单并且高效。 本着标准的精神,我们还在非标准函数中包含了批处理操作,例如 _mintBatch

构建ERC1155智能合约

我们会使用ERC1155来追踪游戏中的不同物品,每个物品都有自己独特的属性。我们将铸造所有物品,并归属到合约部署者账号,迟点可以转给其他的用户。玩家可以自由保留他们的代币或在他们认为合适的时候与其他人交易,就像他们在区块链上的任何其他资产一样!
为简单起见,我们将在构造函数中铸造所有物品,但您可以在合约中添加铸造功能,以便按需铸造给玩家。

提示

关于铸造机制,请参考生成ERC20供应

下面是合约token化物品的代码:

solidity
// contracts/GameItems.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

contract GameItems is ERC1155 {
    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") {
        _mint(msg.sender, GOLD, 10**18, "");
        _mint(msg.sender, SILVER, 10**27, "");
        _mint(msg.sender, THORS_HAMMER, 1, "");
        _mint(msg.sender, SWORD, 10**9, "");
        _mint(msg.sender, SHIELD, 10**9, "");
    }
}

请注意,在游戏物品中,黄金是可替换代币,而雷神之锤是不可替代的代币,所以我们只铸造了一个。
ERC1155合约包含一个可选的扩展项IERC1155MetadataURI,它是uri函数的来源:我们使用它来检索元数据的uri。
同时,和ERC20不同,ERC1155也没有decimals字段,因为它的token每个都是不同的,并且不能被分割。
部署完成后,我们可以查询一下部署者的余额:

sh
> gameItems.balanceOf(deployerAddress,3)
1000000000

我们可以将物品转给玩家:

sh
> gameItems.safeTransferFrom(deployerAddress, playerAddress, 2, 1, "0x0")
> gameItems.balanceOf(playerAddress, 2)
1
> gameItems.balanceOf(deployerAddress, 2)
0

我们也可以批量转账到玩家账户,获取批量余额:

sh
> gameItems.safeBatchTransferFrom(deployerAddress, playerAddress, [0,1,3,4], [50,100,1,1], "0x0")
> gameItems.balanceOfBatch([playerAddress,playerAddress,playerAddress,playerAddress,playerAddress], [0,1,2,3,4])
[50,100,1,1,1]

元数据uri可以这样获取:

sh
> gameItems.uri(2)
"https://game.example/api/item/{id}.json"

uri包含字符串{id},客户端需要将它替换成实际的token ID,需要十六进制格式,没有前缀0x,空余的填充0,一共64位的16进制格式字符。
对于token ID为2,uri为https://game.example/api/item/{id}.json,客户端应该替换{id}0000000000000000000000000000000000000000000000000000000000000002,那么总的json格式uri地址为https://game.example/api/item/0000000000000000000000000000000000000000000000000000000000000002.json,token ID为2的JSON数据可能如下所示:

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
}

更多关于JSON格式的信息,可以参考ERC-1155 Metadata URI JSON Schema

注意

你可能已经注意到了,物品属性的元数据并没有存在链上。所以游戏开发者可以在链下修改元数据从而修改游戏的规则。

提示

如果您想将所有项目信息放在链上,您可以通过提供带有 JSON 模式编码的 Base64 数据 URI 来扩展 ERC721 以这样做(尽管它会相当昂贵)。您还可以利用 IPFS 来存储 tokenURI 信息,但这些技术超出了本概述指南的范围。

发送代币给合约

使用safeTransferFrom的一个关键不同是,token转给其他合约时可能会返回如下信息:

ada
ERC1155: transfer to non ERC1155Receiver implementer

这是个好事!这表示接受合约不支持ERC1155协议,所以转账操作是不允许的,避免token被永久锁住。作为例子,Golem 合约目前持有超过 35 万个 GNT 代币价值数万美元,但却没有方法可以提取出来。几乎每个 ERC20 支持的项目都会发生这种情况,通常是由于用户错误。
为了让我们的合约接收 ERC1155 代币,我们可以继承合约 ERC1155Holder,它为我们处理注册。尽管我们需要记住实现功能以允许将代币从我们的合约中转移出去:

solidity
// contracts/MyContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";

contract MyContract is ERC1155Holder {
}

我们还可以使用onERC1155ReceivedonERC1155BatchReceived函数来实现更复杂的脚步。

预设ERC1155合约

预设ERC1155合约可以通过ERC1155PresetMinterPauser来实现。它允许token铸造(create)-包括批量铸造,停止token转账(pause),拥有者销毁token(destroy)。合约通过访问控制来限定铸造和暂停功能的使用。部署合约的账户将被授予 minter 和 pauser 角色,以及默认的 admin 角色。
该合约无需编写任何 Solidity 代码即可部署。它可以按原样用于快速原型设计和测试,但也适用于生产环境。

Released under the MIT License.