---
title: "Create and Execute PTBs on Sui with the Sui CLI"
description: "The new CLI command streamlines programming on Sui for those who prefer the terminal and Bash scripts."
author: "Mysten Labs"
datePublished: 2024-03-29
dateModified: 2026-07-16
category: "Developer Resources"
readTime: "9 min"
canonical: https://www.sui.io/blog/write-programmable-transaction-blocks-command-line-interface
---

# Create and Execute PTBs on Sui with the Sui CLI

A new command in Sui's [command line interface](https://docs.sui.io/references/cli) (CLI) lets users create and execute [Programmable Transaction Blocks](https://docs.sui.io/guides/developer/sui-101/building-ptb) (PTBs) directly from the terminal or a Bash script. This new command gives developers greater flexibility in implementing and executing PTBs.

PTBs give developers a very powerful programming tool not available on other blockchains. The ability to chain multiple transactions together in a single executable may be taken for granted in the wider world of software engineering, but on blockchains it represents a fundamental leap forward.  

Until now, it was only possible to work with PTBs using one of the available SDKs. Now, developers who prefer working through the terminal and writing scripts have new means of using this powerful feature.

Check out the [Sui CLI PTB command documentation](https://docs.sui.io/references/cli/ptb) for details and examples. 

The example below shows the commands necessary to create a simple PTB. It creates three new coins with values `1000`, `2000` , and `3000` MIST from the gas coin, and transfers them to the address:  0x02a212de6a9dfa3a69e22387acfbafbb1a9e591bd9d636e7895dcfc8de05f331 (note the @ in front of the address to indicate this is an address and not a hexadecimal). 

`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`

Here is the result message:

`╭──────────────────────────────────────────────────────╮   │ 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** 

The main philosophy behind CLI PTB support is to enable developers to build and execute PTBs from the command line. You can also write Bash scripts to construct and execute PTBs just as you would do from the command line, providing great flexibility when it comes to automating different tasks. 

The `sui client ptb` command accepts the following options:

`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')`

# **Concepts and features** 

Besides using existing [traditional PTB](https://docs.sui.io/concepts/transactions/prog-txn-blocks/)\-related concepts, there are a few new concepts needed for this command.  

-   Reuse results or custom defined variables using the `--assign` command. 
-   Preview the input commands in the PTB instead of executing the PTB. 
-   Warn on declaring a variable with the same identifier multiple times. 
-   Benefit from name resolution for common packages such as `sui`, `std`, and `deepbook` (instead of using their addresses).
-   Error messages provide a concise and precise explanation of what is wrong in the PTB syntax.
-   Variable names cannot be `address`, `bool`, `vector`, `some`, `none`, `gas`, `u8`, `u16`, `u32`, `u64`, `u128`, or `u256`.

# **Examples** 

Let’s dig through some examples to showcase the Sui CLI PTB tool's power. Note that these examples were tested using Bash, and your mileage might vary depending on your shell and how it treats and parses strings and quotes (e.g., you might need to use quotation marks around arrays in zsh or fish, but not in Bash).

## **Nested or multi-types** 

When you need to work with nested types, you can simply wrap the types you need in `<` and `>`. For example, if an argument expects an `Option<vector<u256>>` you can write `std::option::Option<vector<u256>>`.

Here is an example:

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

If you need to pass multiple types for a call, you can do so by using a comma between the types, as in `<u64,u8,bool,string>`.

## **Name resolution** 

CLI PTB uses name resolution for common packages like `sui`, `std`, and `deepbook`, so you can use them directly instead of their addresses: `0x2`, `0x1`, or `0xdee9`. You can still use the traditional `package::module::function` way to call a specific function. For example, you can use both `std::option` or `0x1::option`. 

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

You can also refer to addresses in your local wallet by their alias instead of their full address. 

## **Assign data to variables**

The `--assign` argument binds values to variables. There are a few ways you can use it: 

1.  Assign a value to a variable: 

`sui client ptb \   --assign myvar 100 \   --gas-budget 5000000`

2.  Assign a variable to the result of the previous transaction. In the following example, split-coins results in three new objects, which will be accessible via the coins variable due to the usage of `--assign`. 

`sui client ptb \   --split-coins gas "[1000,2000,3000]" \   --assign coins \   --gas-budget 5000000`

3.  Assign a variable to a different variable

`sui client ptb \    --assign myvar vector[100, 200] \    --assign my_addr @0x0fe375fff0ee40d20c54a7f2478b9b5c7eaa3625b761 \    --assign old_address my_addr \    --gas-budget 5000000`

## **Publish package and call function** 

In the Sui repository, the [first package example](https://github.com/MystenLabs/sui/tree/main/examples/move/first_package) is a simple Move project. In a nutshell, this example publishes that package, which creates a `Forge` object that you can use to create swords with the `new_sword` function. The code below assumes that you have a Sui Testnet environment set up and sufficient [gas](https://docs.sui.io/guides/developer/getting-started/get-coins) for your address. 

First, access the example folder and publish the package. 

`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`

Publishing this package results in the following transaction data:

`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              │   ╰──────────────────────────────────────────────────────╯`

From the previous result, we can use the digest and pass it to `sui client tx-digest` to find the `packageId` and the `objectId` for the `Forge` object. We want to forge a new sword and transfer it to the sender of this transaction. 

Next, we look up the function `new_sword` from the package we published to understand what arguments we need to pass to the function. Its signature is:

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

Now that the package exists onchain, let’s use a PTB to create a sword. The first thing to do is to get the sender’s address with the `move-call` command and assign the result of that call to the `sender` variable. We can use `assign` for the `magic` and `strength` arguments, and assign the `Forge` object ID to the variable `forge_obj`.

Finally, we call the `new_sword` function through the `move-call` command, pass all the right function arguments, assign the result of the `move-call` to the `sword` variable, and transfer the new `sword` object to the sender. 

`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`

Because we've set the `--summary` flag, the execution result reads:

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

## **PTBs in a Bash script**

To facilitate automation and reusability, you can copy and paste the PTB commands from the terminal into a Bash script and run it. 

`#!/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`

The above code can be run as a normal Bash script.

## **Mint 100 NFTs and transfer them using PTBs in a Bash script**

This example uses a Bash script to mint 100 NFTs and transfer them to 100 addresses. The NFT is defined in [this smart contract](https://github.com/MystenLabs/sui/blob/main/sui_programmability/examples/nfts/sources/num.move) posted to GitHub. We publish the package, and get the `num_issuer_cap` and the `package` IDs. The project defines the function mint in the `module num`, which we declare.

The script then loads the addresses from a text file where each address is on a new line. It then builds a string of `move-call` commands, calling the `mint` function and transferring the object to an address, all in a for loop. Finally, the PTB is executed.

Note that, being a Bash script, you can pass in environment variables as you’d do with any other Bash script.

`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`

The end result is:

`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 │   ╰──────────────────────────────────────────────────────╯`

## **Purchase of a domain on SuiNS** 

The following example registers a Sui domain name by first creating a zero coin that has no SUI, to be used for move-call, and after that it transfers the newly created domain name object to a specific address. 

`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`

The result of this command is:

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

# **Preview a PTB instead of executing** 

The `--preview` flag, useful for complex PTBs, lists the transactions in the PTB instead of executing it. The preview displays a well-formatted table with each transaction and its arguments after parsing the entire input. It also displays any errors. This helps when dealing with complex PTBs, as it will produce the list of transactions instead of executing the 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`

And the result of this call would be the following: 

![](https://cdn.prod.website-files.com/693322d04141bcb0ce1ffb7e/6a4c32744922c234052ac974_CLI-PTB-05.png)

Click image to expand it.

# **Warnings and errors** 

When using the `--assign` command and the `--warn-shadows` flag, the tool issues a warning when it encounters shadowed variables. That is, if there are multiple declarations for a given variable name, the tool warns the developer that the same variable is declared multiple times. Here is an example:

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

The image below shows the warnings produced when building a PTB: 

![](https://cdn.prod.website-files.com/693322d04141bcb0ce1ffb7e/6a4c32744922c234052ac977_CLI-PTB-01.png)

Click image to expand it.

One area where we dedicated a lot of effort is on how to report useful errors. We aimed for errors to include sufficient information about what is wrong, pointing exactly to the argument that leads to that error, and in some cases even providing a hint on how to fix it.  

For example, if the user calls the `std::Option::is_none` function and passes the module name with an uppercase instead of lowercase, the tool will produce the following message: 

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

And the error message will show the following: 

![](https://cdn.prod.website-files.com/693322d04141bcb0ce1ffb7e/6a4c32744922c234052ac96c_CLI-PTB-02.png)

Click image to expand it.

 Here is another example which showcases the error produced when the user calls `--assign X` without passing a variable, after a command that does not produce any result: 

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

The error in this case would be the following:

![](https://cdn.prod.website-files.com/693322d04141bcb0ce1ffb7e/6a4c32744922c234052ac96f_CLI-PTB-03.png)

Click image to expand it.

 Here is a another example regarding reserved words in the tool: 

`sui client ptb --assign address 5 --gas-budget 5`

The error message here would show:

![](https://cdn.prod.website-files.com/693322d04141bcb0ce1ffb7e/6a4c32744922c234052ac968_CLI-PTB-04.png)

Click image to expand it.
