本文通过对sui-framework包下的coin相关实现进行部分解读,了解move中如何基于coin创建同质化代币代币创建阅读Coin合约的实现
本文通过对sui-framework包下的coin相关实现进行部分解读,了解move中如何基于coin创建同质化代币
阅读Coin合约的实现:
首先来看两个基本的obj
struct CoinMetadata<phantom T> has key, store {
id: UID,
/// Number of decimal places the coin uses.
/// A coin with `value ` N and `decimals` D should be shown as N / 10^D
/// E.g., a coin with `value` 7002 and decimals 3 should be displayed as 7.002
/// This is metadata for display usage only.
decimals: u8,
/// Name for the token
name: string::String,
/// Symbol for the token
symbol: ascii::String,
/// Description of the token
description: string::String,
/// URL for the token logo
icon_url: Option<Url>
}
这个obj定义了同质化代币的元数据,包括小数位数,代币名称,代币标志,代币的描述和url(可为空)。
struct TreasuryCap<phantom T> has key, store {
id: UID,
total_supply: Supply<T>
}
这个obj是同质化代币权限凭证,用有此凭证的人才可以进行铸币和销毁操作。同样地,泛型T也是一次性见证,用来区别代币种类。
那么我们要想创建一个同质化代币,应该进行什么操作呢?
public fun create_currency<T: drop>(
witness: T,
decimals: u8,
symbol: vector<u8>,
name: vector<u8>,
description: vector<u8>,
icon_url: Option<Url>,
ctx: &mut TxContext
): (TreasuryCap<T>, CoinMetadata<T>) {
// Make sure there's only one instance of the type T
assert!(sui::types::is_one_time_witness(&witness), EBadWitness);
(
TreasuryCap {
id: object::new(ctx),
total_supply: balance::create_supply(witness)
},
CoinMetadata {
id: object::new(ctx),
decimals,
name: string::utf8(name),
symbol: ascii::string(symbol),
description: string::utf8(description),
icon_url
}
)
}
调用create_currency后,coin合约检查输入的witness是否为一次性见证,之后会为我们的代币创建TreasuryCap obj和CoinMetadata obj并返回。
实例:
module token::token{
use std::option;
use sui::coin::{Self, Coin, TreasuryCap};
use sui::transfer;
use sui::tx_context::{Self, TxContext};
struct TOKEN has drop{}
fun init(witness: TOKEN,ctx: &mut TxContext){
let (treasury_cap,metadata) = coin::create_currency<TOKEN>(witness,18,b"shaflow",b"shaflow01",b"",option::none(),ctx);
transfer::public_freeze_object(metadata);
transfer::public_transfer(treasury_cap,tx_context::sender(ctx));
}
}
在合约的构造函数中,我调用coin的create_currency创建了一个名称为shaflow的代币,并且将权限凭证转移给了合约创建者。
先阅读合约实现
sui-framework/coin:
struct Coin<phantom T> has key, store {
id: UID,
balance: Balance<T>
}
这是一个obj,实现了key和store能力。其中参数T封装了一次性见证,用于区分代币种类。
可以将Coin理解一类同质化代币的小钱包,T就区分了钱包中封装了什么类型的代币,balance代表了封装的代币的余额,Coin的所有者就规定了这个钱包是属于谁的。而我们花费代币时,一般需要将我们拥有的Coin传入。
在交易过程中,可能有Coin封装balance生成,也可能有Coin被销毁,其中的balance被提取出来。
这是balance的类型
sui-framework/balance:
struct Balance<phantom T> has store {
value: u64
}
balance合约中实现了与balance相关的函数:
sui-framework/balance:
public fun value<T>(self: &Balance<T>): u64 {
self.value
}
public fun zero<T>(): Balance<T> {
Balance { value: 0 }
}
public fun destroy_zero<T>(balance: Balance<T>) {
assert!(balance.value == 0, ENonZero);
let Balance { value: _ } = balance;
}
public fun join<T>(self: &mut Balance<T>, balance: Balance<T>): u64 {
let Balance { value } = balance;
self.value = self.value + value;
self.value
}
public fun split<T>(self: &mut Balance<T>, value: u64): Balance<T> {
assert!(self.value >= value, ENotEnough);
self.value = self.value - value;
Balance { value }
}
public fun withdraw_all<T>(self: &mut Balance<T>): Balance<T> {
let value = self.value;
split(self, value)
}
还记得supply吗?它被封装在TreasuryCap中,代表我们代币的总供应。让我们也阅读一下它的类型和相关函数的实现
struct Supply<phantom T> has store {
value: u64
}
public fun increase_supply<T>(self: &mut Supply<T>, value: u64): Balance<T> {
assert!(value < (18446744073709551615u64 - self.value), EOverflow);
self.value = self.value + value;
Balance { value }
}
public fun decrease_supply<T>(self: &mut Supply<T>, balance: Balance<T>): u64 {
let Balance { value } = balance;
assert!(self.value >= value, EOverflow);
self.value = self.value - value;
value
}
了解了这些,我们来看看如何铸造代币
coin合约提供了两个方法
public fun mint<T>(
cap: &mut TreasuryCap<T>, value: u64, ctx: &mut TxContext,
): Coin<T> {
Coin {
id: object::new(ctx),
balance: balance::increase_supply(&mut cap.total_supply, value)
}
}
public fun mint_balance<T>(
cap: &mut TreasuryCap<T>, value: u64
): Balance<T> {
balance::increase_supply(&mut cap.total_supply, value)
}
通常情况下,我们只需要调用coin mint方法,函数会增加总供应并为我们返回一个Coin对象,之后我们把Coin对象转移给我们指定的铸造地址
如果需要铸造未被Coin封装的Balance,那么可以直接调用mint_balance
public entry fun mint(treasury_cap: &mut TreasuryCap<TOKEN>,amount: u64,receipt: address,ctx: &mut TxContext){
let new_coin = coin::mint(treasury_cap,amount,ctx);
transfer::public_transfer(new_coin,receipt);
}
而继续阅读,会发现coin合约提供了一个更便利的方法,创建Coin后转移
public entry fun mint_and_transfer<T>(
c: &mut TreasuryCap<T>, amount: u64, recipient: address, ctx: &mut TxContext
) {
transfer::public_transfer(mint(c, amount, ctx), recipient)
}
因此可以直接调用此函数实现mint 一下方法与上述事例补充函数等效
public entry fun mint_and_tranfer(treasury_cap: &mut TreasuryCap<TOKEN>,amount: u64,receipt: address,ctx: &mut TxContext){
coin::mint_and_transfer(treasury_cap,amount,receipt,ctx);
}
public entry fun burn<T>(cap: &mut TreasuryCap<T>, c: Coin<T>): u64 {
let Coin { id, balance } = c;
object::delete(id);
balance::decrease_supply(&mut cap.total_supply, balance)
}
传入待销毁的coin,减少总供应,之后Coin被销毁
public entry fun burn(treasury_cap:&mut TreasuryCap<TOKEN>,coin: Coin<TOKEN>){
coin::burn(treasury_cap,coin);
}
Coin的拥有者可以将自己的Coin转移给其他人来实现
也可以我们在自己的合约中实现方便调用
接下来让我们继续完善代币合约,可以在自己的合约中定义转移规则方便代币在用户间的相互转移
public entry fun transfer(coin:&mut Coin<TOKEN>,amount: u64,receipt: address,ctx: &mut TxContext){
let new_coin = coin::split(coin,amount,ctx);
transfer::public_transfer(new_coin,receipt);
}
这里没有amount做检查是因为检查会发生在balance::split
其中与coin有关的有这些函数
public fun take<T>(
balance: &mut Balance<T>, value: u64, ctx: &mut TxContext,
): Coin<T> {
Coin {
id: object::new(ctx),
balance: balance::split(balance, value)
}
}
public entry fun join<T>(self: &mut Coin<T>, c: Coin<T>) {
let Coin { id, balance } = c;
object::delete(id);
balance::join(&mut self.balance, balance);
}
public fun split<T>(
self: &mut Coin<T>, split_amount: u64, ctx: &mut TxContext
): Coin<T> {
take(&mut self.balance, split_amount, ctx)
}
完整实例代码:
module token::token{
use std::option;
use sui::coin::{Self, Coin, TreasuryCap};
use sui::transfer;
use sui::tx_context::{Self, TxContext};
use sui::event;
struct TOKEN has drop{}
fun init(witness: TOKEN,ctx: &mut TxContext){
let (treasury_cap,metadata) = coin::create_currency<TOKEN>(witness,18,b"SULC",b"SUL COIN",b"",option::none(),ctx);
transfer::public_freeze_object(metadata);
transfer::public_transfer(treasury_cap,tx_context::sender(ctx));
}
public entry fun mint_and_transfer(treasury_cap: &mut TreasuryCap<TOKEN>,amount: u64,receipt: address,ctx: &mut TxContext){
coin::mint_and_transfer(treasury_cap,amount,receipt,ctx);
}
public entry fun burn(treasury_cap:&mut TreasuryCap<TOKEN>,coin: Coin<TOKEN>){
coin::burn(treasury_cap,coin);
}
public entry fun mint(treasury_cap: &mut TreasuryCap<TOKEN>,amount: u64,receipt: address,ctx: &mut TxContext){
let new_coin = coin::mint(treasury_cap,amount,ctx);
transfer::public_transfer(new_coin,receipt);
}
public entry fun transfer(coin:&mut Coin<TOKEN>,amount: u64,receipt: address,ctx: &mut TxContext){
let new_coin = coin::split(coin,amount,ctx);
transfer::public_transfer(new_coin,receipt);
}
}
上述是基于coin合约创建同质化代币的基本内容。coin合约中还有提供的一些函数没有涉及,待继续阅读分析。 (未完待续)
Move语言学习交流QQ群: 79489587 Sui官方中文开发者电报群: https://t.me/sui_dev_cn
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!