What are Address Lookup Tables
An Address Lookup Table (ALT) is essentially an on-chain directory where transaction can refer to specific account addresses using just a 1-byte index, rather than listing every full 32-byte address out loud. Used with versioned (v0) transactions, Address Lookup Tables allowed the transaction to read from/write to up to 256 accounts. Previously, legacy transactions could not.
To use an analogy, consider how airports use codes on luggage tags. Nobody writes out “Los Angeles International Airport,” but rather the three-letter code. Everyone understands that code to refer to an entry in the shared airport code registry. A v0 transaction is akin to the luggage tag containing these shortened codes, while an ALT is the shared registry.
Why Solana Needs ALTs
Every Solana transaction must declare, ahead of time, every account it will read from/write to. This declaration, which is what allows parallel processing of non-overlapping transactions, comes into conflict with a hard limit on the size of a transaction: 1,232 bytes. At 32-bytes per account, that leaves room for just 38 addresses. Even if all the addresses were used as a single transaction (no signatures or headers or other transaction metadata), a transaction would be completely useless if the only thing it did was declare all the accounts it will use. As a result, practical address limits were much lower.
Complex DeFi scenarios, like aggregating liquidity across multiple DEXs, hit this problem all the time. To facilitate a multi-hop swap, a router must know the addresses of every pool, token mint, token vault, user account and program across all the different protocols it wants to use. A legacy transaction would not be large enough to declare all these accounts in one go. The solution would be to split the routing logic over multiple transactions, or just settle for taking a worse price.
Technical Implementation of Versioned Transactions
Both ALTs and versioned transactions arrived around mid-2022. In order to create an ALT, a user (or user agent) simply pays for rent on the address. From then on, additional addresses can be added to the ALT until the full 256-address limit is reached. A v0 transaction will declare both a full set of address and a list of address indexes corresponding to an ALT. Because an ALT address index is only 1-byte, it allows a massive size savings.
To visualize this, imagine you need to declare 30 accounts on your transaction. In a v0 transaction, the 30 account addresses could all be declared as indexes corresponding to a lookup table. This allows the 30 addresses to take up 30-bytes, which is the same space as the full account name. This is a 97% space savings for each address.
So, now imagine a v0 transaction using a lookup table to refer to 30 accounts in that transaction. Doing so would consume a mere 30-bytes. That is 932-bytes more room for other transaction content like program instructions and transaction metadata. This type of savings is necessary for routers like Jupiter to construct routes. Take a look at the route Jupiter took for this transaction. It involves interacting with three different pools across three different protocols, totaling 23 program-invoked accounts. It’s physically impossible to fit these into 1,232-bytes without using lookup tables.
To interact with these new tables, a user would use any normal wallet. Wallets such as Phantom are already displaying and signing these transactions correctly. As such, there should be no friction between v0 transactions and user experience.
Ethereum does not have lookup tables. The EVM does not require a transaction to declare the storage slots it will read from/write to. This has an interesting side effect. This means that a contract could read storage at any point in time. One of the reasons why Solana can process transactions non-conflictingly in parallel but EVM can’t is because it is not required to declare storage ahead of time, so it is not able to determine which transactions may conflict until after they run. In other words, lookup tables are the cost Solana pays to do transactions in parallel. Versioned transactions are what allow users to pay this cost without the price to be exorbitant.
Benefits of Address Lookup Tables
Consider the case where Jupiter quotes a swap better price than any single pool would offer. This better price may exist because the quote is made over 4 different pools, across 3 different protocols. Using a v0 transaction, the 4 pools (and other accounts) may all fit into one transaction. Using legacy transactions, such a route could not possibly fit. If we only had legacy transactions, this router would be forced to take a less advantageous price over two (or more) sequential transactions.
This brings us to some operational trade-offs. Creating and maintaining a lookup table involves more overhead than standard transactions. Before you can use it, you need to create it and populate it with enough accounts. If you need more than 256 accounts in one lookup table, you will be forced to construct and populate multiple lookup tables and refer to multiple table indexes in one transaction. You will also need to wait until enough slots occur after a new lookup table was created before the tables can be used by transactions. Lookup table accounts cannot be deallocated until all accounts referencing them become deallocated. This requires another transaction after all other transactions using that table are no longer used. In the event where legacy transactions are the only ones being used by transactions on the chain, this requires an additional transaction to deallocate. Older wallets would only display and sign legacy transactions. Any RPC nodes that cannot resolve tables used by the transaction will result in a bad response. These are all concerns for developers maintaining these lookup tables, rather than everyday users using them for swaps.
Does the use of a lookup table allow a transaction to only declare a subset of the accounts it will use?
Not at all. All v0 transactions must still declare all the addresses the transaction will use. Lookup tables are simply another way to encode the transaction’s set of addresses. This way instead of needing to declare addresses using 32-bytes, the address only uses a 1-byte index. This means we retain the parallel-execution guarantee.