Move —— Sui 合约 (Counter)

通过实现一个完整的计数器(Counter)智能合约,学习 Sui 对象的生命周期管理。本文涵盖对象的创建、修改和销毁逻辑,以及 TxContext(交易上下文)、entry 函数和 assert! 断言的实际使用方式。

1. 核心概念

1.1 TxContext (交易上下文)

在 Sui 的函数中,TxContext 通常作为最后一个参数传递。它有两个核心作用:

  1. 身份识别:知道是谁发起了交易 (tx_context::sender(ctx)).
  2. ID 生成:为新对象生成全局唯一的 UID (object::new(ctx)).
    • 注意:因为生成 ID 会改变上下文内部的状态,所以必须传 &mut TxContext

1.2 entry 函数

普通函数 fun 只能被其他 Move 代码调用。

如果希望函数能被 钱包 或 CLI 直接调用(比如用户点击”铸造”按钮),必须在 fun 前加上 entry 关键字。

2. 业务逻辑流程

我们要实现一个计数器 (Counter),包含三个功能:

  1. 创建 (Mint):创建一个 Counter 对象,并把所有权交给发送者。
  2. 增加 (Increment):让计数器 +1。为了演示错误处理,我们规定最大值不能超过 10
  3. 销毁 (Burn):彻底销毁这个计数器对象。

3. Counter 合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 使用常量定义错误码
const EValueTooLarge: u64 = 0;

// 对象定义必须包含 id: UID,且拥有 key 能力
public struct Counter has key {
id: UID,
value: u64,
}

// ctx 用于生成 UID 和获取发送者地址
public entry fun create(ctx: &mut TxContext) {
// 创建对象
let counter = Counter {
id: object::new(ctx), // 消耗 ctx 生成唯一 ID
value: 0,
};

// 转移所有权,将 counter 发送给交易发起人 (Sender)
transfer::transfer(counter, tx_context::sender(ctx));
}

// 必须传入 &mut 引用,因为我们要修改它的值
public entry fun increment(counter: &mut Counter) {
counter.value = counter.value + 1;

// 逻辑控制与断言 (Assert)
// 语法:assert!(条件, 错误码)
// 如果 value > 10,交易中止 (Abort),回滚所有更改
assert!(counter.value <= 10, EValueTooLarge);
}

// 必须传入对象的值 (非引用),进行解构
public entry fun delete(counter: Counter) {
// 解构结构体
let Counter { id, value: _ } = counter;


// 显式销毁 UID,只有删除了 UID,对象才算真正从链上移除
object::delete(id);
}

4. 总结:对象操作范式

操作 函数参数写法 核心 API 说明
创建 ctx: &mut TxContext object::new(ctx) 需要 Context 生成 ID。
转移 obj: T transfer::transfer 必须拥有对象所有权。
修改 obj: &mut T 直接修改字段 传入可变引用。
销毁 obj: T object::delete(id) 必须解构并销毁 UID。