light_token/compressed_token/v2/transfer2/
cpi_accounts.rs1use light_account_checks::{AccountError, AccountInfoTrait, AccountIterator};
2use light_program_profiler::profile;
3use light_sdk_types::{
4 ACCOUNT_COMPRESSION_AUTHORITY_PDA, ACCOUNT_COMPRESSION_PROGRAM_ID, LIGHT_SYSTEM_PROGRAM_ID,
5 REGISTERED_PROGRAM_PDA,
6};
7use light_token_interface::LIGHT_TOKEN_PROGRAM_ID;
8use light_token_types::CPI_AUTHORITY_PDA;
9use solana_instruction::AccountMeta;
10use solana_msg::msg;
11
12use crate::error::TokenSdkError;
13
14#[derive(Debug)]
16pub struct Transfer2CpiAccounts<'a, A: AccountInfoTrait + Clone> {
17 pub compressed_token_program: &'a A,
19 pub invoking_program_cpi_authority: Option<&'a A>,
21 pub light_system_program: &'a A,
22
23 pub fee_payer: &'a A,
25 pub compressed_token_cpi_authority: &'a A,
26 pub registered_program_pda: &'a A,
27 pub account_compression_authority: &'a A,
28 pub account_compression_program: &'a A,
29 pub system_program: &'a A,
30
31 pub sol_pool_pda: Option<&'a A>,
33 pub sol_decompression_recipient: Option<&'a A>,
34 pub cpi_context: Option<&'a A>,
35
36 pub packed_accounts: &'a [A],
39}
40
41impl<'a, A: AccountInfoTrait + Clone> Transfer2CpiAccounts<'a, A> {
42 #[profile]
45 #[inline(always)]
46 #[track_caller]
47 pub fn try_from_account_infos_full(
48 fee_payer: &'a A,
49 accounts: &'a [A],
50 with_sol_pool: bool,
51 with_sol_decompression: bool,
52 with_cpi_context: bool,
53 light_system_cpi_authority: bool,
54 ) -> Result<Self, TokenSdkError> {
55 let mut iter = AccountIterator::new(accounts);
56
57 let compressed_token_program =
58 iter.next_checked_pubkey("compressed_token_program", LIGHT_TOKEN_PROGRAM_ID)?;
59
60 let invoking_program_cpi_authority =
61 iter.next_option("CPI_SIGNER.cpi_authority", light_system_cpi_authority)?;
62 let compressed_token_cpi_authority =
63 iter.next_checked_pubkey("compressed_token_cpi_authority", CPI_AUTHORITY_PDA)?;
64
65 let light_system_program =
66 iter.next_checked_pubkey("light_system_program", LIGHT_SYSTEM_PROGRAM_ID)?;
67
68 let registered_program_pda =
69 iter.next_checked_pubkey("registered_program_pda", REGISTERED_PROGRAM_PDA)?;
70
71 let account_compression_authority = iter.next_checked_pubkey(
72 "account_compression_authority",
73 ACCOUNT_COMPRESSION_AUTHORITY_PDA,
74 )?;
75
76 let account_compression_program = iter.next_checked_pubkey(
77 "account_compression_program",
78 ACCOUNT_COMPRESSION_PROGRAM_ID,
79 )?;
80
81 let system_program = iter.next_checked_pubkey("system_program", [0u8; 32])?;
82
83 let sol_pool_pda = iter.next_option_mut("sol_pool_pda", with_sol_pool)?;
84
85 let sol_decompression_recipient =
86 iter.next_option_mut("sol_decompression_recipient", with_sol_decompression)?;
87
88 let cpi_context = iter.next_option_mut("cpi_context", with_cpi_context)?;
89
90 let packed_accounts = iter.remaining()?;
91 if !packed_accounts[0].is_owned_by(&ACCOUNT_COMPRESSION_PROGRAM_ID) {
92 msg!("First packed accounts must be tree or queue accounts.");
93 msg!("Found {:?} instead", packed_accounts[0].pubkey());
94 return Err(AccountError::InvalidAccount.into());
95 }
96
97 Ok(Self {
98 compressed_token_program,
99 invoking_program_cpi_authority,
100 light_system_program,
101 fee_payer,
102 compressed_token_cpi_authority,
103 registered_program_pda,
104 account_compression_authority,
105 account_compression_program,
106 system_program,
107 sol_pool_pda,
108 sol_decompression_recipient,
109 cpi_context,
110 packed_accounts,
111 })
112 }
113
114 #[inline(always)]
115 #[track_caller]
116 pub fn try_from_account_infos(
117 fee_payer: &'a A,
118 accounts: &'a [A],
119 ) -> Result<Self, TokenSdkError> {
120 Self::try_from_account_infos_full(fee_payer, accounts, false, false, false, false)
121 }
122
123 #[inline(always)]
124 #[track_caller]
125 pub fn try_from_account_infos_cpi_context(
126 fee_payer: &'a A,
127 accounts: &'a [A],
128 ) -> Result<Self, TokenSdkError> {
129 Self::try_from_account_infos_full(fee_payer, accounts, false, false, true, false)
130 }
131
132 pub fn packed_accounts(&self) -> &'a [A] {
134 self.packed_accounts
135 }
136
137 #[profile]
139 #[inline(always)]
140 pub fn packed_account_metas(&self) -> Vec<AccountMeta> {
141 let mut vec = Vec::with_capacity(self.packed_accounts.len());
142 for account in self.packed_accounts {
143 vec.push(AccountMeta {
144 pubkey: account.key().into(),
145 is_writable: account.is_writable(),
146 is_signer: account.is_signer(),
147 });
148 }
149 vec
150 }
151
152 pub fn packed_account_by_index(&self, index: u8) -> Option<&'a A> {
154 self.packed_accounts.get(index as usize)
155 }
156
157 #[profile]
159 #[inline(always)]
160 pub fn to_account_infos(&self) -> Vec<A> {
161 let mut accounts = Vec::with_capacity(10 + self.packed_accounts.len());
162
163 accounts.extend_from_slice(
164 &[
165 self.light_system_program.clone(),
166 self.fee_payer.clone(),
167 self.compressed_token_cpi_authority.clone(),
168 self.registered_program_pda.clone(),
169 self.account_compression_authority.clone(),
170 self.account_compression_program.clone(),
171 self.system_program.clone(),
172 ][..],
173 );
174
175 if let Some(sol_pool) = self.sol_pool_pda {
176 accounts.push(sol_pool.clone());
177 }
178 if let Some(recipient) = self.sol_decompression_recipient {
179 accounts.push(recipient.clone());
180 }
181 if let Some(context) = self.cpi_context {
182 accounts.push(context.clone());
183 }
184 self.packed_accounts.iter().for_each(|e| {
185 accounts.push(e.clone());
186 });
187
188 accounts
189 }
190}