{"content":{"title":"通过官方合约学习Sui Move语法知识1","body":"# 通过官方合约学习Sui Move语法知识1\r\n\r\n## 合约1：Counter\r\n\r\n### 1.1 合约代码\r\n\r\nhttps://github.com/MystenLabs/sui/blob/main/sui_programmability/examples/basics/sources/counter.move\r\n\r\n功能说明：\r\n\r\n- 任何人都可以创建并共享一个计数器 \r\n\r\n- 任何人都可以将计数器增加1 \r\n\r\n- 计数器的所有者可以将其重置为任何值\r\n- 计数器的所有者可以将计数器对象删除\r\n\r\n```rust\r\nmodule basics::counter {\r\n    use sui::transfer;\r\n    use sui::object::{Self, UID};\r\n    use sui::tx_context::{Self, TxContext};\r\n\r\n    /// 计数器对象类型定义\r\n    struct Counter has key {\r\n        id: UID,\r\n        owner: address,\r\n        value: u64\r\n    }\r\n\r\n    public fun owner(counter: &Counter): address {\r\n        counter.owner\r\n    }\r\n\r\n    public fun value(counter: &Counter): u64 {\r\n        counter.value\r\n    }\r\n\r\n    /// 创建并设置计数器对象为共享对象\r\n    public entry fun create(ctx: &mut TxContext) {\r\n        transfer::share_object(Counter {\r\n            id: object::new(ctx),\r\n            owner: tx_context::sender(ctx),\r\n            value: 0\r\n        })\r\n    }\r\n\r\n    /// 计数器自增1（任何人可以调用）\r\n    public entry fun increment(counter: &mut Counter) {\r\n        counter.value = counter.value + 1;\r\n    }\r\n\r\n    /// 设置计数器的值（只有合约创建者可以调用）\r\n    public entry fun set_value(counter: &mut Counter, value: u64, ctx: &TxContext) {\r\n        assert!(counter.owner == tx_context::sender(ctx), 0);\r\n        counter.value = value;\r\n    }\r\n\r\n    /// 断言计数器的值\r\n    public entry fun assert_value(counter: &Counter, value: u64) {\r\n        assert!(counter.value == value, 0)\r\n    }\r\n\r\n    /// 删除计数器共享对象（只有合约创建者可以调用）\r\n    public entry fun delete(counter: Counter, ctx: &TxContext) {\r\n        assert!(counter.owner == tx_context::sender(ctx), 0);\r\n        let Counter {id, owner:_, value:_} = counter;\r\n        object::delete(id);\r\n    }\r\n}\r\n```\r\n\r\n### 1.2 知识点\r\n\r\n#### （1）UID\r\n\r\n- 创建一个`Sui object`需要一个唯一`ID`\r\n\r\n- 可以根据当前`TxContext`中的信息，使用 `sui::object::new` 函数来创建一个新的`ID`\r\n\r\n#### （2）类型能力\r\n\r\n能力定义对象（Object）的行为。一个对象类型最多拥有4项能力，分别是：\r\n\r\n- **Copy**: 允许具有该类型的值被复制\r\n- **Drop**: 允许具有该类型的值被弹出或丢弃\r\n- **Key**: 允许该类型作为全局存储操作的键\r\n- **Store**: 允许具有该类型的值在全局存储中被存储\r\n\r\n拥有 `Key` 和 `Store` 能力特性的定制类型被视为**资产**，资产可以在全局存储中存储，也可以在不同账号之间转移。\r\n\r\n#### （3）共享对象\r\n\r\n共享对象`（Share object）`可以通过 `sui::transfer::share_object` 被共享，从而让所有人都可以使用。\r\n\r\n因为该对象可以被任何人使用， 因此需要根据需求在逻辑中设计额外的安全检查，如：示例中的`set_value`和`delete`接口，只有合约创建者可以调用去设置计数器的值或删除计数器共享对象。\r\n\r\n#### （4）TxContext\r\n\r\n保存了当前执行交易的上下文信息。定义结构如下：\r\n\r\nhttps://github.com/MystenLabs/sui/blob/main/crates/sui-framework/packages/sui-framework/sources/tx_context.move#L24\r\n\r\n```rust\r\n    struct TxContext has drop {\r\n        /// The address of the user that signed the current transaction\r\n        sender: address,\r\n        /// Hash of the current transaction\r\n        tx_hash: vector<u8>,\r\n        /// The current epoch number\r\n        epoch: u64,\r\n        /// Timestamp that the epoch started at\r\n        epoch_timestamp_ms: u64,\r\n        /// Counter recording the number of fresh id's created while executing\r\n        /// this transaction. Always 0 at the start of a transaction\r\n        ids_created: u64\r\n    }\r\n```\r\n\r\n在示例代码中，通过`tx_context::sender(ctx)`获取到当前交易的发起者地址，以便可以和合约中记录的创建者地址是否一致，用于一些特定接口调用的安全检查，来判断是否是合约创建者进行的调用。\r\n\r\n相关汇总如下：\r\n\r\n| 接口名                                | 说明                                  |\r\n| ------------------------------------- | ------------------------------------- |\r\n| `tx_context::sender(ctx)`             | 获取当前交易发起者（交易签署者）地址  |\r\n| `tx_context::digest(ctx)`             | 获取当前交易Hash                      |\r\n| `tx_context::epoch(ctx)`              | 获取当前世代                          |\r\n| `tx_context::epoch_timestamp_ms(ctx)` | 以毫秒级别unix时间戳返回epoch开始时间 |\r\n\r\n#### （5）对象删除\r\n\r\n要删除一个`Object`，先要解包这个`Object`并且获取它的`Object ID`。在调用`object::delete`进行对象删除。\r\n\r\n注：\r\n\r\n- 不关心的参数可以使用`_`以忽略\r\n\r\n- 解包的操作只能够在定义了这个`Object`的Module内进行\r\n\r\n```rust\r\nlet Counter {id, owner:_, value:_} = counter;\r\nobject::delete(id);\r\n```\r\n\r\n## 合约2：clock\r\n\r\n### 2.1 合约代码\r\n\r\nhttps://github.com/MystenLabs/sui/blob/main/sui_programmability/examples/basics/sources/clock.move\r\n\r\n功能说明：调用`get_time`接口，将通过事件形式抛出当前毫秒级别时间戳。\r\n\r\n```rust\r\nmodule basics::clock {\r\n    use sui::clock::{Self, Clock};\r\n    use sui::event;\r\n    use sui::tx_context::TxContext;\r\n\r\n    struct TimeEvent has copy, drop {\r\n        timestamp_ms: u64,\r\n    }\r\n\r\n    /// Emit event with current time.\r\n    public entry fun get_time(clock: &Clock, _ctx: &mut TxContext) {\r\n        event::emit(TimeEvent { timestamp_ms: clock::timestamp_ms(clock) });\r\n    }\r\n}\r\n```\r\n\r\n### 2.2 知识点\r\n\r\n#### （1）合约事件\r\n\r\n事件对于区块链系统至关重要，它是追踪链上行为的主要方法。\r\n\r\n`Sui`上面的事件也是以`Objects`的形式表示。有几种系统层级的事件, 包括：`Move event`、Publish event、`Transfer object event`等。\r\n\r\n- **事件定义**\r\n\r\n事件对象定义仅需要具有`copy`和`drop`的能力，表明可以被复制并在作用域结束后被销毁。它不代表资产，我们只对其包含的数据感兴趣。\r\n\r\n```rust\r\nstruct TimeEvent has copy, drop {\r\n    timestamp_ms: u64,\r\n}\r\n```\r\n\r\n- **事件抛出**\r\n\r\n```rust\r\nevent::emit(TimeEvent { timestamp_ms: clock::timestamp_ms(clock) });\r\n```\r\n\r\n#### （2）预置对象\r\n\r\n`get_time`接口第一个参数是`sui::clock::Clock`，`Clock`是系统已经预置的硬编码的对象只读引用实例，地址是：`0x6`，即：接口调用将`0x6`作为`Clock`参数的地址传入。\r\n\r\n相关汇总如下：\r\n\r\nhttps://github.com/MystenLabs/sui/blob/mainnet-v1.15.1/crates/sui-framework/packages/sui-framework/sources/object.move#L21\r\n\r\n| 地址 | 说明                      |\r\n| ---- | ------------------------- |\r\n| @0x5 | Sui System State Object   |\r\n| @0x6 | Clock Object              |\r\n| @0x7 | AuthenticatorState Object |\r\n| @0x8 | Random Object             |\r\n\r\n## 合约3：object_basics\r\n\r\n### 3.1 合约代码\r\n\r\nhttps://github.com/MystenLabs/sui/blob/main/sui_programmability/examples/basics/sources/object_basics.move\r\n\r\n```rust\r\n/// Test CTURD object basics (create, transfer, update, read, delete)\r\nmodule basics::object_basics {\r\n    use sui::event;\r\n    use sui::object::{Self, UID};\r\n    use sui::tx_context::{Self, TxContext};\r\n    use sui::transfer;\r\n\r\n    struct Object has key, store {\r\n        id: UID,\r\n        value: u64,\r\n    }\r\n\r\n    struct Wrapper has key {\r\n        id: UID,\r\n        o: Object\r\n    }\r\n\r\n    struct NewValueEvent has copy, drop {\r\n        new_value: u64\r\n    }\r\n\r\n    public entry fun create(value: u64, recipient: address, ctx: &mut TxContext) {\r\n        transfer::public_transfer(\r\n            Object { id: object::new(ctx), value },\r\n            recipient\r\n        )\r\n    }\r\n\r\n    public entry fun transfer(o: Object, recipient: address) {\r\n        transfer::public_transfer(o, recipient)\r\n    }\r\n\r\n    public entry fun freeze_object(o: Object) {\r\n        transfer::public_freeze_object(o)\r\n    }\r\n\r\n    #[no_lint]\r\n    public entry fun set_value(o: &mut Object, value: u64) {\r\n        o.value = value;\r\n    }\r\n\r\n    // test that reading o2 and updating o1 works\r\n    public entry fun update(o1: &mut Object, o2: &Object) {\r\n        o1.value = o2.value;\r\n        // emit an event so the world can see the new value\r\n        event::emit(NewValueEvent { new_value: o2.value })\r\n    }\r\n\r\n    public entry fun delete(o: Object) {\r\n        let Object { id, value: _ } = o;\r\n        object::delete(id);\r\n    }\r\n\r\n    public entry fun wrap(o: Object, ctx: &mut TxContext) {\r\n        transfer::transfer(Wrapper { id: object::new(ctx), o }, tx_context::sender(ctx))\r\n    }\r\n\r\n    public entry fun unwrap(w: Wrapper, ctx: &TxContext) {\r\n        let Wrapper { id, o } = w;\r\n        object::delete(id);\r\n        transfer::public_transfer(o, tx_context::sender(ctx))\r\n    }\r\n}\r\n```\r\n\r\n### 3.2 知识点\r\n\r\n#### （1）对象包装\r\n\r\n- 对象包装指的是将一个对象嵌套在另一个对象内，例如上面的示例中就是将`Object`对象嵌套进入`Wrapper`对象\r\n- 通常`Wrapper`对象具有`key`能力，被包装的`Object`对象必须具有`store`的能力\r\n- 封装起来的`Object`对象就不再能单独根据`object ID`来获取，它会变成`Wrapper`对象的一部分\r\n- 被包装的`Object`对象不能被作为参数进行传递，而只能通过`Wrapper`对象进行获取\r\n- 可以被用作为限制一个对象在特定的合约调用之外不能被获取的方法\r\n\r\n示例中，将`Object`对象包装进入`Wrapper`对象后，转移给交易发起者：\r\n\r\n```rust\r\n    public entry fun wrap(o: Object, ctx: &mut TxContext) {\r\n        transfer::transfer(Wrapper { id: object::new(ctx), o }, tx_context::sender(ctx))\r\n    }\r\n```\r\n\r\n解包后，将`Wrapper`对象删除后，将被包装的`Object`对象转移给交易发起者：\r\n\r\n```rust\r\n    public entry fun unwrap(w: Wrapper, ctx: &TxContext) {\r\n        let Wrapper { id, o } = w;\r\n        object::delete(id);\r\n        transfer::public_transfer(o, tx_context::sender(ctx))\r\n    }\r\n```\r\n\r\n#### （2）transfer\r\n\r\n- 同时拥有 `key` 和 `store` 的对象才可以被随意转移\r\n- 只拥有 `key` 能力的对象的所有权是不可以被随意转移的， 想要使只有 `key` 限制符对象可以变更所有权，必须创建一个自定义的转移函数\r\n\r\n#### （3）冻结对象\r\n\r\n- 在示例中，通过`transfer::public_freeze_object(o)`可以冻结`Object`对象\r\n- 冻结后，将使得`Object`对象变得不可变，将不可再被转移和改变\r\n\r\n## 合约4：managed fungible_tokens\r\n\r\n### 4.1 合约代码\r\n\r\nhttps://github.com/MystenLabs/sui/blob/mainnet-v1.15.1/sui_programmability/examples/fungible_tokens/sources/managed.move\r\n\r\n```rust\r\nmodule fungible_tokens::managed {\r\n    use std::option;\r\n    use sui::coin::{Self, Coin, TreasuryCap};\r\n    use sui::transfer;\r\n    use sui::tx_context::{Self, TxContext};\r\n\r\n    struct MANAGED has drop {}\r\n\r\n    #[allow(unused_function)]\r\n    fun init(witness: MANAGED, ctx: &mut TxContext) {\r\n        let (treasury_cap, metadata) = coin::create_currency<MANAGED>(witness, 2, b\"MANAGED\", b\"\", b\"\", option::none(), ctx);\r\n        transfer::public_freeze_object(metadata);\r\n        transfer::public_transfer(treasury_cap, tx_context::sender(ctx))\r\n    }\r\n\r\n    public entry fun mint(\r\n        treasury_cap: &mut TreasuryCap<MANAGED>, amount: u64, recipient: address, ctx: &mut TxContext\r\n    ) {\r\n        coin::mint_and_transfer(treasury_cap, amount, recipient, ctx)\r\n    }\r\n\r\n    public entry fun burn(treasury_cap: &mut TreasuryCap<MANAGED>, coin: Coin<MANAGED>) {\r\n        coin::burn(treasury_cap, coin);\r\n    }\r\n}\r\n```\r\n\r\n### 4.2 知识点\r\n\r\n#### （1）Capability\r\n\r\n- `Capability`是一种常用的调整获取权限的设计模式\r\n- 在示例中，`mint`和`burn`方法第一个参数是`treasury_cap`，表示只有拥有`TreasuryCap<MANAGED>`的人可以调用\r\n- 如果不需要使用，参数可以传入`_`，表示立即进行消耗\r\n\r\n#### （2）见证者模式\r\n\r\n- 用于证明有关的一个资源或类型`A`，在短暂的`witness`资源被消耗后只能启动一次\r\n- `witness`资源在使用后必须立即被消耗或丢弃，确保它不能被重复使用以创建`A`的多个实例\r\n\r\n#### （3）一次性见证\r\n\r\n- 实例中的代码是一次性见证（`One Time Witness（OTW）`），它是Witness模式的一个子模式：利用模块`init`函数来确保只创建一个`witness`资源的实例。\r\n- `TreasuryCap`是一种资产，通过一次性见证这种模式保证它是一个单体对象\r\n\r\n- 如果一个类型的定义具有以下属性，那么它就被认为是一个OTW。\r\n  - **该类型是以模块的名字命名的，但大写字母。**\r\n  - **该类型只具有`drop`的能力**\r\n\r\n## 参考资料\r\n\r\nhttps://examples.sui-book.com/\r\n\r\nhttps://move-language.github.io/\r\n\r\nhttps://intro-zh.sui-book.com/"},"author":{"user":"https://learnblockchain.cn/people/932","address":null},"history":null,"timestamp":1703684103,"version":1}