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