Building Programmable Transaction Blocks
This guide explores creating a programmable transaction block (PTB) on Sui using the TypeScript SDK. For an overview of what a PTB is, see Programmable Transaction Blocks in the Concepts section. If you don't already have the Sui TypeScript SDK, follow the install instructions on the Sui TypeScript SDK site.
This example starts by constructing a PTB to send Sui. If you are familiar with the legacy Sui transaction types, this is similar to a paySui
transaction. To construct transactions, import the Transaction
class, and construct it:
import { Transaction } from '@mysten/sui/transactions';
const tx = new Transaction();
Using this, you can then add transactions to this PTB.
// Create a new coin with balance 100, based on the coins used as gas payment.
// You can define any balance here.
const [coin] = tx.splitCoins(tx.gas, [tx.pure(100)]);
// Transfer the split coin to a specific address.
tx.transferObjects([coin], tx.pure('0xSomeSuiAddress'));
You can attach multiple transaction commands of the same type to a PTB as well. For example, to get a list of transfers, and iterate over them to transfer coins to each of them:
interface Transfer {
to: string;
amount: number;
}
// Procure a list of some Sui transfers to make:
const transfers: Transfer[] = getTransfers();
const tx = new Transaction();
// First, split the gas coin into multiple coins:
const coins = tx.splitCoins(
tx.gas,
transfers.map((transfer) => tx.pure(transfer.amount)),
);
// Next, create a transfer transaction for each coin:
transfers.forEach((transfer, index) => {
tx.transferObjects([coins[index]], tx.pure(transfer.to));
});
After you have the Transaction defined, you can directly execute it with a SuiClient
and KeyPair
using client.signAndExecuteTransaction
.
client.signAndExecuteTransaction({ signer: keypair, transaction: tx });
Constructing inputs
Inputs are how you provide external values to PTBs. For example, defining an amount of Sui to transfer, or which object to pass into a Move call, or a shared object.
There are currently two ways to define inputs:
- For objects: the
tx.object(objectId)
function is used to construct an input that contains an object reference. - For pure values: the
tx.pure(value, type?)
function is used to construct an input for a non-object input.- If value is a
Uint8Array
, then the value is assumed to be raw bytes and is used directly. - If type is provided, it's used to generate the BCS serialization layout for the value. If not provided, the type is automatically determined based on the value.
- If value is a
Available transactions
Sui supports following transaction commands:
tx.splitCoins(coin, amounts)
: Creates new coins with the defined amounts, split from the provided coin. Returns the coins so that it can be used in subsequent transactions.- Example:
tx.splitCoins(tx.gas, [tx.pure(100), tx.pure(200)])
- Example:
tx.mergeCoins(destinationCoin, sourceCoins)
: Merges the sourceCoins into the destinationCoin.- Example:
tx.mergeCoins(tx.object(coin1), [tx.object(coin2), tx.object(coin3)])
- Example:
tx.transferObjects(objects, address)
: Transfers a list of objects to the specified address.- Example:
tx.transferObjects([tx.object(thing1), tx.object(thing2)], tx.pure(myAddress))
- Example:
tx.moveCall({ target, arguments, typeArguments })
: Executes a Move call. Returns whatever the Sui Move call returns.- Example:
tx.moveCall({ target: '0x2::devnet_nft::mint', arguments: [tx.pure(name), tx.pure(description), tx.pure(image)] })
- Example:
tx.makeMoveVec({ type, elements })
: Constructs a vector of objects that can be passed into a moveCall. This is required as there's no other way to define a vector as an input.- Example:
tx.makeMoveVec({ elements: [tx.object(id1), tx.object(id2)] })
- Example:
tx.publish(modules, dependencies)
: Publishes a Move package. Returns the upgrade capability object.