跳转到内容

Aptos 数字资产标准

数字资产(DA)标准是 Aptos 上现代的非同质化代币(NFT)标准.NFT 代表链上的独特资产,并存储在集合中.这些 NFT 可以自定义,后续可通过你的智能合约进行转移,灵魂绑定,销毁,变更或扩展.

该标准取代了旧的 Aptos Token Standard.主要改进如下:

改进项描述
Token 扩展性Token 通过 Move Objects 实现,易于扩展.
直接 NFT 转账现在可以直接转移 NFT,无需接收方在链上”预先注册”.
NFT 组合性NFT 可以拥有其他 NFT,便于组合.

如果你只需要简单铸造 NFT,无需自定义或扩展功能,可以使用实现了 DA 标准的 aptos_token 模块(见下文用法).

该标准通过两个 Object 实现:

  1. Collection - 具有名称和上下文信息的 NFT 集合.
  2. Token - 代表独特资产的数字资产.通常用于表示 NFT,通常使用 uri 链接指向资产的更多信息(如图片,视频等).
Digital Asset token and collection relationship Digital Asset token and collection relationship

所有 Token 必须引用一个父 Collection,但父 Collection 不拥有 Token.新铸造的 Token 通常归创建者所有,之后可以转移给其他账户.

字段描述
Description可选字符串,少于 2048 字符(可通过 MutatorRef 修改).
Name必填字符串,用于标识 Collection.名称在每个账户内必须唯一,即单个创建者账户不能创建多个同名 Collection.
Royalty可选 Royalty 结构体,表示销售价格中归创作者的分成比例.可通过 Royalty 模块 生成的 MutatorRef 修改.该模块是 DA 标准的扩展.示例见 aptos_token.move.
URI length可选字符串,少于 512 字符,链接到 Collection 相关内容(可通过 MutatorRef 修改).

根据是否需要最大 Token 数量限制,有两种方式创建 Collection.

如需固定供应量的 Collection,可使用 collection::create_fixed_collection:

use aptos_token_objects::collection;
use std::option::{Self, Option};
public entry fun create_collection(creator: &signer) {
let max_supply = 1000;
let royalty = option::none();
// 创建后最大供应量不可更改
collection::create_fixed_collection(
creator,
"My Collection Description",
max_supply,
"My Collection",
royalty,
"https://mycollection.com",
);
}

如需无限供应量的 Collection,可使用 collection::create_unlimited_collection:

use std::option::{Self, Option};
public entry fun create_collection(creator: &signer) {
let royalty = option::none();
collection::create_unlimited_collection(
creator,
"My Collection Description",
"My Collection",
royalty,
"https://mycollection.com",
);
}

每个 Collection 都是 Move Object,可通过生成权限 Ref 进行自定义.每个 Ref 允许你后续修改 Object 的某一方面.除了常规 Object Refs,Collection 还可通过 get_mutator_ref 获取 MutatorRef:

use std::option::{Self, Option};
public entry fun create_collection(creator: &signer) {
let royalty = option::none();
let collection_constructor_ref = &collection::create_unlimited_collection(
creator,
"My Collection Description",
"My Collection",
royalty,
"https://mycollection.com",
);
let mutator_ref = collection::get_mutator_ref(collection_constructor_ref);
// 将 mutator ref 安全存储
}

你还可以通过智能合约为 Collection 添加更多资源或功能.例如,可以记录创建时间以限制 Token 铸造时间:

use std::option::{Self, Option};
struct MyCollectionMetadata has key {
creation_timestamp_secs: u64,
}
public entry fun create_collection(creator: &signer) {
let royalty = option::none();
// Constructor ref 是创建新 object 时返回的不可存储结构体。
// 可生成 object signer 以向 collection object 添加资源。
let collection_constructor_ref = &collection::create_unlimited_collection(
creator,
"My Collection Description",
"My Collection",
royalty,
"https://mycollection.com",
);
// Constructor ref 可换取 signer 以向 collection object 添加资源。
let collection_signer = &object::generate_signer(collection_constructor_ref);
move_to(collection_signer, MyCollectionMetadata { creation_timestamp_secs: timestamp::now_seconds() })
}
字段描述
Description可选字符串,少于 2048 字符(可通过 MutatorRef 修改).
Name必填字符串,用于标识 Collection,在每个 Collection 内唯一.即单个 Collection 账户不能有多个同名 Token.
Royalty可选 Royalty 结构体,表示销售价格中归创作者的分成比例.可通过 Royalty 模块 生成的 MutatorRef 修改(该模块为 DA 标准扩展,示例见 aptos_token.move).通常 royalty 设置在 collection 上,但也可为单独的 Token 设置自定义分成比例.
URI length可选字符串,少于 512 字符,链接到 Collection 相关内容(可通过 MutatorRef 修改).

有几种方式可以创建 Token:

  1. 命名 Token.使用 Token 名称生成命名 Object.这样如果知道 token 和 Collection 名称,便于查找 token 地址,但命名 Object 不可删除.尝试删除命名 token 只会删除数据,Object 本身不会被删除.
