1use anchor_lang::{prelude::*, Bumps};
2use solana_program::{instruction::Instruction, program::invoke_signed};
3
4use crate::{
5 address::NewAddressParamsPacked,
6 compressed_account::{
7 OutputCompressedAccountWithPackedContext, PackedCompressedAccountWithMerkleContext,
8 },
9 error::LightSdkError,
10 proof::CompressedProof,
11 traits::{
12 InvokeAccounts, InvokeCpiAccounts, InvokeCpiContextAccount, LightSystemAccount,
13 SignerAccounts,
14 },
15 CPI_AUTHORITY_PDA_SEED, PROGRAM_ID_LIGHT_SYSTEM,
16};
17
18pub fn find_cpi_signer(program_id: &Pubkey) -> Pubkey {
19 Pubkey::find_program_address([CPI_AUTHORITY_PDA_SEED].as_slice(), program_id).0
20}
21
22#[derive(AnchorSerialize, AnchorDeserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
23pub struct CompressedCpiContext {
24 pub set_context: bool,
27 pub first_set_context: bool,
30 pub cpi_context_account_index: u8,
32}
33
34#[derive(Debug, PartialEq, Default, Clone, AnchorDeserialize, AnchorSerialize)]
35pub struct InstructionDataInvokeCpi {
36 pub proof: Option<CompressedProof>,
37 pub new_address_params: Vec<NewAddressParamsPacked>,
38 pub input_compressed_accounts_with_merkle_context:
39 Vec<PackedCompressedAccountWithMerkleContext>,
40 pub output_compressed_accounts: Vec<OutputCompressedAccountWithPackedContext>,
41 pub relay_fee: Option<u64>,
42 pub compress_or_decompress_lamports: Option<u64>,
43 pub is_compress: bool,
44 pub cpi_context: Option<CompressedCpiContext>,
45}
46
47#[inline(always)]
48pub fn setup_cpi_accounts<'info>(
49 ctx: &Context<
50 '_,
51 '_,
52 '_,
53 'info,
54 impl InvokeAccounts<'info>
55 + LightSystemAccount<'info>
56 + InvokeCpiAccounts<'info>
57 + SignerAccounts<'info>
58 + InvokeCpiContextAccount<'info>
59 + Bumps,
60 >,
61) -> (Vec<AccountInfo<'info>>, Vec<AccountMeta>) {
62 let none_account_info = ctx.accounts.get_light_system_program().to_account_info();
65
66 let (cpi_context_account_info, cpi_context_account_meta) =
67 match ctx.accounts.get_cpi_context_account() {
68 Some(acc) => (
69 acc.to_account_info(),
70 AccountMeta {
71 pubkey: acc.key(),
72 is_signer: false,
73 is_writable: true,
74 },
75 ),
76 None => (
77 none_account_info.clone(),
78 AccountMeta {
79 pubkey: ctx.accounts.get_light_system_program().key(),
80 is_signer: false,
81 is_writable: false,
82 },
83 ),
84 };
85
86 let mut account_infos = vec![
87 ctx.accounts.get_fee_payer().to_account_info(),
89 ctx.accounts.get_authority().to_account_info(),
91 ctx.accounts.get_registered_program_pda().to_account_info(),
93 ctx.accounts.get_noop_program().to_account_info(),
95 ctx.accounts
97 .get_account_compression_authority()
98 .to_account_info(),
99 ctx.accounts
101 .get_account_compression_program()
102 .to_account_info(),
103 ctx.accounts.get_invoking_program().to_account_info(),
105 none_account_info.clone(),
107 none_account_info,
109 ctx.accounts.get_system_program().to_account_info(),
111 cpi_context_account_info,
113 ];
114 for remaining_account in ctx.remaining_accounts {
115 account_infos.push(remaining_account.to_owned());
116 }
117
118 let mut account_metas = vec![
119 AccountMeta {
121 pubkey: account_infos[0].key(),
122 is_signer: true,
123 is_writable: true,
124 },
125 AccountMeta {
127 pubkey: account_infos[1].key(),
128 is_signer: true,
129 is_writable: false,
130 },
131 AccountMeta {
133 pubkey: account_infos[2].key(),
134 is_signer: false,
135 is_writable: false,
136 },
137 AccountMeta {
139 pubkey: account_infos[3].key(),
140 is_signer: false,
141 is_writable: false,
142 },
143 AccountMeta {
145 pubkey: account_infos[4].key(),
146 is_signer: false,
147 is_writable: false,
148 },
149 AccountMeta {
151 pubkey: account_infos[5].key(),
152 is_signer: false,
153 is_writable: false,
154 },
155 AccountMeta {
157 pubkey: account_infos[6].key(),
158 is_signer: false,
159 is_writable: false,
160 },
161 AccountMeta {
163 pubkey: account_infos[7].key(),
164 is_signer: false,
165 is_writable: false,
166 },
167 AccountMeta {
169 pubkey: account_infos[8].key(),
170 is_signer: false,
171 is_writable: false,
172 },
173 AccountMeta {
175 pubkey: account_infos[9].key(),
176 is_signer: false,
177 is_writable: false,
178 },
179 cpi_context_account_meta,
180 ];
181 for remaining_account in ctx.remaining_accounts {
182 account_metas.extend(remaining_account.to_account_metas(None));
183 }
184
185 (account_infos, account_metas)
186}
187
188#[derive(AnchorDeserialize, AnchorSerialize)]
189pub struct InvokeCpi {
190 pub inputs: Vec<u8>,
191}
192
193#[inline(always)]
194pub fn invoke_cpi(
195 account_infos: &[AccountInfo],
196 accounts_metas: Vec<AccountMeta>,
197 inputs: Vec<u8>,
198 signer_seeds: &[&[&[u8]]],
199) -> Result<()> {
200 let instruction_data = InvokeCpi { inputs };
201
202 let mut data = [49, 212, 191, 129, 39, 194, 43, 196].to_vec();
204 data.extend(instruction_data.try_to_vec()?);
205
206 let instruction = Instruction {
207 program_id: PROGRAM_ID_LIGHT_SYSTEM,
208 accounts: accounts_metas,
209 data,
210 };
211 invoke_signed(&instruction, account_infos, signer_seeds)?;
212
213 Ok(())
214}
215
216pub fn verify<'info, 'a, 'b, 'c, T>(
220 ctx: &Context<
221 '_,
222 '_,
223 '_,
224 'info,
225 impl InvokeAccounts<'info>
226 + LightSystemAccount<'info>
227 + InvokeCpiAccounts<'info>
228 + SignerAccounts<'info>
229 + InvokeCpiContextAccount<'info>
230 + Bumps,
231 >,
232 inputs: &T,
233 signer_seeds: &'a [&'b [&'c [u8]]],
234) -> Result<()>
235where
236 T: AnchorSerialize,
237{
238 if ctx.accounts.get_light_system_program().key() != PROGRAM_ID_LIGHT_SYSTEM {
239 return err!(LightSdkError::InvalidLightSystemProgram);
240 }
241
242 let inputs = inputs.try_to_vec()?;
243
244 let (account_infos, account_metas) = setup_cpi_accounts(ctx);
245 invoke_cpi(&account_infos, account_metas, inputs, signer_seeds)?;
246 Ok(())
247}