跳转到内容

使用对象

创建对象后,可以在 Move 入口函数和结构体中使用它,转移它,并使用对象构建期间生成的任意引用修改它。以下介绍在 Move 中使用、管理和与对象交互的多种方式。

Move 函数中的对象类型为 Object<T>,其中 T 是对象拥有的资源类型。所有对象都有包含对象元数据的 ObjectCore 类型。

要使用对象参数,用户可以传入对象地址或对象引用。运行时,合约会在执行函数前验证该地址上存在对象,并且它具有 T 类型的资源。

module my_addr::object_playground {
use aptos_framework::object::{Object, ObjectCore};
struct MyAwesomeStruct has key {}
/// This will fail if the object doesn't have MyAwesomeStruct stored
entry fun do_something(object: Object<MyAwesomeStruct>) {
// ...
}
/// All Objects have ObjectCore, so this will only fail if the
/// address is not an object
entry fun do_something_to_object_core(object: Object<ObjectCore>) {
// ...
}
}

如需让入口函数的用户指定资源类型,可以保留泛型类型 T,如下所示:

module my_addr::object_playground {
use aptos_framework::object::Object;
/// This will fail if the object doesn't have the generic `T` stored
entry fun do_something<T>(object: Object<T>) {
// ...
}
}

可以通过对象拥有的任意资源类型引用对象。为方便起见,只要资源可用,便可使用 address_to_objectconvert 将地址转换为对象,或在对象类型之间转换:

module my_addr::object_playground {
use aptos_framework::object::{Self, Object, ObjectCore};
struct MyAwesomeStruct has key {}
fun convert_type(object: Object<ObjectCore>): Object<MyAwesomeStruct> {
object::convert<ObjectCore, MyAwesomeStruct>(object)
}
fun address_to_type(object_address: address): Object<MyAwesomeStruct> {
object::address_to_object<MyAwesomeStruct>(object_address)
}
}

通过在结构体中使用对象,可以表示复杂类型。例如:

module my_addr::object_playground {
use aptos_framework::object::{Self, Object};
use aptos_framework::fungible_asset::Metadata;
use aptos_framework::primary_fungible_store;
use std::signer;
use std::option;
use std::string::utf8;
struct MyStruct has key {
fungible_asset_object: Object<Metadata>
}
entry fun create_fungible_asset(creator: &signer) {
let fa_obj_constructor_ref = &object::create_sticky_object(@my_addr);
let fa_obj_signer = object::generate_signer(fa_obj_constructor_ref);
let fa_obj_addr = signer::address_of(&fa_obj_signer);
primary_fungible_store::create_primary_store_enabled_fungible_asset(
fa_obj_constructor_ref,
option::none(),
utf8(b"Asset name"),
utf8(b"Asset symbol"),
2,
utf8(b"Icon uri"),
utf8(b"Project uri")
);
move_to(creator, MyStruct {
fungible_asset_object: object::address_to_object(fa_obj_addr)
});
}
}

为对象编写合约时,通常需要在修改对象前验证所有权。由于对象可由任意地址拥有,验证所有权时需要考虑所有者是账户、资源账户还是另一个对象:

module my_addr::object_playground {
use std::signer;
use aptos_framework::object::{Self, Object};
// Not authorized!
const E_NOT_AUTHORIZED: u64 = 1;
fun check_owner_is_caller<T: key>(caller: &signer, object: Object<T>) {
assert!(
object::is_owner(object, signer::address_of(caller)),
E_NOT_AUTHORIZED
);
}
fun check_is_owner_of_object<T: key>(addr: address, object: Object<T>) {
assert!(object::owner(object) == addr, E_NOT_AUTHORIZED);
}
fun check_is_nested_owner_of_object<T: key, U: key>(
caller: &signer,
outside_object: Object<T>,
inside_object: Object<U>
) {
// Ownership expected
// Caller account -> Outside object -> inside object
// Check outside object owns inside object
let outside_address = object::object_address(&outside_object);
assert!(object::owns(inside_object, outside_address), E_NOT_AUTHORIZED);
// Check that the caller owns the outside object
let caller_address = signer::address_of(caller);
assert!(object::owns(outside_object, caller_address), E_NOT_AUTHORIZED);
// Check that the caller owns the inside object (via the outside object)
// This can skip the first two calls (and even more nested)
assert!(object::owns(inside_object, caller_address), E_NOT_AUTHORIZED);
}
}

默认情况下,所有对象均可转移。某些对象在构建时会被配置为禁用 ungated_transfer(有关详细信息,请参阅构建对象)。

可以按如下方式转移对象:

module my_addr::object_playground {
use aptos_framework::object::{Self, Object};
/// Transfer to another address, this can be an object or account
fun transfer<T: key>(owner: &signer, object: Object<T>, destination: address) {
object::transfer(owner, object, destination);
}
/// Transfer to another object
fun transfer_to_object<T: key, U: key>(
owner: &signer,
object: Object<T>,
destination: Object<U>
) {
object::transfer_to_object(owner, object, destination);
}
}

默认情况下,对象只有 TransferEvent,它会在对象被转移时触发。

可以扩展对象以拥有额外事件。

可以使用以下函数为对象创建事件句柄:

module 0x1::object {
/// Create a guid for the object, typically used for events
public fun create_guid(object: &signer): guid::GUID {}
/// Generate a new event handle.
public fun new_event_handle<T: drop + store>(object: &signer): event::EventHandle<T> {}
}

只要持有对象的 SignerRef,就能将生成的事件句柄转移给对象。例如:

module 0x42::example {
use aptos_framework::event;
use aptos_framework::fungible_asset::Metadata;
use aptos_framework::object::{Self, Object};
#[resource_group_member(group = aptos_framework::object::ObjectGroup)]
struct LiquidityPoolResourceGroup has key {
pool: LiquidityPool,
event_store: LiquidityPoolEventStore,
}
struct LiquidityPool has store {
metadata_token_a: Object<Metadata>,
metadata_token_b: Object<Metadata>,
reserves_a: u128,
reserves_b: u128,
}
struct LiquidityPoolEventStore has store {
create_events: event::EventHandle<CreateLiquidityPoolEvent>,
}
#[event]
struct CreateLiquidityPoolEvent has store, drop {
token_a: address,
token_b: address,
reserves_a: u128,
reserves_b: u128,
}
public entry fun create_liquidity_pool_with_events(
account_signer: &signer,
metadata_token_a: Object<Metadata>,
metadata_token_b: Object<Metadata>,
reserves_a: u128,
reserves_b: u128
) {
let liquidity_pool_constructor_ref = &object::create_object_from_account(
account_signer
);
let liquidity_pool_signer = &object::generate_signer(
liquidity_pool_constructor_ref
);
let event_handle = object::new_event_handle<CreateLiquidityPoolEvent>(
liquidity_pool_signer
);
event::emit_event<CreateLiquidityPoolEvent>(&mut event_handle, CreateLiquidityPoolEvent {
token_a: object::object_address(&metadata_token_a),
token_b: object::object_address(&metadata_token_b),
reserves_a,
reserves_b,
});
move_to(liquidity_pool_signer, LiquidityPoolResourceGroup {
pool: LiquidityPool {
metadata_token_a,
metadata_token_b,
reserves_a,
reserves_b
},
event_store: LiquidityPoolEventStore {
create_events: event_handle
}
});
}
}

通常,只能使用构建期间生成的 Refs 修改对象。有关可用的 Refs、如何生成它们及如何使用它们的更多详细信息,请参阅创建和配置对象。这也是向对象添加额外资源、删除对象和扩展对象的方式。

以下是三个使用对象的实际代码示例: