1pub use light_compressed_account::LightInstructionData;
2use light_sdk_types::constants::{CPI_AUTHORITY_PDA_SEED, LIGHT_SYSTEM_PROGRAM_ID};
3use solana_account_info::AccountInfo;
4use solana_cpi::invoke_signed;
5#[cfg(feature = "cpi-context")]
6use solana_instruction::AccountMeta;
7use solana_instruction::Instruction;
8use solana_program_error::ProgramError;
9
10use crate::{
11 cpi::{account::CpiAccountsTrait, instruction::LightCpiInstruction},
12 error::LightSdkError,
13};
14
15pub trait InvokeLightSystemProgram {
16 fn invoke<'info>(self, accounts: impl CpiAccountsTrait<'info>) -> Result<(), ProgramError>;
17
18 #[cfg(feature = "cpi-context")]
19 fn invoke_write_to_cpi_context_first<'info>(
20 self,
21 accounts: impl CpiAccountsTrait<'info>,
22 ) -> Result<(), ProgramError>;
23
24 #[cfg(feature = "cpi-context")]
25 fn invoke_write_to_cpi_context_set<'info>(
26 self,
27 accounts: impl CpiAccountsTrait<'info>,
28 ) -> Result<(), ProgramError>;
29
30 #[cfg(feature = "cpi-context")]
31 fn invoke_execute_cpi_context<'info>(
32 self,
33 accounts: impl CpiAccountsTrait<'info>,
34 ) -> Result<(), ProgramError>;
35}
36
37impl<T> InvokeLightSystemProgram for T
39where
40 T: LightInstructionData + LightCpiInstruction,
41{
42 fn invoke<'info>(self, accounts: impl CpiAccountsTrait<'info>) -> Result<(), ProgramError> {
43 #[cfg(feature = "cpi-context")]
44 {
45 use light_compressed_account::instruction_data::cpi_context::CompressedCpiContext;
47 if self.get_with_cpi_context()
48 || *self.get_cpi_context() == CompressedCpiContext::set()
49 || *self.get_cpi_context() == CompressedCpiContext::first()
50 {
51 solana_msg::msg!(
52 "CPI context operations not supported in invoke(). Use invoke_write_to_cpi_context_first(), invoke_write_to_cpi_context_set(), or invoke_execute_cpi_context() instead"
53 );
54 return Err(ProgramError::InvalidInstructionData);
55 }
56 }
57
58 if let Some(account_mode) = accounts.get_mode() {
60 if account_mode != self.get_mode() {
61 solana_msg::msg!(
62 "Mode mismatch: accounts have mode {} but instruction data has mode {}",
63 account_mode,
64 self.get_mode()
65 );
66 return Err(ProgramError::InvalidInstructionData);
67 }
68 }
69
70 let data = self
72 .data()
73 .map_err(LightSdkError::from)
74 .map_err(ProgramError::from)?;
75
76 let account_infos = accounts.to_account_infos();
78 let account_metas = accounts.to_account_metas()?;
79
80 let instruction = Instruction {
81 program_id: LIGHT_SYSTEM_PROGRAM_ID.into(),
82 accounts: account_metas,
83 data,
84 };
85 invoke_light_system_program(&account_infos, instruction, self.get_bump())
86 }
87
88 #[cfg(feature = "cpi-context")]
89 fn invoke_write_to_cpi_context_first<'info>(
90 self,
91 accounts: impl CpiAccountsTrait<'info>,
92 ) -> Result<(), ProgramError> {
93 let instruction_data = self.write_to_cpi_context_first();
94 inner_invoke_write_to_cpi_context_typed(instruction_data, accounts)
95 }
96
97 #[cfg(feature = "cpi-context")]
98 fn invoke_write_to_cpi_context_set<'info>(
99 self,
100 accounts: impl CpiAccountsTrait<'info>,
101 ) -> Result<(), ProgramError> {
102 let instruction_data = self.write_to_cpi_context_set();
103 inner_invoke_write_to_cpi_context_typed(instruction_data, accounts)
104 }
105
106 #[cfg(feature = "cpi-context")]
107 fn invoke_execute_cpi_context<'info>(
108 self,
109 accounts: impl CpiAccountsTrait<'info>,
110 ) -> Result<(), ProgramError> {
111 let instruction_data = self.execute_with_cpi_context();
112 let data = instruction_data
114 .data()
115 .map_err(LightSdkError::from)
116 .map_err(ProgramError::from)?;
117
118 let account_infos = accounts.to_account_infos();
120 let account_metas = accounts.to_account_metas()?;
121
122 let instruction = Instruction {
123 program_id: LIGHT_SYSTEM_PROGRAM_ID.into(),
124 accounts: account_metas,
125 data,
126 };
127 invoke_light_system_program(&account_infos, instruction, instruction_data.get_bump())
128 }
129}
130
131#[cfg(feature = "cpi-context")]
133#[inline(always)]
134fn inner_invoke_write_to_cpi_context_typed<'info, T>(
135 instruction_data: T,
136 accounts: impl CpiAccountsTrait<'info>,
137) -> Result<(), ProgramError>
138where
139 T: LightInstructionData + LightCpiInstruction,
140{
141 if instruction_data.has_read_only_accounts() {
143 solana_msg::msg!(
144 "Read-only accounts are not supported in write_to_cpi_context operations. Use invoke_execute_cpi_context() instead."
145 );
146 return Err(LightSdkError::ReadOnlyAccountsNotSupportedInCpiContext.into());
147 }
148
149 let data = instruction_data
151 .data()
152 .map_err(LightSdkError::from)
153 .map_err(ProgramError::from)?;
154
155 let account_infos = accounts.to_account_infos();
157
158 if account_infos.len() < 3 {
161 return Err(ProgramError::NotEnoughAccountKeys);
162 }
163
164 let instruction = Instruction {
165 program_id: LIGHT_SYSTEM_PROGRAM_ID.into(),
166 accounts: vec![
167 AccountMeta {
168 pubkey: *account_infos[0].key, is_writable: true,
170 is_signer: true,
171 },
172 AccountMeta {
173 pubkey: *account_infos[1].key, is_writable: false,
175 is_signer: true,
176 },
177 AccountMeta {
178 pubkey: *account_infos[2].key, is_writable: true,
180 is_signer: false,
181 },
182 ],
183 data,
184 };
185
186 invoke_light_system_program(&account_infos, instruction, instruction_data.get_bump())
187}
188
189#[inline(always)]
195pub fn invoke_light_system_program(
196 account_infos: &[AccountInfo],
197 instruction: Instruction,
198 bump: u8,
199) -> Result<(), ProgramError> {
200 let signer_seeds = [CPI_AUTHORITY_PDA_SEED, &[bump]];
201 invoke_signed(&instruction, account_infos, &[signer_seeds.as_slice()])
202}