学习了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): boolmint函数用来铸造代币供应。只有持有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): boolburn函数用于销毁一个地址的代币,同样是持有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 
                如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!