Move on Sui 环境以及项目搭建流程

Sui VsCode 环境配置

  • 安装 VsCode 后, 在扩展商店中下载 move-analyzer 插件
  • 安装 move-analyzer
    • git clone Move存储库并从源代码构建move-analyzer
      • 需要先安装 Rust 和 Cargo 环境
        • curl --proto '=https' --tlsv1.2 -sSf [<https://sh.rustup.rs>](<https://sh.rustup.rs/>) | sh
        • 后面需要升级 Rust 版本, 可以使用: rustup update stable
      • Linux 系统需要先确保依赖已经安装
        • sudo apt-get install curl git-all cmake gcc libssl-dev pkg-config libclang-dev libpq-dev build-essential
      • 安装 Sui 二进制文件 (时间有些长, 耐心等待)
        • cargo install --git https://github.com/move-language/move move-analyzer
        • 安装的二进制文件位于 ~/.cargo/bin
  • 目前插件还不完善, 代码提示和代码格式化比较弱

Sui 项目搭建

创建 package

  • 执行: sui move new my_first_package
    • 目录结构如下, 包含一个 Move.toml 文件以及 sources 目录

      ❯ tree my_first_package
      my_first_package
      ├── Move.toml
      └── sources
      
    • 默认的 Move.toml 文件(随着 sui 的更新, 默认文件可能也会随之更新)

    • 目前比较坑的点是, 要手动在 [package] 下添加 version = "0.0.1” , 否则 vscode move 插件无法提供代码提示和跳转

      cat my_first_package/Move.toml
      [package]
      name = "my_first_package"
      version = "0.0.1"
      # edition = "2024.alpha" # To use the Move 2024 edition, currently in alpha
      # license = ""           # e.g., "MIT", "GPL", "Apache 2.0"
      # authors = ["..."]      # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"]
      
      [dependencies]
      Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }
      
      # For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`.
      # Revision can be a branch, a tag, and a commit hash.
      # MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" }
      
      # For local dependencies use `local = path`. Path is relative to the package root
      # Local = { local = "../path/to" }
      
      # To resolve a version conflict and force a specific version for dependency
      # override use `override = true`
      # Override = { local = "../conflicting/version", override = true }
      
      [addresses]
      my_first_package = "0x0"
      
      # Named addresses will be accessible in Move as `@name`. They're also exported:
      # for example, `std = "0x1"` is exported by the Standard Library.
      # alice = "0xA11CE"
      
      [dev-dependencies]
      # The dev-dependencies section allows overriding dependencies for `--test` and
      # `--dev` modes. You can introduce test-only dependencies here.
      # Local = { local = "../path/to/dev-build" }
      
      [dev-addresses]
      # The dev-addresses section allows overwriting named addresses for the `--test`
      # and `--dev` modes.
      # alice = "0xB0B"
      
      

定义 module

  • 在 sources 目录下新建一个 move 文件: touch my_first_package/sources/my_module.move

    • 内容如下( 现在不需要关心太多语法, 下一篇会实践语法)
    module my_first_package::my_module {
    
        // Part 1: Imports
        use sui::object::{Self, UID};
        use sui::transfer;
        use sui::tx_context::{Self, TxContext};
    
        // Part 2: Struct definitions
        struct Sword has key, store {
            id: UID,
            magic: u64,
            strength: u64,
        }
    
        struct Forge has key, store {
            id: UID,
            swords_created: u64,
        }
    
        // Part 3: Module initializer to be executed when this module is published
        fun init(ctx: &mut TxContext) {
            let admin = Forge {
                id: object::new(ctx),
                swords_created: 0,
            };
            // Transfer the forge object to the module/package publisher
            transfer::public_transfer(admin, tx_context::sender(ctx));
        }
    
        // Part 4: Accessors required to read the struct attributes
        public fun magic(self: &Sword): u64 {
            self.magic
        }
    
        public fun strength(self: &Sword): u64 {
            self.strength
        }
    
        public fun swords_created(self: &Forge): u64 {
            self.swords_created
        }
    
        // Part 5: Public/entry functions (introduced later in the tutorial)
    
        // Part 6: Private functions (if any)
    
    }
    

Build package

  • 执行: 在 my_first_package 目录的根目录下, 执行: sui move build
    • 成功 build 之后, 会在 console 打印出下方的内容:

      UPDATING GIT DEPENDENCY https://github.com/MystenLabs/sui.git
      INCLUDING DEPENDENCY Sui
      INCLUDING DEPENDENCY MoveStdlib
      BUILDING my_first_package
      

测试(test) package

  • 使用 #[test] 声明是一个测试

  • 在上方提供的代码的基础上, 添加下方的内容:

        #[test]
        public fun test_sword_create() {
    
            // Create a dummy TxContext for testing
            let ctx = tx_context::dummy();
    
            // Create a sword
            let sword = Sword {
                id: object::new(&mut ctx),
                magic: 42,
                strength: 7,
            };
    
            // Check if accessor functions return correct values
            assert!(magic(&sword) == 42 && strength(&sword) == 7, 1);
        }
    
  • 使用 sui move test 命令执行带有 #[test]标记的函数

    • 测试成功的结果如下:

      BUILDING Sui
      BUILDING MoveStdlib
      BUILDING my_first_package
      Running Move unit tests
      Test result: OK. Total tests: 0; passed: 0; failed: 0
      
  • 篇幅有限, 本文只是带领大家熟悉一遍流程, 更详细的操作参考官方文档: https://docs.sui.io/guides/developer/first-app/build-test

