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}