| Crates.io | luts |
| lib.rs | luts |
| version | 0.1.1 |
| created_at | 2026-01-09 21:47:21.848201+00 |
| updated_at | 2026-01-09 21:48:52.918881+00 |
| description | Address Lookup Table wrapper with deduplication and readiness tracking |
| homepage | |
| repository | https://github.com/ohaddahan/luts |
| max_upload_size | |
| id | 2032947 |
| size | 369,437 |
An Anchor program that wraps Solana's native Address Lookup Table (ALT) program with deduplication and readiness tracking.
This program provides a managed wrapper around Solana's Address Lookup Tables, adding:
846qK5Drj9NEn2P4AvXCKxoVnyYQYGzMu2W7gyvoYjHT
A PDA wrapper account that tracks ownership and state of an underlying Address Lookup Table.
| Field | Type | Description |
|---|---|---|
bump |
u8 |
PDA bump seed |
signer |
Pubkey |
Owner/authority of this LUT |
size |
u64 |
Number of addresses added through this wrapper |
id |
u64 |
User-defined identifier for multiple LUTs per signer |
address_lookup_table |
Pubkey |
The underlying native ALT address |
last_updated_slot |
u64 |
Slot of last modification (for cooldown tracking) |
PDA Seeds: ["UserAddressLookupTable", signer, id]
The program uses a two-level address derivation scheme:
The wrapper account address is derived deterministically from:
Seeds: ["UserAddressLookupTable", signer_pubkey, id_as_le_bytes]
Program: LUTS Program (846qK5Drj9NEn2P4AvXCKxoVnyYQYGzMu2W7gyvoYjHT)
The id parameter is user-controlled, allowing a single signer to create multiple independent LUTs by incrementing the id.
The underlying Address Lookup Table address is derived by Solana's native ALT program:
Seeds: [authority_pubkey, recent_slot_as_le_bytes]
Program: AddressLookupTab1e1111111111111111111111111
Here, the authority is the UserAddressLookupTable PDA (not the user's signer), giving this program control over the LUT. The recent_slot must be within ~150 slots of the current slot.
User provides: signer + id + recent_slot
│
▼
┌─────────────────────────────────────────────────┐
│ UserAddressLookupTable PDA │
│ = PDA(["UserAddressLookupTable", signer, id]) │
└─────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ Native LUT Address │
│ = PDA([UserAddressLookupTable, recent_slot]) │
└─────────────────────────────────────────────────┘
Creates a new Address Lookup Table with an associated wrapper account.
Arguments:
recent_slot: A recent slot used to derive the LUT addressid: User-defined identifier (allows multiple LUTs per signer)Accounts:
signer: Transaction signer and LUT owner (mut, signer)system_program: System programaddress_lookup_table_program: Native ALT programaddress_lookup_table: The LUT to be created (mut)user_address_lookup_table: Wrapper PDA to be initialized (mut)rent: Rent sysvarAdds new addresses to an existing lookup table. Automatically deduplicates against existing entries.
Accounts:
signer: LUT owner (mut, signer)system_program: System programaddress_lookup_table_program: Native ALT programaddress_lookup_table: The LUT to extend (mut)user_address_lookup_table: Wrapper PDA (mut)rent: Rent sysvarremaining_accounts: Addresses to add to the LUTConstraints:
Begins the deactivation process for a lookup table. After deactivation, the table can be closed once it's no longer in use by any recent transactions.
Accounts:
signer: LUT owner (mut, signer)system_program: System programaddress_lookup_table_program: Native ALT programaddress_lookup_table: The LUT to deactivate (mut)user_address_lookup_table: Wrapper PDA (mut)rent: Rent sysvarCloses a deactivated lookup table and its wrapper account, reclaiming rent to the signer.
Accounts:
signer: LUT owner (mut, signer)system_program: System programaddress_lookup_table_program: Native ALT programaddress_lookup_table: The LUT to close (mut)user_address_lookup_table: Wrapper PDA to close (mut)rent: Rent sysvar| Event | Fields | Description |
|---|---|---|
LutCreated |
wrapper, lut_address, authority, slot | Emitted when a new LUT is created |
LutExtended |
wrapper, addresses_added, total_addresses | Emitted when addresses are added |
LutDeactivated |
wrapper, lut_address | Emitted when a LUT is deactivated |
LutClosed |
wrapper, lut_address | Emitted when a LUT is closed |
| Error | Description |
|---|---|
InvalidLookupTable |
The provided LUT address doesn't match the expected derived address |
LutNotReady |
Cooldown period (15 slots) hasn't passed since last update |
MaxAddressesExceeded |
Adding addresses would exceed the 256 address limit |
NoNewAddresses |
All provided addresses already exist in the LUT |
# Build the program
anchor build
# Or via npm
npm run build
Generate TypeScript and Rust client code using Codama:
npm run gen-clients
This runs:
anchor build - Builds the program and generates IDLnpm run codama - Generates Rust and TypeScript clients from IDLscripts/fix-codama.sh - Applies post-generation fixesAuto-generated Rust client library containing:
instructions/ - Instruction builders for all program instructionsaccounts/ - Account structs with serializationtypes/ - Event types (LutCreated, LutExtended, etc.)errors/ - Program error definitionsshared.rs - Shared utilities and account fetching (with fetch feature)Used by the Mollusk-based tests for instruction building.
Auto-generated TypeScript client library containing:
instructions/ - Instruction buildersaccounts/ - Account decoders (e.g., getUserAddressLookupTableDecoder)types/ - Event type definitionserrors/ - Error definitionsprograms/ - Program address constant (LUTS_PROGRAM_ADDRESS)Hand-written TypeScript utilities that extend the generated client:
constants/ - Seed constants and program IDspda/ - PDA derivation helpers:
getUserAddressLookupTableAddress(signer, id) - Derives wrapper PDAderiveAddressLookupTableAddress(authority, recentSlot) - Derives native LUT addresswrappers/ - High-level instruction builders:
buildCreateAddressLookupTableInstruction()buildExtendAddressLookupTableInstruction()buildDeactivateAddressLookupTableInstruction()buildCloseAddressLookupTableInstruction()utils/ - Transaction helpers (processAndValidateTransaction)Located in tests/:
tests/luts-anchor.ts - Uses the Anchor client library directly:
anchor test
tests/luts-codama.ts - Uses the Codama-generated clients and custom wrappers:
anchor test
Both test suites cover:
Located in programs/luts/tests/:
programs/luts/tests/
├── main.rs # Test entry point
├── common/
│ ├── helpers.rs # Test context and utilities
│ └── pda.rs # PDA derivation helpers
└── integration/
├── test_create_address_lookup_table.rs
├── test_extend_address_lookup_table.rs
├── test_deactivate_address_lookup_table.rs
└── test_close_address_lookup_table.rs
Uses mollusk-helper for fast, deterministic testing without a validator. Tests use the Codama-generated Rust client (codama-rust-luts) for instruction building.
# Run mollusk tests
npm run gen-clients && cargo nextest run --features test-sbf
# Or via anchor script
anchor run test-mollusk
anchor run check
# Or directly:
cargo check --tests --features test-sbf
npm run lint # Check for issues
npm run lint:fix # Auto-fix issues