数字资产:作为对象的 Token

AIP-11 提案为 Aptos 提出了一种新的 Token 标准,该标准使用 Move 对象将 Token 表示为更大的资产集合中的唯一资产。

AIP-11 - 数字资产:对象形式的 Token

摘要

此 AIP 提议一个使用 Move 对象在 Aptos 上实现的新 Token 标准。在此模型中,一个 token 代表一个大型资产集合中的唯一资产。集合和资产的 Move 对象表示都采用了对象模型的可扩展性,以支持创建丰富的应用程序,例如 NFT、游戏资产、无代码解决方案,同时保持完全的接口兼容性。这允许利用 token 的应用程序专注于 token 更高层次的目的,或概括所有 token,例如特定的游戏发行商的 token 店面和 token 市场之间的区别。

动机

对象提供了一种表示 token 的自然方式,具有以下特性:

  • 全局可寻址。早期的 Aptos token 标准允许将 token 放置在任何地方,如果用户没有仔细跟踪他们的 token,这可能会导致令人困惑的用户体验。
  • 通用状态。每个 token 共享相同的核心数据模型和业务逻辑,从而允许通用的 token 应用程序。
  • 可扩展。Token 公开允许修改核心业务逻辑的逻辑。
  • 存储高效。每个 token 将其资源存储在资源组中,资源组可以由其他层共享。
  • 可组合。Token 可以拥有 token,允许创建者构建丰富的链上应用程序。
  • 简化。最后,token 可以具有明确定义的属性,如灵魂绑定、销毁和冻结,这些属性可以无缝工作。

许多相同的属性同样适用于集合。

理由

现有的 Token 模型有两个问题:极其全面以及在 Move 中使用 store 能力。为了解决对象出现之前 Move 的局限性,token 标准别无选择,只能尝试解决 token 世界中尽可能多的可预见挑战,并且如果未能解决就会遭受损失。这导致了诸如 property_versions 等鲜为人知的功能、缺乏冻结或灵魂绑定功能以及其他几个问题。此外,由于该模型利用 store,token 数据可以放置在链上的任何位置,这可能会导致大量用户体验问题。这种基于对象的模型解决了许多已知问题,但需要权衡的是,现有基础设施可能需要移植到新模型。尽管索引器和 SDK 的用户可能不会注意到差异。

与此同时,人们期望继续管理和支持现有的 token 标准,并为现有 token 提供迁移到此标准的路径。同样重要的是,社区需要认识到需要适应 token 标准中的定期迭代,因为 Move 仍然是一种非常年轻的语言,并且正在探索和评估许多功能和范例。

规范

Token 和集合的对象 ID

由于 token 构建在对象之上,因此它们必须遵守对象 ID 的生成方式。对象 ID 使用以下方式生成:sha3_256(address_of(creator) | seed | 0xFE)sha3_256(GUID | 0xFD)。Token 可以使用 seed 字段为每个 token 生成唯一的地址。Token 在生成 seed 时使用以下内容:bcs::to_bytes(collection) | b"::" | bcs::to_bytes(name),并且还支持通过 GUID 生成 ObjectIds。类似地,集合在生成 seed 时使用以下内容:bcs::to_bytes(collection)。这可确保所有集合和 token 的全局唯一性,除非 token 或集合已被销毁。请注意,选择 0xFE 是为了提供域分离,并确保地址生成对于对象是唯一的,以防止对象和帐户之间生成重复的地址。

由于可发现性方面的挑战,当前版本的 token 未利用基于 GUID 的对象 ID 生成。具体来说,一旦 Aptos 具有足够的节点上索引来识别基于 GUID 的对象,将推出另一个标准。

核心逻辑

token 标准由 token、集合和版税组成。

Token

没有首先存在的集合,就无法创建 token。

集合的创建者是唯一能够向该集合添加 token 的实体。

token 公开 ref,允许修改 token 字段和销毁 token。

它不会更改 Move 对象的基础可扩展性。

集合

集合支持供应量跟踪。目前,集合可以使用 FixedSupply 跟踪器或 UnlimitedSupply 跟踪器。FixedSupplyUnlimitedSupply 都会跟踪所有铸造事件以及销毁和铸造事件。此外,FixedSupply 指定与集合关联的最大 token 数量。不使用跟踪器允许并行性,但会牺牲指定最大 token 数量或为每个 token 提供集合中的唯一索引。

集合公开一个 mutation ref,允许修改集合字段。

它不会更改 Move 对象的基础可扩展性。

版税

版税指定了一种方式,指示实体在销售中获得部分付款。

使用可扩展模型,版税可以存在于集合级别或 token 级别。如果两者都定义了,则接口将优先选择 token 级别的版税。

版税公开一个 mutation ref,允许创建和更新与集合或 token 关联的版税。

它不会更改 Move 对象的基础可扩展性。

数据结构

