Sui Programmable Transaction Blocks with the TS SDK
This example starts by constructing a transaction block to send Sui. To construct transactions,
\import the TransactionBlock
class, and construct it:
import { TransactionBlock } from '@mysten/sui.js/transactions';
const txb = new TransactionBlock();
Using this, you can then add transactions to this transaction block.
// Create a new coin with balance 100, based on the coins used as gas payment.
// You can define any balance here.
const [coin] = txb.splitCoins(txb.gas, [txb.pure(100)]);
// Transfer the split coin to a specific address.
txb.transferObjects([coin], txb.pure('0xSomeSuiAddress'));
Note that you can attach multiple transactions of the same type to a transaction block 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 txb = new TransactionBlock();
// First, split the gas coin into multiple coins:
const coins = txb.splitCoins(
txb.gas,
transfers.map((transfer) => txb.pure(transfer.amount)),
);
// Next, create a transfer transaction for each coin:
transfers.forEach((transfer, index) => {
txb.transferObjects([coins[index]], txb.pure(transfer.to));
});
After you have the transaction block defined, you can directly execute it with a signer using
signAndExecuteTransactionBlock
.
signer.signAndExecuteTransactionBlock({ transactionBlock: txb });
Inputs and transactions
Programmable Transaction blocks have two key concepts: inputs and transactions.
Inputs are values that are used as arguments to the transactions in the transaction block. Inputs can either be an object reference (either to an owned object, an immutable object, or a shared object), or a pure BCS value (for example, an encoded string used as an argument to a move call).
Transactions are steps of execution in the transaction block. You can also use the result of previous transaction as an argument to future transactions. By combining multiple transactions together, Programmable Transaction blocks provide a flexible way to create complex transactions.
Constructing inputs
Inputs are how you provide external values to transaction blocks. 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
txb.object(objectId)
function is used to construct an input that contains an object reference. - For pure values: the
txb.pure(rawValue)
function is used, and returns an input reference that you use in transactions.
Available transactions
Sui supports following transactions:
txb.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:
txb.splitCoins(txb.gas, [txb.pure(100), txb.pure(200)])
- Example:
txb.mergeCoins(destinationCoin, sourceCoins)
- Merges the sourceCoins into the destinationCoin.- Example:
txb.mergeCoins(txb.object(coin1), [txb.object(coin2), txb.object(coin3)])
- Example:
txb.transferObjects(objects, address)
- Transfers a list of objects to the specified address.- Example:
txb.transferObjects([txb.object(thing1), txb.object(thing2)], txb.pure(myAddress))
- Example:
txb.moveCall({ target, arguments, typeArguments })
- Executes a Move call. Returns whatever the Sui Move call returns.- Example:
txb.moveCall({ target: '0x2::devnet_nft::mint', arguments: [txb.pure(name), txb.pure(description), txb.pure(image)] })
- Example:
txb.makeMoveVec({ type, objects })
- Constructs a vector of objects that can be passed into amoveCall
. This is required as there’s no way to define a vector as an input.- Example:
txb.makeMoveVec({ objects: [txb.object(id1), txb.object(id2)] })
- Example:
txb.publish(modules, dependencies)
- Publishes a Move package. Returns the upgrade capability object.
Passing transaction results as arguments
You can use the result of a transaction as an argument in a subsequent transactions. Each transaction method on the transaction builder returns a reference to the transaction result.
// Split a coin object off of the gas object:
const [coin] = txb.splitCoins(txb.gas, [txb.pure(100)]);
// Transfer the resulting coin object:
txb.transferObjects([coin], txb.pure(address));
When a transaction returns multiple results, you can access the result at a specific index either using destructuring, or array indexes.
// Destructuring (preferred, as it gives you logical local names):
const [nft1, nft2] = txb.moveCall({ target: '0x2::nft::mint_many' });
txb.transferObjects([nft1, nft2], txb.pure(address));
// Array indexes:
const mintMany = txb.moveCall({ target: '0x2::nft::mint_many' });
txb.transferObjects([mintMany[0], mintMany[1]], txb.pure(address));
Get transaction block bytes
If you need the transaction block bytes, instead of signing or executing the transaction block, you
can use the build
method on the transaction builder itself.
Important: You might need to explicitly call setSender()
on the transaction block to ensure
that the sender
field is populated. This is normally done by the signer before signing the
transaction, but will not be done automatically if you’re building the transaction block bytes
yourself.
const txb = new TransactionBlock();
// ... add some transactions...
await txb.build({ client });
In most cases, building requires your SuiClient to fully resolve input values.
If you have transaction block bytes, you can also convert them back into a TransactionBlock
class:
const bytes = getTransactionBlockBytesFromSomewhere();
const txb = TransactionBlock.from(bytes);