开发
第一个代币
Coin Intro

Coin Introduction

如何定义 Coin

在 Rooch 中使用 Coin 结构来定义,它是一个泛型结构,我们可以给它传递不同的 CoinType 来定义不同的代币。比如 Coin<USDT>Coin<USDC> 等,可以把它看作一个容器。

我们主要关注的是 CoinType,它可以带有不同的能力,从而能让不同的模块具有不同的操控权限。

  • CoinType 必须带有 key 能力,此时它是私有的,只能在定义 CoinType 的模块内操作 Coin
  • 当给 CoinType 添加 store 能力后,它就变成公有的,此时除了定义 CoinType 的模块可以操作外,coin 的函数也可以操作。

定义 Coin 时,我们通过 CoinInfo 来注册某种 Coin,CoinInfo 是一个全局存储的对象,它包含 5 个字段。

  • coin_type:定义 Coin 的类型。
  • name:定义 Coin 的名字。
  • symbol:定义代币的符号。
  • decimals 定义代币的精度,因为 Move 中没有小数,我们必须要精度来换算,让用户能看到小数形式的 Coin
  • supply:定义代币总量。

如何使用 Coin

使用公有类型的 Coin

这个例子将演示拥有公有类型的 Coin 的用法。

module coins::fixed_supply_coin {
    use std::option;
    use std::string;
    use moveos_std::signer;
    use moveos_std::object::{Self, Object};
    use rooch_framework::coin;
    use rooch_framework::coin_store::{Self, CoinStore};
    use rooch_framework::account_coin_store;
    const TOTAL_SUPPLY: u256 = 210_000_000_000u256;
    const DECIMALS: u8 = 1u8;
    // The `FSC` CoinType has `key` and `store` ability.
    // So `FSC` coin is public.
    struct FSC has key, store {}
    // construct the `FSC` coin and make it a global object that stored in `Treasury`.
    struct Treasury has key {
        coin_store: Object<CoinStore<FSC>>
    }
    fun init() {
        let coin_info_obj = coin::register_extend<FSC>(    
            string::utf8(b"Fixed Supply Coin"),
            string::utf8(b"FSC"),
            option::none(),
            DECIMALS,
        );
        // Mint the total supply of coins, and store it to the treasury
        let coin = coin::mint_extend<FSC>(&mut coin_info_obj, TOTAL_SUPPLY);
        // Frozen the CoinInfo object, so that no more coins can be minted
        object::to_frozen(coin_info_obj);
        let coin_store_obj = coin_store::create_coin_store<FSC>();
        coin_store::deposit(&mut coin_store_obj, coin);
        let treasury_obj = object::new_named_object(Treasury { coin_store: coin_store_obj });

        // Make the treasury object to shared, so anyone can get mutable Treasury object
        object::to_shared(treasury_obj);
    }
    /// Provide a faucet to give out coins to users
    /// In a real world scenario, the coins should be given out in the application business logic.
    public entry fun faucet(account: &signer, treasury_obj: &mut Object<Treasury>) {
        let account_addr = signer::address_of(account);
        let treasury = object::borrow_mut(treasury_obj);
        let coin = coin_store::withdraw(&mut treasury.coin_store, 10000);
        account_coin_store::deposit(account_addr, coin);
    }
}

定义 TOTAL_SUPPLY 常量,设定 FSC 的总供应量,以及定义 DECIMALS 设定精度值。

FSC 结构定义的是代币的类型,定义类型时,不需要传递任何字段,只需要控制它所拥有的能力。

Treasury 结构定义的是代币的国库,其包含 CoinStore 对象,这个对象包含了关于 FSC 这种代币的信息:代币名称、代币余额、是否冻结。

init 函数里定义了发布 FSC 这个代币的一些逻辑:

  • 首先通过 register_extend 函数来创建 FSC 代币的元信息,包括:代币类型、代币名字,代币符号、代币精度、代币总供应量(这个函数默认初始化的值为 0)。
  • 接着,使用 mint_extend 函数来铸造代币,铸造完成后,就使用 to_frozen 函数将 coin_info_obj 对象冻结,即冻结了铸币权,不能够再铸造新的代币了。
  • create_coin_store 函数为 FSC 代币创建 CoinStore 对象。
  • deposit 函数将刚才铸造好的代币存到 CoinStoreBalance 中。
  • 最后,将 CoinStore 对象放到国库里,并将国库对象变成共享对象。

