代币与余额 - take、put、transfer、zero、destroy_zero

新开发人员在 Sui 中对 Coin 常见的困惑之一是,有一个看起来相似的 Balance 对象:

public struct Coin<phantom T> has key, store {
    id: UID,
    balance: Balance<T>
}

/// 可存储的余额 - Coin 类型的内部结构体。
/// 可用于存储不需要 key 能力的代币。
public struct Balance<phantom T> has store {
    value: u64
}

Coin 和 Balance 的区别究竟是什么,什么时候应该使用其中一个?最佳对比如下:

  1. Coin 对象更像一个钱包。Coin 钱包有一个内部余额,现金(Balance)可以从中取出并存入另一个 Coin 钱包对象中。
  2. Balance 就像现金。它不能单独存储,需要放入钱包或口袋。开发人员可以选择创建自己的“口袋”对象来存储 Balance。

从技术上讲,由于 Coin 对象也具有 store 能力,开发人员可以将整个 Coin 钱包放入他们自定义的对象中。 然而,这样做相当奇怪,在这种情况下应该使用 Balance,因为 Coin 已经像一个容器。 此外,将 Coin 钱包对象包装到另一个对象中实际上会将其从全局对象存储中移除,正如我们在对象课程中讨论的那样。这通常是不希望的,因为这会使钱包“消失”。

entry fun transfer_coins(from_wallet: &mut Coin<MYCOIN>, amount: u64, to_wallet: &mut Coin<MYCOIN>, ctx: &mut TxContext) {
    let cash = coin::take(coin::balance_mut(from_wallet), amount, ctx);
    coin::put(coin::balance_mut(to_wallet), cash);
}

在上述示例中,我们从 from_wallet Coin 对象中取出一些代币并存入 to_wallet Coin 对象中。注意,只有 from_walletto_wallet 的所有者可以调用 transfer_coins。我们需要使用 coin::balance_mut 来获取 Coin 对象的内部余额,因为结构体字段在定义模块(在这种情况下为 coin)之外是不可见的。

Sui 中的 Coin 模块不提供直接从一个 Coin 对象转移到另一个 Coin 对象的功能。开发人员需要手动使用 takeput 来完成转移。

Coin 模块中的一些其他函数是:

  • balance(&Coin): 返回 Coin 对象的余额。
  • zero(): 创建一个余额为零的 Coin 对象(空钱包)。
  • destroy_zero(): 销毁一个空钱包。一个有非零余额的钱包不能被销毁,必须先将其中的代币转移到其他地方。