(十二)Move 语言学习笔记:妙趣横生的 “对象转移” 探秘
在区块链世界中,Move 语言作为一种灵活、高效的智能合约语言,其独特的设计理念和功能让人眼前一亮。今天我们将深入探讨一个核心功能——对象转移(Transfer to Object)。这一功能不仅解锁了 Move 语言在资产管理中的诸多潜力,也为实现复杂的业务逻辑提供了新的可能性。
一、对象转移的本质:从地址到对象
1. 对象与地址:一脉相承
在 Sui 网络中,对象 ID 和地址 ID 都是 32 字节长,且设计上不重叠。这种统一的设计使得对象 ID 可以直接用于接收转移对象,仿佛它是一个普通的地址。
// 将对象 b 转移到地址 0xADD
sui::transfer::public_transfer(b, @0xADD);
// 将对象 c 转移到对象 ID 为 0x0B 的对象
sui::transfer::public_transfer(c, @0x0B);
无论转移的目标是地址还是对象,其结果都一致——转移后对象的所有者变成所指定的 ID。
2. “父子关系”与访问控制
当对象被转移到另一个对象后,两者之间形成了一种“父子认证”关系。接收对象的模块负责定义访问控制规则,限制子对象的访问权限。这一动态规则由父对象的 UID 在事务执行时实现。
- 子对象的所有权链可以是动态的,例如动态字段中的对象。
- 如果父对象是不可变的,无法将对象转移至其上。
二、转移对象的实际应用
1. 稳定的账户 ID
通过将对象转移到其他对象或地址,可以保持账户或钱包的稳定 ID。无论该对象被转移、包装还是作为动态字段持有,其 ID 始终不变。
- 对象的子对象会随着父对象一起移动。
- 对象的地址始终保持一致。
2. 使用 JSON-RPC 查询对象所有权
Sui 的 JSON-RPC 接口支持查询特定地址或对象 ID 所拥有的对象。
// 查询地址 0xADD 所拥有的对象
{
"jsonrpc": "2.0",
"id": 1,
"method": "suix_getOwnedObjects",
"params": ["0xADD"]
}
// 查询对象 ID 0x0B 所拥有的对象
{
"jsonrpc": "2.0",
"id": 1,
"method": "suix_getOwnedObjects",
"params": ["0x0B"]
}
三、接收对象:从传递到使用
1. 接收机制的核心接口
Move 中,sui::transfer::receive
和 sui::transfer::public_receive
是接收对象的关键函数。
receive
: 只能在定义目标对象类型的模块内使用,适用于具有 key 能力的对象。public_receive
: 可在任何模块中调用,适用于同时具备 key 和 store 能力的对象。
module sui::transfer {
public native fun receive<T: key>(parent: &mut UID, object: Receiving<T>): T;
public native fun public_receive<T: key + store>(parent: &mut UID, object: Receiving<T>): T;
}
// 接收类型为 Coin 的对象
let coin = sui::transfer::public_receive(&mut account.id, sent_coin);
2. 自定义接收规则
通过对父对象模块中 UID 的访问权限控制,可以实现自定义接收规则。
- 限制特定地址才能接收对象。
- 对对象类型或状态施加动态约束。
module examples::shared_object_auth {
struct SharedObject has key {
id: object::UID,
counter: u64,
}
const AuthorizedReceiverAddr: address = @0xB0B;
public fun receive_object<T: key + store>(obj: &mut SharedObject, sent: Receiving<T>, ctx: &TxContext): T {
assert!(tx_context::sender(ctx) == AuthorizedReceiverAddr, 0);
transfer::public_receive(&mut obj.id, sent)
}
}
四、案例:对象转移的多样化实现
1. 基本账户模型
通过动态字段实现账户余额管理。
- 接收不同类型的代币,并按类型存储。
- 支持余额查询和提现功能。
module examples::account {
struct Account has key {
id: object::UID,
}
struct AccountBalance<phantom T> has copy, drop, store { }
public fun accept_payment<T>(account: &mut Account, sent: Receiving<Coin<T>>) {
let coin = transfer::public_receive(&mut account.id, sent);
if (df::exists_(&mut account.id, AccountBalance<T>{})) {
let balance = df::borrow_mut(&mut account.id, AccountBalance<T>{});
coin::join(balance, coin);
} else {
df::add(&mut account.id, AccountBalance<T>{}, coin);
}
}
}
2. “灵魂绑定”对象
通过自定义规则,实现一个必须“归还”的特殊对象。
- 收到对象时生成一个回执,必须在同一事务中销毁。
- 回执仅允许将对象转回原始父对象。
module examples::soul_bound {
struct SoulBound has key { id: UID }
struct ReturnReceipt {
object_id: ID,
return_to: address,
}
public fun get_object(parent: &mut UID, soul_bound_ticket: Receiving<SoulBound>): (SoulBound, ReturnReceipt) {
let soul_bound = transfer::receive(parent, soul_bound_ticket);
let receipt = ReturnReceipt {
return_to: object::uid_to_address(parent),
object_id: object::id(&soul_bound),
};
(soul_bound, receipt)
}
public fun return_object(soul_bound: SoulBound, receipt: ReturnReceipt) {
assert!(object::id(&soul_bound) == receipt.object_id, 0);
sui::transfer::transfer(soul_bound, receipt.return_to);
}
}
五、结语:对象转移的未来展望
对象转移功能是 Move 语言设计中的一颗明珠。它不仅带来了灵活的资产管理能力,还为去中心化应用的设计提供了无限可能。从账户模型到灵魂绑定对象,Move 的独特语言特性为开发者创造了全新的世界。
请用微信关注《HOH水分子》公众号,我们将持续分享和制作变成语言教程,让大家对编程产生化学反应。
版权声明
本文仅代表作者观点,不代表区块链技术网立场。
本文系作者授权本站发表,未经许可,不得转载。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。