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 approve<S: Signers>(
1300 &self,
1301 source: &Address,
1302 delegate: &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 [instruction::approve_checked(
1312 &self.program_id,
1313 source,
1314 &self.pubkey,
1315 delegate,
1316 authority,
1317 &multisig_signers,
1318 amount,
1319 decimals,
1320 )?]
1321 } else {
1322 [instruction::approve(
1323 &self.program_id,
1324 source,
1325 delegate,
1326 authority,
1327 &multisig_signers,
1328 amount,
1329 )?]
1330 };
1331
1332 self.process_ixs(&instructions, signing_keypairs).await
1333 }
1334
1335 pub async fn revoke<S: Signers>(
1337 &self,
1338 source: &Address,
1339 authority: &Address,
1340 signing_keypairs: &S,
1341 ) -> TokenResult<T::Output> {
1342 let signing_pubkeys = signing_keypairs.pubkeys();
1343 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1344
1345 self.process_ixs(
1346 &[instruction::revoke(
1347 &self.program_id,
1348 source,
1349 authority,
1350 &multisig_signers,
1351 )?],
1352 signing_keypairs,
1353 )
1354 .await
1355 }
1356
1357 pub async fn close_account<S: Signers>(
1359 &self,
1360 account: &Address,
1361 lamports_destination: &Address,
1362 authority: &Address,
1363 signing_keypairs: &S,
1364 ) -> TokenResult<T::Output> {
1365 let signing_pubkeys = signing_keypairs.pubkeys();
1366 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1367
1368 let mut instructions = vec![instruction::close_account(
1369 &self.program_id,
1370 account,
1371 lamports_destination,
1372 authority,
1373 &multisig_signers,
1374 )?];
1375
1376 if let Ok(Some(destination_account)) = self.client.get_account(*lamports_destination).await
1377 {
1378 if let Ok(destination_obj) =
1379 StateWithExtensionsOwned::<Account>::unpack(destination_account.data)
1380 {
1381 if destination_obj.base.is_native() {
1382 instructions.push(instruction::sync_native(
1383 &self.program_id,
1384 lamports_destination,
1385 )?);
1386 }
1387 }
1388 }
1389
1390 self.process_ixs(&instructions, signing_keypairs).await
1391 }
1392
1393 pub async fn empty_and_close_account<S: Signers>(
1395 &self,
1396 account_to_close: &Address,
1397 lamports_destination: &Address,
1398 tokens_destination: &Address,
1399 authority: &Address,
1400 signing_keypairs: &S,
1401 ) -> TokenResult<T::Output> {
1402 let signing_pubkeys = signing_keypairs.pubkeys();
1403 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1404
1405 let account_state = self.get_account_info(account_to_close).await?;
1407
1408 let mut instructions = vec![];
1409
1410 if !self.is_native() && account_state.base.amount > 0 {
1411 if let Some(decimals) = self.decimals {
1413 instructions.push(instruction::transfer_checked(
1414 &self.program_id,
1415 account_to_close,
1416 &self.pubkey,
1417 tokens_destination,
1418 authority,
1419 &multisig_signers,
1420 account_state.base.amount,
1421 decimals,
1422 )?);
1423 } else {
1424 #[allow(deprecated)]
1425 instructions.push(instruction::transfer(
1426 &self.program_id,
1427 account_to_close,
1428 tokens_destination,
1429 authority,
1430 &multisig_signers,
1431 account_state.base.amount,
1432 )?);
1433 }
1434 }
1435
1436 instructions.push(instruction::close_account(
1437 &self.program_id,
1438 account_to_close,
1439 lamports_destination,
1440 authority,
1441 &multisig_signers,
1442 )?);
1443
1444 if let Ok(Some(destination_account)) = self.client.get_account(*lamports_destination).await
1445 {
1446 if let Ok(destination_obj) =
1447 StateWithExtensionsOwned::<Account>::unpack(destination_account.data)
1448 {
1449 if destination_obj.base.is_native() {
1450 instructions.push(instruction::sync_native(
1451 &self.program_id,
1452 lamports_destination,
1453 )?);
1454 }
1455 }
1456 }
1457
1458 self.process_ixs(&instructions, signing_keypairs).await
1459 }
1460
1461 pub async fn freeze<S: Signers>(
1463 &self,
1464 account: &Address,
1465 authority: &Address,
1466 signing_keypairs: &S,
1467 ) -> TokenResult<T::Output> {
1468 let signing_pubkeys = signing_keypairs.pubkeys();
1469 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1470
1471 self.process_ixs(
1472 &[instruction::freeze_account(
1473 &self.program_id,
1474 account,
1475 &self.pubkey,
1476 authority,
1477 &multisig_signers,
1478 )?],
1479 signing_keypairs,
1480 )
1481 .await
1482 }
1483
1484 pub async fn thaw<S: Signers>(
1486 &self,
1487 account: &Address,
1488 authority: &Address,
1489 signing_keypairs: &S,
1490 ) -> TokenResult<T::Output> {
1491 let signing_pubkeys = signing_keypairs.pubkeys();
1492 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1493
1494 self.process_ixs(
1495 &[instruction::thaw_account(
1496 &self.program_id,
1497 account,
1498 &self.pubkey,
1499 authority,
1500 &multisig_signers,
1501 )?],
1502 signing_keypairs,
1503 )
1504 .await
1505 }
1506
1507 pub async fn wrap<S: Signers>(
1509 &self,
1510 account: &Address,
1511 owner: &Address,
1512 lamports: u64,
1513 signing_keypairs: &S,
1514 ) -> TokenResult<T::Output> {
1515 let immutable_owner = self.program_id != spl_token_interface::id();
1517 let instructions = self.wrap_ixs(account, owner, lamports, immutable_owner)?;
1518
1519 self.process_ixs(&instructions, signing_keypairs).await
1520 }
1521
1522 pub async fn wrap_with_mutable_ownership<S: Signers>(
1525 &self,
1526 account: &Address,
1527 owner: &Address,
1528 lamports: u64,
1529 signing_keypairs: &S,
1530 ) -> TokenResult<T::Output> {
1531 let instructions = self.wrap_ixs(account, owner, lamports, false)?;
1532
1533 self.process_ixs(&instructions, signing_keypairs).await
1534 }
1535
1536 fn wrap_ixs(
1537 &self,
1538 account: &Address,
1539 owner: &Address,
1540 lamports: u64,
1541 immutable_owner: bool,
1542 ) -> TokenResult<Vec<Instruction>> {
1543 if !self.is_native() {
1544 return Err(TokenError::AccountInvalidMint);
1545 }
1546
1547 let mut instructions = vec![];
1548 if *account == self.get_associated_token_address(owner) {
1549 instructions.push(system_instruction::transfer(owner, account, lamports));
1550 instructions.push(create_associated_token_account(
1551 &self.payer.pubkey(),
1552 owner,
1553 &self.pubkey,
1554 &self.program_id,
1555 ));
1556 } else {
1557 let extensions = if immutable_owner {
1558 vec![ExtensionType::ImmutableOwner]
1559 } else {
1560 vec![]
1561 };
1562 let space = ExtensionType::try_calculate_account_len::<Account>(&extensions)?;
1563
1564 instructions.push(system_instruction::create_account(
1565 &self.payer.pubkey(),
1566 account,
1567 lamports,
1568 space as u64,
1569 &self.program_id,
1570 ));
1571
1572 if immutable_owner {
1573 instructions.push(instruction::initialize_immutable_owner(
1574 &self.program_id,
1575 account,
1576 )?)
1577 }
1578
1579 instructions.push(instruction::initialize_account(
1580 &self.program_id,
1581 account,
1582 &self.pubkey,
1583 owner,
1584 )?);
1585 };
1586
1587 Ok(instructions)
1588 }
1589
1590 pub async fn unwrap_lamports<S: Signers>(
1592 &self,
1593 source: &Address,
1594 destination: &Address,
1595 authority: &Address,
1596 amount: Option<u64>,
1597 signing_keypairs: &S,
1598 ) -> TokenResult<T::Output> {
1599 let signing_pubkeys = signing_keypairs.pubkeys();
1600 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1601
1602 self.process_ixs(
1603 &[instruction::unwrap_lamports(
1604 &self.program_id,
1605 source,
1606 destination,
1607 authority,
1608 &multisig_signers,
1609 amount,
1610 )?],
1611 signing_keypairs,
1612 )
1613 .await
1614 }
1615
1616 pub async fn sync_native(&self, account: &Address) -> TokenResult<T::Output> {
1618 self.process_ixs::<[&dyn Signer; 0]>(
1619 &[instruction::sync_native(&self.program_id, account)?],
1620 &[],
1621 )
1622 .await
1623 }
1624
1625 pub async fn set_transfer_fee<S: Signers>(
1627 &self,
1628 authority: &Address,
1629 transfer_fee_basis_points: u16,
1630 maximum_fee: u64,
1631 signing_keypairs: &S,
1632 ) -> TokenResult<T::Output> {
1633 let signing_pubkeys = signing_keypairs.pubkeys();
1634 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1635
1636 self.process_ixs(
1637 &[transfer_fee::instruction::set_transfer_fee(
1638 &self.program_id,
1639 &self.pubkey,
1640 authority,
1641 &multisig_signers,
1642 transfer_fee_basis_points,
1643 maximum_fee,
1644 )?],
1645 signing_keypairs,
1646 )
1647 .await
1648 }
1649
1650 pub async fn set_default_account_state<S: Signers>(
1652 &self,
1653 authority: &Address,
1654 state: &AccountState,
1655 signing_keypairs: &S,
1656 ) -> TokenResult<T::Output> {
1657 let signing_pubkeys = signing_keypairs.pubkeys();
1658 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1659
1660 self.process_ixs(
1661 &[
1662 default_account_state::instruction::update_default_account_state(
1663 &self.program_id,
1664 &self.pubkey,
1665 authority,
1666 &multisig_signers,
1667 state,
1668 )?,
1669 ],
1670 signing_keypairs,
1671 )
1672 .await
1673 }
1674
1675 pub async fn harvest_withheld_tokens_to_mint(
1677 &self,
1678 sources: &[&Address],
1679 ) -> TokenResult<T::Output> {
1680 self.process_ixs::<[&dyn Signer; 0]>(
1681 &[transfer_fee::instruction::harvest_withheld_tokens_to_mint(
1682 &self.program_id,
1683 &self.pubkey,
1684 sources,
1685 )?],
1686 &[],
1687 )
1688 .await
1689 }
1690
1691 pub async fn withdraw_withheld_tokens_from_mint<S: Signers>(
1693 &self,
1694 destination: &Address,
1695 authority: &Address,
1696 signing_keypairs: &S,
1697 ) -> TokenResult<T::Output> {
1698 let signing_pubkeys = signing_keypairs.pubkeys();
1699 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1700
1701 self.process_ixs(
1702 &[
1703 transfer_fee::instruction::withdraw_withheld_tokens_from_mint(
1704 &self.program_id,
1705 &self.pubkey,
1706 destination,
1707 authority,
1708 &multisig_signers,
1709 )?,
1710 ],
1711 signing_keypairs,
1712 )
1713 .await
1714 }
1715
1716 pub async fn withdraw_withheld_tokens_from_accounts<S: Signers>(
1718 &self,
1719 destination: &Address,
1720 authority: &Address,
1721 sources: &[&Address],
1722 signing_keypairs: &S,
1723 ) -> TokenResult<T::Output> {
1724 let signing_pubkeys = signing_keypairs.pubkeys();
1725 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1726
1727 self.process_ixs(
1728 &[
1729 transfer_fee::instruction::withdraw_withheld_tokens_from_accounts(
1730 &self.program_id,
1731 &self.pubkey,
1732 destination,
1733 authority,
1734 &multisig_signers,
1735 sources,
1736 )?,
1737 ],
1738 signing_keypairs,
1739 )
1740 .await
1741 }
1742
1743 pub async fn reallocate<S: Signers>(
1746 &self,
1747 account: &Address,
1748 authority: &Address,
1749 extension_types: &[ExtensionType],
1750 signing_keypairs: &S,
1751 ) -> TokenResult<T::Output> {
1752 let signing_pubkeys = signing_keypairs.pubkeys();
1753 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1754
1755 self.process_ixs(
1756 &[instruction::reallocate(
1757 &self.program_id,
1758 account,
1759 &self.payer.pubkey(),
1760 authority,
1761 &multisig_signers,
1762 extension_types,
1763 )?],
1764 signing_keypairs,
1765 )
1766 .await
1767 }
1768
1769 pub async fn enable_required_transfer_memos<S: Signers>(
1771 &self,
1772 account: &Address,
1773 authority: &Address,
1774 signing_keypairs: &S,
1775 ) -> TokenResult<T::Output> {
1776 let signing_pubkeys = signing_keypairs.pubkeys();
1777 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1778
1779 self.process_ixs(
1780 &[memo_transfer::instruction::enable_required_transfer_memos(
1781 &self.program_id,
1782 account,
1783 authority,
1784 &multisig_signers,
1785 )?],
1786 signing_keypairs,
1787 )
1788 .await
1789 }
1790
1791 pub async fn disable_required_transfer_memos<S: Signers>(
1793 &self,
1794 account: &Address,
1795 authority: &Address,
1796 signing_keypairs: &S,
1797 ) -> TokenResult<T::Output> {
1798 let signing_pubkeys = signing_keypairs.pubkeys();
1799 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1800
1801 self.process_ixs(
1802 &[memo_transfer::instruction::disable_required_transfer_memos(
1803 &self.program_id,
1804 account,
1805 authority,
1806 &multisig_signers,
1807 )?],
1808 signing_keypairs,
1809 )
1810 .await
1811 }
1812
1813 pub async fn pause<S: Signers>(
1815 &self,
1816 authority: &Address,
1817 signing_keypairs: &S,
1818 ) -> TokenResult<T::Output> {
1819 let signing_pubkeys = signing_keypairs.pubkeys();
1820 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1821
1822 self.process_ixs(
1823 &[pausable::instruction::pause(
1824 &self.program_id,
1825 self.get_address(),
1826 authority,
1827 &multisig_signers,
1828 )?],
1829 signing_keypairs,
1830 )
1831 .await
1832 }
1833
1834 pub async fn resume<S: Signers>(
1836 &self,
1837 authority: &Address,
1838 signing_keypairs: &S,
1839 ) -> TokenResult<T::Output> {
1840 let signing_pubkeys = signing_keypairs.pubkeys();
1841 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1842
1843 self.process_ixs(
1844 &[pausable::instruction::resume(
1845 &self.program_id,
1846 self.get_address(),
1847 authority,
1848 &multisig_signers,
1849 )?],
1850 signing_keypairs,
1851 )
1852 .await
1853 }
1854
1855 pub async fn enable_cpi_guard<S: Signers>(
1857 &self,
1858 account: &Address,
1859 authority: &Address,
1860 signing_keypairs: &S,
1861 ) -> TokenResult<T::Output> {
1862 let signing_pubkeys = signing_keypairs.pubkeys();
1863 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1864
1865 self.process_ixs(
1866 &[cpi_guard::instruction::enable_cpi_guard(
1867 &self.program_id,
1868 account,
1869 authority,
1870 &multisig_signers,
1871 )?],
1872 signing_keypairs,
1873 )
1874 .await
1875 }
1876
1877 pub async fn disable_cpi_guard<S: Signers>(
1879 &self,
1880 account: &Address,
1881 authority: &Address,
1882 signing_keypairs: &S,
1883 ) -> TokenResult<T::Output> {
1884 let signing_pubkeys = signing_keypairs.pubkeys();
1885 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1886
1887 self.process_ixs(
1888 &[cpi_guard::instruction::disable_cpi_guard(
1889 &self.program_id,
1890 account,
1891 authority,
1892 &multisig_signers,
1893 )?],
1894 signing_keypairs,
1895 )
1896 .await
1897 }
1898
1899 pub async fn update_interest_rate<S: Signers>(
1901 &self,
1902 authority: &Address,
1903 new_rate: i16,
1904 signing_keypairs: &S,
1905 ) -> TokenResult<T::Output> {
1906 let signing_pubkeys = signing_keypairs.pubkeys();
1907 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1908
1909 self.process_ixs(
1910 &[interest_bearing_mint::instruction::update_rate(
1911 &self.program_id,
1912 self.get_address(),
1913 authority,
1914 &multisig_signers,
1915 new_rate,
1916 )?],
1917 signing_keypairs,
1918 )
1919 .await
1920 }
1921
1922 pub async fn update_multiplier<S: Signers>(
1924 &self,
1925 authority: &Address,
1926 new_multiplier: f64,
1927 new_multiplier_effective_timestamp: i64,
1928 signing_keypairs: &S,
1929 ) -> TokenResult<T::Output> {
1930 let signing_pubkeys = signing_keypairs.pubkeys();
1931 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1932
1933 self.process_ixs(
1934 &[scaled_ui_amount::instruction::update_multiplier(
1935 &self.program_id,
1936 self.get_address(),
1937 authority,
1938 &multisig_signers,
1939 new_multiplier,
1940 new_multiplier_effective_timestamp,
1941 )?],
1942 signing_keypairs,
1943 )
1944 .await
1945 }
1946
1947 pub async fn update_transfer_hook_program_id<S: Signers>(
1949 &self,
1950 authority: &Address,
1951 new_program_id: Option<Address>,
1952 signing_keypairs: &S,
1953 ) -> TokenResult<T::Output> {
1954 let signing_pubkeys = signing_keypairs.pubkeys();
1955 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1956
1957 self.process_ixs(
1958 &[transfer_hook::instruction::update(
1959 &self.program_id,
1960 self.get_address(),
1961 authority,
1962 &multisig_signers,
1963 new_program_id,
1964 )?],
1965 signing_keypairs,
1966 )
1967 .await
1968 }
1969
1970 pub async fn update_metadata_address<S: Signers>(
1972 &self,
1973 authority: &Address,
1974 new_metadata_address: Option<Address>,
1975 signing_keypairs: &S,
1976 ) -> TokenResult<T::Output> {
1977 let signing_pubkeys = signing_keypairs.pubkeys();
1978 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1979
1980 self.process_ixs(
1981 &[metadata_pointer::instruction::update(
1982 &self.program_id,
1983 self.get_address(),
1984 authority,
1985 &multisig_signers,
1986 new_metadata_address,
1987 )?],
1988 signing_keypairs,
1989 )
1990 .await
1991 }
1992
1993 pub async fn update_group_address<S: Signers>(
1995 &self,
1996 authority: &Address,
1997 new_group_address: Option<Address>,
1998 signing_keypairs: &S,
1999 ) -> TokenResult<T::Output> {
2000 let signing_pubkeys = signing_keypairs.pubkeys();
2001 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2002
2003 self.process_ixs(
2004 &[group_pointer::instruction::update(
2005 &self.program_id,
2006 self.get_address(),
2007 authority,
2008 &multisig_signers,
2009 new_group_address,
2010 )?],
2011 signing_keypairs,
2012 )
2013 .await
2014 }
2015
2016 pub async fn update_group_member_address<S: Signers>(
2018 &self,
2019 authority: &Address,
2020 new_member_address: Option<Address>,
2021 signing_keypairs: &S,
2022 ) -> TokenResult<T::Output> {
2023 let signing_pubkeys = signing_keypairs.pubkeys();
2024 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2025
2026 self.process_ixs(
2027 &[group_member_pointer::instruction::update(
2028 &self.program_id,
2029 self.get_address(),
2030 authority,
2031 &multisig_signers,
2032 new_member_address,
2033 )?],
2034 signing_keypairs,
2035 )
2036 .await
2037 }
2038
2039 pub async fn confidential_transfer_update_mint<S: Signers>(
2041 &self,
2042 authority: &Address,
2043 auto_approve_new_account: bool,
2044 auditor_elgamal_pubkey: Option<PodElGamalPubkey>,
2045 signing_keypairs: &S,
2046 ) -> TokenResult<T::Output> {
2047 let signing_pubkeys = signing_keypairs.pubkeys();
2048 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2049
2050 self.process_ixs(
2051 &[confidential_transfer::instruction::update_mint(
2052 &self.program_id,
2053 &self.pubkey,
2054 authority,
2055 &multisig_signers,
2056 auto_approve_new_account,
2057 auditor_elgamal_pubkey,
2058 )?],
2059 signing_keypairs,
2060 )
2061 .await
2062 }
2063
2064 #[allow(clippy::too_many_arguments)]
2068 pub async fn confidential_transfer_configure_token_account<S: Signers>(
2069 &self,
2070 account: &Address,
2071 authority: &Address,
2072 context_state_account: Option<&Address>,
2073 maximum_pending_balance_credit_counter: Option<u64>,
2074 elgamal_keypair: &ElGamalKeypair,
2075 aes_key: &AeKey,
2076 signing_keypairs: &S,
2077 ) -> TokenResult<T::Output> {
2078 const DEFAULT_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER: u64 = 65536;
2079
2080 let signing_pubkeys = signing_keypairs.pubkeys();
2081 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2082
2083 let maximum_pending_balance_credit_counter = maximum_pending_balance_credit_counter
2084 .unwrap_or(DEFAULT_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER);
2085
2086 let proof_data = if context_state_account.is_some() {
2087 None
2088 } else {
2089 Some(
2090 build_pubkey_validity_proof_data(elgamal_keypair)
2091 .map_err(|_| TokenError::ProofGeneration)?,
2092 )
2093 };
2094
2095 let proof_location = Self::confidential_transfer_create_proof_location(
2098 proof_data.as_ref(),
2099 context_state_account,
2100 1,
2101 )
2102 .unwrap();
2103
2104 let decryptable_balance = aes_key.encrypt(0).into();
2105
2106 self.process_ixs(
2107 &confidential_transfer::instruction::configure_account(
2108 &self.program_id,
2109 account,
2110 &self.pubkey,
2111 &decryptable_balance,
2112 maximum_pending_balance_credit_counter,
2113 authority,
2114 &multisig_signers,
2115 proof_location,
2116 )?,
2117 signing_keypairs,
2118 )
2119 .await
2120 }
2121
2122 pub async fn confidential_transfer_configure_token_account_with_registry(
2125 &self,
2126 account: &Address,
2127 elgamal_registry_account: &Address,
2128 payer: Option<&Address>,
2129 ) -> TokenResult<T::Output> {
2130 self.process_ixs::<[&dyn Signer; 0]>(
2131 &[
2132 confidential_transfer::instruction::configure_account_with_registry(
2133 &self.program_id,
2134 account,
2135 &self.pubkey,
2136 elgamal_registry_account,
2137 payer,
2138 )?,
2139 ],
2140 &[],
2141 )
2142 .await
2143 }
2144
2145 pub async fn confidential_transfer_approve_account<S: Signers>(
2147 &self,
2148 account: &Address,
2149 authority: &Address,
2150 signing_keypairs: &S,
2151 ) -> TokenResult<T::Output> {
2152 let signing_pubkeys = signing_keypairs.pubkeys();
2153 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2154
2155 self.process_ixs(
2156 &[confidential_transfer::instruction::approve_account(
2157 &self.program_id,
2158 account,
2159 &self.pubkey,
2160 authority,
2161 &multisig_signers,
2162 )?],
2163 signing_keypairs,
2164 )
2165 .await
2166 }
2167
2168 pub async fn confidential_transfer_empty_account<S: Signers>(
2171 &self,
2172 account: &Address,
2173 authority: &Address,
2174 context_state_account: Option<&Address>,
2175 account_info: Option<EmptyAccountAccountInfo>,
2176 elgamal_keypair: &ElGamalKeypair,
2177 signing_keypairs: &S,
2178 ) -> TokenResult<T::Output> {
2179 let signing_pubkeys = signing_keypairs.pubkeys();
2180 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2181
2182 let account_info = if let Some(account_info) = account_info {
2183 account_info
2184 } else {
2185 let account = self.get_account_info(account).await?;
2186 let confidential_transfer_account =
2187 account.get_extension::<ConfidentialTransferAccount>()?;
2188 EmptyAccountAccountInfo::new(confidential_transfer_account)
2189 };
2190
2191 let proof_data = if context_state_account.is_some() {
2192 None
2193 } else {
2194 Some(
2195 account_info
2196 .generate_proof_data(elgamal_keypair)
2197 .map_err(|_| TokenError::ProofGeneration)?,
2198 )
2199 };
2200
2201 let proof_location = Self::confidential_transfer_create_proof_location(
2204 proof_data.as_ref(),
2205 context_state_account,
2206 1,
2207 )
2208 .unwrap();
2209
2210 self.process_ixs(
2211 &confidential_transfer::instruction::empty_account(
2212 &self.program_id,
2213 account,
2214 authority,
2215 &multisig_signers,
2216 proof_location,
2217 )?,
2218 signing_keypairs,
2219 )
2220 .await
2221 }
2222
2223 pub async fn confidential_transfer_deposit<S: Signers>(
2226 &self,
2227 account: &Address,
2228 authority: &Address,
2229 amount: u64,
2230 decimals: u8,
2231 signing_keypairs: &S,
2232 ) -> TokenResult<T::Output> {
2233 let signing_pubkeys = signing_keypairs.pubkeys();
2234 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2235
2236 self.process_ixs(
2237 &[confidential_transfer::instruction::deposit(
2238 &self.program_id,
2239 account,
2240 &self.pubkey,
2241 amount,
2242 decimals,
2243 authority,
2244 &multisig_signers,
2245 )?],
2246 signing_keypairs,
2247 )
2248 .await
2249 }
2250
2251 #[allow(clippy::too_many_arguments)]
2254 pub async fn confidential_transfer_withdraw<S: Signers>(
2255 &self,
2256 account: &Address,
2257 authority: &Address,
2258 equality_proof_account: Option<&Address>,
2259 range_proof_account: Option<&Address>,
2260 withdraw_amount: u64,
2261 decimals: u8,
2262 account_info: Option<WithdrawAccountInfo>,
2263 elgamal_keypair: &ElGamalKeypair,
2264 aes_key: &AeKey,
2265 signing_keypairs: &S,
2266 ) -> TokenResult<T::Output> {
2267 let signing_pubkeys = signing_keypairs.pubkeys();
2268 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2269
2270 let account_info = if let Some(account_info) = account_info {
2271 account_info
2272 } else {
2273 let account = self.get_account_info(account).await?;
2274 let confidential_transfer_account =
2275 account.get_extension::<ConfidentialTransferAccount>()?;
2276 WithdrawAccountInfo::new(confidential_transfer_account)
2277 };
2278
2279 let (equality_proof_data, range_proof_data) =
2280 if equality_proof_account.is_some() && range_proof_account.is_some() {
2281 (None, None)
2282 } else {
2283 let WithdrawProofData {
2284 equality_proof_data,
2285 range_proof_data,
2286 } = account_info
2287 .generate_proof_data(withdraw_amount, elgamal_keypair, aes_key)
2288 .map_err(|_| TokenError::ProofGeneration)?;
2289
2290 let equality_proof_data = equality_proof_account
2293 .is_none()
2294 .then_some(equality_proof_data);
2295 let range_proof_data = range_proof_account.is_none().then_some(range_proof_data);
2296
2297 (equality_proof_data, range_proof_data)
2298 };
2299
2300 let equality_proof_location = Self::confidential_transfer_create_proof_location(
2303 equality_proof_data.as_ref(),
2304 equality_proof_account,
2305 1,
2306 )
2307 .unwrap();
2308
2309 let range_proof_location = Self::confidential_transfer_create_proof_location(
2310 range_proof_data.as_ref(),
2311 range_proof_account,
2312 2,
2313 )
2314 .unwrap();
2315
2316 let new_decryptable_available_balance = account_info
2317 .new_decryptable_available_balance(withdraw_amount, aes_key)
2318 .map_err(|_| TokenError::AccountDecryption)?
2319 .into();
2320
2321 self.process_ixs(
2322 &confidential_transfer::instruction::withdraw(
2323 &self.program_id,
2324 account,
2325 &self.pubkey,
2326 withdraw_amount,
2327 decimals,
2328 &new_decryptable_available_balance,
2329 authority,
2330 &multisig_signers,
2331 equality_proof_location,
2332 range_proof_location,
2333 )?,
2334 signing_keypairs,
2335 )
2336 .await
2337 }
2338
2339 #[allow(clippy::too_many_arguments)]
2341 pub async fn confidential_transfer_transfer<S: Signers>(
2342 &self,
2343 source_account: &Address,
2344 destination_account: &Address,
2345 source_authority: &Address,
2346 equality_proof_account: Option<&Address>,
2347 ciphertext_validity_proof_account_with_ciphertext: Option<&ProofAccountWithCiphertext>,
2348 range_proof_account: Option<&Address>,
2349 transfer_amount: u64,
2350 account_info: Option<TransferAccountInfo>,
2351 source_elgamal_keypair: &ElGamalKeypair,
2352 source_aes_key: &AeKey,
2353 destination_elgamal_pubkey: &ElGamalPubkey,
2354 auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
2355 signing_keypairs: &S,
2356 ) -> TokenResult<T::Output> {
2357 let signing_pubkeys = signing_keypairs.pubkeys();
2358 let multisig_signers = self.get_multisig_signers(source_authority, &signing_pubkeys);
2359
2360 let account_info = if let Some(account_info) = account_info {
2361 account_info
2362 } else {
2363 let account = self.get_account_info(source_account).await?;
2364 let confidential_transfer_account =
2365 account.get_extension::<ConfidentialTransferAccount>()?;
2366 TransferAccountInfo::new(confidential_transfer_account)
2367 };
2368
2369 let (equality_proof_data, ciphertext_validity_proof_data_with_ciphertext, range_proof_data) =
2370 if equality_proof_account.is_some()
2371 && ciphertext_validity_proof_account_with_ciphertext.is_some()
2372 && range_proof_account.is_some()
2373 {
2374 (None, None, None)
2375 } else {
2376 let TransferProofData {
2377 equality_proof_data,
2378 ciphertext_validity_proof_data_with_ciphertext,
2379 range_proof_data,
2380 } = account_info
2381 .generate_split_transfer_proof_data(
2382 transfer_amount,
2383 source_elgamal_keypair,
2384 source_aes_key,
2385 destination_elgamal_pubkey,
2386 auditor_elgamal_pubkey,
2387 )
2388 .map_err(|_| TokenError::ProofGeneration)?;
2389
2390 let equality_proof_data = equality_proof_account
2393 .is_none()
2394 .then_some(equality_proof_data);
2395 let ciphertext_validity_proof_data_with_ciphertext =
2396 ciphertext_validity_proof_account_with_ciphertext
2397 .is_none()
2398 .then_some(ciphertext_validity_proof_data_with_ciphertext);
2399 let range_proof_data = range_proof_account.is_none().then_some(range_proof_data);
2400
2401 (
2402 equality_proof_data,
2403 ciphertext_validity_proof_data_with_ciphertext,
2404 range_proof_data,
2405 )
2406 };
2407
2408 let (transfer_amount_auditor_ciphertext_lo, transfer_amount_auditor_ciphertext_hi) =
2409 if let Some(proof_data_with_ciphertext) = ciphertext_validity_proof_data_with_ciphertext
2410 {
2411 (
2412 proof_data_with_ciphertext.ciphertext_lo,
2413 proof_data_with_ciphertext.ciphertext_hi,
2414 )
2415 } else {
2416 (
2420 ciphertext_validity_proof_account_with_ciphertext
2421 .unwrap()
2422 .ciphertext_lo,
2423 ciphertext_validity_proof_account_with_ciphertext
2424 .unwrap()
2425 .ciphertext_hi,
2426 )
2427 };
2428
2429 let equality_proof_location = Self::confidential_transfer_create_proof_location(
2432 equality_proof_data.as_ref(),
2433 equality_proof_account,
2434 1,
2435 )
2436 .unwrap();
2437 let ciphertext_validity_proof_data =
2438 ciphertext_validity_proof_data_with_ciphertext.map(|data| data.proof_data);
2439 let ciphertext_validity_proof_location = Self::confidential_transfer_create_proof_location(
2440 ciphertext_validity_proof_data.as_ref(),
2441 ciphertext_validity_proof_account_with_ciphertext
2442 .map(|account| &account.context_state_account),
2443 2,
2444 )
2445 .unwrap();
2446 let range_proof_location = Self::confidential_transfer_create_proof_location(
2447 range_proof_data.as_ref(),
2448 range_proof_account,
2449 3,
2450 )
2451 .unwrap();
2452
2453 let new_decryptable_available_balance = account_info
2454 .new_decryptable_available_balance(transfer_amount, source_aes_key)
2455 .map_err(|_| TokenError::AccountDecryption)?
2456 .into();
2457
2458 let mut instructions = confidential_transfer::instruction::transfer(
2459 &self.program_id,
2460 source_account,
2461 self.get_address(),
2462 destination_account,
2463 &new_decryptable_available_balance,
2464 &transfer_amount_auditor_ciphertext_lo,
2465 &transfer_amount_auditor_ciphertext_hi,
2466 source_authority,
2467 &multisig_signers,
2468 equality_proof_location,
2469 ciphertext_validity_proof_location,
2470 range_proof_location,
2471 )?;
2472 offchain::add_extra_account_metas(
2473 &mut instructions[0],
2474 source_account,
2475 self.get_address(),
2476 destination_account,
2477 source_authority,
2478 u64::MAX,
2479 |address| {
2480 self.client
2481 .get_account(address)
2482 .map_ok(|opt| opt.map(|acc| acc.data))
2483 },
2484 )
2485 .await
2486 .map_err(|_| TokenError::AccountNotFound)?;
2487 self.process_ixs(&instructions, signing_keypairs).await
2488 }
2489
2490 pub async fn confidential_transfer_create_record_account<
2493 S1: Signer,
2494 S2: Signer,
2495 ZK: Pod + ZkProofData<U>,
2496 U: Pod,
2497 >(
2498 &self,
2499 record_account: &Address,
2500 record_authority: &Address,
2501 proof_data: &ZK,
2502 record_account_signer: &S1,
2503 record_authority_signer: &S2,
2504 ) -> TokenResult<Vec<T::Output>> {
2505 let proof_data = bytes_of(proof_data);
2506 let space = proof_data
2507 .len()
2508 .saturating_add(RecordData::WRITABLE_START_INDEX);
2509 let rent = self
2510 .client
2511 .get_minimum_balance_for_rent_exemption(space)
2512 .await
2513 .map_err(TokenError::Client)?;
2514
2515 let create_record_instructions = |first_instruction: bool, bytes: &[u8], offset: u64| {
2519 let mut ixs = vec![];
2520 if first_instruction {
2521 ixs.push(system_instruction::create_account(
2522 &self.payer.pubkey(),
2523 record_account,
2524 rent,
2525 space as u64,
2526 &spl_record::id(),
2527 ));
2528 ixs.push(spl_record::instruction::initialize(
2529 record_account,
2530 record_authority,
2531 ));
2532 }
2533 ixs.push(spl_record::instruction::write(
2534 record_account,
2535 record_authority,
2536 offset,
2537 bytes,
2538 ));
2539 ixs
2540 };
2541 let first_chunk_size = calculate_record_max_chunk_size(create_record_instructions, true);
2542 let (first_chunk, rest) = if space <= first_chunk_size {
2543 (proof_data, &[] as &[u8])
2544 } else {
2545 proof_data.split_at(first_chunk_size)
2546 };
2547
2548 let first_ixs = create_record_instructions(true, first_chunk, 0);
2549 let first_ixs_signers: [&dyn Signer; 2] = [record_account_signer, record_authority_signer];
2550 self.process_ixs(&first_ixs, &first_ixs_signers).await?;
2551
2552 let subsequent_chunk_size =
2553 calculate_record_max_chunk_size(create_record_instructions, false);
2554 let mut record_offset = first_chunk_size;
2555 let mut ixs_batch = vec![];
2556 for chunk in rest.chunks(subsequent_chunk_size) {
2557 ixs_batch.push(create_record_instructions(
2558 false,
2559 chunk,
2560 record_offset as u64,
2561 ));
2562 record_offset = record_offset.saturating_add(chunk.len());
2563 }
2564
2565 let futures = ixs_batch
2566 .into_iter()
2567 .map(|ixs| async move { self.process_ixs(&ixs, &[record_authority_signer]).await })
2568 .collect::<Vec<_>>();
2569
2570 join_all(futures).await.into_iter().collect()
2571 }
2572
2573 pub async fn confidential_transfer_close_record_account<S: Signers>(
2575 &self,
2576 record_account: &Address,
2577 lamport_destination_account: &Address,
2578 record_account_authority: &Address,
2579 signing_keypairs: &S,
2580 ) -> TokenResult<T::Output> {
2581 self.process_ixs(
2582 &[spl_record::instruction::close_account(
2583 record_account,
2584 record_account_authority,
2585 lamport_destination_account,
2586 )],
2587 signing_keypairs,
2588 )
2589 .await
2590 }
2591
2592 pub async fn confidential_transfer_create_context_state_account<
2595 S: Signers,
2596 ZK: Pod + ZkProofData<U>,
2597 U: Pod,
2598 >(
2599 &self,
2600 context_state_account: &Address,
2601 context_state_authority: &Address,
2602 proof_data: &ZK,
2603 split_account_creation_and_proof_verification: bool,
2604 signing_keypairs: &S,
2605 ) -> TokenResult<T::Output> {
2606 let instruction_type = zk_proof_type_to_instruction(ZK::PROOF_TYPE)?;
2607 let space = size_of::<ProofContextState<U>>();
2608 let rent = self
2609 .client
2610 .get_minimum_balance_for_rent_exemption(space)
2611 .await
2612 .map_err(TokenError::Client)?;
2613
2614 let context_state_info = ContextStateInfo {
2615 context_state_account,
2616 context_state_authority,
2617 };
2618
2619 if split_account_creation_and_proof_verification {
2622 self.process_ixs(
2623 &[system_instruction::create_account(
2624 &self.payer.pubkey(),
2625 context_state_account,
2626 rent,
2627 space as u64,
2628 &zk_elgamal_proof_program::id(),
2629 )],
2630 signing_keypairs,
2631 )
2632 .await?;
2633
2634 let blockhash = self
2635 .client
2636 .get_latest_blockhash()
2637 .await
2638 .map_err(TokenError::Client)?;
2639
2640 let transaction = Transaction::new_signed_with_payer(
2641 &[instruction_type.encode_verify_proof(Some(context_state_info), proof_data)],
2642 Some(&self.payer.pubkey()),
2643 &[self.payer.as_ref()],
2644 blockhash,
2645 );
2646
2647 self.client
2648 .send_transaction(&transaction)
2649 .await
2650 .map_err(TokenError::Client)
2651 } else {
2652 self.process_ixs(
2653 &[
2654 system_instruction::create_account(
2655 &self.payer.pubkey(),
2656 context_state_account,
2657 rent,
2658 space as u64,
2659 &zk_elgamal_proof_program::id(),
2660 ),
2661 instruction_type.encode_verify_proof(Some(context_state_info), proof_data),
2662 ],
2663 signing_keypairs,
2664 )
2665 .await
2666 }
2667 }
2668
2669 pub async fn confidential_transfer_create_context_state_account_from_record<
2672 S: Signers,
2673 ZK: Pod + ZkProofData<U>,
2674 U: Pod,
2675 >(
2676 &self,
2677 context_state_account: &Address,
2678 context_state_authority: &Address,
2679 record_account: &Address,
2680 signing_keypairs: &S,
2681 ) -> TokenResult<T::Output> {
2682 const RECORD_ACCOUNT_PROOF_OFFSET: u32 = 33;
2683
2684 let instruction_type = zk_proof_type_to_instruction(ZK::PROOF_TYPE)?;
2685 let space = size_of::<ProofContextState<U>>();
2686 let rent = self
2687 .client
2688 .get_minimum_balance_for_rent_exemption(space)
2689 .await
2690 .map_err(TokenError::Client)?;
2691
2692 let context_state_info = ContextStateInfo {
2693 context_state_account,
2694 context_state_authority,
2695 };
2696
2697 self.process_ixs(
2700 &[
2701 system_instruction::create_account(
2702 &self.payer.pubkey(),
2703 context_state_account,
2704 rent,
2705 space as u64,
2706 &zk_elgamal_proof_program::id(),
2707 ),
2708 instruction_type.encode_verify_proof_from_account(
2709 Some(context_state_info),
2710 record_account,
2711 RECORD_ACCOUNT_PROOF_OFFSET,
2712 ),
2713 ],
2714 signing_keypairs,
2715 )
2716 .await
2717 }
2718
2719 pub async fn confidential_transfer_close_context_state_account<S: Signers>(
2721 &self,
2722 context_state_account: &Address,
2723 lamport_destination_account: &Address,
2724 context_state_authority: &Address,
2725 signing_keypairs: &S,
2726 ) -> TokenResult<T::Output> {
2727 let context_state_info = ContextStateInfo {
2728 context_state_account,
2729 context_state_authority,
2730 };
2731
2732 self.process_ixs(
2733 &[close_context_state(
2734 context_state_info,
2735 lamport_destination_account,
2736 )],
2737 signing_keypairs,
2738 )
2739 .await
2740 }
2741
2742 #[allow(clippy::too_many_arguments)]
2744 pub async fn confidential_transfer_transfer_with_fee<S: Signers>(
2745 &self,
2746 source_account: &Address,
2747 destination_account: &Address,
2748 source_authority: &Address,
2749 equality_proof_account: Option<&Address>,
2750 transfer_amount_ciphertext_validity_proof_account_with_ciphertext: Option<
2751 &ProofAccountWithCiphertext,
2752 >,
2753 percentage_with_cap_proof_account: Option<&Address>,
2754 fee_ciphertext_validity_proof_account: Option<&Address>,
2755 range_proof_account: Option<&Address>,
2756 transfer_amount: u64,
2757 account_info: Option<TransferAccountInfo>,
2758 source_elgamal_keypair: &ElGamalKeypair,
2759 source_aes_key: &AeKey,
2760 destination_elgamal_pubkey: &ElGamalPubkey,
2761 auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
2762 withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey,
2763 fee_rate_basis_points: u16,
2764 maximum_fee: u64,
2765 signing_keypairs: &S,
2766 ) -> TokenResult<T::Output> {
2767 let signing_pubkeys = signing_keypairs.pubkeys();
2768 let multisig_signers = self.get_multisig_signers(source_authority, &signing_pubkeys);
2769
2770 let account_info = if let Some(account_info) = account_info {
2771 account_info
2772 } else {
2773 let account = self.get_account_info(source_account).await?;
2774 let confidential_transfer_account =
2775 account.get_extension::<ConfidentialTransferAccount>()?;
2776 TransferAccountInfo::new(confidential_transfer_account)
2777 };
2778
2779 let (
2780 equality_proof_data,
2781 transfer_amount_ciphertext_validity_proof_data_with_ciphertext,
2782 percentage_with_cap_proof_data,
2783 fee_ciphertext_validity_proof_data,
2784 range_proof_data,
2785 ) = if equality_proof_account.is_some()
2786 && transfer_amount_ciphertext_validity_proof_account_with_ciphertext.is_some()
2787 && percentage_with_cap_proof_account.is_some()
2788 && fee_ciphertext_validity_proof_account.is_some()
2789 && range_proof_account.is_some()
2790 {
2791 (None, None, None, None, None)
2793 } else {
2794 let TransferWithFeeProofData {
2795 equality_proof_data,
2796 transfer_amount_ciphertext_validity_proof_data_with_ciphertext,
2797 percentage_with_cap_proof_data,
2798 fee_ciphertext_validity_proof_data,
2799 range_proof_data,
2800 } = account_info
2801 .generate_split_transfer_with_fee_proof_data(
2802 transfer_amount,
2803 source_elgamal_keypair,
2804 source_aes_key,
2805 destination_elgamal_pubkey,
2806 auditor_elgamal_pubkey,
2807 withdraw_withheld_authority_elgamal_pubkey,
2808 fee_rate_basis_points,
2809 maximum_fee,
2810 )
2811 .map_err(|_| TokenError::ProofGeneration)?;
2812
2813 let equality_proof_data = equality_proof_account
2814 .is_none()
2815 .then_some(equality_proof_data);
2816 let transfer_amount_ciphertext_validity_proof_data_with_ciphertext =
2817 transfer_amount_ciphertext_validity_proof_account_with_ciphertext
2818 .is_none()
2819 .then_some(transfer_amount_ciphertext_validity_proof_data_with_ciphertext);
2820 let percentage_with_cap_proof_data = percentage_with_cap_proof_account
2821 .is_none()
2822 .then_some(percentage_with_cap_proof_data);
2823 let fee_ciphertext_validity_proof_data = fee_ciphertext_validity_proof_account
2824 .is_none()
2825 .then_some(fee_ciphertext_validity_proof_data);
2826 let range_proof_data = range_proof_account.is_none().then_some(range_proof_data);
2827
2828 (
2829 equality_proof_data,
2830 transfer_amount_ciphertext_validity_proof_data_with_ciphertext,
2831 percentage_with_cap_proof_data,
2832 fee_ciphertext_validity_proof_data,
2833 range_proof_data,
2834 )
2835 };
2836
2837 let (transfer_amount_auditor_ciphertext_lo, transfer_amount_auditor_ciphertext_hi) =
2838 if let Some(proof_data_with_ciphertext) =
2839 transfer_amount_ciphertext_validity_proof_data_with_ciphertext
2840 {
2841 (
2842 proof_data_with_ciphertext.ciphertext_lo,
2843 proof_data_with_ciphertext.ciphertext_hi,
2844 )
2845 } else {
2846 (
2850 transfer_amount_ciphertext_validity_proof_account_with_ciphertext
2851 .unwrap()
2852 .ciphertext_lo,
2853 transfer_amount_ciphertext_validity_proof_account_with_ciphertext
2854 .unwrap()
2855 .ciphertext_hi,
2856 )
2857 };
2858
2859 let equality_proof_location = Self::confidential_transfer_create_proof_location(
2862 equality_proof_data.as_ref(),
2863 equality_proof_account,
2864 1,
2865 )
2866 .unwrap();
2867 let transfer_amount_ciphertext_validity_proof_data =
2868 transfer_amount_ciphertext_validity_proof_data_with_ciphertext
2869 .map(|data| data.proof_data);
2870 let transfer_amount_ciphertext_validity_proof_location =
2871 Self::confidential_transfer_create_proof_location(
2872 transfer_amount_ciphertext_validity_proof_data.as_ref(),
2873 transfer_amount_ciphertext_validity_proof_account_with_ciphertext
2874 .map(|account| &account.context_state_account),
2875 2,
2876 )
2877 .unwrap();
2878 let fee_sigma_proof_location = Self::confidential_transfer_create_proof_location(
2879 percentage_with_cap_proof_data.as_ref(),
2880 percentage_with_cap_proof_account,
2881 3,
2882 )
2883 .unwrap();
2884 let fee_ciphertext_validity_proof_location =
2885 Self::confidential_transfer_create_proof_location(
2886 fee_ciphertext_validity_proof_data.as_ref(),
2887 fee_ciphertext_validity_proof_account,
2888 4,
2889 )
2890 .unwrap();
2891 let range_proof_location = Self::confidential_transfer_create_proof_location(
2892 range_proof_data.as_ref(),
2893 range_proof_account,
2894 5,
2895 )
2896 .unwrap();
2897
2898 let new_decryptable_available_balance = account_info
2899 .new_decryptable_available_balance(transfer_amount, source_aes_key)
2900 .map_err(|_| TokenError::AccountDecryption)?
2901 .into();
2902
2903 let mut instructions = confidential_transfer::instruction::transfer_with_fee(
2904 &self.program_id,
2905 source_account,
2906 self.get_address(),
2907 destination_account,
2908 &new_decryptable_available_balance,
2909 &transfer_amount_auditor_ciphertext_lo,
2910 &transfer_amount_auditor_ciphertext_hi,
2911 source_authority,
2912 &multisig_signers,
2913 equality_proof_location,
2914 transfer_amount_ciphertext_validity_proof_location,
2915 fee_sigma_proof_location,
2916 fee_ciphertext_validity_proof_location,
2917 range_proof_location,
2918 )?;
2919 offchain::add_extra_account_metas(
2920 &mut instructions[0],
2921 source_account,
2922 self.get_address(),
2923 destination_account,
2924 source_authority,
2925 u64::MAX,
2926 |address| {
2927 self.client
2928 .get_account(address)
2929 .map_ok(|opt| opt.map(|acc| acc.data))
2930 },
2931 )
2932 .await
2933 .map_err(|_| TokenError::AccountNotFound)?;
2934 self.process_ixs(&instructions, signing_keypairs).await
2935 }
2936
2937 pub async fn confidential_transfer_apply_pending_balance<S: Signers>(
2940 &self,
2941 account: &Address,
2942 authority: &Address,
2943 account_info: Option<ApplyPendingBalanceAccountInfo>,
2944 elgamal_secret_key: &ElGamalSecretKey,
2945 aes_key: &AeKey,
2946 signing_keypairs: &S,
2947 ) -> TokenResult<T::Output> {
2948 let signing_pubkeys = signing_keypairs.pubkeys();
2949 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2950
2951 let account_info = if let Some(account_info) = account_info {
2952 account_info
2953 } else {
2954 let account = self.get_account_info(account).await?;
2955 let confidential_transfer_account =
2956 account.get_extension::<ConfidentialTransferAccount>()?;
2957 ApplyPendingBalanceAccountInfo::new(confidential_transfer_account)
2958 };
2959
2960 let expected_pending_balance_credit_counter = account_info.pending_balance_credit_counter();
2961 let new_decryptable_available_balance = account_info
2962 .new_decryptable_available_balance(elgamal_secret_key, aes_key)
2963 .map_err(|_| TokenError::AccountDecryption)?
2964 .into();
2965
2966 self.process_ixs(
2967 &[confidential_transfer::instruction::apply_pending_balance(
2968 &self.program_id,
2969 account,
2970 expected_pending_balance_credit_counter,
2971 &new_decryptable_available_balance,
2972 authority,
2973 &multisig_signers,
2974 )?],
2975 signing_keypairs,
2976 )
2977 .await
2978 }
2979
2980 pub async fn confidential_transfer_enable_confidential_credits<S: Signers>(
2983 &self,
2984 account: &Address,
2985 authority: &Address,
2986 signing_keypairs: &S,
2987 ) -> TokenResult<T::Output> {
2988 let signing_pubkeys = signing_keypairs.pubkeys();
2989 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
2990
2991 self.process_ixs(
2992 &[
2993 confidential_transfer::instruction::enable_confidential_credits(
2994 &self.program_id,
2995 account,
2996 authority,
2997 &multisig_signers,
2998 )?,
2999 ],
3000 signing_keypairs,
3001 )
3002 .await
3003 }
3004
3005 pub async fn confidential_transfer_disable_confidential_credits<S: Signers>(
3008 &self,
3009 account: &Address,
3010 authority: &Address,
3011 signing_keypairs: &S,
3012 ) -> TokenResult<T::Output> {
3013 let signing_pubkeys = signing_keypairs.pubkeys();
3014 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3015
3016 self.process_ixs(
3017 &[
3018 confidential_transfer::instruction::disable_confidential_credits(
3019 &self.program_id,
3020 account,
3021 authority,
3022 &multisig_signers,
3023 )?,
3024 ],
3025 signing_keypairs,
3026 )
3027 .await
3028 }
3029
3030 pub async fn confidential_transfer_enable_non_confidential_credits<S: Signers>(
3033 &self,
3034 account: &Address,
3035 authority: &Address,
3036 signing_keypairs: &S,
3037 ) -> TokenResult<T::Output> {
3038 let signing_pubkeys = signing_keypairs.pubkeys();
3039 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3040
3041 self.process_ixs(
3042 &[
3043 confidential_transfer::instruction::enable_non_confidential_credits(
3044 &self.program_id,
3045 account,
3046 authority,
3047 &multisig_signers,
3048 )?,
3049 ],
3050 signing_keypairs,
3051 )
3052 .await
3053 }
3054
3055 pub async fn confidential_transfer_disable_non_confidential_credits<S: Signers>(
3058 &self,
3059 account: &Address,
3060 authority: &Address,
3061 signing_keypairs: &S,
3062 ) -> TokenResult<T::Output> {
3063 let signing_pubkeys = signing_keypairs.pubkeys();
3064 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3065
3066 self.process_ixs(
3067 &[
3068 confidential_transfer::instruction::disable_non_confidential_credits(
3069 &self.program_id,
3070 account,
3071 authority,
3072 &multisig_signers,
3073 )?,
3074 ],
3075 signing_keypairs,
3076 )
3077 .await
3078 }
3079
3080 #[allow(clippy::too_many_arguments)]
3082 pub async fn confidential_transfer_withdraw_withheld_tokens_from_mint<S: Signers>(
3083 &self,
3084 destination_account: &Address,
3085 withdraw_withheld_authority: &Address,
3086 context_state_account: Option<&Address>,
3087 withheld_tokens_info: Option<WithheldTokensInfo>,
3088 withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair,
3089 destination_elgamal_pubkey: &ElGamalPubkey,
3090 new_decryptable_available_balance: &DecryptableBalance,
3091 signing_keypairs: &S,
3092 ) -> TokenResult<T::Output> {
3093 let signing_pubkeys = signing_keypairs.pubkeys();
3094 let multisig_signers =
3095 self.get_multisig_signers(withdraw_withheld_authority, &signing_pubkeys);
3096
3097 let account_info = if let Some(account_info) = withheld_tokens_info {
3098 account_info
3099 } else {
3100 let mint_info = self.get_mint_info().await?;
3101 let confidential_transfer_fee_config =
3102 mint_info.get_extension::<ConfidentialTransferFeeConfig>()?;
3103 WithheldTokensInfo::new(&confidential_transfer_fee_config.withheld_amount)
3104 };
3105
3106 let proof_data = if context_state_account.is_some() {
3107 None
3108 } else {
3109 Some(
3110 account_info
3111 .generate_proof_data(
3112 withdraw_withheld_authority_elgamal_keypair,
3113 destination_elgamal_pubkey,
3114 )
3115 .map_err(|_| TokenError::ProofGeneration)?,
3116 )
3117 };
3118
3119 let proof_location = Self::confidential_transfer_create_proof_location(
3122 proof_data.as_ref(),
3123 context_state_account,
3124 1,
3125 )
3126 .unwrap();
3127
3128 self.process_ixs(
3129 &confidential_transfer_fee::instruction::withdraw_withheld_tokens_from_mint(
3130 &self.program_id,
3131 &self.pubkey,
3132 destination_account,
3133 new_decryptable_available_balance,
3134 withdraw_withheld_authority,
3135 &multisig_signers,
3136 proof_location,
3137 )?,
3138 signing_keypairs,
3139 )
3140 .await
3141 }
3142
3143 #[allow(clippy::too_many_arguments)]
3145 pub async fn confidential_transfer_withdraw_withheld_tokens_from_accounts<S: Signers>(
3146 &self,
3147 destination_account: &Address,
3148 withdraw_withheld_authority: &Address,
3149 context_state_account: Option<&Address>,
3150 withheld_tokens_info: Option<WithheldTokensInfo>,
3151 withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair,
3152 destination_elgamal_pubkey: &ElGamalPubkey,
3153 new_decryptable_available_balance: &DecryptableBalance,
3154 sources: &[&Address],
3155 signing_keypairs: &S,
3156 ) -> TokenResult<T::Output> {
3157 let signing_pubkeys = signing_keypairs.pubkeys();
3158 let multisig_signers =
3159 self.get_multisig_signers(withdraw_withheld_authority, &signing_pubkeys);
3160
3161 let account_info = if let Some(account_info) = withheld_tokens_info {
3162 account_info
3163 } else {
3164 let futures = sources.iter().map(|source| self.get_account_info(source));
3165 let sources_extensions = join_all(futures).await;
3166
3167 let mut aggregate_withheld_amount = ElGamalCiphertext::default();
3168 for source_extension in sources_extensions {
3169 let withheld_amount: ElGamalCiphertext = source_extension?
3170 .get_extension::<ConfidentialTransferFeeAmount>()?
3171 .withheld_amount
3172 .try_into()
3173 .map_err(|_| TokenError::AccountDecryption)?;
3174 aggregate_withheld_amount = aggregate_withheld_amount + withheld_amount;
3175 }
3176
3177 WithheldTokensInfo::new(&aggregate_withheld_amount.into())
3178 };
3179
3180 let proof_data = if context_state_account.is_some() {
3181 None
3182 } else {
3183 Some(
3184 account_info
3185 .generate_proof_data(
3186 withdraw_withheld_authority_elgamal_keypair,
3187 destination_elgamal_pubkey,
3188 )
3189 .map_err(|_| TokenError::ProofGeneration)?,
3190 )
3191 };
3192
3193 let proof_location = Self::confidential_transfer_create_proof_location(
3196 proof_data.as_ref(),
3197 context_state_account,
3198 1,
3199 )
3200 .unwrap();
3201
3202 self.process_ixs(
3203 &confidential_transfer_fee::instruction::withdraw_withheld_tokens_from_accounts(
3204 &self.program_id,
3205 &self.pubkey,
3206 destination_account,
3207 new_decryptable_available_balance,
3208 withdraw_withheld_authority,
3209 &multisig_signers,
3210 sources,
3211 proof_location,
3212 )?,
3213 signing_keypairs,
3214 )
3215 .await
3216 }
3217
3218 pub async fn confidential_transfer_harvest_withheld_tokens_to_mint(
3220 &self,
3221 sources: &[&Address],
3222 ) -> TokenResult<T::Output> {
3223 self.process_ixs::<[&dyn Signer; 0]>(
3224 &[
3225 confidential_transfer_fee::instruction::harvest_withheld_tokens_to_mint(
3226 &self.program_id,
3227 &self.pubkey,
3228 sources,
3229 )?,
3230 ],
3231 &[],
3232 )
3233 .await
3234 }
3235
3236 pub async fn confidential_transfer_enable_harvest_to_mint<S: Signers>(
3238 &self,
3239 withdraw_withheld_authority: &Address,
3240 signing_keypairs: &S,
3241 ) -> TokenResult<T::Output> {
3242 let signing_pubkeys = signing_keypairs.pubkeys();
3243 let multisig_signers =
3244 self.get_multisig_signers(withdraw_withheld_authority, &signing_pubkeys);
3245
3246 self.process_ixs(
3247 &[
3248 confidential_transfer_fee::instruction::enable_harvest_to_mint(
3249 &self.program_id,
3250 &self.pubkey,
3251 withdraw_withheld_authority,
3252 &multisig_signers,
3253 )?,
3254 ],
3255 signing_keypairs,
3256 )
3257 .await
3258 }
3259
3260 pub async fn confidential_transfer_disable_harvest_to_mint<S: Signers>(
3262 &self,
3263 withdraw_withheld_authority: &Address,
3264 signing_keypairs: &S,
3265 ) -> TokenResult<T::Output> {
3266 let signing_pubkeys = signing_keypairs.pubkeys();
3267 let multisig_signers =
3268 self.get_multisig_signers(withdraw_withheld_authority, &signing_pubkeys);
3269
3270 self.process_ixs(
3271 &[
3272 confidential_transfer_fee::instruction::disable_harvest_to_mint(
3273 &self.program_id,
3274 &self.pubkey,
3275 withdraw_withheld_authority,
3276 &multisig_signers,
3277 )?,
3278 ],
3279 signing_keypairs,
3280 )
3281 .await
3282 }
3283
3284 #[allow(clippy::too_many_arguments)]
3286 pub async fn confidential_transfer_rotate_supply_elgamal_pubkey<S: Signers>(
3287 &self,
3288 authority: &Address,
3289 current_supply_elgamal_keypair: &ElGamalKeypair,
3290 new_supply_elgamal_pubkey: &ElGamalPubkey,
3291 aes_key: &AeKey,
3292 context_state_account: Option<&Address>,
3293 account_info: Option<SupplyAccountInfo>,
3294 signing_keypairs: &S,
3295 ) -> TokenResult<T::Output> {
3296 let signing_pubkeys = signing_keypairs.pubkeys();
3297 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3298
3299 let account_info = if let Some(account_info) = account_info {
3300 account_info
3301 } else {
3302 let account = self.get_mint_info().await?;
3303 let confidential_supply_account = account.get_extension::<ConfidentialMintBurn>()?;
3304 SupplyAccountInfo::new(confidential_supply_account)
3305 };
3306
3307 let proof_data = if context_state_account.is_some() {
3308 None
3309 } else {
3310 Some(
3311 account_info
3312 .generate_rotate_supply_elgamal_pubkey_proof(
3313 current_supply_elgamal_keypair,
3314 new_supply_elgamal_pubkey,
3315 aes_key,
3316 )
3317 .map_err(|_| TokenError::ProofGeneration)?,
3318 )
3319 };
3320
3321 let proof_location = Self::confidential_transfer_create_proof_location(
3324 proof_data.as_ref(),
3325 context_state_account,
3326 1,
3327 )
3328 .unwrap();
3329
3330 let new_supply_elgamal_pubkey = (*new_supply_elgamal_pubkey).into();
3331 self.process_ixs(
3332 &confidential_mint_burn::instruction::rotate_supply_elgamal_pubkey(
3333 &self.program_id,
3334 &self.pubkey,
3335 authority,
3336 &multisig_signers,
3337 &new_supply_elgamal_pubkey,
3338 proof_location,
3339 )?,
3340 signing_keypairs,
3341 )
3342 .await
3343 }
3344
3345 pub async fn confidential_transfer_update_decrypt_supply<S: Signers>(
3347 &self,
3348 authority: &Address,
3349 new_decryptable_supply: &DecryptableBalance,
3350 signing_keypairs: &S,
3351 ) -> TokenResult<T::Output> {
3352 let signing_pubkeys = signing_keypairs.pubkeys();
3353 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3354
3355 self.process_ixs(
3356 &[
3357 confidential_mint_burn::instruction::update_decryptable_supply(
3358 &self.program_id,
3359 &self.pubkey,
3360 authority,
3361 &multisig_signers,
3362 new_decryptable_supply,
3363 )?,
3364 ],
3365 signing_keypairs,
3366 )
3367 .await
3368 }
3369
3370 #[allow(clippy::too_many_arguments)]
3372 pub async fn confidential_transfer_mint<S: Signers>(
3373 &self,
3374 authority: &Address,
3375 destination_account: &Address,
3376 equality_proof_account: Option<&Address>,
3377 ciphertext_validity_proof_account_with_ciphertext: Option<&ProofAccountWithCiphertext>,
3378 range_proof_account: Option<&Address>,
3379 mint_amount: u64,
3380 supply_elgamal_keypair: &ElGamalKeypair,
3381 destination_elgamal_pubkey: &ElGamalPubkey,
3382 auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
3383 aes_key: &AeKey,
3384 account_info: Option<SupplyAccountInfo>,
3385 signing_keypairs: &S,
3386 ) -> TokenResult<T::Output> {
3387 let signing_pubkeys = signing_keypairs.pubkeys();
3388 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3389
3390 let account_info = if let Some(account_info) = account_info {
3391 account_info
3392 } else {
3393 let account = self.get_mint_info().await?;
3394 let confidential_supply_account = account.get_extension::<ConfidentialMintBurn>()?;
3395 SupplyAccountInfo::new(confidential_supply_account)
3396 };
3397
3398 let (equality_proof_data, ciphertext_validity_proof_data_with_ciphertext, range_proof_data) =
3399 if equality_proof_account.is_some()
3400 && ciphertext_validity_proof_account_with_ciphertext.is_some()
3401 && range_proof_account.is_some()
3402 {
3403 (None, None, None)
3405 } else {
3406 let MintProofData {
3407 equality_proof_data,
3408 ciphertext_validity_proof_data_with_ciphertext,
3409 range_proof_data,
3410 } = account_info
3411 .generate_split_mint_proof_data(
3412 mint_amount,
3413 supply_elgamal_keypair,
3414 aes_key,
3415 destination_elgamal_pubkey,
3416 auditor_elgamal_pubkey,
3417 )
3418 .map_err(|_| TokenError::ProofGeneration)?;
3419
3420 let equality_proof_data = equality_proof_account
3421 .is_none()
3422 .then_some(equality_proof_data);
3423 let ciphertext_validity_proof_data_with_ciphertext =
3424 ciphertext_validity_proof_account_with_ciphertext
3425 .is_none()
3426 .then_some(ciphertext_validity_proof_data_with_ciphertext);
3427 let range_proof_data = range_proof_account.is_none().then_some(range_proof_data);
3428
3429 (
3430 equality_proof_data,
3431 ciphertext_validity_proof_data_with_ciphertext,
3432 range_proof_data,
3433 )
3434 };
3435
3436 let equality_proof_location = Self::confidential_transfer_create_proof_location(
3439 equality_proof_data.as_ref(),
3440 equality_proof_account,
3441 1,
3442 )
3443 .unwrap();
3444 let ciphertext_validity_proof_data =
3445 ciphertext_validity_proof_data_with_ciphertext.map(|data| data.proof_data);
3446 let ciphertext_validity_proof_location = Self::confidential_transfer_create_proof_location(
3447 ciphertext_validity_proof_data.as_ref(),
3448 ciphertext_validity_proof_account_with_ciphertext
3449 .map(|account| &account.context_state_account),
3450 2,
3451 )
3452 .unwrap();
3453 let range_proof_location = Self::confidential_transfer_create_proof_location(
3454 range_proof_data.as_ref(),
3455 range_proof_account,
3456 3,
3457 )
3458 .unwrap();
3459
3460 let (mint_amount_auditor_ciphertext_lo, mint_amount_auditor_ciphertext_hi) = if let Some(
3461 proof_data_with_ciphertext,
3462 ) =
3463 ciphertext_validity_proof_data_with_ciphertext
3464 {
3465 (
3466 proof_data_with_ciphertext.ciphertext_lo,
3467 proof_data_with_ciphertext.ciphertext_hi,
3468 )
3469 } else {
3470 (
3474 ciphertext_validity_proof_account_with_ciphertext
3475 .unwrap()
3476 .ciphertext_lo,
3477 ciphertext_validity_proof_account_with_ciphertext
3478 .unwrap()
3479 .ciphertext_hi,
3480 )
3481 };
3482
3483 let new_decryptable_supply = account_info
3484 .new_decryptable_supply(mint_amount, supply_elgamal_keypair, aes_key)
3485 .map_err(|_| TokenError::AccountDecryption)?
3486 .into();
3487
3488 self.process_ixs(
3489 &confidential_mint_burn::instruction::confidential_mint_with_split_proofs(
3490 &self.program_id,
3491 destination_account,
3492 &self.pubkey,
3493 &mint_amount_auditor_ciphertext_lo,
3494 &mint_amount_auditor_ciphertext_hi,
3495 authority,
3496 &multisig_signers,
3497 equality_proof_location,
3498 ciphertext_validity_proof_location,
3499 range_proof_location,
3500 &new_decryptable_supply,
3501 )?,
3502 signing_keypairs,
3503 )
3504 .await
3505 }
3506
3507 #[allow(clippy::too_many_arguments)]
3509 pub async fn confidential_transfer_permissioned_burn<S: Signers>(
3510 &self,
3511 authority: &Address,
3512 source_account: &Address,
3513 permissioned_burn_authority: &Address,
3514 equality_proof_account: Option<&Address>,
3515 ciphertext_validity_proof_account_with_ciphertext: Option<&ProofAccountWithCiphertext>,
3516 range_proof_account: Option<&Address>,
3517 burn_amount: u64,
3518 source_elgamal_keypair: &ElGamalKeypair,
3519 supply_elgamal_pubkey: &ElGamalPubkey,
3520 auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
3521 aes_key: &AeKey,
3522 account_info: Option<BurnAccountInfo>,
3523 signing_keypairs: &S,
3524 ) -> TokenResult<T::Output> {
3525 self.confidential_transfer_burn_inner(
3526 authority,
3527 source_account,
3528 Some(permissioned_burn_authority),
3529 equality_proof_account,
3530 ciphertext_validity_proof_account_with_ciphertext,
3531 range_proof_account,
3532 burn_amount,
3533 source_elgamal_keypair,
3534 supply_elgamal_pubkey,
3535 auditor_elgamal_pubkey,
3536 aes_key,
3537 account_info,
3538 signing_keypairs,
3539 )
3540 .await
3541 }
3542
3543 #[allow(clippy::too_many_arguments)]
3545 pub async fn confidential_transfer_burn<S: Signers>(
3546 &self,
3547 authority: &Address,
3548 source_account: &Address,
3549 equality_proof_account: Option<&Address>,
3550 ciphertext_validity_proof_account_with_ciphertext: Option<&ProofAccountWithCiphertext>,
3551 range_proof_account: Option<&Address>,
3552 burn_amount: u64,
3553 source_elgamal_keypair: &ElGamalKeypair,
3554 supply_elgamal_pubkey: &ElGamalPubkey,
3555 auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
3556 aes_key: &AeKey,
3557 account_info: Option<BurnAccountInfo>,
3558 signing_keypairs: &S,
3559 ) -> TokenResult<T::Output> {
3560 self.confidential_transfer_burn_inner(
3561 authority,
3562 source_account,
3563 None,
3564 equality_proof_account,
3565 ciphertext_validity_proof_account_with_ciphertext,
3566 range_proof_account,
3567 burn_amount,
3568 source_elgamal_keypair,
3569 supply_elgamal_pubkey,
3570 auditor_elgamal_pubkey,
3571 aes_key,
3572 account_info,
3573 signing_keypairs,
3574 )
3575 .await
3576 }
3577
3578 #[allow(clippy::too_many_arguments)]
3580 async fn confidential_transfer_burn_inner<S: Signers>(
3581 &self,
3582 authority: &Address,
3583 source_account: &Address,
3584 maybe_permissioned_burn_authority: Option<&Address>,
3585 equality_proof_account: Option<&Address>,
3586 ciphertext_validity_proof_account_with_ciphertext: Option<&ProofAccountWithCiphertext>,
3587 range_proof_account: Option<&Address>,
3588 burn_amount: u64,
3589 source_elgamal_keypair: &ElGamalKeypair,
3590 supply_elgamal_pubkey: &ElGamalPubkey,
3591 auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
3592 aes_key: &AeKey,
3593 account_info: Option<BurnAccountInfo>,
3594 signing_keypairs: &S,
3595 ) -> TokenResult<T::Output> {
3596 let signing_pubkeys = signing_keypairs.pubkeys();
3597 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3598
3599 let account_info = if let Some(account_info) = account_info {
3600 account_info
3601 } else {
3602 let account = self.get_account_info(source_account).await?;
3603 let confidential_supply_account =
3604 account.get_extension::<ConfidentialTransferAccount>()?;
3605 BurnAccountInfo::new(confidential_supply_account)
3606 };
3607
3608 let (equality_proof_data, ciphertext_validity_proof_data_with_ciphertext, range_proof_data) =
3609 if equality_proof_account.is_some()
3610 && ciphertext_validity_proof_account_with_ciphertext.is_some()
3611 && range_proof_account.is_some()
3612 {
3613 (None, None, None)
3615 } else {
3616 let BurnProofData {
3617 equality_proof_data,
3618 ciphertext_validity_proof_data_with_ciphertext,
3619 range_proof_data,
3620 } = account_info
3621 .generate_split_burn_proof_data(
3622 burn_amount,
3623 source_elgamal_keypair,
3624 aes_key,
3625 supply_elgamal_pubkey,
3626 auditor_elgamal_pubkey,
3627 )
3628 .map_err(|_| TokenError::ProofGeneration)?;
3629
3630 let equality_proof_data = equality_proof_account
3631 .is_none()
3632 .then_some(equality_proof_data);
3633 let ciphertext_validity_proof_data_with_ciphertext =
3634 ciphertext_validity_proof_account_with_ciphertext
3635 .is_none()
3636 .then_some(ciphertext_validity_proof_data_with_ciphertext);
3637 let range_proof_data = range_proof_account.is_none().then_some(range_proof_data);
3638
3639 (
3640 equality_proof_data,
3641 ciphertext_validity_proof_data_with_ciphertext,
3642 range_proof_data,
3643 )
3644 };
3645
3646 let equality_proof_location = Self::confidential_transfer_create_proof_location(
3649 equality_proof_data.as_ref(),
3650 equality_proof_account,
3651 1,
3652 )
3653 .unwrap();
3654 let ciphertext_validity_proof_data =
3655 ciphertext_validity_proof_data_with_ciphertext.map(|data| data.proof_data);
3656 let ciphertext_validity_proof_location = Self::confidential_transfer_create_proof_location(
3657 ciphertext_validity_proof_data.as_ref(),
3658 ciphertext_validity_proof_account_with_ciphertext
3659 .map(|account| &account.context_state_account),
3660 2,
3661 )
3662 .unwrap();
3663 let range_proof_location = Self::confidential_transfer_create_proof_location(
3664 range_proof_data.as_ref(),
3665 range_proof_account,
3666 3,
3667 )
3668 .unwrap();
3669
3670 let (burn_amount_auditor_ciphertext_lo, burn_amount_auditor_ciphertext_hi) = if let Some(
3671 proof_data_with_ciphertext,
3672 ) =
3673 ciphertext_validity_proof_data_with_ciphertext
3674 {
3675 (
3676 proof_data_with_ciphertext.ciphertext_lo,
3677 proof_data_with_ciphertext.ciphertext_hi,
3678 )
3679 } else {
3680 (
3684 ciphertext_validity_proof_account_with_ciphertext
3685 .unwrap()
3686 .ciphertext_lo,
3687 ciphertext_validity_proof_account_with_ciphertext
3688 .unwrap()
3689 .ciphertext_hi,
3690 )
3691 };
3692
3693 let new_decryptable_balance = account_info
3694 .new_decryptable_balance(burn_amount, aes_key)
3695 .map_err(|_| TokenError::AccountDecryption)?
3696 .into();
3697
3698 let instructions =
3699 if let Some(permissioned_burn_authority) = maybe_permissioned_burn_authority {
3700 permissioned_burn::instruction::confidential_burn_with_split_proofs(
3701 &self.program_id,
3702 source_account,
3703 &self.pubkey,
3704 permissioned_burn_authority,
3705 &new_decryptable_balance,
3706 &burn_amount_auditor_ciphertext_lo,
3707 &burn_amount_auditor_ciphertext_hi,
3708 authority,
3709 &multisig_signers,
3710 equality_proof_location,
3711 ciphertext_validity_proof_location,
3712 range_proof_location,
3713 )?
3714 } else {
3715 confidential_mint_burn::instruction::confidential_burn_with_split_proofs(
3716 &self.program_id,
3717 source_account,
3718 &self.pubkey,
3719 &new_decryptable_balance,
3720 &burn_amount_auditor_ciphertext_lo,
3721 &burn_amount_auditor_ciphertext_hi,
3722 authority,
3723 &multisig_signers,
3724 equality_proof_location,
3725 ciphertext_validity_proof_location,
3726 range_proof_location,
3727 )?
3728 };
3729 self.process_ixs(&instructions, signing_keypairs).await
3730 }
3731
3732 pub async fn confidential_transfer_apply_pending_burn<S: Signers>(
3734 &self,
3735 authority: &Address,
3736 signing_keypairs: &S,
3737 ) -> TokenResult<T::Output> {
3738 let signing_pubkeys = signing_keypairs.pubkeys();
3739 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3740
3741 self.process_ixs(
3742 &[confidential_mint_burn::instruction::apply_pending_burn(
3743 &self.program_id,
3744 &self.pubkey,
3745 authority,
3746 &multisig_signers,
3747 )?],
3748 signing_keypairs,
3749 )
3750 .await
3751 }
3752
3753 fn confidential_transfer_create_proof_location<'a, ZK: ZkProofData<U>, U: Pod>(
3756 proof_data: Option<&'a ZK>,
3757 context_account: Option<&'a Address>,
3758 instruction_offset: i8,
3759 ) -> Option<ProofLocation<'a, ZK>> {
3760 if let Some(proof_data) = proof_data {
3761 Some(ProofLocation::InstructionOffset(
3762 instruction_offset.try_into().unwrap(),
3763 proof_data,
3764 ))
3765 } else {
3766 context_account.map(ProofLocation::ContextStateAccount)
3767 }
3768 }
3769
3770 pub async fn withdraw_excess_lamports<S: Signers>(
3771 &self,
3772 source: &Address,
3773 destination: &Address,
3774 authority: &Address,
3775 signing_keypairs: &S,
3776 ) -> TokenResult<T::Output> {
3777 let signing_pubkeys = signing_keypairs.pubkeys();
3778 let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
3779
3780 self.process_ixs(
3781 &[
3782 spl_token_2022_interface::instruction::withdraw_excess_lamports(
3783 &self.program_id,
3784 source,
3785 destination,
3786 authority,
3787 &multisig_signers,
3788 )?,
3789 ],
3790 signing_keypairs,
3791 )
3792 .await
3793 }
3794
3795 pub async fn token_metadata_initialize<S: Signers>(
3797 &self,
3798 update_authority: &Address,
3799 mint_authority: &Address,
3800 name: String,
3801 symbol: String,
3802 uri: String,
3803 signing_keypairs: &S,
3804 ) -> TokenResult<T::Output> {
3805 self.process_ixs(
3806 &[spl_token_metadata_interface::instruction::initialize(
3807 &self.program_id,
3808 &self.pubkey,
3809 update_authority,
3810 &self.pubkey,
3811 mint_authority,
3812 name,
3813 symbol,
3814 uri,
3815 )],
3816 signing_keypairs,
3817 )
3818 .await
3819 }
3820
3821 async fn get_additional_rent_for_new_metadata(
3822 &self,
3823 token_metadata: &TokenMetadata,
3824 ) -> TokenResult<u64> {
3825 let account = self.get_account(self.pubkey).await?;
3826 let account_lamports = account.lamports;
3827 let mint_state = self.unpack_mint_info(account)?;
3828 let new_account_len = mint_state
3829 .try_get_new_account_len_for_variable_len_extension::<TokenMetadata>(token_metadata)?;
3830 let new_rent_exempt_minimum = self
3831 .client
3832 .get_minimum_balance_for_rent_exemption(new_account_len)
3833 .await
3834 .map_err(TokenError::Client)?;
3835 Ok(new_rent_exempt_minimum.saturating_sub(account_lamports))
3836 }
3837
3838 #[allow(clippy::too_many_arguments)]
3840 pub async fn token_metadata_initialize_with_rent_transfer<S: Signers>(
3841 &self,
3842 payer: &Address,
3843 update_authority: &Address,
3844 mint_authority: &Address,
3845 name: String,
3846 symbol: String,
3847 uri: String,
3848 signing_keypairs: &S,
3849 ) -> TokenResult<T::Output> {
3850 let token_metadata = TokenMetadata {
3851 name,
3852 symbol,
3853 uri,
3854 ..Default::default()
3855 };
3856 let additional_lamports = self
3857 .get_additional_rent_for_new_metadata(&token_metadata)
3858 .await?;
3859 let mut instructions = vec![];
3860 if additional_lamports > 0 {
3861 instructions.push(system_instruction::transfer(
3862 payer,
3863 &self.pubkey,
3864 additional_lamports,
3865 ));
3866 }
3867 instructions.push(spl_token_metadata_interface::instruction::initialize(
3868 &self.program_id,
3869 &self.pubkey,
3870 update_authority,
3871 &self.pubkey,
3872 mint_authority,
3873 token_metadata.name,
3874 token_metadata.symbol,
3875 token_metadata.uri,
3876 ));
3877 self.process_ixs(&instructions, signing_keypairs).await
3878 }
3879
3880 pub async fn token_metadata_update_field<S: Signers>(
3882 &self,
3883 update_authority: &Address,
3884 field: Field,
3885 value: String,
3886 signing_keypairs: &S,
3887 ) -> TokenResult<T::Output> {
3888 self.process_ixs(
3889 &[spl_token_metadata_interface::instruction::update_field(
3890 &self.program_id,
3891 &self.pubkey,
3892 update_authority,
3893 field,
3894 value,
3895 )],
3896 signing_keypairs,
3897 )
3898 .await
3899 }
3900
3901 async fn get_additional_rent_for_updated_metadata(
3902 &self,
3903 field: Field,
3904 value: String,
3905 ) -> TokenResult<u64> {
3906 let account = self.get_account(self.pubkey).await?;
3907 let account_lamports = account.lamports;
3908 let mint_state = self.unpack_mint_info(account)?;
3909 let mut token_metadata = mint_state.get_variable_len_extension::<TokenMetadata>()?;
3910 token_metadata.update(field, value);
3911 let new_account_len = mint_state
3912 .try_get_new_account_len_for_variable_len_extension::<TokenMetadata>(&token_metadata)?;
3913 let new_rent_exempt_minimum = self
3914 .client
3915 .get_minimum_balance_for_rent_exemption(new_account_len)
3916 .await
3917 .map_err(TokenError::Client)?;
3918 Ok(new_rent_exempt_minimum.saturating_sub(account_lamports))
3919 }
3920
3921 #[allow(clippy::too_many_arguments)]
3924 pub async fn token_metadata_update_field_with_rent_transfer<S: Signers>(
3925 &self,
3926 payer: &Address,
3927 update_authority: &Address,
3928 field: Field,
3929 value: String,
3930 transfer_lamports: Option<u64>,
3931 signing_keypairs: &S,
3932 ) -> TokenResult<T::Output> {
3933 let additional_lamports = if let Some(transfer_lamports) = transfer_lamports {
3934 transfer_lamports
3935 } else {
3936 self.get_additional_rent_for_updated_metadata(field.clone(), value.clone())
3937 .await?
3938 };
3939 let mut instructions = vec![];
3940 if additional_lamports > 0 {
3941 instructions.push(system_instruction::transfer(
3942 payer,
3943 &self.pubkey,
3944 additional_lamports,
3945 ));
3946 }
3947 instructions.push(spl_token_metadata_interface::instruction::update_field(
3948 &self.program_id,
3949 &self.pubkey,
3950 update_authority,
3951 field,
3952 value,
3953 ));
3954 self.process_ixs(&instructions, signing_keypairs).await
3955 }
3956
3957 pub async fn token_metadata_update_authority<S: Signers>(
3959 &self,
3960 current_authority: &Address,
3961 new_authority: Option<Address>,
3962 signing_keypairs: &S,
3963 ) -> TokenResult<T::Output> {
3964 self.process_ixs(
3965 &[spl_token_metadata_interface::instruction::update_authority(
3966 &self.program_id,
3967 &self.pubkey,
3968 current_authority,
3969 MaybeNull::try_from(new_authority).map_err(|_| ProgramError::InvalidArgument)?,
3970 )],
3971 signing_keypairs,
3972 )
3973 .await
3974 }
3975
3976 pub async fn token_metadata_remove_key<S: Signers>(
3978 &self,
3979 update_authority: &Address,
3980 key: String,
3981 idempotent: bool,
3982 signing_keypairs: &S,
3983 ) -> TokenResult<T::Output> {
3984 self.process_ixs(
3985 &[spl_token_metadata_interface::instruction::remove_key(
3986 &self.program_id,
3987 &self.pubkey,
3988 update_authority,
3989 key,
3990 idempotent,
3991 )],
3992 signing_keypairs,
3993 )
3994 .await
3995 }
3996
3997 pub async fn token_group_initialize<S: Signers>(
3999 &self,
4000 mint_authority: &Address,
4001 update_authority: &Address,
4002 max_size: u64,
4003 signing_keypairs: &S,
4004 ) -> TokenResult<T::Output> {
4005 self.process_ixs(
4006 &[spl_token_group_interface::instruction::initialize_group(
4007 &self.program_id,
4008 &self.pubkey,
4009 &self.pubkey,
4010 mint_authority,
4011 Some(*update_authority),
4012 max_size,
4013 )],
4014 signing_keypairs,
4015 )
4016 .await
4017 }
4018
4019 async fn get_additional_rent_for_fixed_len_extension<V: Extension + Pod>(
4020 &self,
4021 ) -> TokenResult<u64> {
4022 let account = self.get_account(self.pubkey).await?;
4023 let account_lamports = account.lamports;
4024 let mint_state = self.unpack_mint_info(account)?;
4025 if mint_state.get_extension::<V>().is_ok() {
4026 Ok(0)
4027 } else {
4028 let new_account_len = mint_state.try_get_new_account_len::<V>()?;
4029 let new_rent_exempt_minimum = self
4030 .client
4031 .get_minimum_balance_for_rent_exemption(new_account_len)
4032 .await
4033 .map_err(TokenError::Client)?;
4034 Ok(new_rent_exempt_minimum.saturating_sub(account_lamports))
4035 }
4036 }
4037
4038 pub async fn token_group_initialize_with_rent_transfer<S: Signers>(
4040 &self,
4041 payer: &Address,
4042 mint_authority: &Address,
4043 update_authority: &Address,
4044 max_size: u64,
4045 signing_keypairs: &S,
4046 ) -> TokenResult<T::Output> {
4047 let additional_lamports = self
4048 .get_additional_rent_for_fixed_len_extension::<TokenGroup>()
4049 .await?;
4050 let mut instructions = vec![];
4051 if additional_lamports > 0 {
4052 instructions.push(system_instruction::transfer(
4053 payer,
4054 &self.pubkey,
4055 additional_lamports,
4056 ));
4057 }
4058 instructions.push(spl_token_group_interface::instruction::initialize_group(
4059 &self.program_id,
4060 &self.pubkey,
4061 &self.pubkey,
4062 mint_authority,
4063 Some(*update_authority),
4064 max_size,
4065 ));
4066 self.process_ixs(&instructions, signing_keypairs).await
4067 }
4068
4069 pub async fn token_group_update_max_size<S: Signers>(
4071 &self,
4072 update_authority: &Address,
4073 new_max_size: u64,
4074 signing_keypairs: &S,
4075 ) -> TokenResult<T::Output> {
4076 self.process_ixs(
4077 &[
4078 spl_token_group_interface::instruction::update_group_max_size(
4079 &self.program_id,
4080 &self.pubkey,
4081 update_authority,
4082 new_max_size,
4083 ),
4084 ],
4085 signing_keypairs,
4086 )
4087 .await
4088 }
4089
4090 pub async fn token_group_update_authority<S: Signers>(
4092 &self,
4093 current_authority: &Address,
4094 new_authority: Option<Address>,
4095 signing_keypairs: &S,
4096 ) -> TokenResult<T::Output> {
4097 self.process_ixs(
4098 &[
4099 spl_token_group_interface::instruction::update_group_authority(
4100 &self.program_id,
4101 &self.pubkey,
4102 current_authority,
4103 new_authority,
4104 ),
4105 ],
4106 signing_keypairs,
4107 )
4108 .await
4109 }
4110
4111 pub async fn token_group_initialize_member<S: Signers>(
4113 &self,
4114 mint_authority: &Address,
4115 group_mint: &Address,
4116 group_update_authority: &Address,
4117 signing_keypairs: &S,
4118 ) -> TokenResult<T::Output> {
4119 self.process_ixs(
4120 &[spl_token_group_interface::instruction::initialize_member(
4121 &self.program_id,
4122 &self.pubkey,
4123 &self.pubkey,
4124 mint_authority,
4125 group_mint,
4126 group_update_authority,
4127 )],
4128 signing_keypairs,
4129 )
4130 .await
4131 }
4132
4133 #[allow(clippy::too_many_arguments)]
4135 pub async fn token_group_initialize_member_with_rent_transfer<S: Signers>(
4136 &self,
4137 payer: &Address,
4138 mint_authority: &Address,
4139 group_mint: &Address,
4140 group_update_authority: &Address,
4141 signing_keypairs: &S,
4142 ) -> TokenResult<T::Output> {
4143 let additional_lamports = self
4144 .get_additional_rent_for_fixed_len_extension::<TokenGroupMember>()
4145 .await?;
4146 let mut instructions = vec![];
4147 if additional_lamports > 0 {
4148 instructions.push(system_instruction::transfer(
4149 payer,
4150 &self.pubkey,
4151 additional_lamports,
4152 ));
4153 }
4154 instructions.push(spl_token_group_interface::instruction::initialize_member(
4155 &self.program_id,
4156 &self.pubkey,
4157 &self.pubkey,
4158 mint_authority,
4159 group_mint,
4160 group_update_authority,
4161 ));
4162 self.process_ixs(&instructions, signing_keypairs).await
4163 }
4164
4165 pub async fn confidential_transfer_get_pending_balance(
4170 &self,
4171 account: &Address,
4172 elgamal_secret_key: &ElGamalSecretKey,
4173 ) -> TokenResult<u64> {
4174 let account_info = self.get_account_info(account).await?;
4175 let confidential_transfer_account =
4176 account_info.get_extension::<ConfidentialTransferAccount>()?;
4177 let account_info = ApplyPendingBalanceAccountInfo::new(confidential_transfer_account);
4178
4179 account_info
4180 .get_pending_balance(elgamal_secret_key)
4181 .map_err(|_| TokenError::AccountDecryption)
4182 }
4183
4184 pub async fn confidential_transfer_get_available_balance(
4188 &self,
4189 account: &Address,
4190 aes_key: &AeKey,
4191 ) -> TokenResult<u64> {
4192 let account_info = self.get_account_info(account).await?;
4193 let confidential_transfer_account =
4194 account_info.get_extension::<ConfidentialTransferAccount>()?;
4195 let account_info = ApplyPendingBalanceAccountInfo::new(confidential_transfer_account);
4196
4197 account_info
4198 .get_available_balance(aes_key)
4199 .map_err(|_| TokenError::AccountDecryption)
4200 }
4201
4202 pub async fn confidential_transfer_get_total_balance(
4206 &self,
4207 account: &Address,
4208 elgamal_secret_key: &ElGamalSecretKey,
4209 aes_key: &AeKey,
4210 ) -> TokenResult<u64> {
4211 let account_info = self.get_account_info(account).await?;
4212 let confidential_transfer_account =
4213 account_info.get_extension::<ConfidentialTransferAccount>()?;
4214 let account_info = ApplyPendingBalanceAccountInfo::new(confidential_transfer_account);
4215
4216 account_info
4217 .get_total_balance(elgamal_secret_key, aes_key)
4218 .map_err(|e| match e {
4219 spl_token_2022_interface::error::TokenError::Overflow => {
4220 TokenError::AccountDecryption
4221 }
4222 _ => TokenError::AccountDecryption,
4223 })
4224 }
4225
4226 pub async fn confidential_transfer_has_pending_balance(
4230 &self,
4231 account: &Address,
4232 ) -> TokenResult<bool> {
4233 let account_info = self.get_account_info(account).await?;
4234 let confidential_transfer_account =
4235 account_info.get_extension::<ConfidentialTransferAccount>()?;
4236 let account_info = ApplyPendingBalanceAccountInfo::new(confidential_transfer_account);
4237
4238 Ok(account_info.has_pending_balance())
4239 }
4240}
4241
4242fn calculate_record_max_chunk_size<F>(
4245 create_record_instructions: F,
4246 first_instruction: bool,
4247) -> usize
4248where
4249 F: Fn(bool, &[u8], u64) -> Vec<Instruction>,
4250{
4251 let ixs = create_record_instructions(first_instruction, &[], 0);
4252 let message = Message::new_with_blockhash(&ixs, Some(&Address::default()), &Hash::default());
4253 let tx_size = bincode::serialized_size(&Transaction {
4254 signatures: vec![Signature::default(); message.header.num_required_signatures as usize],
4255 message,
4256 })
4257 .unwrap() as usize;
4258 PACKET_DATA_SIZE.saturating_sub(tx_size).saturating_sub(1)
4259}