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 (optional). Used to derive the CPI signer PDA.
69    /// When None, the CPI signer is not included (for registry CPI flow).
70    pub self_program: Option<Pubkey>,
71    /// Optional CPI context account for batched operations (v2 only).
72    #[cfg(feature = "cpi-context")]
73    pub cpi_context: Option<Pubkey>,
74    /// Optional account to receive decompressed SOL.
75    pub sol_compression_recipient: Option<Pubkey>,
76    /// Optional SOL pool PDA for SOL compression/decompression.
77    pub sol_pool_pda: Option<Pubkey>,
78}
79
80impl SystemAccountMetaConfig {
81    /// Creates a basic configuration with only the program ID.
82    ///
83    /// Use this for simple compressed account operations without SOL compression
84    /// or CPI context.
85    ///
86    /// # Example
87    ///
88    /// ```rust
89    /// # use light_sdk::instruction::SystemAccountMetaConfig;
90    /// # use solana_pubkey::Pubkey;
91    /// let program_id = Pubkey::new_unique();
92    /// let config = SystemAccountMetaConfig::new(program_id);
93    /// ```
94    pub fn new(self_program: Pubkey) -> Self {
95        Self {
96            self_program: Some(self_program),
97            #[cfg(feature = "cpi-context")]
98            cpi_context: None,
99            sol_compression_recipient: None,
100            sol_pool_pda: None,
101        }
102    }
103
104    /// Creates a configuration with CPI context for batched operations (v2 only).
105    ///
106    /// Use this when you need to batch multiple compressed account operations
107    /// using a CPI context account.
108    ///
109    /// # Example
110    ///
111    /// ```rust
112    /// # use light_sdk::instruction::SystemAccountMetaConfig;
113    /// # use solana_pubkey::Pubkey;
114    /// let program_id = Pubkey::new_unique();
115    /// let cpi_context_account = Pubkey::new_unique();
116    /// let config = SystemAccountMetaConfig::new_with_cpi_context(
117    ///     program_id,
118    ///     cpi_context_account
119    /// );
120    /// ```
121    #[cfg(feature = "cpi-context")]
122    pub fn new_with_cpi_context(self_program: Pubkey, cpi_context: Pubkey) -> Self {
123        Self {
124            self_program: Some(self_program),
125            cpi_context: Some(cpi_context),
126            sol_compression_recipient: None,
127            sol_pool_pda: None,
128        }
129    }
130}
131
132#[derive(Debug, Copy, Clone)]
133pub struct SystemAccountPubkeys {
134    pub light_sytem_program: Pubkey,
135    pub system_program: Pubkey,
136    pub account_compression_program: Pubkey,
137    pub account_compression_authority: Pubkey,
138    pub registered_program_pda: Pubkey,
139    pub noop_program: Pubkey,
140    pub sol_pool_pda: Pubkey,
141}
142
143impl Default for SystemAccountPubkeys {
144    fn default() -> Self {
145        Self {
146            light_sytem_program: Pubkey::from(LIGHT_SYSTEM_PROGRAM_ID),
147            system_program: Pubkey::default(),
148            account_compression_program: Pubkey::from(ACCOUNT_COMPRESSION_PROGRAM_ID),
149            account_compression_authority: Pubkey::find_program_address(
150                &[CPI_AUTHORITY_PDA_SEED],
151                &Pubkey::from(LIGHT_SYSTEM_PROGRAM_ID),
152            )
153            .0,
154            registered_program_pda: Pubkey::find_program_address(
155                &[LIGHT_SYSTEM_PROGRAM_ID.as_slice()],
156                &Pubkey::from(ACCOUNT_COMPRESSION_PROGRAM_ID),
157            )
158            .0,
159            noop_program: Pubkey::from(NOOP_PROGRAM_ID),
160            sol_pool_pda: Pubkey::find_program_address(
161                &[b"sol_pool_pda"],
162                &Pubkey::from(LIGHT_SYSTEM_PROGRAM_ID),
163            )
164            .0,
165        }
166    }
167}
168
169/// InvokeSystemCpi v1.
170pub fn get_light_system_account_metas(config: SystemAccountMetaConfig) -> Vec<AccountMeta> {
171    let default_pubkeys = SystemAccountPubkeys::default();
172
173    let mut vec = if let Some(self_program) = &config.self_program {
174        let cpi_signer = find_cpi_signer_macro!(self_program).0;
175        vec![
176            AccountMeta::new_readonly(default_pubkeys.light_sytem_program, false),
177            AccountMeta::new_readonly(cpi_signer, false),
178            AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false),
179            AccountMeta::new_readonly(default_pubkeys.noop_program, false),
180            AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false),
181            AccountMeta::new_readonly(default_pubkeys.account_compression_program, false),
182            AccountMeta::new_readonly(*self_program, false),
183        ]
184    } else {
185        vec![
186            AccountMeta::new_readonly(default_pubkeys.light_sytem_program, false),
187            AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false),
188            AccountMeta::new_readonly(default_pubkeys.noop_program, false),
189            AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false),
190            AccountMeta::new_readonly(default_pubkeys.account_compression_program, false),
191        ]
192    };
193
194    if let Some(pubkey) = config.sol_pool_pda {
195        vec.push(AccountMeta {
196            pubkey,
197            is_signer: false,
198            is_writable: true,
199        });
200    }
201    if let Some(pubkey) = config.sol_compression_recipient {
202        vec.push(AccountMeta {
203            pubkey,
204            is_signer: false,
205            is_writable: true,
206        });
207    }
208    vec.push(AccountMeta::new_readonly(
209        default_pubkeys.system_program,
210        false,
211    ));
212    #[cfg(feature = "cpi-context")]
213    if let Some(pubkey) = config.cpi_context {
214        vec.push(AccountMeta {
215            pubkey,
216            is_signer: false,
217            is_writable: true,
218        });
219    }
220    vec
221}
222
223/// Can be used in client to add system account metas.
224///
225/// We need the program id account infos in the outer instruction.
226/// Account Metas:
227/// 1. Light System Program
228/// 2. Account Compression Program
229/// 3. System Program
230/// 4. CPI Signer
231/// 5. Registered Program PDA
232/// 6. Account Compression Authority
233#[cfg(feature = "v2")]
234pub fn get_light_system_account_metas_v2(config: SystemAccountMetaConfig) -> Vec<AccountMeta> {
235    let default_pubkeys = SystemAccountPubkeys::default();
236
237    let mut vec = if let Some(self_program) = &config.self_program {
238        let cpi_signer = find_cpi_signer_macro!(self_program).0;
239        vec![
240            AccountMeta::new_readonly(default_pubkeys.light_sytem_program, false),
241            AccountMeta::new_readonly(cpi_signer, false), // authority (cpi_signer)
242            AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false),
243            AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false),
244            AccountMeta::new_readonly(default_pubkeys.account_compression_program, false),
245            AccountMeta::new_readonly(default_pubkeys.system_program, false),
246        ]
247    } else {
248        vec![
249            AccountMeta::new_readonly(default_pubkeys.light_sytem_program, false),
250            AccountMeta::new_readonly(default_pubkeys.registered_program_pda, false),
251            AccountMeta::new_readonly(default_pubkeys.account_compression_authority, false),
252            AccountMeta::new_readonly(default_pubkeys.account_compression_program, false),
253            AccountMeta::new_readonly(default_pubkeys.system_program, false),
254        ]
255    };
256
257    if let Some(pubkey) = config.sol_pool_pda {
258        vec.push(AccountMeta {
259            pubkey,
260            is_signer: false,
261            is_writable: true,
262        });
263    }
264    if let Some(pubkey) = config.sol_compression_recipient {
265        vec.push(AccountMeta {
266            pubkey,
267            is_signer: false,
268            is_writable: true,
269        });
270    }
271    #[cfg(feature = "cpi-context")]
272    if let Some(pubkey) = config.cpi_context {
273        vec.push(AccountMeta {
274            pubkey,
275            is_signer: false,
276            is_writable: true,
277        });
278    }
279    vec
280}