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}