TypeScript SDK: Transactions
Purpose
Guide building, signing, submitting, and simulating transactions with @aptos-labs/ts-sdk . Use the build → sign → submit → wait pattern; optionally simulate before submit.
ALWAYS
-
Call aptos.waitForTransaction({ transactionHash }) after submit – do not assume transaction is committed after signAndSubmitTransaction .
-
Use aptos.transaction.build.simple() for entry function payloads – pass sender and data: { function, functionArguments, typeArguments? } .
-
Simulate before submit for critical/high-value flows – use aptos.transaction.simulate.simple() and check success and vm_status .
-
Use the same Account instance for signer that you use for sender address when building (e.g. account.accountAddress as sender, account as signer).
NEVER
-
Do not skip waitForTransaction – submission returns when the tx is accepted, not when it is committed.
-
Do not use deprecated scriptComposer (removed in v6) – use separate transactions or batch patterns.
-
Do not use number for u64/u128/u256 in functionArguments – use bigint where required to avoid precision loss.
Standard flow (simple transaction)
import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";
const aptos = new Aptos(new AptosConfig({ network: Network.TESTNET })); const MODULE_ADDRESS = "0x...";
// 1. Build
const transaction = await aptos.transaction.build.simple({
sender: account.accountAddress,
data: {
function: ${MODULE_ADDRESS}::counter::increment,
functionArguments: []
}
});
// 2. Sign and submit const pendingTx = await aptos.signAndSubmitTransaction({ signer: account, transaction });
// 3. Wait for commitment const committedTx = await aptos.waitForTransaction({ transactionHash: pendingTx.hash });
if (!committedTx.success) {
throw new Error(Tx failed: ${committedTx.vm_status});
}
Build options
// With type arguments (e.g. coin type) const transaction = await aptos.transaction.build.simple({ sender: account.accountAddress, data: { function: "0x1::coin::transfer", typeArguments: ["0x1::aptos_coin::AptosCoin"], functionArguments: [recipientAddress, amount] } });
// Optional: max gas, expiry, etc. (see SDK types for full options) const transactionWithOptions = await aptos.transaction.build.simple({ sender: account.accountAddress, data: { function: "...", functionArguments: [] }, options: { maxGasAmount: 2000n, gasUnitPrice: 100n, expireTimestamp: BigInt(Math.floor(Date.now() / 1000) + 600) } });
Simulation (before submit)
const transaction = await aptos.transaction.build.simple({
sender: account.accountAddress,
data: {
function: ${MODULE_ADDRESS}::counter::increment,
functionArguments: []
}
});
const [simResult] = await aptos.transaction.simulate.simple({ signerPublicKey: account.publicKey, transaction });
if (!simResult.success) {
throw new Error(Simulation failed: ${simResult.vm_status});
}
console.log("Gas used:", simResult.gas_used);
Sponsored transactions (fee payer)
// 1. Build with fee payer
const transaction = await aptos.transaction.build.simple({
sender: sender.accountAddress,
withFeePayer: true,
data: {
function: ${MODULE_ADDRESS}::counter::increment,
functionArguments: []
}
});
// 2. Sender signs const senderAuth = aptos.transaction.sign({ signer: sender, transaction });
// 3. Fee payer signs (different method) const feePayerAuth = aptos.transaction.signAsFeePayer({ signer: feePayer, transaction });
// 4. Submit with both authenticators const pendingTx = await aptos.transaction.submit.simple({ transaction, senderAuthenticator: senderAuth, feePayerAuthenticator: feePayerAuth });
await aptos.waitForTransaction({ transactionHash: pendingTx.hash });
Multi-agent transactions
const transaction = await aptos.transaction.build.multiAgent({
sender: alice.accountAddress,
secondarySignerAddresses: [bob.accountAddress],
data: {
function: ${MODULE_ADDRESS}::escrow::exchange,
functionArguments: [itemAddress, amount]
}
});
const aliceAuth = aptos.transaction.sign({ signer: alice, transaction }); const bobAuth = aptos.transaction.sign({ signer: bob, transaction });
const pendingTx = await aptos.transaction.submit.multiAgent({ transaction, senderAuthenticator: aliceAuth, additionalSignersAuthenticators: [bobAuth] });
await aptos.waitForTransaction({ transactionHash: pendingTx.hash });
waitForTransaction options
const committed = await aptos.waitForTransaction({ transactionHash: pendingTx.hash, options: { timeoutSecs: 60, checkSuccess: true // throw if tx failed } });
Gas profiling
const gasProfile = await aptos.gasProfile({
sender: account.accountAddress,
data: {
function: ${MODULE_ADDRESS}::module::function_name,
functionArguments: []
}
});
console.log("Gas profile:", gasProfile);
Common mistakes
Mistake Correct approach
Not calling waitForTransaction Always wait and check committedTx.success
Using number for large amounts Use bigint for u64/u128/u256 in functionArguments
Wrong signer for submit Use the Account whose address is the sender (or fee payer / additional signer as appropriate)
Assuming scriptComposer exists Use separate transactions or batch; scriptComposer removed in v6
References
-
SDK: src/api/transaction.ts , src/internal/transactionSubmission.ts , src/internal/transaction.ts
-
Pattern: TYPESCRIPT_SDK.md
-
Related: ts-sdk-account, ts-sdk-client, ts-sdk-wallet-adapter, use-ts-sdk