sui move 动态字段练习(1)

  • shaflow01
  • 更新于 2024-03-10 16:52
  • 阅读 744

引言学习了suimove中的动态字段,table,bag,作为练习,我准备使用它们模拟solidity中的映射类型,在suimove实现一个类似erc20的同质化代币作为之前学习的实践与巩固。本文分享了练习过程中的obj设计、createtoken。

引言

学习了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<T>>
BalanceData中的totalsupply字段储存这一种类型代币的总供应
balance字段储存某一地址代币余额

allowance

以下obj用于实现allowance

    struct AllowanceData&lt;phantom T> has key, store{
        id: UID,
        allowance:Table&lt;address , AllowanceAmountList>,
    }

    struct AllowanceAmountList has key, store{
        id: UID,
        allowance_amount: Table&lt;address, u64>,
    }

    struct AllowanceList has key,store{
        id: UID,
        allowance_list: Bag,
    }

元数据

    struct ERC20MetaData&lt;phantom T> has key, store{
        id: UID,
        name: string::String,
        symbol: ascii::String,
        decimal: u8,
    }

在代币创建时要输入创建的obj,是代币的元数据,将会是share_obj

Cap

    struct TreasuryCap&lt;phantom T> has key,store{
        id:UID,
    }

    struct TokenCap&lt;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&lt;T: drop>(witness: T, name: vector&lt;u8>, symbol: vector&lt;u8>, decimal:u8, ctx:&mut TxContext):TreasuryCap&lt;T>{

        assert!(sui::types::is_one_time_witness(&witness), EBadWitness);

        let erc20_metadata = ERC20MetaData&lt;T>{
            id: object::new(ctx),
            name: string::utf8(name),
            symbol: ascii::string(symbol),
            decimal: decimal,
        };

        let treasury_cap = TreasuryCap&lt;T>{
            id: object::new(ctx),
        };

        let token_cap = TokenCap&lt;T>{
            id: object::new(ctx),
        };

        transfer::share_object(erc20_metadata);
        transfer::share_object(token_cap);
        treasury_cap
    }

    public fun init_token&lt;T>(_:&TokenCap&lt;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&lt;T>()));
        assert!(!bag::contains(& balance_list.balance_list, type),0);
        assert!(!bag::contains(& allowance_list.allowance_list, type),0);
        let balance_data = BalanceData&lt;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&lt;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,它们将只会被初始化一次:
  4. 检查对应type的token是否被初始化
  5. 创建AllowanceData,BalanceData
  6. 将其添加到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&lt;BalanceList>(&mut scenario);
            let allowance_list= test_scenario::take_shared(&mut scenario);
            let token_cap: TokenCap&lt;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
  • 原创
  • 学分: 9
  • 分类: Sui
  • 标签:
点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

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