使用Sui CLI在Sui上创建和执行PTBs

新的CLI命令帮助那些更喜欢终端和Bash脚本的用户,简化在Sui上的编程。

使用Sui CLI在Sui上创建和执行PTBs

Sui命令行界面(CLI)中的新命令允许用户直接从终端或Bash脚本创建和执行可编程交易区块(PTB)。这个新命令为开发人员在实现和执行PTB方面提供了更大的灵活性。

PTB为开发人员提供了一种非常强大的编程工具,这在其他区块链上不可用。将多个交易链接在一起形成单个可执行文件的能力在更广泛的软件工程世界中可能被视为理所当然,但在区块链上却代表了一个根本性的飞跃。  

以往,只能使用可用的SDK之一来处理PTB。现在,偏爱通过终端和编写脚本工作的开发人员有了使用这一强大功能的新途径。

查看Sui CLI PTB命令文档以获取详细信息和示例。 

下面的示例显示了创建简单PTB所需的命令。它从gas coin中创建了三个新的coin,其价值分别为 1000, 20003000 并将它们转移到地址: 0x02a212de6a9dfa369e22387acfbafbb1a9e591bd9d636e7895dcfc8de05f331(注意地址前面的@符号,表示这是一个地址,而不是十六进制)。 

sui client ptb \
--assign to_address @0x02a212de6a9dfa3a69e22387acfbafbb1a9e591bd \
--split-coins gas [1000,2000,3000] \
--assign coins \
--transfer-objects to_address [coins.0, coins.1, coins.2] \
--gas-budget 5000000 \
--summary

以下是结果信息:

╭──────────────────────────────────────────────────────╮ 
│ PTB Execution Summary                                │ 
├──────────────────────────────────────────────────────┤ 
│ Digest: 7S7XpkGns8mQ4mBUzjdM32xqNHGF2P1ErHSLbb4Chn2j │ 
│ Status: success                                      │
│ Gas Cost Summary:                                    │ 
│ Storage Cost: 3952000                                │ 
│ Computation Cost: 1000000                            │ 
│ Storage Rebate: 978120                               │ 
│ Non-refundable Storage Fee: 9880                     │ 
╰──────────────────────────────────────────────────────╯

Sui CLI PTB 

CLI PTB支持的主要理念是让开发人员能够从命令行构建和执行PTB。您也可以编写Bash脚本来构建和执行PTB,就像您从命令行中所做的那样,这在自动化不同任务时提供了很大的灵活性。 

  sui client ptb 命令接受以下选项:

sui client ptb -h
Build, preview, and execute programmable transaction blocks. Depending on your shell, you might have to use quotes around arrays or other passed values. Use --help to see examples for how to use the core functionality of this command.

Usage: sui client ptb [OPTIONS]

Options:
      --assign <NAME> <VALUE>                                         Assign a value to a variable name to use later in the PTB.
      --gas-coin <ID>                                                 The object ID of the gas coin to use. If not specified, it will try to use the first gas coin that it finds that has at least
                                                                      the requested gas-budget balance.
      --gas-budget <MIST>                                             The gas budget for the transaction, in MIST.
      --make-move-vec <TYPE> <[VALUES]>                               Given n-values of the same type, it constructs a vector. For non objects or an empty vector, the type tag must be specified.
      --merge-coins <INTO_COIN> <[COIN OBJECTS]>                      Merge N coins into the provided coin.
      --move-call <PACKAGE::MODULE::FUNCTION> <TYPE> <FUNCTION_ARGS>  Make a move call to a function.
      --split-coins <COIN> <[AMOUNT]>                                 Split the coin into N coins as per the given array of amounts.
      --transfer-objects <[OBJECTS]> <TO>                             Transfer objects to the specified address.
      --publish <MOVE_PACKAGE_PATH>                                   Publish the Move package. It takes as input the folder where the package exists.
      --upgrade <MOVE_PACKAGE_PATH>                                   Upgrade the move package. It takes as input the folder where the package exists.
      --preview                                                       Preview the list of PTB transactions instead of executing them.
      --summary                                                       Show only a short summary (digest, execution status, gas cost). Do not use this flag when you need all the transaction data
                                                                      and the execution effects.
      --warn-shadows                                                  Enable shadow warning when the same variable name is declared multiple times. Off by default.
      --json                                                          Return command outputs in json format.
  -h, --help                                                          Print help (see more with '--help')

