SUI+Move快速上手
November 2nd, 2022

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

官方文档:

开发文档:

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浏览器插件钱包

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

request devnet sui token
request devnet sui token

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

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

还有几款网页游戏和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相似:

基础目录结构
基础目录结构

用户代码放在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代码

官方文档:

$ 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:

Subscribe to lolieatapple
Receive the latest updates directly to your inbox.
Verification
This entry has been permanently stored onchain and signed by its creator.
More from lolieatapple

Skeleton

Skeleton

Skeleton