1use anchor_spl::token::{Mint, TokenAccount};
2use forester_utils::create_account_instruction;
3use forester_utils::indexer::{Indexer, TokenDataWithContext};
4use light_compressed_token::{
5 burn::sdk::{create_burn_instruction, CreateBurnInstructionInputs},
6 delegation::sdk::{
7 create_approve_instruction, create_revoke_instruction, CreateApproveInstructionInputs,
8 CreateRevokeInstructionInputs,
9 },
10 freeze::sdk::{create_instruction, CreateInstructionInputs},
11 get_token_pool_pda,
12 mint_sdk::{create_create_token_pool_instruction, create_mint_to_instruction},
13 process_transfer::{transfer_sdk::create_transfer_instruction, TokenTransferOutputData},
14 token_data::AccountState,
15 TokenData,
16};
17use light_hasher::Poseidon;
18use light_system_program::{
19 invoke::processor::CompressedProof,
20 sdk::{compressed_account::MerkleContext, event::PublicTransactionEvent},
21};
22use solana_program_test::BanksClientError;
23use solana_sdk::{
24 instruction::Instruction,
25 program_pack::Pack,
26 pubkey::Pubkey,
27 signature::{Keypair, Signature, Signer},
28};
29use spl_token::instruction::initialize_mint;
30
31use crate::{
32 assert_compressed_tx::get_merkle_tree_snapshots,
33 assert_token_tx::{assert_create_mint, assert_mint_to, assert_transfer},
34};
35use light_client::rpc::errors::RpcError;
36use light_client::rpc::RpcConnection;
37use light_client::transaction_params::TransactionParams;
38
39pub async fn mint_tokens_helper<R: RpcConnection, I: Indexer<R>>(
40 rpc: &mut R,
41 test_indexer: &mut I,
42 merkle_tree_pubkey: &Pubkey,
43 mint_authority: &Keypair,
44 mint: &Pubkey,
45 amounts: Vec<u64>,
46 recipients: Vec<Pubkey>,
47) {
48 mint_tokens_helper_with_lamports(
49 rpc,
50 test_indexer,
51 merkle_tree_pubkey,
52 mint_authority,
53 mint,
54 amounts,
55 recipients,
56 None,
57 )
58 .await
59}
60#[allow(clippy::too_many_arguments)]
61pub async fn mint_tokens_helper_with_lamports<R: RpcConnection, I: Indexer<R>>(
62 rpc: &mut R,
63 test_indexer: &mut I,
64 merkle_tree_pubkey: &Pubkey,
65 mint_authority: &Keypair,
66 mint: &Pubkey,
67 amounts: Vec<u64>,
68 recipients: Vec<Pubkey>,
69 lamports: Option<u64>,
70) {
71 let payer_pubkey = mint_authority.pubkey();
72 let instruction = create_mint_to_instruction(
73 &payer_pubkey,
74 &payer_pubkey,
75 mint,
76 merkle_tree_pubkey,
77 amounts.clone(),
78 recipients.clone(),
79 lamports,
80 );
81
82 let output_merkle_tree_accounts =
83 test_indexer.get_state_merkle_tree_accounts(&vec![*merkle_tree_pubkey; amounts.len()]);
84
85 let snapshots = get_merkle_tree_snapshots::<R>(rpc, &output_merkle_tree_accounts).await;
86 let previous_mint_supply =
87 spl_token::state::Mint::unpack(&rpc.get_account(*mint).await.unwrap().unwrap().data)
88 .unwrap()
89 .supply;
90
91 let pool: Pubkey = get_token_pool_pda(mint);
92 let previous_pool_amount =
93 spl_token::state::Account::unpack(&rpc.get_account(pool).await.unwrap().unwrap().data)
94 .unwrap()
95 .amount;
96 let (event, _signature, _) = rpc
97 .create_and_send_transaction_with_event::<PublicTransactionEvent>(
98 &[instruction],
99 &payer_pubkey,
100 &[mint_authority],
101 None,
102 )
103 .await
104 .unwrap()
105 .unwrap();
106
107 let (_, created_token_accounts) = test_indexer.add_event_and_compressed_accounts(&event);
108 assert_mint_to(
109 rpc,
110 test_indexer,
111 &recipients,
112 *mint,
113 amounts.as_slice(),
114 &snapshots,
115 &created_token_accounts,
116 previous_mint_supply,
117 previous_pool_amount,
118 )
119 .await;
120}
121
122pub async fn create_token_pool<R: RpcConnection>(
123 rpc: &mut R,
124 payer: &Keypair,
125 mint_authority: &Pubkey,
126 decimals: u8,
127 freeze_authority: Option<&Pubkey>,
128 mint_keypair: Option<&Keypair>,
129) -> Pubkey {
130 let keypair = Keypair::new();
131 let mint_keypair = match mint_keypair {
132 Some(mint_keypair) => mint_keypair,
133 None => &keypair,
134 };
135 let mint_pubkey = (*mint_keypair).pubkey();
136 let mint_rent = rpc
137 .get_minimum_balance_for_rent_exemption(Mint::LEN)
138 .await
139 .unwrap();
140
141 let account_create_ix = create_account_instruction(
142 &payer.pubkey(),
143 Mint::LEN,
144 mint_rent,
145 &light_compressed_token::ID,
146 Some(mint_keypair),
147 );
148
149 let create_mint_ix = spl_token::instruction::initialize_mint2(
150 &spl_token::id(),
151 &mint_pubkey,
152 mint_authority,
153 freeze_authority,
154 decimals,
155 )
156 .unwrap();
157 rpc.create_and_send_transaction(
158 &[account_create_ix, create_mint_ix],
159 &payer.pubkey(),
160 &[payer],
161 )
162 .await
163 .unwrap();
164 mint_pubkey
165}
166
167pub async fn create_mint_helper<R: RpcConnection>(rpc: &mut R, payer: &Keypair) -> Pubkey {
168 let payer_pubkey = payer.pubkey();
169 let rent = rpc
170 .get_minimum_balance_for_rent_exemption(Mint::LEN)
171 .await
172 .unwrap();
173 let mint = Keypair::new();
174
175 let (instructions, pool) =
176 create_initialize_mint_instructions(&payer_pubkey, &payer_pubkey, rent, 2, &mint);
177
178 rpc.create_and_send_transaction(&instructions, &payer_pubkey, &[payer, &mint])
179 .await
180 .unwrap();
181 assert_create_mint(rpc, &payer_pubkey, &mint.pubkey(), &pool).await;
182 mint.pubkey()
183}
184
185pub async fn mint_wrapped_sol<R: RpcConnection>(
186 rpc: &mut R,
187 payer: &Keypair,
188 token_account: &Pubkey,
189 amount: u64,
190) -> Result<Signature, RpcError> {
191 let transfer_ix = anchor_lang::solana_program::system_instruction::transfer(
192 &payer.pubkey(),
193 token_account,
194 amount,
195 );
196 let sync_native_ix = spl_token::instruction::sync_native(&spl_token::ID, token_account)
197 .map_err(|e| RpcError::CustomError(format!("{:?}", e)))?;
198
199 rpc.create_and_send_transaction(&[transfer_ix, sync_native_ix], &payer.pubkey(), &[payer])
200 .await
201}
202
203pub fn create_initialize_mint_instructions(
204 payer: &Pubkey,
205 authority: &Pubkey,
206 rent: u64,
207 decimals: u8,
208 mint_keypair: &Keypair,
209) -> ([Instruction; 4], Pubkey) {
210 let account_create_ix =
211 create_account_instruction(payer, Mint::LEN, rent, &spl_token::ID, Some(mint_keypair));
212
213 let mint_pubkey = mint_keypair.pubkey();
214 let create_mint_instruction = initialize_mint(
215 &spl_token::ID,
216 &mint_keypair.pubkey(),
217 authority,
218 Some(authority),
219 decimals,
220 )
221 .unwrap();
222 let transfer_ix =
223 anchor_lang::solana_program::system_instruction::transfer(payer, &mint_pubkey, rent);
224
225 let instruction = create_create_token_pool_instruction(payer, &mint_pubkey);
226 let pool_pubkey = get_token_pool_pda(&mint_pubkey);
227 (
228 [
229 account_create_ix,
230 create_mint_instruction,
231 transfer_ix,
232 instruction,
233 ],
234 pool_pubkey,
235 )
236}
237
238pub async fn create_token_account<R: RpcConnection>(
241 rpc: &mut R,
242 mint: &Pubkey,
243 account_keypair: &Keypair,
244 owner: &Keypair,
245) -> Result<(), BanksClientError> {
246 let rent = rpc
247 .get_minimum_balance_for_rent_exemption(TokenAccount::LEN)
248 .await
249 .unwrap();
250 let account_create_ix = create_account_instruction(
251 &owner.pubkey(),
252 TokenAccount::LEN,
253 rent,
254 &spl_token::ID,
255 Some(account_keypair),
256 );
257 let instruction = spl_token::instruction::initialize_account(
258 &spl_token::ID,
259 &account_keypair.pubkey(),
260 mint,
261 &owner.pubkey(),
262 )
263 .unwrap();
264 rpc.create_and_send_transaction(
265 &[account_create_ix, instruction],
266 &owner.pubkey(),
267 &[account_keypair, owner],
268 )
269 .await
270 .unwrap();
271 Ok(())
272}
273
274#[allow(clippy::too_many_arguments)]
275pub async fn compressed_transfer_test<R: RpcConnection, I: Indexer<R>>(
276 payer: &Keypair,
277 rpc: &mut R,
278 test_indexer: &mut I,
279 mint: &Pubkey,
280 from: &Keypair,
281 recipients: &[Pubkey],
282 amounts: &[u64],
283 mut lamports: Option<Vec<Option<u64>>>,
284 input_compressed_accounts: &[TokenDataWithContext],
285 output_merkle_tree_pubkeys: &[Pubkey],
286 delegate_change_account_index: Option<u8>,
287 delegate_is_signer: bool,
288 transaction_params: Option<TransactionParams>,
289) {
290 if recipients.len() != amounts.len() && amounts.len() != output_merkle_tree_pubkeys.len() {
291 println!("{:?}", recipients);
292 println!("{:?}", amounts);
293 println!("{:?}", output_merkle_tree_pubkeys);
294 panic!("recipients, amounts, and output_merkle_tree_pubkeys must have the same length");
295 }
296 let mut input_merkle_tree_context = Vec::new();
297 let mut input_compressed_account_token_data = Vec::new();
298 let mut input_compressed_account_hashes = Vec::new();
299 let mut sum_input_amounts = 0;
300 for account in input_compressed_accounts {
301 let leaf_index = account.compressed_account.merkle_context.leaf_index;
302 input_compressed_account_token_data.push(account.token_data.clone());
303 input_compressed_account_hashes.push(
304 account
305 .compressed_account
306 .compressed_account
307 .hash::<Poseidon>(
308 &account.compressed_account.merkle_context.merkle_tree_pubkey,
309 &leaf_index,
310 )
311 .unwrap(),
312 );
313 sum_input_amounts += account.token_data.amount;
314 input_merkle_tree_context.push(MerkleContext {
315 merkle_tree_pubkey: account.compressed_account.merkle_context.merkle_tree_pubkey,
316 nullifier_queue_pubkey: account
317 .compressed_account
318 .merkle_context
319 .nullifier_queue_pubkey,
320 leaf_index,
321 queue_index: None,
322 });
323 }
324 let output_lamports = lamports
325 .clone()
326 .unwrap_or_else(|| vec![None; recipients.len()]);
327 let mut output_compressed_accounts = Vec::new();
328 for (((recipient, amount), merkle_tree_pubkey), lamports) in recipients
329 .iter()
330 .zip(amounts)
331 .zip(output_merkle_tree_pubkeys)
332 .zip(output_lamports)
333 {
334 let account = TokenTransferOutputData {
335 amount: *amount,
336 owner: *recipient,
337 lamports,
338 merkle_tree: *merkle_tree_pubkey,
339 };
340 sum_input_amounts -= amount;
341 output_compressed_accounts.push(account);
342 }
343 if sum_input_amounts > 0 {
345 let account = TokenTransferOutputData {
346 amount: sum_input_amounts,
347 owner: from.pubkey(),
348 lamports: None,
349 merkle_tree: *output_merkle_tree_pubkeys.last().unwrap(),
350 };
351 output_compressed_accounts.push(account);
352 }
353 let input_merkle_tree_pubkeys: Vec<Pubkey> = input_merkle_tree_context
354 .iter()
355 .map(|x| x.merkle_tree_pubkey)
356 .collect();
357 println!("{:?}", input_compressed_accounts);
358 println!(
359 "input_compressed_account_hashes: {:?}",
360 input_compressed_account_hashes
361 );
362 let proof_rpc_result = test_indexer
363 .create_proof_for_compressed_accounts(
364 Some(&input_compressed_account_hashes),
365 Some(&input_merkle_tree_pubkeys),
366 None,
367 None,
368 rpc,
369 )
370 .await;
371 output_compressed_accounts.sort_by(|a, b| a.merkle_tree.cmp(&b.merkle_tree));
372
373 let delegate_pubkey = if delegate_is_signer {
374 Some(payer.pubkey())
375 } else {
376 None
377 };
378 let authority_signer = if delegate_is_signer { payer } else { from };
379 let instruction = create_transfer_instruction(
380 &payer.pubkey(),
381 &authority_signer.pubkey(), &input_merkle_tree_context,
383 &output_compressed_accounts,
384 &proof_rpc_result.root_indices,
385 &Some(proof_rpc_result.proof),
386 &input_compressed_account_token_data, &input_compressed_accounts
388 .iter()
389 .map(|x| &x.compressed_account.compressed_account)
390 .cloned()
391 .collect::<Vec<_>>(),
392 *mint,
393 delegate_pubkey, false, None, None, None, true,
399 delegate_change_account_index,
400 None,
401 )
402 .unwrap();
403 let sum_input_lamports = input_compressed_accounts
404 .iter()
405 .map(|x| &x.compressed_account.compressed_account.lamports)
406 .sum::<u64>();
407 let sum_output_lamports = output_compressed_accounts
408 .iter()
409 .map(|x| x.lamports.unwrap_or(0))
410 .sum::<u64>();
411 let sum_output_amounts = output_compressed_accounts
412 .iter()
413 .map(|x| x.amount)
414 .sum::<u64>();
415 let output_merkle_tree_pubkeys = if sum_input_lamports > sum_output_lamports
416 || sum_input_amounts > sum_output_amounts && delegate_is_signer
417 {
418 let mut output_merkle_tree_pubkeys = output_merkle_tree_pubkeys.to_vec();
419 output_merkle_tree_pubkeys.push(*output_merkle_tree_pubkeys.last().unwrap());
420 if let Some(lamports) = &mut lamports {
421 if sum_input_lamports != sum_output_lamports {
422 lamports.push(Some(sum_input_lamports - sum_output_lamports));
423 } else {
424 lamports.push(None);
425 }
426 }
427 output_merkle_tree_pubkeys
428 } else {
429 output_merkle_tree_pubkeys.to_vec()
430 };
431
432 let output_merkle_tree_accounts =
433 test_indexer.get_state_merkle_tree_accounts(output_merkle_tree_pubkeys.as_slice());
434 let input_merkle_tree_accounts =
435 test_indexer.get_state_merkle_tree_accounts(&input_merkle_tree_pubkeys);
436 let snapshots =
437 get_merkle_tree_snapshots::<R>(rpc, output_merkle_tree_accounts.as_slice()).await;
438 let input_snapshots =
439 get_merkle_tree_snapshots::<R>(rpc, input_merkle_tree_accounts.as_slice()).await;
440
441 let (event, _signature, _) = rpc
442 .create_and_send_transaction_with_event::<PublicTransactionEvent>(
443 &[instruction],
444 &payer.pubkey(),
445 &[payer, authority_signer],
446 transaction_params,
447 )
448 .await
449 .unwrap()
450 .unwrap();
451
452 let (created_change_output_account, created_token_output_accounts) =
453 test_indexer.add_event_and_compressed_accounts(&event);
454 let delegates = if let Some(index) = delegate_change_account_index {
455 let mut delegates = vec![None; created_token_output_accounts.len()];
456 delegates[index as usize] = Some(payer.pubkey());
457 Some(delegates)
458 } else {
459 None
460 };
461 let mut created_output_accounts = Vec::new();
462 created_token_output_accounts.iter().for_each(|x| {
463 created_output_accounts.push(x.compressed_account.clone());
464 });
465 created_change_output_account.iter().for_each(|x| {
466 created_output_accounts.push(x.clone());
467 });
468 assert_transfer(
469 rpc,
470 test_indexer,
471 &output_compressed_accounts,
472 created_output_accounts.as_slice(),
473 lamports,
474 &input_compressed_account_hashes,
475 &snapshots,
476 &input_snapshots,
477 &event,
478 delegates,
479 )
480 .await;
481}
482
483#[allow(clippy::too_many_arguments)]
484pub async fn decompress_test<R: RpcConnection, I: Indexer<R>>(
485 payer: &Keypair,
486 rpc: &mut R,
487 test_indexer: &mut I,
488 input_compressed_accounts: Vec<TokenDataWithContext>,
489 amount: u64,
490 output_merkle_tree_pubkey: &Pubkey,
491 recipient_token_account: &Pubkey,
492 transaction_params: Option<TransactionParams>,
493) {
494 let max_amount: u64 = input_compressed_accounts
495 .iter()
496 .map(|x| x.token_data.amount)
497 .sum();
498 let change_out_compressed_account = TokenTransferOutputData {
499 amount: max_amount - amount,
500 owner: payer.pubkey(),
501 lamports: None,
502 merkle_tree: *output_merkle_tree_pubkey,
503 };
504 let input_compressed_account_hashes = input_compressed_accounts
505 .iter()
506 .map(|x| x.compressed_account.hash().unwrap())
507 .collect::<Vec<_>>();
508 let input_merkle_tree_pubkeys = input_compressed_accounts
509 .iter()
510 .map(|x| x.compressed_account.merkle_context.merkle_tree_pubkey)
511 .collect::<Vec<_>>();
512 let proof_rpc_result = test_indexer
513 .create_proof_for_compressed_accounts(
514 Some(&input_compressed_account_hashes),
515 Some(&input_merkle_tree_pubkeys),
516 None,
517 None,
518 rpc,
519 )
520 .await;
521 let mint = input_compressed_accounts[0].token_data.mint;
522 let instruction = create_transfer_instruction(
523 &rpc.get_payer().pubkey(),
524 &payer.pubkey(), &input_compressed_accounts
526 .iter()
527 .map(|x| x.compressed_account.merkle_context)
528 .collect::<Vec<_>>(), &[change_out_compressed_account], &proof_rpc_result.root_indices, &Some(proof_rpc_result.proof),
532 input_compressed_accounts
533 .iter()
534 .map(|x| x.token_data.clone())
535 .collect::<Vec<_>>()
536 .as_slice(), &input_compressed_accounts
538 .iter()
539 .map(|x| &x.compressed_account.compressed_account)
540 .cloned()
541 .collect::<Vec<_>>(),
542 mint, None, false, Some(amount), Some(get_token_pool_pda(&mint)), Some(*recipient_token_account), true,
549 None,
550 None,
551 )
552 .unwrap();
553 let output_merkle_tree_pubkeys = vec![*output_merkle_tree_pubkey];
554 let output_merkle_tree_accounts =
555 test_indexer.get_state_merkle_tree_accounts(&output_merkle_tree_pubkeys);
556 let input_merkle_tree_accounts =
557 test_indexer.get_state_merkle_tree_accounts(&input_merkle_tree_pubkeys);
558 let output_merkle_tree_test_snapshots =
559 get_merkle_tree_snapshots::<R>(rpc, output_merkle_tree_accounts.as_slice()).await;
560 let input_merkle_tree_test_snapshots =
561 get_merkle_tree_snapshots::<R>(rpc, input_merkle_tree_accounts.as_slice()).await;
562 let recipient_token_account_data_pre = spl_token::state::Account::unpack(
563 &rpc.get_account(*recipient_token_account)
564 .await
565 .unwrap()
566 .unwrap()
567 .data,
568 )
569 .unwrap();
570 let context_payer = rpc.get_payer().insecure_clone();
571 let (event, _signature, _) = rpc
572 .create_and_send_transaction_with_event::<PublicTransactionEvent>(
573 &[instruction],
574 &context_payer.pubkey(),
575 &[&context_payer, payer],
576 transaction_params,
577 )
578 .await
579 .unwrap()
580 .unwrap();
581
582 let (_, created_output_accounts) = test_indexer.add_event_and_compressed_accounts(&event);
583 assert_transfer(
584 rpc,
585 test_indexer,
586 &[change_out_compressed_account],
587 created_output_accounts
588 .iter()
589 .map(|x| x.compressed_account.clone())
590 .collect::<Vec<_>>()
591 .as_slice(),
592 None,
593 input_compressed_account_hashes.as_slice(),
594 &output_merkle_tree_test_snapshots,
595 &input_merkle_tree_test_snapshots,
596 &event,
597 None,
598 )
599 .await;
600
601 let recipient_token_account_data = spl_token::state::Account::unpack(
602 &rpc.get_account(*recipient_token_account)
603 .await
604 .unwrap()
605 .unwrap()
606 .data,
607 )
608 .unwrap();
609 assert_eq!(
610 recipient_token_account_data.amount,
611 recipient_token_account_data_pre.amount + amount
612 );
613}
614
615#[allow(clippy::too_many_arguments)]
616pub async fn compress_test<R: RpcConnection, I: Indexer<R>>(
617 payer: &Keypair,
618 rpc: &mut R,
619 test_indexer: &mut I,
620 amount: u64,
621 mint: &Pubkey,
622 output_merkle_tree_pubkey: &Pubkey,
623 sender_token_account: &Pubkey,
624 transaction_params: Option<TransactionParams>,
625) {
626 let output_compressed_account = TokenTransferOutputData {
627 amount,
628 owner: payer.pubkey(),
629 lamports: None,
630 merkle_tree: *output_merkle_tree_pubkey,
631 };
632
633 let instruction = create_transfer_instruction(
634 &rpc.get_payer().pubkey(),
635 &payer.pubkey(), &Vec::new(), &[output_compressed_account], &Vec::new(), &None,
640 &Vec::new(), &Vec::new(), *mint, None, true, Some(amount), Some(get_token_pool_pda(mint)), Some(*sender_token_account), true,
649 None,
650 None,
651 )
652 .unwrap();
653 let output_merkle_tree_pubkeys = vec![*output_merkle_tree_pubkey];
654 let output_merkle_tree_accounts =
655 test_indexer.get_state_merkle_tree_accounts(&output_merkle_tree_pubkeys);
656 let output_merkle_tree_test_snapshots =
657 get_merkle_tree_snapshots::<R>(rpc, output_merkle_tree_accounts.as_slice()).await;
658 let input_merkle_tree_test_snapshots = Vec::new();
659 let recipient_token_account_data_pre = spl_token::state::Account::unpack(
660 &rpc.get_account(*sender_token_account)
661 .await
662 .unwrap()
663 .unwrap()
664 .data,
665 )
666 .unwrap();
667 let context_payer = rpc.get_payer().insecure_clone();
668 let (event, _signature, _) = rpc
669 .create_and_send_transaction_with_event::<PublicTransactionEvent>(
670 &[instruction],
671 &payer.pubkey(),
672 &[&context_payer, payer],
673 transaction_params,
674 )
675 .await
676 .unwrap()
677 .unwrap();
678
679 let (_, created_output_accounts) = test_indexer.add_event_and_compressed_accounts(&event);
680
681 assert_transfer(
682 rpc,
683 test_indexer,
684 &[output_compressed_account],
685 created_output_accounts
686 .iter()
687 .map(|x| x.compressed_account.clone())
688 .collect::<Vec<_>>()
689 .as_slice(),
690 None,
691 Vec::new().as_slice(),
692 &output_merkle_tree_test_snapshots,
693 &input_merkle_tree_test_snapshots,
694 &event,
695 None,
696 )
697 .await;
698
699 let recipient_token_account_data = spl_token::state::Account::unpack(
700 &rpc.get_account(*sender_token_account)
701 .await
702 .unwrap()
703 .unwrap()
704 .data,
705 )
706 .unwrap();
707 assert_eq!(
708 recipient_token_account_data.amount,
709 recipient_token_account_data_pre.amount - amount
710 );
711}
712
713#[allow(clippy::too_many_arguments)]
714pub async fn approve_test<R: RpcConnection, I: Indexer<R>>(
715 authority: &Keypair,
716 rpc: &mut R,
717 test_indexer: &mut I,
718 input_compressed_accounts: Vec<TokenDataWithContext>,
719 delegated_amount: u64,
720 delegate_lamports: Option<u64>,
721 delegate: &Pubkey,
722 delegated_compressed_account_merkle_tree: &Pubkey,
723 change_compressed_account_merkle_tree: &Pubkey,
724 transaction_params: Option<TransactionParams>,
725) {
726 let input_compressed_account_hashes = input_compressed_accounts
727 .iter()
728 .map(|x| x.compressed_account.hash().unwrap())
729 .collect::<Vec<_>>();
730 let input_merkle_tree_pubkeys = input_compressed_accounts
731 .iter()
732 .map(|x| x.compressed_account.merkle_context.merkle_tree_pubkey)
733 .collect::<Vec<_>>();
734 println!(
735 "input_compressed_account_hashes: {:?}",
736 input_compressed_account_hashes
737 );
738 println!("input compressed accounts: {:?}", input_compressed_accounts);
739 let proof_rpc_result = test_indexer
740 .create_proof_for_compressed_accounts(
741 Some(&input_compressed_account_hashes),
742 Some(&input_merkle_tree_pubkeys),
743 None,
744 None,
745 rpc,
746 )
747 .await;
748 let mint = input_compressed_accounts[0].token_data.mint;
749 let inputs = CreateApproveInstructionInputs {
750 fee_payer: rpc.get_payer().pubkey(),
751 authority: authority.pubkey(),
752 input_merkle_contexts: input_compressed_accounts
753 .iter()
754 .map(|x| x.compressed_account.merkle_context)
755 .collect(),
756 input_token_data: input_compressed_accounts
757 .iter()
758 .map(|x| x.token_data.clone())
759 .collect(),
760 input_compressed_accounts: input_compressed_accounts
761 .iter()
762 .map(|x| &x.compressed_account.compressed_account)
763 .cloned()
764 .collect::<Vec<_>>(),
765 mint,
766 delegated_amount,
767 delegate_lamports,
768 delegated_compressed_account_merkle_tree: *delegated_compressed_account_merkle_tree,
769 change_compressed_account_merkle_tree: *change_compressed_account_merkle_tree,
770 delegate: *delegate,
771 root_indices: proof_rpc_result.root_indices,
772 proof: proof_rpc_result.proof,
773 };
774
775 let instruction = create_approve_instruction(inputs).unwrap();
776 let mut output_merkle_tree_pubkeys = vec![*delegated_compressed_account_merkle_tree];
777 let input_amount = input_compressed_accounts
778 .iter()
779 .map(|x| x.token_data.amount)
780 .sum::<u64>();
781 let change_amount = input_amount - delegated_amount;
782 let input_lamports = input_compressed_accounts
783 .iter()
784 .map(|x| x.compressed_account.compressed_account.lamports)
785 .sum::<u64>();
786 let (change_lamports, change_lamports_greater_zero) =
787 if let Some(delegate_lamports) = delegate_lamports {
788 let change_lamports = input_lamports - delegate_lamports;
789 let option_change_lamports = if change_lamports > 0 {
790 Some(change_lamports)
791 } else {
792 None
793 };
794
795 (
796 Some(vec![Some(delegate_lamports), option_change_lamports]),
797 change_lamports > 0,
798 )
799 } else if input_lamports > 0 {
800 (Some(vec![None, Some(input_lamports)]), true)
801 } else {
802 (None, false)
803 };
804 if change_lamports_greater_zero || change_amount > 0 {
805 output_merkle_tree_pubkeys.push(*change_compressed_account_merkle_tree);
806 }
807 let output_merkle_tree_accounts =
808 test_indexer.get_state_merkle_tree_accounts(&output_merkle_tree_pubkeys);
809
810 let output_merkle_tree_test_snapshots =
811 get_merkle_tree_snapshots::<R>(rpc, output_merkle_tree_accounts.as_slice()).await;
812 let input_merkle_tree_accounts =
813 test_indexer.get_state_merkle_tree_accounts(&input_merkle_tree_pubkeys);
814 let input_merkle_tree_test_snapshots =
815 get_merkle_tree_snapshots::<R>(rpc, input_merkle_tree_accounts.as_slice()).await;
816 let context_payer = rpc.get_payer().insecure_clone();
817 let (event, _signature, _) = rpc
818 .create_and_send_transaction_with_event::<PublicTransactionEvent>(
819 &[instruction],
820 &context_payer.pubkey(),
821 &[&context_payer, authority],
822 transaction_params,
823 )
824 .await
825 .unwrap()
826 .unwrap();
827 let (_, created_output_accounts) = test_indexer.add_event_and_compressed_accounts(&event);
828
829 let expected_delegated_token_data = TokenData {
830 mint,
831 owner: authority.pubkey(),
832 amount: delegated_amount,
833 delegate: Some(*delegate),
834 state: AccountState::Initialized,
835 tlv: None,
836 };
837
838 assert_eq!(
839 expected_delegated_token_data,
840 created_output_accounts[0].token_data
841 );
842 let mut expected_token_data = vec![expected_delegated_token_data];
843 let mut delegates = vec![Some(*delegate)];
844 if delegated_amount != input_amount {
845 let expected_change_token_data = TokenData {
846 mint,
847 owner: authority.pubkey(),
848 amount: change_amount,
849 delegate: None,
850 state: AccountState::Initialized,
851 tlv: None,
852 };
853 assert_eq!(
854 expected_change_token_data,
855 created_output_accounts[1].token_data
856 );
857 expected_token_data.push(expected_change_token_data);
858 delegates.push(None);
859 }
860
861 let expected_compressed_output_accounts =
862 create_expected_token_output_data(expected_token_data, &output_merkle_tree_pubkeys);
863
864 assert_transfer(
865 rpc,
866 test_indexer,
867 expected_compressed_output_accounts.as_slice(),
868 created_output_accounts
869 .iter()
870 .map(|x| x.compressed_account.clone())
871 .collect::<Vec<_>>()
872 .as_slice(),
873 change_lamports,
874 input_compressed_account_hashes.as_slice(),
875 &output_merkle_tree_test_snapshots,
876 &input_merkle_tree_test_snapshots,
877 &event,
878 Some(delegates),
879 )
880 .await;
881}
882
883#[allow(clippy::too_many_arguments)]
884pub async fn revoke_test<R: RpcConnection, I: Indexer<R>>(
885 authority: &Keypair,
886 rpc: &mut R,
887 test_indexer: &mut I,
888 input_compressed_accounts: Vec<TokenDataWithContext>,
889 output_account_merkle_tree: &Pubkey,
890 transaction_params: Option<TransactionParams>,
891) {
892 let input_compressed_account_hashes = input_compressed_accounts
893 .iter()
894 .map(|x| x.compressed_account.hash().unwrap())
895 .collect::<Vec<_>>();
896 let input_merkle_tree_pubkeys = input_compressed_accounts
897 .iter()
898 .map(|x| x.compressed_account.merkle_context.merkle_tree_pubkey)
899 .collect::<Vec<_>>();
900 let proof_rpc_result = test_indexer
901 .create_proof_for_compressed_accounts(
902 Some(&input_compressed_account_hashes),
903 Some(&input_merkle_tree_pubkeys),
904 None,
905 None,
906 rpc,
907 )
908 .await;
909 let mint = input_compressed_accounts[0].token_data.mint;
910 let inputs = CreateRevokeInstructionInputs {
911 fee_payer: rpc.get_payer().pubkey(),
912 authority: authority.pubkey(),
913 input_merkle_contexts: input_compressed_accounts
914 .iter()
915 .map(|x| x.compressed_account.merkle_context)
916 .collect(),
917 input_token_data: input_compressed_accounts
918 .iter()
919 .map(|x| x.token_data.clone())
920 .collect(),
921 input_compressed_accounts: input_compressed_accounts
922 .iter()
923 .map(|x| &x.compressed_account.compressed_account)
924 .cloned()
925 .collect::<Vec<_>>(),
926 mint,
927 output_account_merkle_tree: *output_account_merkle_tree,
928 root_indices: proof_rpc_result.root_indices,
929 proof: proof_rpc_result.proof,
930 };
931
932 let instruction = create_revoke_instruction(inputs).unwrap();
933 let output_merkle_tree_pubkeys = vec![*output_account_merkle_tree];
934 let output_merkle_tree_accounts =
935 test_indexer.get_state_merkle_tree_accounts(&output_merkle_tree_pubkeys);
936 let input_merkle_tree_accounts =
937 test_indexer.get_state_merkle_tree_accounts(&input_merkle_tree_pubkeys);
938 let output_merkle_tree_test_snapshots =
939 get_merkle_tree_snapshots::<R>(rpc, output_merkle_tree_accounts.as_slice()).await;
940 let input_merkle_tree_test_snapshots =
941 get_merkle_tree_snapshots::<R>(rpc, input_merkle_tree_accounts.as_slice()).await;
942 let context_payer = rpc.get_payer().insecure_clone();
943 let (event, _signature, _) = rpc
944 .create_and_send_transaction_with_event::<PublicTransactionEvent>(
945 &[instruction],
946 &context_payer.pubkey(),
947 &[&context_payer, authority],
948 transaction_params,
949 )
950 .await
951 .unwrap()
952 .unwrap();
953 let (_, created_output_accounts) = test_indexer.add_event_and_compressed_accounts(&event);
954 let input_amount = input_compressed_accounts
955 .iter()
956 .map(|x| x.token_data.amount)
957 .sum::<u64>();
958 let expected_token_data = TokenData {
959 mint,
960 owner: authority.pubkey(),
961 amount: input_amount,
962 delegate: None,
963 state: AccountState::Initialized,
964 tlv: None,
965 };
966 assert_eq!(expected_token_data, created_output_accounts[0].token_data);
967 let expected_compressed_output_accounts =
968 create_expected_token_output_data(vec![expected_token_data], &output_merkle_tree_pubkeys);
969 let sum_inputs = input_compressed_accounts
970 .iter()
971 .map(|x| x.compressed_account.compressed_account.lamports)
972 .sum::<u64>();
973 let change_lamports = if sum_inputs > 0 {
974 Some(vec![Some(sum_inputs)])
975 } else {
976 None
977 };
978 assert_transfer(
979 rpc,
980 test_indexer,
981 expected_compressed_output_accounts.as_slice(),
982 created_output_accounts
983 .iter()
984 .map(|x| x.compressed_account.clone())
985 .collect::<Vec<_>>()
986 .as_slice(),
987 change_lamports,
988 input_compressed_account_hashes.as_slice(),
989 &output_merkle_tree_test_snapshots,
990 &input_merkle_tree_test_snapshots,
991 &event,
992 None,
993 )
994 .await;
995}
996
997pub async fn freeze_test<R: RpcConnection, I: Indexer<R>>(
998 authority: &Keypair,
999 rpc: &mut R,
1000 test_indexer: &mut I,
1001 input_compressed_accounts: Vec<TokenDataWithContext>,
1002 outputs_merkle_tree: &Pubkey,
1003 transaction_params: Option<TransactionParams>,
1004) {
1005 freeze_or_thaw_test::<R, true, I>(
1006 authority,
1007 rpc,
1008 test_indexer,
1009 input_compressed_accounts,
1010 outputs_merkle_tree,
1011 transaction_params,
1012 )
1013 .await;
1014}
1015
1016pub async fn thaw_test<R: RpcConnection, I: Indexer<R>>(
1017 authority: &Keypair,
1018 rpc: &mut R,
1019 test_indexer: &mut I,
1020 input_compressed_accounts: Vec<TokenDataWithContext>,
1021 outputs_merkle_tree: &Pubkey,
1022 transaction_params: Option<TransactionParams>,
1023) {
1024 freeze_or_thaw_test::<R, false, I>(
1025 authority,
1026 rpc,
1027 test_indexer,
1028 input_compressed_accounts,
1029 outputs_merkle_tree,
1030 transaction_params,
1031 )
1032 .await;
1033}
1034
1035pub async fn freeze_or_thaw_test<R: RpcConnection, const FREEZE: bool, I: Indexer<R>>(
1036 authority: &Keypair,
1037 rpc: &mut R,
1038 test_indexer: &mut I,
1039 input_compressed_accounts: Vec<TokenDataWithContext>,
1040 outputs_merkle_tree: &Pubkey,
1041 transaction_params: Option<TransactionParams>,
1042) {
1043 let input_compressed_account_hashes = input_compressed_accounts
1044 .iter()
1045 .map(|x| x.compressed_account.hash().unwrap())
1046 .collect::<Vec<_>>();
1047 let input_merkle_tree_pubkeys = input_compressed_accounts
1048 .iter()
1049 .map(|x| x.compressed_account.merkle_context.merkle_tree_pubkey)
1050 .collect::<Vec<_>>();
1051 let proof_rpc_result = test_indexer
1052 .create_proof_for_compressed_accounts(
1053 Some(&input_compressed_account_hashes),
1054 Some(&input_merkle_tree_pubkeys),
1055 None,
1056 None,
1057 rpc,
1058 )
1059 .await;
1060 let mint = input_compressed_accounts[0].token_data.mint;
1061 let inputs = CreateInstructionInputs {
1062 fee_payer: rpc.get_payer().pubkey(),
1063 authority: authority.pubkey(),
1064 input_merkle_contexts: input_compressed_accounts
1065 .iter()
1066 .map(|x| x.compressed_account.merkle_context)
1067 .collect(),
1068 input_token_data: input_compressed_accounts
1069 .iter()
1070 .map(|x| x.token_data.clone())
1071 .collect(),
1072 input_compressed_accounts: input_compressed_accounts
1073 .iter()
1074 .map(|x| &x.compressed_account.compressed_account)
1075 .cloned()
1076 .collect::<Vec<_>>(),
1077 outputs_merkle_tree: *outputs_merkle_tree,
1078 root_indices: proof_rpc_result.root_indices,
1079 proof: proof_rpc_result.proof,
1080 };
1081
1082 let instruction = create_instruction::<FREEZE>(inputs).unwrap();
1083 let output_merkle_tree_pubkeys =
1084 vec![*outputs_merkle_tree; input_compressed_account_hashes.len()];
1085 let output_merkle_tree_accounts =
1086 test_indexer.get_state_merkle_tree_accounts(&output_merkle_tree_pubkeys);
1087 let input_merkle_tree_accounts =
1088 test_indexer.get_state_merkle_tree_accounts(&input_merkle_tree_pubkeys);
1089 let output_merkle_tree_test_snapshots =
1090 get_merkle_tree_snapshots::<R>(rpc, output_merkle_tree_accounts.as_slice()).await;
1091 let input_merkle_tree_test_snapshots =
1092 get_merkle_tree_snapshots::<R>(rpc, input_merkle_tree_accounts.as_slice()).await;
1093 let context_payer = rpc.get_payer().insecure_clone();
1094 let (event, _signature, _) = rpc
1095 .create_and_send_transaction_with_event::<PublicTransactionEvent>(
1096 &[instruction],
1097 &context_payer.pubkey(),
1098 &[&context_payer, authority],
1099 transaction_params,
1100 )
1101 .await
1102 .unwrap()
1103 .unwrap();
1104 let (_, created_output_accounts) = test_indexer.add_event_and_compressed_accounts(&event);
1105
1106 let mut delegates = Vec::new();
1107 let mut expected_output_accounts = Vec::new();
1108 for account in input_compressed_accounts.iter() {
1109 let state = if FREEZE {
1110 AccountState::Frozen
1111 } else {
1112 AccountState::Initialized
1113 };
1114 let expected_token_data = TokenData {
1115 mint,
1116 owner: input_compressed_accounts[0].token_data.owner,
1117 amount: account.token_data.amount,
1118 delegate: account.token_data.delegate,
1119 state,
1120 tlv: None,
1121 };
1122 if let Some(delegate) = account.token_data.delegate {
1123 delegates.push(Some(delegate));
1124 } else {
1125 delegates.push(None);
1126 }
1127 expected_output_accounts.push(expected_token_data);
1128 }
1129 let expected_compressed_output_accounts =
1130 create_expected_token_output_data(expected_output_accounts, &output_merkle_tree_pubkeys);
1131 let sum_inputs = input_compressed_accounts
1132 .iter()
1133 .map(|x| x.compressed_account.compressed_account.lamports)
1134 .sum::<u64>();
1135 let change_lamports = if sum_inputs > 0 {
1136 let mut change_lamports = Vec::new();
1137 for account in input_compressed_accounts.iter() {
1138 if account.compressed_account.compressed_account.lamports > 0 {
1139 change_lamports.push(Some(account.compressed_account.compressed_account.lamports));
1140 } else {
1141 change_lamports.push(None);
1142 }
1143 }
1144 Some(change_lamports)
1145 } else {
1146 None
1147 };
1148 assert_transfer(
1149 rpc,
1150 test_indexer,
1151 expected_compressed_output_accounts.as_slice(),
1152 created_output_accounts
1153 .iter()
1154 .map(|x| x.compressed_account.clone())
1155 .collect::<Vec<_>>()
1156 .as_slice(),
1157 change_lamports,
1158 input_compressed_account_hashes.as_slice(),
1159 &output_merkle_tree_test_snapshots,
1160 &input_merkle_tree_test_snapshots,
1161 &event,
1162 Some(delegates),
1163 )
1164 .await;
1165}
1166
1167#[allow(clippy::too_many_arguments)]
1168pub async fn burn_test<R: RpcConnection, I: Indexer<R>>(
1169 authority: &Keypair,
1170 rpc: &mut R,
1171 test_indexer: &mut I,
1172 input_compressed_accounts: Vec<TokenDataWithContext>,
1173 change_account_merkle_tree: &Pubkey,
1174 burn_amount: u64,
1175 signer_is_delegate: bool,
1176 transaction_params: Option<TransactionParams>,
1177) {
1178 let (
1179 input_compressed_account_hashes,
1180 input_merkle_tree_pubkeys,
1181 mint,
1182 output_amount,
1183 instruction,
1184 ) = create_burn_test_instruction(
1185 authority,
1186 rpc,
1187 test_indexer,
1188 &input_compressed_accounts,
1189 change_account_merkle_tree,
1190 burn_amount,
1191 signer_is_delegate,
1192 BurnInstructionMode::Normal,
1193 )
1194 .await;
1195 let output_merkle_tree_pubkeys = vec![*change_account_merkle_tree; 1];
1196 let output_merkle_tree_test_snapshots = if output_amount > 0 {
1197 let output_merkle_tree_accounts =
1198 test_indexer.get_state_merkle_tree_accounts(&output_merkle_tree_pubkeys);
1199
1200 get_merkle_tree_snapshots::<R>(rpc, output_merkle_tree_accounts.as_slice()).await
1201 } else {
1202 Vec::new()
1203 };
1204
1205 let token_pool_pda_address = get_token_pool_pda(&mint);
1206 let pre_token_pool_account = rpc
1207 .get_account(token_pool_pda_address)
1208 .await
1209 .unwrap()
1210 .unwrap();
1211 let pre_token_pool_balance = spl_token::state::Account::unpack(&pre_token_pool_account.data)
1212 .unwrap()
1213 .amount;
1214
1215 let input_merkle_tree_accounts =
1216 test_indexer.get_state_merkle_tree_accounts(&input_merkle_tree_pubkeys);
1217 let input_merkle_tree_test_snapshots =
1218 get_merkle_tree_snapshots::<R>(rpc, input_merkle_tree_accounts.as_slice()).await;
1219 let context_payer = rpc.get_payer().insecure_clone();
1220 let (event, _signature, _) = rpc
1221 .create_and_send_transaction_with_event::<PublicTransactionEvent>(
1222 &[instruction],
1223 &context_payer.pubkey(),
1224 &[&context_payer, authority],
1225 transaction_params,
1226 )
1227 .await
1228 .unwrap()
1229 .unwrap();
1230 let (_, created_output_accounts) = test_indexer.add_event_and_compressed_accounts(&event);
1231 let mut delegates = Vec::new();
1232 let mut expected_output_accounts = Vec::new();
1233
1234 let delegate = if signer_is_delegate {
1235 Some(authority.pubkey())
1236 } else {
1237 None
1238 };
1239 if output_amount > 0 {
1240 let expected_token_data = TokenData {
1241 mint,
1242 owner: input_compressed_accounts[0].token_data.owner,
1243 amount: output_amount,
1244 delegate,
1245 state: AccountState::Initialized,
1246 tlv: None,
1247 };
1248 if let Some(delegate) = expected_token_data.delegate {
1249 delegates.push(Some(delegate));
1250 } else {
1251 delegates.push(None);
1252 }
1253 expected_output_accounts.push(expected_token_data);
1254 }
1255 let expected_compressed_output_accounts =
1256 create_expected_token_output_data(expected_output_accounts, &output_merkle_tree_pubkeys);
1257 let sum_inputs = input_compressed_accounts
1258 .iter()
1259 .map(|x| x.compressed_account.compressed_account.lamports)
1260 .sum::<u64>();
1261 let change_lamports = if sum_inputs > 0 {
1262 Some(vec![Some(sum_inputs)])
1263 } else {
1264 None
1265 };
1266 assert_transfer(
1267 rpc,
1268 test_indexer,
1269 expected_compressed_output_accounts.as_slice(),
1270 created_output_accounts
1271 .iter()
1272 .map(|x| x.compressed_account.clone())
1273 .collect::<Vec<_>>()
1274 .as_slice(),
1275 change_lamports,
1276 input_compressed_account_hashes.as_slice(),
1277 &output_merkle_tree_test_snapshots,
1278 &input_merkle_tree_test_snapshots,
1279 &event,
1280 Some(delegates),
1281 )
1282 .await;
1283 let post_token_pool_account = rpc
1284 .get_account(token_pool_pda_address)
1285 .await
1286 .unwrap()
1287 .unwrap();
1288 let post_token_pool_balance = spl_token::state::Account::unpack(&post_token_pool_account.data)
1289 .unwrap()
1290 .amount;
1291 assert_eq!(
1292 post_token_pool_balance,
1293 pre_token_pool_balance - burn_amount
1294 );
1295}
1296
1297#[derive(Debug, Clone, PartialEq)]
1298pub enum BurnInstructionMode {
1299 Normal,
1300 InvalidProof,
1301 InvalidMint,
1302}
1303
1304#[allow(clippy::too_many_arguments)]
1305pub async fn create_burn_test_instruction<R: RpcConnection, I: Indexer<R>>(
1306 authority: &Keypair,
1307 rpc: &mut R,
1308 test_indexer: &mut I,
1309 input_compressed_accounts: &[TokenDataWithContext],
1310 change_account_merkle_tree: &Pubkey,
1311 burn_amount: u64,
1312 signer_is_delegate: bool,
1313 mode: BurnInstructionMode,
1314) -> (Vec<[u8; 32]>, Vec<Pubkey>, Pubkey, u64, Instruction) {
1315 let input_compressed_account_hashes = input_compressed_accounts
1316 .iter()
1317 .map(|x| x.compressed_account.hash().unwrap())
1318 .collect::<Vec<_>>();
1319 let input_merkle_tree_pubkeys = input_compressed_accounts
1320 .iter()
1321 .map(|x| x.compressed_account.merkle_context.merkle_tree_pubkey)
1322 .collect::<Vec<_>>();
1323 let proof_rpc_result = test_indexer
1324 .create_proof_for_compressed_accounts(
1325 Some(&input_compressed_account_hashes),
1326 Some(&input_merkle_tree_pubkeys),
1327 None,
1328 None,
1329 rpc,
1330 )
1331 .await;
1332 let mint = if mode == BurnInstructionMode::InvalidMint {
1333 Pubkey::new_unique()
1334 } else {
1335 input_compressed_accounts[0].token_data.mint
1336 };
1337 let proof = if mode == BurnInstructionMode::InvalidProof {
1338 CompressedProof {
1339 a: proof_rpc_result.proof.a,
1340 b: proof_rpc_result.proof.b,
1341 c: proof_rpc_result.proof.a, }
1343 } else {
1344 proof_rpc_result.proof
1345 };
1346 let inputs = CreateBurnInstructionInputs {
1347 fee_payer: rpc.get_payer().pubkey(),
1348 authority: authority.pubkey(),
1349 input_merkle_contexts: input_compressed_accounts
1350 .iter()
1351 .map(|x| x.compressed_account.merkle_context)
1352 .collect(),
1353 input_token_data: input_compressed_accounts
1354 .iter()
1355 .map(|x| x.token_data.clone())
1356 .collect(),
1357 input_compressed_accounts: input_compressed_accounts
1358 .iter()
1359 .map(|x| &x.compressed_account.compressed_account)
1360 .cloned()
1361 .collect::<Vec<_>>(),
1362 change_account_merkle_tree: *change_account_merkle_tree,
1363 root_indices: proof_rpc_result.root_indices,
1364 proof,
1365 mint,
1366 signer_is_delegate,
1367 burn_amount,
1368 };
1369 let input_amount_sum = input_compressed_accounts
1370 .iter()
1371 .map(|x| x.token_data.amount)
1372 .sum::<u64>();
1373 let output_amount = input_amount_sum - burn_amount;
1374 let instruction = create_burn_instruction(inputs).unwrap();
1375 (
1376 input_compressed_account_hashes,
1377 input_merkle_tree_pubkeys,
1378 mint,
1379 output_amount,
1380 instruction,
1381 )
1382}
1383
1384pub fn create_expected_token_output_data(
1385 expected_token_data: Vec<TokenData>,
1386 merkle_tree_pubkeys: &[Pubkey],
1387) -> Vec<TokenTransferOutputData> {
1388 let mut expected_compressed_output_accounts = Vec::new();
1389 for (token_data, merkle_tree_pubkey) in
1390 expected_token_data.iter().zip(merkle_tree_pubkeys.iter())
1391 {
1392 expected_compressed_output_accounts.push(TokenTransferOutputData {
1393 owner: token_data.owner,
1394 amount: token_data.amount,
1395 merkle_tree: *merkle_tree_pubkey,
1396 lamports: None,
1397 });
1398 }
1399 expected_compressed_output_accounts
1400}