概念和特性 

除了使用现有的传统PTB相关概念外,还需要一些新的概念来支持这个命令。  

  • use --assign 命令重新使用结果或自定义定义的变量。 
  • 在执行PTB之前预览PTB中的输入命令。 
  • 在多次声明具有相同标识符的变量时发出警告。 
  • 受益于对常见包(如 sui, stddeepbook )的名称解析(而不是使用它们的地址)。
  • 错误消息提供了对PTB语法出错的简明而准确的解释。
  • 变量名不能是 address, bool, vector, some, none, gas, u8, u16, u32, u64, u128u256.

示例 

让我们通过一些示例来展示Sui CLI PTB工具的强大功能。请注意,这些示例是在Bash中进行测试的,您的情况可能会有所不同,这取决于您的shell以及它如何处理和解析字符串和引号(例如,在zsh或fish中,您可能需要在数组周围使用引号,但在Bash中则不需要)。

嵌套或多类型 

当您需要使用嵌套类型时,您可以简单地将所需的类型包装在 <>中。例如,如果参数期望一个 Option<vector<u256>> ,您可以写成 std::option::Option<vector<u256>>.

以下是一个例子:

sui client ptb 
--make-move-vec "<std::option::Option<vector<u256>>>" "[vector[]]" \
--gas-budget 5000000

如果您需要为调用传递多个类型,可以在类型之间使用逗号,例如 <u64,u8,bool,string>.

域名解析 

CLI PTB使用域名解析来识别常见包如 sui, stddeepbook,因此您可以直接使用它们,而不是它们的地址: 0x2, 0x10xdee9。您仍然可以使用传统的 package::module::function 方法来调用特定函数。例如,您可以同时使用 std::option0x1::option

sui client ptb \
--assign A none \
--move-call std::option::is_none "<u64>" A \
--gas-budget 5000000

您还可以通过其别名而不是完整地址来引用本地钱包中的地址。 

为变量分配数据

  --assign 参数将值绑定到变量。您可以以几种方式使用它: 

  1. 将值分配给变量 
sui client ptb \
--assign myvar 100 \
--gas-budget 5000000
  1. 将变量分配给上一个交易的结果。在以下示例中,split-coins会产生三个新对象,可以通过coins变量访问这些对象,由于使用了 --assign
sui client ptb \
--split-coins gas "[1000,2000,3000]" \
--assign coins \
--gas-budget 5000000
  1. 将变量分配给另一个变量
sui client ptb \  
--assign myvar vector[100, 200] \  
--assign my_addr @0x0fe375fff0ee40d20c54a7f2478b9b5c7eaa3625b761 \  
--assign old_address my_addr \  
--gas-budget 5000000

发布包并调用函数 

在Sui Repo中, 第一个包示例 是一个简单的Move项目。简而言之,该示例发布了该包,创建了一个 Forge 对象,您可以使用该对象调用 new_sword 函数创建swords。下面的代码假设您已经设置了Sui测试网环境,并且您的地址有足够的 gas 您的地址。 

首先,进入示例文件夹并发布该包。 

cd sui/examples/move/first_package
sui client ptb 
--move-call sui::tx_context::sender \
--assign "sender" \
--publish "." \
--assign upgrade_cap \
--transfer-objects [upgrade_cap] sender \
--gas-budget 50000000

发布此包会生成以下交易数据:

INCLUDING DEPENDENCY Sui
INCLUDING DEPENDENCY MoveStdlib
BUILDING first_package
Successfully verified dependencies on-chain against source.
╭──────────────────────────────────────────────────────╮
│ PTB Execution Summary                                │
├──────────────────────────────────────────────────────┤
│ Digest: 8ULSyjJXhX5CPiEge3a7T2kLSA3YA8V44BAGZPdknJ1h │
│ Status: success                                      │
│ Gas Cost Summary:                                    │
│   Storage Cost: 9978800                              │
│   Computation Cost: 1000000                          │
│   Storage Rebate: 978120                             │
│   Non-refundable Storage Fee: 9880                   │
╰──────────────────────────────────────────────────────╯

