What are Address Lookup Tables and How to use them in your Projects

Shuvajit Mahanta
9 min readNov 20, 2023

--

Address Lookup Tables

Address Lookup Tables (ALTs), also known as Lookup Tables (LUTs), have emerged as a groundbreaking technology in Solana development. Introduced with the Versioned Transactions update, ALTs allow developers to efficiently load and manage a collection of related addresses within a single transaction. Unlike traditional approaches, ALTs provide a scalable solution for complex interactions with multiple accounts, revolutionizing the way Solana applications are built.

Versioned Transactions

On 2022, Solana released the concept of Versioned Transactions or ‘TransactionV0’. Which introduces a new technology called ALTs. It allows to create a collection of related addresses to efficiently load more addresses in a single transaction.

After the Versioned Transactions update the Solana runtime recognizes two types of transaction. First, the always used transactions ( Legacy transaction ), and a new transaction format called Version Transaction which can make use of on-chain address lookup tables.

Sending a Versioned Transaction

Legacy Transaction

A transaction on Solana has only 1232 bytes of Maximum Transaction size.

A transaction is comprised of :

  • A compact array of signatures
  • A message

Because of the Transaction size limit of 1232 bytes, a transaction can at very best store 35 account addresses ( each account is 32 bytes), taking into account some space for headers, signatures and some other metadata.

Transaction V0

In Transaction V0 by changing the massage format of a Legacy transaction and adding Address Lookup Tables there, a transaction can hold a maximum of 256 accounts.

In Address Lookup Tables we store account addresses in a table-like (array) data structure on-chain with an index associated with every address.

The address of the table can be referenced in a transaction message.

To point to an individual account within the table, a 1- byte u8 index is needed, this leads to a possibility of referencing 2⁸=256 accounts, as accounts are formatted using a u8 index.

This is the format of an Address Lookup Table

The Need for optimizing transactions

Advanced Solana developers often find themselves juggling multiple accounts in complex transactions, pushing the limits of traditional approaches. In this landscape, the need for optimization becomes paramount. Without efficient tools, managing and interacting with numerous accounts in a single transaction can be a daunting task, leading to increased costs and decreased performance.

Benefits and Advantages of Address Lookup Tables

1. Improved Efficiency

In complex decentralized applications (DApps), developers often need to interact with and update multiple accounts within a single transaction. Without an efficient organizational structure, the code can become convoluted, error-prone, and challenging to maintain.

Scenario: Let’s take the example of a decentralized exchange (DEX) where a trade involves updating the user’s token balance, the DEX’s liquidity pool, and a fee collector account. Traditionally, developers might approach this by individually handling each account operation, leading to repetitive code and increased risk of errors.

ALT Solution: With ALT, you can organize these accounts into a lookup table. The transaction logic becomes more streamlined, allowing you to fetch, process, and update these accounts within a single transaction. This enhances code readability and reduces the risk of errors.

2. Cost Savings

High transaction costs can be a significant concern in decentralized applications, particularly in scenarios where multiple operations need to be performed across various accounts. Executing these operations individually can lead to increased gas costs for users.

Scenario: In a decentralized lending platform, executing a loan repayment involves updating the borrower’s balance, the lender’s balance, and potentially adjusting interest rate-related accounts. Performing these actions in separate transactions can lead to higher gas costs for users.

ALT Solution: By using ALT to manage the involved accounts, you can bundle these operations into a single transaction. This reduces the number of transactions needed, resulting in lower gas costs for users and a more efficient lending platform.

3. Scalability and Performance

From an advanced developer’s perspective, scalability and performance are not just goals; they are prerequisites. ALTs play a pivotal role in achieving these objectives. By efficiently managing addresses, these tables become tools for optimizing resource utilization, ensuring the scalability and performance of Solana-based applications even in the face of complex requirements.

Scenario: In a decentralized social media platform, actions such as posting content, updating user profiles, and managing followers involve multiple accounts. Without ALT, scaling the platform to handle a large number of concurrent user interactions could become a bottleneck.

