入门 Sui Move 开发:3. Sui Move 语法和设计模式

  • greyhao
  • 更新于 2024-11-13 15:42
  • 阅读 366

Sui Move 语法和设计模式,并发布一个代币到链上。

在第一节入门 Sui Move 开发:1. 环境安装 中我们已经成功安装了 Sui Move 开发环境及开发 IDE。

在第二节# 入门 Sui Move 开发:2. 常用命令、编写并发布 Hello World 合约 中我们也了解了常用的命令并在链上发布了自己的第一个合约 hello world

本节介绍 Sui Move 中的基础语法,学完本节再回头看我们在 hello world 中的代码,相信你就可以很轻松的看懂了,学完本节你将学会如何在 Sui 链上发布代币。

Sui Move 语法

如果你没有学过其他编程语言可以这么理解:语法就是编写代码的一些规定。

数据类型

Move 是强类型语言,并且不会自动转换类型,转换类型使用 as 关键字。

数据类型包含:

  • 基本数据类型 其中基本数据类型又包括:
    • 整型,只支持正整数,根据数值的范围又分为:
      • u8
      • u32
      • u64
      • u128
      • u256
    • 布尔,值只有 truefalse
    • 地址,
  • 自定义数据类型(使用 struct 关键字定义)
let a: u8 = 1; // 指定 a 的类型为 u8

let c = 10; // 不指定则默认类型为 u64

let isSui = true; // isSui 是 bool 类型

let sender: address; // 定义类型为 address 的变量 sender

let sender = @0x0; // 定义 address 类型变量并设置初始值

// Hello 结构体 
public struct Hello has key, store { i
    d: UID, 
    text: String
 }

变量

定义变量

使用 let 关键字定义变量,格式 let 变量名: 类型 = 赋初始值;,变量的作用域是在它所在的大括号中。

  fun dog() {
    let a = 1;
  }

  fun cat() {
    a = 2; // 这句会报错
  }

常量

使用 const 关键字定义,常量名大写。

const T:u8 = 10;

流程控制

语法:if(布尔表达式) 表达式1 else 表达式2; 注意括号结尾需要分号。

let i = 10;
let mut a:u64;

// 单行不加花括号
if(i > 5) a = i + 1;

// 多行加花括号
if(i > 6) {
  a = i + 2;
};

// if...else...
if(i > 3) {
  a = i + 1;
} else {
  a = i -1;
};

循环

支持 whileloop 循环。 continue: 跳过本次循环,进入下一个循环。 break:跳出循环。

let mut i = 1;
// while 循环
while( i < 3) {
    i = i + 1;
};

// loop 循环
loop {
    if(i >= 8) {
        break; // 满足条件跳出循环
    };
}

函数

定义函数

语法:

<public> <entry> fun 方法名(<参数名: 参数类型>): (<返回值类型>) {
    // 函数体
}

<> 表示可没有的部分

函数的可见性

  • fun 同模块内可以被调用
  • public fun 模块外可以被调用
  • public(package) fun 可以在同包的不同模块中被调用
  • public entry fun 可以被任意模块,被命令行,dapp 调用
  • entry fun 可以被同模块,命令行,dapp 调用
fun init() {

}

public fun add(a: u64, b: u64): u64 {
    a + b
}

函数的参数

参数传递格式:

  • ref (不可变引用,格式 & 参数类型)只能查看参数
  • mut ref (可变引用,格式 &mut 参数类型)可以查看和编辑参数
  • value (值引用,格式 参数类型)可以查看、编辑、删除参数
public struct Person {
    name: String,
    score: u128,
}

// 不可变引用:访问对象的某个字段
public fun view_person(person: &Person): String {
  person.name
}

// 可变引用:编辑对象的值
public fun edit_person(person: &mut Person, newName: String) {
  person.name = newName
}

// 值引用:删除对象
public fun del_person(person: Person) {
  let Person {id, name: _} = person; // 解包语法
  object::delete(id);
}

函数返回值

单个返回值
fun compare(a: u64, b: u64): u8 {
  if(a == b) {
    return 0 // 直接返回
  };
  // 返回值在最后一时可以省略 return
  if(a > b) {
    1 // 省略 return
  } else {
    2 // 省略 return
  }
}
多个返回值
// 返回两个值
fun return_nums(): (u64, bool) {
  (19, true)
}

fun call_return_nums(): u64 {
  // 接收多个函数返回值,必须有括号
  let (a, b) = return_nums();
  if(b) {
    a
  } else {
    0
  }
}

entry 函数

一种特殊类型的函数,通常用来执行具有链上交易特性的操作,是用户通过交易调用智能合约的主要入口,用于管理状态更改(代币转账、数据更新等)和触发不同的操作。

定义要求:

  • 函数定义中有 entry 关键词
  • 没有返回值
  • 最后一个参数是 TxContext 类型的可变引用(可选)