从上一个结果中,我们可以使用digest,并将其传递给 sui client tx-digest 来查找 packageIdobjectId 来处理 Forge 对象的)。我们想要锻造一个新sword并将其转移到此交易的发送者。 

接下来,我们查找我们发布的包中的 new_sword 函数,以了解我们需要传递给函数的参数。其签名是:

/// Constructor for creating swords 
 public fun new_sword( 
     forge: &mut Forge, 
     magic: u64, 
     strength: u64, 
     ctx: &mut TxContext, 
 ): Sword 

现在,由于包存在于链上,让我们使用PTB来创建一个sword。首先要做的是使用 move-call 命令获取发送者的地址,并将该调用的结果分配给 sender 变量。我们可以使用 assign 来处理 magicstrength 参数,并将 Forge 对象的ID分配给 forge_obj.

最后,我们调用 new_sword 函数(通过 move-call 命令实现),传递所有正确的函数参数,将 move-call 的结果分配给 sword 变量,并将新 sword 对象转移到发送者。 

sui client ptb \
--move-call sui::tx_context::sender \
--assign sender \
--assign magic 10u64 \
--assign strength 50u64 \
--assign forge_obj @0xd5d19fde64ea43876add02efb2a769372869f30118 \
--move-call  0xbe6edeefea02371d35fdde5a45033d8d8e2e00b3eb911b23fa::example::new_sword  forge_obj magic strength \
--assign sword \
--transfer-objects "[sword]" sender \
--gas-budget 5000000 \
--summary

因为我们设置了 --summary 标志,所以执行结果如下所示:

╭──────────────────────────────────────────────────────╮ 
│ PTB Execution Summary                                │ 
├──────────────────────────────────────────────────────┤ 
│ Digest: HYqWpN9sYBGLawj4BE9VnRM7qaezUFA8aLrGBbjf72L7 │ 
│ Status: success                                      │ 
│ Gas Cost Summary:                                    │ 
│    Storage Cost: 3739200                             │ 
│    Computation Cost: 1000000                         │ 
│    Storage Rebate: 2309868                           │ 
│    Non-refundable Storage Fee: 23332                 │ 
╰──────────────────────────────────────────────────────╯ 

在Bash脚本中使用PTB

为了方便自动化和重用,您可以将终端中的PTB命令复制粘贴到Bash脚本中并运行它。 

#!/usr/bin/bash 

sui client ptb \ 
--assign to_address @0x02a212de6a9dfa3a69e22387acfbafbb1a9e59
--split-coins gas "[1000,2000,3000]" \
--assign coins \
--transfer-objects "[coins.0, coins.1, coins.2]" to_address \
--gas-budget 5000000

上述代码可以作为普通的 Bash 脚本运行。

在Bash脚本中使用PTB发行100个NFT并转移它们

此示例使用Bash脚本来发行100个NFT并将它们转移到100个地址。NFT在此发布到GitHub的 该智能合约 中进行定义。我们发布了该包,并获取了 num_issuer_cappackage
ID。项目在 module num中定义了mint函数,我们在此声明。

然后脚本从一个文本文件中加载地址,每个地址都在新行上。然后它构建了一个 move-call 命令字符串,在for循环中调用 mint 函数并将对象转移到一个地址。最后,执行了PTB。

请注意,作为Bash脚本,您可以像处理其他Bash脚本一样传递环境变量。

num_issuer_cap=0x3e8a046807edd63b75014b9aff0f8dc30b76344a4dfdb1ae4655932e0514086b
package=0x610834b5fa960b297a5caf6af56bcc045d67d158889da5b511a20031307ce099
module=num
fun=mint

echo "Loading addresses from file"
readarray -t addresses < addresses.txt

cmd=""
for address in ${addresses[@]}; do
  cmd+="--move-call $package::$module::$fun @$num_issuer_cap --assign X --transfer-objects [X] @$address "
done

echo "Executing PTB"
sui client ptb \
 $cmd \
 --gas-budget 5000000000 \
 --summary

