{"content":{"title":"sui move 动态字段练习(3)","body":"## 引言\r\n学习了sui move中的动态字段，table，bag，作为练习，我准备使用它们模拟solidity中的映射类型，在sui move实现一个**类似**erc20的同质化代币作为之前学习的实践与巩固。本文分享了练习过程中的transfer,和approve,transferFrom。  \r\n注：本例实现仅用于学习动态字段，由于访问gas和便捷性不强，无法用于生产。在sui move中使用的同质化代币请使用官方标准库中内置的coin  \r\n\r\n## transfer函数  \r\ntransfer函数用于转账，需要传入tokencap，要转账的地址，转账数量  \r\n```rust\r\n public fun transfer<T>(_:& TokenCap<T>, balance_list: &mut BalanceList, to:address, value: u64, ctx:&mut TxContext):bool\r\n```\r\n\r\n1. 检查to地址是否为0地址，如果是抛出异常；检查转账余额是否为0，如果为0直接返回。\r\n```rust\r\n        assert!(to != AddressZero, 1);\r\n        if(value == 0){\r\n            return true\r\n        };\r\n```\r\n  \r\n2. 检查代币信息，取出对应代币余额列表  \r\n```rust\r\n        let type = ascii::into_bytes(type_name::into_string(type_name::get_with_original_ids<T>()));\r\n        assert!(bag::contains(&balance_list.balance_list, type), 2);\r\n        let balance_table = bag::borrow_mut<vector<u8>, BalanceData<T>>(&mut balance_list.balance_list, type);\r\n```\r\n3. 检查转账人是否有足够的余额，之后将余额扣除\r\n```rust\r\n        assert!(table::contains(&balance_table.balance, tx_context::sender(ctx)), 3);\r\n        let balance_from = table::borrow_mut(&mut balance_table.balance,tx_context::sender(ctx));\r\n        assert!(*balance_from >= value , 4);\r\n        if(*balance_from > value){\r\n            *balance_from = *balance_from - value;\r\n        }\r\n        else{\r\n            table::remove(&mut balance_table.balance ,tx_context::sender(ctx));\r\n        };\r\n```\r\n\r\n4. 对to地址的余额进行变更  \r\n```rust\r\n        if(table::contains(&balance_table.balance, to)){\r\n            let balance_to = table::borrow_mut(&mut balance_table.balance,to);\r\n            assert!(*balance_to + value >= *balance_to, 8);\r\n            *balance_to = *balance_to + value;\r\n        }else{\r\n            table::add(&mut balance_table.balance, to, value);\r\n        };\r\n```\r\n\r\n5. 函数返回true  \r\n\r\ntransfer函数完整代码：  \r\n```rust\r\n    public fun transfer<T>(_:& TokenCap<T>, balance_list: &mut BalanceList, to:address, value: u64, ctx:&mut TxContext):bool{\r\n        assert!(to != AddressZero, 1);\r\n        if(value == 0){\r\n            return true\r\n        };\r\n        let type = ascii::into_bytes(type_name::into_string(type_name::get_with_original_ids<T>()));\r\n        assert!(bag::contains(&balance_list.balance_list, type), 2);\r\n        let balance_table = bag::borrow_mut<vector<u8>, BalanceData<T>>(&mut balance_list.balance_list, type);\r\n        assert!(table::contains(&balance_table.balance, tx_context::sender(ctx)), 3);\r\n        let balance_from = table::borrow_mut(&mut balance_table.balance,tx_context::sender(ctx));\r\n        assert!(*balance_from >= value , 4);\r\n        if(*balance_from > value){\r\n            *balance_from = *balance_from - value;\r\n        }\r\n        else{\r\n            table::remove(&mut balance_table.balance ,tx_context::sender(ctx));\r\n        };\r\n        if(table::contains(&balance_table.balance, to)){\r\n            let balance_to = table::borrow_mut(&mut balance_table.balance,to);\r\n            assert!(*balance_to + value >= *balance_to, 8);\r\n            *balance_to = *balance_to + value;\r\n        }else{\r\n            table::add(&mut balance_table.balance, to, value);\r\n        };\r\n        return true\r\n    }\r\n\r\n```\r\n\r\n## approve函数  \r\napprove函数用于授权他人转账,需要传入代币类型，授权列表，要授权对象的地址，要授权代币的数量  \r\n```rust\r\n    public fun approve<T>(_:& TokenCap<T>, allowance_list: &mut AllowanceList,spender:address, value:u64, ctx:&mut TxContext):bool \r\n```\r\n1. 检查授权余额是否为0，如果是，直接返回true\r\n```rust\r\n        assert!(spender != AddressZero, 1);\r\n        if(value == 0){\r\n            return true\r\n        };\r\n```\r\n2. 检查代币类型,取出相应授权列表  \r\n```rust\r\n        let type = ascii::into_bytes(type_name::into_string(type_name::get_with_original_ids<T>()));\r\n        assert!(bag::contains(&allowance_list.allowance_list, type), 2);\r\n        //get AllowanceData\r\n        let allowance_table = bag::borrow_mut<vector<u8>, AllowanceData<T>>(&mut allowance_list.allowance_list, type);\r\n\r\n```\r\n3. 如果授权者已存在授权列表，取出更改数据，如没有，创建并填入初始数据  \r\n```rust\r\n        //if have AllowanceAmountList\r\n        if(table::contains(&allowance_table.allowance, tx_context::sender(ctx))){\r\n            let allowance = table::borrow_mut(&mut allowance_table.allowance, tx_context::sender(ctx));\r\n\r\n            //if have allowance_amount\r\n            if(table::contains(&allowance.allowance_amount,spender)){\r\n                let amount = table::borrow_mut(&mut allowance.allowance_amount,spender);\r\n                *amount = value;\r\n            }\r\n            else{\r\n                table::add(&mut allowance.allowance_amount, spender, value);\r\n            }\r\n        } else{\r\n            //add AllowanceAmountList\r\n            let allowance_amount = table::new(ctx);\r\n            table::add(&mut allowance_amount, spender, value);\r\n            let allowance_list = AllowanceAmountList{\r\n                id: object::new(ctx),\r\n                allowance_amount: allowance_amount,\r\n            };\r\n            table::add(&mut allowance_table.allowance,tx_context::sender(ctx),allowance_list);\r\n        };\r\n```\r\n4. 函数返回true  \r\n\r\napprove函数完整代码  \r\n```rust\r\n    public fun approve<T>(_:& TokenCap<T>, allowance_list: &mut AllowanceList,spender:address, value:u64, ctx:&mut TxContext):bool {\r\n        assert!(spender != AddressZero, 1);\r\n        if(value == 0){\r\n            return true\r\n        };\r\n        let type = ascii::into_bytes(type_name::into_string(type_name::get_with_original_ids<T>()));\r\n        assert!(bag::contains(&allowance_list.allowance_list, type), 2);\r\n        //get AllowanceData\r\n        let allowance_table = bag::borrow_mut<vector<u8>, AllowanceData<T>>(&mut allowance_list.allowance_list, type);\r\n\r\n        //if have AllowanceAmountList\r\n        if(table::contains(&allowance_table.allowance, tx_context::sender(ctx))){\r\n            let allowance = table::borrow_mut(&mut allowance_table.allowance, tx_context::sender(ctx));\r\n\r\n            //if have allowance_amount\r\n            if(table::contains(&allowance.allowance_amount,spender)){\r\n                let amount = table::borrow_mut(&mut allowance.allowance_amount,spender);\r\n                *amount = value;\r\n            }\r\n            else{\r\n                table::add(&mut allowance.allowance_amount, spender, value);\r\n            }\r\n        } else{\r\n            //add AllowanceAmountList\r\n            let allowance_amount = table::new(ctx);\r\n            table::add(&mut allowance_amount, spender, value);\r\n            let allowance_list = AllowanceAmountList{\r\n                id: object::new(ctx),\r\n                allowance_amount: allowance_amount,\r\n            };\r\n            table::add(&mut allowance_table.allowance,tx_context::sender(ctx),allowance_list);\r\n        };\r\n        return true\r\n    }\r\n```\r\n\r\n## transferFrom函数\r\ntransferFrom用于已被授权的spender进行转账  \r\n```rust\r\npublic fun transferFrom<T>(_:& TokenCap<T>, allowance_list: &mut AllowanceList,balance_list: &mut BalanceList,from: address, to: address, value: u64, ctx:&mut TxContext):bool\r\n```\r\n1. 检查地址及转账金额  \r\n```rust\r\n        assert!(from != AddressZero, 1);\r\n        assert!(to != AddressZero, 1);\r\n        if(value == 0){\r\n            return true\r\n        };\r\n```\r\n2. 检查代币类型\r\n```rust\r\n        let type = ascii::into_bytes(type_name::into_string(type_name::get_with_original_ids<T>()));\r\n        assert!(bag::contains(&allowance_list.allowance_list, type), 2);\r\n        assert!(bag::contains(&balance_list.balance_list, type), 2);\r\n\r\n```\r\n3. 检查授权额度是否足够交易发起者支付  \r\n```rust\r\n        let allowance_table = bag::borrow_mut<vector<u8>, AllowanceData<T>>(&mut allowance_list.allowance_list, type);\r\n        assert!(table::contains(&allowance_table.allowance, from), 5);\r\n\r\n        let allowance = table::borrow_mut(&mut allowance_table.allowance, from);\r\n        assert!(table::contains(&allowance.allowance_amount,tx_context::sender(ctx)),6);\r\n        let amount = table::borrow_mut(&mut allowance.allowance_amount,tx_context::sender(ctx));\r\n        assert!(*amount >= value, 7);\r\n        if(*amount > value){\r\n            *amount = *amount - value;\r\n        }\r\n        else{\r\n            *amount = *amount - value;\r\n            table::remove(&mut allowance.allowance_amount, tx_context::sender(ctx));\r\n        };\r\n```\r\n\r\n4. 对from和to的余额进行变更  \r\n```rust\r\n        let balance_table = bag::borrow_mut<vector<u8>, BalanceData<T>>(&mut balance_list.balance_list, type);\r\n        assert!(table::contains(&balance_table.balance, from) , 3);\r\n        let balance_from = table::borrow_mut(&mut balance_table.balance, from);\r\n        assert!(*balance_from >= value , 4);\r\n        *balance_from = *balance_from - value;\r\n\r\n        if(table::contains(&balance_table.balance, to)){\r\n            let balance_to = table::borrow_mut(&mut balance_table.balance,to);\r\n            *balance_to = *balance_to + value;\r\n        }else{\r\n            table::add(&mut balance_table.balance, to, value);\r\n        };\r\n```\r\n\r\n5. 函数返回true\r\n\r\ntransferFrom函数完整代码  \r\n```rust\r\n    public fun transferFrom<T>(_:& TokenCap<T>, allowance_list: &mut AllowanceList,balance_list: &mut BalanceList,from: address, to: address, value: u64, ctx:&mut TxContext):bool{\r\n        assert!(from != AddressZero, 1);\r\n        assert!(to != AddressZero, 1);\r\n        if(value == 0){\r\n            return true\r\n        };\r\n        let type = ascii::into_bytes(type_name::into_string(type_name::get_with_original_ids<T>()));\r\n        assert!(bag::contains(&allowance_list.allowance_list, type), 2);\r\n        assert!(bag::contains(&balance_list.balance_list, type), 2);\r\n\r\n        let allowance_table = bag::borrow_mut<vector<u8>, AllowanceData<T>>(&mut allowance_list.allowance_list, type);\r\n        assert!(table::contains(&allowance_table.allowance, from), 5);\r\n\r\n        let allowance = table::borrow_mut(&mut allowance_table.allowance, from);\r\n        assert!(table::contains(&allowance.allowance_amount,tx_context::sender(ctx)),6);\r\n        let amount = table::borrow_mut(&mut allowance.allowance_amount,tx_context::sender(ctx));\r\n        assert!(*amount >= value, 7);\r\n        if(*amount > value){\r\n            *amount = *amount - value;\r\n        }\r\n        else{\r\n            *amount = *amount - value;\r\n            table::remove(&mut allowance.allowance_amount, tx_context::sender(ctx));\r\n        };\r\n\r\n        let balance_table = bag::borrow_mut<vector<u8>, BalanceData<T>>(&mut balance_list.balance_list, type);\r\n        assert!(table::contains(&balance_table.balance, from) , 3);\r\n        let balance_from = table::borrow_mut(&mut balance_table.balance, from);\r\n        assert!(*balance_from >= value , 4);\r\n        *balance_from = *balance_from - value;\r\n\r\n        if(table::contains(&balance_table.balance, to)){\r\n            let balance_to = table::borrow_mut(&mut balance_table.balance,to);\r\n            *balance_to = *balance_to + value;\r\n        }else{\r\n            table::add(&mut balance_table.balance, to, value);\r\n        };\r\n        return true\r\n    }\r\n```\r\n## get_allowance函数\r\n用来访问授权余额  \r\n```rust\r\n    public fun get_allowance<T>(_token_cap :& TokenCap<T>,allowance_list: &mut AllowanceList, owner:address, spender:address,ctx:&mut TxContext):u64{\r\n        let type = ascii::into_bytes(type_name::into_string(type_name::get_with_original_ids<T>()));\r\n        assert!(bag::contains(&allowance_list.allowance_list, type), 2);\r\n\r\n        let allowance_data_list = bag::borrow<vector<u8>, AllowanceData<T>>(& allowance_list.allowance_list, type);\r\n\r\n        if(table::contains(&allowance_data_list.allowance, owner)){\r\n            let allowance = table::borrow<address, AllowanceAmountList>(& allowance_data_list.allowance, owner);\r\n            if(table::contains(&allowance.allowance_amount,spender)){\r\n                let allowance_amount = *(table::borrow<address, u64>(& allowance.allowance_amount,spender));\r\n                return allowance_amount\r\n            }\r\n        };\r\n        return 0\r\n    }\r\n```\r\n先检查代币类型，再尝试一步步访问对应的值，如果中间有一处对应映射不存在，都会返回0.  \r\n\r\n## test  \r\n### 封装测试使用函数\r\n```rust\r\n    #[test_only]\r\n    fun get_allowance(scenario: &mut Scenario,owner: address, spender: address): u64{\r\n        test_scenario::next_tx(scenario, spender);\r\n        let allowance_list = test_scenario::take_shared<AllowanceList>(scenario);\r\n        let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(scenario);\r\n        let allowance_amount = erc20::get_allowance(&token_cap, &mut allowance_list, owner, spender,test_scenario::ctx(scenario));\r\n        test_scenario::return_shared(allowance_list);\r\n        test_scenario::return_shared(token_cap);\r\n        return allowance_amount\r\n    }\r\n\r\n        #[test_only]\r\n    fun test_transfer(scenario: &mut Scenario, from:address, to: address, amount:u64){\r\n        test_scenario::next_tx(scenario, from);\r\n        let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(scenario);\r\n        let balance_list:BalanceList = test_scenario::take_shared(scenario);\r\n        erc20::transfer(&token_cap,&mut balance_list, to, amount, test_scenario::ctx(scenario));\r\n        test_scenario::return_shared(balance_list);\r\n        test_scenario::return_shared(token_cap);\r\n    }\r\n\r\n    #[test_only]\r\n    fun test_approve(scenario: &mut Scenario, owner:address, spender: address,amount:u64){\r\n        test_scenario::next_tx(scenario, owner);\r\n        let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(scenario);\r\n        let allowance_list = test_scenario::take_shared<AllowanceList>(scenario);\r\n        erc20::approve(&token_cap,&mut allowance_list,spender,amount,test_scenario::ctx(scenario));\r\n        test_scenario::return_shared(allowance_list);\r\n        test_scenario::return_shared(token_cap);\r\n    }\r\n\r\n    #[test_only]\r\n    fun test_transfer_from(scenario: &mut Scenario,owner:address, spender:address,amount:u64){\r\n        test_scenario::next_tx(scenario, spender);\r\n        let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(scenario);\r\n        let allowance_list = test_scenario::take_shared<AllowanceList>(scenario);\r\n        let balance_list = test_scenario::take_shared<BalanceList>(scenario);\r\n        erc20::transferFrom(&token_cap,&mut allowance_list,&mut balance_list,owner,spender,amount,test_scenario::ctx(scenario));\r\n        test_scenario::return_shared(allowance_list);\r\n        test_scenario::return_shared(token_cap);\r\n        test_scenario::return_shared(balance_list);\r\n    }\r\n```\r\n### 在主函数中进行测试\r\n```rust\r\n        //3.transfer\r\n        {   \r\n            test_scenario::next_tx(&mut scenario, addr1);\r\n            test_mint(&mut scenario, addr1, addr1, 1000);\r\n\r\n            assert!(get_balance(&mut scenario,addr1,  addr1) == 1000, 0);\r\n            assert!(get_balance(&mut scenario,addr2,  addr2) == 0, 0);\r\n\r\n            test_transfer(&mut scenario, addr1, addr2, 500);\r\n\r\n            assert!(get_balance(&mut scenario,addr1,  addr1) == 500, 0);\r\n            assert!(get_balance(&mut scenario,addr2,  addr2) == 500, 0);\r\n\r\n        };\r\n\r\n        //4.transferFrom\r\n        {\r\n\r\n            assert!(get_allowance(&mut scenario, addr1, addr2) == 0,0);\r\n\r\n            test_approve(&mut scenario, addr1, addr2, 500);\r\n\r\n            assert!(get_allowance(&mut scenario, addr1, addr2 ) == 500,0);\r\n\r\n            test_transfer_from(&mut scenario, addr1, addr2, 300);\r\n\r\n            assert!(get_allowance(&mut scenario, addr1, addr2 ) == 200,0);\r\n            assert!(get_balance(&mut scenario,addr2,  addr1) == 200, 0);\r\n            assert!(get_balance(&mut scenario,addr2, addr2) == 800, 0);\r\n\r\n        };\r\n```\r\n\r\n### 目前完整测试代码\r\n```rust\r\n\r\n#[test_only]\r\nmodule erc20::erc20test{\r\n    use erc20::erc20::{Self,TokenCap,BalanceList,TreasuryCap,AllowanceList};\r\n    use sui::test_scenario::{Self, Scenario};\r\n    use sui::tx_context::{Self,TxContext};\r\n    use sui::transfer;\r\n\r\n    struct ERC20TEST has drop{}\r\n    fun init(witness: ERC20TEST, ctx: &mut TxContext){\r\n        let treasury_cap = erc20::create_token(witness,b\"ETC20Test\", b\"ERCT\", 18, ctx);\r\n        transfer::public_transfer(treasury_cap, tx_context::sender(ctx));\r\n    }\r\n    #[test_only]\r\n    fun test_mint(scenario: &mut Scenario, sender: address, to: address, amount:u64) {\r\n        test_scenario::next_tx(scenario, sender);\r\n        let treasury_cap: TreasuryCap<ERC20TEST> = test_scenario::take_from_sender(scenario);\r\n        let balance_list = test_scenario::take_shared<BalanceList>(scenario);\r\n        erc20::mint(&treasury_cap, &mut balance_list, to, amount, test_scenario::ctx(scenario));\r\n        test_scenario::return_shared(balance_list);\r\n        test_scenario::return_to_sender(scenario, treasury_cap);\r\n    }\r\n\r\n    \r\n\r\n    #[test_only]\r\n    fun test_burn(scenario: &mut Scenario, sender: address, to: address, amount:u64){\r\n        test_scenario::next_tx(scenario, sender);\r\n        let treasury_cap: TreasuryCap<ERC20TEST> = test_scenario::take_from_sender(scenario);\r\n        let balance_list = test_scenario::take_shared<BalanceList>(scenario);\r\n        erc20::burn(&treasury_cap, &mut balance_list, to, amount, test_scenario::ctx(scenario));\r\n        test_scenario::return_shared(balance_list);\r\n        test_scenario::return_to_sender(scenario, treasury_cap);\r\n    }\r\n\r\n    #[test_only]\r\n    fun test_transfer(scenario: &mut Scenario, from:address, to: address, amount:u64){\r\n        test_scenario::next_tx(scenario, from);\r\n        let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(scenario);\r\n        let balance_list:BalanceList = test_scenario::take_shared(scenario);\r\n        erc20::transfer(&token_cap,&mut balance_list, to, amount, test_scenario::ctx(scenario));\r\n        test_scenario::return_shared(balance_list);\r\n        test_scenario::return_shared(token_cap);\r\n    }\r\n\r\n    #[test_only]\r\n    fun test_approve(scenario: &mut Scenario, owner:address, spender: address,amount:u64){\r\n        test_scenario::next_tx(scenario, owner);\r\n        let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(scenario);\r\n        let allowance_list = test_scenario::take_shared<AllowanceList>(scenario);\r\n        erc20::approve(&token_cap,&mut allowance_list,spender,amount,test_scenario::ctx(scenario));\r\n        test_scenario::return_shared(allowance_list);\r\n        test_scenario::return_shared(token_cap);\r\n    }\r\n\r\n    #[test_only]\r\n    fun test_transfer_from(scenario: &mut Scenario,owner:address, spender:address,amount:u64){\r\n        test_scenario::next_tx(scenario, spender);\r\n        let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(scenario);\r\n        let allowance_list = test_scenario::take_shared<AllowanceList>(scenario);\r\n        let balance_list = test_scenario::take_shared<BalanceList>(scenario);\r\n        erc20::transferFrom(&token_cap,&mut allowance_list,&mut balance_list,owner,spender,amount,test_scenario::ctx(scenario));\r\n        test_scenario::return_shared(allowance_list);\r\n        test_scenario::return_shared(token_cap);\r\n        test_scenario::return_shared(balance_list);\r\n    }\r\n\r\n    #[test_only]\r\n    fun get_balance(scenario: &mut Scenario, sender: address, to:address): u64{\r\n        test_scenario::next_tx(scenario, sender);\r\n        let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(scenario);\r\n        let balance_list = test_scenario::take_shared<BalanceList>(scenario);\r\n        let balance = erc20::balance_of(&token_cap,&mut balance_list, to,test_scenario::ctx(scenario));\r\n        test_scenario::return_shared(balance_list);\r\n        test_scenario::return_shared(token_cap);\r\n        return balance\r\n    }\r\n\r\n    #[test_only]\r\n    fun get_totalsupply(scenario: &mut Scenario, sender: address): u64{\r\n        test_scenario::next_tx(scenario, sender);\r\n        let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(scenario);\r\n        let balance_list = test_scenario::take_shared<BalanceList>(scenario);\r\n        let total_supply = erc20::total_supply(&token_cap,&mut balance_list,test_scenario::ctx(scenario));\r\n        test_scenario::return_shared(balance_list);\r\n        test_scenario::return_shared(token_cap);\r\n        return total_supply\r\n    }\r\n\r\n    #[test_only]\r\n    fun get_allowance(scenario: &mut Scenario,owner: address, spender: address): u64{\r\n        test_scenario::next_tx(scenario, spender);\r\n        let allowance_list = test_scenario::take_shared<AllowanceList>(scenario);\r\n        let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(scenario);\r\n        let allowance_amount = erc20::get_allowance(&token_cap, &mut allowance_list, owner, spender,test_scenario::ctx(scenario));\r\n        test_scenario::return_shared(allowance_list);\r\n        test_scenario::return_shared(token_cap);\r\n        return allowance_amount\r\n    }\r\n\r\n    #[test]\r\n    public fun test(){\r\n        let addr1 = @0xA;\r\n        let addr2 = @0xB;\r\n\r\n        let scenario = test_scenario::begin(addr1);\r\n        //1. create a token\r\n        {\r\n            erc20::test_init(test_scenario::ctx(&mut scenario));\r\n            test_scenario::next_tx(&mut scenario, addr1);\r\n            init(ERC20TEST{}, test_scenario::ctx(&mut scenario));\r\n            test_scenario::next_tx(&mut scenario, addr1);\r\n            let balance_list = test_scenario::take_shared<BalanceList>(&mut scenario);\r\n            let allowance_list= test_scenario::take_shared(&mut scenario);\r\n            let token_cap: TokenCap<ERC20TEST> = test_scenario::take_shared(&mut scenario);\r\n            erc20::init_token(&token_cap,&mut balance_list,&mut allowance_list, test_scenario::ctx(&mut scenario));\r\n            test_scenario::return_shared(balance_list);\r\n            test_scenario::return_shared(allowance_list);\r\n            test_scenario::return_shared(token_cap);\r\n        };\r\n\r\n        //2. mint\r\n        {\r\n\r\n            assert!(get_balance(&mut scenario,addr1,  addr1) == 0, 0);\r\n            assert!(get_totalsupply(&mut scenario, addr1) == 0, 0);\r\n\r\n            test_mint(&mut scenario, addr1, addr1, 1000);\r\n\r\n            assert!(get_balance(&mut scenario,addr1,  addr1) == 1000, 0);\r\n            assert!(get_totalsupply(&mut scenario,addr1) == 1000, 0);\r\n\r\n            test_mint(&mut scenario, addr1, addr1, 1000);\r\n\r\n            assert!(get_balance(&mut scenario,addr1,  addr1) == 2000, 0);\r\n            assert!(get_totalsupply(&mut scenario,addr1) == 2000, 0);\r\n\r\n        };\r\n\r\n        //3. burn \r\n        {\r\n\r\n            test_burn(&mut scenario, addr1, addr1, 1000);\r\n\r\n            assert!(get_balance(&mut scenario,addr1, addr1) == 1000, 0);\r\n            assert!(get_totalsupply(&mut scenario,addr1) == 1000, 0);\r\n\r\n            test_burn(&mut scenario, addr1, addr1, 1000);\r\n\r\n            assert!(get_balance(&mut scenario,addr1,  addr1) == 0, 0);\r\n            assert!(get_totalsupply(&mut scenario,addr1) == 0, 0);\r\n\r\n        };\r\n\r\n        //3.transfer\r\n        {   \r\n            test_scenario::next_tx(&mut scenario, addr1);\r\n            test_mint(&mut scenario, addr1, addr1, 1000);\r\n\r\n            assert!(get_balance(&mut scenario,addr1,  addr1) == 1000, 0);\r\n            assert!(get_balance(&mut scenario,addr2,  addr2) == 0, 0);\r\n\r\n            test_transfer(&mut scenario, addr1, addr2, 500);\r\n\r\n            assert!(get_balance(&mut scenario,addr1,  addr1) == 500, 0);\r\n            assert!(get_balance(&mut scenario,addr2,  addr2) == 500, 0);\r\n\r\n        };\r\n\r\n        //4.transferFrom\r\n        {\r\n\r\n            assert!(get_allowance(&mut scenario, addr1, addr2) == 0,0);\r\n\r\n            test_approve(&mut scenario, addr1, addr2, 500);\r\n\r\n            assert!(get_allowance(&mut scenario, addr1, addr2 ) == 500,0);\r\n\r\n            test_transfer_from(&mut scenario, addr1, addr2, 300);\r\n\r\n            assert!(get_allowance(&mut scenario, addr1, addr2 ) == 200,0);\r\n            assert!(get_balance(&mut scenario,addr2,  addr1) == 200, 0);\r\n            assert!(get_balance(&mut scenario,addr2, addr2) == 800, 0);\r\n\r\n        };\r\n\r\n        test_scenario::end(scenario);\r\n    }\r\n}\r\n\r\n```\r\n### result\r\n```rust\r\nRunning Move unit tests\r\n[ PASS    ] 0x0::erc20test::test\r\nTest result: OK. Total tests: 1; passed: 1; failed: 0\r\n```"},"author":{"user":"https://learnblockchain.cn/people/18488","address":null},"history":"bafkreihlt6sm4nzysijs6c2ic7jncdlhyjoiwnhshtfukeioqqu6xu4q5u","timestamp":1710226161,"version":1}