ERC-721

我们已经讨论了如何使用 ERC-20 创建一个 同质化 token,但是如果不是所有的 token 都一样怎么办? 这在 房地产投票权收藏品 等情况下会出现,由于它们的用途,稀有性等原因,某些物品比其他物品更有价值。ERC-721 是表示 非同质化 token 所有权的标准,也就是说,每个 token 都是唯一的。

ERC-721 是一个比 ERC-20 更复杂的标准,具有多个可选扩展,并且分布在多个合约中。 OpenZeppelin Contracts 提供了关于如何组合这些合约的灵活性,以及自定义的有用扩展。 查看 API 参考 以了解更多关于这些的信息。

构建 ERC-721 Token 合约

我们将使用 ERC-721 追踪我们游戏中的物品,每个物品都有自己独特的属性。 每当要奖励给玩家物品时,它将被铸造并发送给他们。 玩家可以自由地保留他们的 token,或者像在区块链上交易任何其他资产一样,与其他玩家进行交易! 请注意,任何帐户都可以调用 awardItem 来铸造物品。 为了限制哪些帐户可以铸造物品,我们可以添加 访问控制

以下是 token 化物品的合约可能的样子:

Unresolved include directive in modules/ROOT/pages/erc721.adoc - include::api:example$token/ERC721/GameItem.sol[]

ERC721URIStorage 合约是 ERC-721 的一个实现,它包括元数据标准扩展 (IERC721Metadata) 以及每个 token 元数据的机制。 这就是 _setTokenURI 方法的来源:我们使用它来存储物品的元数据。

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

可以创建新物品:

> gameItem.awardItem(playerAddress, "https://game.example/item-id-8u5h2m.json")
Transaction successful. Transaction hash: 0x...
Events emitted:
 - Transfer(0x0000000000000000000000000000000000000000, playerAddress, 7)

并且可以查询每个物品的所有者和元数据:

> gameItem.ownerOf(7)
playerAddress
> gameItem.tokenURI(7)
"https://game.example/item-id-8u5h2m.json"

这个 tokenURI 应该解析为一个 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
}

有关 tokenURI 元数据 JSON Schema 的更多信息,请查看 ERC-721 规范

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