faucet 函数包含将国库里的代币分发给账户的逻辑,这个只是用来开发测试。

使用私有类型的 Coin

这个例子介绍私有类型 Coin 的用法。

module coins::private_coin {

    use std::option;
    use std::string;
    use moveos_std::signer;
    use moveos_std::object::{Self, Object};
    use rooch_framework::coin::{Self, Coin, CoinInfo};
    use rooch_framework::coin_store::{Self, CoinStore};
    use rooch_framework::account_coin_store;

    const ErrorTransferAmountTooLarge: u64 = 1;

    /// This Coin has no `store` ability, 
    /// so it can not be operate via `account_coin_store::transfer`, `account_coin_store::deposit` and `account_coin_store::withdraw`
    struct PRC has key {}

    struct Treasury has key {
        coin_store: Object<CoinStore<PRC>>
    }

    fun init() {
        let coin_info_obj = coin::register_extend<PRC>(
            string::utf8(b"Private Coin"),
            string::utf8(b"PRC"),
            option::none(),
            1,
        );
        object::transfer(coin_info_obj, @coins);
        let coin_store = coin_store::create_coin_store_extend<PRC>();
        let treasury_obj = object::new_named_object(Treasury { coin_store });
        object::transfer_extend(treasury_obj, @coins);
    }

    /// Provide a faucet to give out coins to users
    /// In a real world scenario, the coins should be given out in the application business logic.
    public entry fun faucet(account: &signer) {
        let account_addr = signer::address_of(account);
        let coin_signer = signer::module_signer<Treasury>();
        let coin_info_obj = object::borrow_mut_object<CoinInfo<PRC>>(&coin_signer, coin::coin_info_id<PRC>());
        let coin = coin::mint_extend<PRC>(coin_info_obj, 10000);
        account_coin_store::deposit_extend(account_addr, coin);
    }

    /// This function shows how to use `coin::transfer_extend` to define a custom transfer logic
    /// This transfer function limits the amount of transfer to 10000, and take 1% of the amount as fee
    public entry fun transfer(from: &signer, to_addr: address, amount: u256) {
        assert!(amount <= 10000u256, ErrorTransferAmountTooLarge);
        let from_addr = signer::address_of(from);
        let fee_amount = amount / 100u256;
        if (fee_amount > 0u256) {
            let fee = account_coin_store::withdraw_extend<PRC>(from_addr, fee_amount);
            deposit_to_treaury(fee);
        };
        account_coin_store::transfer_extend<PRC>(from_addr, to_addr, amount);
    }

    fun deposit_to_treaury(coin: Coin<PRC>) {
        let treasury_object_id = object::named_object_id<Treasury>();
        let treasury_obj = object::borrow_mut_object_extend<Treasury>(treasury_object_id);
        coin_store::deposit_extend(&mut object::borrow_mut(treasury_obj).coin_store, coin);
    }
}

同样,通过两个结构定义代币的类型和它的国库。PRC 是私有类型的代币。

使用私有类型的 CoinType 来创建一个代币,它能够让开发者自定义 transferwithdrawdeposit 的逻辑,比如例子中演示的如何自定义收取转账的手续费。

私有类型的代币就无法使用 account_coin_store 模块中提供的通用转账、提款、存款等函数。

init 定义了创建 PRC 代币的铸造逻辑。

  • 同样是注册代币的信息 CoinInfo,并将其对象转移到发布这个代币合约的地址上。
  • 使用 create_coin_store_extend 创建 CoinStore 对象后,再将其存到国库中,因为这是私有的 Coin 需要使用具有私有泛型的 extend 函数,确保其只能由 CoinType 的模块来创建。
  • 最后将国库对象转移到发布代币的地址上。

定义 faucet 函数来铸造代币并将其转移到账户里。

transfer 函数展示了如何使用 coin::transfer_extend 定义自定义转账逻辑,该转账函数限制转账金额为 10000,并收取金额的 1% 的手续费。