Address Lookup Tables for Solana Transactions
Learn when Solana Address Lookup Tables help, what they do not solve, and how to debug versioned transactions in Carbium swap and RPC flows.
Address Lookup Tables for Solana Transactions
Address Lookup Tables, often called ALTs or lookup tables, help Solana transactions reference more accounts without putting every 32-byte address directly in the transaction message.
Use this page when a swap, bot, wallet, or backend flow is running into transaction-size pressure, too many accounts, or confusing VersionedTransaction behavior. The goal is not to make every transaction use ALTs. The goal is to know when they are the right tool and when the real issue is route shape, signing, freshness, or execution.
Part of the Carbium Solana infrastructure stack.
The Short Rule
Use Address Lookup Tables when the transaction needs many non-signer accounts and a normal transaction message is becoming too large or account-heavy.
Do not use ALTs as a generic fix for every failed transaction.
| If the problem is... | ALT helps? | Better first check |
|---|---|---|
| Too many account addresses in the transaction message | Usually | Use a v0 versioned transaction with lookup tables |
| Transaction exceeds message-size limits | Often | Reduce unnecessary instructions first, then consider ALTs |
| A required signer is missing | No | Fix the signer boundary |
| A recent blockhash expired | No | Rebuild and re-sign with a fresh blockhash |
| A route cannot be found | No | Debug quote and routing behavior |
| Simulation fails inside a program | Maybe not | Read simulation logs and instruction index first |
The official Solana docs describe ALTs as a way to compress account references: after addresses are stored in a lookup table, a transaction can reference them by 1-byte indexes instead of full 32-byte addresses. Solana's current guide also frames ALTs as a way to raise the practical account-address ceiling from about 32 addresses to 64 addresses in one transaction.
What ALTs Actually Change
Every Solana instruction needs accounts. Without lookup tables, those accounts are carried directly in the transaction message. That is simple, but it creates pressure when a transaction touches many pools, token accounts, program accounts, or routing accounts.
An Address Lookup Table moves some account addresses into an on-chain table. A v0 versioned transaction can then reference those addresses by table index.
That changes the wire format. It does not change what the runtime sees after account resolution.
| Layer | What changes |
|---|---|
| Transaction message | Some addresses are referenced through lookup-table indexes |
| Runtime execution | Resolved accounts are still full Solana addresses |
| Program logic | Programs do not become ALT-aware just because the transaction used ALTs |
| Signing | ALT-resolved accounts cannot be signers |
This is why ALTs are useful for large account lists, but not for custody, authorization, blockhash expiry, or program-specific failures.
Address Lookup Tables only help v0 versioned transactions. Legacy transactions cannot use lookup tables to resolve extra accounts during execution.
When They Matter In Carbium Flows
Most Carbium integrators meet ALTs indirectly through versioned transactions.
For example, Carbium Swap API can return a base64 txn payload when a Q1 request includes user_account. Your app then deserializes, signs, submits, and confirms that transaction. The transaction may already be shaped by the route and account set returned by the quote flow.
In that kind of flow, do not mutate the transaction casually after quote generation. Treat the returned transaction as the execution artifact for that quote.
| Carbium flow | ALT relevance |
|---|---|
| Simple RPC reads | Usually none |
| Q1 executable swap quotes | May appear through returned versioned transactions |
| Multi-hop or account-heavy swap routes | Can reduce transaction message pressure |
| Custom backend transaction builders | Useful when the app controls instruction assembly |
| Jito bundle handoff | Bundle submission still requires correctly built and signed transactions |
Use Executing Swaps for the full quote-to-confirm flow and Transaction Lifecycle for the stage map.
The Safe Decision Flow
Before adding a lookup table, identify the failure boundary.
flowchart TD
A["Transaction path failed"] --> B{"Where did it fail?"}
B -->|"Quote or route"| C["Use Swap API Errors Reference"]
B -->|"Message too large or too many accounts"| D["Consider v0 + Address Lookup Tables"]
B -->|"Signing"| E["Check signer boundary"]
B -->|"Simulation"| F["Read logs and instruction index"]
B -->|"Blockhash / expiry"| G["Rebuild with fresh blockhash"]
B -->|"Submitted but unclear"| H["Check signature status"]
ALTs belong mainly on the "message too large or too many accounts" branch.
If simulation says the program rejected the transaction, a lookup table probably will not help. If the transaction expired during wallet approval, a lookup table will not make the old signature fresh. If a swap route is missing, a lookup table will not create liquidity.
Implementation Shape
When your app owns transaction construction, the high-level build path looks like this:
- Create or reuse a lookup table that contains stable non-signer accounts your flow needs.
- Fetch the lookup table account before building the transaction.
- Compile the message as a v0 versioned message with that lookup table.
- Create a
VersionedTransaction. - Sign and submit normally.
In @solana/web3.js, the core shape is:
import {
AddressLookupTableAccount,
Connection,
PublicKey,
TransactionInstruction,
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
const connection = new Connection(
"https://rpc.carbium.io/?apiKey=" + process.env.CARBIUM_RPC_KEY,
"confirmed"
);
async function buildV0Transaction(input: {
payer: PublicKey;
lookupTableAddress: PublicKey;
instructions: TransactionInstruction[];
}) {
const { value: lookupTable } = await connection.getAddressLookupTable(
input.lookupTableAddress
);
if (!lookupTable) {
throw new Error("Address lookup table was not found");
}
const { blockhash } = await connection.getLatestBlockhash("confirmed");
const message = new TransactionMessage({
payerKey: input.payer,
recentBlockhash: blockhash,
instructions: input.instructions,
}).compileToV0Message([lookupTable as AddressLookupTableAccount]);
return new VersionedTransaction(message);
}Keep the RPC key server-side. Lookup-table reads, simulation, send, and confirmation are all infrastructure operations that should not expose authenticated endpoints in browser code.
If your app receives a transaction payload from Carbium Swap API, sign and relay that payload instead of rebuilding it unless your integration explicitly owns transaction construction.
Common Mistakes
Treating ALTs As A Retry Strategy
ALTs change how accounts are referenced. They do not refresh blockhashes, adjust slippage, fix user rejection, or make a stale signed transaction valid again.
If a transaction aged out, use Blockhash Expiry Recovery Playbook.
Putting Signers In Lookup Tables
ALT-resolved accounts can be writable or read-only, but they cannot be signers. Required signers still need to be present in the signed transaction message.
If the error points to missing signatures or signature verification, debug signing before lookup-table design.
Using A Lookup Table Before It Is Ready
Lookup tables are on-chain accounts. Your transaction builder should fetch the lookup table account and fail clearly if it is missing, empty, stale, or not the table your flow expects.
Do not hide lookup-table fetch failures behind a generic "transaction failed" message.
Solving A Route Problem At The Transaction Layer
If a swap quote cannot find a route, the issue is upstream of transaction construction. Check pair, size, liquidity, request family, and route availability before changing transaction assembly.
Use Supported DEXs and Routing Sources and Swap API Errors Reference for route-stage failures.
Debug Checklist
When a versioned transaction or ALT-backed flow breaks, preserve these facts:
- transaction version: legacy or v0
- whether lookup table accounts were fetched successfully
- lookup table address or addresses used
- number of inline account keys vs lookup-table accounts
- signer list and signing boundary
- recent blockhash or validity window
- simulation
err, logs, and instruction index - submitted signature, if the transaction reached RPC submission
That evidence tells you whether the issue belongs to account compression, signing, freshness, simulation, or confirmation.
Use ALTs for account-list pressure, not as a mystery fix. Start with the transaction stage map, keep signing and RPC keys backend-side, and build on Carbium when you need the full quote, submit, and confirm path.
