您的第一个 NFT
注意:以下教程正在进行中。 此外,Aptos(非同质化)代币规范尚未正式确定。

Aptos 中的代币和 NFT

NFT 是一种不可替代的代币或存储在区块链上的数据,它唯一地定义了资产的所有权。 NFT 最初是在 EIP-721 中定义的,后来在 EIP-1155 中进行了扩展。 NFT 通常包括以下几个方面:
  • 名称,资产的名称,在集合中必须是唯一的
  • 描述,资产的描述
  • URL,一个指向关于资产的更多信息的链外非描述指针可以是媒体,如图像或视频或更多元数据
  • 供应量,这个 NFT 的单位总数,许多 NFT 只有一个,而那些有多个的被称为版本
此外,大多数 NFT 是具有共同属性(例如主题、创建者或最小合约)的集合或一组 NFT 的一部分。每个集合都有一组相似的属性:
  • 名称,集合的名称,在创建者的帐户中必须是唯一的
  • 描述,资产的描述
  • URL,一个指向关于资产的更多信息的链外非描述指针可以是媒体,如图像或视频或更多元数据
核心 NFT 或代币的 Aptos 实现可以在 Token.move 中找到。

Aptos Token 定义

Token

Aptos Token 被定义为:
类型
描述
id
GUID:ID
代币的全局唯一标识符也可用于识别创建者
name
ASCII::String
代币的名称,在集合中必须是唯一的
collection
GUID:ID
包含此代币的集合的全局唯一标识符
balance
u64
与供应相关的此代币的当前存储量, 1 <= balance <= supply
Aptos TokenData 被定义为:
类型
描述
id
GUID:ID
代币的全局唯一标识符也可用于识别创建者
description
ASCII::String
代币描述信息
name
ASCII::String
代币的名称,在集合中必须是唯一的
supply
u64
此 Token 的总版本数
uri
ASCII::String
附加信息/媒体的 URL
metadata
TokenType
一个通用的、可选的用户定义结构,用于包含有关此代币链上的附加信息
Token 是使用 Move 属性 store 定义的,这意味着它们可以保存到全局存储中。Token 不能被隐式丢弃,必须被销毁以确保总余额等于供应。Token 不能被复制。也就是说,由于缺少复制操作,除了创建者之外,任何人都无法更改总余额或供应量。请注意,当前的 API 没有公开发布创建代币的能力。Token 可以通过其 id 或 TokenType、集合名称和 Token 名称的元组来唯一标识。
TokenData 具有 copy 属性,支持简单的代币余额拆分。每当余额大于 1 的个人向另一个人提供其余额的一部分少于其总余额时,就会发生代币拆分。交易代币的用户请注意,两个代币可以共享相同的 TokenData,因为 Aptos 标准不会尝试识别代币是否复制了另一个代币的属性。重复前面所说的,代币可以通过其 id 或 TokenType、集合名称和代币名称来唯一标识。创建者可以更改 TokenType、集合名称或代币名称集中的任何值,以创建相似但不完全相同的代币。

Token Collections

Aptos 定义了一组按其唯一 ID 分组在一起的集合:
struct Collections<TokenType: copy + drop + store> has key {
collections: Table<ASCII::String, Collection>,
}
struct TokenMetadata<TokenType: store> has key {
metadata: Table<ID, TokenType>,
}
由于 Collections 具有属性键,因此它直接存储到创建者帐户。 需要注意的是,如果没有 Collections 的概念,而 Collection 具有 key 属性,则 Aptos 帐户只能有一个集合,而通常情况并非如此。 可以按名称在集合集中查找集合,因此强制使用唯一的集合名称。
Token 和 TokenData 结构在其内容中是固定的。 资源 TokenMetadata 使创建者能够存储额外的 Token 数据。 表中的数据存储为 Token 的唯一 ID。 由于 script 函数不能支持结构或泛型的限制,使用它是可选的并且需要定制化 API。
每个集合都有以下字段:
类型
描述
tokens
Table<ASCII::String, TokenMetadata<TokenType>>
跟踪与此集合关联的所有Token
claimed_tokens
Table<ASCII::String, address>
跟踪 supply == 1 的 Token 的存储地址
description
ASCII::String
集合描述
name
ASCII::String
此集合的名称在指定 TokenType 的创建者帐户中必须是唯一的。
uri
ASCII::String
附加信息/媒体的 URL
count
u64
此集合跟踪的不同 Token 总数
maximum
Option<u64>
可在此集合中铸造的可选 Token 的最大数量
集合不是用于累积 Token 的存储,因此它不包含 Token,而是包含 TokenData:
类型
描述
id
GUID:ID
此 Token 的全球唯一标识符也可用于识别创建者
data
TokenData
关于此代币的附加数据,这是代币的供应量 > 1

Token 存储

为了获取和存储 Token,用户必须拥有一个 TokenType 库:
struct Gallery has key {
gallery: Table<ID, Token>,
}
与 Collections 一样,它作为资源存储在 Aptos 帐户上。

Token 介绍

作为我们核心框架的一部分,Aptos 提供了一个基本的 Token 接口,没有额外的数据,或者明确的 TokenMetadata 资源没有该令牌的条目。 这样做的动机包括:
  • 创建新 Token 需要编写 Move 代码
  • 用于创建新标记的脚本函数必须定制化,因为 Move 不支持模板类型或结构作为输入参数
  • 脚本函数上的模板类型为编写脚本函数增加了额外的难度
本教程将引导您完成:
  • 创建自己的 Token 集合
  • 我们最喜欢的猫的 Token
  • 并将该 Token 给其他人。
