Contract calls
Execute smart contract functions with transactions
Contract calls allow you to execute state-changing functions in smart contracts. Unlike read-only calls, these create transactions that must be signed and broadcast to the network.
Basic contract call
Execute a simple contract function by creating a transaction with the required parameters.
import {makeContractCall,broadcastTransaction,AnchorMode} from '@stacks/transactions';async function callContract() {const txOptions = {contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM',contractName: 'my-contract',functionName: 'transfer',functionArgs: [],senderKey: 'your-private-key',network: 'testnet',anchorMode: AnchorMode.Any,};const transaction = await makeContractCall(txOptions);const broadcastResponse = await broadcastTransaction({ transaction });console.log('Transaction ID:', broadcastResponse.txid);}
The makeContractCall
function creates a transaction that will execute the specified function when confirmed on-chain.
Passing function arguments
Most contract functions require arguments. Use Clarity value constructors to match the expected parameter types.
import {Cl,makeContractCall,} from '@stacks/transactions';const functionArgs = [Cl.principal('ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG'), // recipientCl.uint(1000000), // amountCl.buffer(Buffer.from('Transfer memo', 'utf-8')), // memo];const txOptions = {contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM',contractName: 'sip-010-token',functionName: 'transfer',functionArgs,senderKey: 'your-private-key',network: "testnet",};const transaction = await makeContractCall(txOptions);const result = await broadcastTransaction({ transaction });console.log('Transaction ID:', result.txid);
Each Clarity type has a corresponding constructor function that ensures proper encoding for the blockchain.
Complex argument types
// Tuple argumentsconst userInfo = Cl.tuple({name: Cl.string('Alice'),age: Cl.uint(30),active: Cl.bool(true),});// List argumentsconst addresses = Cl.list([Cl.principal('ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM'),Cl.principal('ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG'),]);
Optional and response values have dedicated constructors for proper type safety.
// Optional valuesconst optionalValue = Cl.some(Cl.uint(42)); // (some 42)const noValue = Cl.none(); // none// Response valuesconst successResponse = Cl.ok(Cl.uint(100));const errorResponse = Cl.err(Cl.uint(404));
Contract call with STX transfer
Some contracts require STX to be sent along with the function call, such as when minting NFTs or paying for services.
async function mintNFT() {const mintPrice = 1000000; // 1 STXconst txOptions = {contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM',contractName: 'nft-collection',functionName: 'mint',functionArgs: [],senderKey: 'your-private-key',network: new StacksTestnet(),anchorMode: AnchorMode.Any,// Attach STX to the contract callamount: mintPrice,};const transaction = await makeContractCall(txOptions);return broadcastTransaction(transaction, network);}
Handling contract responses
Process transaction results and contract responses:
async function executeAndMonitor() {// Execute contract callconst transaction = await makeContractCall({contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM',contractName: 'my-contract',functionName: 'process',functionArgs: [uintCV(100)],senderKey: 'your-private-key',network: new StacksTestnet(),anchorMode: AnchorMode.Any,});const broadcastResponse = await broadcastTransaction(transaction, network);const txId = broadcastResponse.txid;// Wait for confirmationconst txInfo = await waitForConfirmation(txId, network);// Check transaction resultif (txInfo.tx_status === 'success') {console.log('Contract returned:', txInfo.tx_result);// Parse the result based on expected return type} else {console.error('Transaction failed:', txInfo.tx_result);}}async function waitForConfirmation(txId: string, network: StacksNetwork) {let attempts = 0;const maxAttempts = 30;while (attempts < maxAttempts) {const response = await fetch(`${network.coreApiUrl}/extended/v1/tx/${txId}`);const txInfo = await response.json();if (txInfo.tx_status === 'success' || txInfo.tx_status === 'abort_by_response') {return txInfo;}await new Promise(resolve => setTimeout(resolve, 10000));attempts++;}throw new Error('Transaction confirmation timeout');}
Multi-step contract interactions
Chain multiple contract calls:
async function complexWorkflow() {// Step 1: Approve spendingconst approveTx = await makeContractCall({contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM',contractName: 'token',functionName: 'approve',functionArgs: [standardPrincipalCV('ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG'),uintCV(1000000),],senderKey: 'your-private-key',network: new StacksTestnet(),anchorMode: AnchorMode.Any,});const approveResult = await broadcastTransaction(approveTx, network);await waitForConfirmation(approveResult.txid, network);// Step 2: Execute swap after approvalconst swapTx = await makeContractCall({contractAddress: 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG',contractName: 'dex',functionName: 'swap-tokens',functionArgs: [standardPrincipalCV('ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM'),uintCV(1000000),],senderKey: 'your-private-key',network: new StacksTestnet(),anchorMode: AnchorMode.Any,});return broadcastTransaction(swapTx, network);}