light_sdk/cpi/v1/
accounts.rs

1pub use light_sdk_types::cpi_accounts::v1::SYSTEM_ACCOUNTS_LEN;
2use light_sdk_types::{
3    cpi_accounts::v1::CpiAccounts as GenericCpiAccounts, ACCOUNT_COMPRESSION_AUTHORITY_PDA,
4    ACCOUNT_COMPRESSION_PROGRAM_ID, LIGHT_SYSTEM_PROGRAM_ID, NOOP_PROGRAM_ID,
5    REGISTERED_PROGRAM_PDA,
6};
7
8use crate::{
9    error::{LightSdkError, Result},
10    AccountInfo, AccountMeta, Pubkey,
11};
12
13#[derive(Debug)]
14pub struct CpiInstructionConfig<'a, 'info> {
15    pub fee_payer: Pubkey,
16    pub cpi_signer: Pubkey,
17    pub invoking_program: Pubkey,
18    pub sol_pool_pda_pubkey: Option<Pubkey>,
19    pub sol_compression_recipient_pubkey: Option<Pubkey>,
20    pub cpi_context_pubkey: Option<Pubkey>,
21    pub packed_accounts: &'a [AccountInfo<'info>],
22}
23
24/// Light system program CPI accounts struct.
25///
26/// Use with [`LightSystemProgramCpi`](super::LightSystemProgramCpi) to invoke the Light system program.
27pub type CpiAccounts<'c, 'info> = GenericCpiAccounts<'c, AccountInfo<'info>>;
28
29pub fn get_account_metas_from_config(config: CpiInstructionConfig<'_, '_>) -> Vec<AccountMeta> {
30    let mut account_metas = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN);
31
32    // 1. Fee payer (signer, writable)
33    account_metas.push(AccountMeta {
34        pubkey: config.fee_payer,
35        is_signer: true,
36        is_writable: true,
37    });
38
39    // 2. Authority/CPI Signer (signer, readonly)
40    account_metas.push(AccountMeta {
41        pubkey: config.cpi_signer,
42        is_signer: true,
43        is_writable: false,
44    });
45
46    // 3. Registered Program PDA (readonly) - hardcoded constant
47    account_metas.push(AccountMeta {
48        pubkey: Pubkey::from(REGISTERED_PROGRAM_PDA),
49        is_signer: false,
50        is_writable: false,
51    });
52
53    // 4. Noop Program (readonly) - hardcoded constant
54    account_metas.push(AccountMeta {
55        pubkey: Pubkey::from(NOOP_PROGRAM_ID),
56        is_signer: false,
57        is_writable: false,
58    });
59
60    // 5. Account Compression Authority (readonly) - hardcoded constant
61    account_metas.push(AccountMeta {
62        pubkey: Pubkey::from(ACCOUNT_COMPRESSION_AUTHORITY_PDA),
63        is_signer: false,
64        is_writable: false,
65    });
66
67    // 6. Account Compression Program (readonly) - hardcoded constant
68    account_metas.push(AccountMeta {
69        pubkey: Pubkey::from(ACCOUNT_COMPRESSION_PROGRAM_ID),
70        is_signer: false,
71        is_writable: false,
72    });
73
74    // 7. Invoking Program (readonly)
75    account_metas.push(AccountMeta {
76        pubkey: config.invoking_program,
77        is_signer: false,
78        is_writable: false,
79    });
80
81    // 8. Light System Program (readonly) - reused for optional accounts
82    let create_light_system_meta = || AccountMeta {
83        pubkey: Pubkey::from(LIGHT_SYSTEM_PROGRAM_ID),
84        is_signer: false,
85        is_writable: false,
86    };
87
88    // 9. Sol Pool PDA (writable) OR Light System Program (readonly)
89    if let Some(sol_pool_pda_pubkey) = config.sol_pool_pda_pubkey {
90        account_metas.push(AccountMeta {
91            pubkey: sol_pool_pda_pubkey,
92            is_signer: false,
93            is_writable: true,
94        });
95    } else {
96        account_metas.push(create_light_system_meta());
97    }
98
99    // 10. Sol Compression Recipient (writable) OR Light System Program (readonly)
100    if let Some(sol_compression_recipient_pubkey) = config.sol_compression_recipient_pubkey {
101        account_metas.push(AccountMeta {
102            pubkey: sol_compression_recipient_pubkey,
103            is_signer: false,
104            is_writable: true,
105        });
106    } else {
107        account_metas.push(create_light_system_meta());
108    }
109
110    // 11. System Program (readonly) - always default pubkey
111    account_metas.push(AccountMeta {
112        pubkey: Pubkey::default(),
113        is_signer: false,
114        is_writable: false,
115    });
116
117    // 12. CPI Context (writable) OR Light System Program (readonly)
118    if let Some(cpi_context_pubkey) = config.cpi_context_pubkey {
119        account_metas.push(AccountMeta {
120            pubkey: cpi_context_pubkey,
121            is_signer: false,
122            is_writable: true,
123        });
124    } else {
125        account_metas.push(create_light_system_meta());
126    }
127
128    for acc in config.packed_accounts {
129        account_metas.push(AccountMeta {
130            pubkey: *acc.key,
131            is_signer: false,
132            is_writable: acc.is_writable,
133        });
134    }
135
136    account_metas
137}
138
139impl<'a, 'info> TryFrom<&'a CpiAccounts<'a, 'info>> for CpiInstructionConfig<'a, 'info> {
140    type Error = LightSdkError;
141
142    fn try_from(cpi_accounts: &'a CpiAccounts<'a, 'info>) -> Result<Self> {
143        Ok(CpiInstructionConfig {
144            fee_payer: *cpi_accounts.fee_payer().key,
145            cpi_signer: cpi_accounts.config().cpi_signer().into(),
146            invoking_program: cpi_accounts.config().cpi_signer.program_id.into(),
147            sol_pool_pda_pubkey: if cpi_accounts.config().sol_pool_pda {
148                Some(*cpi_accounts.sol_pool_pda()?.key)
149            } else {
150                None
151            },
152            sol_compression_recipient_pubkey: if cpi_accounts.config().sol_compression_recipient {
153                Some(*cpi_accounts.decompression_recipient()?.key)
154            } else {
155                None
156            },
157            cpi_context_pubkey: if cpi_accounts.config().cpi_context {
158                Some(*cpi_accounts.cpi_context()?.key)
159            } else {
160                None
161            },
162            packed_accounts: cpi_accounts.tree_accounts().unwrap_or(&[]),
163        })
164    }
165}