light_sdk/compressible/
compress_runtime.rs1use light_compressed_account::instruction_data::with_account_info::CompressedAccountInfo;
3use light_sdk_types::{
4 instruction::account_meta::CompressedAccountMetaNoLamportsNoAddress, CpiSigner,
5};
6use solana_account_info::AccountInfo;
7use solana_program_error::ProgramError;
8use solana_pubkey::Pubkey;
9
10pub trait CompressContext<'info> {
11 fn fee_payer(&self) -> &AccountInfo<'info>;
12 fn config(&self) -> &AccountInfo<'info>;
13 fn rent_sponsor(&self) -> &AccountInfo<'info>;
14 fn compression_authority(&self) -> &AccountInfo<'info>;
15
16 fn compress_pda_account(
17 &self,
18 account_info: &AccountInfo<'info>,
19 meta: &CompressedAccountMetaNoLamportsNoAddress,
20 cpi_accounts: &crate::cpi::v2::CpiAccounts<'_, 'info>,
21 compression_config: &crate::compressible::CompressibleConfig,
22 program_id: &Pubkey,
23 ) -> Result<Option<CompressedAccountInfo>, ProgramError>;
24}
25
26#[inline(never)]
27#[allow(clippy::too_many_arguments)]
28pub fn process_compress_pda_accounts_idempotent<'info, Ctx>(
29 ctx: &Ctx,
30 remaining_accounts: &[AccountInfo<'info>],
31 compressed_accounts: Vec<CompressedAccountMetaNoLamportsNoAddress>,
32 system_accounts_offset: u8,
33 cpi_signer: CpiSigner,
34 program_id: &Pubkey,
35) -> Result<(), ProgramError>
36where
37 Ctx: CompressContext<'info>,
38{
39 use crate::cpi::{
40 v2::{CpiAccounts, LightSystemProgramCpi},
41 InvokeLightSystemProgram, LightCpiInstruction,
42 };
43
44 let proof = crate::instruction::ValidityProof::new(None);
45
46 let compression_config =
47 crate::compressible::CompressibleConfig::load_checked(ctx.config(), program_id)?;
48
49 if *ctx.rent_sponsor().key != compression_config.rent_sponsor {
50 return Err(ProgramError::Custom(0));
51 }
52 if *ctx.compression_authority().key != compression_config.compression_authority {
53 return Err(ProgramError::Custom(0));
54 }
55
56 let system_accounts_offset_usize = system_accounts_offset as usize;
57 if system_accounts_offset_usize > remaining_accounts.len() {
58 return Err(ProgramError::from(
59 crate::error::LightSdkError::ConstraintViolation,
60 ));
61 }
62
63 let cpi_accounts = CpiAccounts::new(
64 ctx.fee_payer(),
65 &remaining_accounts[system_accounts_offset_usize..],
66 cpi_signer,
67 );
68
69 let mut compressed_pda_infos: Vec<CompressedAccountInfo> =
70 Vec::with_capacity(compressed_accounts.len());
71 let mut pda_indices_to_close: Vec<usize> = Vec::with_capacity(compressed_accounts.len());
72
73 let system_accounts_start = cpi_accounts.system_accounts_end_offset();
74 let account_infos = cpi_accounts.to_account_infos();
75 let all_post_system = account_infos
76 .get(system_accounts_start..)
77 .ok_or_else(|| ProgramError::from(crate::error::LightSdkError::ConstraintViolation))?;
78
79 let pda_start_in_all_accounts = all_post_system
81 .len()
82 .checked_sub(compressed_accounts.len())
83 .ok_or_else(|| ProgramError::from(crate::error::LightSdkError::ConstraintViolation))?;
84 let solana_accounts = all_post_system
85 .get(pda_start_in_all_accounts..)
86 .ok_or_else(|| ProgramError::from(crate::error::LightSdkError::ConstraintViolation))?;
87
88 for (i, account_info) in solana_accounts.iter().enumerate() {
89 if account_info.data_is_empty() {
90 continue;
91 }
92
93 if account_info.owner != program_id {
94 continue;
95 }
96
97 let meta = compressed_accounts[i];
98
99 if let Some(compressed_info) = ctx.compress_pda_account(
100 account_info,
101 &meta,
102 &cpi_accounts,
103 &compression_config,
104 program_id,
105 )? {
106 compressed_pda_infos.push(compressed_info);
107 pda_indices_to_close.push(i);
108 }
109 }
110
111 if !compressed_pda_infos.is_empty() {
112 LightSystemProgramCpi::new_cpi(cpi_signer, proof)
113 .with_account_infos(&compressed_pda_infos)
114 .invoke(cpi_accounts.clone())?;
115
116 for idx in pda_indices_to_close {
117 let mut info = solana_accounts[idx].clone();
118 crate::compressible::close::close(&mut info, ctx.rent_sponsor().clone())
119 .map_err(ProgramError::from)?;
120 }
121 }
122
123 Ok(())
124}