light_sdk/instruction/system_accounts.rs
1use light_sdk_types::constants::{
2 ACCOUNT_COMPRESSION_PROGRAM_ID, CPI_AUTHORITY_PDA_SEED, LIGHT_SYSTEM_PROGRAM_ID,
3 NOOP_PROGRAM_ID,
4};
5
6use crate::{find_cpi_signer_macro, AccountMeta, Pubkey};
7
8/// Configuration for Light system program accounts when building instructions.
9///
10/// This struct specifies which system accounts to include when using
11/// [`PackedAccounts::add_system_accounts()`](crate::instruction::PackedAccounts::add_system_accounts)
12/// or `PackedAccounts::add_system_accounts_v2()`.
13///
14/// # Required Fields
15///
16/// - **`self_program`**: Your program's ID (the one calling the Light system program).
17/// Used to derive the CPI signer PDA.
18///
19/// # Optional Fields
20///
21/// - **`cpi_context`**: CPI context account for batched operations (v2 only).
22/// Required when using CPI context for multi-step compressed account operations.
23///
24/// - **`sol_compression_recipient`**: Account to receive decompressed SOL.
25/// Required when decompressing SOL from compressed accounts.
26///
27/// - **`sol_pool_pda`**: SOL pool PDA for SOL compression/decompression.
28/// Required when compressing or decompressing SOL.
29///
30/// # Examples
31///
32/// Basic usage (no SOL operations):
33///
34/// ```rust
35/// # use light_sdk::instruction::SystemAccountMetaConfig;
36/// # use solana_pubkey::Pubkey;
37/// let program_id = Pubkey::new_unique();
38/// let config = SystemAccountMetaConfig::new(program_id);
39/// ```
40///
41/// With CPI context (v2 batched operations):
42///
43#[cfg_attr(not(feature = "cpi-context"), doc = "```ignore")]
44#[cfg_attr(feature = "cpi-context", doc = "```rust")]
45/// # use light_sdk::instruction::SystemAccountMetaConfig;
46/// # use solana_pubkey::Pubkey;
47/// let program_id = Pubkey::new_unique();
48/// let cpi_context_account = Pubkey::new_unique();
49/// let config = SystemAccountMetaConfig::new_with_cpi_context(program_id, cpi_context_account);
50/// ```
51///
52/// With SOL compression:
53///
54/// ```rust
55/// # use light_sdk::instruction::SystemAccountMetaConfig;
56/// # use solana_pubkey::Pubkey;
57/// let program_id = Pubkey::new_unique();
58/// let sol_pool_pda = Pubkey::new_unique();
59/// let recipient = Pubkey::new_unique();
60///
61/// let mut config = SystemAccountMetaConfig::new(program_id);
62/// config.sol_pool_pda = Some(sol_pool_pda);
63/// config.sol_compression_recipient = Some(recipient);
64/// ```
65#[derive(Debug, Default, Copy, Clone)]
66#[non_exhaustive]
67pub struct SystemAccountMetaConfig {
68 /// Your program's ID (required). Used to derive the CPI signer PDA.
69 pub self_program: Pubkey,
70 /// Optional CPI context account for batched operations (v2 only).
71 #[cfg(feature = "cpi-context")]
72 pub cpi_context: Option<Pubkey>,
73 /// Optional account to receive decompressed SOL.
74 pub sol_compression_recipient: Option<Pubkey>,
75 /// Optional SOL pool PDA for SOL compression/decompression.
76 pub sol_pool_pda: Option<Pubkey>,
77}
78
79impl SystemAccountMetaConfig {
80 /// Creates a basic configuration with only the program ID.
81 ///
82 /// Use this for simple compressed account operations without SOL compression
83 /// or CPI context.
84 ///
85 /// # Example
86 ///
87 /// ```rust
88 /// # use light_sdk::instruction::SystemAccountMetaConfig;
89 /// # use solana_pubkey::Pubkey;
90 /// let program_id = Pubkey::new_unique();
91 /// let config = SystemAccountMetaConfig::new(program_id);
92 /// ```
93 pub fn new(self_program: Pubkey) -> Self {
94 Self {
95 self_program,
96 #[cfg(feature = "cpi-context")]
97 cpi_context: None,
98 sol_compression_recipient: None,
99 sol_pool_pda: None,
100 }
101 }
102
103 /// Creates a configuration with CPI context for batched operations (v2 only).
104 ///
105 /// Use this when you need to batch multiple compressed account operations
106 /// using a CPI context account.
107 ///
108 /// # Example
109 ///
110 /// ```rust
111 /// # use light_sdk::instruction::SystemAccountMetaConfig;
112 /// # use solana_pubkey::Pubkey;
113 /// let program_id = Pubkey::new_unique();
114 /// let cpi_context_account = Pubkey::new_unique();
115 /// let config = SystemAccountMetaConfig::new_with_cpi_context(
116 /// program_id,
117 /// cpi_context_account
118 /// );
119 /// ```
120 #[cfg(feature = "cpi-context")]
121 pub fn new_with_cpi_context(self_program: Pubkey, cpi_context: Pubkey) -> Self {
122 Self {
123 self_program,
124 cpi_context: Some(cpi_context),
125 sol_compression_recipient: None,
126 sol_pool_pda: None,
127 }
128 }
129}
130
131#[derive(Debug, Copy, Clone)]
132pub struct SystemAccountPubkeys {
133 pub light_sytem_program: Pubkey,
134 pub system_program: Pubkey,
135 pub account_compression_program: Pubkey,
136 pub account_compression_authority: Pubkey,
137 pub registered_program_pda: Pubkey,
138 pub noop_program: Pubkey,
139 pub sol_pool_pda: Pubkey,
140}
141
142impl Default for SystemAccountPubkeys {
143 fn default() -> Self {
144 Self {
145 light_sytem_program: Pubkey::from(LIGHT_SYSTEM_PROGRAM_ID),
146 system_program: Pubkey::default(),
147 account_compression_program: Pubkey::from(ACCOUNT_COMPRESSION_PROGRAM_ID),
148 account_compression_authority: Pubkey::find_program_address(
149 &[CPI_AUTHORITY_PDA_SEED],
150 &Pubkey::from(LIGHT_SYSTEM_PROGRAM_ID),
151 )
152 .0,
153 registered_program_pda: Pubkey::find_program_address(
154 &[LIGHT_SYSTEM_PROGRAM_ID.as_slice()],
155 &Pubkey::from(ACCOUNT_COMPRESSION_PROGRAM_ID),
156 )
157 .0,
158 noop_program: Pubkey::from(NOOP_PROGRAM_ID),
159 sol_pool_pda: Pubkey::find_program_address(
160 &[b"sol_pool_pda"],
161 &Pubkey::from(LIGHT_SYSTEM_PROGRAM_ID),
162 )
163 .0,
164 }
165 }
166}
167
168/// InvokeSystemCpi v1.
169pub fn get_light_system_account_metas(config: SystemAccountMetaConfig) -> Vec<AccountMeta> {
170 let cpi_signer = find_cpi_signer_macro!(&config.self_program).0;
171 let default_pubkeys = SystemAccountPubkeys::default();
172
173 let mut vec = vec![
174 AccountMeta::new_readonly(default_pubkeys.light_sytem_program, false),
175 AccountMeta::new_readonly(cpi_signer, false),
176 AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false),
177 AccountMeta::new_readonly(default_pubkeys.noop_program, false),
178 AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false),
179 AccountMeta::new_readonly(default_pubkeys.account_compression_program, false),
180 AccountMeta::new_readonly(config.self_program, false), // with read only doesnt have this one
181 ];
182
183 if let Some(pubkey) = config.sol_pool_pda {
184 vec.push(AccountMeta {
185 pubkey,
186 is_signer: false,
187 is_writable: true,
188 });
189 }
190 if let Some(pubkey) = config.sol_compression_recipient {
191 vec.push(AccountMeta {
192 pubkey,
193 is_signer: false,
194 is_writable: true,
195 });
196 }
197 vec.push(AccountMeta::new_readonly(
198 default_pubkeys.system_program,
199 false,
200 ));
201 #[cfg(feature = "cpi-context")]
202 if let Some(pubkey) = config.cpi_context {
203 vec.push(AccountMeta {
204 pubkey,
205 is_signer: false,
206 is_writable: true,
207 });
208 }
209 vec
210}
211
212/// Can be used in client to add system account metas.
213///
214/// We need the program id account infos in the outer instruction.
215/// Account Metas:
216/// 1. Light System Program
217/// 2. Account Compression Program
218/// 3. System Program
219/// 4. CPI Signer
220/// 5. Registered Program PDA
221/// 6. Account Compression Authority
222#[cfg(feature = "v2")]
223pub fn get_light_system_account_metas_v2(config: SystemAccountMetaConfig) -> Vec<AccountMeta> {
224 let cpi_signer = find_cpi_signer_macro!(&config.self_program).0;
225 let default_pubkeys = SystemAccountPubkeys::default();
226
227 let mut vec = vec![
228 AccountMeta::new_readonly(default_pubkeys.light_sytem_program, false),
229 AccountMeta::new_readonly(cpi_signer, false), // authority (cpi_signer)
230 AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false),
231 AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false),
232 AccountMeta::new_readonly(default_pubkeys.account_compression_program, false),
233 AccountMeta::new_readonly(default_pubkeys.system_program, false),
234 ];
235
236 if let Some(pubkey) = config.sol_pool_pda {
237 vec.push(AccountMeta {
238 pubkey,
239 is_signer: false,
240 is_writable: true,
241 });
242 }
243 if let Some(pubkey) = config.sol_compression_recipient {
244 vec.push(AccountMeta {
245 pubkey,
246 is_signer: false,
247 is_writable: true,
248 });
249 }
250 #[cfg(feature = "cpi-context")]
251 if let Some(pubkey) = config.cpi_context {
252 vec.push(AccountMeta {
253 pubkey,
254 is_signer: false,
255 is_writable: true,
256 });
257 }
258 vec
259}