// entry 函数,调用时不需要传入 ctx 参数值,虚拟机会提供
public entry fun mint(ctx: &mut TxContext) {
  let object = Hello{
    id: object::new(ctx),
    text: utf8(b"Hello Sui!")
  };
  transfer::public_transfer(object, tx_context::sender(ctx));
}

结构体

定义结构体

注意:结构体不能包含自身类型

结构体命名规则:

  • 结构体名称大写字母开头,包含大小写字母、下划线和数字组成,大驼峰命名法
  • 字段名小写字母、数字和下划线组成
public struct Person {
    name: String,
    score: u128,
}

实例化结构体

// 实例化 Person 结构体
let p = Person { name: utf8(b"greyhao"), score: 10 };

结构体的 Ability

结构体数据类型的能力,包含:keystorecopydrop 四种。使用 has 关键字来声明结构体的 albility

给结构体设置 store,copy,drop 时,需要确保结构体内所有字段包含这些能力。

基础数据类型和内建数据类型默认有:store、copy、drop 能力。

key

key 用来标识结构体是否是对象。对象是特殊的结构体。

public struct PersonObj has key, store {
  id: UID, // key ability 要求第一个字段必须为 id: UID,反过来说有 id 说明这是个对象
  name: String,
}
store

使用场景:

  • 1.对象需要在定义他的模块之外调用 transfer 方法;
  • 2.结构体需要被嵌套;
  • 3.非对象结构体,要作为对象的字段,也需要 store 能力;
copy

标记结构体是否可以被复制,不能用于对象结构体。

drop

标记这个结构体是否能在作用域结束的时候自动删除。不能自动删除则需要手动删除。同样只能用于非对象结构体。

销毁结构体实例

// 普通结构体
public fun drop(p: Person) {
  let Person{name: _, score: _} = p;
}

// 删除对象,开发中积极删除无用对象,减少 gas 消耗
public fun drop_article(po: PersonObj) {
  let PersonObj{id: id, title:_, name: _} = po;
  object::delete(id); // 删除对象 id 的唯一方法
}

设计模式

Capability

Capability 是用来控制和管理对特定资源或模块的访问,实现细粒度的权限管理。

public struct TeacherCap has key {
  id: UID
}

// 这个函数方法使其只能被拥有 TeacherCap capability 的 object 调用
// _ 表示 TeacherCap 会被立即消耗
public entry fun create_object(_: &TeacherCap, ctx: &mut TxContext) {
  //
  let xx_obj = {};
  transfer::transfer(xx_obj, tx_context::sender(ctx))
}

Witness

witness 是一种设计模式,用于证明有关的一个资源或类型 A,在短暂的 witness 资源被消耗后只能启动一次。

One Time Witness(OTW)是 witness的一个子模式,在创建同质化代币(也就是 Coin )时会用到。

下面是创建一个代币的简单例子,如何创建合约项目以及如何发布合约可以在上一节中找到相关内容。

// 一个创建代币的简单例子
module coin_greyhao::greyhaocoin {
  use sui::coin::{Self, TreasuryCap, Coin};

  // 结构体的名字是模块名的大写,并且必须有 drop 能力
  public struct GREYHAOCOIN has drop { }

  fun init(witness: GREYHAOCOIN, ctx: &mut TxContext) {
    // 代币的信息
    let decimals = 8;
    let symbol = b"GREYHAOCOIN";
    let name = b"GHC";
    let description = b"Coin was created by Greyhao.";

    // 返回 treasury_cap
    let (treasury_cap, metadata) = coin::create_currency(witness, decimals, symbol, name, description, option::none(), ctx);

    transfer::public_freeze_object(metadata);

    // treasury_cap 转给了发起交易的地址,所以 mint、burn 方法只能创建代币的地址可以调用
    transfer::public_transfer(treasury_cap, tx_context::sender(ctx));
  }

  public entry fun mint(witness: &mut TreasuryCap<GREYHAOCOIN>, amount: u64, recipitent: address, ctx: &mut TxContext) {
    coin::mint_and_transfer(witness, amount, recipitent, ctx);
  }

  public entry fun burn(witness: &mut TreasuryCap<GREYHAOCOIN>, coin: Coin<GREYHAOCOIN>) {
    coin::burn(witness, coin);
  }
}

到这里我们就介绍了 Sui Move 的一些语法并完成了发布一个代币需要的代码的编写。

你可以尝试自己发一个代币到链上,然后使用不同的地址来调用 mint 方法看看会有什么不同。

本节中有很多需要记忆的地方,多写就会变得熟练。

如果觉得本节内容对你有所帮助,可以点赞鼓励下。

如果你对文章中内容有任何疑问可以留言。

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
greyhao
greyhao
开发过多链钱包。目前学习 Sui 合约开发中。欢迎一起学习交流