请从下面的代码仓库中 clone 最新代码!
本教程以您的第一个事务为基础,作为此示例的库。 以下教程包含可以在下面完整下载的示例代码:
Python
Rust
Typescript
对于本教程,将重点关注 first_nft.py 并重新使用上一教程中的 first_transaction.py 库。 你可以在这里找到 python 项目
TODO
TODO

创建一个集合

Aptos Token 使创作者能够创建有限或无限的收藏。 许多 NFT 具有有限的性质,创造者只打算永远创造一定数量,这会导致稀缺性。 而其他代币可能具有无限的性质,例如,用于实用程序的集合可能会随着时间的推移出现新的代币。 SimpleToken 集合可以通过使用适当的脚本函数以任一行为实例化:
有限,即不能超过最大数量的代币可以铸造:
public(script) fun create_finite_collection_script(
account: signer,
description: vector<u8>,
name: vector<u8>,
uri: vector<u8>,
maximum: u64,
)
无限制,即可以添加到集合中的 Token 数量没有限制:
public(script) fun create_unlimited_collection_script(
account: signer,
description: vector<u8>,
name: vector<u8>,
uri: vector<u8>,
)
这些脚本函数可以通过 REST API 调用。 下面演示了如何调用 ,如下所示:
Python
def create_collection(self, account: Account, description: str, name: str, uri: str):
"""Creates a new collection within the specified account"""
payload = {
"type": "script_function_payload",
"function": f"0x1::Token::create_unlimited_collection_script",
"type_arguments": [],
"arguments": [
description.encode("utf-8").hex(),
name.encode("utf-8").hex(),
uri.encode("utf-8").hex(),
]
}
self.submit_transaction_helper(account, payload)

创建 Token

可以在集合创建后创建 Token。 为此,Token 必须指定与先前创建的集合名称相同的 collection_name。 创建 SimpleToken 的 Move 脚本函数是:
public(script) fun create_token_script(
account: signer,
collection_name: vector<u8>,
description: vector<u8>,
name: vector<u8>,
supply: u64,
uri: vector<u8>,
)
这些脚本函数可以通过 REST API 调用。 下面演示了如何调用 ,如下所示:
Python
def create_token(
self,
account: Account,
collection_name: str,
description: str,
name: str,
supply: int,
uri: str,
):
payload = {
"type": "script_function_payload",
"function": f"0x1::Token::create_token_script",
"type_arguments": [],
"arguments": [
collection_name.encode("utf-8").hex(),
description.encode("utf-8").hex(),
name.encode("utf-8").hex(),
str(supply),
uri.encode("utf-8").hex(),
]
}
self.submit_transaction_helper(account, payload)

发送 Token

在 Aptos 和 Move 中,每个代币都占据空间并拥有所有权。 因此,代币转移不是单方面的,需要类似于公告板的两个阶段过程。 发送者必须首先注册一个 Token 可供接收者声明,然后接收者必须声明这个 Token。 这已在名为 TokenTransfer 的概念验证 Move 模块中实现。 SimpleToken 提供了一些包装函数来支持转账到另一个帐户、声明该转账或停止该转账。
获得 Token ID
为了发送代币,发送者必须首先基于知道创建者的账户、集合名称和代币名称来识别代币id。 这可以通过查询 REST 获得:
Python
def get_token_id(self, creator: str, collection_name: str, token_name: str) -> int:
""" Retrieve the token's creation_num, which is useful for non-creator operations """
resources = self.account_resources(creator)
collections = []
tokens = []
for resource in resources:
if resource["type"] == f"0x1::Token::Collections":
collections = resource["data"]["collections"]["data"]
for collection in collections:
if collection["key"] == collection_name:
tokens = collection["value"]["tokens"]["data"]
for token in tokens:
if token["key"] == token_name:
return int(token["value"]["id"]["creation_num"])
assert False
提供 Token
Token 中的以下 Move 脚本功能支持将 Token 转移到另一个帐户,有效地注册另一个帐户可以获取该 Token:
public(script) fun offer_script(
sender: signer,
receiver: address,
creator: address,
token_creation_num: u64,
amount: u64,
)
Python
def offer_token(
self,
account: Account,
receiver: str,
creator: str,
token_creation_num: int,
amount: int
):
payload = {
"type": "script_function_payload",
"function": f"0x1::TokenTransfers::offer_script",
"type_arguments": [],
"arguments": [
receiver,
creator,
str(token_creation_num),
str(amount),
]
}
self.submit_transaction_helper(account, payload)
领取 Token
SimpleToken 中的以下 Move 脚本函数支持接收上一个函数提供的 Token,有效地声明 Token:
public(script) fun claim_script(
sender: signer,
receiver: address,
creator: address,
token_creation_num: u64,
amount: u64,
)
Python
def claim_token(
self,
account: Account,
sender: str,
creator: str,
token_creation_num: int,
):
payload = {
"type": "script_function_payload",
"function": f"0x1::TokenTransfers::claim_script",
"type_arguments": [],
"arguments": [
sender,
creator,
str(token_creation_num),
]
}
self.submit_transaction_helper(account, payload)

待办

  • 添加额外的铸造能力
  • 确保在铸造时至少产生一个代币
  • 添加事件——需要关于什么事件的反馈
  • 为 Token 提供可变 API
  • 直接为泛型和简单 Token 写一个冒烟测试
  • 以安全的方式启用燃烧
Copy link
On this page
Aptos 中的代币和 NFT
Aptos Token 定义​
Token 介绍​
待办