发布(publish) package

  • 使用 sui client publish 命令发布一个 package
  • 使用 sui client call 命令调用 package 中的功能

Debug

  • 由于 Move 目前还没有调试器, 因此使用 std::debug 模块进行调试
  • 常用操作:
    • use std::debug; : 导入调试模块
    • debug::print(&v);
    • debug::print(v); : 在 v 已经是引用的情况下, 直接传入 v
    • debug::print_stack_trace(); : 打印当前堆栈跟踪

客户端应用程序

例如连接钱包, 使用 Sui RPC 查询数据

完整代码

module my_first_package::my_module {

    // Part 1: Imports
    use sui::object::{Self, UID};
    use sui::transfer;
    use sui::tx_context::{Self, TxContext};
    // Part 2: Struct definitions
    struct Sword has key, store{
        id: UID,
        magic: u64,
        strength: u64,
    }

    struct Forge has key, store {
        id: UID,
        swords_created: u64,
    }

    // Part 3: Module initializer to be executed when this module is published
    fun init(ctx: &mut TxContext) {
        let admin = Forge {
            id: object::new(ctx),
            swords_created: 0,
        };
        // Transfer the forge object to the module/package publisher
        transfer::public_transfer(admin, tx_context::sender(ctx));
    }

    // Part 4: Accessors required to read the struct attributes
    public fun magic(self: &Sword): u64 {
        self.magic
    }

    public fun strength(self: &Sword): u64 {
        self.strength
    }

    public fun swords_created(self: &Forge): u64 {
        self.swords_created
    }
    public fun sword_create(magic: u64, strength: u64, recipient: address, ctx: &mut TxContext) {
        use sui::transfer;

        // create a sword
        let sword = Sword {
            id: object::new(ctx),
            magic: magic,
            strength: strength,
        };
        // transfer the sword
        transfer::transfer(sword, recipient);
    }
    public fun sword_transfer(sword: Sword, recipient: address, _ctx: &mut TxContext) {
        use sui::transfer;
        // transfer the sword
        transfer::transfer(sword, recipient);
    }

    public fun new_sword (
            forge: &mut Forge,
            magic: u64,
            strength: u64,
            ctx: &mut TxContext,
        ): Sword {
            forge.swords_created = forge.swords_created + 1;
            let sword = Sword {
                id: object::new(ctx),
                magic: magic,
                strength: strength,
            };
            sword
        }

    // Part 5: Public/entry functions (introduced later in the tutorial)

    // Part 6: Private functions (if any)

    #[test]
    public fun test_sword_create() {
        use sui::transfer;

        // Create a dummy TxContext for testing
        let ctx = tx_context::dummy();

        // Create a sword
        let sword = Sword {
            id: object::new(&mut ctx),
            magic: 42,
            strength: 7,
        };

        // Check if accessor functions return correct values
        assert!(magic(&sword) == 42 && strength(&sword) == 7, 1);

        // Create a dummy address and transfer the sword
        let dummy_address = @0xCAFE;
        transfer::transfer(sword, dummy_address);
    }

    #[test]
    fun test_sword_transactions() {
        use sui::test_scenario;

        // create test addresses representing users
        let admin = @0xBABE;
        let initial_owner = @0xCAFE;
        let final_owner = @0xFACE;

        // first transaction to emulate module initialization
        let scenario_val = test_scenario::begin(admin);
        let scenario = &mut scenario_val;
        {
            init(test_scenario::ctx(scenario));
        };
        // second transaction executed by admin to create the sword
        test_scenario::next_tx(scenario, admin);
        {
            // create the sword and transfer it to the initial owner
            sword_create(42, 7, initial_owner, test_scenario::ctx(scenario));
        };
        // third transaction executed by the initial sword owner
        test_scenario::next_tx(scenario, initial_owner);
        {
            // extract the sword owned by the initial owner
            let sword = test_scenario::take_from_sender<Sword>(scenario);
            // transfer the sword to the final owner
            sword_transfer(sword, final_owner, test_scenario::ctx(scenario))
        };
        // fourth transaction executed by the final sword owner
        test_scenario::next_tx(scenario, final_owner);
        {
            // extract the sword owned by the final owner
            let sword = test_scenario::take_from_sender<Sword>(scenario);
            // verify that the sword has expected properties
            assert!(magic(&sword) == 42 && strength(&sword) == 7, 1);
            // return the sword to the object pool
            test_scenario::return_to_sender(scenario, sword)
            // or uncomment the line below to destroy the sword instead
            // test_utils::destroy(sword)
        };
        test_scenario::end(scenario_val);
    }

    #[test_only] use sui::test_scenario as ts;
    #[test_only] const ADMIN: address = @0xAD;

    #[test]
    public fun test_module_init() {
        let ts = ts::begin(@0x0);

        // first transaction to emulate module initialization.
        {
            ts::next_tx(&mut ts, ADMIN);
            init(ts::ctx(&mut ts));
        };

        // second transaction to check if the forge has been created
        // and has initial value of zero swords created
        {
            ts::next_tx(&mut ts, ADMIN);

            // extract the Forge object
            let forge: Forge = ts::take_from_sender(&mut ts);

            // verify number of created swords
            assert!(swords_created(&forge) == 0, 1);

            // return the Forge object to the object pool
            ts::return_to_sender(&mut ts, forge);
        };
        ts::end(ts);
    }
}

加入组织, 一起交流/学习!

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值