light_token/compressed_token/v2/transfer2/
instruction.rs1use light_compressed_account::instruction_data::compressed_proof::ValidityProof;
2use light_program_profiler::profile;
3use light_token_interface::{
4 instructions::{
5 extensions::ExtensionInstructionData,
6 transfer2::{CompressedCpiContext, CompressedTokenInstructionDataTransfer2},
7 },
8 LIGHT_TOKEN_PROGRAM_ID, TRANSFER2,
9};
10use solana_instruction::Instruction;
11use solana_pubkey::Pubkey;
12
13use super::account_metas::{get_transfer2_instruction_account_metas, Transfer2AccountsMetaConfig};
14use crate::{
15 compressed_token::CTokenAccount2,
16 error::{Result, TokenSdkError},
17 AnchorSerialize,
18};
19
20#[derive(Debug, Default, PartialEq, Copy, Clone)]
21pub struct Transfer2Config {
22 pub cpi_context: Option<CompressedCpiContext>,
23 pub with_transaction_hash: bool,
24 pub sol_pool_pda: bool,
25 pub sol_decompression_recipient: Option<Pubkey>,
26 pub filter_zero_amount_outputs: bool,
27 pub max_top_up: u16,
29}
30
31impl Transfer2Config {
32 pub fn new() -> Self {
33 Default::default()
34 }
35
36 pub fn with_cpi_context(mut self, cpi_context: CompressedCpiContext) -> Self {
37 self.cpi_context = Some(cpi_context);
38 self
39 }
40
41 pub fn with_transaction_hash(mut self) -> Self {
42 self.with_transaction_hash = true;
43 self
44 }
45
46 pub fn with_sol_pool(mut self, sol_decompression_recipient: Pubkey) -> Self {
47 self.sol_pool_pda = true;
48 self.sol_decompression_recipient = Some(sol_decompression_recipient);
49 self
50 }
51
52 pub fn filter_zero_amount_outputs(mut self) -> Self {
53 self.filter_zero_amount_outputs = true;
54 self
55 }
56
57 pub fn with_max_top_up(mut self, max_top_up: u16) -> Self {
59 self.max_top_up = max_top_up;
60 self
61 }
62}
63
64#[derive(Debug, Clone, PartialEq, Default)]
66pub struct Transfer2Inputs {
67 pub token_accounts: Vec<CTokenAccount2>,
68 pub validity_proof: ValidityProof,
69 pub transfer_config: Transfer2Config,
70 pub meta_config: Transfer2AccountsMetaConfig,
71 pub in_lamports: Option<Vec<u64>>,
74 pub out_lamports: Option<Vec<u64>>,
75 pub output_queue: u8,
76 pub in_tlv: Option<Vec<Vec<ExtensionInstructionData>>>,
79}
80
81#[profile]
83pub fn create_transfer2_instruction(inputs: Transfer2Inputs) -> Result<Instruction> {
84 let Transfer2Inputs {
85 token_accounts,
86 validity_proof,
87 transfer_config,
88 meta_config,
89 in_lamports,
90 out_lamports,
91 output_queue,
92 in_tlv,
93 } = inputs;
94 let mut input_token_data_with_context = Vec::new();
95 let mut output_compressed_accounts = Vec::new();
96 let mut collected_compressions = Vec::new();
97
98 for token_account in token_accounts {
100 if let Some(compression) = token_account.compression() {
102 collected_compressions.push(*compression);
103 }
104 let (inputs, output) = token_account.into_inputs_and_outputs();
105 input_token_data_with_context.extend(inputs);
107
108 if !transfer_config.filter_zero_amount_outputs || output.amount > 0 {
110 output_compressed_accounts.push(output);
111 }
112 }
113
114 let instruction_data = CompressedTokenInstructionDataTransfer2 {
116 with_transaction_hash: transfer_config.with_transaction_hash,
117 with_lamports_change_account_merkle_tree_index: false, lamports_change_account_merkle_tree_index: 0,
119 lamports_change_account_owner_index: 0,
120 output_queue,
121 proof: validity_proof.into(),
122 in_token_data: input_token_data_with_context,
123 out_token_data: output_compressed_accounts,
124 in_lamports,
125 out_lamports,
126 in_tlv,
127 out_tlv: None, compressions: if collected_compressions.is_empty() {
129 None
130 } else {
131 Some(collected_compressions)
132 },
133 cpi_context: transfer_config.cpi_context,
134 max_top_up: transfer_config.max_top_up,
135 };
136
137 let serialized = instruction_data
139 .try_to_vec()
140 .map_err(|_| TokenSdkError::SerializationError)?;
141
142 let mut data = Vec::with_capacity(1 + serialized.len());
144 data.push(TRANSFER2);
145 data.extend(serialized);
146
147 let account_metas = get_transfer2_instruction_account_metas(meta_config);
148
149 Ok(Instruction {
150 program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID),
151 accounts: account_metas,
152 data,
153 })
154}
155
156