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
83 invoke_light_system_program(&account_infos, instruction, self.get_bump())
84 }
85
86 #[cfg(feature = "cpi-context")]
87 fn invoke_write_to_cpi_context_first<'info>(
88 self,
89 accounts: impl CpiAccountsTrait<'info>,
90 ) -> Result<(), ProgramError> {
91 let instruction_data = self.write_to_cpi_context_first();
92 inner_invoke_write_to_cpi_context_typed(instruction_data, accounts)
93 }
94
95 #[cfg(feature = "cpi-context")]
96 fn invoke_write_to_cpi_context_set<'info>(
97 self,
98 accounts: impl CpiAccountsTrait<'info>,
99 ) -> Result<(), ProgramError> {
100 let instruction_data = self.write_to_cpi_context_set();
101 inner_invoke_write_to_cpi_context_typed(instruction_data, accounts)
102 }
103
104 #[cfg(feature = "cpi-context")]
105 fn invoke_execute_cpi_context<'info>(
106 self,
107 accounts: impl CpiAccountsTrait<'info>,
108 ) -> Result<(), ProgramError> {
109 let instruction_data = self.execute_with_cpi_context();
110 let data = instruction_data
112 .data()
113 .map_err(LightSdkError::from)
114 .map_err(ProgramError::from)?;
115
116 let account_infos = accounts.to_account_infos();
118 let account_metas = accounts.to_account_metas()?;
119
120 let instruction = Instruction {
121 program_id: LIGHT_SYSTEM_PROGRAM_ID.into(),
122 accounts: account_metas,
123 data,
124 };
125 invoke_light_system_program(&account_infos, instruction, instruction_data.get_bump())
126 }
127}
128
129#[cfg(feature = "cpi-context")]
131#[inline(always)]
132fn inner_invoke_write_to_cpi_context_typed<'info, T>(
133 instruction_data: T,
134 accounts: impl CpiAccountsTrait<'info>,
135) -> Result<(), ProgramError>
136where
137 T: LightInstructionData + LightCpiInstruction,
138{
139 if instruction_data.has_read_only_accounts() {
141 solana_msg::msg!(
142 "Read-only accounts are not supported in write_to_cpi_context operations. Use invoke_execute_cpi_context() instead."
143 );
144 return Err(LightSdkError::ReadOnlyAccountsNotSupportedInCpiContext.into());
145 }
146
147 let data = instruction_data
149 .data()
150 .map_err(LightSdkError::from)
151 .map_err(ProgramError::from)?;
152
153 let account_infos = accounts.to_account_infos();
155
156 if account_infos.len() < 3 {
159 return Err(ProgramError::NotEnoughAccountKeys);
160 }
161
162 let instruction = Instruction {
163 program_id: LIGHT_SYSTEM_PROGRAM_ID.into(),
164 accounts: vec![
165 AccountMeta {
166 pubkey: *account_infos[0].key, is_writable: true,
168 is_signer: true,
169 },
170 AccountMeta {
171 pubkey: *account_infos[1].key, is_writable: false,
173 is_signer: true,
174 },
175 AccountMeta {
176 pubkey: *account_infos[2].key, is_writable: true,
178 is_signer: false,
179 },
180 ],
181 data,
182 };
183
184 invoke_light_system_program(&account_infos, instruction, instruction_data.get_bump())
185}
186
187#[inline(always)]
193pub fn invoke_light_system_program(
194 account_infos: &[AccountInfo],
195 instruction: Instruction,
196 bump: u8,
197) -> Result<(), ProgramError> {
198 let signer_seeds = [CPI_AUTHORITY_PDA_SEED, &[bump]];
199 invoke_signed(&instruction, account_infos, &[signer_seeds.as_slice()])
200}