use aptos_token_objects::token;
use std::option::{Self, Option};
public entry fun mint_token(creator: &signer) {
let royalty = option::none();
token::create_named_token(
creator,
"Collection Name",
"Description",
"Token Name",
royalty,
"https://mycollection.com/my-named-token.jpeg",
);
}
  1. “未命名” token.创建未命名 Object(可删除),但仍有 Token 名称.由于 Object 地址不可预测,需通过 Indexer 查找其地址.
use aptos_token_objects::token;
use std::option::{Self, Option};
public entry fun mint_token(creator: &signer) {
let royalty = option::none();
token::create(
creator,
"Collection Name",
"Description",
"Token Name",
royalty,
"https://mycollection.com/my-named-token.jpeg",
);
}

通过 Indexer 查找未命名 Token 地址

Section titled “通过 Indexer 查找未命名 Token 地址”

你可以通过 Aptos Indexer 查询最近创建的”未命名” Token 地址,示例如下:

  1. 通过账户地址和 Collection 名称查找 collection id.
  1. 再通过 collection_id(上一步结果)和 token 名称查找 Token 的地址(token_data_id):

可通过 object::transfer 转移 Token.

public entry fun transfer<T: key>(owner: &signer, object: object::Object<T>, to: address)

销毁 / 删除 Token 需先用 token::generate_burn_ref 生成 BurnRef,再调用 token::burn.

module 0x42::example {
use std::option;
use aptos_token_objects::token::{Self, BurnRef, Token};
use std::string::utf8;
use aptos_framework::object::{Self, Object};
struct CustomData has key, drop {
burn_ref: BurnRef,
}
public entry fun mint_token(creator: &signer) {
let token_constructor_ref = &token::create(
creator,
utf8(b"My Collection"),
utf8(b"My named Token description"),
utf8(b"My named token"),
option::none(),
utf8(b"https://mycollection.com/my-named-token.jpeg"),
);
let token_signer = &object::generate_signer(token_constructor_ref);
let burn_ref = token::generate_burn_ref(token_constructor_ref);
// 将 burn ref 安全存储
move_to(token_signer, CustomData {
burn_ref,
});
}
public entry fun burn_token(token: Object<Token>) acquires CustomData {
let token_address = object::object_address(&token);
// 删除 token object 上的所有自定义数据。
// 从存储中取出 burn ref
let CustomData { burn_ref } = move_from<CustomData>(token_address);
// 销毁 token
token::burn(burn_ref);
}
}

要修改 TokenURIdescription,需在创建时生成 MutatorRef 并存储,后续使用.

module 0x42::example {
use std::option;
use aptos_token_objects::token::{Self, MutatorRef, Token};
use std::string::utf8;
use aptos_framework::object::{Self, Object};
struct CustomData has key, drop {
mutator_ref: MutatorRef,
}
public entry fun mint_token(creator: &signer) {
// Constructor ref 是创建新 object 时返回的不可存储结构体。
// 可换取 signer 以向 token object 添加资源。
let token_constructor_ref = &token::create(
creator,
utf8(b"My Collection"),
utf8(b"My named Token description"),
utf8(b"My named Token"),
option::none(),
utf8(b"https://mycollection.com/my-named-token.jpeg"),
);
let token_signer = &object::generate_signer(token_constructor_ref);
let mutator_ref = token::generate_mutator_ref(token_constructor_ref);
// 将 mutator ref 安全存储
move_to(token_signer, CustomData {
mutator_ref,
});
}
public entry fun mutate_token(token: Object<Token>) acquires CustomData {
let token_address = object::object_address(&token);
// 从存储中取出 mutator ref
let CustomData { mutator_ref } = move_from<CustomData>(token_address);
// 修改 token 描述
token::set_description(&mutator_ref, utf8(b"This is my named Token description"));
}
}

Token 可通过添加额外资源(作为 Object)或使用 Ref 修改 Object 进行扩展.

如果你不想自定义 NFT 逻辑,可直接使用 aptos_token 模块铸造 NFT.该模块已部署在 0x4,支持:

  1. 铸造可转让,带分成的 Token.
  2. 铸造灵魂绑定 Token.
  3. 管理 NFT 的资源.

所有可用辅助函数见 aptos_token 参考文档.

使用 aptos_token 铸造 Token 所需参数与实现 DA 标准的 token 相同.此外,aptos_token 模块允许你指定属性键值对(property map)以满足 NFT 的其他需求.

可通过调用 aptos_token::mint 铸造 Token:

public entry fun mint(
creator: &signer,
collection: String,
description: String,
name: String,
uri: String,
property_keys: vector<String>,
property_types: vector<String>,
property_values: vector<vector<u8>>,
) acquires AptosCollection, AptosToken

如需铸造灵魂绑定 Token,可调用 aptos_token::mint_soul_bound:

public entry fun mint_soul_bound(
creator: &signer,
collection: String,
description: String,
name: String,
uri: String,
property_keys: vector<String>,
property_types: vector<String>,
property_values: vector<vector<u8>>,
soul_bound_to: address,
) acquires AptosCollection