Skip to content
Algorand Developer Portal

Account Transactions

← Back to Indexer Client

This example demonstrates how to query an account’s transaction history using the IndexerClient lookupAccountTransactions() method.

  • LocalNet running (via algokit localnet start)

From the repository root:

Terminal window
cd examples
npm run example indexer_client/05-account-transactions.ts

View source on GitHub

05-account-transactions.ts
/**
* Example: Account Transactions
*
* This example demonstrates how to query an account's transaction history using
* the IndexerClient lookupAccountTransactions() method.
*
* Prerequisites:
* - LocalNet running (via `algokit localnet start`)
*/
import { algo } from '@algorandfoundation/algokit-utils';
import {
createAlgorandClient,
createIndexerClient,
formatMicroAlgo,
printError,
printHeader,
printInfo,
printStep,
printSuccess,
shortenAddress,
} from '../shared/utils.js';
async function main() {
printHeader('Account Transactions Example');
// Create clients
const indexer = createIndexerClient();
const algorand = createAlgorandClient();
// =========================================================================
// Step 1: Get a funded account from LocalNet
// =========================================================================
printStep(1, 'Getting a funded account from LocalNet');
let senderAddress: string;
let senderAccount: Awaited<ReturnType<typeof algorand.account.kmd.getLocalNetDispenserAccount>>;
try {
senderAccount = await algorand.account.kmd.getLocalNetDispenserAccount();
senderAddress = senderAccount.addr.toString();
algorand.setSignerFromAccount(senderAccount);
printSuccess(`Using dispenser account: ${shortenAddress(senderAddress)}`);
} catch (error) {
printError(
`Failed to get dispenser account: ${error instanceof Error ? error.message : String(error)}`,
);
printInfo('');
printInfo('Make sure LocalNet is running: algokit localnet start');
printInfo('If issues persist, try: algokit localnet reset');
return;
}
// =========================================================================
// Step 2: Create test transactions for demonstration
// =========================================================================
printStep(2, 'Creating test transactions for demonstration');
let receiverAddress: string;
let assetId: bigint;
try {
// Create a random receiver account
const receiverAccount = algorand.account.random();
receiverAddress = receiverAccount.addr.toString();
algorand.setSignerFromAccount(receiverAccount);
printInfo(`Created receiver account: ${shortenAddress(receiverAddress)}`);
// Fund the receiver with some initial ALGO
printInfo('Sending initial payment to receiver...');
await algorand.send.payment({
sender: senderAccount.addr,
receiver: receiverAccount.addr,
amount: algo(10),
});
printSuccess('Initial payment sent: 10 ALGO');
// Send a few more payments with different amounts
printInfo('Sending additional payments...');
await algorand.send.payment({
sender: senderAccount.addr,
receiver: receiverAccount.addr,
amount: algo(5),
});
printSuccess('Payment sent: 5 ALGO');
await algorand.send.payment({
sender: senderAccount.addr,
receiver: receiverAccount.addr,
amount: algo(0.5),
});
printSuccess('Payment sent: 0.5 ALGO');
// Create an asset
printInfo('Creating a test asset...');
const assetCreateResult = await algorand.send.assetCreate({
sender: senderAccount.addr,
total: 1_000_000n,
decimals: 6,
assetName: 'TestToken',
unitName: 'TEST',
});
assetId = assetCreateResult.assetId;
printSuccess(`Created asset: TestToken (ID: ${assetId})`);
// Opt-in receiver to the asset
printInfo('Opting receiver into asset...');
await algorand.send.assetOptIn({
sender: receiverAccount.addr,
assetId: assetId,
});
printSuccess('Receiver opted into asset');
// Transfer some assets
printInfo('Sending asset transfer...');
await algorand.send.assetTransfer({
sender: senderAccount.addr,
receiver: receiverAccount.addr,
assetId: assetId,
amount: 100_000n,
});
printSuccess('Asset transfer sent: 100,000 TestToken (0.1 TEST)');
// Small delay to allow indexer to catch up
printInfo('Waiting for indexer to index transactions...');
await new Promise(resolve => setTimeout(resolve, 3000));
printInfo('');
} catch (error) {
printError(
`Failed to create test transactions: ${error instanceof Error ? error.message : String(error)}`,
);
printInfo('');
printInfo('If LocalNet errors occur, try: algokit localnet reset');
return;
}
// =========================================================================
// Step 3: Lookup account transactions with lookupAccountTransactions()
// =========================================================================
printStep(3, 'Looking up account transactions with lookupAccountTransactions()');
try {
// Note: Results are returned newest to oldest for account transactions
const txnsResult = await indexer.lookupAccountTransactions(senderAddress);
printSuccess(`Found ${txnsResult.transactions.length} transaction(s) for account`);
printInfo('Note: Results are returned newest to oldest');
printInfo('');
if (txnsResult.transactions.length > 0) {
printInfo('Recent transactions:');
for (const tx of txnsResult.transactions.slice(0, 5)) {
printInfo(` Transaction ID: ${tx.id ? shortenAddress(tx.id, 8, 6) : 'N/A'}`);
printInfo(` - txType: ${tx.txType}`);
printInfo(` - sender: ${shortenAddress(tx.sender)}`);
printInfo(` - fee: ${formatMicroAlgo(tx.fee)}`);
if (tx.confirmedRound !== undefined) {
printInfo(` - confirmedRound: ${tx.confirmedRound}`);
}
if (tx.roundTime !== undefined) {
const date = new Date(tx.roundTime * 1000);
printInfo(` - roundTime: ${date.toISOString()}`);
}
// Show payment details if present
if (tx.paymentTransaction) {
printInfo(` - receiver: ${shortenAddress(tx.paymentTransaction.receiver)}`);
printInfo(` - amount: ${formatMicroAlgo(tx.paymentTransaction.amount)}`);
}
// Show asset transfer details if present
if (tx.assetTransferTransaction) {
printInfo(` - assetId: ${tx.assetTransferTransaction.assetId}`);
printInfo(` - amount: ${tx.assetTransferTransaction.amount}`);
printInfo(` - receiver: ${shortenAddress(tx.assetTransferTransaction.receiver)}`);
}
// Show asset config details if present
if (tx.assetConfigTransaction) {
printInfo(` - assetId: ${tx.assetConfigTransaction.assetId ?? 'new asset'}`);
if (tx.createdAssetId) {
printInfo(` - createdAssetId: ${tx.createdAssetId}`);
}
}
printInfo('');
}
}
printInfo(`Query performed at round: ${txnsResult.currentRound}`);
} catch (error) {
printError(
`lookupAccountTransactions failed: ${error instanceof Error ? error.message : String(error)}`,
);
}
// =========================================================================
// Step 4: Filter by transaction type (pay, axfer, appl)
// =========================================================================
printStep(4, 'Filtering by transaction type (txType)');
try {
// Filter for payment transactions only
printInfo('Querying payment transactions (txType=pay)...');
const payTxns = await indexer.lookupAccountTransactions(senderAddress, { txType: 'pay' });
printSuccess(`Found ${payTxns.transactions.length} payment transaction(s)`);
if (payTxns.transactions.length > 0) {
for (const tx of payTxns.transactions.slice(0, 3)) {
printInfo(
` - ${tx.id ? shortenAddress(tx.id, 8, 6) : 'N/A'}: ${formatMicroAlgo(tx.paymentTransaction?.amount ?? 0n)}`,
);
}
}
printInfo('');
// Filter for asset transfer transactions
printInfo('Querying asset transfer transactions (txType=axfer)...');
const axferTxns = await indexer.lookupAccountTransactions(senderAddress, { txType: 'axfer' });
printSuccess(`Found ${axferTxns.transactions.length} asset transfer transaction(s)`);
if (axferTxns.transactions.length > 0) {
for (const tx of axferTxns.transactions.slice(0, 3)) {
printInfo(
` - ${tx.id ? shortenAddress(tx.id, 8, 6) : 'N/A'}: assetId=${tx.assetTransferTransaction?.assetId}, amount=${tx.assetTransferTransaction?.amount}`,
);
}
}
printInfo('');
// Filter for asset config transactions
printInfo('Querying asset config transactions (txType=acfg)...');
const acfgTxns = await indexer.lookupAccountTransactions(senderAddress, { txType: 'acfg' });
printSuccess(`Found ${acfgTxns.transactions.length} asset config transaction(s)`);
if (acfgTxns.transactions.length > 0) {
for (const tx of acfgTxns.transactions.slice(0, 3)) {
const assetIdDisplay = tx.createdAssetId ?? tx.assetConfigTransaction?.assetId ?? 'N/A';
printInfo(` - ${tx.id ? shortenAddress(tx.id, 8, 6) : 'N/A'}: assetId=${assetIdDisplay}`);
}
}
} catch (error) {
printError(
`Transaction type filter failed: ${error instanceof Error ? error.message : String(error)}`,
);
}
// =========================================================================
// Step 5: Filter by round range (minRound, maxRound)
// =========================================================================
printStep(5, 'Filtering by round range (minRound, maxRound)');
try {
// Get current round
const allTxns = await indexer.lookupAccountTransactions(senderAddress, { limit: 1 });
const currentRound = allTxns.currentRound;
// Filter to recent rounds only (last 100 rounds, but not negative)
const minRound = currentRound > 100n ? currentRound - 100n : 0n;
printInfo(`Querying transactions from round ${minRound} to ${currentRound}...`);
const roundFilteredTxns = await indexer.lookupAccountTransactions(senderAddress, {
minRound: minRound,
maxRound: currentRound,
});
printSuccess(
`Found ${roundFilteredTxns.transactions.length} transaction(s) in round range ${minRound}-${currentRound}`,
);
if (roundFilteredTxns.transactions.length > 0) {
const rounds = roundFilteredTxns.transactions
.map(tx => tx.confirmedRound)
.filter(r => r !== undefined);
if (rounds.length > 0) {
const minFoundRound = rounds.reduce((a, b) => (a! < b! ? a : b));
const maxFoundRound = rounds.reduce((a, b) => (a! > b! ? a : b));
printInfo(` Rounds of found transactions: ${minFoundRound} to ${maxFoundRound}`);
}
}
} catch (error) {
printError(`Round filter failed: ${error instanceof Error ? error.message : String(error)}`);
}
// =========================================================================
// Step 6: Filter by time (beforeTime, afterTime)
// =========================================================================
printStep(6, 'Filtering by time (beforeTime, afterTime)');
try {
// Get current time and a time window
const now = new Date();
const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000); // 1 hour ago
// Format as RFC 3339 (ISO 8601 format that indexer expects)
const afterTimeStr = oneHourAgo.toISOString();
const beforeTimeStr = now.toISOString();
printInfo(`Querying transactions from ${afterTimeStr} to ${beforeTimeStr}...`);
const timeFilteredTxns = await indexer.lookupAccountTransactions(senderAddress, {
afterTime: afterTimeStr,
beforeTime: beforeTimeStr,
});
printSuccess(`Found ${timeFilteredTxns.transactions.length} transaction(s) in the last hour`);
if (timeFilteredTxns.transactions.length > 0) {
const times = timeFilteredTxns.transactions
.map(tx => tx.roundTime)
.filter(t => t !== undefined);
if (times.length > 0) {
const minTime = Math.min(...(times as number[]));
const maxTime = Math.max(...(times as number[]));
printInfo(` Time range of found transactions:`);
printInfo(` - Earliest: ${new Date(minTime * 1000).toISOString()}`);
printInfo(` - Latest: ${new Date(maxTime * 1000).toISOString()}`);
}
}
} catch (error) {
printError(`Time filter failed: ${error instanceof Error ? error.message : String(error)}`);
}
// =========================================================================
// Step 7: Filter by amount (currencyGreaterThan, currencyLessThan)
// =========================================================================
printStep(7, 'Filtering by amount (currencyGreaterThan, currencyLessThan)');
try {
// Filter for transactions with amount greater than 1 ALGO (1,000,000 microAlgo)
const minAmount = 1_000_000n;
printInfo(`Querying transactions with amount > ${formatMicroAlgo(minAmount)}...`);
const largeAmountTxns = await indexer.lookupAccountTransactions(senderAddress, {
currencyGreaterThan: minAmount,
});
printSuccess(
`Found ${largeAmountTxns.transactions.length} transaction(s) with amount > 1 ALGO`,
);
if (largeAmountTxns.transactions.length > 0) {
for (const tx of largeAmountTxns.transactions.slice(0, 3)) {
const amount = tx.paymentTransaction?.amount ?? tx.assetTransferTransaction?.amount ?? 0n;
printInfo(
` - ${tx.id ? shortenAddress(tx.id, 8, 6) : 'N/A'}: ${tx.txType}, amount=${amount}`,
);
}
}
printInfo('');
// Filter for transactions with amount less than 5 ALGO (5,000,000 microAlgo)
const maxAmount = 5_000_000n;
printInfo(`Querying transactions with amount < ${formatMicroAlgo(maxAmount)}...`);
const smallAmountTxns = await indexer.lookupAccountTransactions(senderAddress, {
currencyLessThan: maxAmount,
});
printSuccess(
`Found ${smallAmountTxns.transactions.length} transaction(s) with amount < 5 ALGO`,
);
if (smallAmountTxns.transactions.length > 0) {
for (const tx of smallAmountTxns.transactions.slice(0, 3)) {
const amount = tx.paymentTransaction?.amount ?? tx.assetTransferTransaction?.amount ?? 0n;
printInfo(
` - ${tx.id ? shortenAddress(tx.id, 8, 6) : 'N/A'}: ${tx.txType}, amount=${amount}`,
);
}
}
printInfo('');
// Combine both filters to find transactions in a specific range
printInfo(
`Querying transactions with amount between ${formatMicroAlgo(minAmount)} and ${formatMicroAlgo(maxAmount)}...`,
);
const rangeAmountTxns = await indexer.lookupAccountTransactions(senderAddress, {
currencyGreaterThan: minAmount,
currencyLessThan: maxAmount,
});
printSuccess(
`Found ${rangeAmountTxns.transactions.length} transaction(s) with amount in range 1-5 ALGO`,
);
} catch (error) {
printError(`Amount filter failed: ${error instanceof Error ? error.message : String(error)}`);
}
// =========================================================================
// Step 8: Pagination with limit and next
// =========================================================================
printStep(8, 'Demonstrating pagination with limit and next');
try {
printInfo('Querying transactions with limit=2...');
const page1 = await indexer.lookupAccountTransactions(senderAddress, { limit: 2 });
printInfo(`Page 1: Retrieved ${page1.transactions.length} transaction(s)`);
for (const tx of page1.transactions) {
printInfo(` - ${tx.id ? shortenAddress(tx.id, 8, 6) : 'N/A'}: ${tx.txType}`);
}
if (page1.nextToken) {
printInfo(` - Next token available: ${page1.nextToken.substring(0, 20)}...`);
printInfo('');
// Get next page
printInfo('Querying next page...');
const page2 = await indexer.lookupAccountTransactions(senderAddress, {
limit: 2,
next: page1.nextToken,
});
printInfo(`Page 2: Retrieved ${page2.transactions.length} transaction(s)`);
for (const tx of page2.transactions) {
printInfo(` - ${tx.id ? shortenAddress(tx.id, 8, 6) : 'N/A'}: ${tx.txType}`);
}
if (page2.nextToken) {
printInfo(` - More pages available (nextToken present)`);
} else {
printInfo(` - No more pages (no nextToken)`);
}
} else {
printInfo(' - No pagination needed (all results fit in one page)');
}
} catch (error) {
printError(`Pagination demo failed: ${error instanceof Error ? error.message : String(error)}`);
}
// =========================================================================
// Summary
// =========================================================================
printHeader('Summary');
printInfo('This example demonstrated:');
printInfo(' 1. lookupAccountTransactions(address) - Get transaction history for an account');
printInfo(' 2. Results are returned newest to oldest');
printInfo(' 3. Filtering by txType (pay, axfer, acfg, appl, etc.)');
printInfo(' 4. Filtering by round range (minRound, maxRound)');
printInfo(' 5. Filtering by time (beforeTime, afterTime) using RFC 3339 format');
printInfo(' 6. Filtering by amount (currencyGreaterThan, currencyLessThan)');
printInfo(' 7. Pagination using limit and next parameters');
printInfo('');
printInfo('Key Transaction fields:');
printInfo(' - id: Transaction ID (string)');
printInfo(' - txType: Transaction type (pay, keyreg, acfg, axfer, afrz, appl, stpf, hb)');
printInfo(' - sender: Sender address (string)');
printInfo(' - fee: Transaction fee in microAlgos (bigint)');
printInfo(' - confirmedRound: Round when confirmed (bigint)');
printInfo(' - roundTime: Unix timestamp when confirmed (number)');
printInfo(' - paymentTransaction: Payment details (receiver, amount, closeRemainderTo)');
printInfo(' - assetTransferTransaction: Asset transfer details (assetId, amount, receiver)');
printInfo(' - assetConfigTransaction: Asset config details (assetId, params)');
printInfo(' - applicationTransaction: App call details (applicationId, onComplete, etc.)');
printInfo('');
printInfo('Filter parameters:');
printInfo(' - txType: Filter by transaction type');
printInfo(' - minRound/maxRound: Filter by round range');
printInfo(' - beforeTime/afterTime: Filter by time (RFC 3339 format)');
printInfo(' - currencyGreaterThan/currencyLessThan: Filter by amount');
printInfo(' - assetId: Filter by specific asset');
printInfo(' - limit/next: Pagination controls');
}
main().catch(error => {
console.error('Fatal error:', error);
process.exit(1);
});