最终结果如下:

bash-5.2$ bash working-demo.sh
Loading addresses from file
Executing PTB
╭──────────────────────────────────────────────────────╮
│ PTB Execution Summary                                │
├──────────────────────────────────────────────────────┤
│ Digest: 7THBdpRHWSTiWExxSB7A3m9E7PDP9kQaD16N8xXevdcA │
│ Status: success                                      │
│ Gas Cost Summary:                                    │
│   Storage Cost: 132376800                            │
│   Computation Cost: 1000000                          │
│   Storage Rebate: 2392632                            │
│   Non-refundable Storage Fee: 24168                  │
╰──────────────────────────────────────────────────────╯

购买SuiNS上的域名 

以下示例通过首先创建一个没有SUI的零币来注册一个Sui域名,以供move-call使用,然后将新创建的域名对象转移到特定地址。 

sui client ptb \
--assign suins_object_id  @0x300369e8909b9a6464da265b9a5a9ab6fe2158a040e84e808628cde7a07ee5a3 \
--split-coins gas "[0]" \
--assign zero_coin \
--move-call 0x4255184a0143c0ce4394a3f16a6f5aa5d64507269e54e51ea396d569fe8f1ba5::register::register suins_object_id '"mysui.sui"' 1u8 zero_coin @0x6 \
--assign suins_name \
--transfer-objects "[suins_name]"  @0x0fe375fff0ee40d20c54a7f2478b9b5c7eaa3625b7611f9661ec5faefb4a6fea \
--gas-budget 50000000

该命令的结果如下:

╭──────────────────────────────────────────────────────╮
│ PTB Execution Summary                                │
├──────────────────────────────────────────────────────┤
│ Digest: 5D2W4beXGWzF4Cn69y6ihnTvoyBC99vb5zGhpxAhdq1z │
│ Status: success                                      │
│ Gas Cost Summary:                                    │
│   Storage Cost: 10039600                             │
│   Computation Cost: 1000000                          │
│   Storage Rebate: 5394708                            │
│   Non-refundable Storage Fee: 54492                  │ 
╰──────────────────────────────────────────────────────╯

预览而不是执行PTB 

  --preview 标志对于复杂的PTB很有用,它列出PTB中的交易而不是执行它。预览显示了在解析整个输入后的每个交易及其参数的格式良好的表格。它还显示了任何错误。这在处理复杂的PTB时很有帮助,因为它会生成交易列表而不是执行PTB。 

sui client ptb \
--assign to_address @0x02a212de6a9dfa3a69e22387acfbafbb1a9e59 \
--split-coins gas [1000,2000,3000] \
--assign coins \
--transfer-objects [coins.0, coins.1, coins.2] to_address \
--gas-budget 5000000 \
--summary \
--preview

这次调用的结果如下: 

点击图片展开。

警告和错误 

当使用 --assign 命令和 --warn-shadows 标志时,工具在遇到阴影变量时会发出警告。也就是说,如果给定变量名有多个声明,工具会警告开发人员同一变量被声明多次。以下是一个示例:

sui client ptb \ 
--assign a 10 \ 
--assign a 15 \ 
--gas-budget 5000000 \ 
--warn-shadows 

下图显示了构建PTB时产生的警告: 

点击图片展开。

我们在报告有用的错误方面投入了大量的工作。我们的目标是让错误信息包含足够的信息来说明问题所在,准确指出导致该错误的参数,并在某些情况下甚至提供如何修复的提示。  

例如,如果用户调用 std::Option::is_none 函数并将模块名称用大写而不是小写传递,工具将生成以下消息: 

sui client ptb \  
--move-call std::Option::is_none none \ 
--gas-budget 5000000

错误消息将显示如下: 

点击图片展开。

 这是另一个例子,展示了用户在命令不产生任何结果后调用 --assign X 而不传递变量时产生的错误: 

sui client ptb \ 
--assign X \ 
--gas-budget 5000000

在这种情况下,错误将如下所示:

点击图片展开。

 这里是另一个关于工具中保留字段的错误示例: 

sui 客户端 ptb --assign address 5 --gas-budget 5

在这种情况下,错误消息将显示为:

点击图片展开。