ALT Solution: By using ALT to manage the involved accounts, you can bundle these operations into a single transaction. This reduces the number of transactions needed, resulting in lower gas costs for users and a more efficient lending platform.

4. Enhanced Transaction Throughput

Transaction throughput is a key metric for advanced Solana developers seeking to build scalable and high-performance DApps. Address Lookup Tables contribute significantly by allowing the loading and manipulation of data from multiple addresses within a single transaction. The result is an elevated transaction throughput, a coveted feature in the competitive landscape of blockchain development.

Use Cases

Decentralized Finance (DeFi) Protocols

In the complex world of decentralized finance, ALTs shine brightly. Advanced developers can seamlessly navigate DeFi scenarios where multiple accounts need attention within a single transaction. Whether calculating interest payments, managing collateral ratios, or executing liquidations, ALTs simplify the process, making DeFi applications more efficient and cost-effective.

Non-Fungible Tokens (NFTs) Collections

Advanced developers in the NFT space understand the diversity and complexity of NFT collections. ALTs become indispensable tools, especially when dealing with batch transfers, royalties distribution, or intricate governance structures within advanced NFT applications. The ability to manage addresses efficiently becomes a key differentiator in delivering a superior user experience.

Multi-Signature Wallets

Security and efficiency are paramount in multi-signature operations. Experienced developers can leverage ALTs to organize and manage the addresses of authorized signers effectively. This ensures that complex authorization scenarios are handled with finesse, elevating the security and efficiency of multi-signature transactions.

Prerequisites to use ALTs

  • Nodejs (version 16.15 or higher) installed
  • Typescript experience and ts-node installed
  • Experience with running basic Solana Transactions

RPC in Solana Development

Remote Procedure Call (RPC) is a communication protocol used in blockchain development to facilitate interactions between a client (such as a developer’s application) and a server (the blockchain network). RPC allows developers to query information, submit transactions, and interact with the blockchain.

Versioned Transactions and the Impact on RPC

The introduction of Versioned Transactions in Solana represents a significant evolution in the capabilities of transactions on the blockchain. This update expands the traditional transaction format and introduces a new transaction type, offering developers increased flexibility and efficiency.

RPC Changes

Transaction responses will require a new version field: maxSupportedTransactionVersion to indicate to clients which transaction structure needs to be followed for deserialization.

The following methods need to be updated to avoid errors:

  • getTransaction
  • getBlock

The following parameter needs to be added to the requests: maxSupportedTransactionVersion: 0

( The ‘0’ represents the version of transaction, for V0 it is set to 0)

If maxSupportedTransactionVersion is not explicitly added to the request, the transaction version will fallback to legacy, we are not gonna be able to use Address Lookup Tables. Any block that contains a versioned transaction will return with an error by the client in the case of a legacy transaction.

Example of a JSON formatted requests to the RPC endpoint:

Sending a Versioned Transaction:

You should use the VersionedTransaction class from the Solana web3js library instead of the Transaction class to send a transaction.

// create an array with your desired `instructions`
// in this case, just a transfer instruction
const instructions = [
SystemProgram. transfer({
fromPubkey: publickey,
toPubkey: publickey,
lamports: minRent,
}),
];
// create v0 compatible message
const messageV0 = new Transaction Message({
payerKey: publickey,
recent Blockhash: blockhash,
instructions,
}).compileToV@Message([lookupTableAccount]);
// make a versioned transaction
const transactionV0 = new VersionedTransaction(messageV0);
const signature = await signAndSend Transaction (provider,
transactionV0);

Signing and Sending a Versioned Transaction:

Once a Versioned transaction is created, it can be signed and sent via Phantom using the signAndSendTransaction method on the provider. The call will return a Promise for an object containing the signature. This is the same way a legacy transaction is sent via the Phantom provider.

