# SUI+Move快速上手

By [lolieatapple](https://paragraph.com/@lolieatapple-2) · 2022-11-02

---

Sui Move，一切以对象为核心。

官方文档：

[https://docs.sui.io/build](https://docs.sui.io/build)

开发文档：

[https://examples.sui.io/](https://examples.sui.io/)

1\. 安装sui cli命令行工具
==================

    $ cargo install --locked --git https://github.com/MystenLabs/sui.git --branch devnet sui sui-node
    

2\. 初始化测试环境，创建本地测试地址
====================

    $ sui client
    $ sui client addresses
    

可以看到，sui的地址长度与以太坊相同，都是20个字节，与Aptos不同。

3\. 安装sui浏览器插件钱包
================

在插件钱包页面上申请测试币。

[https://chrome.google.com/webstore/detail/sui-wallet/opcgpfmipidbgpenhmajoajpbobppdil](https://chrome.google.com/webstore/detail/sui-wallet/opcgpfmipidbgpenhmajoajpbobppdil)

![request devnet sui token](https://storage.googleapis.com/papyrus_images/ff333284055ef7e57b64b0aa5af968547a4c5db206e963b6dc86e10f2bd2fefb.png)

request devnet sui token

在sui浏览器中查看钱包地址信息：

[https://explorer.devnet.sui.io/addresses/0xdd36d012f391deb5cbf56a54285ac8f735b2736b](https://explorer.devnet.sui.io/addresses/0xdd36d012f391deb5cbf56a54285ac8f735b2736b)

在sui app页面可以测试NFT mint。

![](https://storage.googleapis.com/papyrus_images/8096c5341a72157e61c3599f84f33e104c237c80715d5d909300cc3705497b29.png)

还有几款网页游戏和NFT市场可以体验。

4\. 命令行转账
=========

向命令行测试地址转账一些SUI，之后可以使用如下指令查看地址余额(不转账测试币的话命令查不到信息)：

    $ sui client gas
    $ sui client objects
    

通过objects命令可以看到地址下数据对象信息，可以看到sui token默认的数据结构和地址是：0x2::coin::Coin<0x2::sui::SUI> 这里与Aptos不同。（Aptos是0x1::coin::CoinStore<0x1::aptos\_coin::AptosCoin>，可以看到地址与数据类型均不同）

使用如下指令可以创建命令行新地址，新地址助记词在命令执行后展示，请自行备份。

    $ sui client new-address ed25519
    

SUI token默认的decimals是9，这里与Aptos默认是8不同。

SUI的每次转账都会为接收地址创建一个新的Coin对象，默认不会自动合并。这里与Aptos不同。

用户可以自己通过命令来拆分或合并objects。

在调用transfer转账时，必须有另一个object来支付gas费，才能将一个object转走。因此，我们可以总结一个命令行转账过程如下：

1）拆分ojbect，将想要转出的部分拆分出独立的object，剩余部分是另一个object；

2）将拆分好的object转给对方；

3）接收方可以根据需要，决定是否要合并接收到的object到自己现有的object。

这个合并和拆分的过程，看起来有点类似于BTC的UTXO。估计是为了提升TPS而做出的设计。

5\. 命令行调用Move代码
===============

    $ sui client call --function transfer --module sui --package 0x2 --args 0x187f2af1f680bc1c9ae103dbcb28dc98b151c7fa 0xb5dede8f99c57266a221f1a88bc8cb71da6dd241 --gas-budget 1000
    

使用call指令，指定function名称，module名称，package地址和传入参数即可完成Move接口调用。

这里需要注意，这里即使是使用Move合约接口转账，依然需要有独立的object来支付gas费。与上一节转账时提到过程类似。

Object内容每次发生变更时，version号回自动加一。

6\. 开发Move代码
============

新建一个工程目录：

    $ sui move new my_first_package
    

sui的代码目录结构与Aptos相似：

![基础目录结构](https://storage.googleapis.com/papyrus_images/cbaab0cea0b3cf829009905323098276c2177f4980600fd2da4de5f71eb27a88.png)

基础目录结构

用户代码放在sources目录下，这里需要注意几点与Aptos不同：

1）框架库在sui::下面，标准库std::位置和内容与Aptos有所差别

2）module的默认初始化函数名和定义不同，sui是`fun init(ctx: &mut TxContext)`, Aptos是：`fun init_module(sender: &signer)`

1.  sui的函数入口参数中没有默认的signer对象，sui的entry函数可以包含一个`ctx: &mut TxContext`对象作为函数的最后一个参数，另外参数只能传入owner为sender或shared的object
    
2.  sui使用transfer::transfer的方式来转移对象，而不是Aptos的move\_to
    
3.  一个object的owner既可以是一个地址，也可以是另一个object
    

编译Move使用指令：

    $ sui move build
    

(这里与Aptos不同，aptos用的是aptos move compile)

单元测试指令：

    $ sui move test
    

7\. 部署sui代码
===========

官方文档：

[https://docs.sui.io/build/cli-client#publish-packages](https://docs.sui.io/build/cli-client#publish-packages)

    $ sui client publish --path $PATH_TO_PACKAGE/my_move_package --gas-budget 30000
    

部署完成后的合约默认是一个不可变更的对象(object)。部署过程中，会消耗gas费对象，生成合约对象和数据对象。

8\. Sui Move Library
====================

Sui提供了一系列在Move中操作object的接口。

Sui中对象的ownership所有权限分为：

1.  owner为地址；
    
2.  owner为另一个对象；
    
3.  owner为shared and immutable, 即对象公开但不可再更改；
    
4.  owner为可shared and mutable, 对象公开并且任何人均可以更改；（未开发完成功能）
    

操作object的接口包括：

    use sui::transfer;
    transfer::transfer(obj, recipient);
    transfer::transfer_to_object(obj, &mut owner);
    transfer::transfer_to_object_id(child, parent);
    transfer::transfer_child_to_object(child, child_ref, &mut new_parent);
    transfer::transfer_child_to_address(child, child_ref, recipient);
    transfer::freeze_object(obj);
    transfer::share_object(obj);
    

Transaction Context:

    // assmue `ctx` has type `&mut TxContext`.
    let info = sui::object::new(ctx);
    
    sui::tx_context::sender(ctx)
    

9\. 基础语法
========

一个可以创建sui对象的结构体，第一个参数必须为id，并且定义key能力（key代表其具有全局索引的能力）:

    use sui::object::UID;
    
    struct ColorObject has key {
        id: UID,
        red: u8,
        green: u8,
        blue: u8,
    }
    

UID可以使用`object::new(ctx)`接口创建，其中ctx是entry类型函数参数可选自带的交易信息(只能是最后一个参数)。entry类型函数表示是可以被交易直接调用的函数。

使用transfer函数可以将object的owner转给对应的地址。每一个object必须有对应的owner，owner可以是一个地址，一个obj，或者shared。

因为Sui的全局对象存储在Move之外，因此不使用Move全局api语法。同时，单元测试的时候，也只能通过test\_scenario来访问内部对象。

test\_scenario的主要接口包括：

    test_scenario::begin
    test_scenario::next_tx
    test_scenario::ctx
    test_scenario::has_most_recent_for_sender
    test_scenario::take_from_sender
    test_scenario::return_to_sender
    object::id_from_address(tx_context::last_created_object_id(ctx))
    test_scenario::take_from_sender_by_id
    test_scenario::take_immutable
    test_scenario::return_immutable
    ...
    

例如：

    let owner = @0x1;
    // Create a ColorObject and transfer it to @owner.
    let scenario_val = test_scenario::begin(owner);
    let scenario = &mut scenario_val;
    {
        let ctx = test_scenario::ctx(scenario);
        color_object::create(255, 0, 255, ctx);
    };
    

(大括号外面的分号是必须的)

在Sui中，只有Owner可以在交易中使用Object，不论是读或者写。

删除不用的Object可使用:

    public fun delete(id: UID) { ... }
    

在多个struct结构体互相包含时，子结构体需要具备store能力。

当一个对象被包装在另一个对象中时，子对象不能作为函数参数传递，除非将其转移出来完成解包。

10\. Sui Wallet
===============

    const Permissions = ['viewAccount','suggestTransactions'] as const;
    
    // Check connection status of wallet
    const result = await window.suiWallet.requestPermissions(Permissions);
    
    const result = await window.suiWallet.mi(Permissions);
    
    const result = await window.suiWallet.getAccounts();
    
    // Note
    interface MoveCallTransaction {
        packageObjectId: ObjectId;
        module: string;
        function: string;
        typeArguments: string[];
        arguments: SuiJsonValue[];
        gasPayment?: ObjectId;
        gasBudget: number;
    }
    // Sign transaction
    const result = await window.suiWallet.executeMoveCall(txs)
    
    // Sign transaction (with Serialized)
    const result = await window.suiWallet.executeSerializedMoveCall(tx.toString())
    

11\. SDK
========

JavaScript SDK:

[https://github.com/MystenLabs/sui/tree/main/sdk/typescript/](https://github.com/MystenLabs/sui/tree/main/sdk/typescript/)

---

*Originally published on [lolieatapple](https://paragraph.com/@lolieatapple-2/sui-move)*
