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