light_sdk/cpi/v2/
invoke.rs

1use light_compressed_account::instruction_data::{
2    compressed_proof::ValidityProof, with_account_info::InstructionDataInvokeCpiWithAccountInfo,
3};
4use light_sdk_types::CpiSigner;
5
6#[cfg(feature = "cpi-context")]
7use super::lowlevel::CompressedCpiContext;
8use super::lowlevel::{to_account_metas, InAccount, InstructionDataInvokeCpiWithReadOnly};
9#[cfg(feature = "poseidon")]
10use crate::{account::poseidon::LightAccount as LightAccountPoseidon, DataHasher};
11use crate::{
12    account::LightAccount,
13    cpi::{account::CpiAccountsTrait, instruction::LightCpiInstruction, v2::CpiAccounts},
14    error::LightSdkError,
15    instruction::account_info::CompressedAccountInfoTrait,
16    AccountInfo, AccountMeta, AnchorDeserialize, AnchorSerialize, LightDiscriminator, ProgramError,
17};
18
19impl<'info> CpiAccountsTrait<'info> for CpiAccounts<'_, 'info> {
20    fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
21        self.to_account_infos()
22    }
23
24    fn to_account_metas(&self) -> Result<Vec<AccountMeta>, ProgramError> {
25        to_account_metas(self).map_err(ProgramError::from)
26    }
27
28    fn get_mode(&self) -> Option<u8> {
29        Some(1) // v2 mode
30    }
31}
32
33impl LightCpiInstruction for InstructionDataInvokeCpiWithReadOnly {
34    fn new_cpi(cpi_signer: CpiSigner, proof: ValidityProof) -> Self {
35        Self {
36            bump: cpi_signer.bump,
37            invoking_program_id: cpi_signer.program_id.into(),
38            proof: proof.into(),
39            mode: 1,
40            ..Default::default()
41        }
42    }
43
44    fn with_light_account<A>(mut self, account: LightAccount<A>) -> Result<Self, ProgramError>
45    where
46        A: AnchorSerialize + AnchorDeserialize + LightDiscriminator + Default,
47    {
48        // Check if this is a read-only account
49        if account.read_only_account_hash.is_some() {
50            let read_only_account = account.to_packed_read_only_account()?;
51            self.read_only_accounts.push(read_only_account);
52            return Ok(self);
53        }
54
55        // Convert LightAccount to instruction data format
56        let account_info = account.to_account_info()?;
57
58        // Handle input accounts
59        if let Some(input_account) = account_info
60            .input_compressed_account(self.invoking_program_id.to_bytes().into())
61            .map_err(LightSdkError::from)
62            .map_err(ProgramError::from)?
63        {
64            // Convert to InAccount format
65            let in_account = InAccount {
66                discriminator: input_account
67                    .compressed_account
68                    .data
69                    .as_ref()
70                    .map(|d| d.discriminator)
71                    .unwrap_or_default(),
72                data_hash: input_account
73                    .compressed_account
74                    .data
75                    .as_ref()
76                    .map(|d| d.data_hash)
77                    .unwrap_or_default(),
78                merkle_context: input_account.merkle_context,
79                root_index: input_account.root_index,
80                lamports: input_account.compressed_account.lamports,
81                address: input_account.compressed_account.address,
82            };
83            self.input_compressed_accounts.push(in_account);
84        }
85
86        // Handle output accounts
87        if let Some(output_account) = account_info
88            .output_compressed_account(self.invoking_program_id.to_bytes().into())
89            .map_err(LightSdkError::from)
90            .map_err(ProgramError::from)?
91        {
92            self.output_compressed_accounts.push(output_account);
93        }
94
95        Ok(self)
96    }
97
98    #[cfg(feature = "poseidon")]
99    fn with_light_account_poseidon<A>(
100        mut self,
101        account: LightAccountPoseidon<A>,
102    ) -> Result<Self, ProgramError>
103    where
104        A: AnchorSerialize + AnchorDeserialize + DataHasher + LightDiscriminator + Default,
105    {
106        // Check if this is a read-only account
107        if account.read_only_account_hash.is_some() {
108            let read_only_account = account.to_packed_read_only_account()?;
109            self.read_only_accounts.push(read_only_account);
110            return Ok(self);
111        }
112
113        // Convert LightAccount to instruction data format
114        let account_info = account.to_account_info()?;
115
116        // Handle input accounts
117        if let Some(input_account) = account_info
118            .input_compressed_account(self.invoking_program_id.to_bytes().into())
119            .map_err(LightSdkError::from)
120            .map_err(ProgramError::from)?
121        {
122            // Convert to InAccount format
123            let in_account = InAccount {
124                discriminator: input_account
125                    .compressed_account
126                    .data
127                    .as_ref()
128                    .map(|d| d.discriminator)
129                    .unwrap_or_default(),
130                data_hash: input_account
131                    .compressed_account
132                    .data
133                    .as_ref()
134                    .map(|d| d.data_hash)
135                    .unwrap_or_default(),
136                merkle_context: input_account.merkle_context,
137                root_index: input_account.root_index,
138                lamports: input_account.compressed_account.lamports,
139                address: input_account.compressed_account.address,
140            };
141            self.input_compressed_accounts.push(in_account);
142        }
143
144        // Handle output accounts
145        if let Some(output_account) = account_info
146            .output_compressed_account(self.invoking_program_id.to_bytes().into())
147            .map_err(LightSdkError::from)
148            .map_err(ProgramError::from)?
149        {
150            self.output_compressed_accounts.push(output_account);
151        }
152
153        Ok(self)
154    }
155
156    #[cfg(feature = "cpi-context")]
157    fn write_to_cpi_context_first(self) -> Self {
158        self.write_to_cpi_context_first()
159    }
160
161    #[cfg(feature = "cpi-context")]
162    fn write_to_cpi_context_set(self) -> Self {
163        self.write_to_cpi_context_set()
164    }
165
166    #[cfg(feature = "cpi-context")]
167    fn execute_with_cpi_context(self) -> Self {
168        self.execute_with_cpi_context()
169    }
170
171    fn get_mode(&self) -> u8 {
172        self.mode
173    }
174
175    #[cfg(feature = "cpi-context")]
176    fn get_with_cpi_context(&self) -> bool {
177        self.with_cpi_context
178    }
179
180    #[cfg(feature = "cpi-context")]
181    fn get_cpi_context(&self) -> &CompressedCpiContext {
182        &self.cpi_context
183    }
184
185    fn get_bump(&self) -> u8 {
186        self.bump
187    }
188
189    #[cfg(feature = "cpi-context")]
190    fn has_read_only_accounts(&self) -> bool {
191        !self.read_only_accounts.is_empty()
192    }
193}
194
195impl LightCpiInstruction for InstructionDataInvokeCpiWithAccountInfo {
196    fn new_cpi(cpi_signer: CpiSigner, proof: ValidityProof) -> Self {
197        Self {
198            bump: cpi_signer.bump,
199            invoking_program_id: cpi_signer.program_id.into(),
200            proof: proof.into(),
201            mode: 1,
202            ..Default::default()
203        }
204    }
205
206    fn with_light_account<A>(mut self, account: LightAccount<A>) -> Result<Self, ProgramError>
207    where
208        A: AnchorSerialize + AnchorDeserialize + LightDiscriminator + Default,
209    {
210        // Check if this is a read-only account
211        if account.read_only_account_hash.is_some() {
212            let read_only_account = account.to_packed_read_only_account()?;
213            self.read_only_accounts.push(read_only_account);
214            return Ok(self);
215        }
216
217        // Convert LightAccount to instruction data format
218        let account_info = account.to_account_info()?;
219        self.account_infos.push(account_info);
220        Ok(self)
221    }
222
223    #[cfg(feature = "poseidon")]
224    fn with_light_account_poseidon<A>(
225        mut self,
226        account: crate::account::poseidon::LightAccount<A>,
227    ) -> Result<Self, ProgramError>
228    where
229        A: AnchorSerialize + AnchorDeserialize + LightDiscriminator + DataHasher + Default,
230    {
231        // Check if this is a read-only account
232        if account.read_only_account_hash.is_some() {
233            let read_only_account = account.to_packed_read_only_account()?;
234            self.read_only_accounts.push(read_only_account);
235            return Ok(self);
236        }
237
238        // Convert LightAccount to instruction data format
239        let account_info = account.to_account_info()?;
240        self.account_infos.push(account_info);
241        Ok(self)
242    }
243
244    #[cfg(feature = "cpi-context")]
245    fn write_to_cpi_context_first(self) -> Self {
246        self.write_to_cpi_context_first()
247    }
248
249    #[cfg(feature = "cpi-context")]
250    fn write_to_cpi_context_set(self) -> Self {
251        self.write_to_cpi_context_set()
252    }
253
254    #[cfg(feature = "cpi-context")]
255    fn execute_with_cpi_context(self) -> Self {
256        self.execute_with_cpi_context()
257    }
258
259    fn get_mode(&self) -> u8 {
260        self.mode
261    }
262
263    #[cfg(feature = "cpi-context")]
264    fn get_with_cpi_context(&self) -> bool {
265        self.with_cpi_context
266    }
267
268    #[cfg(feature = "cpi-context")]
269    fn get_cpi_context(&self) -> &CompressedCpiContext {
270        &self.cpi_context
271    }
272
273    fn get_bump(&self) -> u8 {
274        self.bump
275    }
276
277    #[cfg(feature = "cpi-context")]
278    fn has_read_only_accounts(&self) -> bool {
279        !self.read_only_accounts.is_empty()
280    }
281}