ERC-1155

ERC-1155 是一种新颖的 token 标准,旨在从先前的标准中汲取精华,创建一个 同质性无关gas 高效token 合约

ERC-1155 从 ERC-20ERC-721ERC-777 中汲取了灵感。如果你不熟悉这些标准,请先阅读它们的指南再继续。

多 Token 标准

ERC-1155 的独特之处在于它使用单个智能合约一次性表示多个 token。这就是为什么它的 balanceOf 函数与 ERC-20 和 ERC-777 的不同:它有一个额外的 id 参数,用于你要查询余额的 token 的标识符。

这与 ERC-721 的工作方式类似,但在该标准中,token id 没有余额的概念:每个 token 都是非同质化的,要么存在,要么不存在。ERC-721 的 balanceOf 函数指的是一个账户拥有 多少不同的 token,而不是每个 token 有多少。另一方面,在 ERC-1155 中,账户对每个 token id 都有不同的余额,而非同质化 token 的实现方式仅仅是铸造一个。

这种方法为需要多个 token 的项目节省了大量的 Gas。单个 ERC-1155 token 合约可以保存整个系统状态,从而降低部署成本和复杂性,而无需为每种 token 类型部署新的合约。

批量操作

因为所有状态都保存在一个合约中,所以可以在单个交易中非常高效地对多个 token 进行操作。该标准提供了两个函数,balanceOfBatchsafeBatchTransferFrom,它们使查询多个余额和转移多个 token 变得更简单且 Gas 消耗更少。

本着该标准的精神,我们还在非标准函数中包含了批量操作,例如 _mintBatch

构建一个 ERC-1155 Token 合约

我们将使用 ERC-1155 来跟踪我们游戏中的多个物品,每个物品都有其独特的属性。我们将所有物品都铸造给合约的部署者,稍后我们可以将其转移给玩家。玩家可以自由地保留他们的 token 或与其他玩家进行交易,就像他们在区块链上的任何其他资产一样!

为了简单起见,我们将在构造函数中铸造所有物品,但你可以向合约添加铸造功能,以便按需铸造给玩家。

有关铸造机制的概述,请查看 创建 ERC-20 供应

以下是一个 token 化物品的合约示例:

Unresolved include directive in modules/ROOT/pages/erc1155.adoc - include::api:example$token/ERC1155/GameItems.sol[]

请注意,对于我们的游戏物品,Gold 是一种同质化 token,而 Thor’s Hammer 是一种非同质化 token,因为我们只铸造了一个。

ERC1155 合约包括可选的扩展 IERC1155MetadataURI。这就是 uri 函数的来源:我们使用它来检索元数据 URI。

另请注意,与 ERC-20 不同,ERC-1155 缺少 decimals 字段,因为每个 token 都是不同的,并且无法分割。

部署后,我们将能够查询部署者的余额:

> gameItems.balanceOf(deployerAddress,3)
1000000000

我们可以将物品转移到玩家账户:

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

我们还可以批量将物品转移到玩家账户并获取批量的余额:

> 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:

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

uri 可以包含字符串 {id},客户端必须将其替换为实际的 token ID,以小写十六进制(不带 0x 前缀)和前导零填充到 64 个十六进制字符。

对于 token ID 2 和 URI https://game.example/api/item/{id}.json,客户端会将 {id} 替换为 0000000000000000000000000000000000000000000000000000000000000002 以检索 https://game.example/api/item/0000000000000000000000000000000000000000000000000000000000000002.json 处的 JSON。

token ID 2 的 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 Schema 的更多信息,请查看 ERC-1155 元数据 URI JSON Schema

你会注意到物品的信息包含在元数据中,但该信息不在链上!因此,游戏开发者可以更改底层元数据,从而更改游戏规则!
如果你想将所有物品信息都放在链上,你可以扩展 ERC-721 来做到这一点(虽然成本会相当高),方法是提供一个带有编码的 JSON schema 的 Base64 Data URI。你也可以利用 IPFS 来存储 URI 信息,但这些技术超出了本概述指南的范围

将 Token 发送到合约

使用 safeTransferFrom 时的一个主要区别是,将 token 转移到其他合约可能会因以下自定义错误而回退:

ERC1155InvalidReceiver("<ADDRESS>")

这是一件好事!这表示接收合约尚未将自身注册为知道 ERC-1155 协议,因此禁用了向其转移 token,以 防止 token 永远被锁定。例如,https://etherscan.io/token/0xa74476443119A942dE498590Fe1f2454d7D4aC0d?a=0xa74476443119A942dE498590Fe1f2454d7D4aC0d[Golem 合约目前持有超过 35 万个 GNT token],并且缺少将其从那里取出的方法。这种情况几乎发生在每个 ERC20 支持的项目中,通常是由于用户错误造成的。

为了使我们的合约能够接收 ERC-1155 token,我们可以继承便捷合约 ERC1155Holder,它会为我们处理注册。但是,我们需要记住实现允许 token 从我们的合约中转移出去的功能:

Unresolved include directive in modules/ROOT/pages/erc1155.adoc - include::api:example$token/ERC1155/MyERC115HolderContract.sol[]

我们还可以使用 onERC1155ReceivedonERC1155BatchReceived 函数实现更复杂的场景。