const provider = getProvider (); // see "Detecting the Provider"
const network = "<NETWORK_URL>";
const connection = new Connection (network);
const versionedTransaction = new Versioned Transaction();
const { signature } = await provider.signAndSendTransaction (versioned Transaction)
await connection.getSignatureStatus (signature);

You can also specify a SendOptions object as a second argument into AndSendTransaction() or as an options parameter when using request.

Building an Address Lookup Table (LUT):

Once we have the lookup table instruction, we can construct a transaction, sign it, and send it to create a lookup table on-chain. Address lookup tables can be created with either a vO transaction or a legacy transaction. However, the Solana runtime can only retrieve and handle the additional
addresses within a lookup table while using vO transactions.

// create an Address Lookup Table
const [lookup TableInst, lookupTableAddress] = Address Lookup Table Program.createLookup Table({
authority: publickey,
payer: publickey,
recentSlot: slot,
});
// To create the Address Lookup Table on chain:
// send the `lookupTableInst` instruction in a transaction
const lookupMessage = new Transaction Message({
payerKey: publickey,
recent Blockhash: blockhash,
instructions: [lookupTableInst],
}).compileToVOMessage();
const lookupTransaction = new Versioned Transaction (lookupMessage);
const lookupSignature = await signAndSend Transaction (provider, lookupTransaction);

Extending an Address Lookup Table (LUT):

Once an Address Lookup Table is created, it can then be extended (i.e. accounts can be appended to the table). Using the @solana/web3.js library, you can create a new extend instruction using the extendLookupTable method. Once the extend instruction is created, it can be sent in a transaction.

// add addresses to the `lookupTableAddress` table via an extend instruction
const extendInstruction = Address LookupTable Program.extend LookupTable({
payer: publicKey,
authority: publickey,
lookupTable: lookupTableAddres
addresses: [
publickey,
SystemProgram.programId,
// more `publickey` addresses can be listed here.
],
});
// Send this `extend Instruction in a transaction to the cluster
// to insert the listing of addresses into your lookup table with address lookup TableAddress
const extension MessageVO = new TransactionMessage({
payerKey: publickey,
recent Blockhash: blockhash,
instructions: [extend Instruction],
}).compileTOVOMessage();
const extensionTransactionV0 = new Versioned Transaction(extensionMessageV0);
const extensionSignature = await signAndSend Transaction (provider, extensionTransactionV0);

Signing and Sending a Versioned Transaction utilizing a LUT:

First, we need to fetch the account of the created Address Lookup Table.
We can also parse and read all the addresses currently stores in the fetched Address Lookup Table.

// get the table from the cluster
const lookup TableAccount = await connection.getAddress Lookup Table (lookup TableAddress).then((res) => res.value);
// `lookupTableAccount will now be a `Address Lookup TableAccount` object
console.log('Table address from cluster:', lookupTableAccount.key.toBase58());
// Loop through and parse all the address 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());
}

Signing and Sending a Versioned Transaction utilizing a LUT (cont.):

We can now create the instructions array with an arbitrary transfer instruction, just the way we did while creating the Versioned Transaction earlier. This Versioned Transaction can then be sent using
the signAndSend Transaction() provider function.

// create an array with your desired instructions`
// in this case, just a transfer instruction
const instructions = [
SystemProgram. transfer({
fromPubkey: publickey,
toPubkey: publickey,
lamports: minRent,
}),
];
// create v0 compatible message
const messageV0 = new TransactionMessage({
payerKey: publickey,
recent Blockhash: blockhash,
instructions,
}).compileToV@Message([lookup TableAccount]);
// make a versioned transaction
const transactionV0 = new Versioned Transaction (messageV0);
const signature = await signAndSend Transaction (provider,
transactionV0);

Code Yourself

If you want try this or want to implement ALTs in your project, Phantom has a Developer sandbox that showcases a demo of implementing Address Lookup Tables you can check it out.

Or you can find the GitHub repository of that Sandbox here.

Some Additional Resources

--

--