Skip to main content

以物件形式接收 (Receiving as Object)

address 擁有 的物件狀態支援兩種擁有者型別:一個帳戶,或另一個物件。如果某物件被轉移到另一個物件,Sui 提供了一種方式,可以透過其擁有者的 UID 接收(receive)這個物件。

這項功能也被稱為 "Transfer to Object",即 TTO。

定義 (Definition)

接收功能是在 sui::transfer 模組中實作的。它包含一個特殊型別 Receiving,透過特殊的交易參數來實例化,以及 receive 函式,該函式接收父物件的 UID

transfer::receive 中的 TInternal Constraint 約束。公開版本的 receive 稱為 public_receive,和其他儲存函式一樣,它要求 T 具有 store

module sui::transfer;

// 一個圍繞 `Receiving` 參數的暫時性包裝。作為交易區塊中的特殊輸入提供。
// 注意:這個型別必須明確 import 才能使用!
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)

轉移到物件是一項強大的功能,它讓物件能夠作為其他物件的擁有者,並且能夠實現純粹的 address 擁有權無法表達的設計:

  • 受控的接收。 因為接收會經過父物件的模組,可以附加額外的邏輯——例如上述的 PostOffice,可以對每一件收到的物品收取費用。
  • 物件作為容器。 父物件收集寄送給它的資產,且自身也能被轉移,並隨身攜帶其整個「庫存」——完全不需要在交易中列出內容物。
  • 延遲交付。 資產可以在擁有者尚未準備好領取之前,就先寄送到某個物件——例如一個郵箱,能持續累積物品,直到使用者啟用其帳戶。
  • 類帳戶物件。 一個具有 ID、能夠接收與釋放資產的物件,其行為就非常類似於一個帳戶,這使得 TTO 成為帳戶抽象(account-abstraction)設計的一個建構區塊。

寄送 到某個物件 本質上也是平行的:對某個物件 ID 的轉移就是單純的轉移——它們在交易中不會參考到父物件,因此不會與父物件產生競爭。

下一步 (Next Steps)

本章節結束了「使用物件」這一章:你現在已經能夠定義物件、將它們置於任何擁有權狀態、管理它們的身分,甚至讓物件擁有其他物件。進階程式設計 這一章會在此基礎上繼續深入——從執行環境開始,並回頭探討物件組合,介紹父子物件關係背後的第二種機制:動態欄位

延伸閱讀 (Further Reading)