Skip to main content

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