引言学习了suimove中的动态字段,table,bag,作为练习,我准备使用它们模拟solidity中的映射类型,在suimove实现一个类似erc20的同质化代币作为之前学习的实践与巩固。本文分享了练习过程中的obj设计、createtoken。
学习了sui move中的动态字段,table,bag,作为练习,我准备使用它们模拟solidity中的映射类型,在sui move实现一个类似erc20的同质化代币作为之前学习的实践与巩固。本文分享了练习过程中的obj设计、create token。
注:本例实现仅用于学习动态字段,由于访问gas和便捷性不强,无法用于生产。在sui move中使用的同质化代币请使用官方标准库中内置的coin
首先,我是用如下结构体模拟balanceOf映射
struct BalanceData<phantom T> has key, store{
id: UID,
balance:Table<address,u64>,
totalsupply: u64,
}
struct BalanceList has key,store{
id: UID,
balance_list: Bag,
}
BalanceList obj中的balance_list预计存储不同类型代币的余额相关信息 它的键值对将会是type(T) -> BalanceData<T<T>>
BalanceData中的totalsupply字段储存这一种类型代币的总供应
balance字段储存某一地址代币余额
以下obj用于实现allowance
struct AllowanceData<phantom T> has key, store{
id: UID,
allowance:Table<address , AllowanceAmountList>,
}
struct AllowanceAmountList has key, store{
id: UID,
allowance_amount: Table<address, u64>,
}
struct AllowanceList has key,store{
id: UID,
allowance_list: Bag,
}
struct ERC20MetaData<phantom T> has key, store{
id: UID,
name: string::String,
symbol: ascii::String,
decimal: u8,
}
在代币创建时要输入创建的obj,是代币的元数据,将会是share_obj
struct TreasuryCap<phantom T> has key,store{
id:UID,
}
struct TokenCap<phantom T> has key{
id: UID,
}
定义了两个cap
TreasuryCap持有者有权利铸造销毁代币
TokenCap将会是share_obj,在转账等其他操作中传入,用于区分代币类型
fun init(ctx:&mut TxContext){
let balance_list = BalanceList{
id: object::new(ctx),
balance_list: bag::new(ctx),
};
let allowance_list = AllowanceList{
id: object::new(ctx),
allowance_list: bag::new(ctx),
};
transfer::share_object(balance_list);
transfer::share_object(allowance_list);
}
在构造函数中,将BalanceList和AllowanceList初始化,将他们设置成为share_obj。
public fun create_token<T: drop>(witness: T, name: vector<u8>, symbol: vector<u8>, decimal:u8, ctx:&mut TxContext):TreasuryCap<T>{
assert!(sui::types::is_one_time_witness(&witness), EBadWitness);
let erc20_metadata = ERC20MetaData<T>{
id: object::new(ctx),
name: string::utf8(name),
symbol: ascii::string(symbol),
decimal: decimal,
};
let treasury_cap = TreasuryCap<T>{
id: object::new(ctx),
};
let token_cap = TokenCap<T>{
id: object::new(ctx),
};
transfer::share_object(erc20_metadata);
transfer::share_object(token_cap);
treasury_cap
}
public fun init_token<T>(_:&TokenCap<T>,balance_list: &mut BalanceList, allowance_list: &mut AllowanceList,ctx:&mut TxContext){
let type = ascii::into_bytes(type_name::into_string(type_name::get_with_original_ids<T>()));
assert!(!bag::contains(& balance_list.balance_list, type),0);
assert!(!bag::contains(& allowance_list.allowance_list, type),0);
let balance_data = BalanceData<T>{
id: object::new(ctx),
balance: table::new(ctx),
totalsupply: 0,
};
bag::add(&mut balance_list.balance_list, type, balance_data);
let allowance_data = AllowanceData<T>{
id: object::new(ctx),
allowance: table::new(ctx),
};
bag::add(&mut allowance_list.allowance_list, type, allowance_data);
}
创建代币需要先后调用create_token和init_token,它们分别做了什么呢?
create_token:
调用create_token需要传入元数据相关信息和一次性见证(区分代币种类)
在进行test之前,我们在erc20.move中添加一个只能在测试中被调用的函数,其目的是模拟erc20进行部署时的初始化
#[test_only]
public fun test_init(ctx:&mut TxContext){
init(ctx);
}
接下来我们新建一个文件erc20test.move来测试我们刚刚完成的模块
#[test_only]
module erc20::erc20test{
use erc20::erc20::{Self,TokenCap,BalanceList,TreasuryCap};
use sui::test_scenario::{Self, Scenario};
use sui::tx_context::{Self,TxContext};
use sui::transfer;
struct ERC20TEST has drop{}
fun init(witness: ERC20TEST, ctx: &mut TxContext){
let treasury_cap = erc20::create_token(witness,b"ETC20Test", b"ERCT", 18, ctx);
transfer::public_transfer(treasury_cap, tx_context::sender(ctx));
}
#[test]
public fun test(){
let addr1 = @0xA;
let scenario = test_scenario::begin(addr1);
//1. create a token
{
erc20::test_init(test_scenario::ctx(&mut scenario));
test_scenario::next_tx(&mut scenario, addr1);
init(ERC20TEST{}, test_scenario::ctx(&mut scenario));
test_scenario::next_tx(&mut scenario, addr1);
let balance_list = test_scenario::take_shared<BalanceList>(&mut scenario);
let allowance_list= test_scenario::take_shared(&mut scenario);
let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(&mut scenario);
erc20::init_token(&token_cap,&mut balance_list,&mut allowance_list, test_scenario::ctx(&mut scenario));
test_scenario::return_shared(balance_list);
test_scenario::return_shared(allowance_list);
test_scenario::return_shared(token_cap);
};
test_scenario::end(scenario);
}
}
测试中先初始化了erc20 module然后再初始化test的过程中创建了ERC20TEST token,调用init_token为ERC20TEST token初始化BalanceData和AllowanceData
result:
Running Move unit tests
[ PASS ] 0x0::erc20test::test
Test result: OK. Total tests: 1; passed: 1; failed: 0
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!