Aptos Token Object V2 | Move dApp 极速入门(贰拾肆)
作者介绍:
I’m Aladeen, fresh graduate student and novi blockchain developer. I love meeting people across the globe and make new connections. I’m looking for people that have interest in coding and problem solving to join me for a wonderful journey so if you’re interested feel free to reach out. Cheers!
作者Github:https://github.com/Aladeenb
Move Learning Camp:
https://github.com/NonceGeek/Web3-dApp-Camp
「Token」一词通常是指链上同质化代币和非同质化代币。随着区块链的发展,定义代币的标准和模型也在不断发展。在 Aptos 中,「Token V2」是指升级后的非同质化代币标准。原始模型「Token V1」使用资源 Resource 作为链上代币的表示方式,而新模型则使用 Object。这一升级打破了初始版本的限制,使其能从对象模型中受益。
0x01 Token V1:概述
Aptos 区块链中的原始同质化代币模型称为「Token V1」,使用资源作为在链上表示代币的方式。这种模式有优点也有缺点。
1.1 优点
「Token V1」可存储在链上资源中,可以最大限度地减少与模块 Modules 本身的手动交互次数,并确保 Token属性的不变性。
它还使用「PropertyMap」表示 Token在链上的特征。这允许更多的可扩展性,这意味着我们可以动态存储各种数据类型的属性,以便在初始创建后向Token添加更多特征。它还使用允许存储不同类型的数据通过 Binary Canonical Serialization (BCS) 的序列化和反序列化后的结果
1.2 缺点
在此 Token模型中,数据存储在资源帐户中,这意味着 Token 可以嵌套在多层数据中,从而导致各种问题,例如查询所有者。
「PropertyMap」提供了很强的灵活性和稳健性,但这是以增加内存使用和计算开销为代价的。
0x02 Token V2:新时代
Token V2是指Aptos区块链内升级的非同质化代币标准,使用的是对象模型。
2.1 Token V2 组件
Aptos token object model 由以下模块组成:
collection.move
:定义基于对象的集合。token.move
:定义基于对象的Token(TokenV2)的核心逻辑。royalty.move
:定义基于对象的版税系统。版税可用于Token 合集(Collection)或独立 Token。aptos_token.move
:定义无代码解决方案的基本Token,类似于 0x3::token 模块中的原始Token。具有基本的 Token 和 Collection 功能。允许创建者控制Token 如何变化以及其是否可冻结、可焚毁和可铸币。它还具有传输对象和相关事件的标准方法,以及元数据的属性类型。property_map.move
:为「AptosToken」提供通用元数据帮助。它是“SimpleMap”的特定版本,通过利用常量 u64 来表示类型并以 bcs 格式存储值,确保精确的数据类型并节省存储空间。
2.2 Token V2 的特点
Token V2 构建在对象模型之上,这意味着Token是属于集合(也是对象)一部分的对象。这提供了多种新功能,同时成本仍然低廉。
对象模型将所有权与数据分离,这意味着所有对象资源都是顶级数据,使它们可以全局寻址和查询。
在可扩展性方面,即使在最初创建对象之后,您也可以向对象添加资源,这允许将来具有更多的可扩展性和灵活性。
在可组合性方面,对象与帐户非常相似,它们甚至可以拥有其他对象,这使得能够创建真正的链上对象可组合性,即彼此存储 native, typed objects 的能力。
点击阅读有关对象模型的更多信息:
https://noncegeek.medium.com/aptos-object-model-learning-move-0x01-550708f0fd33
2.3 Token V2 的生命周期
Entity 调用顶级对象类型上的 create。 顶级对象类型在其 direct ancestor 上调用 create 。 重复此操作,直到最顶层的 ancestor 是「Object」结构,该结构定义了 create_object
函数。create_object
生成一个新地址,在ObjectGroup
资源组内的该地址存储一个Object
结构,并返回一个ConstructorRef
。之前调用的创建函数可以使用 ConstructorRef
来获取签名者,在ObjectGroup
资源中存储适当的结构,进行任何其他修改,然后将ConstructorRef
返回堆栈。在对象创建堆栈中,任何模块都可以定义所有权、删除性、可转移性和可变性等属性。
0x03 Token V1 与Token V2 的比较
Token V1 | Token V2 |
---|---|
基于资源模型 | 基于对象模型 |
将其特征存储在链上的成本昂贵 | 在链上存储其特征的成本低廉 |
无法拥有Token | 可以拥有 Token |
可扩展 | 更具可扩展性 |
在某些情况下,数据既不可寻址也不可查询 | 数据始终是全局可寻址和可查询的 |
总之,从 Token V1 到 Token V2 的演变代表了 Aptos 区块链的Token 标准的超级重大飞跃。
新模型提供了更大的灵活性、可扩展性和可组合性,为更复杂和动态的链上交互铺平了道路。
0x04 Tolem V2 的实际应用:hero.move
在学习语言的过程中,没有比向其先驱学习更好的方法了。Aptos 实验室在 aptos-core repository 中提供了如何使用新代币模型的示例:
https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/token_objects/hero
截至撰写本文时,「hero.move」是 token V2 最简单但完整的示例。
智能合约允许创建 Hero,为其添加属性,并将 Hero 本身及其属性转移到另一个帐户:
struct Hero has key {
armor: Option<Object<Armor>>,
gender: String,
race: String,
shield: Option<Object<Shield>>,
weapon: Option<Object<Weapon>>,
mutator_ref: token::MutatorRef,
}
“hero”是 token,“armor”、“gender”、“race”、“shield”、“weapon”是“hero”中代表其属性的 tokens。
“hero”可以拥有“armor”、“shield”和“weapon”。 “weapon”、“shield”和“armor”可以有“gems”。 “gems”可以改变“weapon”、“shield”和“armor”的“攻击”和“防御”值。它们还具有“魔法”属性。
代码的生命周期如下所示:
4.1 初始化模块
在创建 hero 前,我们需要初始化模块
fun init_module(account: &signer) {
let collection = string::utf8(b"Hero Quest!");
collection::create_unlimited_collection(
account,
string::utf8(b"collection description"),
collection,
option::none(),
string::utf8(b"collection uri"),
);
let on_chain_config = OnChainConfig {
collection,
};
move_to(account, on_chain_config);
}
这是通过创建一个集合并将其存储在“OnChainConfig”资源中来完成的,该资源具有“key”关键字来全局存储它。稍后将对集合进行简单查询。
struct OnChainConfig has key {
collection: String,
}
4.2 创建Hero
public fun create_hero(
creator: &signer,
description: String,
gender: String,
name: String,
race: String,
uri: String,
): Object<Hero> acquires OnChainConfig {
let constructor_ref = create(creator, description, name, uri);
let token_signer = object::generate_signer(&constructor_ref);
let hero = Hero {
armor: option::none(),
gender,
race,
shield: option::none(),
weapon: option::none(),
mutator_ref: token::generate_mutator_ref(&constructor_ref),
};
move_to(&token_signer, hero);
object::address_to_object(signer::address_of(&token_signer))
}
Hero 本质上是一个对象。create_hero
函数创建一个 Hero 并在函数结束之前返回它。
public fun create_hero(
): Object<Hero>
...
object::address_to_object()
新创建的 Hero数据存储在资源帐户中。
let hero = Hero {
armor: option::none(),
gender,
race,
shield: option::none(),
weapon: option::none(),
mutator_ref: token::generate_mutator_ref(&constructor_ref),
};
move_to(&token_signer, hero);
constructor_ref
是对 hero
对象的引用。它生成一个用于完成存储操作的签名人。
create_hero
最终将被包裹在入口函数 mint_hero
中。
entry fun mint_hero(
account: &signer,
description: String,
gender: String,
name: String,
race: String,
uri: String,
) acquires OnChainConfig {
create_hero(account, description, gender, name, race, uri);
}
4.3 创建 Shield、Armor、Weapon 和 Gem
创建这些对象遵循与创建Hero 相同的模式。
public fun create_weapon(
creator: &signer,
attack: u64,
description: String,
name: String,
uri: String,
weapon_type: String,
weight: u64,
): Object<Weapon> acquires OnChainConfig {
let constructor_ref = create(creator, description, name, uri);
let token_signer = object::generate_signer(&constructor_ref);
let weapon = Weapon {
attack,
gem: option::none(),
weapon_type,
weight,
};
move_to(&token_signer, weapon);
object::address_to_object(signer::address_of(&token_signer))
}
4.4 添加/删除 Hero属性
为 Hero 添加属性(或为 Weapon, Armor, Shield 添加 Gem)的步骤很简单:
获取对象 将对象放置入 Hero 对象中 将对象转移给新的所有人
public fun weapon_equip_gem(owner: &signer, weapon: Object<Weapon>, gem: Object<Gem>) acquires Weapon {
let weapon_obj = borrow_global_mut<Weapon>(object::object_address(&weapon));
option::fill(&mut weapon_obj.gem, gem);
object::transfer_to_object(owner, gem, weapon);
}
删除属性也是一样的,只是我们需要检查英雄对象中是否存在该属性。
public fun weapon_unequip_gem(owner: &signer, weapon: Object<Weapon>, gem: Object<Gem>) acquires Weapon {
let weapon_obj = borrow_global_mut<Weapon>(object::object_address(&weapon));
let stored_gem = option::extract(&mut weapon_obj.gem);
assert!(stored_gem == gem, error::not_found(EINVALID_GEM_UNEQUIP));
object::transfer(owner, gem, signer::address_of(owner));
}
0x05 参考
Token v1 代码库:https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/framework/aptos-token Token v2 代码库:https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/framework/aptos-token-objects 对象模型文章:https://noncegeek.medium.com/aptos-object-model-learning-move-0x01-550708f0fd33 Gas 计算规则:https://aptos.dev/concepts/base-gas/#instruction-gas hero.move:https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/token_objects/hero