1use {
5 crate::{error::TransferHookError, get_extra_account_metas_address, instruction},
6 miraland_program::{
7 account_info::AccountInfo,
8 entrypoint::ProgramResult,
9 instruction::{AccountMeta, Instruction},
10 program::invoke,
11 pubkey::Pubkey,
12 },
13 spl_tlv_account_resolution::state::ExtraAccountMetaList,
14};
15pub fn invoke_execute<'a>(
18 program_id: &Pubkey,
19 source_info: AccountInfo<'a>,
20 mint_info: AccountInfo<'a>,
21 destination_info: AccountInfo<'a>,
22 authority_info: AccountInfo<'a>,
23 additional_accounts: &[AccountInfo<'a>],
24 amount: u64,
25) -> ProgramResult {
26 let validation_pubkey = get_extra_account_metas_address(mint_info.key, program_id);
27 let validation_info = additional_accounts
28 .iter()
29 .find(|&x| *x.key == validation_pubkey)
30 .ok_or(TransferHookError::IncorrectAccount)?;
31 let mut cpi_instruction = instruction::execute(
32 program_id,
33 source_info.key,
34 mint_info.key,
35 destination_info.key,
36 authority_info.key,
37 &validation_pubkey,
38 amount,
39 );
40
41 let mut cpi_account_infos = vec![
42 source_info,
43 mint_info,
44 destination_info,
45 authority_info,
46 validation_info.clone(),
47 ];
48 ExtraAccountMetaList::add_to_cpi_instruction::<instruction::ExecuteInstruction>(
49 &mut cpi_instruction,
50 &mut cpi_account_infos,
51 &validation_info.try_borrow_data()?,
52 additional_accounts,
53 )?;
54 invoke(&cpi_instruction, &cpi_account_infos)
55}
56
57#[allow(clippy::too_many_arguments)]
67pub fn add_extra_accounts_for_execute_cpi<'a>(
68 cpi_instruction: &mut Instruction,
69 cpi_account_infos: &mut Vec<AccountInfo<'a>>,
70 program_id: &Pubkey,
71 source_info: AccountInfo<'a>,
72 mint_info: AccountInfo<'a>,
73 destination_info: AccountInfo<'a>,
74 authority_info: AccountInfo<'a>,
75 amount: u64,
76 additional_accounts: &[AccountInfo<'a>],
77) -> ProgramResult {
78 let validate_state_pubkey = get_extra_account_metas_address(mint_info.key, program_id);
79 let validate_state_info = additional_accounts
80 .iter()
81 .find(|&x| *x.key == validate_state_pubkey)
82 .ok_or(TransferHookError::IncorrectAccount)?;
83
84 let program_info = additional_accounts
85 .iter()
86 .find(|&x| x.key == program_id)
87 .ok_or(TransferHookError::IncorrectAccount)?;
88
89 let mut execute_instruction = instruction::execute(
90 program_id,
91 source_info.key,
92 mint_info.key,
93 destination_info.key,
94 authority_info.key,
95 &validate_state_pubkey,
96 amount,
97 );
98 let mut execute_account_infos = vec![
99 source_info,
100 mint_info,
101 destination_info,
102 authority_info,
103 validate_state_info.clone(),
104 ];
105
106 ExtraAccountMetaList::add_to_cpi_instruction::<instruction::ExecuteInstruction>(
107 &mut execute_instruction,
108 &mut execute_account_infos,
109 &validate_state_info.try_borrow_data()?,
110 additional_accounts,
111 )?;
112
113 cpi_instruction
115 .accounts
116 .extend_from_slice(&execute_instruction.accounts[5..]);
117 cpi_account_infos.extend_from_slice(&execute_account_infos[5..]);
118
119 cpi_instruction
121 .accounts
122 .push(AccountMeta::new_readonly(*program_id, false));
123 cpi_instruction
124 .accounts
125 .push(AccountMeta::new_readonly(validate_state_pubkey, false));
126 cpi_account_infos.push(program_info.clone());
127 cpi_account_infos.push(validate_state_info.clone());
128
129 Ok(())
130}
131
132#[cfg(test)]
133mod tests {
134 use {
135 super::*,
136 crate::instruction::ExecuteInstruction,
137 miraland_program::{bpf_loader_upgradeable, system_program},
138 spl_tlv_account_resolution::{
139 account::ExtraAccountMeta, error::AccountResolutionError, seeds::Seed,
140 },
141 };
142
143 const EXTRA_META_1: Pubkey = Pubkey::new_from_array([2u8; 32]);
144 const EXTRA_META_2: Pubkey = Pubkey::new_from_array([3u8; 32]);
145
146 fn setup_validation_data() -> Vec<u8> {
147 let extra_metas = vec![
148 ExtraAccountMeta::new_with_pubkey(&EXTRA_META_1, true, false).unwrap(),
149 ExtraAccountMeta::new_with_pubkey(&EXTRA_META_2, true, false).unwrap(),
150 ExtraAccountMeta::new_with_seeds(
151 &[
152 Seed::AccountKey { index: 0 }, Seed::AccountKey { index: 2 }, Seed::AccountKey { index: 4 }, ],
156 false,
157 true,
158 )
159 .unwrap(),
160 ExtraAccountMeta::new_with_seeds(
161 &[
162 Seed::InstructionData {
163 index: 8,
164 length: 8,
165 }, Seed::AccountKey { index: 2 }, Seed::AccountKey { index: 5 }, Seed::AccountKey { index: 7 }, ],
170 false,
171 true,
172 )
173 .unwrap(),
174 ];
175 let account_size = ExtraAccountMetaList::size_of(extra_metas.len()).unwrap();
176 let mut data = vec![0u8; account_size];
177 ExtraAccountMetaList::init::<ExecuteInstruction>(&mut data, &extra_metas).unwrap();
178 data
179 }
180
181 #[test]
182 fn test_add_extra_accounts_for_execute_cpi() {
183 let spl_token_2022_program_id = Pubkey::new_unique(); let transfer_hook_program_id = Pubkey::new_unique();
185
186 let amount = 100u64;
187
188 let source_pubkey = Pubkey::new_unique();
189 let mut source_data = vec![0; 165]; let mut source_lamports = 0; let source_account_info = AccountInfo::new(
192 &source_pubkey,
193 false,
194 true,
195 &mut source_lamports,
196 &mut source_data,
197 &spl_token_2022_program_id,
198 false,
199 0,
200 );
201
202 let mint_pubkey = Pubkey::new_unique();
203 let mut mint_data = vec![0; 165]; let mut mint_lamports = 0; let mint_account_info = AccountInfo::new(
206 &mint_pubkey,
207 false,
208 true,
209 &mut mint_lamports,
210 &mut mint_data,
211 &spl_token_2022_program_id,
212 false,
213 0,
214 );
215
216 let destination_pubkey = Pubkey::new_unique();
217 let mut destination_data = vec![0; 165]; let mut destination_lamports = 0; let destination_account_info = AccountInfo::new(
220 &destination_pubkey,
221 false,
222 true,
223 &mut destination_lamports,
224 &mut destination_data,
225 &spl_token_2022_program_id,
226 false,
227 0,
228 );
229
230 let authority_pubkey = Pubkey::new_unique();
231 let mut authority_data = vec![]; let mut authority_lamports = 0; let authority_account_info = AccountInfo::new(
234 &authority_pubkey,
235 false,
236 true,
237 &mut authority_lamports,
238 &mut authority_data,
239 &system_program::ID,
240 false,
241 0,
242 );
243
244 let validate_state_pubkey =
245 get_extra_account_metas_address(&mint_pubkey, &transfer_hook_program_id);
246
247 let extra_meta_1_pubkey = EXTRA_META_1;
248 let mut extra_meta_1_data = vec![]; let mut extra_meta_1_lamports = 0; let extra_meta_1_account_info = AccountInfo::new(
251 &extra_meta_1_pubkey,
252 true,
253 false,
254 &mut extra_meta_1_lamports,
255 &mut extra_meta_1_data,
256 &system_program::ID,
257 false,
258 0,
259 );
260
261 let extra_meta_2_pubkey = EXTRA_META_2;
262 let mut extra_meta_2_data = vec![]; let mut extra_meta_2_lamports = 0; let extra_meta_2_account_info = AccountInfo::new(
265 &extra_meta_2_pubkey,
266 true,
267 false,
268 &mut extra_meta_2_lamports,
269 &mut extra_meta_2_data,
270 &system_program::ID,
271 false,
272 0,
273 );
274
275 let extra_meta_3_pubkey = Pubkey::find_program_address(
276 &[
277 &source_pubkey.to_bytes(),
278 &destination_pubkey.to_bytes(),
279 &validate_state_pubkey.to_bytes(),
280 ],
281 &transfer_hook_program_id,
282 )
283 .0;
284 let mut extra_meta_3_data = vec![]; let mut extra_meta_3_lamports = 0; let extra_meta_3_account_info = AccountInfo::new(
287 &extra_meta_3_pubkey,
288 false,
289 true,
290 &mut extra_meta_3_lamports,
291 &mut extra_meta_3_data,
292 &transfer_hook_program_id,
293 false,
294 0,
295 );
296
297 let extra_meta_4_pubkey = Pubkey::find_program_address(
298 &[
299 &amount.to_le_bytes(),
300 &destination_pubkey.to_bytes(),
301 &extra_meta_1_pubkey.to_bytes(),
302 &extra_meta_3_pubkey.to_bytes(),
303 ],
304 &transfer_hook_program_id,
305 )
306 .0;
307 let mut extra_meta_4_data = vec![]; let mut extra_meta_4_lamports = 0; let extra_meta_4_account_info = AccountInfo::new(
310 &extra_meta_4_pubkey,
311 false,
312 true,
313 &mut extra_meta_4_lamports,
314 &mut extra_meta_4_data,
315 &transfer_hook_program_id,
316 false,
317 0,
318 );
319
320 let mut validate_state_data = setup_validation_data();
321 let mut validate_state_lamports = 0; let validate_state_account_info = AccountInfo::new(
323 &validate_state_pubkey,
324 false,
325 true,
326 &mut validate_state_lamports,
327 &mut validate_state_data,
328 &transfer_hook_program_id,
329 false,
330 0,
331 );
332
333 let mut transfer_hook_program_data = vec![]; let mut transfer_hook_program_lamports = 0; let transfer_hook_program_account_info = AccountInfo::new(
336 &transfer_hook_program_id,
337 false,
338 true,
339 &mut transfer_hook_program_lamports,
340 &mut transfer_hook_program_data,
341 &bpf_loader_upgradeable::ID,
342 false,
343 0,
344 );
345
346 let mut cpi_instruction = Instruction::new_with_bytes(
347 spl_token_2022_program_id,
348 &[],
349 vec![
350 AccountMeta::new(source_pubkey, false),
351 AccountMeta::new_readonly(mint_pubkey, false),
352 AccountMeta::new(destination_pubkey, false),
353 AccountMeta::new_readonly(authority_pubkey, true),
354 ],
355 );
356 let mut cpi_account_infos = vec![
357 source_account_info.clone(),
358 mint_account_info.clone(),
359 destination_account_info.clone(),
360 authority_account_info.clone(),
361 ];
362 let additional_account_infos = vec![
363 extra_meta_1_account_info.clone(),
364 extra_meta_2_account_info.clone(),
365 extra_meta_3_account_info.clone(),
366 extra_meta_4_account_info.clone(),
367 transfer_hook_program_account_info.clone(),
368 validate_state_account_info.clone(),
369 ];
370
371 let additional_account_infos_missing_infos = vec![
373 extra_meta_1_account_info.clone(),
374 extra_meta_2_account_info.clone(),
375 extra_meta_3_account_info.clone(),
376 extra_meta_4_account_info.clone(),
377 transfer_hook_program_account_info.clone(),
379 ];
380 assert_eq!(
381 add_extra_accounts_for_execute_cpi(
382 &mut cpi_instruction,
383 &mut cpi_account_infos,
384 &transfer_hook_program_id,
385 source_account_info.clone(),
386 mint_account_info.clone(),
387 destination_account_info.clone(),
388 authority_account_info.clone(),
389 amount,
390 &additional_account_infos_missing_infos, )
392 .unwrap_err(),
393 TransferHookError::IncorrectAccount.into()
394 );
395
396 let additional_account_infos_missing_infos = vec![
398 extra_meta_1_account_info.clone(),
399 extra_meta_2_account_info.clone(),
400 extra_meta_3_account_info.clone(),
401 extra_meta_4_account_info.clone(),
402 validate_state_account_info.clone(),
403 ];
405 assert_eq!(
406 add_extra_accounts_for_execute_cpi(
407 &mut cpi_instruction,
408 &mut cpi_account_infos,
409 &transfer_hook_program_id,
410 source_account_info.clone(),
411 mint_account_info.clone(),
412 destination_account_info.clone(),
413 authority_account_info.clone(),
414 amount,
415 &additional_account_infos_missing_infos, )
417 .unwrap_err(),
418 TransferHookError::IncorrectAccount.into()
419 );
420
421 let additional_account_infos_missing_infos = vec![
423 extra_meta_1_account_info.clone(),
424 extra_meta_2_account_info.clone(),
425 extra_meta_4_account_info.clone(),
427 validate_state_account_info.clone(),
428 transfer_hook_program_account_info.clone(),
429 ];
430 assert_eq!(
431 add_extra_accounts_for_execute_cpi(
432 &mut cpi_instruction,
433 &mut cpi_account_infos,
434 &transfer_hook_program_id,
435 source_account_info.clone(),
436 mint_account_info.clone(),
437 destination_account_info.clone(),
438 authority_account_info.clone(),
439 amount,
440 &additional_account_infos_missing_infos, )
442 .unwrap_err(),
443 AccountResolutionError::IncorrectAccount.into() );
445
446 add_extra_accounts_for_execute_cpi(
448 &mut cpi_instruction,
449 &mut cpi_account_infos,
450 &transfer_hook_program_id,
451 source_account_info.clone(),
452 mint_account_info.clone(),
453 destination_account_info.clone(),
454 authority_account_info.clone(),
455 amount,
456 &additional_account_infos,
457 )
458 .unwrap();
459
460 let check_metas = [
461 AccountMeta::new(source_pubkey, false),
462 AccountMeta::new_readonly(mint_pubkey, false),
463 AccountMeta::new(destination_pubkey, false),
464 AccountMeta::new_readonly(authority_pubkey, true),
465 AccountMeta::new_readonly(EXTRA_META_1, true),
466 AccountMeta::new_readonly(EXTRA_META_2, true),
467 AccountMeta::new(extra_meta_3_pubkey, false),
468 AccountMeta::new(extra_meta_4_pubkey, false),
469 AccountMeta::new_readonly(transfer_hook_program_id, false),
470 AccountMeta::new_readonly(validate_state_pubkey, false),
471 ];
472
473 let check_account_infos = vec![
474 source_account_info,
475 mint_account_info,
476 destination_account_info,
477 authority_account_info,
478 extra_meta_1_account_info,
479 extra_meta_2_account_info,
480 extra_meta_3_account_info,
481 extra_meta_4_account_info,
482 transfer_hook_program_account_info,
483 validate_state_account_info,
484 ];
485
486 assert_eq!(cpi_instruction.accounts, check_metas);
487 for (a, b) in std::iter::zip(cpi_account_infos, check_account_infos) {
488 assert_eq!(a.key, b.key);
489 assert_eq!(a.is_signer, b.is_signer);
490 assert_eq!(a.is_writable, b.is_writable);
491 }
492 }
493}