更新 Coin 元数据

当我们之前创建 MyCoin 时,我们冻结了返回的元数据对象。这将不允许将来对元数据(小数位数/符号/名称/描述/logo URL)进行任何更改。

说到小数位数,我们从未真正解释过它的作用。小数位数通常用于代币/代币,以减少舍入误差。 大多数智能合约语言,包括 Move,都没有分数,所有数学运算都是基于整数的。这意味着在 Move 中,5 / 2 = 2,导致舍入误差为1。 如果没有小数位数,人们将会损失大量资金。加密货币中通常使用至少6位小数,有时可以高达18位(1个代币 = 10^18单位)。 在大多数情况下,9位小数足以使舍入误差小到用户可以忽略不计。

回到元数据对象,如果你认为将来可能会想要更改代币的元数据,那么你不应该冻结它,而应该将其转移到一个管理员账户进行保管。 将来,你可以利用 coin 模块中的不同函数来更新你想要的元数据:

/// 更新`CoinMetadata`里coin的名字
public entry fun update_name<T>(
    _treasury: &TreasuryCap<T>, metadata: &mut CoinMetadata<T>, name: string::String
) {
    metadata.name = name;
}

/// 更新`CoinMetadata`里coin的符号
public entry fun update_symbol<T>(
    _treasury: &TreasuryCap<T>, metadata: &mut CoinMetadata<T>, symbol: ascii::String
) {
    metadata.symbol = symbol;
}

/// 更新`CoinMetadata`里coin的描述
public entry fun update_description<T>(
    _treasury: &TreasuryCap<T>, metadata: &mut CoinMetadata<T>, description: string::String
) {
    metadata.description = description;
}

/// 更新`CoinMetadata`里coin的链接
public entry fun update_icon_url<T>(
    _treasury: &TreasuryCap<T>, metadata: &mut CoinMetadata<T>, url: ascii::String
) {
    metadata.icon_url = option::some(url::new_unsafe(url));
}

请注意,小数位数(decimals)是一个特殊情况,没有相应的更新函数。这是因为小数位数是代币的一个基本属性,如果被更新,会改变每个人的余额。 因此,为了安全性和简便性,Sui 的 Coin 标准不允许修改小数位数。

要调用更新函数,例如 coin::update_symbol,调用者需要访问 TreasuryCap 和 metadata 对象。 请注意,TreasuryCap 和 Metadata 结构体都有 store 能力,因此我们可以将它们存储在某个地方,以便以后可以进行编程访问和修改:

use std::string;
use sui::url;

public struct MYCOIN has drop {}

public struct CoinDataHolder has key {
    id: UID,
    treasury_cap: TreasuryCap<MYCOIN>,
    metadata: CoinMetadata<MYCOIN>,
}

fun init(otw: MYCOIN, ctx: &mut TxContext) {
    let (treasury_cap, metadata) = coin::create_currency(
        otw,
        9,
        b"MYC",
        b"MyCoin",
        b"My Coin description",                       
        option::some(url::new_unsafe(string::utf8(b"https://mycoin.com/logo.png"))),
        ctx,
    );
    
    let treasury_cap_holder = TreasuryCapHolder {
        id: object::new(ctx),
        treasury_cap,
        metadata,
    };
    transfer::share_object(treasury_cap_holder);
}

entry fun update_symbol(holder: &mut CoinDataHolder, new_symbol: String) {
    let metadata = &mut holder.metadata;
    let treasury_cap = &holder.treasury_cap;
    coin::update_symbol(treasury_cap, metadata, new_symbol);
}

在上面的例子中,我们将 TreasuryCap 和 Metadata 对象都包装在一个共享对象中,以便以后任何人都可以访问该对象来更新代币的元数据。 在实际应用中,开发人员可以添加逻辑,要求发送者是特定的管理员地址,以确保用户不会随意更新代币元数据。