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