此标准指定 token、集合和版税的数据结构。

Token 数据结构

##[resource_group_member(group = aptos_framework::object::ObjectGroup)]
/// 表示所有 token 的公共字段。
struct Token has key {
    /// 此 token 所在的集合。
    collection: Object<Collection>,
    /// 集合中的唯一标识符,可选,0 表示未分配
    collection_id: u64,
    /// token 的简短描述。
    description: String,
    /// token 的名称,在集合中应该是唯一的;名称的长度应小于 128 个字符,例如:“Aptos Animal #1234”
    name: String,
    /// token 的创建名称。由于 token 的创建名称是
    /// 指向存储在链下存储中的 JSON 文件的统一资源标识符 (uri)。
    uri: String,
    /// 在 token 的任何修改时发出。
    mutation_events: event::EventHandle<MutationEvent>,
}

/// 如果可能,这可以销毁 NFT,它也会删除该对象。请注意,内部和 self 中的数据
/// 每个占据 32 字节,而不是两者都有,此数据结构进行了一个小的优化,以支持两者并占用固定数量的 34 字节。
struct BurnRef has drop, store {
    inner: Option<object::DeleteRef>,
    self: Option<address>,
}

/// 这使更高级别的服务能够修改描述和 URI。
struct MutatorRef has drop, store {
    self: address,
}

/// 包含修改后的字段名称。这使索引器的生活更轻松,以便它们可以直接理解写入集中的行为。
struct MutationEvent has drop, store {
    mutated_field_name: String,
}
集合数据结构
##[resource_group_member(group = aptos_framework::object::ObjectGroup)]
/// 表示集合的公共字段。
struct Collection has key {
    /// 此集合的创建者。
    creator: address,
    /// 集合的简短描述。
    description: String,
    /// 相似 token 的可选分类。
    name: String,
    /// 指向存储在链下存储中的 JSON 文件的统一资源标识符 (uri);URL 长度可能需要最大值,有什么建议吗?
    uri: String,
    /// 在集合的任何修改时发出。
    mutation_events: event::EventHandle<MutationEvent>,
}

/// 这使更高级别的服务能够修改描述和 URI。
struct MutatorRef has drop, store {
    self: address,
}

/// 包含修改后的字段名称。这使索引器的生活更轻松,以便它们可以直接理解写入集中的行为。
struct MutationEvent has drop, store {
    mutated_field_name: String,
}

##[resource_group_member(group = aptos_framework::object::ObjectGroup)]
/// 固定供应量跟踪器,这对于确保铸造有限数量的 token 非常有用。
/// 并向集合添加事件和供应量跟踪。
struct FixedSupply has key {
    /// 总铸造量 - 总销毁量
    current_supply: u64,
    max_supply: u64,
    total_minted: u64,
    /// 在销毁 Token 时发出。
    burn_events: event::EventHandle<BurnEvent>,
    /// 在铸造 Token 时发出。
    mint_events: event::EventHandle<MintEvent>,
}

##[resource_group_member(group = aptos_framework::object::ObjectGroup)]
/// 无限制供应量跟踪器,这对于向集合添加事件和供应量跟踪非常有用。
struct UnlimitedSupply has key {
    current_supply: u64,
    total_minted: u64,
    /// 在销毁 Token 时发出。
    burn_events: event::EventHandle<BurnEvent>,
    /// 在铸造 Token 时发出。
    mint_events: event::EventHandle<MintEvent>,
}

数据限制

为了确保访问链上存储的数据(例如,在索引或查询 API 期间),该标准对具有动态长度的字段采用了一些限制:

  • 描述字段最多可以包含 2048 个字符。
  • 名称字段最多可以包含 128 个字符。
  • URI 字段最多可以包含 512 个字符。
版税数据结构
##[resource_group_member(group = aptos_framework::object::ObjectGroup)]
/// 此集合中 token 的版税 -- 这是可选的
struct Royalty has copy, drop, key {
    numerator: u64,
    denominator: u64,
    /// 版税支付的接收者。有关如何处理多个
    /// 创建者,请参阅 `shared_account`。
    payee_address: address,
}

/// 这可以创建或覆盖 MutatorRef。
struct MutatorRef has drop, store {
    inner: ExtendRef,
}

APIs

该标准包含用于访问、修改和删除 token、集合和版税的所有可行 API。没有入口函数,因为 token 标准将其留给更具体的实现。

Token API
/// 从 token 名称创建一个新的 token 对象,并返回 ConstructorRef 以进行
/// 其他专业化。
public fun create_named_token(
    creator: &signer,
    collection_name: String,
    description: String,
    name: String,
    royalty: Option<Royalty>,
    uri: String,
): ConstructorRef;

/// 从帐户 GUID 创建一个新的 token 对象,并返回 ConstructorRef 以进行
/// 其他专业化。
public fun create_from_account(
    creator: &signer,
    collection_name: String,
    description: String,
    name: String,
    royalty: Option<Royalty>,
    uri: String,
): ConstructorRef;

