Transaction Confirmation

How to reliably confirm Solana transactions using lastValidBlockHeight.

Confirming Transactions

Every transaction-building endpoint returns lastValidBlockHeight alongside the transaction. Use it to detect transaction expiry without indefinite polling.

import { Connection, VersionedTransaction } from '@solana/web3.js'
import bs58 from 'bs58'

const connection = new Connection('https://api.mainnet-beta.solana.com', 'confirmed')

const { transaction, lastValidBlockHeight } = await buildTx(...)
const tx = VersionedTransaction.deserialize(bs58.decode(transaction))
const signed = await wallet.signTransaction(tx)

const sig = await connection.sendRawTransaction(signed.serialize(), {
  skipPreflight: false,
  preflightCommitment: 'confirmed',
})

// Wait with expiry detection
const result = await connection.confirmTransaction(
  { signature: sig, lastValidBlockHeight, blockhash: tx.message.recentBlockhash },
  'confirmed',
)

if (result.value.err) {
  throw new Error(`Transaction failed: ${JSON.stringify(result.value.err)}`)
}
console.log('Confirmed:', sig)

Handling Expiry

A Solana transaction expires after ~90 seconds if not included in a block. If confirmTransaction returns with TransactionExpiredBlockheightExceededError, the transaction was never executed and it is safe to retry.

import { TransactionExpiredBlockheightExceededError } from '@solana/web3.js'

try {
  await connection.confirmTransaction({ signature, lastValidBlockHeight, blockhash }, 'confirmed')
} catch (err) {
  if (err instanceof TransactionExpiredBlockheightExceededError) {
    // Safe to retry — transaction was never executed
    console.warn('Transaction expired, rebuilding...')
    // Call the API again to get a fresh transaction
  } else {
    throw err
  }
}

Retry Strategy

  1. Build transaction via API → get transaction + lastValidBlockHeight
  2. Sign and submit
  3. Confirm with expiry detection
  4. If expired: rebuild from API (new blockhash), sign, submit
  5. If confirmed but on-chain error: inspect error, fix parameters, do not retry blindly

Do not resubmit the same signed transaction after expiry — always rebuild via the API to get a fresh blockhash.