Building transactions
Learn how to build transactions programmatically for complete control over blockchain interactions.
What you'll learn
Build signed transactions for immediate broadcast
Create unsigned transactions for multi-signature workflows
Implement sponsored transactions to pay fees for users
Transaction types
Stacks supports five primary transaction types, each serving a specific purpose.
// STX Transfer - Send native tokensconst stxTransfer = await makeSTXTokenTransfer(options);// Contract Deployment - Deploy Clarity contractsconst deployment = await makeContractDeploy(options);// Contract Call - Execute contract functionsconst contractCall = await makeContractCall(options);// Each transaction type accepts similar base options:interface TransactionOptions {senderKey: string; // Private key for signingnetwork: string; // 'mainnet' or 'testnet'fee?: bigint; // Manual fee in microSTXnonce?: bigint; // Manual nonceanchorMode?: AnchorMode; // Block anchoring strategy}
Building signed transactions
Signed transactions are ready to broadcast immediately. The private key signs during creation.
STX token transfer
import { makeSTXTokenTransfer, broadcastTransaction } from '@stacks/transactions';const transaction = await makeSTXTokenTransfer({recipient: 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG',amount: 1000000n, // 1 STX = 1,000,000 microSTXsenderKey: 'your-private-key-hex',network: 'testnet',memo: 'Payment for services', // Optional memo (max 34 bytes)});const result = await broadcastTransaction({ transaction });console.log('Transaction ID:', result.txid);
Smart contract deployment
import { makeContractDeploy, ClarityVersion } from '@stacks/transactions';const contractCode = `(define-public (say-hello)(ok "Hello, Stacks!"))`;const transaction = await makeContractDeploy({contractName: 'hello-world',codeBody: contractCode,senderKey: 'your-private-key-hex',network: 'testnet',clarityVersion: ClarityVersion.Clarity3,});const result = await broadcastTransaction({ transaction });
Contract function calls
import { makeContractCall, Cl } from '@stacks/transactions';const transaction = await makeContractCall({contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM',contractName: 'counter',functionName: 'increment',functionArgs: [Cl.uint(1)],senderKey: 'your-private-key-hex',network: 'testnet',});
Unsigned transactions
Create unsigned transactions for multi-signature workflows or offline signing.
import { makeUnsignedSTXTokenTransfer, TransactionSigner } from '@stacks/transactions';import { publicKeyFromPrivate } from '@stacks/encryption';// Create unsigned transactionconst publicKey = publicKeyFromPrivate('your-private-key');const unsignedTx = await makeUnsignedSTXTokenTransfer({recipient: 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG',amount: 1000000n,publicKey,network: 'testnet',});// Sign separatelyconst signer = new TransactionSigner(unsignedTx);signer.signOrigin('your-private-key');const signedTx = signer.transaction;
Sponsored transactions
Let one account pay fees for another account's transaction.
// User creates sponsored transactionconst userTx = await makeContractCall({contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM',contractName: 'my-contract',functionName: 'transfer',functionArgs: [Cl.principal('ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG')],senderKey: 'user-private-key',sponsored: true,fee: 0n, // User doesn't paynetwork: 'testnet',});// Sponsor completes and paysimport { sponsorTransaction } from '@stacks/transactions';const sponsoredTx = await sponsorTransaction({transaction: userTx,sponsorPrivateKey: 'sponsor-private-key',fee: 2000n, // Sponsor pays the feenetwork: 'testnet',});const result = await broadcastTransaction({ transaction: sponsoredTx });
Multi-signature transactions
Require multiple signatures for enhanced security.
// Create multi-sig transaction (2-of-3)const publicKeys = [publicKey1, publicKey2, publicKey3];const multiSigTx = await makeUnsignedSTXTokenTransfer({recipient: 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG',amount: 5000000n,numSignatures: 2, // Require 2 of 3publicKeys,network: 'testnet',});// Collect signaturesconst signer = new TransactionSigner(multiSigTx);signer.signOrigin(privateKey1); // First signaturesigner.appendOrigin(privateKey2); // Second signatureconst signedTx = signer.transaction;
Working with Clarity values
Use the Cl
helper for type-safe contract arguments.
import { Cl } from '@stacks/transactions';const functionArgs = [// PrimitivesCl.uint(42),Cl.int(-10),Cl.bool(true),Cl.stringUtf8('Hello 世界'),Cl.stringAscii('Hello World'),// PrincipalsCl.standardPrincipal('ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG'),Cl.contractPrincipal('ST123...', 'my-contract'),// CompositesCl.list([Cl.uint(1), Cl.uint(2), Cl.uint(3)]),Cl.tuple({name: Cl.stringUtf8('Alice'),age: Cl.uint(30)}),// Optionals and responsesCl.some(Cl.uint(42)),Cl.none(),Cl.ok(Cl.uint(200)),Cl.err(Cl.uint(404))];
Post-conditions
Add safety constraints to protect users from unexpected transfers.
import { Pc, PostConditionMode } from '@stacks/transactions';const transaction = await makeContractCall({// ... other optionspostConditions: [// Sender must send exactly 1 STXPc.principal('ST1ADDRESS...').willSendEq(1000000n).ustx(),// Contract must transfer tokensPc.principal('ST2CONTRACT...').willSendGte(100n).ft('ST2CONTRACT.token-contract', 'my-token')],postConditionMode: PostConditionMode.Deny, // Strict mode});
Fee estimation
Get accurate fee estimates before broadcasting.
import { estimateFee } from '@stacks/transactions';// Build transaction firstconst tx = await makeSTXTokenTransfer({recipient: 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG',amount: 1000000n,senderKey: privateKey,network: 'testnet',fee: 1n, // Minimal fee for estimation});// Estimate appropriate feeconst feeRate = await estimateFee(tx);tx.setFee(feeRate);// Now broadcast with accurate feeconst result = await broadcastTransaction({ transaction: tx });