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}