/// 根据创建者的地址和集合的名称生成集合的地址
public fun create_token_address(creator: &address, collection: &String, name: &String): address;

/// 命名对象派生自种子,集合的种子是其名称。
public fun create_token_seed(collection: &String, name: &String): vector<u8>;

/// 创建一个 MutatorRef,它控制修改任何支持修改的字段的能力。
public fun generate_mutator_ref(ref: &ConstructorRef): MutatorRef;

/// 创建一个 BurnRef,它控制销毁给定 token 的能力。
public fun generate_burn_ref(ref: &ConstructorRef): BurnRef;

/// 从 BurnRef 中提取 token 的地址。
public fun address_from_burn_ref(ref: &BurnRef): address;

public fun creator<T: key>(token: Object<T>): address;

public fun collection<T: key>(token: Object<T>): String;

public fun collection_object<T: key>(token: Object<T>): Object<Collection>;

public fun description<T: key>(token: Object<T>): String;

public fun name<T: key>(token: Object<T>): String;

public fun uri<T: key>(token: Object<T>): String;

public fun royalty<T: key>(token: Object<T>): Option<Royalty>;

public fun burn(burn_ref: BurnRef);

public fun set_description(mutator_ref: &MutatorRef, description: String);

public fun set_name(mutator_ref: &MutatorRef, name: String);

public fun set_uri(mutator_ref: &MutatorRef, uri: String);
集合 API
/// 创建一个固定大小的集合,或一个支持固定数量的 token 的集合。
/// 这对于在链上创建有保证的、有限的供应数字资产非常有用。例如,
/// 集合 1111 vicious vipers。请注意,创建诸如向上限制之类的限制会导致
/// 数据结构,阻止 Aptos 并行化此集合类型的铸造。
public fun create_fixed_collection(
    creator: &signer,
    description: String,
    max_supply: u64,
    name: String,
    royalty: Option<Royalty>,
    uri: String,
): ConstructorRef;

/// 创建一个无限集合。这支持供应量跟踪,但不限制
/// token 的供应。
public fun create_unlimited_collection(
    creator: &signer,
    description: String,
    name: String,
    royalty: Option<Royalty>,
    uri: String,
): ConstructorRef;

public fun create_collection_address(creator: &address, name: &String): address;

/// 命名对象派生自种子,集合的种子是其名称。
public fun create_collection_seed(name: &String): vector<u8>;

/// 创建一个 MutatorRef,它控制修改任何支持修改的字段的能力。
public fun generate_mutator_ref(ref: &ConstructorRef): MutatorRef;

public fun count<T: key>(collection: Object<T>): Option<u64> acquires FixedSupply;

public fun creator<T: key>(collection: Object<T>): address;

public fun description<T: key>(collection: Object<T>): String;

public fun name<T: key>(collection: Object<T>): String;

public fun uri<T: key>(collection: Object<T>): String;

public fun set_description(mutator_ref: &MutatorRef description: String);

public fun set_uri(mutator_ref: &MutatorRef, uri: String);

版税 API

/// 添加版税,给定一个 ConstructorRef。
public fun init(ref: &ConstructorRef, royalty: Royalty);

/// 如果版税不存在,则设置版税,否则替换它。
public fun update(mutator_ref: &MutatorRef, royalty: Royalty);

public fun create(numerator: u64, denominator: u64, payee_address: address): Royalty;

public fun generate_mutator_ref(ref: ExtendRef): MutatorRef;

public fun exists_at(addr: address): bool;

public fun get<T: key>(maybe_royalty: Object<T>): Option<Royalty>;

public fun denominator(royalty: &Royalty): u64;

public fun numerator(royalty: &Royalty): u64;

public fun payee_address(royalty: &Royalty): address;

参考实现

https://github.com/aptos-labs/aptos-core/pull/7277 中的第一个提交

风险和缺点

在 token 领域快速迭代可能会对可能希望保持稳定的构建者产生负面影响。至关重要的是,我们要认识到迭代的正确频率和动机。我们也不希望由于这种紧张关系而限制可以构建的内容的潜力。

未来潜力

作为对象的 Token 允许更丰富的表达,并将核心 token 代码与 token 的应用程序分离。在此时间点之后,核心 token 代码发生的变化可能非常有限,因为新的应用程序会生成自己的代码,该代码位于核心 token 模块之上。

建议的实施时间表

  • 自 2023 年 1 月以来,一个探索性版本已存在于 move-examples 中。
  • 到 2023 年 3 月中旬,在框架上达成总体一致。
  • 到 2023 年 5 月中旬在主网上发布 -- 版本 1.4。
  • 原文链接: github.com/aptos-foundat...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
aptos-foundation
aptos-foundation
江湖只有他的大名,没有他的介绍。