Ledger State Deltas
Description
Section titled “Description”This example demonstrates how to retrieve ledger state deltas using:
- ledgerStateDelta(round) - Get state changes for a specific round
- ledgerStateDeltaForTransactionGroup(txId) - Get deltas for a specific transaction group
- transactionGroupLedgerStateDeltasForRound(round) - Get all transaction group deltas in a round State deltas show what changed in the ledger (accounts, balances, apps, assets) between rounds. Note: These endpoints may require node configuration to enable (EnableDeveloperAPI=true).
Prerequisites
Section titled “Prerequisites”- LocalNet running (via
algokit localnet start)
Run This Example
Section titled “Run This Example”From the repository root:
cd examplesnpm run example algod_client/14-state-deltas.ts/** * Example: Ledger State Deltas * * This example demonstrates how to retrieve ledger state deltas using: * - ledgerStateDelta(round) - Get state changes for a specific round * - ledgerStateDeltaForTransactionGroup(txId) - Get deltas for a specific transaction group * - transactionGroupLedgerStateDeltasForRound(round) - Get all transaction group deltas in a round * * State deltas show what changed in the ledger (accounts, balances, apps, assets) between rounds. * * Note: These endpoints may require node configuration to enable (EnableDeveloperAPI=true). * * Prerequisites: * - LocalNet running (via `algokit localnet start`) */
import { algo } from '@algorandfoundation/algokit-utils'import type { LedgerStateDelta, TransactionGroupLedgerStateDeltasForRoundResponse,} from '@algorandfoundation/algokit-utils/algod-client'import { createAlgodClient, createAlgorandClient, formatMicroAlgo, printError, printHeader, printInfo, printStep, printSuccess, shortenAddress,} from '../shared/utils.js'
async function main() { printHeader('Ledger State Deltas Example')
// Create clients const algod = createAlgodClient() const algorand = createAlgorandClient()
// ========================================================================= // Step 1: Set up accounts and submit a transaction to create state changes // ========================================================================= printStep(1, 'Setting up accounts and submitting a payment transaction')
// Get a funded account from LocalNet (the dispenser) const sender = await algorand.account.dispenserFromEnvironment() printInfo(`Sender address: ${shortenAddress(sender.addr.toString())}`)
// Get sender initial balance const senderInfoBefore = await algod.accountInformation(sender.addr.toString()) printInfo(`Sender initial balance: ${formatMicroAlgo(senderInfoBefore.amount)}`)
// Create a new random account as receiver const receiver = algorand.account.random() printInfo(`Receiver address: ${shortenAddress(receiver.addr.toString())}`)
// Submit a payment transaction - this will create state changes (balance changes) const paymentAmount = algo(5) printInfo(`Sending ${paymentAmount.algo} ALGO to receiver...`)
const result = await algorand.send.payment({ sender: sender.addr, receiver: receiver.addr, amount: paymentAmount, })
const txId = result.txIds[0] const confirmedRound = result.confirmation.confirmedRound!
printSuccess(`Transaction confirmed!`) printInfo(`Transaction ID: ${txId}`) printInfo(`Confirmed in round: ${confirmedRound.toLocaleString('en-US')}`) printInfo('')
// Get balances after transaction const senderInfoAfter = await algod.accountInformation(sender.addr.toString()) const receiverInfo = await algod.accountInformation(receiver.addr.toString())
printInfo(`Sender balance after: ${formatMicroAlgo(senderInfoAfter.amount)}`) printInfo(`Receiver balance after: ${formatMicroAlgo(receiverInfo.amount)}`) printInfo('')
// ========================================================================= // Step 2: Demonstrate ledgerStateDelta(round) // ========================================================================= printStep(2, 'Getting ledger state delta for a round using ledgerStateDelta(round)')
printInfo('ledgerStateDelta(round) returns all state changes that occurred in a specific round.') printInfo('This includes account balance changes, app state changes, and more.') printInfo('')
try { const stateDelta = await algod.ledgerStateDelta(confirmedRound) printSuccess('Successfully retrieved state delta for the round!') printInfo('')
displayStateDelta(stateDelta, 'ledgerStateDelta') } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error) if (errorMessage.includes('not supported') || errorMessage.includes('not enabled') || errorMessage.includes('404')) { printError('ledgerStateDelta endpoint may not be enabled on this node.') printInfo('Node configuration may need EnableDeveloperAPI=true or specific delta tracking settings.') } else { printError(`Error getting state delta: ${errorMessage}`) } printInfo('') }
// ========================================================================= // Step 3: Demonstrate ledgerStateDeltaForTransactionGroup(txId) // ========================================================================= printStep(3, 'Getting state delta for a specific transaction group using ledgerStateDeltaForTransactionGroup(txId)')
printInfo('ledgerStateDeltaForTransactionGroup(txId) returns the state changes') printInfo('caused by a specific transaction group, identified by any transaction ID in the group.') printInfo('')
try { const txGroupDelta = await algod.ledgerStateDeltaForTransactionGroup(txId) printSuccess('Successfully retrieved state delta for the transaction group!') printInfo('')
displayStateDelta(txGroupDelta, 'ledgerStateDeltaForTransactionGroup') } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error) if (errorMessage.includes('tracer') || errorMessage.includes('501')) { printError('ledgerStateDeltaForTransactionGroup requires delta tracking to be enabled.') printInfo('This endpoint needs EnableDeveloperAPI=true AND EnableTxnEvalTracer=true in node config.') printInfo('On LocalNet, this may require custom configuration.') } else if (errorMessage.includes('not supported') || errorMessage.includes('not enabled') || errorMessage.includes('404')) { printError('ledgerStateDeltaForTransactionGroup endpoint may not be enabled on this node.') } else { printError(`Error getting transaction group delta: ${errorMessage}`) } printInfo('') }
// ========================================================================= // Step 4: Demonstrate transactionGroupLedgerStateDeltasForRound(round) // ========================================================================= printStep(4, 'Getting all transaction group deltas for a round using transactionGroupLedgerStateDeltasForRound(round)')
printInfo('transactionGroupLedgerStateDeltasForRound(round) returns deltas for ALL') printInfo('transaction groups that were included in a specific round.') printInfo('Each entry includes the delta and the transaction IDs in that group.') printInfo('')
try { const roundDeltas = await algod.transactionGroupLedgerStateDeltasForRound(confirmedRound) printSuccess('Successfully retrieved all transaction group deltas for the round!') printInfo('')
displayRoundDeltas(roundDeltas) } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error) if (errorMessage.includes('tracer') || errorMessage.includes('501')) { printError('transactionGroupLedgerStateDeltasForRound requires delta tracking to be enabled.') printInfo('This endpoint needs EnableDeveloperAPI=true AND EnableTxnEvalTracer=true in node config.') printInfo('On LocalNet, this may require custom configuration.') } else if (errorMessage.includes('not supported') || errorMessage.includes('not enabled') || errorMessage.includes('404')) { printError('transactionGroupLedgerStateDeltasForRound endpoint may not be enabled on this node.') } else { printError(`Error getting round deltas: ${errorMessage}`) } printInfo('') }
// ========================================================================= // Step 5: Submit more transactions to see different state changes // ========================================================================= printStep(5, 'Submitting additional transactions to demonstrate more state changes')
// Create another account and do a multi-transaction round const receiver2 = algorand.account.random() printInfo(`Created second receiver: ${shortenAddress(receiver2.addr.toString())}`)
// Send payments to create more activity const result2 = await algorand.send.payment({ sender: sender.addr, receiver: receiver2.addr, amount: algo(3), })
const confirmedRound2 = result2.confirmation.confirmedRound! printSuccess(`Second transaction confirmed in round ${confirmedRound2.toLocaleString('en-US')}`) printInfo('')
// Try to get deltas for this new round try { const roundDeltas2 = await algod.transactionGroupLedgerStateDeltasForRound(confirmedRound2) printSuccess(`Found ${roundDeltas2.deltas.length} transaction group(s) in round ${confirmedRound2.toLocaleString('en-US')}`)
for (let i = 0; i < roundDeltas2.deltas.length; i++) { const groupDelta = roundDeltas2.deltas[i] printInfo(`\n Transaction Group ${i + 1}:`) printInfo(` Transaction IDs: ${groupDelta.ids.length}`) for (const id of groupDelta.ids) { printInfo(` - ${id}`) }
// Show account changes summary const accounts = groupDelta.delta.accounts if (accounts.accounts && accounts.accounts.length > 0) { printInfo(` Accounts modified: ${accounts.accounts.length}`) } } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error) if (errorMessage.includes('tracer') || errorMessage.includes('501')) { printInfo('(Skipped - requires EnableTxnEvalTracer node configuration)') } else { printError(`Could not get deltas: ${errorMessage}`) } } printInfo('')
// ========================================================================= // Summary // ========================================================================= printHeader('Summary')
printInfo('This example demonstrated three ways to get ledger state deltas:') printInfo('') printInfo('1. ledgerStateDelta(round):') printInfo(' - Returns ALL state changes for a specific round') printInfo(' - Includes: account balances, app state, asset holdings') printInfo(' - LedgerStateDelta contains: accounts, block, totals, kvMods, txIds, creatables') printInfo('') printInfo('2. ledgerStateDeltaForTransactionGroup(txId):') printInfo(' - Returns state changes for a SPECIFIC transaction group') printInfo(' - Accepts any transaction ID from the group') printInfo(' - Useful for tracking changes from your transactions') printInfo('') printInfo('3. transactionGroupLedgerStateDeltasForRound(round):') printInfo(' - Returns deltas for ALL transaction groups in a round') printInfo(' - TransactionGroupLedgerStateDeltasForRoundResponse has deltas array') printInfo(' - Each LedgerStateDeltaForTransactionGroup has: delta, ids (transaction IDs)') printInfo('') printInfo('State Delta Structure (LedgerStateDelta):') printInfo(' accounts: LedgerAccountDeltas') printInfo(' - accounts?: LedgerBalanceRecord[] (address + microAlgos)') printInfo(' - appResources?: LedgerAppResourceRecord[]') printInfo(' - assetResources?: LedgerAssetResourceRecord[]') printInfo(' block: Block (block header information)') printInfo(' totals: LedgerAccountTotals (online, offline, notParticipating algo counts)') printInfo(' stateProofNext: bigint') printInfo(' prevTimestamp: bigint') printInfo(' kvMods?: Map<Uint8Array, LedgerKvValueDelta> (key-value store changes)') printInfo(' txIds?: Map<Uint8Array, LedgerIncludedTransactions>') printInfo(' creatables?: Map<number, LedgerModifiedCreatable>') printInfo('') printInfo('Note: Node configuration requirements:') printInfo(' - ledgerStateDelta(round) - Works with default LocalNet configuration') printInfo(' - ledgerStateDeltaForTransactionGroup(txId) - Requires EnableTxnEvalTracer=true') printInfo(' - transactionGroupLedgerStateDeltasForRound(round) - Requires EnableTxnEvalTracer=true') printInfo(' - All endpoints need EnableDeveloperAPI=true (enabled by default on LocalNet)')}
/** * Display details from a LedgerStateDelta */function displayStateDelta(delta: LedgerStateDelta, source: string): void { printInfo(`State Delta from ${source}:`) printInfo('')
// Block information printInfo(' Block Information:') printInfo(` Round: ${delta.block.header.round.toLocaleString('en-US')}`) printInfo(` Timestamp: ${new Date(Number(delta.block.header.timestamp) * 1000).toISOString()}`) printInfo(` Previous timestamp: ${delta.prevTimestamp.toLocaleString('en-US')}`) if (delta.block.header.proposer) { printInfo(` Proposer: ${shortenAddress(delta.block.header.proposer.toString())}`) } printInfo('')
// Account changes printInfo(' Account Changes (LedgerAccountDeltas):') const accounts = delta.accounts
if (accounts.accounts && accounts.accounts.length > 0) { printInfo(` Modified accounts: ${accounts.accounts.length}`) for (const record of accounts.accounts.slice(0, 5)) { // Show first 5 const baseData = record.accountData.accountBaseData printInfo(` - ${shortenAddress(record.address.toString())}`) printInfo(` Balance: ${formatMicroAlgo(baseData.microAlgos)}`) printInfo(` Status: ${getAccountStatus(baseData.status)}`) } if (accounts.accounts.length > 5) { printInfo(` ... and ${accounts.accounts.length - 5} more`) } } else { printInfo(' No account balance changes') }
if (accounts.appResources && accounts.appResources.length > 0) { printInfo(` App resources modified: ${accounts.appResources.length}`) for (const app of accounts.appResources.slice(0, 3)) { printInfo(` - App ${app.appId}: ${app.params.deleted ? 'DELETED' : 'MODIFIED'}`) } }
if (accounts.assetResources && accounts.assetResources.length > 0) { printInfo(` Asset resources modified: ${accounts.assetResources.length}`) for (const asset of accounts.assetResources.slice(0, 3)) { printInfo(` - Asset ${asset.assetId}: ${asset.params.deleted ? 'DELETED' : 'MODIFIED'}`) } } printInfo('')
// Totals printInfo(' Account Totals (LedgerAccountTotals):') printInfo(` Online money: ${formatMicroAlgo(delta.totals.online.money)}`) printInfo(` Offline money: ${formatMicroAlgo(delta.totals.offline.money)}`) printInfo(` Not participating: ${formatMicroAlgo(delta.totals.notParticipating.money)}`) printInfo(` Rewards level: ${delta.totals.rewardsLevel.toLocaleString('en-US')}`) printInfo('')
// KV mods if (delta.kvMods && delta.kvMods.size > 0) { printInfo(` KV Store Modifications: ${delta.kvMods.size} keys changed`) }
// Transaction IDs if (delta.txIds && delta.txIds.size > 0) { printInfo(` Transactions included: ${delta.txIds.size}`) }
// Creatables if (delta.creatables && delta.creatables.size > 0) { printInfo(` Creatables modified: ${delta.creatables.size}`) delta.creatables.forEach((creatable, id) => { const type = creatable.creatableType === 0 ? 'Asset' : 'Application' const action = creatable.created ? 'CREATED' : 'DELETED' printInfo(` - ${type} ${id}: ${action} by ${shortenAddress(creatable.creator.toString())}`) }) } printInfo('')}
/** * Display details from a TransactionGroupLedgerStateDeltasForRoundResponse */function displayRoundDeltas(response: TransactionGroupLedgerStateDeltasForRoundResponse): void { printInfo('Transaction Group Deltas for Round:') printInfo(` Total transaction groups: ${response.deltas.length}`) printInfo('')
for (let i = 0; i < response.deltas.length; i++) { const groupDelta = response.deltas[i] printInfo(` Group ${i + 1}:`) printInfo(` Transaction IDs in group: ${groupDelta.ids.length}`)
for (const txId of groupDelta.ids) { printInfo(` - ${txId}`) }
// Summary of delta const accounts = groupDelta.delta.accounts const modifiedCount = accounts.accounts?.length ?? 0 const appCount = accounts.appResources?.length ?? 0 const assetCount = accounts.assetResources?.length ?? 0
printInfo(` State changes:`) printInfo(` - Accounts modified: ${modifiedCount}`) if (appCount > 0) { printInfo(` - App resources: ${appCount}`) } if (assetCount > 0) { printInfo(` - Asset resources: ${assetCount}`) } printInfo('') }}
/** * Convert an account status number to a string */function getAccountStatus(status: number): string { switch (status) { case 0: return 'Offline' case 1: return 'Online' case 2: return 'NotParticipating' default: return `Unknown (${status})` }}
main().catch((error) => { console.error('Fatal error:', error) process.exit(1)})Other examples in Algod Client
Section titled “Other examples in Algod Client”- Node Health and Status
- Version and Genesis Information
- Ledger Supply Information
- Account Information
- Transaction Parameters
- Send and Confirm Transaction
- Pending Transactions
- Block Data
- Asset Information
- Application Information
- Application Boxes
- TEAL Compile and Disassemble
- Transaction Simulation
- Ledger State Deltas
- Transaction Proof
- Light Block Header Proof
- State Proof
- DevMode Timestamp Offset
- Sync Round Management