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
函数将刚才铸造好的代币存到CoinStore
的Balance
中。- 最后,将
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
来创建一个代币,它能够让开发者自定义 transfer
、withdraw
和 deposit
的逻辑,比如例子中演示的如何自定义收取转账的手续费。
私有类型的代币就无法使用 account_coin_store
模块中提供的通用转账、提款、存款等函数。
init
定义了创建 PRC
代币的铸造逻辑。
- 同样是注册代币的信息
CoinInfo
,并将其对象转移到发布这个代币合约的地址上。 - 使用
create_coin_store_extend
创建CoinStore
对象后,再将其存到国库中,因为这是私有的Coin
需要使用具有私有泛型的extend
函数,确保其只能由CoinType
的模块来创建。 - 最后将国库对象转移到发布代币的地址上。
定义 faucet
函数来铸造代币并将其转移到账户里。
transfer
函数展示了如何使用 coin::transfer_extend
定义自定义转账逻辑,该转账函数限制转账金额为 10000
,并收取金额的 1%
的手续费。