Solana Basics

Solana Transaction Fundamentals

Last updated September 21, 2026

A comprehensive guide to understanding how Solana transactions work from structure to confirmation.

What You'll Learn

  • The anatomy of a Solana transaction
  • How to sign and send transactions
  • Transaction confirmation and finality
  • Versioned transactions vs legacy
  • Common transaction errors and their meanings

Prerequisites

Transaction Anatomy

A Solana transaction consists of several components:

┌─────────────────────────────────────────────────────────────┐
│ Transaction │
├─────────────────────────────────────────────────────────────┤
│ Signatures: [sig1, sig2, ...] │
│ │
│ Message: │
│ ├── Header │
│ │ ├── num_required_signatures │
│ │ ├── num_readonly_signed_accounts │
│ │ └── num_readonly_unsigned_accounts │
│ │ │
│ ├── Account Keys: [pubkey1, pubkey2, ...] │
│ │ │
│ ├── Recent Blockhash │
│ │ │
│ └── Instructions: [ │
│ { program_id_index, accounts, data }, │
│ { program_id_index, accounts, data }, │
│ ] │
└─────────────────────────────────────────────────────────────┘

Key Components

ComponentDescription
SignaturesEd25519 signatures from required signers
Recent BlockhashA recent block hash (valid for ~60-90 seconds)
InstructionsThe operations to perform
Account KeysAll accounts involved in the transaction

Instructions

Instructions are the actual operations in a transaction. Each instruction specifies:

  • Program ID - Which program to execute
  • Accounts - Which accounts the program needs
  • Data - Serialized arguments for the program
Instruction:
├── program_id: 11111111111111111111111111111111 (System Program)
├── accounts:
│ ├── sender (signer, writable)
│ └── recipient (not signer, writable)
└── data: [encoded transfer amount]

Multiple Instructions

Transactions can contain multiple instructions that execute atomically:

import { transactionBuilder } from '@metaplex-foundation/umi'
// All instructions succeed or all fail (atomic)
const builder = transactionBuilder()
.add(createAccountInstruction)
.add(initializeMintInstruction)
.add(mintTokensInstruction)
await builder.sendAndConfirm(umi)

This atomicity is powerful. If any instruction fails, the entire transaction is reverted.

Recent Blockhash

Every transaction requires a recent blockhash that:

  • Proves the transaction was created recently
  • Prevents replay attacks
  • Expires after ~60-90 seconds (~150 slots)
// UMI handles blockhash automatically when sending transactions.
// To fetch it manually:
const { blockhash, lastValidBlockHeight } = await umi.rpc.getLatestBlockhash()

Blockhash Expiration

If your transaction isn't confirmed before the blockhash expires, it will be dropped. For long-running operations, fetch a fresh blockhash before sending.

Signing Transactions

Transactions must be signed by all accounts marked as isSigner:

// UMI signs automatically with the identity signer when sending.
// For additional signers, pass them during the build:
const tx = await myBuilder
.setBlockhash(await umi.rpc.getLatestBlockhash())
.buildAndSign(umi)
// For partial signing (multi-sig workflows), you can sign
// and serialize the transaction, then pass it to another party:
import { base64 } from '@metaplex-foundation/umi/serializers'
const serialized = umi.transactions.serialize(tx)
const encoded = base64.deserialize(serialized)[0]
// ... send encoded string to another party for additional signing ...

Sending Transactions

Basic Send

// Send and wait for confirmation (recommended)
const result = await myBuilder.sendAndConfirm(umi)
// Or just send without waiting
const signature = await myBuilder.send(umi)

Send with Options

const result = await myBuilder.sendAndConfirm(umi, {
send: { skipPreflight: false },
confirm: { commitment: 'confirmed' },
})

Transaction Confirmation

Solana has multiple commitment levels indicating transaction finality:

CommitmentDescriptionUse Case
processedTransaction received by leaderReal-time updates
confirmedVoted on by supermajorityMost applications
finalized31+ blocks deep, irreversibleFinancial operations

Checking Confirmation

// sendAndConfirm waits for confirmation automatically.
// To check a signature status manually:
const result = await umi.rpc.getSignatureStatuses([signature])

Commitment in Practice

// For most operations, 'confirmed' is the right default
const result = await myBuilder.sendAndConfirm(umi, {
confirm: { commitment: 'confirmed' },
})
// For financial operations where you need full finality
const result = await myBuilder.sendAndConfirm(umi, {
confirm: { commitment: 'finalized' },
})

Versioned Transactions

Solana supports three transaction formats:

Legacy Transactions

Legacy transactions use Solana's original transaction format without Address Lookup Tables.

  • Original format
  • Limited to 35 accounts
  • Simpler structure

V0 Transactions

V0 transactions add Address Lookup Table support for transactions that need more accounts.

  • Support Address Lookup Tables (ALTs)
  • Can reference up to 256 accounts
  • Required for complex DeFi operations

