{"content":{"title":"Sui Testnet 简单发布a + b","body":"# 一：Testnet\r\n\r\n$\\mathit {Sui}$ 拥有主网、开发网和测试网网络，其中，测试网网络又可以分为 $\\mathit {Devnet}$ 和 $\\mathit {Testnet}$，当然，你也可以构建本地 $\\mathit {Sui}$ 网络进行纯本地开发，但是本地的开发环境不是持久的，每一次启动，本地网络都会作为一个全新的网络启用。<br>这里我们选择连接到 $\\mathit {Testnet}$ 尝试发布我们的第一个程序，当然，你也可以选择 $\\mathit {Devnet}$ 或者本地网络再或者其它，原理都是一样的，只是代码细节需要根据实际选择进行调整。\r\n\r\n如果你还没有安装 $\\mathit {Sui}$ 的话，可以根据之前的文章布置环境，现在，在命令行输入 `sui client`，如果此前没有运行过相关命令，那么系统将为您配置客户端信息，显示为：<br>`Config file [\"<PATH-TO-FILE>/client.yaml\"] doesn't exist, do you want to connect to a Sui Full node server [y/N]?`<br>按 $\\mathit y$ 并回车后，终端会提示：<br>`Sui Full node server URL (Defaults to Sui Devnet if not specified) :`<br>这里将默认连接到 $\\mathit {Devnet}$，如果要在初次连接就一步到位选择我们即将使用的 $\\mathit {Testnet}$ 的话，需要在这里键入对应的地址。不过，由于后续也可以更换连接的网络，并且之后的那种方法更加通用~~（这也是为什么要选 Testnet，方便展现那种方法）~~，所以我们这里不如直接回车，得到如下提示：<br>`Select key scheme to generate keypair (0 for ed25519, 1 for secp256k1, 2 for secp256r1):`<br>这里有 $\\text 0,\\ \\text 1,\\ \\text 2$ 三种加密方案可供选择，选哪个都行，键入任何一个供选择的数字后回车，根据具体情况，你将得到类似的，包含地址和 $\\text {12}$ 个恢复时使用的单词信息：<br>`Generated new keypair for address with scheme \"ed25519\" [0xb9c83a8b40d3263c9ba40d551514fbac1f8c12e98a4005a0dac072d3549c2442]\r\nSecret Recovery Phrase : [cap wheat many line human lazy few solid bored proud speed grocery]`<br>这条信息最好能记录一下，说不定往后哪天就用到了呢。\r\n\r\n此时，可以通过`sui client envs`来查看当前的网络环境，不难发现只有一个 $\\mathit {devnet}$ 的网络正在活跃，换句话说，也就是我们当前的网络环境是 $\\mathit {Devnet}$。<br>如果想要切换到 $\\mathit {Testnet}$，就需要使用添加新网络环境的命令：<br>`sui client new-env --alias <ALIAS> --rpc <RPC-SERVER-URL>`<br>其中，尖括号里的内容是需要替换的，$\\mathit {ALIAS}$ 是自定义的别名，$\\mathit {PRC}$-$\\mathit {SERVER}$-$\\mathit{URL}$ 是想要连接到的新的网络的地址，已知 $\\mathit {Testnet}$ 的 $\\mathit {URL}$ 为 `https://fullnode.devnet.sui.io:443`，如果将其命名为`testnet`，就可以通过这条命令来添加：<br>`sui client new-env --alias testnet --rpc https://fullnode.devnet.sui.io:443`<br>此时再通过`sui client envs`来查看网络环境，就可以看到有两条，但是当前依旧处于 $\\mathit {devnet}$，用如下命令就可以将其切换到 $\\mathit {testnet}$：<br>`sui client switch --env testnet`<br>同理，如果你想要添加新的网络，或者切换到别的网络，就可以以此举一反三。\r\n\r\n# 二：重写 a + b\r\n\r\n这是我们上一篇写的 $\\mathit a + \\mathit b$：\r\n\r\n```move\r\nmodule my_first_package::my_module {\r\n    public fun add(a: u64, b: u64): u64 {\r\n        a + b\r\n    }\r\n}\r\n```\r\n\r\n发布到链上之后测试代码都无法使用了，似乎也没有什么好的输出手段让用户得到 $\\mathit a + \\mathit b$ 的结果，那么我们来考虑一个问题，**对象**和**拥有者**。<br>$\\mathit {move}$ 代码中，任何一个东西都是对象，如果丧失了拥有者，那么这个所谓的对象也将走向消亡，当然，这里的“消亡”有的时候必须手动进行，这就不是这篇文章需要讨论的内容了。<br>回到上述问题，如果我们将 $\\mathit a + \\mathit b$ 的结果对象，拥有权从当前函数转交到程序的拥有者（在这里，也就是我们）的手上，这样一来，就增加了该结果对象的寿命，我们再通过对象的地址去询问答案，是不是就完美解决了问题？\r\n\r\n稍加拓展，如果我们想要重新定义一个对象呢？\r\n\r\n在 $\\mathit {move}$ 语言当中，定义一个类型可以有四种修饰词：\r\n\r\n- **$\\mathit {Copy}$**：被修饰的值可以被复制。\r\n- **$\\mathit {Drop}$**：被修饰的值在作用域结束时可以被丢弃。\r\n- **$\\mathit {Key}$**：被修饰的值可以作为键值对全局状态进行访问。\r\n- **$\\mathit {Srote}$**：被修饰的值可以被存储到全局状态。\r\n\r\n基本类型和内建类型的修饰词都是预先定义并且不可更改的，比如整型、布尔类型同时拥有除了 $\\mathit {Key}$​ 之外的其它三项。\r\n\r\n显然，针对上述问题，我们需要对自定义的对象，添加 $\\mathit {Key}$ 修饰词，让它能够被唯一标识并全局访问。因此，我们可以将代码进行如下修改：\r\n\r\n```move\r\nmodule my_first_package::my_module {\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 Result has key {\r\n        id: UID,\r\n        res: u64,\r\n    }\r\n\r\n    public fun add(a: u64, b: u64, ctx: &mut TxContext) {\r\n        let result = Result {\r\n            id: object::new(ctx),\r\n            res: a + b,\r\n        };\r\n        transfer::transfer(result, tx_context::sender(ctx));\r\n    }\r\n}\r\n```\r\n\r\n导入相关模块后，定义了一个名为 $\\mathit {Result}$ 的结构体，它拥有唯一表示用的 $\\mathit {id}$ 以及用来存储 $\\mathit a + \\mathit b$ 结果的整型字段 $\\mathit {res}$。<br>在 $\\mathit {add}$ 函数当中，我们创建了一个 $\\mathit {Result}$ 存储在 $\\mathit {result}$ 当中，$\\mathit {id}$ 是通过自带的方式唯一生成的，$\\mathit {res}$ 自然就是通过传入的两个参数相加得到。<br>很容易注意到 $\\mathit {TxContext}$，这是一个特殊参数，不需要用户自己传递，这也是为什么一般作为函数最后一个参数而不写在前面的原因，它记录有有关交易的基本信息，本篇文章不适合深究，有兴趣的小伙伴可以去官方文档查看。<br>最后，有一个 $\\mathit {transfer}$ 操作，目的就是将 $\\mathit {result}$​ 的拥有权转交到“我们”手上。\r\n\r\n# 三：发布\r\n\r\n链上的交易都是需要开销的，即使是测试网络，那么，开销怎么来？既然是测试用网，肯定不需要自己掏腰包，只需要一行命令就可以搞定：`sui client faucet`，回车后会提示你到账有延迟，大概等个一分钟左右，通过`sui client gas`查看余额。<br>如果你申请了多次，就会到账多次，对于每一笔金额，可以拆分也可以合并，这不是本篇文章的重点也就不再赘述。\r\n\r\n现在，我们就可以用领到的测试用水去发布上链第一个程序：`sui client publish --gas-budget 5000000`，其中`--gas-budget`表示本次交易的最大开销，如果超过了这个金额，那么发布将会失败。<br>如果你得到了`error: InsufficientGas`，这就是金额设置的不够大，扩大金额重新发布即可。\r\n\r\n成功之后终端会返回交易的详细信息，包括交易数据、交易效果、交易块事件、对象更改和余额更改。<br>在 $\\mathit {Object\\ Changes}$ 表中，可以查看到以发布内容的详细信息，包括它的 $\\mathit {ID}$，这也是我们通过调用链上程序时所需要的信息。\r\n\r\n`sui client call --function <FUNCTION_NAME> --package <PACKAGE_ID> --module <MODULE_NAME> --args <...> --gas-budget 10000000`\r\n\r\n- $\\mathit {FUNCTION\\_NAME}$ 是调用的函数名。\r\n- $\\mathit {PACKAGE\\_ID}$ 是从 $\\mathit {Object\\ Changes}$ 表中得到的 $\\mathit {ID}$ 信息。\r\n- $\\mathit {MODULE\\_NAME}$  是调用的模块名。\r\n- $\\mathit {args}$ 后的尖括号内依次填传入函数的参数，之间用空格隔开。\r\n- 最后的数据，同样的，代表本次交易的最大开销。\r\n\r\n按照要求填入并执行命令，例如：`sui client call --function add --package 0xe2496799139225a06e7251857cdf46a32c20d773030c62b9bf24095cd60aac43 --module my_module --args 333 666 --gas-budget 10000000`\r\n\r\n同样的，终端会返回交易的详细信息，其中可以在 $\\mathit {Object\\ Changes}$ 表当中，看到这一笔交易过后我们有了一个自己所拥有的对象，这其中就存储有 $\\mathit a + \\mathit b$ 的结果，可以借助其 $\\mathit {ObjectID}$​ 进行查询。<br>通过 `sui client object <OBJECT_ID>`就可以看这个对象的详细信息，如下图所示：<br>![result.png](https://img.learnblockchain.cn/attachments/2024/03/KQdmgaJB65feb32c2823d.png)\r\n\r\n$\\text {333} + \\text {666} = \\text {999}$，$\\mathit {res}$ 当中存储的就是这个值，大成功(๑•̀ㅂ•́)و✧！\r\n\r\n# 四：加入组织，共同进步！\r\n\r\n- [Sui 中文开发群(TG)](https://t.me/move_cn)\r\n- $\\mathit{Move}$ 语言学习交流群: 79489587"},"author":{"user":"https://learnblockchain.cn/people/19165","address":null},"history":null,"timestamp":1711191406,"version":1}