sui move 动态字段练习(2)

  • shaflow01
  • 更新于 2024-03-11 21:22
  • 阅读 805

学习了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.

mint函数

    public fun mint<T>(_: &TreasuryCap<T> ,balance_list: &mut BalanceList, to:address, value: u64, ctx:&mut TxContext): bool

mint函数用来铸造代币供应。只有持有TreasuryCap的地址才能进行铸币
需要传入BalanceList,铸币地址to,和铸币数量amount

  1. 首先检查value是否为0,如果为0,直接返回

        if(value == 0){
            return true
        };
  2. 确定代币类型,检查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);
  3. 检查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);
        };
  4. 将总供应变更

        let totalsupply = &mut balance_table.totalsupply;
        assert!(*totalsupply + value >= *totalsupply, 8);
        *totalsupply = *totalsupply + value;
  5. 最后返回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
    }

burn函数

    public fun burn<T>(_: &TreasuryCap<T>, balance_list: &mut BalanceList, from:address, value: u64, ctx:&mut TxContext): bool

burn函数用于销毁一个地址的代币,同样是持有TreasuryCap的地址才可以销毁
传入balancelist,要销毁代币的地址,销毁数量

  1. 首先检查value是否为0,如果为0,直接返回
        if(value == 0){
            return true
        };
  2. 确定代币类型,检查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);
  3. 检查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)
        };
  4. 函数返回true

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

设置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
    }
  1. 获取代币类型,检查代币是否初始化,如果有,取出借用,如没有,抛出异常
  2. 检查addr是否作为键存在于table中,如果有,返回余额,如果没有,返回0

total_supply

设置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

接下来对我们刚刚完成的模块进行测试
让我们接着上次的测试代码继续写
我们将重复的测试代码进行重构

获取某地址余额

    #[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);
    }
}

result

运行:

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
  • 原创
  • 学分: 5
  • 分类: Sui
  • 标签:
点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
shaflow01
shaflow01
0x4937...bA76
江湖只有他的大名,没有他的介绍。