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