Aptos 数字资产(DA)标准
数字资产(DA)标准是 Aptos 上现代的非同质化代币(NFT)标准。NFT 代表链上的独特资产,并存储在集合中。这些 NFT 可以自定义,后续可通过你的智能合约进行转移、灵魂绑定、销毁、变更或扩展。
该标准取代了旧的 Aptos Token Standard。主要改进如下:
改进项 | 描述 |
---|---|
Token 扩展性 | Token 通过 Move Objects 实现,易于扩展。 |
直接 NFT 转账 | 现在可以直接转移 NFT,无需接收方在链上”预先注册”。 |
NFT 组合性 | NFT 可以拥有其他 NFT,便于组合。 |
如果你只需要简单铸造 NFT,无需自定义或扩展功能,可以使用实现了 DA 标准的 aptos_token
模块(见下文用法)。
请注意,所有数字资产模块都部署在保留地址 0x4
。
使用数字资产标准
该标准通过两个 Object 实现:
Collection
- 具有名称和上下文信息的 NFT 集合。Token
- 代表独特资产的数字资产。通常用于表示 NFT,通常使用uri
链接指向资产的更多信息(如图片、视频等)。
所有 Token
必须引用一个父 Collection
,但父 Collection
不拥有 Token
。新铸造的 Token
通常归创建者所有,之后可以转移给其他账户。
集合(Collections)
字段 | 描述 |
---|---|
Description | 可选字符串,少于 2048 字符(可通过 MutatorRef 修改)。 |
Name | 必填字符串,用于标识 Collection 。名称在每个账户内必须唯一,即单个创建者账户不能创建多个同名 Collection 。 |
Royalty | 可选 Royalty 结构体,表示销售价格中归创作者的分成比例。可通过 Royalty 模块 生成的 MutatorRef 修改。该模块是 DA 标准的扩展。示例见 aptos_token.move 。 |
URI length | 可选字符串,少于 512 字符,链接到 Collection 相关内容(可通过 MutatorRef 修改)。 |
创建 Collection
根据是否需要最大 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
的最大供应量创建后不可更改。
自定义 Collection
每个 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 安全存储
}
Ref 必须在 Object 创建时生成。用于生成其他 Ref 的 ConstructorRef
在创建 Object 的交易结束后即失效。
你还可以通过智能合约为 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() })
}
Token
字段 | 描述 |
---|---|
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
有几种方式可以创建 Token
:
- 命名 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",
);
}
你可以通过以下方式推导命名 token 的地址:
- 拼接创建者地址、collection 名称和 token 名称。
- 对拼接后的字符串做 sha256 哈希。
- “未命名” 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 地址
你可以通过 Aptos Indexer 查询最近创建的”未命名” Token
地址,示例如下:
- 通过账户地址和
Collection
名称查找 collection id。
- 再通过 collection_id(上一步结果)和 token 名称查找
Token
的地址(token_data_id
):
一般来说,使用未命名 token 灵活性更高,因为 Object 可后续删除;而命名 token 便于地址查找。
Token 用法
Token 转账
可通过 object::transfer
转移 Token
。
public entry fun transfer<T: key>(owner: &signer, object: object::Object<T>, to: address)
Token 销毁
销毁 / 删除 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);
}
}
如果 Token 上有自定义资源,必须先移除/删除,token::burn
才能删除 Token。对于不可删除的命名 token,token::burn
只会移除 Token 内容。
Token 创建后修改
要修改 Token
的 URI
或 description
,需在创建时生成 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"));
}
}
修改 royalty 需通过 Royalty 模块 生成 单独 的 MutatorRef
。
Token 扩展
Token
可通过添加额外资源(作为 Object)或使用 Ref
修改 Object 进行扩展。
Aptos Token
如果你不想自定义 NFT 逻辑,可直接使用 aptos_token
模块铸造 NFT。该模块已部署在 0x4
,支持:
- 铸造可转让、带分成的
Token
。 - 铸造灵魂绑定
Token
。 - 管理 NFT 的资源。
所有可用辅助函数见 aptos_token
参考文档。
使用 aptos_token
模块的主要限制是 Token 不可扩展(mint
函数不会返回 ConstructorRef
)。
使用 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
如需铸造灵魂绑定 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
近期将发布新模块 TokenMinter
替代 aptos_token
。可在 此处 跟进进展。