sui move 动态字段练习(1)

引言

学习了sui move中的动态字段,table,bag,作为练习,我准备使用它们模拟solidity中的映射类型,在sui move实现一个类似erc20的同质化代币作为之前学习的实践与巩固。本文分享了练习过程中的obj设计、create token。
注:本例实现仅用于学习动态字段,由于访问gas和便捷性不强,无法用于生产。在sui move中使用的同质化代币请使用官方标准库中内置的coin

obj设计

balance

首先,我是用如下结构体模拟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>
BalanceData中的totalsupply字段储存这一种类型代币的总供应
balance字段储存某一地址代币余额

allowance

以下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

Cap

    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需要传入元数据相关信息和一次性见证(区分代币种类)

  1. 检查一次性见证
  2. 生成元数据相关信息
  3. 生成treasury_cap,token_cap

init_token:
调用init_token是为了初始化对应代币的AllowanceData和BalanceData,它们将只会被初始化一次:

  1. 检查对应type的token是否被初始化
  2. 创建AllowanceData,BalanceData
  3. 将其添加到BalanceList和AllowanceList中

test

在进行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
  • 33
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值