Skip to content

Block Data

← Back to Algod Client

This example demonstrates how to retrieve block information using the AlgodClient methods: block(), blockHash(), and blockTxIds().

  • LocalNet running (via algokit localnet start)

From the repository root:

Terminal window
cd examples
npm run example algod_client/08-block-data.ts

View source on GitHub

08-block-data.ts
/**
* Example: Block Data
*
* This example demonstrates how to retrieve block information using
* the AlgodClient methods: block(), blockHash(), and blockTxIds().
*
* Prerequisites:
* - LocalNet running (via `algokit localnet start`)
*/
import { algo } from '@algorandfoundation/algokit-utils'
import { createAlgodClient, createAlgorandClient, formatMicroAlgo, getFundedAccount, printHeader, printInfo, printStep, printSuccess, shortenAddress } from '../shared/utils.js'
/**
* Format a Unix timestamp to a human-readable date string
*/
function formatTimestamp(timestamp: bigint): string {
const date = new Date(Number(timestamp) * 1000)
return date.toISOString()
}
/**
* Format bytes as a hex string
*/
function bytesToHex(bytes: Uint8Array): string {
return Buffer.from(bytes).toString('hex')
}
async function main() {
printHeader('Block Data Example')
// Create an Algod client connected to LocalNet
const algod = createAlgodClient()
const algorand = createAlgorandClient()
// =========================================================================
// Step 1: Get the latest round from status()
// =========================================================================
printStep(1, 'Getting current node status to find the latest round')
const nodeStatus = await algod.status()
let latestRound = nodeStatus.lastRound
printSuccess(`Current node status retrieved`)
printInfo(` - Latest round: ${latestRound}`)
// On LocalNet dev mode, lastRound may be 0 if no transactions have been submitted
// Let's submit a transaction to create a block with transactions
if (latestRound === 0n) {
printInfo('Round is 0 (LocalNet dev mode). Submitting a transaction to create a block...')
const sender = await getFundedAccount(algorand)
const receiver = algorand.account.random()
await algorand.send.payment({
sender: sender.addr,
receiver: receiver.addr,
amount: algo(1), // 1 ALGO
})
// Get updated status
const updatedStatus = await algod.status()
latestRound = updatedStatus.lastRound
printSuccess(`Transaction submitted. New latest round: ${latestRound}`)
}
// =========================================================================
// Step 2: Get full block data with block(round)
// =========================================================================
printStep(2, `Getting full block data for round ${latestRound} with block()`)
const blockResponse = await algod.block(latestRound)
const block = blockResponse.block
printSuccess('Block data retrieved successfully!')
printInfo('')
printInfo('Block Header Fields:')
printInfo(` - Round: ${block.header.round}`)
printInfo(` - Timestamp: ${formatTimestamp(block.header.timestamp)} (${block.header.timestamp})`)
printInfo(` - Previous Block Hash: ${bytesToHex(block.header.previousBlockHash).substring(0, 16)}...`)
printInfo(` - Seed: ${bytesToHex(block.header.seed).substring(0, 16)}...`)
printInfo(` - Genesis ID: ${block.header.genesisId}`)
printInfo(` - Genesis Hash: ${bytesToHex(block.header.genesisHash).substring(0, 16)}...`)
if (block.header.proposer) {
printInfo(` - Proposer: ${shortenAddress(block.header.proposer.toString())}`)
}
if (block.header.feesCollected !== undefined) {
printInfo(` - Fees Collected: ${formatMicroAlgo(block.header.feesCollected)}`)
}
if (block.header.txnCounter !== undefined) {
printInfo(` - Transaction Counter: ${block.header.txnCounter}`)
}
// Display transaction commitment hashes
printInfo('')
printInfo('Transaction Commitments:')
printInfo(` - SHA512/256: ${bytesToHex(block.header.txnCommitments.nativeSha512_256Commitment).substring(0, 32)}...`)
if (block.header.txnCommitments.sha256Commitment) {
printInfo(` - SHA256: ${bytesToHex(block.header.txnCommitments.sha256Commitment).substring(0, 32)}...`)
}
// Display reward state
printInfo('')
printInfo('Reward State:')
printInfo(` - Fee Sink: ${shortenAddress(block.header.rewardState.feeSink.toString())}`)
printInfo(` - Rewards Pool: ${shortenAddress(block.header.rewardState.rewardsPool.toString())}`)
printInfo(` - Rewards Level: ${block.header.rewardState.rewardsLevel}`)
printInfo(` - Rewards Rate: ${block.header.rewardState.rewardsRate}`)
// Display upgrade state
printInfo('')
printInfo('Upgrade State:')
printInfo(` - Current Protocol: ${block.header.upgradeState.currentProtocol}`)
if (block.header.upgradeState.nextProtocol) {
printInfo(` - Next Protocol: ${block.header.upgradeState.nextProtocol}`)
}
// =========================================================================
// Step 3: Explore transactions in the block (payset)
// =========================================================================
printStep(3, 'Exploring transactions in the block (payset)')
const transactions = block.payset
printInfo(`Number of transactions in block: ${transactions.length}`)
if (transactions.length > 0) {
printInfo('')
printInfo('Transaction Details:')
for (let i = 0; i < Math.min(transactions.length, 5); i++) {
const txnInBlock = transactions[i]
const signedTxn = txnInBlock.signedTxn.signedTxn
const txn = signedTxn.txn
printInfo('')
printInfo(` Transaction ${i + 1}:`)
printInfo(` - Type: ${txn.type}`)
printInfo(` - Sender: ${shortenAddress(txn.sender.toString())}`)
printInfo(` - Fee: ${formatMicroAlgo(txn.fee ?? 0n)}`)
printInfo(` - First Valid: ${txn.firstValid}`)
printInfo(` - Last Valid: ${txn.lastValid}`)
// Show payment-specific fields
if (txn.type === 'pay' && txn.payment) {
printInfo(` - Receiver: ${shortenAddress(txn.payment.receiver.toString())}`)
printInfo(` - Amount: ${formatMicroAlgo(txn.payment.amount)}`)
}
// Show apply data if available
const applyData = txnInBlock.signedTxn.applyData
if (applyData) {
if (applyData.senderRewards !== undefined && applyData.senderRewards > 0n) {
printInfo(` - Sender Rewards: ${formatMicroAlgo(applyData.senderRewards)}`)
}
if (applyData.receiverRewards !== undefined && applyData.receiverRewards > 0n) {
printInfo(` - Receiver Rewards: ${formatMicroAlgo(applyData.receiverRewards)}`)
}
}
// Show hasGenesisId and hasGenesisHash flags
printInfo(` - Has Genesis ID: ${txnInBlock.hasGenesisId ?? 'not set'}`)
printInfo(` - Has Genesis Hash: ${txnInBlock.hasGenesisHash ?? 'not set'}`)
}
if (transactions.length > 5) {
printInfo(` ... and ${transactions.length - 5} more transactions`)
}
} else {
printInfo('This block contains no transactions.')
printInfo('On LocalNet in dev mode, blocks are only created when transactions are submitted.')
}
// =========================================================================
// Step 4: Get block hash with blockHash(round)
// =========================================================================
printStep(4, `Getting block hash for round ${latestRound} with blockHash()`)
const blockHashResponse = await algod.blockHash(latestRound)
printSuccess('Block hash retrieved successfully!')
printInfo(` - Block Hash: ${blockHashResponse.blockHash}`)
printInfo('The block hash is a base64-encoded SHA256 hash of the block header.')
printInfo('It uniquely identifies this block and is used for cryptographic verification.')
// =========================================================================
// Step 5: Get transaction IDs in block with blockTxIds(round)
// =========================================================================
printStep(5, `Getting transaction IDs for round ${latestRound} with blockTxIds()`)
const blockTxIdsResponse = await algod.blockTxIds(latestRound)
const txIds = blockTxIdsResponse.blockTxIds
printSuccess(`Transaction IDs retrieved successfully!`)
printInfo(` - Number of transactions: ${txIds.length}`)
if (txIds.length > 0) {
printInfo('')
printInfo('Transaction IDs:')
for (let i = 0; i < Math.min(txIds.length, 5); i++) {
printInfo(` ${i + 1}. ${txIds[i]}`)
}
if (txIds.length > 5) {
printInfo(` ... and ${txIds.length - 5} more`)
}
printInfo('Transaction IDs can be used with pendingTransactionInformation() to get details.')
} else {
printInfo('This block contains no transactions.')
}
// =========================================================================
// Step 6: Demonstrate header-only mode
// =========================================================================
printStep(6, 'Getting block header only (without transactions)')
const headerOnlyResponse = await algod.block(latestRound, { headerOnly: true })
printSuccess('Block header retrieved (header-only mode)!')
printInfo(` - Round: ${headerOnlyResponse.block.header.round}`)
printInfo(` - Timestamp: ${formatTimestamp(headerOnlyResponse.block.header.timestamp)}`)
printInfo(` - Transactions in payset: ${headerOnlyResponse.block.payset.length}`)
printInfo('With headerOnly=true, the payset is empty, reducing response size.')
printInfo('Use this when you only need block metadata, not transaction details.')
// =========================================================================
// Step 7: Compare blocks across rounds (if multiple rounds exist)
// =========================================================================
if (latestRound > 1n) {
printStep(7, 'Comparing blocks across multiple rounds')
const previousRound = latestRound - 1n
const previousBlockResponse = await algod.block(previousRound)
const previousBlock = previousBlockResponse.block
printInfo('Comparing consecutive blocks:')
printInfo('')
printInfo(` Round ${previousRound}:`)
printInfo(` - Timestamp: ${formatTimestamp(previousBlock.header.timestamp)}`)
printInfo(` - Transactions: ${previousBlock.payset.length}`)
printInfo(` - Block Hash: ${bytesToHex(previousBlock.header.txnCommitments.nativeSha512_256Commitment).substring(0, 16)}...`)
printInfo('')
printInfo(` Round ${latestRound}:`)
printInfo(` - Timestamp: ${formatTimestamp(block.header.timestamp)}`)
printInfo(` - Transactions: ${block.payset.length}`)
printInfo(` - Previous Block Hash: ${bytesToHex(block.header.previousBlockHash).substring(0, 16)}...`)
// Verify chain linkage
printInfo('Each block contains the hash of the previous block, forming a chain.')
}
// =========================================================================
// Summary
// =========================================================================
printHeader('Summary')
printInfo('This example demonstrated:')
printInfo(' 1. status() - Get the latest round number')
printInfo(' 2. block(round) - Get full block data including header and transactions')
printInfo(' 3. blockHash(round) - Get just the block hash for verification')
printInfo(' 4. blockTxIds(round) - Get transaction IDs without full transaction data')
printInfo(' 5. block(round, { headerOnly: true }) - Get header without transactions')
printInfo('')
printInfo('Key block structure:')
printInfo(' - BlockResponse.block.header - Block metadata (round, timestamp, hashes, etc.)')
printInfo(' - BlockResponse.block.payset - Array of SignedTxnInBlock')
printInfo(' - BlockHashResponse.blockHash - Base64-encoded block hash')
printInfo(' - BlockTxidsResponse.blockTxIds - Array of transaction IDs')
printInfo('')
printInfo('Important header fields:')
printInfo(' - round: Block number')
printInfo(' - timestamp: Unix timestamp (seconds since epoch)')
printInfo(' - previousBlockHash: Links to prior block (chain integrity)')
printInfo(' - seed: VRF seed for sortition')
printInfo(' - txnCommitments: Merkle root of transactions')
printInfo(' - rewardState: Fee sink, rewards pool, and reward rates')
printInfo(' - upgradeState: Current and pending protocol versions')
}
main().catch((error) => {
console.error('Fatal error:', error)
process.exit(1)
})