学习了suimove中的动态字段,table,bag,作为练习,我准备使用它们模拟solidity中的映射类型,在suimove实现一个类似erc20的同质化代币作为之前学习的实践与巩固。本文分享了练习过程中的mint,和burn,balance_of,total_supply.
学习了sui move中的动态字段,table,bag,作为练习,我准备使用它们模拟solidity中的映射类型,在sui move实现一个类似erc20的同质化代币作为之前学习的实践与巩固。本文分享了练习过程中的mint,和burn,balance_of,total_supply.
public fun mint<T>(_: &TreasuryCap<T> ,balance_list: &mut BalanceList, to:address, value: u64, ctx:&mut TxContext): bool
mint函数用来铸造代币供应。只有持有TreasuryCap的地址才能进行铸币
需要传入BalanceList,铸币地址to,和铸币数量amount
首先检查value是否为0,如果为0,直接返回
if(value == 0){
return true
};
确定代币类型,检查balancelist中是否拥有此代币的余额信息(代币是否初始化),如果有,取出借用。
let type = ascii::into_bytes(type_name::into_string(type_name::get_with_original_ids<T>()));
assert!(bag::contains(&balance_list.balance_list, type), 2);
let balance_table = bag::borrow_mut< vector<u8>, BalanceData<T> >(&mut balance_list.balance_list, type);
检查to是否作为键存在于table中,如果没有创建table传入value,如果有,将新铸币的余额加上
if(table::contains(&balance_table.balance, to)){
let balance_to = table::borrow_mut(&mut balance_table.balance,to);
assert!(*balance_to + value >= *balance_to, 8);
*balance_to = *balance_to + value;
}else{
table::add(&mut balance_table.balance, to, value);
};
将总供应变更
let totalsupply = &mut balance_table.totalsupply;
assert!(*totalsupply + value >= *totalsupply, 8);
*totalsupply = *totalsupply + value;
最后返回true
mint函数完整代码
public fun mint<T>(_: &TreasuryCap<T> ,balance_list: &mut BalanceList, to:address, value: u64, ctx:&mut TxContext): bool{
if(value == 0){
return true
};
let type = ascii::into_bytes(type_name::into_string(type_name::get_with_original_ids<T>()));
assert!(bag::contains(&balance_list.balance_list, type), 2);
let balance_table = bag::borrow_mut< vector<u8>, BalanceData<T> >(&mut balance_list.balance_list, type);
if(table::contains(&balance_table.balance, to)){
let balance_to = table::borrow_mut(&mut balance_table.balance,to);
assert!(*balance_to + value >= *balance_to, 8);
*balance_to = *balance_to + value;
}else{
table::add(&mut balance_table.balance, to, value);
};
let totalsupply = &mut balance_table.totalsupply;
assert!(*totalsupply + value >= *totalsupply, 8);
*totalsupply = *totalsupply + value;
return true
}
public fun burn<T>(_: &TreasuryCap<T>, balance_list: &mut BalanceList, from:address, value: u64, ctx:&mut TxContext): bool
burn函数用于销毁一个地址的代币,同样是持有TreasuryCap的地址才可以销毁
传入balancelist,要销毁代币的地址,销毁数量
if(value == 0){
return true
};
let type = ascii::into_bytes(type_name::into_string(type_name::get_with_original_ids<T>()));
assert!(bag::contains(&balance_list.balance_list, type), 2);
let balance_table = bag::borrow_mut< vector<u8>, BalanceData<T> >(&mut balance_list.balance_list, type);
检查from是否作为键存在于table中,如果不存在,将会回滚,如果有,余额是否足够销毁,如果不够将会回滚,之后变更余额信息和总供应信息
if(table::contains(&balance_table.balance, from)){
let balance_to = table::borrow_mut(&mut balance_table.balance,from);
assert!(*balance_to >= value, 8);
if(*balance_to > value){
*balance_to = *balance_to - value;
}
else{
table::remove(&mut balance_table.balance,from);
};
let totalsupply = &mut balance_table.totalsupply;
*totalsupply = *totalsupply - value;
}else{
assert!(false, 0)
};
burn函数完整代码
public fun burn<T>(_: &TreasuryCap<T>, balance_list: &mut BalanceList, from:address, value: u64, ctx:&mut TxContext): bool{
if(value == 0){
return true
};
let type = ascii::into_bytes(type_name::into_string(type_name::get_with_original_ids<T>()));
assert!(bag::contains(&balance_list.balance_list, type), 2);
let balance_table = bag::borrow_mut<vector<u8>, BalanceData<T>>(&mut balance_list.balance_list, type);
if(table::contains(&balance_table.balance, from)){
let balance_to = table::borrow_mut(&mut balance_table.balance,from);
assert!(*balance_to >= value, 8);
if(*balance_to > value){
*balance_to = *balance_to - value;
}
else{
table::remove(&mut balance_table.balance,from);
};
let totalsupply = &mut balance_table.totalsupply;
*totalsupply = *totalsupply - value;
}else{
assert!(false, 0)
};
return true
}
设置balance_of函数用于获取余额
public fun balance_of<T>(_token_cap :& TokenCap<T>, balance_list: &mut BalanceList, addr:address, ctx:&mut TxContext):u64{
let type = ascii::into_bytes(type_name::into_string(type_name::get_with_original_ids<T>()));
assert!(bag::contains(&balance_list.balance_list, type), 2);
let balance_table = bag::borrow_mut<vector<u8>, BalanceData<T>>(&mut balance_list.balance_list, type);
if(table::contains(&balance_table.balance, addr)){
let amount = *(table::borrow(&mut balance_table.balance,addr));
return amount
};
return 0
}
设置total_supply函数获取当前总供应
public fun total_supply<T>(_token_cap :& TokenCap<T>, balance_list: &mut BalanceList, ctx:&mut TxContext): u64{
let type = ascii::into_bytes(type_name::into_string(type_name::get_with_original_ids<T>()));
assert!(bag::contains(&balance_list.balance_list, type), 2);
let balance_table = bag::borrow_mut<vector<u8>, BalanceData<T>>(&mut balance_list.balance_list, type);
return balance_table.totalsupply
}
接下来对我们刚刚完成的模块进行测试
让我们接着上次的测试代码继续写
我们将重复的测试代码进行重构
#[test_only]
fun get_balance<T>(scenario: &mut Scenario, sender: address ,token_cap: &TokenCap<T>, to:address): u64{
test_scenario::next_tx(scenario, sender);
let balance_list = test_scenario::take_shared<BalanceList>(scenario);
let balance = erc20::balance_of(token_cap,&mut balance_list, to,test_scenario::ctx(scenario));
test_scenario::return_shared(balance_list);
return balance
}
#[test_only]
fun get_totalsupply<T>(scenario: &mut Scenario, sender: address, token_cap: &TokenCap<T>): u64{
test_scenario::next_tx(scenario, sender);
let balance_list = test_scenario::take_shared<BalanceList>(scenario);
let total_supply = erc20::total_supply(token_cap,&mut balance_list,test_scenario::ctx(scenario));
test_scenario::return_shared(balance_list);
return total_supply
}
#[test_only]
fun test_mint(scenario: &mut Scenario, sender: address, to: address, amount:u64) {
test_scenario::next_tx(scenario, sender);
let treasury_cap: TreasuryCap<ERC20TEST> = test_scenario::take_from_sender(scenario);
let balance_list = test_scenario::take_shared<BalanceList>(scenario);
erc20::mint(&treasury_cap, &mut balance_list, to, amount, test_scenario::ctx(scenario));
test_scenario::return_shared(balance_list);
test_scenario::return_to_sender(scenario, treasury_cap);
}
#[test_only]
fun test_burn(scenario: &mut Scenario, sender: address, to: address, amount:u64){
test_scenario::next_tx(scenario, sender);
let treasury_cap: TreasuryCap<ERC20TEST> = test_scenario::take_from_sender(scenario);
let balance_list = test_scenario::take_shared<BalanceList>(scenario);
erc20::burn(&treasury_cap, &mut balance_list, to, amount, test_scenario::ctx(scenario));
test_scenario::return_shared(balance_list);
test_scenario::return_to_sender(scenario, treasury_cap);
}
将它们移到测试主函数中
//2. mint
{
test_scenario::next_tx(&mut scenario, addr1);
let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(&mut scenario);
assert(get_balance(&mut scenario,addr1, &token_cap, addr1) == 0, 0);
assert(get_totalsupply(&mut scenario, addr1,&token_cap) == 0, 0);
test_scenario::return_shared(token_cap);
test_mint(&mut scenario, addr1, addr1, 1000);
test_scenario::next_tx(&mut scenario, addr1);
let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(&mut scenario);
assert(get_balance(&mut scenario,addr1, &token_cap, addr1) == 1000, 0);
assert(get_totalsupply(&mut scenario,addr1, &token_cap) == 1000, 0);
test_scenario::return_shared(token_cap);
test_mint(&mut scenario, addr1, addr1, 1000);
test_scenario::next_tx(&mut scenario, addr1);
let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(&mut scenario);
assert(get_balance(&mut scenario,addr1, &token_cap, addr1) == 2000, 0);
assert(get_totalsupply(&mut scenario,addr1, &token_cap) == 2000, 0);
test_scenario::return_shared(token_cap);
};
//3. burn
{
test_scenario::next_tx(&mut scenario, addr1);
let treasury_cap: TreasuryCap<ERC20TEST> = test_scenario::take_from_sender(&mut scenario);
let balance_list = test_scenario::take_shared<BalanceList>(&mut scenario);
erc20::burn(&treasury_cap,&mut balance_list,addr1,1000,test_scenario::ctx(&mut scenario));
test_scenario::return_shared(balance_list);
test_scenario::return_to_sender(& scenario, treasury_cap);
test_scenario::next_tx(&mut scenario, addr1);
let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(&mut scenario);
assert(get_balance(&mut scenario,addr1, &token_cap, addr1) == 1000, 0);
assert(get_totalsupply(&mut scenario,addr1, &token_cap) == 1000, 0);
test_scenario::return_shared(token_cap);
test_scenario::next_tx(&mut scenario, addr1);
let treasury_cap: TreasuryCap<ERC20TEST> = test_scenario::take_from_sender(&mut scenario);
let balance_list = test_scenario::take_shared<BalanceList>(&mut scenario);
erc20::burn(&treasury_cap,&mut balance_list,addr1,1000,test_scenario::ctx(&mut scenario));
test_scenario::return_shared(balance_list);
test_scenario::return_to_sender(& scenario, treasury_cap);
test_scenario::next_tx(&mut scenario, addr1);
let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(&mut scenario);
assert(get_balance(&mut scenario,addr1, &token_cap, addr1) == 0, 0);
assert(get_totalsupply(&mut scenario,addr1, &token_cap) == 0, 0);
test_scenario::return_shared(token_cap);
};
#[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_only]
fun test_mint(scenario: &mut Scenario, sender: address, to: address, amount:u64) {
test_scenario::next_tx(scenario, sender);
let treasury_cap: TreasuryCap<ERC20TEST> = test_scenario::take_from_sender(scenario);
let balance_list = test_scenario::take_shared<BalanceList>(scenario);
erc20::mint(&treasury_cap, &mut balance_list, to, amount, test_scenario::ctx(scenario));
test_scenario::return_shared(balance_list);
test_scenario::return_to_sender(scenario, treasury_cap);
}
#[test_only]
fun test_burn(scenario: &mut Scenario, sender: address, to: address, amount:u64){
test_scenario::next_tx(scenario, sender);
let treasury_cap: TreasuryCap<ERC20TEST> = test_scenario::take_from_sender(scenario);
let balance_list = test_scenario::take_shared<BalanceList>(scenario);
erc20::burn(&treasury_cap, &mut balance_list, to, amount, test_scenario::ctx(scenario));
test_scenario::return_shared(balance_list);
test_scenario::return_to_sender(scenario, treasury_cap);
}
#[test_only]
fun get_balance<T>(scenario: &mut Scenario, sender: address ,token_cap: &TokenCap<T>, to:address): u64{
test_scenario::next_tx(scenario, sender);
let balance_list = test_scenario::take_shared<BalanceList>(scenario);
let balance = erc20::balance_of(token_cap,&mut balance_list, to,test_scenario::ctx(scenario));
test_scenario::return_shared(balance_list);
return balance
}
#[test_only]
fun get_totalsupply<T>(scenario: &mut Scenario, sender: address, token_cap: &TokenCap<T>): u64{
test_scenario::next_tx(scenario, sender);
let balance_list = test_scenario::take_shared<BalanceList>(scenario);
let total_supply = erc20::total_supply(token_cap,&mut balance_list,test_scenario::ctx(scenario));
test_scenario::return_shared(balance_list);
return total_supply
}
#[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);
};
//2. mint
{
test_scenario::next_tx(&mut scenario, addr1);
let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(&mut scenario);
assert(get_balance(&mut scenario,addr1, &token_cap, addr1) == 0, 0);
assert(get_totalsupply(&mut scenario, addr1,&token_cap) == 0, 0);
test_scenario::return_shared(token_cap);
test_mint(&mut scenario, addr1, addr1, 1000);
test_scenario::next_tx(&mut scenario, addr1);
let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(&mut scenario);
assert(get_balance(&mut scenario,addr1, &token_cap, addr1) == 1000, 0);
assert(get_totalsupply(&mut scenario,addr1, &token_cap) == 1000, 0);
test_scenario::return_shared(token_cap);
test_mint(&mut scenario, addr1, addr1, 1000);
test_scenario::next_tx(&mut scenario, addr1);
let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(&mut scenario);
assert(get_balance(&mut scenario,addr1, &token_cap, addr1) == 2000, 0);
assert(get_totalsupply(&mut scenario,addr1, &token_cap) == 2000, 0);
test_scenario::return_shared(token_cap);
};
//3. burn
{
test_scenario::next_tx(&mut scenario, addr1);
let treasury_cap: TreasuryCap<ERC20TEST> = test_scenario::take_from_sender(&mut scenario);
let balance_list = test_scenario::take_shared<BalanceList>(&mut scenario);
erc20::burn(&treasury_cap,&mut balance_list,addr1,1000,test_scenario::ctx(&mut scenario));
test_scenario::return_shared(balance_list);
test_scenario::return_to_sender(& scenario, treasury_cap);
test_scenario::next_tx(&mut scenario, addr1);
let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(&mut scenario);
assert(get_balance(&mut scenario,addr1, &token_cap, addr1) == 1000, 0);
assert(get_totalsupply(&mut scenario,addr1, &token_cap) == 1000, 0);
test_scenario::return_shared(token_cap);
test_scenario::next_tx(&mut scenario, addr1);
let treasury_cap: TreasuryCap<ERC20TEST> = test_scenario::take_from_sender(&mut scenario);
let balance_list = test_scenario::take_shared<BalanceList>(&mut scenario);
erc20::burn(&treasury_cap,&mut balance_list,addr1,1000,test_scenario::ctx(&mut scenario));
test_scenario::return_shared(balance_list);
test_scenario::return_to_sender(& scenario, treasury_cap);
test_scenario::next_tx(&mut scenario, addr1);
let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(&mut scenario);
assert(get_balance(&mut scenario,addr1, &token_cap, addr1) == 0, 0);
assert(get_totalsupply(&mut scenario,addr1, &token_cap) == 0, 0);
test_scenario::return_shared(token_cap);
};
test_scenario::end(scenario);
}
}
运行:
sui move test --skip-fetch-latest-git-deps
结果:
Running Move unit tests
[ PASS ] 0x0::erc20test::test
Test result: OK. Total tests: 1; passed: 1; failed: 0
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!