1mod generated;
2mod metadata;
3use std::future::Future;
4
5pub use generated::*;
6pub use metadata::*;
7
8#[cfg(feature = "fetch")]
9use solana_client::nonblocking;
10use solana_instruction::Instruction;
11use solana_program_error::ProgramError;
12use solana_program_option::COption;
13use solana_program_pack::Pack;
14use solana_pubkey::Pubkey;
15use spl_associated_token_account_interface::address::get_associated_token_address_with_program_id;
16use spl_associated_token_account_interface::instruction::{
17 create_associated_token_account, create_associated_token_account_idempotent,
18};
19pub use spl_tlv_account_resolution::state::{AccountDataResult, AccountFetchError};
20use spl_token_2022_interface::state::{Account, AccountState};
21use spl_token_2022_interface::ID as SPL_TOKEN_2022_ID;
22
23use crate::generated::errors::token_acl::TokenAclError;
24
25#[allow(clippy::too_many_arguments)]
26pub async fn create_thaw_permissionless_instruction_with_extra_metas<F, Fut>(
27 signer_pubkey: &Pubkey,
28 token_account_pubkey: &Pubkey,
29 mint_pubkey: &Pubkey,
30 mint_config_pubkey: &Pubkey,
31 token_program_pubkey: &Pubkey,
32 token_account_owner_pubkey: &Pubkey,
33 idempotent: bool,
34 fetch_account_data_fn: F,
35) -> Result<Instruction, AccountFetchError>
36where
37 F: Fn(Pubkey) -> Fut,
38 Fut: Future<Output = AccountDataResult>,
39{
40 let mint_config = fetch_account_data_fn(*mint_config_pubkey)
41 .await?
42 .and_then(|data| crate::accounts::MintConfig::from_bytes(&data).ok())
43 .ok_or(ProgramError::InvalidAccountData)?;
44
45 let flag_account = crate::accounts::FlagAccount::find_pda(token_account_pubkey).0;
46
47 if !mint_config.enable_permissionless_thaw {
48 return Err(TokenAclError::PermissionlessThawNotEnabled.into());
49 }
50
51 let mut ix = if idempotent {
52 crate::instructions::ThawPermissionlessIdempotentBuilder::new()
53 .gating_program(mint_config.gating_program)
54 .authority(*signer_pubkey)
55 .mint(*mint_pubkey)
56 .token_account(*token_account_pubkey)
57 .token_account_owner(*token_account_owner_pubkey)
58 .mint_config(*mint_config_pubkey)
59 .token_program(*token_program_pubkey)
60 .flag_account(flag_account)
61 .system_program(solana_system_interface::program::ID)
62 .instruction()
63 } else {
64 crate::instructions::ThawPermissionlessBuilder::new()
65 .gating_program(mint_config.gating_program)
66 .authority(*signer_pubkey)
67 .mint(*mint_pubkey)
68 .token_account(*token_account_pubkey)
69 .token_account_owner(*token_account_owner_pubkey)
70 .mint_config(*mint_config_pubkey)
71 .token_program(*token_program_pubkey)
72 .flag_account(flag_account)
73 .system_program(solana_system_interface::program::ID)
74 .instruction()
75 };
76
77 if mint_config.gating_program != Pubkey::default() {
78 token_acl_interface::offchain::add_extra_account_metas_for_thaw(
79 &mut ix,
80 &mint_config.gating_program,
81 signer_pubkey,
82 token_account_pubkey,
83 mint_pubkey,
84 token_account_owner_pubkey,
85 &flag_account,
86 fetch_account_data_fn,
87 )
88 .await?;
89 }
90
91 Ok(ix)
92}
93
94#[allow(clippy::too_many_arguments)]
95pub async fn create_freeze_permissionless_instruction_with_extra_metas<F, Fut>(
96 signer_pubkey: &Pubkey,
97 token_account_pubkey: &Pubkey,
98 mint_pubkey: &Pubkey,
99 mint_config_pubkey: &Pubkey,
100 token_program_pubkey: &Pubkey,
101 token_account_owner_pubkey: &Pubkey,
102 idempotent: bool,
103 fetch_account_data_fn: F,
104) -> Result<Instruction, AccountFetchError>
105where
106 F: Fn(Pubkey) -> Fut,
107 Fut: Future<Output = AccountDataResult>,
108{
109 let mint_config = fetch_account_data_fn(*mint_config_pubkey)
110 .await?
111 .and_then(|data| crate::accounts::MintConfig::from_bytes(&data).ok())
112 .ok_or(ProgramError::InvalidAccountData)?;
113
114 if !mint_config.enable_permissionless_freeze {
115 return Err(TokenAclError::PermissionlessFreezeNotEnabled.into());
116 }
117
118 let flag_account = crate::accounts::FlagAccount::find_pda(&token_account_pubkey).0;
119
120 let mut ix = if idempotent {
121 crate::instructions::FreezePermissionlessIdempotentBuilder::new()
122 .gating_program(mint_config.gating_program)
123 .authority(*signer_pubkey)
124 .mint(*mint_pubkey)
125 .token_account(*token_account_pubkey)
126 .token_account_owner(*token_account_owner_pubkey)
127 .mint_config(*mint_config_pubkey)
128 .token_program(*token_program_pubkey)
129 .system_program(solana_system_interface::program::ID)
130 .flag_account(flag_account)
131 .instruction()
132 } else {
133 crate::instructions::FreezePermissionlessBuilder::new()
134 .gating_program(mint_config.gating_program)
135 .authority(*signer_pubkey)
136 .mint(*mint_pubkey)
137 .token_account(*token_account_pubkey)
138 .token_account_owner(*token_account_owner_pubkey)
139 .mint_config(*mint_config_pubkey)
140 .token_program(*token_program_pubkey)
141 .system_program(solana_system_interface::program::ID)
142 .flag_account(flag_account)
143 .instruction()
144 };
145
146 if mint_config.gating_program != Pubkey::default() {
147 token_acl_interface::offchain::add_extra_account_metas_for_freeze(
148 &mut ix,
149 &mint_config.gating_program,
150 signer_pubkey,
151 token_account_pubkey,
152 mint_pubkey,
153 token_account_owner_pubkey,
154 &flag_account,
155 fetch_account_data_fn,
156 )
157 .await?;
158 }
159
160 Ok(ix)
161}
162
163#[cfg(feature = "fetch")]
164pub async fn create_ata_and_thaw_permissionless(
165 rpc: &nonblocking::rpc_client::RpcClient,
166 payer_pubkey: &Pubkey,
167 mint_pubkey: &Pubkey,
168 token_account_owner_pubkey: &Pubkey,
169 idempotent: bool,
170) -> Result<Vec<Instruction>, AccountFetchError> {
171 let fetch_account_data_fn = |pubkey: Pubkey| async move {
172 rpc.get_account_data(&pubkey)
173 .await
174 .map(|data| Some(data.to_vec()))
175 .map_err(Into::<AccountFetchError>::into)
176 };
177
178 create_ata_and_thaw_permissionless_instructions(
179 payer_pubkey,
180 mint_pubkey,
181 &SPL_TOKEN_2022_ID,
182 token_account_owner_pubkey,
183 idempotent,
184 &fetch_account_data_fn,
185 )
186 .await
187}
188
189#[allow(clippy::too_many_arguments)]
190pub async fn create_ata_and_thaw_permissionless_instructions<F, Fut>(
191 payer_pubkey: &Pubkey,
192 mint_pubkey: &Pubkey,
193 token_program_pubkey: &Pubkey,
194 token_account_owner_pubkey: &Pubkey,
195 idempotent: bool,
196 fetch_account_data_fn: &F,
197) -> Result<Vec<Instruction>, AccountFetchError>
198where
199 F: Fn(Pubkey) -> Fut,
200 Fut: Future<Output = AccountDataResult>,
201{
202 let token_account = get_associated_token_address_with_program_id(
203 &token_account_owner_pubkey,
204 &mint_pubkey,
205 &SPL_TOKEN_2022_ID,
206 );
207
208 let ix = if idempotent {
209 create_associated_token_account_idempotent(
210 &payer_pubkey,
211 &token_account_owner_pubkey,
212 &mint_pubkey,
213 &SPL_TOKEN_2022_ID,
214 )
215 } else {
216 create_associated_token_account(
217 &payer_pubkey,
218 &token_account_owner_pubkey,
219 &mint_pubkey,
220 &SPL_TOKEN_2022_ID,
221 )
222 };
223 let mut instructions = vec![ix];
224
225 let acc = Account {
227 mint: *mint_pubkey,
228 owner: *token_account_owner_pubkey,
229 amount: 0,
230 delegate: COption::None,
231 state: AccountState::Frozen,
232 is_native: COption::None,
233 delegated_amount: 0,
234 close_authority: COption::None,
235 };
236
237 let mut data = vec![0u8; Account::LEN];
238 Account::pack(acc, &mut data)?;
239
240 let mint_data = fetch_account_data_fn(*mint_pubkey)
241 .await?
242 .ok_or(Into::<ProgramError>::into(TokenAclError::InvalidTokenMint))?;
243
244 let mint_config_pubkey = crate::accounts::MintConfig::find_pda(mint_pubkey).0;
245 let gating_program = get_gating_program_from_mint_data(&mint_data);
246 let flag_account = crate::accounts::FlagAccount::find_pda(&token_account).0;
247
248 if let Ok(gating_program) = gating_program {
249 let mut ix = if idempotent {
250 crate::instructions::ThawPermissionlessIdempotentBuilder::new()
251 .gating_program(gating_program)
252 .authority(*payer_pubkey)
253 .mint(*mint_pubkey)
254 .token_account(token_account)
255 .token_account_owner(*token_account_owner_pubkey)
256 .mint_config(mint_config_pubkey)
257 .token_program(*token_program_pubkey)
258 .flag_account(flag_account)
259 .system_program(solana_system_interface::program::ID)
260 .instruction()
261 } else {
262 crate::instructions::ThawPermissionlessBuilder::new()
263 .gating_program(gating_program)
264 .authority(*payer_pubkey)
265 .mint(*mint_pubkey)
266 .token_account(token_account)
267 .token_account_owner(*token_account_owner_pubkey)
268 .mint_config(mint_config_pubkey)
269 .token_program(*token_program_pubkey)
270 .flag_account(flag_account)
271 .system_program(solana_system_interface::program::ID)
272 .instruction()
273 };
274
275 token_acl_interface::offchain::add_extra_account_metas_for_thaw(
276 &mut ix,
277 &gating_program,
278 payer_pubkey,
279 &token_account,
280 mint_pubkey,
281 token_account_owner_pubkey,
282 &flag_account,
283 |pubkey| {
284 let data = data.clone();
285 async move {
286 if pubkey == token_account {
287 return Ok(Some(data));
288 }
289 let data = fetch_account_data_fn(pubkey).await.unwrap_or(None);
290 Ok(data)
291 }
292 },
293 )
294 .await?;
295
296 instructions.push(ix);
297 }
298
299 Ok(instructions)
300}