Invoke chains of Move calls with Dynamic Script Composer
Overview
Section titled “Overview”In the basic API, you can only specify one entry function call for a single transaction. Advanced builders might want to invoke multiple public Move functions in one transaction. Script Composer enables this: you describe a sequence of calls with addBatchedCalls, pass return values from one call into the next, and the SDK compiles them into a single script transaction.
Installation
Section titled “Installation”Install the standalone package together with its @aptos-labs/ts-sdk and WASM (@aptos-labs/script-composer-pack) peer dependencies:
npm i @aptos-labs/script-composer-sdk @aptos-labs/script-composer-pack @aptos-labs/ts-sdkyarn add @aptos-labs/script-composer-sdk @aptos-labs/script-composer-pack @aptos-labs/ts-sdkpnpm add @aptos-labs/script-composer-sdk @aptos-labs/script-composer-pack @aptos-labs/ts-sdkBrowser applications also need the buffer package and a global Buffer polyfill before importing the SDK:
import { Buffer } from "buffer";
(window as typeof window & { Buffer: typeof Buffer }).Buffer = Buffer;Basic Usage
Section titled “Basic Usage”Import BuildScriptComposerTransaction and CallArgument from @aptos-labs/script-composer-sdk — not from @aptos-labs/ts-sdk:
import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";import { BuildScriptComposerTransaction, CallArgument,} from "@aptos-labs/script-composer-sdk";
const aptosConfig = new AptosConfig({ network: Network.TESTNET });// Replace with the sender's account address.const sender = "0x1";
const tx = await BuildScriptComposerTransaction({ sender, // Building a composed transaction reads on-chain state (module ABIs and // bytecode), so it needs an AptosConfig for the target network. aptosConfig, builder: async (composer) => { // Withdraw some Coin. const coin = await composer.addBatchedCalls({ function: "0x1::coin::withdraw", functionArguments: [CallArgument.newSigner(0), 1], typeArguments: ["0x1::aptos_coin::AptosCoin"], });
// Convert the coin into a fungible asset. coin[0] is the first return // value of the first call added above. const fungibleAsset = await composer.addBatchedCalls({ function: "0x1::coin::coin_to_fungible_asset", functionArguments: [coin[0]], typeArguments: ["0x1::aptos_coin::AptosCoin"], });
// Deposit the fungible asset returned by the second call. await composer.addBatchedCalls({ function: "0x1::primary_fungible_store::deposit", functionArguments: [sender, fungibleAsset[0]], typeArguments: [], }); return composer; },});Please note that the current example is only for reference on how to use Script Composer to combine transactions, receive return values from public functions, and pass them to the next function.
For transactions that need secondary signers and/or a fee payer, use BuildScriptComposerMultiAgentTransaction from the same package instead. For a sponsored single-signer transaction, pass withFeePayer: true to BuildScriptComposerTransaction.
Transaction Processing
Section titled “Transaction Processing”After combining the transaction, we can use interfaces like sign transaction / simulate transaction / submit transaction from @aptos-labs/ts-sdk:
We’ll use the simulate transaction interface to show how to use it:
const aptos = new Aptos(aptosConfig);
const [simulateResult] = await aptos.transaction.simulate.simple({ transaction: tx,});
console.log("simulate_result: ", simulateResult);Examples
Section titled “Examples”If you need some practical examples, we have also prepared usage examples for common environments:
In the examples, you will see a combined transaction and the return value of the simulated transaction (the simulated transaction uses the 0x1 address as the sender. Although it can be simulated successfully by default, if you want to actually use this feature to initiate a simulated transaction, please replace it with your account address and set up the corresponding network)
- nodejs: https://github.com/aptos-labs/script-composer-sdk/tree/main/examples/nodejs
- multi-agent nodejs: https://github.com/aptos-labs/script-composer-sdk/tree/main/examples/multi-agent-nodejs
- nextjs: https://github.com/aptos-labs/script-composer-sdk/tree/main/examples/nextjs-project
- react: https://github.com/aptos-labs/script-composer-sdk/tree/main/examples/react-project
Technical Principles
Section titled “Technical Principles”Under the hood, the SDK will invoke a WASM binary (provided by @aptos-labs/script-composer-pack) to compile the series of Move calls into a CompiledScript. This will guarantee that the type and ability safety of Move is still being honored during the construction process. For the SDK users, this means:
- Ability safety: a. If the returned value does not have the Drop ability, the returned value needs to be consumed by subsequent calls. b. If the returned value does not have the Copy ability, the returned value can only be passed to subsequent calls once.
- The caller will need to make sure they pass the right values as arguments to subsequent calls. In the previous example, the
0x1::coin::coin_to_fungible_assetfunction will expect an argument ofCoin<AptosCoin>.
This implements AIP-102