Using Address Lookup Tables in Solana: A Comprehensive Guide

Shubh Kesharwani
Coinmonks
4 min readNov 18, 2023

--

Using Address Lookup Tables in Solana
Using Address Lookup Tables in Solana

Introduction

Address Lookup Tables (ALTs), also known as “lookup tables,” empower developers to efficiently load multiple addresses within a single transaction on the Solana blockchain. This guide aims to provide a step-by-step tutorial for developers with an intermediate to advanced understanding of Solana development. The tutorial includes code examples and snippets, allowing developers to grasp the concepts and implement them in their projects. Additionally, a GitHub repository with an example codebase is provided for a hands-on experience.

Benefits of Address Lookup Tables

Increasing Transaction Efficiency

In Solana, each transaction requires listing every address involved, with a typical limit of 32 addresses per transaction. Address Lookup Tables elevate this limit to 256 addresses per transaction, enabling developers to handle more data within a single transaction.

Compressing On-chain Addresses

After storing addresses on-chain within a lookup table, each address can be referenced by its 1-byte index within the table instead of the full 32-byte address. This compression allows storing up to 256 addresses in a single lookup table, optimizing on-chain storage.

Versioned Transactions

To utilize an Address Lookup Table within a transaction, developers must use v0 transactions, introduced with the new Versioned Transaction format.

Creating an Address Lookup Table

Creating a new lookup table with the @solana/web3.js library involves a few steps. Below is an example using TypeScript:

const web3 = require("@solana/web3.js");

// Connect to a Solana cluster and get the current slot
const connection = new web3.Connection(web3.clusterApiUrl("devnet"));
const slot = await connection.getSlot();

// Assumption: 'payer' is a valid 'Keypair' with enough SOL to pay for the execution

const [lookupTableInst, lookupTableAddress] =
web3.AddressLookupTableProgram.createLookupTable({
authority: payer.publicKey,
payer: payer.publicKey,
recentSlot: slot,
});

console.log("Lookup table address:", lookupTableAddress.toBase58());

// To create the Address Lookup Table on-chain:
// Send the 'lookupTableInst' instruction in a transaction

Note: Address lookup tables can be created with either a v0 transaction or a legacy transaction, but the Solana runtime can only retrieve additional addresses within a lookup table when using v0 Versioned Transactions.

Adding Addresses to a Lookup Table

Adding addresses to a lookup table, known as “extending,” is done using the @solana/web3.js library. Here's an example:

// Add addresses to the 'lookupTableAddress' table via an 'extend' instruction
const extendInstruction = web3.AddressLookupTableProgram.extendLookupTable({
payer: payer.publicKey,
authority: payer.publicKey,
lookupTable: lookupTableAddress,
addresses: [
payer.publicKey,
web3.SystemProgram.programId,
// Add more 'publicKey' addresses here
],
});

// Send this 'extendInstruction' in a transaction to the cluster
// to insert the listing of 'addresses' into your lookup table with address 'lookupTableAddress'

Due to memory limits, any transaction used to extend an Address Lookup Table is also limited in how many addresses can be added at a time. Multiple transactions may be required to extend a table with more addresses.

Fetching an Address Lookup Table

To fetch a complete Address Lookup Table from the cluster, use the getAddressLookupTable method:

// Define the 'PublicKey' of the lookup table to fetch
const lookupTableAddress = new web3.PublicKey("...");

// Get the table from the cluster
const lookupTableAccount = (await connection.getAddressLookupTable(lookupTableAddress)).value;

// 'lookupTableAccount' will now be an 'AddressLookupTableAccount' object

console.log("Table address from cluster:", lookupTableAccount.key.toBase58());

// Loop through and parse all the addresses stored in the table
for (let i = 0; i < lookupTableAccount.state.addresses.length; i++) {
const address = lookupTableAccount.state.addresses[i];
console.log(i, address.toBase58());
}

Using an Address Lookup Table in a Transaction

After creating and storing addresses on-chain in the lookup table, you can utilize it in future transactions. Here’s an example of creating a v0 transaction:

// Assumptions:
// - 'arrayOfInstructions' has been created as an array of 'TransactionInstruction'
// - We are using the 'lookupTableAccount' obtained above

// Construct a v0 compatible transaction 'Message'
const messageV0 = new web3.TransactionMessage({
payerKey: payer.publicKey,
recentBlockhash: blockhash,
instructions: arrayOfInstructions, // Note: this is an array of instructions
}).compileToV0Message([lookupTableAccount]);

// Create a v0 transaction from the v0 message
const transactionV0 = new web3.VersionedTransaction(messageV0);

// Sign the v0 transaction using the file system wallet created named 'payer'
transactionV0.sign([payer]);

// Send and confirm the transaction
// (Note: There is NOT an array of Signers here; see the note below...)
const txid = await web3.sendAndConfirmTransaction(connection, transactionV0);

console.log(`Transaction: https://explorer.solana.com/tx/${txid}?cluster=devnet`);

Note: When sending a VersionedTransaction to the cluster, it must be signed BEFORE calling the sendAndConfirmTransaction method. If you pass an array of Signer (like with legacy transactions), the method will trigger an error.

Conclusion

Address Lookup Tables offer a powerful solution for optimizing Solana transactions, allowing developers to efficiently handle multiple addresses within a single transaction. By compressing on-chain addresses and leveraging versioned transactions, developers can achieve significant efficiency gains. The provided examples and code snippets offer a practical guide for incorporating Address Lookup Tables into Solana projects, empowering developers to navigate and leverage this powerful feature.

Resources

--

--