V1 Transactions

V1 transactions increase the transaction size limit and store compute configuration in the message.

  • Support transactions up to 4,096 bytes
  • Store compute budget configuration in the transaction message
  • Do not support Address Lookup Tables
// Umi uses V0 transactions by default. Opt in to V1 explicitly.
const result = await myBuilder
.useV1()
.sendAndConfirm(umi)
// To use legacy transactions instead
const result = await myBuilder
.useLegacyVersion()
.sendAndConfirm(umi)
// With Address Lookup Tables
import { createLut } from '@metaplex-foundation/mpl-toolbox'
const [lutBuilder, lut] = createLut(umi, {
recentSlot: await umi.rpc.getSlot({ commitment: 'finalized' }),
addresses: [addressA, addressB, addressC],
})
await lutBuilder.sendAndConfirm(umi)
// Address Lookup Tables require V0.
await myBuilder
.useV0()
.setAddressLookupTables([lut])
.sendAndConfirm(umi)

Choosing a Transaction Version

  • Use V1 for transactions larger than 1,232 bytes when the wallet supports transaction version 1.
  • Use V0 when the transaction requires an Address Lookup Table.
  • Use legacy transactions only when compatibility requires the original format.

Umi defaults to V0 for backward compatibility. See Migrating from V0 to V1 Transactions before changing the application-wide default.

Transaction Size Limits

Solana transactions have strict size limits:

LimitValue
Legacy and V0 transaction size1,232 bytes
V1 transaction size4,096 bytes
Address Lookup TablesV0 only
Maximum instructionsLimited by size

Dealing with Size Limits

If your transaction is too large:

  1. Use V1 - Increase the size limit to 4,096 bytes when no Address Lookup Table is required
  2. Use Address Lookup Tables with V0 - Compress account references
  3. Split into multiple transactions - Execute sequentially
  4. Optimize instruction data - Minimize serialized data

Simulation

Before sending, simulate transactions to catch errors:

// Build the transaction without sending
const tx = await myBuilder
.setBlockhash(await umi.rpc.getLatestBlockhash())
.buildAndSign(umi)
// Simulate it
const simulation = await umi.rpc.simulateTransaction(tx, {
commitment: 'confirmed',
})
console.log('Simulation result:', simulation)

Simulation helps you:

  • Catch errors before paying fees
  • Estimate compute units
  • Debug program logic

Common Transaction Errors

"Blockhash not found"

Cause: The blockhash expired before confirmation.

Solutions:

  1. Retry with a fresh blockhash (UMI fetches a new blockhash automatically on each send)
  2. Use 'finalized' commitment for blockhash when network is congested
  3. Implement retry logic in your application

"Insufficient funds"

Cause: Account doesn't have enough SOL for transaction fees + rent.

Solution: Ensure the fee payer has sufficient balance:

solana balance
solana airdrop 1 # On devnet

"Transaction simulation failed"

Cause: Program logic error.

Solution: Check simulation logs on an explorer (see Using Solana Explorers), or simulate the transaction before sending to inspect the error output.

"Account not found"

Cause: An account in the transaction doesn't exist.

Solution: Create the account first or check addresses.

"Invalid account owner"

Cause: Account is owned by a different program than expected.

Solution: Verify account ownership matches the program you're calling.

Practical Example: Complete Flow

import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
import { transferSol } from '@metaplex-foundation/mpl-toolbox'
import { signerIdentity, generateSigner, sol, publicKey } from '@metaplex-foundation/umi'
import { base58 } from '@metaplex-foundation/umi/serializers'
// 1. Create UMI instance
const umi = createUmi('https://api.devnet.solana.com')
// 2. Set up signer (your wallet)
const signer = generateSigner(umi)
umi.use(signerIdentity(signer))
// 3. Build and send the transaction
const result = await transferSol(umi, {
source: umi.identity,
destination: publicKey('RecipientAddressHere...'),
amount: sol(1),
}).sendAndConfirm(umi)
// 4. Get the transaction signature
const signature = base58.deserialize(result.signature)[0]
console.log('Transaction confirmed:', signature)
console.log(`Explorer: https://explorer.solana.com/tx/${signature}?cluster=devnet`)

Next Steps

FAQ

How long do I have to confirm a transaction?

A transaction's blockhash is valid for approximately 60-90 seconds (~150 slots). After that, the transaction will be dropped if not confirmed.

Can I cancel a transaction?

No, once submitted, you cannot cancel a transaction. However, if it hasn't been confirmed, you can submit a new transaction with the same nonce (using durable nonces) to effectively "replace" it.

What's the difference between "processed" and "confirmed"?

"Processed" means a validator received it. "Confirmed" means a supermajority (66%+) of validators voted on the block containing it. Always use "confirmed" or "finalized" for important operations.

Why did my transaction fail after simulation succeeded?

State can change between simulation and execution. Another transaction may have modified the accounts. This is common in competitive scenarios like NFT mints.