Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Transfer 函数参考

本附录汇总 sui::transfer 模块中所有转移函数的签名、用途和权限要求。

函数总览

函数公共变体最终状态权限
transferpublic_transfer地址所有完全权限
share_objectpublic_share_object共享引用、可变引用、删除
freeze_objectpublic_freeze_object冻结仅不可变引用
party_transferpublic_party_transferParty取决于 Party 设置

对象状态说明

状态描述
地址所有(Address Owned)对象可以被一个地址(或对象)完全访问
共享(Shared)对象可以被任何人引用和删除
冻结(Frozen)对象只能通过不可变引用访问
Party取决于 Party 设置

详细函数签名

transfer / public_transfer

将对象转移到指定地址,使其成为地址所有的对象。

// 模块内部使用(不需要 store 能力)
public fun transfer<T: key>(obj: T, recipient: address);

// 公共使用(需要 store 能力)
public fun public_transfer<T: key + store>(obj: T, recipient: address);

使用示例

module my_package::example;

// key only 的类型只能在定义模块内 transfer
public struct AdminCap has key {
    id: UID,
}

// key + store 的类型可以从任何地方 public_transfer
public struct NFT has key, store {
    id: UID,
}

fun init(ctx: &mut TxContext) {
    // 模块内使用 transfer
    transfer::transfer(AdminCap {
        id: object::new(ctx),
    }, ctx.sender());
}

public fun mint(ctx: &mut TxContext): NFT {
    NFT { id: object::new(ctx) }
    // 调用者可以使用 public_transfer 转移
}

share_object / public_share_object

将对象变为共享对象,任何人都可以访问。

// 模块内部使用
public fun share_object<T: key>(obj: T);

// 公共使用(需要 store)
public fun public_share_object<T: key + store>(obj: T);

使用示例

public struct Registry has key {
    id: UID,
    items: vector<ID>,
}

fun init(ctx: &mut TxContext) {
    transfer::share_object(Registry {
        id: object::new(ctx),
        items: vector[],
    });
}

注意:共享后不可逆——对象永远保持共享状态。

freeze_object / public_freeze_object

将对象冻结为不可变对象。

// 模块内部使用
public fun freeze_object<T: key>(obj: T);

// 公共使用(需要 store)
public fun public_freeze_object<T: key + store>(obj: T);

使用示例

public struct Config has key, store {
    id: UID,
    max_supply: u64,
    name: String,
}

public fun freeze_config(config: Config) {
    transfer::public_freeze_object(config);
    // 此后 config 只能通过 &Config 访问
}

receive

从“父“对象中接收一个发送给它的“子“对象。

public fun receive<T: key>(parent: &mut UID, to_receive: Receiving<T>): T;

public fun public_receive<T: key + store>(parent: &mut UID, to_receive: Receiving<T>): T;

使用示例

public struct Wallet has key {
    id: UID,
}

public fun accept_nft(
    wallet: &mut Wallet,
    nft_receiving: Receiving<NFT>,
): NFT {
    transfer::public_receive(&mut wallet.id, nft_receiving)
}

选择指南

何时使用 transfer vs public_transfer

// 使用 transfer:希望限制转移权限在模块内
public struct SoulboundNFT has key {
    id: UID,
    // 没有 store 能力,外部无法调用 public_transfer
}

// 使用 public_transfer:允许自由转让
public struct TradableNFT has key, store {
    id: UID,
    // 有 store 能力,任何模块都可以调用 public_transfer
}

决策流程图

创建对象后要做什么?
    │
    ├── 转给特定地址 ──────────── transfer / public_transfer
    │
    ├── 所有人都能访问和修改 ──── share_object / public_share_object
    │
    ├── 永远不再修改 ──────────── freeze_object / public_freeze_object
    │
    └── 发送给另一个对象 ──────── transfer(收件人为对象地址)
                                  └── 使用 receive 接收

store 能力的影响

有无 storetransferpublic_transfer被包装动态字段
无 store✓ 模块内
有 store✓ 任何地方

常见模式

铸造并转让

public fun mint_and_transfer(
    name: String,
    recipient: address,
    ctx: &mut TxContext,
) {
    let nft = NFT {
        id: object::new(ctx),
        name,
    };
    transfer::public_transfer(nft, recipient);
}

可组合铸造(推荐)

// 返回对象,让 PTB 决定如何处理
public fun mint(name: String, ctx: &mut TxContext): NFT {
    NFT {
        id: object::new(ctx),
        name,
    }
}

// PTB 中:
// const [nft] = tx.moveCall({ target: '...::mint', ... });
// tx.transferObjects([nft], recipient);

小结

  • transfer 系列函数控制对象的最终状态:地址所有、共享或冻结
  • public_* 变体需要对象有 store 能力,允许从任何模块调用
  • public_* 变体只能在定义该类型的模块内调用
  • 共享和冻结操作不可逆
  • 推荐可组合设计:函数返回对象,让调用者(PTB)决定后续操作