solana_address_lookup_table_interface/
instruction.rs

1#[cfg(feature = "serde")]
2use serde_derive::{Deserialize, Serialize};
3use {solana_clock::Slot, solana_pubkey::Pubkey, solana_sdk_ids::address_lookup_table::id};
4#[cfg(feature = "bincode")]
5use {
6    solana_instruction::{AccountMeta, Instruction},
7    solana_sdk_ids::system_program,
8};
9
10#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
11#[derive(Debug, PartialEq, Eq, Clone)]
12pub enum ProgramInstruction {
13    /// Create an address lookup table
14    ///
15    /// # Account references
16    ///   0. `[WRITE]` Uninitialized address lookup table account
17    ///   1. `[SIGNER]` Account used to derive and control the new address lookup table.
18    ///   2. `[SIGNER, WRITE]` Account that will fund the new address lookup table.
19    ///   3. `[]` System program for CPI.
20    CreateLookupTable {
21        /// A recent slot must be used in the derivation path
22        /// for each initialized table. When closing table accounts,
23        /// the initialization slot must no longer be "recent" to prevent
24        /// address tables from being recreated with reordered or
25        /// otherwise malicious addresses.
26        recent_slot: Slot,
27        /// Address tables are always initialized at program-derived
28        /// addresses using the funding address, recent blockhash, and
29        /// the user-passed `bump_seed`.
30        bump_seed: u8,
31    },
32
33    /// Permanently freeze an address lookup table, making it immutable.
34    ///
35    /// # Account references
36    ///   0. `[WRITE]` Address lookup table account to freeze
37    ///   1. `[SIGNER]` Current authority
38    FreezeLookupTable,
39
40    /// Extend an address lookup table with new addresses. Funding account and
41    /// system program account references are only required if the lookup table
42    /// account requires additional lamports to cover the rent-exempt balance
43    /// after being extended.
44    ///
45    /// # Account references
46    ///   0. `[WRITE]` Address lookup table account to extend
47    ///   1. `[SIGNER]` Current authority
48    ///   2. `[SIGNER, WRITE, OPTIONAL]` Account that will fund the table reallocation
49    ///   3. `[OPTIONAL]` System program for CPI.
50    ExtendLookupTable { new_addresses: Vec<Pubkey> },
51
52    /// Deactivate an address lookup table, making it unusable and
53    /// eligible for closure after a short period of time.
54    ///
55    /// # Account references
56    ///   0. `[WRITE]` Address lookup table account to deactivate
57    ///   1. `[SIGNER]` Current authority
58    DeactivateLookupTable,
59
60    /// Close an address lookup table account
61    ///
62    /// # Account references
63    ///   0. `[WRITE]` Address lookup table account to close
64    ///   1. `[SIGNER]` Current authority
65    ///   2. `[WRITE]` Recipient of closed account lamports
66    CloseLookupTable,
67}
68
69/// Derives the address of an address table account from a wallet address and a recent block's slot.
70pub fn derive_lookup_table_address(
71    authority_address: &Pubkey,
72    recent_block_slot: Slot,
73) -> (Pubkey, u8) {
74    Pubkey::find_program_address(
75        &[authority_address.as_ref(), &recent_block_slot.to_le_bytes()],
76        &id(),
77    )
78}
79
80#[cfg(feature = "bincode")]
81/// Constructs an instruction to create a table account and returns
82/// the instruction and the table account's derived address.
83fn create_lookup_table_common(
84    authority_address: Pubkey,
85    payer_address: Pubkey,
86    recent_slot: Slot,
87    authority_is_signer: bool,
88) -> (Instruction, Pubkey) {
89    let (lookup_table_address, bump_seed) =
90        derive_lookup_table_address(&authority_address, recent_slot);
91    let instruction = Instruction::new_with_bincode(
92        id(),
93        &ProgramInstruction::CreateLookupTable {
94            recent_slot,
95            bump_seed,
96        },
97        vec![
98            AccountMeta::new(lookup_table_address, false),
99            AccountMeta::new_readonly(authority_address, authority_is_signer),
100            AccountMeta::new(payer_address, true),
101            AccountMeta::new_readonly(system_program::id(), false),
102        ],
103    );
104
105    (instruction, lookup_table_address)
106}
107
108/// Constructs an instruction to create a table account and returns
109/// the instruction and the table account's derived address.
110///
111/// # Note
112///
113/// This instruction doesn't require the authority to be a signer but
114/// until v1.12 the address lookup table program still requires the
115/// authority to sign the transaction.
116#[cfg(feature = "bincode")]
117pub fn create_lookup_table(
118    authority_address: Pubkey,
119    payer_address: Pubkey,
120    recent_slot: Slot,
121) -> (Instruction, Pubkey) {
122    create_lookup_table_common(authority_address, payer_address, recent_slot, false)
123}
124
125/// Constructs an instruction that freezes an address lookup
126/// table so that it can never be closed or extended again. Empty
127/// lookup tables cannot be frozen.
128#[cfg(feature = "bincode")]
129pub fn freeze_lookup_table(lookup_table_address: Pubkey, authority_address: Pubkey) -> Instruction {
130    Instruction::new_with_bincode(
131        id(),
132        &ProgramInstruction::FreezeLookupTable,
133        vec![
134            AccountMeta::new(lookup_table_address, false),
135            AccountMeta::new_readonly(authority_address, true),
136        ],
137    )
138}
139
140/// Constructs an instruction which extends an address lookup
141/// table account with new addresses.
142#[cfg(feature = "bincode")]
143pub fn extend_lookup_table(
144    lookup_table_address: Pubkey,
145    authority_address: Pubkey,
146    payer_address: Option<Pubkey>,
147    new_addresses: Vec<Pubkey>,
148) -> Instruction {
149    let mut accounts = vec![
150        AccountMeta::new(lookup_table_address, false),
151        AccountMeta::new_readonly(authority_address, true),
152    ];
153
154    if let Some(payer_address) = payer_address {
155        accounts.extend([
156            AccountMeta::new(payer_address, true),
157            AccountMeta::new_readonly(system_program::id(), false),
158        ]);
159    }
160
161    Instruction::new_with_bincode(
162        id(),
163        &ProgramInstruction::ExtendLookupTable { new_addresses },
164        accounts,
165    )
166}
167
168/// Constructs an instruction that deactivates an address lookup
169/// table so that it cannot be extended again and will be unusable
170/// and eligible for closure after a short amount of time.
171#[cfg(feature = "bincode")]
172pub fn deactivate_lookup_table(
173    lookup_table_address: Pubkey,
174    authority_address: Pubkey,
175) -> Instruction {
176    Instruction::new_with_bincode(
177        id(),
178        &ProgramInstruction::DeactivateLookupTable,
179        vec![
180            AccountMeta::new(lookup_table_address, false),
181            AccountMeta::new_readonly(authority_address, true),
182        ],
183    )
184}
185
186/// Returns an instruction that closes an address lookup table
187/// account. The account will be deallocated and the lamports
188/// will be drained to the recipient address.
189#[cfg(feature = "bincode")]
190pub fn close_lookup_table(
191    lookup_table_address: Pubkey,
192    authority_address: Pubkey,
193    recipient_address: Pubkey,
194) -> Instruction {
195    Instruction::new_with_bincode(
196        id(),
197        &ProgramInstruction::CloseLookupTable,
198        vec![
199            AccountMeta::new(lookup_table_address, false),
200            AccountMeta::new_readonly(authority_address, true),
201            AccountMeta::new(recipient_address, false),
202        ],
203    )
204}