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