Skip to content
Algorand Developer Portal

Send Payment

← Back to Algorand Client

This example demonstrates how to send ALGO payment transactions:

  • algorand.send.payment() with basic parameters (sender, receiver, amount)
  • Using AlgoAmount for the amount parameter
  • Payment with note field
  • Payment with closeRemainderTo to close account and send remaining balance
  • Understanding the SendSingleTransactionResult return value
  • Displaying transaction ID and confirmed round
  • Verifying balances before and after payment
  • LocalNet running (via algokit localnet start)

From the repository root:

Terminal window
cd examples
npm run example algorand_client/06-send-payment.ts

View source on GitHub

06-send-payment.ts
/**
* Example: Send Payment
*
* This example demonstrates how to send ALGO payment transactions:
* - algorand.send.payment() with basic parameters (sender, receiver, amount)
* - Using AlgoAmount for the amount parameter
* - Payment with note field
* - Payment with closeRemainderTo to close account and send remaining balance
* - Understanding the SendSingleTransactionResult return value
* - Displaying transaction ID and confirmed round
* - Verifying balances before and after payment
*
* Prerequisites:
* - LocalNet running (via `algokit localnet start`)
*/
import { AlgorandClient, algo, microAlgo } from '@algorandfoundation/algokit-utils';
import {
formatAlgo,
printError,
printHeader,
printInfo,
printStep,
printSuccess,
shortenAddress,
} from '../shared/utils.js';
async function main() {
printHeader('Send Payment Example');
// Initialize client and verify LocalNet is running
const algorand = AlgorandClient.defaultLocalNet();
try {
await algorand.client.algod.status();
printSuccess('Connected to LocalNet');
} catch (error) {
printError(
`Failed to connect to LocalNet: ${error instanceof Error ? error.message : String(error)}`,
);
printInfo('Make sure LocalNet is running (e.g., algokit localnet start)');
return;
}
// Step 1: Create and fund test accounts
printStep(1, 'Create and fund test accounts using account manager');
printInfo('Creating test accounts and funding them from the LocalNet dispenser');
const sender = algorand.account.random();
const receiver = algorand.account.random();
const closeToAccount = algorand.account.random();
printInfo(`\nCreated accounts:`);
printInfo(` Sender: ${shortenAddress(sender.addr.toString())}`);
printInfo(` Receiver: ${shortenAddress(receiver.addr.toString())}`);
printInfo(` CloseToAccount: ${shortenAddress(closeToAccount.addr.toString())}`);
// Fund the sender account using ensureFundedFromEnvironment
const fundResult = await algorand.account.ensureFundedFromEnvironment(sender.addr, algo(20));
if (fundResult) {
printInfo(`\nFunded sender with: ${formatAlgo(fundResult.amountFunded)}`);
}
// Also fund closeToAccount so it exists on the network
await algorand.account.ensureFundedFromEnvironment(closeToAccount.addr, algo(1));
// Get initial balances
const senderInitialInfo = await algorand.account.getInformation(sender.addr);
const receiverInitialInfo = await algorand.account.getInformation(receiver.addr);
printInfo(`\nInitial balances:`);
printInfo(` Sender: ${formatAlgo(senderInitialInfo.balance)}`);
printInfo(` Receiver: ${formatAlgo(receiverInitialInfo.balance)}`);
printSuccess('Created and funded test accounts');
// Step 2: Basic payment with algorand.send.payment()
printStep(2, 'Basic payment with algorand.send.payment()');
printInfo('Sending a simple ALGO payment with sender, receiver, and amount');
const basicPaymentResult = await algorand.send.payment({
sender: sender.addr,
receiver: receiver.addr,
amount: algo(5), // Using AlgoAmount helper
});
printInfo(`\nBasic payment sent:`);
printInfo(` From: ${shortenAddress(sender.addr.toString())}`);
printInfo(` To: ${shortenAddress(receiver.addr.toString())}`);
printInfo(` Amount: 5 ALGO`);
// Examine the SendSingleTransactionResult return value
printInfo(`\nSendSingleTransactionResult properties:`);
printInfo(` txIds[0]: ${basicPaymentResult.txIds[0]}`);
printInfo(` confirmation.confirmedRound: ${basicPaymentResult.confirmation.confirmedRound}`);
printInfo(` transaction.txId(): ${basicPaymentResult.transaction.txId()}`);
printInfo(` groupId: ${basicPaymentResult.groupId ?? 'undefined (single transaction)'}`);
printInfo(` transactions.length: ${basicPaymentResult.transactions.length}`);
printInfo(` confirmations.length: ${basicPaymentResult.confirmations.length}`);
printSuccess('Basic payment completed');
// Step 3: Using AlgoAmount for the amount parameter
printStep(3, 'Using AlgoAmount for the amount parameter');
printInfo('AlgoAmount provides type-safe handling of ALGO and microALGO values');
// Different ways to specify amounts
const amount1 = algo(1); // 1 ALGO using helper function
const amount2 = microAlgo(500_000); // 0.5 ALGO in microALGO
printInfo(`\nDifferent amount specifications:`);
printInfo(` algo(1) = ${formatAlgo(amount1)} (${amount1.microAlgo} µALGO)`);
printInfo(` microAlgo(500_000) = ${formatAlgo(amount2)} (${amount2.microAlgo} µALGO)`);
// Send payment with microAlgo amount
const microAlgoPaymentResult = await algorand.send.payment({
sender: sender.addr,
receiver: receiver.addr,
amount: microAlgo(250_000), // 0.25 ALGO
});
printInfo(`\nPayment with microAlgo amount:`);
printInfo(` Amount: ${formatAlgo(microAlgo(250_000))} (250,000 µALGO)`);
printInfo(` Transaction ID: ${microAlgoPaymentResult.txIds[0]}`);
printSuccess('Demonstrated AlgoAmount usage');
// Step 4: Payment with note field
printStep(4, 'Payment with note field');
printInfo('Adding arbitrary data to a payment using the note field');
printInfo('Notes can be strings, byte arrays, or structured data (JSON)');
// String note
const stringNoteResult = await algorand.send.payment({
sender: sender.addr,
receiver: receiver.addr,
amount: algo(0.1),
note: 'Payment for services rendered',
});
printInfo(`\nPayment with string note:`);
printInfo(` Note: "Payment for services rendered"`);
printInfo(` Transaction ID: ${stringNoteResult.txIds[0]}`);
// JSON note (useful for structured data)
const jsonNote = JSON.stringify({
type: 'invoice',
id: '12345',
timestamp: new Date().toISOString(),
});
const jsonNoteResult = await algorand.send.payment({
sender: sender.addr,
receiver: receiver.addr,
amount: algo(0.1),
note: jsonNote,
});
printInfo(`\nPayment with JSON note:`);
printInfo(` Note: ${jsonNote}`);
printInfo(` Transaction ID: ${jsonNoteResult.txIds[0]}`);
// Byte array note
const byteNote = new TextEncoder().encode('Binary data note');
const byteNoteResult = await algorand.send.payment({
sender: sender.addr,
receiver: receiver.addr,
amount: algo(0.1),
note: byteNote,
});
printInfo(`\nPayment with byte array note:`);
printInfo(` Note: Uint8Array(${byteNote.length}) - "Binary data note"`);
printInfo(` Transaction ID: ${byteNoteResult.txIds[0]}`);
printSuccess('Demonstrated payment with notes');
// Step 5: Verify balances before and after payment
printStep(5, 'Verify balances before and after payment using getInformation()');
printInfo('Using algorand.account.getInformation() to check account balances');
// Get current balances
const senderBeforeInfo = await algorand.account.getInformation(sender.addr);
const receiverBeforeInfo = await algorand.account.getInformation(receiver.addr);
printInfo(`\nBalances before payment:`);
printInfo(` Sender: ${formatAlgo(senderBeforeInfo.balance)}`);
printInfo(` Receiver: ${formatAlgo(receiverBeforeInfo.balance)}`);
// Send a precise amount
const precisAmount = algo(2);
await algorand.send.payment({
sender: sender.addr,
receiver: receiver.addr,
amount: precisAmount,
});
// Get balances after payment
const senderAfterInfo = await algorand.account.getInformation(sender.addr);
const receiverAfterInfo = await algorand.account.getInformation(receiver.addr);
printInfo(`\nBalances after sending ${formatAlgo(precisAmount)}:`);
printInfo(` Sender: ${formatAlgo(senderAfterInfo.balance)}`);
printInfo(` Receiver: ${formatAlgo(receiverAfterInfo.balance)}`);
// Calculate the difference (includes transaction fee)
const senderDiff = senderBeforeInfo.balance.microAlgo - senderAfterInfo.balance.microAlgo;
const receiverDiff = receiverAfterInfo.balance.microAlgo - receiverBeforeInfo.balance.microAlgo;
printInfo(`\nBalance changes:`);
printInfo(` Sender lost: ${formatAlgo(senderDiff)} (amount + fee)`);
printInfo(` Receiver gained: ${formatAlgo(receiverDiff)}`);
printInfo(` Transaction fee: ${formatAlgo(senderDiff - receiverDiff)}`);
printSuccess('Verified balance changes');
// Step 6: Demonstrate closeRemainderTo to close account
printStep(6, 'Demonstrate closeRemainderTo to close account and send remaining balance');
printInfo('closeRemainderTo closes the sender account and sends ALL remaining balance');
printInfo('WARNING: This permanently closes the account - use with caution!');
// Create a new account specifically for closing
const accountToClose = algorand.account.random();
await algorand.account.ensureFundedFromEnvironment(accountToClose.addr, algo(5));
const accountToCloseInitialInfo = await algorand.account.getInformation(accountToClose.addr);
const closeToInitialInfo = await algorand.account.getInformation(closeToAccount.addr);
printInfo(`\nAccount to close: ${shortenAddress(accountToClose.addr.toString())}`);
printInfo(` Initial balance: ${formatAlgo(accountToCloseInitialInfo.balance)}`);
printInfo(`\nClose remainder to: ${shortenAddress(closeToAccount.addr.toString())}`);
printInfo(` Initial balance: ${formatAlgo(closeToInitialInfo.balance)}`);
// Send a payment with closeRemainderTo
// This will:
// 1. Send the specified amount to receiver
// 2. Send ALL remaining balance to closeRemainderTo address
// 3. Close the sender account
const closeResult = await algorand.send.payment({
sender: accountToClose.addr,
receiver: receiver.addr, // Receiver gets the explicit amount
amount: algo(1), // Explicit amount to receiver
closeRemainderTo: closeToAccount.addr, // Remainder goes here, account closes
});
printInfo(`\nClose account transaction:`);
printInfo(` Transaction ID: ${closeResult.txIds[0]}`);
printInfo(` Confirmed round: ${closeResult.confirmation.confirmedRound}`);
printInfo(` Explicit amount to receiver: ${formatAlgo(algo(1))}`);
// Verify the close operation
const accountToCloseFinalInfo = await algorand.account.getInformation(accountToClose.addr);
const receiverFinalInfo = await algorand.account.getInformation(receiver.addr);
const closeToFinalInfo = await algorand.account.getInformation(closeToAccount.addr);
printInfo(`\nAfter closing:`);
printInfo(
` Closed account balance: ${formatAlgo(accountToCloseFinalInfo.balance)} (should be 0)`,
);
printInfo(
` Receiver gained: ${formatAlgo(receiverFinalInfo.balance.microAlgo - receiverBeforeInfo.balance.microAlgo)}`,
);
printInfo(` CloseToAccount balance: ${formatAlgo(closeToFinalInfo.balance)}`);
printInfo(
` CloseToAccount gained: ${formatAlgo(closeToFinalInfo.balance.microAlgo - closeToInitialInfo.balance.microAlgo)}`,
);
printSuccess('Demonstrated closeRemainderTo');
// Step 7: Waiting for confirmation
printStep(7, 'Understanding transaction confirmation');
printInfo('algorand.send.payment() automatically waits for confirmation');
printInfo('The result includes confirmation details from the network');
const confirmationResult = await algorand.send.payment({
sender: sender.addr,
receiver: receiver.addr,
amount: algo(0.5),
});
printInfo(`\nTransaction confirmation details:`);
printInfo(` Transaction ID: ${confirmationResult.txIds[0]}`);
printInfo(` Confirmed round: ${confirmationResult.confirmation.confirmedRound}`);
printInfo(` Pool error: ${confirmationResult.confirmation.poolError ?? 'none'}`);
// Access the Transaction object
printInfo(`\nTransaction object details:`);
printInfo(` txId(): ${confirmationResult.transaction.txId()}`);
printInfo(` type: ${confirmationResult.transaction.type}`);
printInfo(` firstValid: ${confirmationResult.transaction.firstValid}`);
printInfo(` lastValid: ${confirmationResult.transaction.lastValid}`);
printInfo(` fee: ${confirmationResult.transaction.fee} µALGO`);
printSuccess('Demonstrated transaction confirmation');
// Step 8: Summary of SendSingleTransactionResult
printStep(8, 'Summary - SendSingleTransactionResult properties');
printInfo('When you call algorand.send.payment(), you get a SendSingleTransactionResult:');
printInfo('');
printInfo('Primary transaction properties:');
printInfo(' txIds[0]: string - The transaction ID for single transactions');
printInfo(' transaction: Transaction - The Transaction object');
printInfo(' confirmation: PendingTransactionResponse - Confirmation details');
printInfo('');
printInfo('Group properties (also present for single transactions):');
printInfo(' groupId: string | undefined - The group ID if part of a group');
printInfo(' txIds: string[] - Array of transaction IDs');
printInfo(' transactions: Transaction[] - Array of Transaction objects');
printInfo(' confirmations: PendingTransactionResponse[] - Array of confirmations');
printInfo('');
printInfo('Useful confirmation properties:');
printInfo(' confirmation.confirmedRound - The round the transaction was confirmed');
printInfo(' confirmation.poolError - Any error message from the pool');
printInfo(' confirmation.closingAmount - Amount sent to closeRemainderTo (if used)');
printInfo('');
printInfo('Payment parameters:');
printInfo(' sender: Address - Who is sending the payment');
printInfo(' receiver: Address - Who receives the payment');
printInfo(' amount: AlgoAmount - How much to send');
printInfo(' note?: string | Uint8Array - Optional note data');
printInfo(' closeRemainderTo?: Address - Close account and send remainder here');
// Final balance summary
printStep(9, 'Final balance summary');
const finalSenderInfo = await algorand.account.getInformation(sender.addr);
const finalReceiverInfo = await algorand.account.getInformation(receiver.addr);
printInfo(`\nFinal balances:`);
printInfo(
` Sender: ${formatAlgo(finalSenderInfo.balance)} (started with ${formatAlgo(senderInitialInfo.balance)})`,
);
printInfo(
` Receiver: ${formatAlgo(finalReceiverInfo.balance)} (started with ${formatAlgo(receiverInitialInfo.balance)})`,
);
printSuccess('Send Payment example completed!');
}
main().catch(error => {
printError(`Unhandled error: ${error instanceof Error ? error.message : String(error)}`);
process.exit(1);
});