以物件身分接收 (Receiving as Object)
地址擁有的物件狀態支援兩種擁有者:帳戶與另一個物件。若物件已轉移給另一個物件,Sui 提供透過其擁有者的 UID 來接收此物件的方法。
此功能也稱為 「轉移至物件」("Transfer to Object")或 TTO。
定義 (Definition)
接收功能實作於 sui::transfer 模組中。它由特殊型別 Receiving 組成,此型別會透過特殊交易引數具現化;另有 receive 函式,該函式接受父物件的 UID。
transfer::receive 中的 T 受限於內部約束。receive 的公開版本稱為 public_receive;和其他儲存函式一樣,它要求 T 具有 store。
module sui::transfer;
// `Receiving` 引數的暫時性包裝器。作為特殊輸入提供
// 於交易區塊中。
// 注意:必須明確匯入此型別才能使用!
public struct Receiving<phantom T: key> has drop {
id: ID,
version: u64,
}
/// 透過特殊型別 `Receiving`,從父層 `UID` 接收 `T`。
public fun receive<T: key>(parent: &mut UID, to_receive: Receiving<T>): T;
由於 receive 需要父物件 UID 的可變參考,因此只能透過定義父物件的模組,或該模組選擇公開的存取方式來接收。模組未提供接收實作的物件無法釋放轉移給它的物件,因此應謹慎並在受控環境中使用此功能。
範例 (Example)
為說明轉移與接收,請考慮一個 PostOffice,它會註冊郵箱,並讓任何人都能將物件傳送給這些郵箱:
module book::receiving;
use sui::derived_object;
use sui::transfer::Receiving; // not imported by default!
/// Base derivation object to create derived `PostBox`-es.
public struct PostOffice has key { id: UID }
/// Object with derived UID which receives objects sent to an address.
public struct PostBox has key { id: UID, owner: address }
/// Transfer functionality. Anyone can come to the PostOffice and send to a specific
/// recipient's PostBox. Items can be received from the `PostBox` by the recipient.
public fun send<T: key + store>(office: &PostOffice, parcel: T, recipient: address) {
let postbox = derived_object::derive_address(office.id.to_inner(), recipient);
transfer::public_transfer(parcel, postbox)
}
/// Receive the parcel. Requires the sender to be the owner of the `PostBox`!
public fun receive<T: key + store>(
box: &mut PostBox,
to_receive: Receiving<T>,
ctx: &TxContext
): T {
assert!(box.owner == ctx.sender());
// Receive `to_receive` from `PostBox`.
let parcel = transfer::public_receive(&mut box.id, to_receive);
parcel
}
/// If user hasn't claimed their `PostBox` yet, create it.
/// Note: this is not a requirement for transferring assets!
/// Parcels can be sent even to unregistered post boxes, see `send` implementation.
public fun register_address(office: &mut PostOffice, ctx: &mut TxContext) {
transfer::share_object(PostBox {
id: derived_object::claim(&mut office.id, ctx.sender()),
owner: ctx.sender()
})
}
// Create a PostOffice on module publish.
fun init(ctx: &mut TxContext) {
transfer::share_object(PostOffice { id: object::new(ctx) });
}
使用案例 (Use Cases)
轉移至物件是一項強大的功能,可讓物件成為其他物件的擁有者,並支援單純地址擁有權無法表達的設計:
- 受控接收。 由於接收會經過父物件的模組,因此可以附加額外邏輯;例如,上述 PostOffice 可對每個接收的項目收取費用。
- 作為容器的物件。 父物件會收集傳送給它的資產,且其本身可被轉移,並攜帶整個「庫存」——無須在交易中列出內容。
- 延後交付。 資產可在擁有者準備好領取前先傳送給物件,例如郵箱會累積項目,直到使用者啟用其帳戶。
- 類帳戶物件。 具有可接收及釋放資產之 ID 的物件,其行為很像帳戶,使 TTO 成為帳戶抽象化設計的建構基礎。
轉移至物件也自然地支援平行處理:轉移至物件 ID 屬於一般轉移——它們不會在交易中參考父物件,因此不會與其產生競爭。
後續步驟 (Next Steps)
本節結束「使用物件」章節:你現在可以定義物件、將其置於任何擁有權狀態、管理其身分,甚至讓物件擁有其他物件。進階可程式化章節會建立於這些概念之上——從執行環境開始,並透過動態欄位回到物件組合;動態欄位是父子物件關係背後的第二種機制。
延伸閱讀 (Further Reading)
- Sui 文件中的轉移至物件
- sui::transfer 模組文件