ERC-1155
多 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 进行操作。该标准提供了两个函数,balanceOfBatch
和 safeBatchTransferFrom
,它们使查询多个余额和转移多个 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[]
我们还可以使用 onERC1155Received
和 onERC1155BatchReceived
函数实现更复杂的场景。