1use {
2 crate::client::{
3 ProgramClient, ProgramClientError, SendTransaction, SimulateTransaction, SimulationResult,
4 },
5 bytemuck::{bytes_of, Pod},
6 futures::future::join_all,
7 futures_util::TryFutureExt,
8 solana_account::Account as BaseAccount,
9 solana_compute_budget_interface::ComputeBudgetInstruction,
10 solana_hash::Hash,
11 solana_instruction::{AccountMeta, Instruction},
12 solana_message::Message,
13 solana_packet::PACKET_DATA_SIZE,
14 solana_program_error::ProgramError,
15 solana_program_pack::Pack,
16 solana_pubkey::Pubkey,
17 solana_signature::Signature,
18 solana_signer::{signers::Signers, Signer, SignerError},
19 solana_system_interface::instruction as system_instruction,
20 solana_transaction::Transaction,
21 spl_associated_token_account_interface::{
22 address::get_associated_token_address_with_program_id,
23 instruction::{
24 create_associated_token_account, create_associated_token_account_idempotent,
25 },
26 },
27 spl_record::state::RecordData,
28 spl_token_2022::{
29 extension::{
30 confidential_mint_burn::account_info::{BurnAccountInfo, SupplyAccountInfo},
31 confidential_transfer::account_info::{
32 ApplyPendingBalanceAccountInfo, EmptyAccountAccountInfo, TransferAccountInfo,
33 WithdrawAccountInfo,
34 },
35 confidential_transfer_fee::account_info::WithheldTokensInfo,
36 },
37 offchain,
38 },
39 spl_token_2022_interface::{
40 extension::{
41 confidential_mint_burn::{self, ConfidentialMintBurn},
42 confidential_transfer::{self, ConfidentialTransferAccount, DecryptableBalance},
43 confidential_transfer_fee::{
44 self, ConfidentialTransferFeeAmount, ConfidentialTransferFeeConfig,
45 },
46 cpi_guard, default_account_state, group_member_pointer, group_pointer,
47 interest_bearing_mint, memo_transfer, metadata_pointer, pausable, scaled_ui_amount,
48 transfer_fee, transfer_hook, BaseStateWithExtensions, Extension, ExtensionType,
49 StateWithExtensionsOwned,
50 },
51 instruction,
52 solana_zk_sdk::{
53 encryption::{
54 auth_encryption::AeKey,
55 elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey, ElGamalSecretKey},
56 pod::{
57 auth_encryption::PodAeCiphertext,
58 elgamal::{PodElGamalCiphertext, PodElGamalPubkey},
59 },
60 },
61 zk_elgamal_proof_program::{
62 self,
63 instruction::{close_context_state, ContextStateInfo},
64 proof_data::*,
65 state::ProofContextState,
66 },
67 },
68 state::{Account, AccountState, Mint, Multisig},
69 },
70 spl_token_confidential_transfer_proof_extraction::instruction::{
71 zk_proof_type_to_instruction, ProofLocation,
72 },
73 spl_token_confidential_transfer_proof_generation::{
74 burn::BurnProofData, mint::MintProofData, transfer::TransferProofData,
75 transfer_with_fee::TransferWithFeeProofData, withdraw::WithdrawProofData,
76 },
77 spl_token_group_interface::state::{TokenGroup, TokenGroupMember},
78 spl_token_metadata_interface::state::{Field, TokenMetadata},
79 std::{
80 fmt, io,
81 mem::size_of,
82 sync::{Arc, RwLock},
83 time::{Duration, Instant},
84 },
85 thiserror::Error,
86 tokio::time,
87};
88
89#[derive(Error, Debug)]
90pub enum TokenError {
91 #[error("client error: {0}")]
92 Client(ProgramClientError),
93 #[error("program error: {0}")]
94 Program(#[from] ProgramError),
95 #[error("account not found")]
96 AccountNotFound,
97 #[error("invalid account owner")]
98 AccountInvalidOwner,
99 #[error("invalid account mint")]
100 AccountInvalidMint,
101 #[error("invalid associated account address")]
102 AccountInvalidAssociatedAddress,
103 #[error("invalid auxiliary account address")]
104 AccountInvalidAuxiliaryAddress,
105 #[error("proof generation")]
106 ProofGeneration,
107 #[error("maximum deposit transfer amount exceeded")]
108 MaximumDepositTransferAmountExceeded,
109 #[error("encryption key error")]
110 Key(SignerError),
111 #[error("account decryption failed")]
112 AccountDecryption,
113 #[error("not enough funds in account")]
114 NotEnoughFunds,
115 #[error("missing memo signer")]
116 MissingMemoSigner,
117 #[error("decimals required, but missing")]
118 MissingDecimals,
119 #[error("decimals specified, but incorrect")]
120 InvalidDecimals,
121}
122impl PartialEq for TokenError {
123 fn eq(&self, other: &Self) -> bool {
124 match (self, other) {
125 (Self::Client(ref a), Self::Client(ref b)) => a.to_string() == b.to_string(),
128 (Self::Program(ref a), Self::Program(ref b)) => a == b,
129 (Self::AccountNotFound, Self::AccountNotFound) => true,
130 (Self::AccountInvalidOwner, Self::AccountInvalidOwner) => true,
131 (Self::AccountInvalidMint, Self::AccountInvalidMint) => true,
132 (Self::AccountInvalidAssociatedAddress, Self::AccountInvalidAssociatedAddress) => true,
133 (Self::AccountInvalidAuxiliaryAddress, Self::AccountInvalidAuxiliaryAddress) => true,
134 (Self::ProofGeneration, Self::ProofGeneration) => true,
135 (
136 Self::MaximumDepositTransferAmountExceeded,
137 Self::MaximumDepositTransferAmountExceeded,
138 ) => true,
139 (Self::AccountDecryption, Self::AccountDecryption) => true,
140 (Self::NotEnoughFunds, Self::NotEnoughFunds) => true,
141 (Self::MissingMemoSigner, Self::MissingMemoSigner) => true,
142 (Self::MissingDecimals, Self::MissingDecimals) => true,
143 (Self::InvalidDecimals, Self::InvalidDecimals) => true,
144 _ => false,
145 }
146 }
147}
148
149#[derive(Clone, Debug, PartialEq)]
151pub enum ExtensionInitializationParams {
152 ConfidentialTransferMint {
153 authority: Option<Pubkey>,
154 auto_approve_new_accounts: bool,
155 auditor_elgamal_pubkey: Option<PodElGamalPubkey>,
156 },
157 DefaultAccountState {
158 state: AccountState,
159 },
160 MintCloseAuthority {
161 close_authority: Option<Pubkey>,
162 },
163 TransferFeeConfig {
164 transfer_fee_config_authority: Option<Pubkey>,
165 withdraw_withheld_authority: Option<Pubkey>,
166 transfer_fee_basis_points: u16,
167 maximum_fee: u64,
168 },
169 InterestBearingConfig {
170 rate_authority: Option<Pubkey>,
171 rate: i16,
172 },
173 NonTransferable,
174 PermanentDelegate {
175 delegate: Pubkey,
176 },
177 TransferHook {
178 authority: Option<Pubkey>,
179 program_id: Option<Pubkey>,
180 },
181 MetadataPointer {
182 authority: Option<Pubkey>,
183 metadata_address: Option<Pubkey>,
184 },
185 ConfidentialTransferFeeConfig {
186 authority: Option<Pubkey>,
187 withdraw_withheld_authority_elgamal_pubkey: PodElGamalPubkey,
188 },
189 GroupPointer {
190 authority: Option<Pubkey>,
191 group_address: Option<Pubkey>,
192 },
193 GroupMemberPointer {
194 authority: Option<Pubkey>,
195 member_address: Option<Pubkey>,
196 },
197 ScaledUiAmountConfig {
198 authority: Option<Pubkey>,
199 multiplier: f64,
200 },
201 PausableConfig {
202 authority: Pubkey,
203 },
204 ConfidentialMintBurn {
205 supply_elgamal_pubkey: PodElGamalPubkey,
206 decryptable_supply: PodAeCiphertext,
207 },
208}
209impl ExtensionInitializationParams {
210 pub fn extension(&self) -> ExtensionType {
212 match self {
213 Self::ConfidentialTransferMint { .. } => ExtensionType::ConfidentialTransferMint,
214 Self::DefaultAccountState { .. } => ExtensionType::DefaultAccountState,
215 Self::MintCloseAuthority { .. } => ExtensionType::MintCloseAuthority,
216 Self::TransferFeeConfig { .. } => ExtensionType::TransferFeeConfig,
217 Self::InterestBearingConfig { .. } => ExtensionType::InterestBearingConfig,
218 Self::NonTransferable => ExtensionType::NonTransferable,
219 Self::PermanentDelegate { .. } => ExtensionType::PermanentDelegate,
220 Self::TransferHook { .. } => ExtensionType::TransferHook,
221 Self::MetadataPointer { .. } => ExtensionType::MetadataPointer,
222 Self::ConfidentialTransferFeeConfig { .. } => {
223 ExtensionType::ConfidentialTransferFeeConfig
224 }
225 Self::GroupPointer { .. } => ExtensionType::GroupPointer,
226 Self::GroupMemberPointer { .. } => ExtensionType::GroupMemberPointer,
227 Self::ScaledUiAmountConfig { .. } => ExtensionType::ScaledUiAmount,
228 Self::PausableConfig { .. } => ExtensionType::Pausable,
229 Self::ConfidentialMintBurn { .. } => ExtensionType::ConfidentialMintBurn,
230 }
231 }
232 pub fn instruction(
234 self,
235 token_program_id: &Pubkey,
236 mint: &Pubkey,
237 ) -> Result<Instruction, ProgramError> {
238 match self {
239 Self::ConfidentialTransferMint {
240 authority,
241 auto_approve_new_accounts,
242 auditor_elgamal_pubkey,
243 } => confidential_transfer::instruction::initialize_mint(
244 token_program_id,
245 mint,
246 authority,
247 auto_approve_new_accounts,
248 auditor_elgamal_pubkey,
249 ),
250 Self::DefaultAccountState { state } => {
251 default_account_state::instruction::initialize_default_account_state(
252 token_program_id,
253 mint,
254 &state,
255 )
256 }
257 Self::MintCloseAuthority { close_authority } => {
258 instruction::initialize_mint_close_authority(
259 token_program_id,
260 mint,
261 close_authority.as_ref(),
262 )
263 }
264 Self::TransferFeeConfig {
265 transfer_fee_config_authority,
266 withdraw_withheld_authority,
267 transfer_fee_basis_points,
268 maximum_fee,
269 } => transfer_fee::instruction::initialize_transfer_fee_config(
270 token_program_id,
271 mint,
272 transfer_fee_config_authority.as_ref(),
273 withdraw_withheld_authority.as_ref(),
274 transfer_fee_basis_points,
275 maximum_fee,
276 ),
277 Self::InterestBearingConfig {
278 rate_authority,
279 rate,
280 } => interest_bearing_mint::instruction::initialize(
281 token_program_id,
282 mint,
283 rate_authority,
284 rate,
285 ),
286 Self::NonTransferable => {
287 instruction::initialize_non_transferable_mint(token_program_id, mint)
288 }
289 Self::PermanentDelegate { delegate } => {
290 instruction::initialize_permanent_delegate(token_program_id, mint, &delegate)
291 }
292 Self::TransferHook {
293 authority,
294 program_id,
295 } => transfer_hook::instruction::initialize(
296 token_program_id,
297 mint,
298 authority,
299 program_id,
300 ),
301 Self::MetadataPointer {
302 authority,
303 metadata_address,
304 } => metadata_pointer::instruction::initialize(
305 token_program_id,
306 mint,
307 authority,
308 metadata_address,
309 ),
310 Self::ConfidentialTransferFeeConfig {
311 authority,
312 withdraw_withheld_authority_elgamal_pubkey,
313 } => {
314 confidential_transfer_fee::instruction::initialize_confidential_transfer_fee_config(
315 token_program_id,
316 mint,
317 authority,
318 &withdraw_withheld_authority_elgamal_pubkey,
319 )
320 }
321 Self::GroupPointer {
322 authority,
323 group_address,
324 } => group_pointer::instruction::initialize(
325 token_program_id,
326 mint,
327 authority,
328 group_address,
329 ),
330 Self::GroupMemberPointer {
331 authority,
332 member_address,
333 } => group_member_pointer::instruction::initialize(
334 token_program_id,
335 mint,
336 authority,
337 member_address,
338 ),
339 Self::ScaledUiAmountConfig {
340 authority,
341 multiplier,
342 } => scaled_ui_amount::instruction::initialize(
343 token_program_id,
344 mint,
345 authority,
346 multiplier,
347 ),
348 Self::PausableConfig { authority } => {
349 pausable::instruction::initialize(token_program_id, mint, &authority)
350 }
351 Self::ConfidentialMintBurn {
352 supply_elgamal_pubkey,
353 decryptable_supply,
354 } => confidential_mint_burn::instruction::initialize_mint(
355 token_program_id,
356 mint,
357 &supply_elgamal_pubkey,
358 &decryptable_supply,
359 ),
360 }
361 }
362}
363
364pub type TokenResult<T> = Result<T, TokenError>;
365
366#[derive(Debug)]
367struct TokenMemo {
368 text: String,
369 signers: Vec<Pubkey>,
370}
371impl TokenMemo {
372 pub fn to_instruction(&self) -> Instruction {
373 spl_memo_interface::instruction::build_memo(
374 &spl_memo_interface::v3::id(),
375 self.text.as_bytes(),
376 &self.signers.iter().collect::<Vec<_>>(),
377 )
378 }
379}
380
381#[derive(Debug, Clone)]
382pub enum ComputeUnitLimit {
383 Default,
384 Simulated,
385 Static(u32),
386}
387
388pub struct ProofAccountWithCiphertext {
389 pub context_state_account: Pubkey,
390 pub ciphertext_lo: PodElGamalCiphertext,
391 pub ciphertext_hi: PodElGamalCiphertext,
392}
393
394pub struct Token<T> {
395 client: Arc<dyn ProgramClient<T>>,
396 pubkey: Pubkey, decimals: Option<u8>,
398 payer: Arc<dyn Signer>,
399 program_id: Pubkey,
400 nonce_account: Option<Pubkey>,
401 nonce_authority: Option<Arc<dyn Signer>>,
402 nonce_blockhash: Option<Hash>,
403 memo: Arc<RwLock<Option<TokenMemo>>>,
404 transfer_hook_accounts: Option<Vec<AccountMeta>>,
405 compute_unit_price: Option<u64>,
406 compute_unit_limit: ComputeUnitLimit,
407}
408
409impl<T> fmt::Debug for Token<T> {
410 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
411 f.debug_struct("Token")
412 .field("pubkey", &self.pubkey)
413 .field("decimals", &self.decimals)
414 .field("payer", &self.payer.pubkey())
415 .field("program_id", &self.program_id)
416 .field("nonce_account", &self.nonce_account)
417 .field(
418 "nonce_authority",
419 &self.nonce_authority.as_ref().map(|s| s.pubkey()),
420 )
421 .field("nonce_blockhash", &self.nonce_blockhash)
422 .field("memo", &self.memo.read().unwrap())
423 .field("transfer_hook_accounts", &self.transfer_hook_accounts)
424 .field("compute_unit_price", &self.compute_unit_price)
425 .field("compute_unit_limit", &self.compute_unit_limit)
426 .finish()
427 }
428}
429
430fn native_mint(program_id: &Pubkey) -> Pubkey {
431 if program_id == &spl_token_2022_interface::id() {
432 spl_token_2022_interface::native_mint::id()
433 } else if program_id == &spl_token_interface::id() {
434 spl_token_interface::native_mint::id()
435 } else {
436 panic!("Unrecognized token program id: {}", program_id);
437 }
438}
439
440fn native_mint_decimals(program_id: &Pubkey) -> u8 {
441 if program_id == &spl_token_2022_interface::id() {
442 spl_token_2022_interface::native_mint::DECIMALS
443 } else if program_id == &spl_token_interface::id() {
444 spl_token_interface::native_mint::DECIMALS
445 } else {
446 panic!("Unrecognized token program id: {}", program_id);
447 }
448}
449
450impl<T> Token<T>
451where
452 T: SendTransaction + SimulateTransaction,
453{
454 pub fn new(
455 client: Arc<dyn ProgramClient<T>>,
456 program_id: &Pubkey,
457 address: &Pubkey,
458 decimals: Option<u8>,
459 payer: Arc<dyn Signer>,
460 ) -> Self {
461 Token {
462 client,
463 pubkey: *address,
464 decimals,
465 payer,
466 program_id: *program_id,
467 nonce_account: None,
468 nonce_authority: None,
469 nonce_blockhash: None,
470 memo: Arc::new(RwLock::new(None)),
471 transfer_hook_accounts: None,
472 compute_unit_price: None,
473 compute_unit_limit: ComputeUnitLimit::Default,
474 }
475 }
476
477 pub fn new_native(
478 client: Arc<dyn ProgramClient<T>>,
479 program_id: &Pubkey,
480 payer: Arc<dyn Signer>,
481 ) -> Self {
482 Self::new(
483 client,
484 program_id,
485 &native_mint(program_id),
486 Some(native_mint_decimals(program_id)),
487 payer,
488 )
489 }
490
491 pub fn is_native(&self) -> bool {
492 self.pubkey == native_mint(&self.program_id)
493 }
494
495 pub fn get_address(&self) -> &Pubkey {
497 &self.pubkey
498 }
499
500 pub fn with_payer(mut self, payer: Arc<dyn Signer>) -> Self {
501 self.payer = payer;
502 self
503 }
504
505 pub fn with_nonce(
506 mut self,
507 nonce_account: &Pubkey,
508 nonce_authority: Arc<dyn Signer>,
509 nonce_blockhash: &Hash,
510 ) -> Self {
511 self.nonce_account = Some(*nonce_account);
512 self.nonce_authority = Some(nonce_authority);
513 self.nonce_blockhash = Some(*nonce_blockhash);
514 self.transfer_hook_accounts = Some(vec![]);
515 self
516 }
517
518 pub fn with_transfer_hook_accounts(mut self, transfer_hook_accounts: Vec<AccountMeta>) -> Self {
519 self.transfer_hook_accounts = Some(transfer_hook_accounts);
520 self
521 }
522
523 pub fn with_compute_unit_price(mut self, compute_unit_price: u64) -> Self {
524 self.compute_unit_price = Some(compute_unit_price);
525 self
526 }
527
528 pub fn with_compute_unit_limit(mut self, compute_unit_limit: ComputeUnitLimit) -> Self {
529 self.compute_unit_limit = compute_unit_limit;
530 self
531 }
532
533 pub fn with_memo<M: AsRef<str>>(&self, memo: M, signers: Vec<Pubkey>) -> &Self {
534 let mut w_memo = self.memo.write().unwrap();
535 *w_memo = Some(TokenMemo {
536 text: memo.as_ref().to_string(),
537 signers,
538 });
539 self
540 }
541
542 pub async fn get_new_latest_blockhash(&self) -> TokenResult<Hash> {
543 let blockhash = self
544 .client
545 .get_latest_blockhash()
546 .await
547 .map_err(TokenError::Client)?;
548 let start = Instant::now();
549 let mut num_retries = 0;
550 while start.elapsed().as_secs() < 5 {
551 let new_blockhash = self
552 .client
553 .get_latest_blockhash()
554 .await
555 .map_err(TokenError::Client)?;
556 if new_blockhash != blockhash {
557 return Ok(new_blockhash);
558 }
559
560 time::sleep(Duration::from_millis(200)).await;
561 num_retries += 1;
562 }
563
564 Err(TokenError::Client(Box::new(io::Error::new(
565 io::ErrorKind::Other,
566 format!(
567 "Unable to get new blockhash after {}ms (retried {} times), stuck at {}",
568 start.elapsed().as_millis(),
569 num_retries,
570 blockhash
571 ),
572 ))))
573 }
574
575 fn get_multisig_signers<'a>(
576 &self,
577 authority: &Pubkey,
578 signing_pubkeys: &'a [Pubkey],
579 ) -> Vec<&'a Pubkey> {
580 if signing_pubkeys == [*authority] {
581 vec![]
582 } else {
583 signing_pubkeys.iter().collect::<Vec<_>>()
584 }
585 }
586
587 async fn add_compute_unit_limit_from_simulation(
590 &self,
591 instructions: &mut Vec<Instruction>,
592 blockhash: &Hash,
593 ) -> TokenResult<()> {
594 const MAX_COMPUTE_UNIT_LIMIT: u32 = 1_400_000;
596 instructions.push(ComputeBudgetInstruction::set_compute_unit_limit(
597 MAX_COMPUTE_UNIT_LIMIT,
598 ));
599
600 let transaction = Transaction::new_unsigned(Message::new_with_blockhash(
601 instructions,
602 Some(&self.payer.pubkey()),
603 blockhash,
604 ));
605 let simulation_result = self
606 .client
607 .simulate_transaction(&transaction)
608 .await
609 .map_err(TokenError::Client)?;
610 let units_consumed = simulation_result
611 .get_compute_units_consumed()
612 .map_err(TokenError::Client)?;
613 let compute_unit_limit =
615 u32::try_from(units_consumed).map_err(|x| TokenError::Client(x.into()))?;
616 instructions
617 .last_mut()
618 .expect("Compute budget instruction was added earlier")
619 .data = ComputeBudgetInstruction::set_compute_unit_limit(compute_unit_limit).data;
620 Ok(())
621 }
622
623 async fn construct_tx<S: Signers>(
624 &self,
625 token_instructions: &[Instruction],
626 signing_keypairs: &S,
627 ) -> TokenResult<Transaction> {
628 let mut instructions = vec![];
629 let payer_key = self.payer.pubkey();
630 let fee_payer = Some(&payer_key);
631
632 {
633 let mut w_memo = self.memo.write().unwrap();
634 if let Some(memo) = w_memo.take() {
635 let signing_pubkeys = signing_keypairs.pubkeys();
636 if !memo
637 .signers
638 .iter()
639 .all(|signer| signing_pubkeys.contains(signer))
640 {
641 return Err(TokenError::MissingMemoSigner);
642 }
643
644 instructions.push(memo.to_instruction());
645 }
646 }
647
648 instructions.extend_from_slice(token_instructions);
649
650 let blockhash = if let (Some(nonce_account), Some(nonce_authority), Some(nonce_blockhash)) = (
651 self.nonce_account,
652 &self.nonce_authority,
653 self.nonce_blockhash,
654 ) {
655 let nonce_instruction = system_instruction::advance_nonce_account(
656 &nonce_account,
657 &nonce_authority.pubkey(),
658 );
659 instructions.insert(0, nonce_instruction);
660 nonce_blockhash
661 } else {
662 self.client
663 .get_latest_blockhash()
664 .await
665 .map_err(TokenError::Client)?
666 };
667
668 if let Some(compute_unit_price) = self.compute_unit_price {
669 instructions.push(ComputeBudgetInstruction::set_compute_unit_price(
670 compute_unit_price,
671 ));
672 }
673
674 match self.compute_unit_limit {
679 ComputeUnitLimit::Default => {}
680 ComputeUnitLimit::Simulated => {
681 self.add_compute_unit_limit_from_simulation(&mut instructions, &blockhash)
682 .await?;
683 }
684 ComputeUnitLimit::Static(compute_unit_limit) => {
685 instructions.push(ComputeBudgetInstruction::set_compute_unit_limit(
686 compute_unit_limit,
687 ));
688 }
689 }
690
691 let message = Message::new_with_blockhash(&instructions, fee_payer, &blockhash);
692 let mut transaction = Transaction::new_unsigned(message);
693 let signing_pubkeys = signing_keypairs.pubkeys();
694
695 if !signing_pubkeys.contains(&self.payer.pubkey()) {
696 transaction
697 .try_partial_sign(&vec![self.payer.clone()], blockhash)
698 .map_err(|error| TokenError::Client(error.into()))?;
699 }
700 if let Some(nonce_authority) = &self.nonce_authority {
701 let nonce_authority_pubkey = nonce_authority.pubkey();
702 if nonce_authority_pubkey != self.payer.pubkey()
703 && !signing_pubkeys.contains(&nonce_authority_pubkey)
704 {
705 transaction
706 .try_partial_sign(&vec![nonce_authority.clone()], blockhash)
707 .map_err(|error| TokenError::Client(error.into()))?;
708 }
709 }
710 transaction
711 .try_partial_sign(signing_keypairs, blockhash)
712 .map_err(|error| TokenError::Client(error.into()))?;
713
714 Ok(transaction)
715 }
716
717 pub async fn simulate_ixs<S: Signers>(
718 &self,
719 token_instructions: &[Instruction],
720 signing_keypairs: &S,
721 ) -> TokenResult<T::SimulationOutput> {
722 let transaction = self
723 .construct_tx(token_instructions, signing_keypairs)
724 .await?;
725
726 self.client
727 .simulate_transaction(&transaction)
728 .await
729 .map_err(TokenError::Client)
730 }
731
732 pub async fn process_ixs<S: Signers>(
733 &self,
734 token_instructions: &[Instruction],
735 signing_keypairs: &S,
736 ) -> TokenResult<T::Output> {
737 let transaction = self
738 .construct_tx(token_instructions, signing_keypairs)
739 .await?;
740
741 self.client
742 .send_transaction(&transaction)
743 .await
744 .map_err(TokenError::Client)
745 }
746
747 #[allow(clippy::too_many_arguments)]
748 pub async fn create_mint<'a, S: Signers>(
749 &self,
750 mint_authority: &'a Pubkey,
751 freeze_authority: Option<&'a Pubkey>,
752 extension_initialization_params: Vec<ExtensionInitializationParams>,
753 signing_keypairs: &S,
754 ) -> TokenResult<T::Output> {
755 let decimals = self.decimals.ok_or(TokenError::MissingDecimals)?;
756
757 let extension_types = extension_initialization_params
758 .iter()
759 .map(|e| e.extension())
760 .collect::<Vec<_>>();
761 let space = ExtensionType::try_calculate_account_len::<Mint>(&extension_types)?;
762
763 let mut instructions = vec![system_instruction::create_account(
764 &self.payer.pubkey(),
765 &self.pubkey,
766 self.client
767 .get_minimum_balance_for_rent_exemption(space)
768 .await
769 .map_err(TokenError::Client)?,
770 space as u64,
771 &self.program_id,
772 )];
773
774 for params in extension_initialization_params {
775 instructions.push(params.instruction(&self.program_id, &self.pubkey)?);
776 }
777
778 instructions.push(instruction::initialize_mint(
779 &self.program_id,
780 &self.pubkey,
781 mint_authority,
782 freeze_authority,
783 decimals,
784 )?);
785
786 self.process_ixs(&instructions, signing_keypairs).await
787 }
788
789 pub async fn create_native_mint(
791 client: Arc<dyn ProgramClient<T>>,
792 program_id: &Pubkey,
793 payer: Arc<dyn Signer>,
794 ) -> TokenResult<Self> {
795 let token = Self::new_native(client, program_id, payer);
796 token
797 .process_ixs::<[&dyn Signer; 0]>(
798 &[instruction::create_native_mint(
799 program_id,
800 &token.payer.pubkey(),
801 )?],
802 &[],
803 )
804 .await?;
805
806 Ok(token)
807 }
808
809 pub async fn create_multisig(
811 &self,
812 account: &dyn Signer,
813 multisig_members: &[&Pubkey],
814 minimum_signers: u8,
815 ) -> TokenResult<T::Output> {
816 let instructions = vec![
817 system_instruction::create_account(
818 &self.payer.pubkey(),
819 &account.pubkey(),
820 self.client
821 .get_minimum_balance_for_rent_exemption(Multisig::LEN)
822 .await
823 .map_err(TokenError::Client)?,
824 Multisig::LEN as u64,
825 &self.program_id,
826 ),
827 instruction::initialize_multisig(
828 &self.program_id,
829 &account.pubkey(),
830 multisig_members,
831 minimum_signers,
832 )?,
833 ];
834
835 self.process_ixs(&instructions, &[account]).await
836 }
837
838 pub fn get_associated_token_address(&self, owner: &Pubkey) -> Pubkey {
840 get_associated_token_address_with_program_id(owner, &self.pubkey, &self.program_id)
841 }
842
843 pub async fn create_associated_token_account(&self, owner: &Pubkey) -> TokenResult<T::Output> {
845 self.process_ixs::<[&dyn Signer; 0]>(
846 &[create_associated_token_account(
847 &self.payer.pubkey(),
848 owner,
849 &self.pubkey,
850 &self.program_id,
851 )],
852 &[],
853 )
854 .await
855 }
856
857 pub async fn create_auxiliary_token_account(
859 &self,
860 account: &dyn Signer,
861 owner: &Pubkey,
862 ) -> TokenResult<T::Output> {
863 self.create_auxiliary_token_account_with_extension_space(account, owner, vec![])
864 .await
865 }
866
867 pub async fn create_auxiliary_token_account_with_extension_space(
869 &self,
870 account: &dyn Signer,
871 owner: &Pubkey,
872 extensions: Vec<ExtensionType>,
873 ) -> TokenResult<T::Output> {
874 let state = self.get_mint_info().await?;
875 let mint_extensions: Vec<ExtensionType> = state.get_extension_types()?;
876 let mut required_extensions =
877 ExtensionType::get_required_init_account_extensions(&mint_extensions);
878 for extension_type in extensions.into_iter() {
879 if !required_extensions.contains(&extension_type) {
880 required_extensions.push(extension_type);
881 }
882 }
883 let space = ExtensionType::try_calculate_account_len::<Account>(&required_extensions)?;
884 let mut instructions = vec![system_instruction::create_account(
885 &self.payer.pubkey(),
886 &account.pubkey(),
887 self.client
888 .get_minimum_balance_for_rent_exemption(space)
889 .await
890 .map_err(TokenError::Client)?,
891 space as u64,
892 &self.program_id,
893 )];
894
895 if required_extensions.contains(&ExtensionType::ImmutableOwner) {
896 instructions.push(instruction::initialize_immutable_owner(
897 &self.program_id,
898 &account.pubkey(),
899 )?)
900 }
901
902 instructions.push(instruction::initialize_account(
903 &self.program_id,
904 &account.pubkey(),
905 &self.pubkey,
906 owner,
907 )?);
908
909 self.process_ixs(&instructions, &[account]).await
910 }
911
912 pub async fn get_account(&self, account: Pubkey) -> TokenResult<BaseAccount> {
914 self.client
915 .get_account(account)
916 .await
917 .map_err(TokenError::Client)?
918 .ok_or(TokenError::AccountNotFound)
919 }
920
921 fn unpack_mint_info(
922 &self,
923 account: BaseAccount,
924 ) -> TokenResult<StateWithExtensionsOwned<Mint>> {
925 if account.owner != self.program_id {
926 return Err(TokenError::AccountInvalidOwner);
927 }
928
929 let mint_result =
930 StateWithExtensionsOwned::<Mint>::unpack(account.data).map_err(Into::into);
931
932 if let (Ok(mint), Some(decimals)) = (&mint_result, self.decimals) {
933 if decimals != mint.base.decimals {
934 return Err(TokenError::InvalidDecimals);
935 }
936 }
937
938 mint_result
939 }
940
941 pub async fn get_mint_info(&self) -> TokenResult<StateWithExtensionsOwned<Mint>> {
943 let account = self.get_account(self.pubkey).await?;
944 self.unpack_mint_info(account)
945 }
946
947 pub async fn get_account_info(
949 &self,
950 account: &Pubkey,
951 ) -> TokenResult<StateWithExtensionsOwned<Account>> {
952 let account = self.get_account(*account).await?;
953 if account.owner != self.program_id {
954 return Err(TokenError::AccountInvalidOwner);
955 }
956 let account = StateWithExtensionsOwned::<Account>::unpack(account.data)?;
957 if account.base.mint != *self.get_address() {
958 return Err(TokenError::AccountInvalidMint);
959 }
960
961 Ok(account)
962 }
963
964 pub async fn get_or_create_associated_account_info(
966 &self,
967 owner: &Pubkey,
968 ) -> TokenResult<StateWithExtensionsOwned<Account>> {
969 let account = self.get_associated_token_address(owner);
970 match self.get_account_info(&account).await {
971 Ok(account) => Ok(account),
972 Err(TokenError::AccountNotFound) | Err(TokenError::AccountInvalidOwner) => {
974 self.create_associated_token_account(owner).await?;
975 self.get_account_info(&account).await
976 }
977 Err(error) => Err(error),
978 }
979 }
980
981 pub async fn set_authority<S: Signers>(
983 &self,
984 account: &Pubkey,
985 authority: &Pubkey,
986 new_authority: Option<&Pubkey>,
987 authority_type: instruction::AuthorityType,
988 signing_keypairs: &S,
989 ) -> TokenResult<T::Output> {
990 let signing_pubkeys = signing_keypairs.pubkeys();
991 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
992
993 self.process_ixs(
994 &[instruction::set_authority(
995 &self.program_id,
996 account,
997 new_authority,
998 authority_type,
999 authority,
1000 &multisig_signers,
1001 )?],
1002 signing_keypairs,
1003 )
1004 .await
1005 }
1006
1007 pub async fn mint_to<S: Signers>(
1009 &self,
1010 destination: &Pubkey,
1011 authority: &Pubkey,
1012 amount: u64,
1013 signing_keypairs: &S,
1014 ) -> TokenResult<T::Output> {
1015 let signing_pubkeys = signing_keypairs.pubkeys();
1016 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1017
1018 let instructions = if let Some(decimals) = self.decimals {
1019 [instruction::mint_to_checked(
1020 &self.program_id,
1021 &self.pubkey,
1022 destination,
1023 authority,
1024 &multisig_signers,
1025 amount,
1026 decimals,
1027 )?]
1028 } else {
1029 [instruction::mint_to(
1030 &self.program_id,
1031 &self.pubkey,
1032 destination,
1033 authority,
1034 &multisig_signers,
1035 amount,
1036 )?]
1037 };
1038
1039 self.process_ixs(&instructions, signing_keypairs).await
1040 }
1041
1042 #[allow(clippy::too_many_arguments)]
1044 pub async fn transfer<S: Signers>(
1045 &self,
1046 source: &Pubkey,
1047 destination: &Pubkey,
1048 authority: &Pubkey,
1049 amount: u64,
1050 signing_keypairs: &S,
1051 ) -> TokenResult<T::Output> {
1052 let signing_pubkeys = signing_keypairs.pubkeys();
1053 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1054
1055 let fetch_account_data_fn = |address| {
1056 self.client
1057 .get_account(address)
1058 .map_ok(|opt| opt.map(|acc| acc.data))
1059 };
1060
1061 let instruction = if let Some(decimals) = self.decimals {
1062 if let Some(transfer_hook_accounts) = &self.transfer_hook_accounts {
1063 let mut instruction = instruction::transfer_checked(
1064 &self.program_id,
1065 source,
1066 self.get_address(),
1067 destination,
1068 authority,
1069 &multisig_signers,
1070 amount,
1071 decimals,
1072 )?;
1073 instruction.accounts.extend(transfer_hook_accounts.clone());
1074 instruction
1075 } else {
1076 offchain::create_transfer_checked_instruction_with_extra_metas(
1077 &self.program_id,
1078 source,
1079 self.get_address(),
1080 destination,
1081 authority,
1082 &multisig_signers,
1083 amount,
1084 decimals,
1085 fetch_account_data_fn,
1086 )
1087 .await
1088 .map_err(|_| TokenError::AccountNotFound)?
1089 }
1090 } else {
1091 #[allow(deprecated)]
1092 instruction::transfer(
1093 &self.program_id,
1094 source,
1095 destination,
1096 authority,
1097 &multisig_signers,
1098 amount,
1099 )?
1100 };
1101
1102 self.process_ixs(&[instruction], signing_keypairs).await
1103 }
1104
1105 #[allow(clippy::too_many_arguments)]
1108 pub async fn create_recipient_associated_account_and_transfer<S: Signers>(
1109 &self,
1110 source: &Pubkey,
1111 destination: &Pubkey,
1112 destination_owner: &Pubkey,
1113 authority: &Pubkey,
1114 amount: u64,
1115 fee: Option<u64>,
1116 signing_keypairs: &S,
1117 ) -> TokenResult<T::Output> {
1118 let signing_pubkeys = signing_keypairs.pubkeys();
1119 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1120
1121 let fetch_account_data_fn = |address| {
1122 self.client
1123 .get_account(address)
1124 .map_ok(|opt| opt.map(|acc| acc.data))
1125 };
1126
1127 if *destination != self.get_associated_token_address(destination_owner) {
1128 return Err(TokenError::AccountInvalidAssociatedAddress);
1129 }
1130
1131 let mut instructions = vec![
1132 (create_associated_token_account_idempotent(
1133 &self.payer.pubkey(),
1134 destination_owner,
1135 &self.pubkey,
1136 &self.program_id,
1137 )),
1138 ];
1139
1140 if let Some(fee) = fee {
1141 let decimals = self.decimals.ok_or(TokenError::MissingDecimals)?;
1142 instructions.push(transfer_fee::instruction::transfer_checked_with_fee(
1143 &self.program_id,
1144 source,
1145 &self.pubkey,
1146 destination,
1147 authority,
1148 &multisig_signers,
1149 amount,
1150 decimals,
1151 fee,
1152 )?);
1153 } else if let Some(decimals) = self.decimals {
1154 instructions.push(
1155 if let Some(transfer_hook_accounts) = &self.transfer_hook_accounts {
1156 let mut instruction = instruction::transfer_checked(
1157 &self.program_id,
1158 source,
1159 self.get_address(),
1160 destination,
1161 authority,
1162 &multisig_signers,
1163 amount,
1164 decimals,
1165 )?;
1166 instruction.accounts.extend(transfer_hook_accounts.clone());
1167 instruction
1168 } else {
1169 offchain::create_transfer_checked_instruction_with_extra_metas(
1170 &self.program_id,
1171 source,
1172 self.get_address(),
1173 destination,
1174 authority,
1175 &multisig_signers,
1176 amount,
1177 decimals,
1178 fetch_account_data_fn,
1179 )
1180 .await
1181 .map_err(|_| TokenError::AccountNotFound)?
1182 },
1183 );
1184 } else {
1185 #[allow(deprecated)]
1186 instructions.push(instruction::transfer(
1187 &self.program_id,
1188 source,
1189 destination,
1190 authority,
1191 &multisig_signers,
1192 amount,
1193 )?);
1194 }
1195
1196 self.process_ixs(&instructions, signing_keypairs).await
1197 }
1198
1199 #[allow(clippy::too_many_arguments)]
1201 pub async fn transfer_with_fee<S: Signers>(
1202 &self,
1203 source: &Pubkey,
1204 destination: &Pubkey,
1205 authority: &Pubkey,
1206 amount: u64,
1207 fee: u64,
1208 signing_keypairs: &S,
1209 ) -> TokenResult<T::Output> {
1210 let signing_pubkeys = signing_keypairs.pubkeys();
1211 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1212 let decimals = self.decimals.ok_or(TokenError::MissingDecimals)?;
1213
1214 let fetch_account_data_fn = |address| {
1215 self.client
1216 .get_account(address)
1217 .map_ok(|opt| opt.map(|acc| acc.data))
1218 };
1219
1220 let instruction = if let Some(transfer_hook_accounts) = &self.transfer_hook_accounts {
1221 let mut instruction = transfer_fee::instruction::transfer_checked_with_fee(
1222 &self.program_id,
1223 source,
1224 self.get_address(),
1225 destination,
1226 authority,
1227 &multisig_signers,
1228 amount,
1229 decimals,
1230 fee,
1231 )?;
1232 instruction.accounts.extend(transfer_hook_accounts.clone());
1233 instruction
1234 } else {
1235 offchain::create_transfer_checked_with_fee_instruction_with_extra_metas(
1236 &self.program_id,
1237 source,
1238 self.get_address(),
1239 destination,
1240 authority,
1241 &multisig_signers,
1242 amount,
1243 decimals,
1244 fee,
1245 fetch_account_data_fn,
1246 )
1247 .await
1248 .map_err(|_| TokenError::AccountNotFound)?
1249 };
1250
1251 self.process_ixs(&[instruction], signing_keypairs).await
1252 }
1253
1254 pub async fn burn<S: Signers>(
1256 &self,
1257 source: &Pubkey,
1258 authority: &Pubkey,
1259 amount: u64,
1260 signing_keypairs: &S,
1261 ) -> TokenResult<T::Output> {
1262 let signing_pubkeys = signing_keypairs.pubkeys();
1263 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1264
1265 let instructions = if let Some(decimals) = self.decimals {
1266 [instruction::burn_checked(
1267 &self.program_id,
1268 source,
1269 &self.pubkey,
1270 authority,
1271 &multisig_signers,
1272 amount,
1273 decimals,
1274 )?]
1275 } else {
1276 [instruction::burn(
1277 &self.program_id,
1278 source,
1279 &self.pubkey,
1280 authority,
1281 &multisig_signers,
1282 amount,
1283 )?]
1284 };
1285
1286 self.process_ixs(&instructions, signing_keypairs).await
1287 }
1288
1289 pub async fn approve<S: Signers>(
1291 &self,
1292 source: &Pubkey,
1293 delegate: &Pubkey,
1294 authority: &Pubkey,
1295 amount: u64,
1296 signing_keypairs: &S,
1297 ) -> TokenResult<T::Output> {
1298 let signing_pubkeys = signing_keypairs.pubkeys();
1299 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1300
1301 let instructions = if let Some(decimals) = self.decimals {
1302 [instruction::approve_checked(
1303 &self.program_id,
1304 source,
1305 &self.pubkey,
1306 delegate,
1307 authority,
1308 &multisig_signers,
1309 amount,
1310 decimals,
1311 )?]
1312 } else {
1313 [instruction::approve(
1314 &self.program_id,
1315 source,
1316 delegate,
1317 authority,
1318 &multisig_signers,
1319 amount,
1320 )?]
1321 };
1322
1323 self.process_ixs(&instructions, signing_keypairs).await
1324 }
1325
1326 pub async fn revoke<S: Signers>(
1328 &self,
1329 source: &Pubkey,
1330 authority: &Pubkey,
1331 signing_keypairs: &S,
1332 ) -> TokenResult<T::Output> {
1333 let signing_pubkeys = signing_keypairs.pubkeys();
1334 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1335
1336 self.process_ixs(
1337 &[instruction::revoke(
1338 &self.program_id,
1339 source,
1340 authority,
1341 &multisig_signers,
1342 )?],
1343 signing_keypairs,
1344 )
1345 .await
1346 }
1347
1348 pub async fn close_account<S: Signers>(
1350 &self,
1351 account: &Pubkey,
1352 lamports_destination: &Pubkey,
1353 authority: &Pubkey,
1354 signing_keypairs: &S,
1355 ) -> TokenResult<T::Output> {
1356 let signing_pubkeys = signing_keypairs.pubkeys();
1357 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1358
1359 let mut instructions = vec![instruction::close_account(
1360 &self.program_id,
1361 account,
1362 lamports_destination,
1363 authority,
1364 &multisig_signers,
1365 )?];
1366
1367 if let Ok(Some(destination_account)) = self.client.get_account(*lamports_destination).await
1368 {
1369 if let Ok(destination_obj) =
1370 StateWithExtensionsOwned::<Account>::unpack(destination_account.data)
1371 {
1372 if destination_obj.base.is_native() {
1373 instructions.push(instruction::sync_native(
1374 &self.program_id,
1375 lamports_destination,
1376 )?);
1377 }
1378 }
1379 }
1380
1381 self.process_ixs(&instructions, signing_keypairs).await
1382 }
1383
1384 pub async fn empty_and_close_account<S: Signers>(
1386 &self,
1387 account_to_close: &Pubkey,
1388 lamports_destination: &Pubkey,
1389 tokens_destination: &Pubkey,
1390 authority: &Pubkey,
1391 signing_keypairs: &S,
1392 ) -> TokenResult<T::Output> {
1393 let signing_pubkeys = signing_keypairs.pubkeys();
1394 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1395
1396 let account_state = self.get_account_info(account_to_close).await?;
1398
1399 let mut instructions = vec![];
1400
1401 if !self.is_native() && account_state.base.amount > 0 {
1402 if let Some(decimals) = self.decimals {
1404 instructions.push(instruction::transfer_checked(
1405 &self.program_id,
1406 account_to_close,
1407 &self.pubkey,
1408 tokens_destination,
1409 authority,
1410 &multisig_signers,
1411 account_state.base.amount,
1412 decimals,
1413 )?);
1414 } else {
1415 #[allow(deprecated)]
1416 instructions.push(instruction::transfer(
1417 &self.program_id,
1418 account_to_close,
1419 tokens_destination,
1420 authority,
1421 &multisig_signers,
1422 account_state.base.amount,
1423 )?);
1424 }
1425 }
1426
1427 instructions.push(instruction::close_account(
1428 &self.program_id,
1429 account_to_close,
1430 lamports_destination,
1431 authority,
1432 &multisig_signers,
1433 )?);
1434
1435 if let Ok(Some(destination_account)) = self.client.get_account(*lamports_destination).await
1436 {
1437 if let Ok(destination_obj) =
1438 StateWithExtensionsOwned::<Account>::unpack(destination_account.data)
1439 {
1440 if destination_obj.base.is_native() {
1441 instructions.push(instruction::sync_native(
1442 &self.program_id,
1443 lamports_destination,
1444 )?);
1445 }
1446 }
1447 }
1448
1449 self.process_ixs(&instructions, signing_keypairs).await
1450 }
1451
1452 pub async fn freeze<S: Signers>(
1454 &self,
1455 account: &Pubkey,
1456 authority: &Pubkey,
1457 signing_keypairs: &S,
1458 ) -> TokenResult<T::Output> {
1459 let signing_pubkeys = signing_keypairs.pubkeys();
1460 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1461
1462 self.process_ixs(
1463 &[instruction::freeze_account(
1464 &self.program_id,
1465 account,
1466 &self.pubkey,
1467 authority,
1468 &multisig_signers,
1469 )?],
1470 signing_keypairs,
1471 )
1472 .await
1473 }
1474
1475 pub async fn thaw<S: Signers>(
1477 &self,
1478 account: &Pubkey,
1479 authority: &Pubkey,
1480 signing_keypairs: &S,
1481 ) -> TokenResult<T::Output> {
1482 let signing_pubkeys = signing_keypairs.pubkeys();
1483 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1484
1485 self.process_ixs(
1486 &[instruction::thaw_account(
1487 &self.program_id,
1488 account,
1489 &self.pubkey,
1490 authority,
1491 &multisig_signers,
1492 )?],
1493 signing_keypairs,
1494 )
1495 .await
1496 }
1497
1498 pub async fn wrap<S: Signers>(
1500 &self,
1501 account: &Pubkey,
1502 owner: &Pubkey,
1503 lamports: u64,
1504 signing_keypairs: &S,
1505 ) -> TokenResult<T::Output> {
1506 let immutable_owner = self.program_id != spl_token_interface::id();
1508 let instructions = self.wrap_ixs(account, owner, lamports, immutable_owner)?;
1509
1510 self.process_ixs(&instructions, signing_keypairs).await
1511 }
1512
1513 pub async fn wrap_with_mutable_ownership<S: Signers>(
1516 &self,
1517 account: &Pubkey,
1518 owner: &Pubkey,
1519 lamports: u64,
1520 signing_keypairs: &S,
1521 ) -> TokenResult<T::Output> {
1522 let instructions = self.wrap_ixs(account, owner, lamports, false)?;
1523
1524 self.process_ixs(&instructions, signing_keypairs).await
1525 }
1526
1527 fn wrap_ixs(
1528 &self,
1529 account: &Pubkey,
1530 owner: &Pubkey,
1531 lamports: u64,
1532 immutable_owner: bool,
1533 ) -> TokenResult<Vec<Instruction>> {
1534 if !self.is_native() {
1535 return Err(TokenError::AccountInvalidMint);
1536 }
1537
1538 let mut instructions = vec![];
1539 if *account == self.get_associated_token_address(owner) {
1540 instructions.push(system_instruction::transfer(owner, account, lamports));
1541 instructions.push(create_associated_token_account(
1542 &self.payer.pubkey(),
1543 owner,
1544 &self.pubkey,
1545 &self.program_id,
1546 ));
1547 } else {
1548 let extensions = if immutable_owner {
1549 vec![ExtensionType::ImmutableOwner]
1550 } else {
1551 vec![]
1552 };
1553 let space = ExtensionType::try_calculate_account_len::<Account>(&extensions)?;
1554
1555 instructions.push(system_instruction::create_account(
1556 &self.payer.pubkey(),
1557 account,
1558 lamports,
1559 space as u64,
1560 &self.program_id,
1561 ));
1562
1563 if immutable_owner {
1564 instructions.push(instruction::initialize_immutable_owner(
1565 &self.program_id,
1566 account,
1567 )?)
1568 }
1569
1570 instructions.push(instruction::initialize_account(
1571 &self.program_id,
1572 account,
1573 &self.pubkey,
1574 owner,
1575 )?);
1576 };
1577
1578 Ok(instructions)
1579 }
1580
1581 pub async fn sync_native(&self, account: &Pubkey) -> TokenResult<T::Output> {
1583 self.process_ixs::<[&dyn Signer; 0]>(
1584 &[instruction::sync_native(&self.program_id, account)?],
1585 &[],
1586 )
1587 .await
1588 }
1589
1590 pub async fn set_transfer_fee<S: Signers>(
1592 &self,
1593 authority: &Pubkey,
1594 transfer_fee_basis_points: u16,
1595 maximum_fee: u64,
1596 signing_keypairs: &S,
1597 ) -> TokenResult<T::Output> {
1598 let signing_pubkeys = signing_keypairs.pubkeys();
1599 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1600
1601 self.process_ixs(
1602 &[transfer_fee::instruction::set_transfer_fee(
1603 &self.program_id,
1604 &self.pubkey,
1605 authority,
1606 &multisig_signers,
1607 transfer_fee_basis_points,
1608 maximum_fee,
1609 )?],
1610 signing_keypairs,
1611 )
1612 .await
1613 }
1614
1615 pub async fn set_default_account_state<S: Signers>(
1617 &self,
1618 authority: &Pubkey,
1619 state: &AccountState,
1620 signing_keypairs: &S,
1621 ) -> TokenResult<T::Output> {
1622 let signing_pubkeys = signing_keypairs.pubkeys();
1623 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1624
1625 self.process_ixs(
1626 &[
1627 default_account_state::instruction::update_default_account_state(
1628 &self.program_id,
1629 &self.pubkey,
1630 authority,
1631 &multisig_signers,
1632 state,
1633 )?,
1634 ],
1635 signing_keypairs,
1636 )
1637 .await
1638 }
1639
1640 pub async fn harvest_withheld_tokens_to_mint(
1642 &self,
1643 sources: &[&Pubkey],
1644 ) -> TokenResult<T::Output> {
1645 self.process_ixs::<[&dyn Signer; 0]>(
1646 &[transfer_fee::instruction::harvest_withheld_tokens_to_mint(
1647 &self.program_id,
1648 &self.pubkey,
1649 sources,
1650 )?],
1651 &[],
1652 )
1653 .await
1654 }
1655
1656 pub async fn withdraw_withheld_tokens_from_mint<S: Signers>(
1658 &self,
1659 destination: &Pubkey,
1660 authority: &Pubkey,
1661 signing_keypairs: &S,
1662 ) -> TokenResult<T::Output> {
1663 let signing_pubkeys = signing_keypairs.pubkeys();
1664 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1665
1666 self.process_ixs(
1667 &[
1668 transfer_fee::instruction::withdraw_withheld_tokens_from_mint(
1669 &self.program_id,
1670 &self.pubkey,
1671 destination,
1672 authority,
1673 &multisig_signers,
1674 )?,
1675 ],
1676 signing_keypairs,
1677 )
1678 .await
1679 }
1680
1681 pub async fn withdraw_withheld_tokens_from_accounts<S: Signers>(
1683 &self,
1684 destination: &Pubkey,
1685 authority: &Pubkey,
1686 sources: &[&Pubkey],
1687 signing_keypairs: &S,
1688 ) -> TokenResult<T::Output> {
1689 let signing_pubkeys = signing_keypairs.pubkeys();
1690 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1691
1692 self.process_ixs(
1693 &[
1694 transfer_fee::instruction::withdraw_withheld_tokens_from_accounts(
1695 &self.program_id,
1696 &self.pubkey,
1697 destination,
1698 authority,
1699 &multisig_signers,
1700 sources,
1701 )?,
1702 ],
1703 signing_keypairs,
1704 )
1705 .await
1706 }
1707
1708 pub async fn reallocate<S: Signers>(
1711 &self,
1712 account: &Pubkey,
1713 authority: &Pubkey,
1714 extension_types: &[ExtensionType],
1715 signing_keypairs: &S,
1716 ) -> TokenResult<T::Output> {
1717 let signing_pubkeys = signing_keypairs.pubkeys();
1718 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1719
1720 self.process_ixs(
1721 &[instruction::reallocate(
1722 &self.program_id,
1723 account,
1724 &self.payer.pubkey(),
1725 authority,
1726 &multisig_signers,
1727 extension_types,
1728 )?],
1729 signing_keypairs,
1730 )
1731 .await
1732 }
1733
1734 pub async fn enable_required_transfer_memos<S: Signers>(
1736 &self,
1737 account: &Pubkey,
1738 authority: &Pubkey,
1739 signing_keypairs: &S,
1740 ) -> TokenResult<T::Output> {
1741 let signing_pubkeys = signing_keypairs.pubkeys();
1742 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1743
1744 self.process_ixs(
1745 &[memo_transfer::instruction::enable_required_transfer_memos(
1746 &self.program_id,
1747 account,
1748 authority,
1749 &multisig_signers,
1750 )?],
1751 signing_keypairs,
1752 )
1753 .await
1754 }
1755
1756 pub async fn disable_required_transfer_memos<S: Signers>(
1758 &self,
1759 account: &Pubkey,
1760 authority: &Pubkey,
1761 signing_keypairs: &S,
1762 ) -> TokenResult<T::Output> {
1763 let signing_pubkeys = signing_keypairs.pubkeys();
1764 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1765
1766 self.process_ixs(
1767 &[memo_transfer::instruction::disable_required_transfer_memos(
1768 &self.program_id,
1769 account,
1770 authority,
1771 &multisig_signers,
1772 )?],
1773 signing_keypairs,
1774 )
1775 .await
1776 }
1777
1778 pub async fn pause<S: Signers>(
1780 &self,
1781 authority: &Pubkey,
1782 signing_keypairs: &S,
1783 ) -> TokenResult<T::Output> {
1784 let signing_pubkeys = signing_keypairs.pubkeys();
1785 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1786
1787 self.process_ixs(
1788 &[pausable::instruction::pause(
1789 &self.program_id,
1790 self.get_address(),
1791 authority,
1792 &multisig_signers,
1793 )?],
1794 signing_keypairs,
1795 )
1796 .await
1797 }
1798
1799 pub async fn resume<S: Signers>(
1801 &self,
1802 authority: &Pubkey,
1803 signing_keypairs: &S,
1804 ) -> TokenResult<T::Output> {
1805 let signing_pubkeys = signing_keypairs.pubkeys();
1806 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1807
1808 self.process_ixs(
1809 &[pausable::instruction::resume(
1810 &self.program_id,
1811 self.get_address(),
1812 authority,
1813 &multisig_signers,
1814 )?],
1815 signing_keypairs,
1816 )
1817 .await
1818 }
1819
1820 pub async fn enable_cpi_guard<S: Signers>(
1822 &self,
1823 account: &Pubkey,
1824 authority: &Pubkey,
1825 signing_keypairs: &S,
1826 ) -> TokenResult<T::Output> {
1827 let signing_pubkeys = signing_keypairs.pubkeys();
1828 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1829
1830 self.process_ixs(
1831 &[cpi_guard::instruction::enable_cpi_guard(
1832 &self.program_id,
1833 account,
1834 authority,
1835 &multisig_signers,
1836 )?],
1837 signing_keypairs,
1838 )
1839 .await
1840 }
1841
1842 pub async fn disable_cpi_guard<S: Signers>(
1844 &self,
1845 account: &Pubkey,
1846 authority: &Pubkey,
1847 signing_keypairs: &S,
1848 ) -> TokenResult<T::Output> {
1849 let signing_pubkeys = signing_keypairs.pubkeys();
1850 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1851
1852 self.process_ixs(
1853 &[cpi_guard::instruction::disable_cpi_guard(
1854 &self.program_id,
1855 account,
1856 authority,
1857 &multisig_signers,
1858 )?],
1859 signing_keypairs,
1860 )
1861 .await
1862 }
1863
1864 pub async fn update_interest_rate<S: Signers>(
1866 &self,
1867 authority: &Pubkey,
1868 new_rate: i16,
1869 signing_keypairs: &S,
1870 ) -> TokenResult<T::Output> {
1871 let signing_pubkeys = signing_keypairs.pubkeys();
1872 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1873
1874 self.process_ixs(
1875 &[interest_bearing_mint::instruction::update_rate(
1876 &self.program_id,
1877 self.get_address(),
1878 authority,
1879 &multisig_signers,
1880 new_rate,
1881 )?],
1882 signing_keypairs,
1883 )
1884 .await
1885 }
1886
1887 pub async fn update_multiplier<S: Signers>(
1889 &self,
1890 authority: &Pubkey,
1891 new_multiplier: f64,
1892 new_multiplier_effective_timestamp: i64,
1893 signing_keypairs: &S,
1894 ) -> TokenResult<T::Output> {
1895 let signing_pubkeys = signing_keypairs.pubkeys();
1896 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1897
1898 self.process_ixs(
1899 &[scaled_ui_amount::instruction::update_multiplier(
1900 &self.program_id,
1901 self.get_address(),
1902 authority,
1903 &multisig_signers,
1904 new_multiplier,
1905 new_multiplier_effective_timestamp,
1906 )?],
1907 signing_keypairs,
1908 )
1909 .await
1910 }
1911
1912 pub async fn update_transfer_hook_program_id<S: Signers>(
1914 &self,
1915 authority: &Pubkey,
1916 new_program_id: Option<Pubkey>,
1917 signing_keypairs: &S,
1918 ) -> TokenResult<T::Output> {
1919 let signing_pubkeys = signing_keypairs.pubkeys();
1920 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1921
1922 self.process_ixs(
1923 &[transfer_hook::instruction::update(
1924 &self.program_id,
1925 self.get_address(),
1926 authority,
1927 &multisig_signers,
1928 new_program_id,
1929 )?],
1930 signing_keypairs,
1931 )
1932 .await
1933 }
1934
1935 pub async fn update_metadata_address<S: Signers>(
1937 &self,
1938 authority: &Pubkey,
1939 new_metadata_address: Option<Pubkey>,
1940 signing_keypairs: &S,
1941 ) -> TokenResult<T::Output> {
1942 let signing_pubkeys = signing_keypairs.pubkeys();
1943 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1944
1945 self.process_ixs(
1946 &[metadata_pointer::instruction::update(
1947 &self.program_id,
1948 self.get_address(),
1949 authority,
1950 &multisig_signers,
1951 new_metadata_address,
1952 )?],
1953 signing_keypairs,
1954 )
1955 .await
1956 }
1957
1958 pub async fn update_group_address<S: Signers>(
1960 &self,
1961 authority: &Pubkey,
1962 new_group_address: Option<Pubkey>,
1963 signing_keypairs: &S,
1964 ) -> TokenResult<T::Output> {
1965 let signing_pubkeys = signing_keypairs.pubkeys();
1966 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1967
1968 self.process_ixs(
1969 &[group_pointer::instruction::update(
1970 &self.program_id,
1971 self.get_address(),
1972 authority,
1973 &multisig_signers,
1974 new_group_address,
1975 )?],
1976 signing_keypairs,
1977 )
1978 .await
1979 }
1980
1981 pub async fn update_group_member_address<S: Signers>(
1983 &self,
1984 authority: &Pubkey,
1985 new_member_address: Option<Pubkey>,
1986 signing_keypairs: &S,
1987 ) -> TokenResult<T::Output> {
1988 let signing_pubkeys = signing_keypairs.pubkeys();
1989 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1990
1991 self.process_ixs(
1992 &[group_member_pointer::instruction::update(
1993 &self.program_id,
1994 self.get_address(),
1995 authority,
1996 &multisig_signers,
1997 new_member_address,
1998 )?],
1999 signing_keypairs,
2000 )
2001 .await
2002 }
2003
2004 pub async fn confidential_transfer_update_mint<S: Signers>(
2006 &self,
2007 authority: &Pubkey,
2008 auto_approve_new_account: bool,
2009 auditor_elgamal_pubkey: Option<PodElGamalPubkey>,
2010 signing_keypairs: &S,
2011 ) -> TokenResult<T::Output> {
2012 let signing_pubkeys = signing_keypairs.pubkeys();
2013 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2014
2015 self.process_ixs(
2016 &[confidential_transfer::instruction::update_mint(
2017 &self.program_id,
2018 &self.pubkey,
2019 authority,
2020 &multisig_signers,
2021 auto_approve_new_account,
2022 auditor_elgamal_pubkey,
2023 )?],
2024 signing_keypairs,
2025 )
2026 .await
2027 }
2028
2029 #[allow(clippy::too_many_arguments)]
2033 pub async fn confidential_transfer_configure_token_account<S: Signers>(
2034 &self,
2035 account: &Pubkey,
2036 authority: &Pubkey,
2037 context_state_account: Option<&Pubkey>,
2038 maximum_pending_balance_credit_counter: Option<u64>,
2039 elgamal_keypair: &ElGamalKeypair,
2040 aes_key: &AeKey,
2041 signing_keypairs: &S,
2042 ) -> TokenResult<T::Output> {
2043 const DEFAULT_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER: u64 = 65536;
2044
2045 let signing_pubkeys = signing_keypairs.pubkeys();
2046 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2047
2048 let maximum_pending_balance_credit_counter = maximum_pending_balance_credit_counter
2049 .unwrap_or(DEFAULT_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER);
2050
2051 let proof_data = if context_state_account.is_some() {
2052 None
2053 } else {
2054 Some(
2055 confidential_transfer::instruction::PubkeyValidityProofData::new(elgamal_keypair)
2056 .map_err(|_| TokenError::ProofGeneration)?,
2057 )
2058 };
2059
2060 let proof_location = Self::confidential_transfer_create_proof_location(
2063 proof_data.as_ref(),
2064 context_state_account,
2065 1,
2066 )
2067 .unwrap();
2068
2069 let decryptable_balance = aes_key.encrypt(0).into();
2070
2071 self.process_ixs(
2072 &confidential_transfer::instruction::configure_account(
2073 &self.program_id,
2074 account,
2075 &self.pubkey,
2076 &decryptable_balance,
2077 maximum_pending_balance_credit_counter,
2078 authority,
2079 &multisig_signers,
2080 proof_location,
2081 )?,
2082 signing_keypairs,
2083 )
2084 .await
2085 }
2086
2087 pub async fn confidential_transfer_configure_token_account_with_registry(
2090 &self,
2091 account: &Pubkey,
2092 elgamal_registry_account: &Pubkey,
2093 payer: Option<&Pubkey>,
2094 ) -> TokenResult<T::Output> {
2095 self.process_ixs::<[&dyn Signer; 0]>(
2096 &[
2097 confidential_transfer::instruction::configure_account_with_registry(
2098 &self.program_id,
2099 account,
2100 &self.pubkey,
2101 elgamal_registry_account,
2102 payer,
2103 )?,
2104 ],
2105 &[],
2106 )
2107 .await
2108 }
2109
2110 pub async fn confidential_transfer_approve_account<S: Signers>(
2112 &self,
2113 account: &Pubkey,
2114 authority: &Pubkey,
2115 signing_keypairs: &S,
2116 ) -> TokenResult<T::Output> {
2117 let signing_pubkeys = signing_keypairs.pubkeys();
2118 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2119
2120 self.process_ixs(
2121 &[confidential_transfer::instruction::approve_account(
2122 &self.program_id,
2123 account,
2124 &self.pubkey,
2125 authority,
2126 &multisig_signers,
2127 )?],
2128 signing_keypairs,
2129 )
2130 .await
2131 }
2132
2133 pub async fn confidential_transfer_empty_account<S: Signers>(
2136 &self,
2137 account: &Pubkey,
2138 authority: &Pubkey,
2139 context_state_account: Option<&Pubkey>,
2140 account_info: Option<EmptyAccountAccountInfo>,
2141 elgamal_keypair: &ElGamalKeypair,
2142 signing_keypairs: &S,
2143 ) -> TokenResult<T::Output> {
2144 let signing_pubkeys = signing_keypairs.pubkeys();
2145 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2146
2147 let account_info = if let Some(account_info) = account_info {
2148 account_info
2149 } else {
2150 let account = self.get_account_info(account).await?;
2151 let confidential_transfer_account =
2152 account.get_extension::<ConfidentialTransferAccount>()?;
2153 EmptyAccountAccountInfo::new(confidential_transfer_account)
2154 };
2155
2156 let proof_data = if context_state_account.is_some() {
2157 None
2158 } else {
2159 Some(
2160 account_info
2161 .generate_proof_data(elgamal_keypair)
2162 .map_err(|_| TokenError::ProofGeneration)?,
2163 )
2164 };
2165
2166 let proof_location = Self::confidential_transfer_create_proof_location(
2169 proof_data.as_ref(),
2170 context_state_account,
2171 1,
2172 )
2173 .unwrap();
2174
2175 self.process_ixs(
2176 &confidential_transfer::instruction::empty_account(
2177 &self.program_id,
2178 account,
2179 authority,
2180 &multisig_signers,
2181 proof_location,
2182 )?,
2183 signing_keypairs,
2184 )
2185 .await
2186 }
2187
2188 pub async fn confidential_transfer_deposit<S: Signers>(
2191 &self,
2192 account: &Pubkey,
2193 authority: &Pubkey,
2194 amount: u64,
2195 decimals: u8,
2196 signing_keypairs: &S,
2197 ) -> TokenResult<T::Output> {
2198 let signing_pubkeys = signing_keypairs.pubkeys();
2199 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2200
2201 self.process_ixs(
2202 &[confidential_transfer::instruction::deposit(
2203 &self.program_id,
2204 account,
2205 &self.pubkey,
2206 amount,
2207 decimals,
2208 authority,
2209 &multisig_signers,
2210 )?],
2211 signing_keypairs,
2212 )
2213 .await
2214 }
2215
2216 #[allow(clippy::too_many_arguments)]
2219 pub async fn confidential_transfer_withdraw<S: Signers>(
2220 &self,
2221 account: &Pubkey,
2222 authority: &Pubkey,
2223 equality_proof_account: Option<&Pubkey>,
2224 range_proof_account: Option<&Pubkey>,
2225 withdraw_amount: u64,
2226 decimals: u8,
2227 account_info: Option<WithdrawAccountInfo>,
2228 elgamal_keypair: &ElGamalKeypair,
2229 aes_key: &AeKey,
2230 signing_keypairs: &S,
2231 ) -> TokenResult<T::Output> {
2232 let signing_pubkeys = signing_keypairs.pubkeys();
2233 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2234
2235 let account_info = if let Some(account_info) = account_info {
2236 account_info
2237 } else {
2238 let account = self.get_account_info(account).await?;
2239 let confidential_transfer_account =
2240 account.get_extension::<ConfidentialTransferAccount>()?;
2241 WithdrawAccountInfo::new(confidential_transfer_account)
2242 };
2243
2244 let (equality_proof_data, range_proof_data) =
2245 if equality_proof_account.is_some() && range_proof_account.is_some() {
2246 (None, None)
2247 } else {
2248 let WithdrawProofData {
2249 equality_proof_data,
2250 range_proof_data,
2251 } = account_info
2252 .generate_proof_data(withdraw_amount, elgamal_keypair, aes_key)
2253 .map_err(|_| TokenError::ProofGeneration)?;
2254
2255 let equality_proof_data = equality_proof_account
2258 .is_none()
2259 .then_some(equality_proof_data);
2260 let range_proof_data = range_proof_account.is_none().then_some(range_proof_data);
2261
2262 (equality_proof_data, range_proof_data)
2263 };
2264
2265 let equality_proof_location = Self::confidential_transfer_create_proof_location(
2268 equality_proof_data.as_ref(),
2269 equality_proof_account,
2270 1,
2271 )
2272 .unwrap();
2273
2274 let range_proof_location = Self::confidential_transfer_create_proof_location(
2275 range_proof_data.as_ref(),
2276 range_proof_account,
2277 2,
2278 )
2279 .unwrap();
2280
2281 let new_decryptable_available_balance = account_info
2282 .new_decryptable_available_balance(withdraw_amount, aes_key)
2283 .map_err(|_| TokenError::AccountDecryption)?
2284 .into();
2285
2286 self.process_ixs(
2287 &confidential_transfer::instruction::withdraw(
2288 &self.program_id,
2289 account,
2290 &self.pubkey,
2291 withdraw_amount,
2292 decimals,
2293 &new_decryptable_available_balance,
2294 authority,
2295 &multisig_signers,
2296 equality_proof_location,
2297 range_proof_location,
2298 )?,
2299 signing_keypairs,
2300 )
2301 .await
2302 }
2303
2304 #[allow(clippy::too_many_arguments)]
2306 pub async fn confidential_transfer_transfer<S: Signers>(
2307 &self,
2308 source_account: &Pubkey,
2309 destination_account: &Pubkey,
2310 source_authority: &Pubkey,
2311 equality_proof_account: Option<&Pubkey>,
2312 ciphertext_validity_proof_account_with_ciphertext: Option<&ProofAccountWithCiphertext>,
2313 range_proof_account: Option<&Pubkey>,
2314 transfer_amount: u64,
2315 account_info: Option<TransferAccountInfo>,
2316 source_elgamal_keypair: &ElGamalKeypair,
2317 source_aes_key: &AeKey,
2318 destination_elgamal_pubkey: &ElGamalPubkey,
2319 auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
2320 signing_keypairs: &S,
2321 ) -> TokenResult<T::Output> {
2322 let signing_pubkeys = signing_keypairs.pubkeys();
2323 let multisig_signers = self.get_multisig_signers(source_authority, &signing_pubkeys);
2324
2325 let account_info = if let Some(account_info) = account_info {
2326 account_info
2327 } else {
2328 let account = self.get_account_info(source_account).await?;
2329 let confidential_transfer_account =
2330 account.get_extension::<ConfidentialTransferAccount>()?;
2331 TransferAccountInfo::new(confidential_transfer_account)
2332 };
2333
2334 let (equality_proof_data, ciphertext_validity_proof_data_with_ciphertext, range_proof_data) =
2335 if equality_proof_account.is_some()
2336 && ciphertext_validity_proof_account_with_ciphertext.is_some()
2337 && range_proof_account.is_some()
2338 {
2339 (None, None, None)
2340 } else {
2341 let TransferProofData {
2342 equality_proof_data,
2343 ciphertext_validity_proof_data_with_ciphertext,
2344 range_proof_data,
2345 } = account_info
2346 .generate_split_transfer_proof_data(
2347 transfer_amount,
2348 source_elgamal_keypair,
2349 source_aes_key,
2350 destination_elgamal_pubkey,
2351 auditor_elgamal_pubkey,
2352 )
2353 .map_err(|_| TokenError::ProofGeneration)?;
2354
2355 let equality_proof_data = equality_proof_account
2358 .is_none()
2359 .then_some(equality_proof_data);
2360 let ciphertext_validity_proof_data_with_ciphertext =
2361 ciphertext_validity_proof_account_with_ciphertext
2362 .is_none()
2363 .then_some(ciphertext_validity_proof_data_with_ciphertext);
2364 let range_proof_data = range_proof_account.is_none().then_some(range_proof_data);
2365
2366 (
2367 equality_proof_data,
2368 ciphertext_validity_proof_data_with_ciphertext,
2369 range_proof_data,
2370 )
2371 };
2372
2373 let (transfer_amount_auditor_ciphertext_lo, transfer_amount_auditor_ciphertext_hi) =
2374 if let Some(proof_data_with_ciphertext) = ciphertext_validity_proof_data_with_ciphertext
2375 {
2376 (
2377 proof_data_with_ciphertext.ciphertext_lo,
2378 proof_data_with_ciphertext.ciphertext_hi,
2379 )
2380 } else {
2381 (
2385 ciphertext_validity_proof_account_with_ciphertext
2386 .unwrap()
2387 .ciphertext_lo,
2388 ciphertext_validity_proof_account_with_ciphertext
2389 .unwrap()
2390 .ciphertext_hi,
2391 )
2392 };
2393
2394 let equality_proof_location = Self::confidential_transfer_create_proof_location(
2397 equality_proof_data.as_ref(),
2398 equality_proof_account,
2399 1,
2400 )
2401 .unwrap();
2402 let ciphertext_validity_proof_data =
2403 ciphertext_validity_proof_data_with_ciphertext.map(|data| data.proof_data);
2404 let ciphertext_validity_proof_location = Self::confidential_transfer_create_proof_location(
2405 ciphertext_validity_proof_data.as_ref(),
2406 ciphertext_validity_proof_account_with_ciphertext
2407 .map(|account| &account.context_state_account),
2408 2,
2409 )
2410 .unwrap();
2411 let range_proof_location = Self::confidential_transfer_create_proof_location(
2412 range_proof_data.as_ref(),
2413 range_proof_account,
2414 3,
2415 )
2416 .unwrap();
2417
2418 let new_decryptable_available_balance = account_info
2419 .new_decryptable_available_balance(transfer_amount, source_aes_key)
2420 .map_err(|_| TokenError::AccountDecryption)?
2421 .into();
2422
2423 let mut instructions = confidential_transfer::instruction::transfer(
2424 &self.program_id,
2425 source_account,
2426 self.get_address(),
2427 destination_account,
2428 &new_decryptable_available_balance,
2429 &transfer_amount_auditor_ciphertext_lo,
2430 &transfer_amount_auditor_ciphertext_hi,
2431 source_authority,
2432 &multisig_signers,
2433 equality_proof_location,
2434 ciphertext_validity_proof_location,
2435 range_proof_location,
2436 )?;
2437 offchain::add_extra_account_metas(
2438 &mut instructions[0],
2439 source_account,
2440 self.get_address(),
2441 destination_account,
2442 source_authority,
2443 u64::MAX,
2444 |address| {
2445 self.client
2446 .get_account(address)
2447 .map_ok(|opt| opt.map(|acc| acc.data))
2448 },
2449 )
2450 .await
2451 .map_err(|_| TokenError::AccountNotFound)?;
2452 self.process_ixs(&instructions, signing_keypairs).await
2453 }
2454
2455 pub async fn confidential_transfer_create_record_account<
2458 S1: Signer,
2459 S2: Signer,
2460 ZK: Pod + ZkProofData<U>,
2461 U: Pod,
2462 >(
2463 &self,
2464 record_account: &Pubkey,
2465 record_authority: &Pubkey,
2466 proof_data: &ZK,
2467 record_account_signer: &S1,
2468 record_authority_signer: &S2,
2469 ) -> TokenResult<Vec<T::Output>> {
2470 let proof_data = bytes_of(proof_data);
2471 let space = proof_data
2472 .len()
2473 .saturating_add(RecordData::WRITABLE_START_INDEX);
2474 let rent = self
2475 .client
2476 .get_minimum_balance_for_rent_exemption(space)
2477 .await
2478 .map_err(TokenError::Client)?;
2479
2480 let create_record_instructions = |first_instruction: bool, bytes: &[u8], offset: u64| {
2484 let mut ixs = vec![];
2485 if first_instruction {
2486 ixs.push(system_instruction::create_account(
2487 &self.payer.pubkey(),
2488 record_account,
2489 rent,
2490 space as u64,
2491 &spl_record::id(),
2492 ));
2493 ixs.push(spl_record::instruction::initialize(
2494 record_account,
2495 record_authority,
2496 ));
2497 }
2498 ixs.push(spl_record::instruction::write(
2499 record_account,
2500 record_authority,
2501 offset,
2502 bytes,
2503 ));
2504 ixs
2505 };
2506 let first_chunk_size = calculate_record_max_chunk_size(create_record_instructions, true);
2507 let (first_chunk, rest) = if space <= first_chunk_size {
2508 (proof_data, &[] as &[u8])
2509 } else {
2510 proof_data.split_at(first_chunk_size)
2511 };
2512
2513 let first_ixs = create_record_instructions(true, first_chunk, 0);
2514 let first_ixs_signers: [&dyn Signer; 2] = [record_account_signer, record_authority_signer];
2515 self.process_ixs(&first_ixs, &first_ixs_signers).await?;
2516
2517 let subsequent_chunk_size =
2518 calculate_record_max_chunk_size(create_record_instructions, false);
2519 let mut record_offset = first_chunk_size;
2520 let mut ixs_batch = vec![];
2521 for chunk in rest.chunks(subsequent_chunk_size) {
2522 ixs_batch.push(create_record_instructions(
2523 false,
2524 chunk,
2525 record_offset as u64,
2526 ));
2527 record_offset = record_offset.saturating_add(chunk.len());
2528 }
2529
2530 let futures = ixs_batch
2531 .into_iter()
2532 .map(|ixs| async move { self.process_ixs(&ixs, &[record_authority_signer]).await })
2533 .collect::<Vec<_>>();
2534
2535 join_all(futures).await.into_iter().collect()
2536 }
2537
2538 pub async fn confidential_transfer_close_record_account<S: Signers>(
2540 &self,
2541 record_account: &Pubkey,
2542 lamport_destination_account: &Pubkey,
2543 record_account_authority: &Pubkey,
2544 signing_keypairs: &S,
2545 ) -> TokenResult<T::Output> {
2546 self.process_ixs(
2547 &[spl_record::instruction::close_account(
2548 record_account,
2549 record_account_authority,
2550 lamport_destination_account,
2551 )],
2552 signing_keypairs,
2553 )
2554 .await
2555 }
2556
2557 pub async fn confidential_transfer_create_context_state_account<
2560 S: Signers,
2561 ZK: Pod + ZkProofData<U>,
2562 U: Pod,
2563 >(
2564 &self,
2565 context_state_account: &Pubkey,
2566 context_state_authority: &Pubkey,
2567 proof_data: &ZK,
2568 split_account_creation_and_proof_verification: bool,
2569 signing_keypairs: &S,
2570 ) -> TokenResult<T::Output> {
2571 let instruction_type = zk_proof_type_to_instruction(ZK::PROOF_TYPE)?;
2572 let space = size_of::<ProofContextState<U>>();
2573 let rent = self
2574 .client
2575 .get_minimum_balance_for_rent_exemption(space)
2576 .await
2577 .map_err(TokenError::Client)?;
2578
2579 let context_state_info = ContextStateInfo {
2580 context_state_account,
2581 context_state_authority,
2582 };
2583
2584 if split_account_creation_and_proof_verification {
2587 self.process_ixs(
2588 &[system_instruction::create_account(
2589 &self.payer.pubkey(),
2590 context_state_account,
2591 rent,
2592 space as u64,
2593 &zk_elgamal_proof_program::id(),
2594 )],
2595 signing_keypairs,
2596 )
2597 .await?;
2598
2599 let blockhash = self
2600 .client
2601 .get_latest_blockhash()
2602 .await
2603 .map_err(TokenError::Client)?;
2604
2605 let transaction = Transaction::new_signed_with_payer(
2606 &[instruction_type.encode_verify_proof(Some(context_state_info), proof_data)],
2607 Some(&self.payer.pubkey()),
2608 &[self.payer.as_ref()],
2609 blockhash,
2610 );
2611
2612 self.client
2613 .send_transaction(&transaction)
2614 .await
2615 .map_err(TokenError::Client)
2616 } else {
2617 self.process_ixs(
2618 &[
2619 system_instruction::create_account(
2620 &self.payer.pubkey(),
2621 context_state_account,
2622 rent,
2623 space as u64,
2624 &zk_elgamal_proof_program::id(),
2625 ),
2626 instruction_type.encode_verify_proof(Some(context_state_info), proof_data),
2627 ],
2628 signing_keypairs,
2629 )
2630 .await
2631 }
2632 }
2633
2634 pub async fn confidential_transfer_create_context_state_account_from_record<
2637 S: Signers,
2638 ZK: Pod + ZkProofData<U>,
2639 U: Pod,
2640 >(
2641 &self,
2642 context_state_account: &Pubkey,
2643 context_state_authority: &Pubkey,
2644 record_account: &Pubkey,
2645 signing_keypairs: &S,
2646 ) -> TokenResult<T::Output> {
2647 const RECORD_ACCOUNT_PROOF_OFFSET: u32 = 33;
2648
2649 let instruction_type = zk_proof_type_to_instruction(ZK::PROOF_TYPE)?;
2650 let space = size_of::<ProofContextState<U>>();
2651 let rent = self
2652 .client
2653 .get_minimum_balance_for_rent_exemption(space)
2654 .await
2655 .map_err(TokenError::Client)?;
2656
2657 let context_state_info = ContextStateInfo {
2658 context_state_account,
2659 context_state_authority,
2660 };
2661
2662 self.process_ixs(
2665 &[
2666 system_instruction::create_account(
2667 &self.payer.pubkey(),
2668 context_state_account,
2669 rent,
2670 space as u64,
2671 &zk_elgamal_proof_program::id(),
2672 ),
2673 instruction_type.encode_verify_proof_from_account(
2674 Some(context_state_info),
2675 record_account,
2676 RECORD_ACCOUNT_PROOF_OFFSET,
2677 ),
2678 ],
2679 signing_keypairs,
2680 )
2681 .await
2682 }
2683
2684 pub async fn confidential_transfer_close_context_state_account<S: Signers>(
2686 &self,
2687 context_state_account: &Pubkey,
2688 lamport_destination_account: &Pubkey,
2689 context_state_authority: &Pubkey,
2690 signing_keypairs: &S,
2691 ) -> TokenResult<T::Output> {
2692 let context_state_info = ContextStateInfo {
2693 context_state_account,
2694 context_state_authority,
2695 };
2696
2697 self.process_ixs(
2698 &[close_context_state(
2699 context_state_info,
2700 lamport_destination_account,
2701 )],
2702 signing_keypairs,
2703 )
2704 .await
2705 }
2706
2707 #[allow(clippy::too_many_arguments)]
2709 pub async fn confidential_transfer_transfer_with_fee<S: Signers>(
2710 &self,
2711 source_account: &Pubkey,
2712 destination_account: &Pubkey,
2713 source_authority: &Pubkey,
2714 equality_proof_account: Option<&Pubkey>,
2715 transfer_amount_ciphertext_validity_proof_account_with_ciphertext: Option<
2716 &ProofAccountWithCiphertext,
2717 >,
2718 percentage_with_cap_proof_account: Option<&Pubkey>,
2719 fee_ciphertext_validity_proof_account: Option<&Pubkey>,
2720 range_proof_account: Option<&Pubkey>,
2721 transfer_amount: u64,
2722 account_info: Option<TransferAccountInfo>,
2723 source_elgamal_keypair: &ElGamalKeypair,
2724 source_aes_key: &AeKey,
2725 destination_elgamal_pubkey: &ElGamalPubkey,
2726 auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
2727 withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey,
2728 fee_rate_basis_points: u16,
2729 maximum_fee: u64,
2730 signing_keypairs: &S,
2731 ) -> TokenResult<T::Output> {
2732 let signing_pubkeys = signing_keypairs.pubkeys();
2733 let multisig_signers = self.get_multisig_signers(source_authority, &signing_pubkeys);
2734
2735 let account_info = if let Some(account_info) = account_info {
2736 account_info
2737 } else {
2738 let account = self.get_account_info(source_account).await?;
2739 let confidential_transfer_account =
2740 account.get_extension::<ConfidentialTransferAccount>()?;
2741 TransferAccountInfo::new(confidential_transfer_account)
2742 };
2743
2744 let (
2745 equality_proof_data,
2746 transfer_amount_ciphertext_validity_proof_data_with_ciphertext,
2747 percentage_with_cap_proof_data,
2748 fee_ciphertext_validity_proof_data,
2749 range_proof_data,
2750 ) = if equality_proof_account.is_some()
2751 && transfer_amount_ciphertext_validity_proof_account_with_ciphertext.is_some()
2752 && percentage_with_cap_proof_account.is_some()
2753 && fee_ciphertext_validity_proof_account.is_some()
2754 && range_proof_account.is_some()
2755 {
2756 (None, None, None, None, None)
2758 } else {
2759 let TransferWithFeeProofData {
2760 equality_proof_data,
2761 transfer_amount_ciphertext_validity_proof_data_with_ciphertext,
2762 percentage_with_cap_proof_data,
2763 fee_ciphertext_validity_proof_data,
2764 range_proof_data,
2765 } = account_info
2766 .generate_split_transfer_with_fee_proof_data(
2767 transfer_amount,
2768 source_elgamal_keypair,
2769 source_aes_key,
2770 destination_elgamal_pubkey,
2771 auditor_elgamal_pubkey,
2772 withdraw_withheld_authority_elgamal_pubkey,
2773 fee_rate_basis_points,
2774 maximum_fee,
2775 )
2776 .map_err(|_| TokenError::ProofGeneration)?;
2777
2778 let equality_proof_data = equality_proof_account
2779 .is_none()
2780 .then_some(equality_proof_data);
2781 let transfer_amount_ciphertext_validity_proof_data_with_ciphertext =
2782 transfer_amount_ciphertext_validity_proof_account_with_ciphertext
2783 .is_none()
2784 .then_some(transfer_amount_ciphertext_validity_proof_data_with_ciphertext);
2785 let percentage_with_cap_proof_data = percentage_with_cap_proof_account
2786 .is_none()
2787 .then_some(percentage_with_cap_proof_data);
2788 let fee_ciphertext_validity_proof_data = fee_ciphertext_validity_proof_account
2789 .is_none()
2790 .then_some(fee_ciphertext_validity_proof_data);
2791 let range_proof_data = range_proof_account.is_none().then_some(range_proof_data);
2792
2793 (
2794 equality_proof_data,
2795 transfer_amount_ciphertext_validity_proof_data_with_ciphertext,
2796 percentage_with_cap_proof_data,
2797 fee_ciphertext_validity_proof_data,
2798 range_proof_data,
2799 )
2800 };
2801
2802 let (transfer_amount_auditor_ciphertext_lo, transfer_amount_auditor_ciphertext_hi) =
2803 if let Some(proof_data_with_ciphertext) =
2804 transfer_amount_ciphertext_validity_proof_data_with_ciphertext
2805 {
2806 (
2807 proof_data_with_ciphertext.ciphertext_lo,
2808 proof_data_with_ciphertext.ciphertext_hi,
2809 )
2810 } else {
2811 (
2815 transfer_amount_ciphertext_validity_proof_account_with_ciphertext
2816 .unwrap()
2817 .ciphertext_lo,
2818 transfer_amount_ciphertext_validity_proof_account_with_ciphertext
2819 .unwrap()
2820 .ciphertext_hi,
2821 )
2822 };
2823
2824 let equality_proof_location = Self::confidential_transfer_create_proof_location(
2827 equality_proof_data.as_ref(),
2828 equality_proof_account,
2829 1,
2830 )
2831 .unwrap();
2832 let transfer_amount_ciphertext_validity_proof_data =
2833 transfer_amount_ciphertext_validity_proof_data_with_ciphertext
2834 .map(|data| data.proof_data);
2835 let transfer_amount_ciphertext_validity_proof_location =
2836 Self::confidential_transfer_create_proof_location(
2837 transfer_amount_ciphertext_validity_proof_data.as_ref(),
2838 transfer_amount_ciphertext_validity_proof_account_with_ciphertext
2839 .map(|account| &account.context_state_account),
2840 2,
2841 )
2842 .unwrap();
2843 let fee_sigma_proof_location = Self::confidential_transfer_create_proof_location(
2844 percentage_with_cap_proof_data.as_ref(),
2845 percentage_with_cap_proof_account,
2846 3,
2847 )
2848 .unwrap();
2849 let fee_ciphertext_validity_proof_location =
2850 Self::confidential_transfer_create_proof_location(
2851 fee_ciphertext_validity_proof_data.as_ref(),
2852 fee_ciphertext_validity_proof_account,
2853 4,
2854 )
2855 .unwrap();
2856 let range_proof_location = Self::confidential_transfer_create_proof_location(
2857 range_proof_data.as_ref(),
2858 range_proof_account,
2859 5,
2860 )
2861 .unwrap();
2862
2863 let new_decryptable_available_balance = account_info
2864 .new_decryptable_available_balance(transfer_amount, source_aes_key)
2865 .map_err(|_| TokenError::AccountDecryption)?
2866 .into();
2867
2868 let mut instructions = confidential_transfer::instruction::transfer_with_fee(
2869 &self.program_id,
2870 source_account,
2871 self.get_address(),
2872 destination_account,
2873 &new_decryptable_available_balance,
2874 &transfer_amount_auditor_ciphertext_lo,
2875 &transfer_amount_auditor_ciphertext_hi,
2876 source_authority,
2877 &multisig_signers,
2878 equality_proof_location,
2879 transfer_amount_ciphertext_validity_proof_location,
2880 fee_sigma_proof_location,
2881 fee_ciphertext_validity_proof_location,
2882 range_proof_location,
2883 )?;
2884 offchain::add_extra_account_metas(
2885 &mut instructions[0],
2886 source_account,
2887 self.get_address(),
2888 destination_account,
2889 source_authority,
2890 u64::MAX,
2891 |address| {
2892 self.client
2893 .get_account(address)
2894 .map_ok(|opt| opt.map(|acc| acc.data))
2895 },
2896 )
2897 .await
2898 .map_err(|_| TokenError::AccountNotFound)?;
2899 self.process_ixs(&instructions, signing_keypairs).await
2900 }
2901
2902 pub async fn confidential_transfer_apply_pending_balance<S: Signers>(
2905 &self,
2906 account: &Pubkey,
2907 authority: &Pubkey,
2908 account_info: Option<ApplyPendingBalanceAccountInfo>,
2909 elgamal_secret_key: &ElGamalSecretKey,
2910 aes_key: &AeKey,
2911 signing_keypairs: &S,
2912 ) -> TokenResult<T::Output> {
2913 let signing_pubkeys = signing_keypairs.pubkeys();
2914 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2915
2916 let account_info = if let Some(account_info) = account_info {
2917 account_info
2918 } else {
2919 let account = self.get_account_info(account).await?;
2920 let confidential_transfer_account =
2921 account.get_extension::<ConfidentialTransferAccount>()?;
2922 ApplyPendingBalanceAccountInfo::new(confidential_transfer_account)
2923 };
2924
2925 let expected_pending_balance_credit_counter = account_info.pending_balance_credit_counter();
2926 let new_decryptable_available_balance = account_info
2927 .new_decryptable_available_balance(elgamal_secret_key, aes_key)
2928 .map_err(|_| TokenError::AccountDecryption)?
2929 .into();
2930
2931 self.process_ixs(
2932 &[confidential_transfer::instruction::apply_pending_balance(
2933 &self.program_id,
2934 account,
2935 expected_pending_balance_credit_counter,
2936 &new_decryptable_available_balance,
2937 authority,
2938 &multisig_signers,
2939 )?],
2940 signing_keypairs,
2941 )
2942 .await
2943 }
2944
2945 pub async fn confidential_transfer_enable_confidential_credits<S: Signers>(
2948 &self,
2949 account: &Pubkey,
2950 authority: &Pubkey,
2951 signing_keypairs: &S,
2952 ) -> TokenResult<T::Output> {
2953 let signing_pubkeys = signing_keypairs.pubkeys();
2954 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2955
2956 self.process_ixs(
2957 &[
2958 confidential_transfer::instruction::enable_confidential_credits(
2959 &self.program_id,
2960 account,
2961 authority,
2962 &multisig_signers,
2963 )?,
2964 ],
2965 signing_keypairs,
2966 )
2967 .await
2968 }
2969
2970 pub async fn confidential_transfer_disable_confidential_credits<S: Signers>(
2973 &self,
2974 account: &Pubkey,
2975 authority: &Pubkey,
2976 signing_keypairs: &S,
2977 ) -> TokenResult<T::Output> {
2978 let signing_pubkeys = signing_keypairs.pubkeys();
2979 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2980
2981 self.process_ixs(
2982 &[
2983 confidential_transfer::instruction::disable_confidential_credits(
2984 &self.program_id,
2985 account,
2986 authority,
2987 &multisig_signers,
2988 )?,
2989 ],
2990 signing_keypairs,
2991 )
2992 .await
2993 }
2994
2995 pub async fn confidential_transfer_enable_non_confidential_credits<S: Signers>(
2998 &self,
2999 account: &Pubkey,
3000 authority: &Pubkey,
3001 signing_keypairs: &S,
3002 ) -> TokenResult<T::Output> {
3003 let signing_pubkeys = signing_keypairs.pubkeys();
3004 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3005
3006 self.process_ixs(
3007 &[
3008 confidential_transfer::instruction::enable_non_confidential_credits(
3009 &self.program_id,
3010 account,
3011 authority,
3012 &multisig_signers,
3013 )?,
3014 ],
3015 signing_keypairs,
3016 )
3017 .await
3018 }
3019
3020 pub async fn confidential_transfer_disable_non_confidential_credits<S: Signers>(
3023 &self,
3024 account: &Pubkey,
3025 authority: &Pubkey,
3026 signing_keypairs: &S,
3027 ) -> TokenResult<T::Output> {
3028 let signing_pubkeys = signing_keypairs.pubkeys();
3029 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3030
3031 self.process_ixs(
3032 &[
3033 confidential_transfer::instruction::disable_non_confidential_credits(
3034 &self.program_id,
3035 account,
3036 authority,
3037 &multisig_signers,
3038 )?,
3039 ],
3040 signing_keypairs,
3041 )
3042 .await
3043 }
3044
3045 #[allow(clippy::too_many_arguments)]
3047 pub async fn confidential_transfer_withdraw_withheld_tokens_from_mint<S: Signers>(
3048 &self,
3049 destination_account: &Pubkey,
3050 withdraw_withheld_authority: &Pubkey,
3051 context_state_account: Option<&Pubkey>,
3052 withheld_tokens_info: Option<WithheldTokensInfo>,
3053 withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair,
3054 destination_elgamal_pubkey: &ElGamalPubkey,
3055 new_decryptable_available_balance: &DecryptableBalance,
3056 signing_keypairs: &S,
3057 ) -> TokenResult<T::Output> {
3058 let signing_pubkeys = signing_keypairs.pubkeys();
3059 let multisig_signers =
3060 self.get_multisig_signers(withdraw_withheld_authority, &signing_pubkeys);
3061
3062 let account_info = if let Some(account_info) = withheld_tokens_info {
3063 account_info
3064 } else {
3065 let mint_info = self.get_mint_info().await?;
3066 let confidential_transfer_fee_config =
3067 mint_info.get_extension::<ConfidentialTransferFeeConfig>()?;
3068 WithheldTokensInfo::new(&confidential_transfer_fee_config.withheld_amount)
3069 };
3070
3071 let proof_data = if context_state_account.is_some() {
3072 None
3073 } else {
3074 Some(
3075 account_info
3076 .generate_proof_data(
3077 withdraw_withheld_authority_elgamal_keypair,
3078 destination_elgamal_pubkey,
3079 )
3080 .map_err(|_| TokenError::ProofGeneration)?,
3081 )
3082 };
3083
3084 let proof_location = Self::confidential_transfer_create_proof_location(
3087 proof_data.as_ref(),
3088 context_state_account,
3089 1,
3090 )
3091 .unwrap();
3092
3093 self.process_ixs(
3094 &confidential_transfer_fee::instruction::withdraw_withheld_tokens_from_mint(
3095 &self.program_id,
3096 &self.pubkey,
3097 destination_account,
3098 new_decryptable_available_balance,
3099 withdraw_withheld_authority,
3100 &multisig_signers,
3101 proof_location,
3102 )?,
3103 signing_keypairs,
3104 )
3105 .await
3106 }
3107
3108 #[allow(clippy::too_many_arguments)]
3110 pub async fn confidential_transfer_withdraw_withheld_tokens_from_accounts<S: Signers>(
3111 &self,
3112 destination_account: &Pubkey,
3113 withdraw_withheld_authority: &Pubkey,
3114 context_state_account: Option<&Pubkey>,
3115 withheld_tokens_info: Option<WithheldTokensInfo>,
3116 withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair,
3117 destination_elgamal_pubkey: &ElGamalPubkey,
3118 new_decryptable_available_balance: &DecryptableBalance,
3119 sources: &[&Pubkey],
3120 signing_keypairs: &S,
3121 ) -> TokenResult<T::Output> {
3122 let signing_pubkeys = signing_keypairs.pubkeys();
3123 let multisig_signers =
3124 self.get_multisig_signers(withdraw_withheld_authority, &signing_pubkeys);
3125
3126 let account_info = if let Some(account_info) = withheld_tokens_info {
3127 account_info
3128 } else {
3129 let futures = sources.iter().map(|source| self.get_account_info(source));
3130 let sources_extensions = join_all(futures).await;
3131
3132 let mut aggregate_withheld_amount = ElGamalCiphertext::default();
3133 for source_extension in sources_extensions {
3134 let withheld_amount: ElGamalCiphertext = source_extension?
3135 .get_extension::<ConfidentialTransferFeeAmount>()?
3136 .withheld_amount
3137 .try_into()
3138 .map_err(|_| TokenError::AccountDecryption)?;
3139 aggregate_withheld_amount = aggregate_withheld_amount + withheld_amount;
3140 }
3141
3142 WithheldTokensInfo::new(&aggregate_withheld_amount.into())
3143 };
3144
3145 let proof_data = if context_state_account.is_some() {
3146 None
3147 } else {
3148 Some(
3149 account_info
3150 .generate_proof_data(
3151 withdraw_withheld_authority_elgamal_keypair,
3152 destination_elgamal_pubkey,
3153 )
3154 .map_err(|_| TokenError::ProofGeneration)?,
3155 )
3156 };
3157
3158 let proof_location = Self::confidential_transfer_create_proof_location(
3161 proof_data.as_ref(),
3162 context_state_account,
3163 1,
3164 )
3165 .unwrap();
3166
3167 self.process_ixs(
3168 &confidential_transfer_fee::instruction::withdraw_withheld_tokens_from_accounts(
3169 &self.program_id,
3170 &self.pubkey,
3171 destination_account,
3172 new_decryptable_available_balance,
3173 withdraw_withheld_authority,
3174 &multisig_signers,
3175 sources,
3176 proof_location,
3177 )?,
3178 signing_keypairs,
3179 )
3180 .await
3181 }
3182
3183 pub async fn confidential_transfer_harvest_withheld_tokens_to_mint(
3185 &self,
3186 sources: &[&Pubkey],
3187 ) -> TokenResult<T::Output> {
3188 self.process_ixs::<[&dyn Signer; 0]>(
3189 &[
3190 confidential_transfer_fee::instruction::harvest_withheld_tokens_to_mint(
3191 &self.program_id,
3192 &self.pubkey,
3193 sources,
3194 )?,
3195 ],
3196 &[],
3197 )
3198 .await
3199 }
3200
3201 pub async fn confidential_transfer_enable_harvest_to_mint<S: Signers>(
3203 &self,
3204 withdraw_withheld_authority: &Pubkey,
3205 signing_keypairs: &S,
3206 ) -> TokenResult<T::Output> {
3207 let signing_pubkeys = signing_keypairs.pubkeys();
3208 let multisig_signers =
3209 self.get_multisig_signers(withdraw_withheld_authority, &signing_pubkeys);
3210
3211 self.process_ixs(
3212 &[
3213 confidential_transfer_fee::instruction::enable_harvest_to_mint(
3214 &self.program_id,
3215 &self.pubkey,
3216 withdraw_withheld_authority,
3217 &multisig_signers,
3218 )?,
3219 ],
3220 signing_keypairs,
3221 )
3222 .await
3223 }
3224
3225 pub async fn confidential_transfer_disable_harvest_to_mint<S: Signers>(
3227 &self,
3228 withdraw_withheld_authority: &Pubkey,
3229 signing_keypairs: &S,
3230 ) -> TokenResult<T::Output> {
3231 let signing_pubkeys = signing_keypairs.pubkeys();
3232 let multisig_signers =
3233 self.get_multisig_signers(withdraw_withheld_authority, &signing_pubkeys);
3234
3235 self.process_ixs(
3236 &[
3237 confidential_transfer_fee::instruction::disable_harvest_to_mint(
3238 &self.program_id,
3239 &self.pubkey,
3240 withdraw_withheld_authority,
3241 &multisig_signers,
3242 )?,
3243 ],
3244 signing_keypairs,
3245 )
3246 .await
3247 }
3248
3249 #[allow(clippy::too_many_arguments)]
3251 pub async fn confidential_transfer_rotate_supply_elgamal_pubkey<S: Signers>(
3252 &self,
3253 authority: &Pubkey,
3254 current_supply_elgamal_keypair: &ElGamalKeypair,
3255 new_supply_elgamal_pubkey: &ElGamalPubkey,
3256 aes_key: &AeKey,
3257 context_state_account: Option<&Pubkey>,
3258 account_info: Option<SupplyAccountInfo>,
3259 signing_keypairs: &S,
3260 ) -> TokenResult<T::Output> {
3261 let signing_pubkeys = signing_keypairs.pubkeys();
3262 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3263
3264 let account_info = if let Some(account_info) = account_info {
3265 account_info
3266 } else {
3267 let account = self.get_mint_info().await?;
3268 let confidential_supply_account = account.get_extension::<ConfidentialMintBurn>()?;
3269 SupplyAccountInfo::new(confidential_supply_account)
3270 };
3271
3272 let proof_data = if context_state_account.is_some() {
3273 None
3274 } else {
3275 Some(
3276 account_info
3277 .generate_rotate_supply_elgamal_pubkey_proof(
3278 current_supply_elgamal_keypair,
3279 new_supply_elgamal_pubkey,
3280 aes_key,
3281 )
3282 .map_err(|_| TokenError::ProofGeneration)?,
3283 )
3284 };
3285
3286 let proof_location = Self::confidential_transfer_create_proof_location(
3289 proof_data.as_ref(),
3290 context_state_account,
3291 1,
3292 )
3293 .unwrap();
3294
3295 let new_supply_elgamal_pubkey = (*new_supply_elgamal_pubkey).into();
3296 self.process_ixs(
3297 &confidential_mint_burn::instruction::rotate_supply_elgamal_pubkey(
3298 &self.program_id,
3299 &self.pubkey,
3300 authority,
3301 &multisig_signers,
3302 &new_supply_elgamal_pubkey,
3303 proof_location,
3304 )?,
3305 signing_keypairs,
3306 )
3307 .await
3308 }
3309
3310 pub async fn confidential_transfer_update_decrypt_supply<S: Signers>(
3312 &self,
3313 authority: &Pubkey,
3314 new_decryptable_supply: &DecryptableBalance,
3315 signing_keypairs: &S,
3316 ) -> TokenResult<T::Output> {
3317 let signing_pubkeys = signing_keypairs.pubkeys();
3318 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3319
3320 self.process_ixs(
3321 &[
3322 confidential_mint_burn::instruction::update_decryptable_supply(
3323 &self.program_id,
3324 &self.pubkey,
3325 authority,
3326 &multisig_signers,
3327 new_decryptable_supply,
3328 )?,
3329 ],
3330 signing_keypairs,
3331 )
3332 .await
3333 }
3334
3335 #[allow(clippy::too_many_arguments)]
3337 pub async fn confidential_transfer_mint<S: Signers>(
3338 &self,
3339 authority: &Pubkey,
3340 destination_account: &Pubkey,
3341 equality_proof_account: Option<&Pubkey>,
3342 ciphertext_validity_proof_account_with_ciphertext: Option<&ProofAccountWithCiphertext>,
3343 range_proof_account: Option<&Pubkey>,
3344 mint_amount: u64,
3345 supply_elgamal_keypair: &ElGamalKeypair,
3346 destination_elgamal_pubkey: &ElGamalPubkey,
3347 auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
3348 aes_key: &AeKey,
3349 account_info: Option<SupplyAccountInfo>,
3350 signing_keypairs: &S,
3351 ) -> TokenResult<T::Output> {
3352 let signing_pubkeys = signing_keypairs.pubkeys();
3353 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3354
3355 let account_info = if let Some(account_info) = account_info {
3356 account_info
3357 } else {
3358 let account = self.get_mint_info().await?;
3359 let confidential_supply_account = account.get_extension::<ConfidentialMintBurn>()?;
3360 SupplyAccountInfo::new(confidential_supply_account)
3361 };
3362
3363 let (equality_proof_data, ciphertext_validity_proof_data_with_ciphertext, range_proof_data) =
3364 if equality_proof_account.is_some()
3365 && ciphertext_validity_proof_account_with_ciphertext.is_some()
3366 && range_proof_account.is_some()
3367 {
3368 (None, None, None)
3370 } else {
3371 let MintProofData {
3372 equality_proof_data,
3373 ciphertext_validity_proof_data_with_ciphertext,
3374 range_proof_data,
3375 } = account_info
3376 .generate_split_mint_proof_data(
3377 mint_amount,
3378 supply_elgamal_keypair,
3379 aes_key,
3380 destination_elgamal_pubkey,
3381 auditor_elgamal_pubkey,
3382 )
3383 .map_err(|_| TokenError::ProofGeneration)?;
3384
3385 let equality_proof_data = equality_proof_account
3386 .is_none()
3387 .then_some(equality_proof_data);
3388 let ciphertext_validity_proof_data_with_ciphertext =
3389 ciphertext_validity_proof_account_with_ciphertext
3390 .is_none()
3391 .then_some(ciphertext_validity_proof_data_with_ciphertext);
3392 let range_proof_data = range_proof_account.is_none().then_some(range_proof_data);
3393
3394 (
3395 equality_proof_data,
3396 ciphertext_validity_proof_data_with_ciphertext,
3397 range_proof_data,
3398 )
3399 };
3400
3401 let equality_proof_location = Self::confidential_transfer_create_proof_location(
3404 equality_proof_data.as_ref(),
3405 equality_proof_account,
3406 1,
3407 )
3408 .unwrap();
3409 let ciphertext_validity_proof_data =
3410 ciphertext_validity_proof_data_with_ciphertext.map(|data| data.proof_data);
3411 let ciphertext_validity_proof_location = Self::confidential_transfer_create_proof_location(
3412 ciphertext_validity_proof_data.as_ref(),
3413 ciphertext_validity_proof_account_with_ciphertext
3414 .map(|account| &account.context_state_account),
3415 2,
3416 )
3417 .unwrap();
3418 let range_proof_location = Self::confidential_transfer_create_proof_location(
3419 range_proof_data.as_ref(),
3420 range_proof_account,
3421 3,
3422 )
3423 .unwrap();
3424
3425 let (mint_amount_auditor_ciphertext_lo, mint_amount_auditor_ciphertext_hi) = if let Some(
3426 proof_data_with_ciphertext,
3427 ) =
3428 ciphertext_validity_proof_data_with_ciphertext
3429 {
3430 (
3431 proof_data_with_ciphertext.ciphertext_lo,
3432 proof_data_with_ciphertext.ciphertext_hi,
3433 )
3434 } else {
3435 (
3439 ciphertext_validity_proof_account_with_ciphertext
3440 .unwrap()
3441 .ciphertext_lo,
3442 ciphertext_validity_proof_account_with_ciphertext
3443 .unwrap()
3444 .ciphertext_hi,
3445 )
3446 };
3447
3448 let new_decryptable_supply = account_info
3449 .new_decryptable_supply(mint_amount, supply_elgamal_keypair, aes_key)
3450 .map_err(|_| TokenError::AccountDecryption)?
3451 .into();
3452
3453 self.process_ixs(
3454 &confidential_mint_burn::instruction::confidential_mint_with_split_proofs(
3455 &self.program_id,
3456 destination_account,
3457 &self.pubkey,
3458 &mint_amount_auditor_ciphertext_lo,
3459 &mint_amount_auditor_ciphertext_hi,
3460 authority,
3461 &multisig_signers,
3462 equality_proof_location,
3463 ciphertext_validity_proof_location,
3464 range_proof_location,
3465 &new_decryptable_supply,
3466 )?,
3467 signing_keypairs,
3468 )
3469 .await
3470 }
3471
3472 #[allow(clippy::too_many_arguments)]
3474 pub async fn confidential_transfer_burn<S: Signers>(
3475 &self,
3476 authority: &Pubkey,
3477 source_account: &Pubkey,
3478 equality_proof_account: Option<&Pubkey>,
3479 ciphertext_validity_proof_account_with_ciphertext: Option<&ProofAccountWithCiphertext>,
3480 range_proof_account: Option<&Pubkey>,
3481 burn_amount: u64,
3482 source_elgamal_keypair: &ElGamalKeypair,
3483 supply_elgamal_pubkey: &ElGamalPubkey,
3484 auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
3485 aes_key: &AeKey,
3486 account_info: Option<BurnAccountInfo>,
3487 signing_keypairs: &S,
3488 ) -> TokenResult<T::Output> {
3489 let signing_pubkeys = signing_keypairs.pubkeys();
3490 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3491
3492 let account_info = if let Some(account_info) = account_info {
3493 account_info
3494 } else {
3495 let account = self.get_account_info(source_account).await?;
3496 let confidential_supply_account =
3497 account.get_extension::<ConfidentialTransferAccount>()?;
3498 BurnAccountInfo::new(confidential_supply_account)
3499 };
3500
3501 let (equality_proof_data, ciphertext_validity_proof_data_with_ciphertext, range_proof_data) =
3502 if equality_proof_account.is_some()
3503 && ciphertext_validity_proof_account_with_ciphertext.is_some()
3504 && range_proof_account.is_some()
3505 {
3506 (None, None, None)
3508 } else {
3509 let BurnProofData {
3510 equality_proof_data,
3511 ciphertext_validity_proof_data_with_ciphertext,
3512 range_proof_data,
3513 } = account_info
3514 .generate_split_burn_proof_data(
3515 burn_amount,
3516 source_elgamal_keypair,
3517 aes_key,
3518 supply_elgamal_pubkey,
3519 auditor_elgamal_pubkey,
3520 )
3521 .map_err(|_| TokenError::ProofGeneration)?;
3522
3523 let equality_proof_data = equality_proof_account
3524 .is_none()
3525 .then_some(equality_proof_data);
3526 let ciphertext_validity_proof_data_with_ciphertext =
3527 ciphertext_validity_proof_account_with_ciphertext
3528 .is_none()
3529 .then_some(ciphertext_validity_proof_data_with_ciphertext);
3530 let range_proof_data = range_proof_account.is_none().then_some(range_proof_data);
3531
3532 (
3533 equality_proof_data,
3534 ciphertext_validity_proof_data_with_ciphertext,
3535 range_proof_data,
3536 )
3537 };
3538
3539 let equality_proof_location = Self::confidential_transfer_create_proof_location(
3542 equality_proof_data.as_ref(),
3543 equality_proof_account,
3544 1,
3545 )
3546 .unwrap();
3547 let ciphertext_validity_proof_data =
3548 ciphertext_validity_proof_data_with_ciphertext.map(|data| data.proof_data);
3549 let ciphertext_validity_proof_location = Self::confidential_transfer_create_proof_location(
3550 ciphertext_validity_proof_data.as_ref(),
3551 ciphertext_validity_proof_account_with_ciphertext
3552 .map(|account| &account.context_state_account),
3553 2,
3554 )
3555 .unwrap();
3556 let range_proof_location = Self::confidential_transfer_create_proof_location(
3557 range_proof_data.as_ref(),
3558 range_proof_account,
3559 3,
3560 )
3561 .unwrap();
3562
3563 let (burn_amount_auditor_ciphertext_lo, burn_amount_auditor_ciphertext_hi) = if let Some(
3564 proof_data_with_ciphertext,
3565 ) =
3566 ciphertext_validity_proof_data_with_ciphertext
3567 {
3568 (
3569 proof_data_with_ciphertext.ciphertext_lo,
3570 proof_data_with_ciphertext.ciphertext_hi,
3571 )
3572 } else {
3573 (
3577 ciphertext_validity_proof_account_with_ciphertext
3578 .unwrap()
3579 .ciphertext_lo,
3580 ciphertext_validity_proof_account_with_ciphertext
3581 .unwrap()
3582 .ciphertext_hi,
3583 )
3584 };
3585
3586 let new_decryptable_balance = account_info
3587 .new_decryptable_balance(burn_amount, aes_key)
3588 .map_err(|_| TokenError::AccountDecryption)?
3589 .into();
3590
3591 self.process_ixs(
3592 &confidential_mint_burn::instruction::confidential_burn_with_split_proofs(
3593 &self.program_id,
3594 source_account,
3595 &self.pubkey,
3596 &new_decryptable_balance,
3597 &burn_amount_auditor_ciphertext_lo,
3598 &burn_amount_auditor_ciphertext_hi,
3599 authority,
3600 &multisig_signers,
3601 equality_proof_location,
3602 ciphertext_validity_proof_location,
3603 range_proof_location,
3604 )?,
3605 signing_keypairs,
3606 )
3607 .await
3608 }
3609
3610 pub async fn confidential_transfer_apply_pending_burn<S: Signers>(
3612 &self,
3613 authority: &Pubkey,
3614 signing_keypairs: &S,
3615 ) -> TokenResult<T::Output> {
3616 let signing_pubkeys = signing_keypairs.pubkeys();
3617 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3618
3619 self.process_ixs(
3620 &[confidential_mint_burn::instruction::apply_pending_burn(
3621 &self.program_id,
3622 &self.pubkey,
3623 authority,
3624 &multisig_signers,
3625 )?],
3626 signing_keypairs,
3627 )
3628 .await
3629 }
3630
3631 fn confidential_transfer_create_proof_location<'a, ZK: ZkProofData<U>, U: Pod>(
3634 proof_data: Option<&'a ZK>,
3635 context_account: Option<&'a Pubkey>,
3636 instruction_offset: i8,
3637 ) -> Option<ProofLocation<'a, ZK>> {
3638 if let Some(proof_data) = proof_data {
3639 Some(ProofLocation::InstructionOffset(
3640 instruction_offset.try_into().unwrap(),
3641 proof_data,
3642 ))
3643 } else {
3644 context_account.map(ProofLocation::ContextStateAccount)
3645 }
3646 }
3647
3648 pub async fn withdraw_excess_lamports<S: Signers>(
3649 &self,
3650 source: &Pubkey,
3651 destination: &Pubkey,
3652 authority: &Pubkey,
3653 signing_keypairs: &S,
3654 ) -> TokenResult<T::Output> {
3655 let signing_pubkeys = signing_keypairs.pubkeys();
3656 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3657
3658 self.process_ixs(
3659 &[
3660 spl_token_2022_interface::instruction::withdraw_excess_lamports(
3661 &self.program_id,
3662 source,
3663 destination,
3664 authority,
3665 &multisig_signers,
3666 )?,
3667 ],
3668 signing_keypairs,
3669 )
3670 .await
3671 }
3672
3673 pub async fn token_metadata_initialize<S: Signers>(
3675 &self,
3676 update_authority: &Pubkey,
3677 mint_authority: &Pubkey,
3678 name: String,
3679 symbol: String,
3680 uri: String,
3681 signing_keypairs: &S,
3682 ) -> TokenResult<T::Output> {
3683 self.process_ixs(
3684 &[spl_token_metadata_interface::instruction::initialize(
3685 &self.program_id,
3686 &self.pubkey,
3687 update_authority,
3688 &self.pubkey,
3689 mint_authority,
3690 name,
3691 symbol,
3692 uri,
3693 )],
3694 signing_keypairs,
3695 )
3696 .await
3697 }
3698
3699 async fn get_additional_rent_for_new_metadata(
3700 &self,
3701 token_metadata: &TokenMetadata,
3702 ) -> TokenResult<u64> {
3703 let account = self.get_account(self.pubkey).await?;
3704 let account_lamports = account.lamports;
3705 let mint_state = self.unpack_mint_info(account)?;
3706 let new_account_len = mint_state
3707 .try_get_new_account_len_for_variable_len_extension::<TokenMetadata>(token_metadata)?;
3708 let new_rent_exempt_minimum = self
3709 .client
3710 .get_minimum_balance_for_rent_exemption(new_account_len)
3711 .await
3712 .map_err(TokenError::Client)?;
3713 Ok(new_rent_exempt_minimum.saturating_sub(account_lamports))
3714 }
3715
3716 #[allow(clippy::too_many_arguments)]
3718 pub async fn token_metadata_initialize_with_rent_transfer<S: Signers>(
3719 &self,
3720 payer: &Pubkey,
3721 update_authority: &Pubkey,
3722 mint_authority: &Pubkey,
3723 name: String,
3724 symbol: String,
3725 uri: String,
3726 signing_keypairs: &S,
3727 ) -> TokenResult<T::Output> {
3728 let token_metadata = TokenMetadata {
3729 name,
3730 symbol,
3731 uri,
3732 ..Default::default()
3733 };
3734 let additional_lamports = self
3735 .get_additional_rent_for_new_metadata(&token_metadata)
3736 .await?;
3737 let mut instructions = vec![];
3738 if additional_lamports > 0 {
3739 instructions.push(system_instruction::transfer(
3740 payer,
3741 &self.pubkey,
3742 additional_lamports,
3743 ));
3744 }
3745 instructions.push(spl_token_metadata_interface::instruction::initialize(
3746 &self.program_id,
3747 &self.pubkey,
3748 update_authority,
3749 &self.pubkey,
3750 mint_authority,
3751 token_metadata.name,
3752 token_metadata.symbol,
3753 token_metadata.uri,
3754 ));
3755 self.process_ixs(&instructions, signing_keypairs).await
3756 }
3757
3758 pub async fn token_metadata_update_field<S: Signers>(
3760 &self,
3761 update_authority: &Pubkey,
3762 field: Field,
3763 value: String,
3764 signing_keypairs: &S,
3765 ) -> TokenResult<T::Output> {
3766 self.process_ixs(
3767 &[spl_token_metadata_interface::instruction::update_field(
3768 &self.program_id,
3769 &self.pubkey,
3770 update_authority,
3771 field,
3772 value,
3773 )],
3774 signing_keypairs,
3775 )
3776 .await
3777 }
3778
3779 async fn get_additional_rent_for_updated_metadata(
3780 &self,
3781 field: Field,
3782 value: String,
3783 ) -> TokenResult<u64> {
3784 let account = self.get_account(self.pubkey).await?;
3785 let account_lamports = account.lamports;
3786 let mint_state = self.unpack_mint_info(account)?;
3787 let mut token_metadata = mint_state.get_variable_len_extension::<TokenMetadata>()?;
3788 token_metadata.update(field, value);
3789 let new_account_len = mint_state
3790 .try_get_new_account_len_for_variable_len_extension::<TokenMetadata>(&token_metadata)?;
3791 let new_rent_exempt_minimum = self
3792 .client
3793 .get_minimum_balance_for_rent_exemption(new_account_len)
3794 .await
3795 .map_err(TokenError::Client)?;
3796 Ok(new_rent_exempt_minimum.saturating_sub(account_lamports))
3797 }
3798
3799 #[allow(clippy::too_many_arguments)]
3802 pub async fn token_metadata_update_field_with_rent_transfer<S: Signers>(
3803 &self,
3804 payer: &Pubkey,
3805 update_authority: &Pubkey,
3806 field: Field,
3807 value: String,
3808 transfer_lamports: Option<u64>,
3809 signing_keypairs: &S,
3810 ) -> TokenResult<T::Output> {
3811 let additional_lamports = if let Some(transfer_lamports) = transfer_lamports {
3812 transfer_lamports
3813 } else {
3814 self.get_additional_rent_for_updated_metadata(field.clone(), value.clone())
3815 .await?
3816 };
3817 let mut instructions = vec![];
3818 if additional_lamports > 0 {
3819 instructions.push(system_instruction::transfer(
3820 payer,
3821 &self.pubkey,
3822 additional_lamports,
3823 ));
3824 }
3825 instructions.push(spl_token_metadata_interface::instruction::update_field(
3826 &self.program_id,
3827 &self.pubkey,
3828 update_authority,
3829 field,
3830 value,
3831 ));
3832 self.process_ixs(&instructions, signing_keypairs).await
3833 }
3834
3835 pub async fn token_metadata_update_authority<S: Signers>(
3837 &self,
3838 current_authority: &Pubkey,
3839 new_authority: Option<Pubkey>,
3840 signing_keypairs: &S,
3841 ) -> TokenResult<T::Output> {
3842 self.process_ixs(
3843 &[spl_token_metadata_interface::instruction::update_authority(
3844 &self.program_id,
3845 &self.pubkey,
3846 current_authority,
3847 new_authority.try_into()?,
3848 )],
3849 signing_keypairs,
3850 )
3851 .await
3852 }
3853
3854 pub async fn token_metadata_remove_key<S: Signers>(
3856 &self,
3857 update_authority: &Pubkey,
3858 key: String,
3859 idempotent: bool,
3860 signing_keypairs: &S,
3861 ) -> TokenResult<T::Output> {
3862 self.process_ixs(
3863 &[spl_token_metadata_interface::instruction::remove_key(
3864 &self.program_id,
3865 &self.pubkey,
3866 update_authority,
3867 key,
3868 idempotent,
3869 )],
3870 signing_keypairs,
3871 )
3872 .await
3873 }
3874
3875 pub async fn token_group_initialize<S: Signers>(
3877 &self,
3878 mint_authority: &Pubkey,
3879 update_authority: &Pubkey,
3880 max_size: u64,
3881 signing_keypairs: &S,
3882 ) -> TokenResult<T::Output> {
3883 self.process_ixs(
3884 &[spl_token_group_interface::instruction::initialize_group(
3885 &self.program_id,
3886 &self.pubkey,
3887 &self.pubkey,
3888 mint_authority,
3889 Some(*update_authority),
3890 max_size,
3891 )],
3892 signing_keypairs,
3893 )
3894 .await
3895 }
3896
3897 async fn get_additional_rent_for_fixed_len_extension<V: Extension + Pod>(
3898 &self,
3899 ) -> TokenResult<u64> {
3900 let account = self.get_account(self.pubkey).await?;
3901 let account_lamports = account.lamports;
3902 let mint_state = self.unpack_mint_info(account)?;
3903 if mint_state.get_extension::<V>().is_ok() {
3904 Ok(0)
3905 } else {
3906 let new_account_len = mint_state.try_get_new_account_len::<V>()?;
3907 let new_rent_exempt_minimum = self
3908 .client
3909 .get_minimum_balance_for_rent_exemption(new_account_len)
3910 .await
3911 .map_err(TokenError::Client)?;
3912 Ok(new_rent_exempt_minimum.saturating_sub(account_lamports))
3913 }
3914 }
3915
3916 pub async fn token_group_initialize_with_rent_transfer<S: Signers>(
3918 &self,
3919 payer: &Pubkey,
3920 mint_authority: &Pubkey,
3921 update_authority: &Pubkey,
3922 max_size: u64,
3923 signing_keypairs: &S,
3924 ) -> TokenResult<T::Output> {
3925 let additional_lamports = self
3926 .get_additional_rent_for_fixed_len_extension::<TokenGroup>()
3927 .await?;
3928 let mut instructions = vec![];
3929 if additional_lamports > 0 {
3930 instructions.push(system_instruction::transfer(
3931 payer,
3932 &self.pubkey,
3933 additional_lamports,
3934 ));
3935 }
3936 instructions.push(spl_token_group_interface::instruction::initialize_group(
3937 &self.program_id,
3938 &self.pubkey,
3939 &self.pubkey,
3940 mint_authority,
3941 Some(*update_authority),
3942 max_size,
3943 ));
3944 self.process_ixs(&instructions, signing_keypairs).await
3945 }
3946
3947 pub async fn token_group_update_max_size<S: Signers>(
3949 &self,
3950 update_authority: &Pubkey,
3951 new_max_size: u64,
3952 signing_keypairs: &S,
3953 ) -> TokenResult<T::Output> {
3954 self.process_ixs(
3955 &[
3956 spl_token_group_interface::instruction::update_group_max_size(
3957 &self.program_id,
3958 &self.pubkey,
3959 update_authority,
3960 new_max_size,
3961 ),
3962 ],
3963 signing_keypairs,
3964 )
3965 .await
3966 }
3967
3968 pub async fn token_group_update_authority<S: Signers>(
3970 &self,
3971 current_authority: &Pubkey,
3972 new_authority: Option<Pubkey>,
3973 signing_keypairs: &S,
3974 ) -> TokenResult<T::Output> {
3975 self.process_ixs(
3976 &[
3977 spl_token_group_interface::instruction::update_group_authority(
3978 &self.program_id,
3979 &self.pubkey,
3980 current_authority,
3981 new_authority,
3982 ),
3983 ],
3984 signing_keypairs,
3985 )
3986 .await
3987 }
3988
3989 pub async fn token_group_initialize_member<S: Signers>(
3991 &self,
3992 mint_authority: &Pubkey,
3993 group_mint: &Pubkey,
3994 group_update_authority: &Pubkey,
3995 signing_keypairs: &S,
3996 ) -> TokenResult<T::Output> {
3997 self.process_ixs(
3998 &[spl_token_group_interface::instruction::initialize_member(
3999 &self.program_id,
4000 &self.pubkey,
4001 &self.pubkey,
4002 mint_authority,
4003 group_mint,
4004 group_update_authority,
4005 )],
4006 signing_keypairs,
4007 )
4008 .await
4009 }
4010
4011 #[allow(clippy::too_many_arguments)]
4013 pub async fn token_group_initialize_member_with_rent_transfer<S: Signers>(
4014 &self,
4015 payer: &Pubkey,
4016 mint_authority: &Pubkey,
4017 group_mint: &Pubkey,
4018 group_update_authority: &Pubkey,
4019 signing_keypairs: &S,
4020 ) -> TokenResult<T::Output> {
4021 let additional_lamports = self
4022 .get_additional_rent_for_fixed_len_extension::<TokenGroupMember>()
4023 .await?;
4024 let mut instructions = vec![];
4025 if additional_lamports > 0 {
4026 instructions.push(system_instruction::transfer(
4027 payer,
4028 &self.pubkey,
4029 additional_lamports,
4030 ));
4031 }
4032 instructions.push(spl_token_group_interface::instruction::initialize_member(
4033 &self.program_id,
4034 &self.pubkey,
4035 &self.pubkey,
4036 mint_authority,
4037 group_mint,
4038 group_update_authority,
4039 ));
4040 self.process_ixs(&instructions, signing_keypairs).await
4041 }
4042
4043 pub async fn confidential_transfer_get_pending_balance(
4048 &self,
4049 account: &Pubkey,
4050 elgamal_secret_key: &ElGamalSecretKey,
4051 ) -> TokenResult<u64> {
4052 let account_info = self.get_account_info(account).await?;
4053 let confidential_transfer_account =
4054 account_info.get_extension::<ConfidentialTransferAccount>()?;
4055 let account_info = ApplyPendingBalanceAccountInfo::new(confidential_transfer_account);
4056
4057 account_info
4058 .get_pending_balance(elgamal_secret_key)
4059 .map_err(|_| TokenError::AccountDecryption)
4060 }
4061
4062 pub async fn confidential_transfer_get_available_balance(
4066 &self,
4067 account: &Pubkey,
4068 aes_key: &AeKey,
4069 ) -> TokenResult<u64> {
4070 let account_info = self.get_account_info(account).await?;
4071 let confidential_transfer_account =
4072 account_info.get_extension::<ConfidentialTransferAccount>()?;
4073 let account_info = ApplyPendingBalanceAccountInfo::new(confidential_transfer_account);
4074
4075 account_info
4076 .get_available_balance(aes_key)
4077 .map_err(|_| TokenError::AccountDecryption)
4078 }
4079
4080 pub async fn confidential_transfer_get_total_balance(
4084 &self,
4085 account: &Pubkey,
4086 elgamal_secret_key: &ElGamalSecretKey,
4087 aes_key: &AeKey,
4088 ) -> TokenResult<u64> {
4089 let account_info = self.get_account_info(account).await?;
4090 let confidential_transfer_account =
4091 account_info.get_extension::<ConfidentialTransferAccount>()?;
4092 let account_info = ApplyPendingBalanceAccountInfo::new(confidential_transfer_account);
4093
4094 account_info
4095 .get_total_balance(elgamal_secret_key, aes_key)
4096 .map_err(|e| match e {
4097 spl_token_2022_interface::error::TokenError::Overflow => {
4098 TokenError::AccountDecryption
4099 }
4100 _ => TokenError::AccountDecryption,
4101 })
4102 }
4103
4104 pub async fn confidential_transfer_has_pending_balance(
4108 &self,
4109 account: &Pubkey,
4110 ) -> TokenResult<bool> {
4111 let account_info = self.get_account_info(account).await?;
4112 let confidential_transfer_account =
4113 account_info.get_extension::<ConfidentialTransferAccount>()?;
4114 let account_info = ApplyPendingBalanceAccountInfo::new(confidential_transfer_account);
4115
4116 Ok(account_info.has_pending_balance())
4117 }
4118}
4119
4120fn calculate_record_max_chunk_size<F>(
4123 create_record_instructions: F,
4124 first_instruction: bool,
4125) -> usize
4126where
4127 F: Fn(bool, &[u8], u64) -> Vec<Instruction>,
4128{
4129 let ixs = create_record_instructions(first_instruction, &[], 0);
4130 let message = Message::new_with_blockhash(&ixs, Some(&Pubkey::default()), &Hash::default());
4131 let tx_size = bincode::serialized_size(&Transaction {
4132 signatures: vec![Signature::default(); message.header.num_required_signatures as usize],
4133 message,
4134 })
4135 .unwrap() as usize;
4136 PACKET_DATA_SIZE.saturating_sub(tx_size).saturating_sub(1)
4137}