Skip to content

Transaction Lookup

← Back to Indexer Client

This example demonstrates how to lookup a single transaction by ID using the IndexerClient lookupTransactionById() method.

  • LocalNet running (via algokit localnet start)

From the repository root:

Terminal window
cd examples
npm run example indexer_client/06-transaction-lookup.ts

View source on GitHub

06-transaction-lookup.ts
/**
* Example: Transaction Lookup
*
* This example demonstrates how to lookup a single transaction by ID using
* the IndexerClient lookupTransactionById() 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('Transaction Lookup 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 senderAccount: Awaited<ReturnType<typeof algorand.account.kmd.getLocalNetDispenserAccount>>
try {
senderAccount = await algorand.account.kmd.getLocalNetDispenserAccount()
algorand.setSignerFromAccount(senderAccount)
const senderAddress = senderAccount.addr.toString()
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 a test transaction and capture its txId
// =========================================================================
printStep(2, 'Creating a test transaction and capturing its txId')
let paymentTxId: string
let assetTransferTxId: string
let assetId: bigint
let receiverAddress: string
try {
// Create a random receiver account
const receiverAccount = algorand.account.random()
receiverAddress = receiverAccount.addr.toString()
algorand.setSignerFromAccount(receiverAccount)
printInfo(`Created receiver account: ${shortenAddress(receiverAddress)}`)
// Send a payment transaction
printInfo('Sending payment transaction...')
const paymentResult = await algorand.send.payment({
sender: senderAccount.addr,
receiver: receiverAccount.addr,
amount: algo(5),
})
paymentTxId = paymentResult.txIds[0]
printSuccess(`Payment transaction sent: ${shortenAddress(paymentTxId, 8, 6)}`)
printInfo(` Full txId: ${paymentTxId}`)
// Create an asset for asset transfer demonstration
printInfo('Creating a test asset...')
const assetCreateResult = await algorand.send.assetCreate({
sender: senderAccount.addr,
total: 1_000_000n,
decimals: 6,
assetName: 'LookupToken',
unitName: 'LOOK',
})
assetId = assetCreateResult.assetId
printSuccess(`Created asset: LookupToken (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')
// Send an asset transfer transaction
printInfo('Sending asset transfer transaction...')
const assetTransferResult = await algorand.send.assetTransfer({
sender: senderAccount.addr,
receiver: receiverAccount.addr,
assetId: assetId,
amount: 50_000n,
})
assetTransferTxId = assetTransferResult.txIds[0]
printSuccess(`Asset transfer transaction sent: ${shortenAddress(assetTransferTxId, 8, 6)}`)
printInfo(` Full txId: ${assetTransferTxId}`)
// Wait for 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 payment transaction by ID
// =========================================================================
printStep(3, 'Looking up payment transaction by ID')
try {
const txnResult = await indexer.lookupTransactionById(paymentTxId)
const tx = txnResult.transaction
printSuccess(`Transaction found!`)
printInfo('')
printInfo('Common transaction fields:')
printInfo(` - id: ${tx.id}`)
printInfo(` - txType: ${tx.txType}`)
printInfo(` - sender: ${shortenAddress(tx.sender)}`)
printInfo(` - fee: ${formatMicroAlgo(tx.fee)}`)
printInfo(` - firstValid: ${tx.firstValid}`)
printInfo(` - lastValid: ${tx.lastValid}`)
printInfo('')
printInfo('Confirmation info:')
if (tx.confirmedRound !== undefined) {
printInfo(` - confirmedRound: ${tx.confirmedRound}`)
}
if (tx.roundTime !== undefined) {
const date = new Date(tx.roundTime * 1000)
printInfo(` - roundTime: ${date.toISOString()} (Unix: ${tx.roundTime})`)
}
if (tx.intraRoundOffset !== undefined) {
printInfo(` - intraRoundOffset: ${tx.intraRoundOffset}`)
}
printInfo('')
// Display payment-specific fields
if (tx.paymentTransaction) {
printInfo('Payment transaction details:')
printInfo(` - receiver: ${shortenAddress(tx.paymentTransaction.receiver)}`)
printInfo(` - amount: ${formatMicroAlgo(tx.paymentTransaction.amount)}`)
if (tx.paymentTransaction.closeRemainderTo) {
printInfo(` - closeRemainderTo: ${shortenAddress(tx.paymentTransaction.closeRemainderTo)}`)
}
if (tx.paymentTransaction.closeAmount !== undefined) {
printInfo(` - closeAmount: ${formatMicroAlgo(tx.paymentTransaction.closeAmount)}`)
}
}
printInfo('')
printInfo(`Query performed at round: ${txnResult.currentRound}`)
} catch (error) {
printError(`lookupTransactionById failed: ${error instanceof Error ? error.message : String(error)}`)
}
// =========================================================================
// Step 4: Lookup asset transfer transaction by ID
// =========================================================================
printStep(4, 'Looking up asset transfer transaction by ID')
try {
const txnResult = await indexer.lookupTransactionById(assetTransferTxId)
const tx = txnResult.transaction
printSuccess(`Transaction found!`)
printInfo('')
printInfo('Common transaction fields:')
printInfo(` - id: ${tx.id}`)
printInfo(` - txType: ${tx.txType}`)
printInfo(` - sender: ${shortenAddress(tx.sender)}`)
printInfo(` - fee: ${formatMicroAlgo(tx.fee)}`)
printInfo(` - firstValid: ${tx.firstValid}`)
printInfo(` - lastValid: ${tx.lastValid}`)
printInfo('')
printInfo('Confirmation info:')
if (tx.confirmedRound !== undefined) {
printInfo(` - confirmedRound: ${tx.confirmedRound}`)
}
if (tx.roundTime !== undefined) {
const date = new Date(tx.roundTime * 1000)
printInfo(` - roundTime: ${date.toISOString()} (Unix: ${tx.roundTime})`)
}
if (tx.intraRoundOffset !== undefined) {
printInfo(` - intraRoundOffset: ${tx.intraRoundOffset}`)
}
printInfo('')
// Display asset transfer-specific fields
if (tx.assetTransferTransaction) {
printInfo('Asset transfer transaction details:')
printInfo(` - assetId: ${tx.assetTransferTransaction.assetId}`)
printInfo(` - amount: ${tx.assetTransferTransaction.amount}`)
printInfo(` - receiver: ${shortenAddress(tx.assetTransferTransaction.receiver)}`)
if (tx.assetTransferTransaction.sender) {
printInfo(` - sender (clawback): ${shortenAddress(tx.assetTransferTransaction.sender)}`)
}
if (tx.assetTransferTransaction.closeTo) {
printInfo(` - closeTo: ${shortenAddress(tx.assetTransferTransaction.closeTo)}`)
}
if (tx.assetTransferTransaction.closeAmount !== undefined) {
printInfo(` - closeAmount: ${tx.assetTransferTransaction.closeAmount}`)
}
}
printInfo('')
printInfo(`Query performed at round: ${txnResult.currentRound}`)
} catch (error) {
printError(`lookupTransactionById failed: ${error instanceof Error ? error.message : String(error)}`)
}
// =========================================================================
// Step 5: Handle transaction not found case
// =========================================================================
printStep(5, 'Handling transaction not found case')
try {
// Use a fake transaction ID that doesn't exist
const fakeTxId = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1234'
printInfo(`Attempting to lookup non-existent transaction: ${shortenAddress(fakeTxId, 8, 6)}`)
await indexer.lookupTransactionById(fakeTxId)
// If we get here, the transaction was somehow found (shouldn't happen)
printInfo('Transaction was unexpectedly found')
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
if (errorMessage.toLowerCase().includes('not found') || errorMessage.toLowerCase().includes('no transactions')) {
printSuccess('Transaction not found - error handled correctly')
printInfo(` Error message: ${errorMessage}`)
} else {
printError(`Unexpected error: ${errorMessage}`)
}
}
// =========================================================================
// Step 6: Display additional transaction fields (if available)
// =========================================================================
printStep(6, 'Displaying additional transaction fields')
try {
const txnResult = await indexer.lookupTransactionById(paymentTxId)
const tx = txnResult.transaction
printInfo('Additional fields (if present):')
if (tx.genesisId) {
printInfo(` - genesisId: ${tx.genesisId}`)
}
if (tx.genesisHash) {
const hashHex = Buffer.from(tx.genesisHash).toString('base64')
printInfo(` - genesisHash: ${hashHex.substring(0, 20)}...`)
}
if (tx.group) {
const groupHex = Buffer.from(tx.group).toString('base64')
printInfo(` - group: ${groupHex}`)
}
if (tx.note) {
const noteText = new TextDecoder().decode(tx.note)
printInfo(` - note: ${noteText || '(empty)'}`)
}
if (tx.lease) {
const leaseHex = Buffer.from(tx.lease).toString('base64')
printInfo(` - lease: ${leaseHex}`)
}
if (tx.rekeyTo) {
printInfo(` - rekeyTo: ${tx.rekeyTo}`)
}
if (tx.senderRewards !== undefined) {
printInfo(` - senderRewards: ${formatMicroAlgo(tx.senderRewards)}`)
}
if (tx.receiverRewards !== undefined) {
printInfo(` - receiverRewards: ${formatMicroAlgo(tx.receiverRewards)}`)
}
if (tx.closeRewards !== undefined) {
printInfo(` - closeRewards: ${formatMicroAlgo(tx.closeRewards)}`)
}
if (tx.closingAmount !== undefined) {
printInfo(` - closingAmount: ${formatMicroAlgo(tx.closingAmount)}`)
}
if (tx.authAddr) {
printInfo(` - authAddr: ${tx.authAddr}`)
}
if (tx.signature) {
printInfo(` - signature: (present)`)
if (tx.signature.sig) {
printInfo(` - type: single signature`)
}
if (tx.signature.multisig) {
printInfo(` - type: multisig`)
}
if (tx.signature.logicsig) {
printInfo(` - type: logic signature`)
}
}
// Check for created assets or applications
if (tx.createdAssetId !== undefined) {
printInfo(` - createdAssetId: ${tx.createdAssetId}`)
}
if (tx.createdAppId !== undefined) {
printInfo(` - createdAppId: ${tx.createdAppId}`)
}
// Inner transactions (for app calls)
if (tx.innerTxns && tx.innerTxns.length > 0) {
printInfo(` - innerTxns: ${tx.innerTxns.length} inner transaction(s)`)
}
// Logs (for app calls)
if (tx.logs && tx.logs.length > 0) {
printInfo(` - logs: ${tx.logs.length} log entry(ies)`)
}
// State deltas (for app calls)
if (tx.globalStateDelta) {
printInfo(` - globalStateDelta: (present)`)
}
if (tx.localStateDelta && tx.localStateDelta.length > 0) {
printInfo(` - localStateDelta: ${tx.localStateDelta.length} account(s) affected`)
}
} catch (error) {
printError(`Failed to display additional fields: ${error instanceof Error ? error.message : String(error)}`)
}
// =========================================================================
// Summary
// =========================================================================
printHeader('Summary')
printInfo('This example demonstrated:')
printInfo(' 1. lookupTransactionById(txId) - Get full transaction details by ID')
printInfo(' 2. Displaying common transaction fields: id, txType, sender, fee, firstValid, lastValid')
printInfo(' 3. Displaying confirmation info: confirmedRound, roundTime, intraRoundOffset')
printInfo(' 4. Displaying payment-specific fields: receiver, amount, closeRemainderTo')
printInfo(' 5. Displaying asset transfer-specific fields: assetId, amount, receiver')
printInfo(' 6. Handling transaction not found errors')
printInfo(' 7. Displaying additional fields: genesisId, note, signature, etc.')
printInfo('')
printInfo('TransactionResponse structure:')
printInfo(' - transaction: The full Transaction object')
printInfo(' - currentRound: Round at which the results were computed')
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(' - firstValid: First valid round (bigint)')
printInfo(' - lastValid: Last valid round (bigint)')
printInfo(' - confirmedRound: Round when confirmed (bigint, optional)')
printInfo(' - roundTime: Unix timestamp when confirmed (number, optional)')
printInfo(' - intraRoundOffset: Position within the round (number, optional)')
printInfo('')
printInfo('Type-specific fields:')
printInfo(' - paymentTransaction: { receiver, amount, closeRemainderTo, closeAmount }')
printInfo(' - assetTransferTransaction: { assetId, amount, receiver, sender, closeTo, closeAmount }')
printInfo(' - assetConfigTransaction: { assetId, params }')
printInfo(' - applicationTransaction: { applicationId, onComplete, accounts, etc. }')
}
main().catch((error) => {
console.error('Fatal error:', error)
process.exit(1)
})