1use {
2 crate::{
3 check_program_account,
4 error::TokenError,
5 extension::{
6 confidential_transfer::{instruction::*, *},
7 BaseStateWithExtensions, StateWithExtensions, StateWithExtensionsMut,
8 },
9 instruction::{decode_instruction_data, decode_instruction_type},
10 processor::Processor,
11 state::{Account, Mint},
12 },
13 solana_program::{
14 account_info::{next_account_info, AccountInfo},
15 entrypoint::ProgramResult,
16 instruction::Instruction,
17 msg,
18 program_error::ProgramError,
19 pubkey::Pubkey,
20 sysvar::instructions::get_instruction_relative,
21 },
22 safe_zk_token_sdk::zk_token_proof_program,
23};
24#[cfg(feature = "zk-ops")]
26use {
27 crate::extension::{
28 memo_transfer::{check_previous_sibling_instruction_is_memo, memo_required},
29 non_transferable::NonTransferable,
30 transfer_fee::TransferFeeConfig,
31 },
32 solana_program::{clock::Clock, sysvar::Sysvar},
33 safe_zk_token_sdk::zk_token_elgamal::ops as syscall,
34};
35
36fn decode_proof_instruction<T: Pod>(
42 expected: ProofInstruction,
43 instruction: &Instruction,
44) -> Result<&T, ProgramError> {
45 if instruction.program_id != zk_token_proof_program::id()
46 || ProofInstruction::decode_type(&instruction.data) != Some(expected)
47 {
48 msg!("Unexpected proof instruction");
49 return Err(ProgramError::InvalidInstructionData);
50 }
51
52 ProofInstruction::decode_data(&instruction.data).ok_or(ProgramError::InvalidInstructionData)
53}
54
55fn process_initialize_mint(
57 accounts: &[AccountInfo],
58 authority: &OptionalNonZeroPubkey,
59 auto_approve_new_account: PodBool,
60 auditor_encryption_pubkey: &OptionalNonZeroEncryptionPubkey,
61 withdraw_withheld_authority_encryption_pubkey: &OptionalNonZeroEncryptionPubkey,
62) -> ProgramResult {
63 let account_info_iter = &mut accounts.iter();
64 let mint_info = next_account_info(account_info_iter)?;
65
66 check_program_account(mint_info.owner)?;
67 let mint_data = &mut mint_info.data.borrow_mut();
68 let mut mint = StateWithExtensionsMut::<Mint>::unpack_uninitialized(mint_data)?;
69 let confidential_transfer_mint = mint.init_extension::<ConfidentialTransferMint>(true)?;
70
71 confidential_transfer_mint.authority = *authority;
72 confidential_transfer_mint.auto_approve_new_accounts = auto_approve_new_account;
73 confidential_transfer_mint.auditor_encryption_pubkey = *auditor_encryption_pubkey;
74 confidential_transfer_mint.withdraw_withheld_authority_encryption_pubkey =
75 *withdraw_withheld_authority_encryption_pubkey;
76 confidential_transfer_mint.withheld_amount = EncryptedWithheldAmount::zeroed();
77
78 Ok(())
79}
80
81fn process_update_mint(
83 accounts: &[AccountInfo],
84 auto_approve_new_account: PodBool,
85 auditor_encryption_pubkey: &OptionalNonZeroEncryptionPubkey,
86) -> ProgramResult {
87 let account_info_iter = &mut accounts.iter();
88 let mint_info = next_account_info(account_info_iter)?;
89 let authority_info = next_account_info(account_info_iter)?;
90
91 check_program_account(mint_info.owner)?;
92 let mint_data = &mut mint_info.data.borrow_mut();
93 let mut mint = StateWithExtensionsMut::<Mint>::unpack(mint_data)?;
94 let confidential_transfer_mint = mint.get_extension_mut::<ConfidentialTransferMint>()?;
95 let maybe_confidential_transfer_mint_authority: Option<Pubkey> =
96 confidential_transfer_mint.authority.into();
97 let confidential_transfer_mint_authority =
98 maybe_confidential_transfer_mint_authority.ok_or(TokenError::NoAuthorityExists)?;
99
100 if !authority_info.is_signer {
101 return Err(ProgramError::MissingRequiredSignature);
102 }
103
104 if confidential_transfer_mint_authority != *authority_info.key {
105 return Err(TokenError::OwnerMismatch.into());
106 }
107
108 confidential_transfer_mint.auto_approve_new_accounts = auto_approve_new_account;
109 confidential_transfer_mint.auditor_encryption_pubkey = *auditor_encryption_pubkey;
110 Ok(())
111}
112
113fn process_configure_account(
115 program_id: &Pubkey,
116 accounts: &[AccountInfo],
117 decryptable_zero_balance: &DecryptableBalance,
118 maximum_pending_balance_credit_counter: &PodU64,
119 proof_instruction_offset: i64,
120) -> ProgramResult {
121 let account_info_iter = &mut accounts.iter();
122 let token_account_info = next_account_info(account_info_iter)?;
123 let mint_info = next_account_info(account_info_iter)?;
124 let instructions_sysvar_info = next_account_info(account_info_iter)?;
125 let authority_info = next_account_info(account_info_iter)?;
126 let authority_info_data_len = authority_info.data_len();
127
128 check_program_account(token_account_info.owner)?;
129 let token_account_data = &mut token_account_info.data.borrow_mut();
130 let mut token_account = StateWithExtensionsMut::<Account>::unpack(token_account_data)?;
131
132 if token_account.base.mint != *mint_info.key {
133 return Err(TokenError::MintMismatch.into());
134 }
135
136 Processor::validate_owner(
137 program_id,
138 &token_account.base.owner,
139 authority_info,
140 authority_info_data_len,
141 account_info_iter.as_slice(),
142 )?;
143
144 check_program_account(mint_info.owner)?;
145 let mint_data = &mut mint_info.data.borrow();
146 let mint = StateWithExtensions::<Mint>::unpack(mint_data)?;
147 let confidential_transfer_mint = mint.get_extension::<ConfidentialTransferMint>()?;
148
149 let zkp_instruction =
151 get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?;
152 let proof_data = decode_proof_instruction::<PubkeyValidityData>(
153 ProofInstruction::VerifyPubkeyValidity,
154 &zkp_instruction,
155 )?;
156
157 let mut confidential_transfer_account =
160 token_account.init_extension::<ConfidentialTransferAccount>(false)?;
161 confidential_transfer_account.approved = confidential_transfer_mint.auto_approve_new_accounts;
162 confidential_transfer_account.encryption_pubkey = proof_data.pubkey;
163 confidential_transfer_account.maximum_pending_balance_credit_counter =
164 *maximum_pending_balance_credit_counter;
165
166 confidential_transfer_account.pending_balance_lo = EncryptedBalance::zeroed();
168 confidential_transfer_account.pending_balance_hi = EncryptedBalance::zeroed();
169 confidential_transfer_account.available_balance = EncryptedBalance::zeroed();
170
171 confidential_transfer_account.decryptable_available_balance = *decryptable_zero_balance;
172 confidential_transfer_account.allow_confidential_credits = true.into();
173 confidential_transfer_account.pending_balance_credit_counter = 0.into();
174 confidential_transfer_account.expected_pending_balance_credit_counter = 0.into();
175 confidential_transfer_account.actual_pending_balance_credit_counter = 0.into();
176 confidential_transfer_account.allow_non_confidential_credits = true.into();
177 confidential_transfer_account.withheld_amount = EncryptedWithheldAmount::zeroed();
178
179 Ok(())
180}
181
182fn process_approve_account(accounts: &[AccountInfo]) -> ProgramResult {
184 let account_info_iter = &mut accounts.iter();
185 let token_account_info = next_account_info(account_info_iter)?;
186 let mint_info = next_account_info(account_info_iter)?;
187 let authority_info = next_account_info(account_info_iter)?;
188
189 check_program_account(token_account_info.owner)?;
190 let token_account_data = &mut token_account_info.data.borrow_mut();
191 let mut token_account = StateWithExtensionsMut::<Account>::unpack(token_account_data)?;
192
193 check_program_account(mint_info.owner)?;
194 let mint_data = &mint_info.data.borrow_mut();
195 let mint = StateWithExtensions::<Mint>::unpack(mint_data)?;
196 let confidential_transfer_mint = mint.get_extension::<ConfidentialTransferMint>()?;
197 let maybe_confidential_transfer_mint_authority: Option<Pubkey> =
198 confidential_transfer_mint.authority.into();
199 let confidential_transfer_mint_authority =
200 maybe_confidential_transfer_mint_authority.ok_or(TokenError::NoAuthorityExists)?;
201
202 if authority_info.is_signer && *authority_info.key == confidential_transfer_mint_authority {
203 let mut confidential_transfer_state =
204 token_account.get_extension_mut::<ConfidentialTransferAccount>()?;
205 confidential_transfer_state.approved = true.into();
206 Ok(())
207 } else {
208 Err(ProgramError::MissingRequiredSignature)
209 }
210}
211
212fn process_empty_account(
214 program_id: &Pubkey,
215 accounts: &[AccountInfo],
216 proof_instruction_offset: i64,
217) -> ProgramResult {
218 let account_info_iter = &mut accounts.iter();
219 let token_account_info = next_account_info(account_info_iter)?;
220 let instructions_sysvar_info = next_account_info(account_info_iter)?;
221 let authority_info = next_account_info(account_info_iter)?;
222 let authority_info_data_len = authority_info.data_len();
223
224 check_program_account(token_account_info.owner)?;
225 let token_account_data = &mut token_account_info.data.borrow_mut();
226 let mut token_account = StateWithExtensionsMut::<Account>::unpack(token_account_data)?;
227
228 Processor::validate_owner(
229 program_id,
230 &token_account.base.owner,
231 authority_info,
232 authority_info_data_len,
233 account_info_iter.as_slice(),
234 )?;
235
236 let mut confidential_transfer_account =
237 token_account.get_extension_mut::<ConfidentialTransferAccount>()?;
238
239 let zkp_instruction =
255 get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?;
256 let proof_data = decode_proof_instruction::<CloseAccountData>(
257 ProofInstruction::VerifyCloseAccount,
258 &zkp_instruction,
259 )?;
260 if confidential_transfer_account.encryption_pubkey != proof_data.pubkey {
263 msg!("Encryption public-key mismatch");
264 return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into());
265 }
266 if confidential_transfer_account.available_balance != proof_data.ciphertext {
267 msg!("Available balance mismatch");
268 return Err(ProgramError::InvalidInstructionData);
269 }
270 confidential_transfer_account.available_balance = EncryptedBalance::zeroed();
271
272 confidential_transfer_account.closable()?;
274
275 Ok(())
276}
277
278#[cfg(feature = "zk-ops")]
280fn process_deposit(
281 program_id: &Pubkey,
282 accounts: &[AccountInfo],
283 amount: u64,
284 expected_decimals: u8,
285) -> ProgramResult {
286 let account_info_iter = &mut accounts.iter();
287 let token_account_info = next_account_info(account_info_iter)?;
288 let mint_info = next_account_info(account_info_iter)?;
289 let authority_info = next_account_info(account_info_iter)?;
290 let authority_info_data_len = authority_info.data_len();
291
292 check_program_account(mint_info.owner)?;
293 let mint_data = &mint_info.data.borrow_mut();
294 let mint = StateWithExtensions::<Mint>::unpack(mint_data)?;
295
296 if expected_decimals != mint.base.decimals {
297 return Err(TokenError::MintDecimalsMismatch.into());
298 }
299
300 if mint.get_extension::<NonTransferable>().is_ok() {
301 return Err(TokenError::NonTransferable.into());
302 }
303
304 check_program_account(token_account_info.owner)?;
305 let token_account_data = &mut token_account_info.data.borrow_mut();
306 let mut token_account = StateWithExtensionsMut::<Account>::unpack(token_account_data)?;
307
308 Processor::validate_owner(
309 program_id,
310 &token_account.base.owner,
311 authority_info,
312 authority_info_data_len,
313 account_info_iter.as_slice(),
314 )?;
315
316 if token_account.base.is_frozen() {
317 return Err(TokenError::AccountFrozen.into());
318 }
319
320 if token_account.base.mint != *mint_info.key {
321 return Err(TokenError::MintMismatch.into());
322 }
323
324 assert!(!token_account.base.is_native());
326
327 token_account.base.amount = token_account
328 .base
329 .amount
330 .checked_sub(amount)
331 .ok_or(TokenError::Overflow)?;
332 token_account.pack_base();
333
334 let mut confidential_transfer_account =
335 token_account.get_extension_mut::<ConfidentialTransferAccount>()?;
336 confidential_transfer_account.valid_as_destination()?;
337
338 let (amount_lo, amount_hi) = verify_and_split_deposit_amount(amount)?;
340
341 if amount_lo > 0 {
343 confidential_transfer_account.pending_balance_lo =
344 syscall::add_to(&confidential_transfer_account.pending_balance_lo, amount_lo)
345 .ok_or(ProgramError::InvalidInstructionData)?;
346 }
347 if amount_hi > 0 {
348 confidential_transfer_account.pending_balance_hi =
349 syscall::add_to(&confidential_transfer_account.pending_balance_hi, amount_hi)
350 .ok_or(ProgramError::InvalidInstructionData)?;
351 }
352
353 confidential_transfer_account.increment_pending_balance_credit_counter()?;
354
355 Ok(())
356}
357
358#[cfg(feature = "zk-ops")]
361fn verify_and_split_deposit_amount(amount: u64) -> Result<(u64, u64), TokenError> {
362 if amount >> MAXIMUM_DEPOSIT_TRANSFER_AMOUNT_BIT_LENGTH > 0 {
363 return Err(TokenError::MaximumDepositAmountExceeded);
364 }
365 let deposit_amount_lo =
366 amount << (64 - PENDING_BALANCE_LO_BIT_LENGTH) >> PENDING_BALANCE_HI_BIT_LENGTH;
367 let deposit_amount_hi = amount >> PENDING_BALANCE_LO_BIT_LENGTH;
368
369 Ok((deposit_amount_lo, deposit_amount_hi))
370}
371
372#[cfg(feature = "zk-ops")]
374fn process_withdraw(
375 program_id: &Pubkey,
376 accounts: &[AccountInfo],
377 amount: u64,
378 expected_decimals: u8,
379 new_decryptable_available_balance: DecryptableBalance,
380 proof_instruction_offset: i64,
381) -> ProgramResult {
382 let account_info_iter = &mut accounts.iter();
383 let token_account_info = next_account_info(account_info_iter)?;
384 let mint_info = next_account_info(account_info_iter)?;
385 let instructions_sysvar_info = next_account_info(account_info_iter)?;
386 let authority_info = next_account_info(account_info_iter)?;
387 let authority_info_data_len = authority_info.data_len();
388
389 check_program_account(mint_info.owner)?;
390 let mint_data = &mint_info.data.borrow_mut();
391 let mint = StateWithExtensions::<Mint>::unpack(mint_data)?;
392
393 if expected_decimals != mint.base.decimals {
394 return Err(TokenError::MintDecimalsMismatch.into());
395 }
396
397 if mint.get_extension::<NonTransferable>().is_ok() {
398 return Err(TokenError::NonTransferable.into());
399 }
400
401 check_program_account(token_account_info.owner)?;
402 let token_account_data = &mut token_account_info.data.borrow_mut();
403 let mut token_account = StateWithExtensionsMut::<Account>::unpack(token_account_data)?;
404
405 Processor::validate_owner(
406 program_id,
407 &token_account.base.owner,
408 authority_info,
409 authority_info_data_len,
410 account_info_iter.as_slice(),
411 )?;
412
413 if token_account.base.is_frozen() {
414 return Err(TokenError::AccountFrozen.into());
415 }
416
417 if token_account.base.mint != *mint_info.key {
418 return Err(TokenError::MintMismatch.into());
419 }
420
421 assert!(!token_account.base.is_native());
423
424 let mut confidential_transfer_account =
425 token_account.get_extension_mut::<ConfidentialTransferAccount>()?;
426 confidential_transfer_account.valid_as_source()?;
427
428 let zkp_instruction =
431 get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?;
432 let proof_data = decode_proof_instruction::<WithdrawData>(
433 ProofInstruction::VerifyWithdraw,
434 &zkp_instruction,
435 )?;
436 if confidential_transfer_account.encryption_pubkey != proof_data.pubkey {
439 return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into());
440 }
441
442 if amount > 0 {
444 confidential_transfer_account.available_balance =
445 syscall::subtract_from(&confidential_transfer_account.available_balance, amount)
446 .ok_or(ProgramError::InvalidInstructionData)?;
447 }
448 if confidential_transfer_account.available_balance != proof_data.final_ciphertext {
451 return Err(TokenError::ConfidentialTransferBalanceMismatch.into());
452 }
453
454 confidential_transfer_account.decryptable_available_balance = new_decryptable_available_balance;
455 token_account.base.amount = token_account
456 .base
457 .amount
458 .checked_add(amount)
459 .ok_or(TokenError::Overflow)?;
460 token_account.pack_base();
461
462 Ok(())
463}
464
465#[cfg(feature = "zk-ops")]
467fn process_transfer(
468 program_id: &Pubkey,
469 accounts: &[AccountInfo],
470 new_source_decryptable_available_balance: DecryptableBalance,
471 proof_instruction_offset: i64,
472) -> ProgramResult {
473 let account_info_iter = &mut accounts.iter();
474 let source_account_info = next_account_info(account_info_iter)?;
475 let destination_token_account_info = next_account_info(account_info_iter)?;
476 let mint_info = next_account_info(account_info_iter)?;
477 let instructions_sysvar_info = next_account_info(account_info_iter)?;
478 let authority_info = next_account_info(account_info_iter)?;
479
480 check_program_account(mint_info.owner)?;
481 let mint_data = &mint_info.data.borrow_mut();
482 let mint = StateWithExtensions::<Mint>::unpack(mint_data)?;
483
484 if mint.get_extension::<NonTransferable>().is_ok() {
485 return Err(TokenError::NonTransferable.into());
486 }
487 let confidential_transfer_mint = mint.get_extension::<ConfidentialTransferMint>()?;
488
489 if mint.get_extension::<TransferFeeConfig>().is_err()
497 || source_account_info.key == destination_token_account_info.key
498 {
499 let zkp_instruction =
505 get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?;
506 let proof_data = decode_proof_instruction::<TransferData>(
507 ProofInstruction::VerifyTransfer,
508 &zkp_instruction,
509 )?;
510 if !confidential_transfer_mint
513 .auditor_encryption_pubkey
514 .equals(&proof_data.transfer_pubkeys.auditor_pubkey)
515 {
516 return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into());
517 }
518
519 let source_ciphertext_lo = EncryptedBalance::from((
520 proof_data.ciphertext_lo.commitment,
521 proof_data.ciphertext_lo.source_handle,
522 ));
523 let source_ciphertext_hi = EncryptedBalance::from((
524 proof_data.ciphertext_hi.commitment,
525 proof_data.ciphertext_hi.source_handle,
526 ));
527
528 process_source_for_transfer(
529 program_id,
530 source_account_info,
531 mint_info,
532 authority_info,
533 account_info_iter.as_slice(),
534 &proof_data.transfer_pubkeys.source_pubkey,
535 &source_ciphertext_lo,
536 &source_ciphertext_hi,
537 &proof_data.new_source_ciphertext,
538 new_source_decryptable_available_balance,
539 )?;
540
541 let destination_ciphertext_lo = EncryptedBalance::from((
542 proof_data.ciphertext_lo.commitment,
543 proof_data.ciphertext_lo.destination_handle,
544 ));
545 let destination_ciphertext_hi = EncryptedBalance::from((
546 proof_data.ciphertext_hi.commitment,
547 proof_data.ciphertext_hi.destination_handle,
548 ));
549
550 process_destination_for_transfer(
551 destination_token_account_info,
552 mint_info,
553 &proof_data.transfer_pubkeys.destination_pubkey,
554 &destination_ciphertext_lo,
555 &destination_ciphertext_hi,
556 None,
557 )?;
558 } else {
559 let zkp_instruction =
566 get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?;
567 let proof_data = decode_proof_instruction::<TransferWithFeeData>(
568 ProofInstruction::VerifyTransferWithFee,
569 &zkp_instruction,
570 )?;
571 if !confidential_transfer_mint
574 .auditor_encryption_pubkey
575 .equals(&proof_data.transfer_with_fee_pubkeys.auditor_pubkey)
576 {
577 return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into());
578 }
579 if !confidential_transfer_mint
580 .withdraw_withheld_authority_encryption_pubkey
581 .equals(
582 &proof_data
583 .transfer_with_fee_pubkeys
584 .withdraw_withheld_authority_pubkey,
585 )
586 {
587 return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into());
588 }
589 let transfer_fee_config = mint.get_extension::<TransferFeeConfig>()?;
592 let fee_parameters = transfer_fee_config.get_epoch_fee(Clock::get()?.epoch);
593 if u64::from(fee_parameters.maximum_fee) != u64::from(proof_data.fee_parameters.maximum_fee)
594 || u16::from(fee_parameters.transfer_fee_basis_points)
595 != u16::from(proof_data.fee_parameters.fee_rate_basis_points)
596 {
597 return Err(TokenError::FeeParametersMismatch.into());
598 }
599
600 let source_transfer_amount_lo = EncryptedBalance::from((
603 proof_data.ciphertext_lo.commitment,
604 proof_data.ciphertext_lo.source_handle,
605 ));
606 let source_transfer_amount_hi = EncryptedBalance::from((
607 proof_data.ciphertext_hi.commitment,
608 proof_data.ciphertext_hi.source_handle,
609 ));
610
611 process_source_for_transfer(
612 program_id,
613 source_account_info,
614 mint_info,
615 authority_info,
616 account_info_iter.as_slice(),
617 &proof_data.transfer_with_fee_pubkeys.source_pubkey,
618 &source_transfer_amount_lo,
619 &source_transfer_amount_hi,
620 &proof_data.new_source_ciphertext,
621 new_source_decryptable_available_balance,
622 )?;
623
624 let destination_transfer_amount_lo = EncryptedBalance::from((
627 proof_data.ciphertext_lo.commitment,
628 proof_data.ciphertext_lo.destination_handle,
629 ));
630 let destination_transfer_amount_hi = EncryptedBalance::from((
631 proof_data.ciphertext_hi.commitment,
632 proof_data.ciphertext_hi.destination_handle,
633 ));
634
635 process_destination_for_transfer(
636 destination_token_account_info,
637 mint_info,
638 &proof_data.transfer_with_fee_pubkeys.destination_pubkey,
639 &destination_transfer_amount_lo,
640 &destination_transfer_amount_hi,
641 Some((&proof_data.fee_ciphertext_lo, &proof_data.fee_ciphertext_hi)),
642 )?;
643 }
644
645 Ok(())
646}
647
648#[allow(clippy::too_many_arguments)]
649#[cfg(feature = "zk-ops")]
650fn process_source_for_transfer(
651 program_id: &Pubkey,
652 source_account_info: &AccountInfo,
653 mint_info: &AccountInfo,
654 authority_info: &AccountInfo,
655 signers: &[AccountInfo],
656 source_encryption_pubkey: &EncryptionPubkey,
657 source_transfer_amount_lo: &EncryptedBalance,
658 source_transfer_amount_hi: &EncryptedBalance,
659 expected_new_source_available_balance: &EncryptedBalance,
660 new_source_decryptable_available_balance: DecryptableBalance,
661) -> ProgramResult {
662 check_program_account(source_account_info.owner)?;
663 let authority_info_data_len = authority_info.data_len();
664 let token_account_data = &mut source_account_info.data.borrow_mut();
665 let mut token_account = StateWithExtensionsMut::<Account>::unpack(token_account_data)?;
666
667 Processor::validate_owner(
668 program_id,
669 &token_account.base.owner,
670 authority_info,
671 authority_info_data_len,
672 signers,
673 )?;
674
675 if token_account.base.is_frozen() {
676 return Err(TokenError::AccountFrozen.into());
677 }
678
679 if token_account.base.mint != *mint_info.key {
680 return Err(TokenError::MintMismatch.into());
681 }
682
683 let mut confidential_transfer_account =
684 token_account.get_extension_mut::<ConfidentialTransferAccount>()?;
685 confidential_transfer_account.valid_as_source()?;
686
687 if *source_encryption_pubkey != confidential_transfer_account.encryption_pubkey {
690 return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into());
691 }
692
693 let new_source_available_balance = syscall::subtract_with_lo_hi(
694 &confidential_transfer_account.available_balance,
695 source_transfer_amount_lo,
696 source_transfer_amount_hi,
697 )
698 .ok_or(ProgramError::InvalidInstructionData)?;
699
700 if new_source_available_balance != *expected_new_source_available_balance {
703 return Err(TokenError::ConfidentialTransferBalanceMismatch.into());
704 }
705
706 confidential_transfer_account.available_balance = new_source_available_balance;
707 confidential_transfer_account.decryptable_available_balance =
708 new_source_decryptable_available_balance;
709
710 Ok(())
711}
712
713#[cfg(feature = "zk-ops")]
714fn process_destination_for_transfer(
715 destination_token_account_info: &AccountInfo,
716 mint_info: &AccountInfo,
717 destination_encryption_pubkey: &EncryptionPubkey,
718 destination_transfer_amount_lo: &EncryptedBalance,
719 destination_transfer_amount_hi: &EncryptedBalance,
720 encrypted_fee: Option<(&EncryptedFee, &EncryptedFee)>,
721) -> ProgramResult {
722 check_program_account(destination_token_account_info.owner)?;
723 let destination_token_account_data = &mut destination_token_account_info.data.borrow_mut();
724 let mut destination_token_account =
725 StateWithExtensionsMut::<Account>::unpack(destination_token_account_data)?;
726
727 if destination_token_account.base.is_frozen() {
728 return Err(TokenError::AccountFrozen.into());
729 }
730
731 if destination_token_account.base.mint != *mint_info.key {
732 return Err(TokenError::MintMismatch.into());
733 }
734
735 if memo_required(&destination_token_account) {
736 check_previous_sibling_instruction_is_memo()?;
737 }
738
739 let mut destination_confidential_transfer_account =
740 destination_token_account.get_extension_mut::<ConfidentialTransferAccount>()?;
741 destination_confidential_transfer_account.valid_as_destination()?;
742
743 if *destination_encryption_pubkey != destination_confidential_transfer_account.encryption_pubkey
744 {
745 return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into());
746 }
747
748 destination_confidential_transfer_account.pending_balance_lo = syscall::add(
749 &destination_confidential_transfer_account.pending_balance_lo,
750 destination_transfer_amount_lo,
751 )
752 .ok_or(ProgramError::InvalidInstructionData)?;
753
754 destination_confidential_transfer_account.pending_balance_hi = syscall::add(
755 &destination_confidential_transfer_account.pending_balance_hi,
756 destination_transfer_amount_hi,
757 )
758 .ok_or(ProgramError::InvalidInstructionData)?;
759
760 destination_confidential_transfer_account.increment_pending_balance_credit_counter()?;
761
762 if let Some((ciphertext_fee_lo, ciphertext_fee_hi)) = encrypted_fee {
764 let destination_fee_lo: EncryptedWithheldAmount = (
766 ciphertext_fee_lo.commitment,
767 ciphertext_fee_lo.destination_handle,
768 )
769 .into();
770 let destination_fee_hi: EncryptedWithheldAmount = (
771 ciphertext_fee_hi.commitment,
772 ciphertext_fee_hi.destination_handle,
773 )
774 .into();
775
776 destination_confidential_transfer_account.pending_balance_lo = syscall::subtract(
778 &destination_confidential_transfer_account.pending_balance_lo,
779 &destination_fee_lo,
780 )
781 .ok_or(ProgramError::InvalidInstructionData)?;
782 destination_confidential_transfer_account.pending_balance_hi = syscall::subtract(
783 &destination_confidential_transfer_account.pending_balance_hi,
784 &destination_fee_hi,
785 )
786 .ok_or(ProgramError::InvalidInstructionData)?;
787
788 let withdraw_withheld_authority_fee_lo: EncryptedWithheldAmount = (
791 ciphertext_fee_lo.commitment,
792 ciphertext_fee_lo.withdraw_withheld_authority_handle,
793 )
794 .into();
795 let withdraw_withheld_authority_fee_hi: EncryptedWithheldAmount = (
796 ciphertext_fee_hi.commitment,
797 ciphertext_fee_hi.withdraw_withheld_authority_handle,
798 )
799 .into();
800
801 destination_confidential_transfer_account.withheld_amount = syscall::add_with_lo_hi(
803 &destination_confidential_transfer_account.withheld_amount,
804 &withdraw_withheld_authority_fee_lo,
805 &withdraw_withheld_authority_fee_hi,
806 )
807 .ok_or(ProgramError::InvalidInstructionData)?;
808 }
809
810 Ok(())
811}
812
813#[cfg(feature = "zk-ops")]
815fn process_apply_pending_balance(
816 program_id: &Pubkey,
817 accounts: &[AccountInfo],
818 ApplyPendingBalanceData {
819 expected_pending_balance_credit_counter,
820 new_decryptable_available_balance,
821 }: &ApplyPendingBalanceData,
822) -> ProgramResult {
823 let account_info_iter = &mut accounts.iter();
824 let token_account_info = next_account_info(account_info_iter)?;
825 let authority_info = next_account_info(account_info_iter)?;
826 let authority_info_data_len = authority_info.data_len();
827
828 check_program_account(token_account_info.owner)?;
829 let token_account_data = &mut token_account_info.data.borrow_mut();
830 let mut token_account = StateWithExtensionsMut::<Account>::unpack(token_account_data)?;
831
832 Processor::validate_owner(
833 program_id,
834 &token_account.base.owner,
835 authority_info,
836 authority_info_data_len,
837 account_info_iter.as_slice(),
838 )?;
839
840 let mut confidential_transfer_account =
841 token_account.get_extension_mut::<ConfidentialTransferAccount>()?;
842
843 confidential_transfer_account.available_balance = syscall::add_with_lo_hi(
844 &confidential_transfer_account.available_balance,
845 &confidential_transfer_account.pending_balance_lo,
846 &confidential_transfer_account.pending_balance_hi,
847 )
848 .ok_or(ProgramError::InvalidInstructionData)?;
849
850 confidential_transfer_account.actual_pending_balance_credit_counter =
851 confidential_transfer_account.pending_balance_credit_counter;
852 confidential_transfer_account.expected_pending_balance_credit_counter =
853 *expected_pending_balance_credit_counter;
854 confidential_transfer_account.decryptable_available_balance =
855 *new_decryptable_available_balance;
856 confidential_transfer_account.pending_balance_credit_counter = 0.into();
857 confidential_transfer_account.pending_balance_lo = EncryptedBalance::zeroed();
858 confidential_transfer_account.pending_balance_hi = EncryptedBalance::zeroed();
859
860 Ok(())
861}
862
863fn process_allow_confidential_credits(
865 program_id: &Pubkey,
866 accounts: &[AccountInfo],
867 allow_confidential_credits: bool,
868) -> ProgramResult {
869 let account_info_iter = &mut accounts.iter();
870 let token_account_info = next_account_info(account_info_iter)?;
871 let authority_info = next_account_info(account_info_iter)?;
872 let authority_info_data_len = authority_info.data_len();
873
874 check_program_account(token_account_info.owner)?;
875 let token_account_data = &mut token_account_info.data.borrow_mut();
876 let mut token_account = StateWithExtensionsMut::<Account>::unpack(token_account_data)?;
877
878 Processor::validate_owner(
879 program_id,
880 &token_account.base.owner,
881 authority_info,
882 authority_info_data_len,
883 account_info_iter.as_slice(),
884 )?;
885
886 let mut confidential_transfer_account =
887 token_account.get_extension_mut::<ConfidentialTransferAccount>()?;
888 confidential_transfer_account.allow_confidential_credits = allow_confidential_credits.into();
889
890 Ok(())
891}
892
893fn process_allow_non_confidential_credits(
895 program_id: &Pubkey,
896 accounts: &[AccountInfo],
897 allow_non_confidential_credits: bool,
898) -> ProgramResult {
899 let account_info_iter = &mut accounts.iter();
900 let token_account_info = next_account_info(account_info_iter)?;
901 let authority_info = next_account_info(account_info_iter)?;
902 let authority_info_data_len = authority_info.data_len();
903
904 check_program_account(token_account_info.owner)?;
905 let token_account_data = &mut token_account_info.data.borrow_mut();
906 let mut token_account = StateWithExtensionsMut::<Account>::unpack(token_account_data)?;
907
908 Processor::validate_owner(
909 program_id,
910 &token_account.base.owner,
911 authority_info,
912 authority_info_data_len,
913 account_info_iter.as_slice(),
914 )?;
915
916 let mut confidential_transfer_account =
917 token_account.get_extension_mut::<ConfidentialTransferAccount>()?;
918 confidential_transfer_account.allow_non_confidential_credits =
919 allow_non_confidential_credits.into();
920
921 Ok(())
922}
923
924#[cfg(feature = "zk-ops")]
926fn process_withdraw_withheld_tokens_from_mint(
927 program_id: &Pubkey,
928 accounts: &[AccountInfo],
929 proof_instruction_offset: i64,
930) -> ProgramResult {
931 let account_info_iter = &mut accounts.iter();
932 let mint_account_info = next_account_info(account_info_iter)?;
933 let destination_account_info = next_account_info(account_info_iter)?;
934 let instructions_sysvar_info = next_account_info(account_info_iter)?;
935 let authority_info = next_account_info(account_info_iter)?;
936 let authority_info_data_len = authority_info.data_len();
937
938 check_program_account(mint_account_info.owner)?;
940 let mut mint_data = mint_account_info.data.borrow_mut();
941 let mut mint = StateWithExtensionsMut::<Mint>::unpack(&mut mint_data)?;
942
943 {
945 let transfer_fee_config = mint.get_extension::<TransferFeeConfig>()?;
946 let withdraw_withheld_authority =
947 Option::<Pubkey>::from(transfer_fee_config.withdraw_withheld_authority)
948 .ok_or(TokenError::NoAuthorityExists)?;
949 Processor::validate_owner(
950 program_id,
951 &withdraw_withheld_authority,
952 authority_info,
953 authority_info_data_len,
954 account_info_iter.as_slice(),
955 )?;
956 } let confidential_transfer_mint = mint.get_extension_mut::<ConfidentialTransferMint>()?;
959
960 let mut destination_account_data = destination_account_info.data.borrow_mut();
962 let mut destination_account =
963 StateWithExtensionsMut::<Account>::unpack(&mut destination_account_data)?;
964
965 if destination_account.base.mint != *mint_account_info.key {
966 return Err(TokenError::MintMismatch.into());
967 }
968 if destination_account.base.is_frozen() {
969 return Err(TokenError::AccountFrozen.into());
970 }
971 let mut destination_confidential_transfer_account =
972 destination_account.get_extension_mut::<ConfidentialTransferAccount>()?;
973 destination_confidential_transfer_account.valid_as_destination()?;
974
975 let zkp_instruction =
978 get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?;
979 let proof_data = decode_proof_instruction::<WithdrawWithheldTokensData>(
980 ProofInstruction::VerifyWithdrawWithheldTokens,
981 &zkp_instruction,
982 )?;
983 if !confidential_transfer_mint
986 .withdraw_withheld_authority_encryption_pubkey
987 .equals(&proof_data.withdraw_withheld_authority_pubkey)
988 {
989 return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into());
990 }
991 if proof_data.destination_pubkey != destination_confidential_transfer_account.encryption_pubkey
994 {
995 return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into());
996 }
997 if proof_data.withdraw_withheld_authority_ciphertext
1000 != confidential_transfer_mint.withheld_amount
1001 {
1002 return Err(TokenError::ConfidentialTransferBalanceMismatch.into());
1003 }
1004
1005 destination_confidential_transfer_account.pending_balance_lo = syscall::add(
1008 &destination_confidential_transfer_account.pending_balance_lo,
1009 &proof_data.destination_ciphertext,
1010 )
1011 .ok_or(ProgramError::InvalidInstructionData)?;
1012
1013 destination_confidential_transfer_account.increment_pending_balance_credit_counter()?;
1014
1015 confidential_transfer_mint.withheld_amount = EncryptedWithheldAmount::zeroed();
1017
1018 Ok(())
1019}
1020
1021#[cfg(feature = "zk-ops")]
1022fn process_withdraw_withheld_tokens_from_accounts(
1023 program_id: &Pubkey,
1024 accounts: &[AccountInfo],
1025 num_token_accounts: u8,
1026 proof_instruction_offset: i64,
1027) -> ProgramResult {
1028 let account_info_iter = &mut accounts.iter();
1029 let mint_account_info = next_account_info(account_info_iter)?;
1030 let destination_account_info = next_account_info(account_info_iter)?;
1031 let instructions_sysvar_info = next_account_info(account_info_iter)?;
1032 let authority_info = next_account_info(account_info_iter)?;
1033 let authority_info_data_len = authority_info.data_len();
1034 let account_infos = account_info_iter.as_slice();
1035 let num_signers = account_infos
1036 .len()
1037 .saturating_sub(num_token_accounts as usize);
1038
1039 check_program_account(mint_account_info.owner)?;
1041 let mut mint_data = mint_account_info.data.borrow_mut();
1042 let mut mint = StateWithExtensionsMut::<Mint>::unpack(&mut mint_data)?;
1043
1044 let transfer_fee_config = mint.get_extension::<TransferFeeConfig>()?;
1046 let withdraw_withheld_authority =
1047 Option::<Pubkey>::from(transfer_fee_config.withdraw_withheld_authority)
1048 .ok_or(TokenError::NoAuthorityExists)?;
1049 Processor::validate_owner(
1050 program_id,
1051 &withdraw_withheld_authority,
1052 authority_info,
1053 authority_info_data_len,
1054 &account_infos[..num_signers],
1055 )?;
1056
1057 let mut destination_account_data = destination_account_info.data.borrow_mut();
1058 let mut destination_account =
1059 StateWithExtensionsMut::<Account>::unpack(&mut destination_account_data)?;
1060 if destination_account.base.mint != *mint_account_info.key {
1061 return Err(TokenError::MintMismatch.into());
1062 }
1063 if destination_account.base.is_frozen() {
1064 return Err(TokenError::AccountFrozen.into());
1065 }
1066
1067 let mut aggregate_withheld_amount = EncryptedWithheldAmount::zeroed();
1069 for account_info in &account_infos[num_signers..] {
1070 if account_info.key == destination_account_info.key {
1072 let confidential_transfer_destination_account = destination_account
1073 .get_extension_mut::<ConfidentialTransferAccount>()
1074 .map_err(|_| TokenError::InvalidState)?;
1075
1076 aggregate_withheld_amount = syscall::add(
1077 &aggregate_withheld_amount,
1078 &confidential_transfer_destination_account.withheld_amount,
1079 )
1080 .ok_or(ProgramError::InvalidInstructionData)?;
1081
1082 confidential_transfer_destination_account.withheld_amount =
1083 EncryptedWithheldAmount::zeroed();
1084 } else {
1085 match harvest_from_account(mint_account_info.key, account_info) {
1086 Ok(encrypted_withheld_amount) => {
1087 aggregate_withheld_amount =
1088 syscall::add(&aggregate_withheld_amount, &encrypted_withheld_amount)
1089 .ok_or(ProgramError::InvalidInstructionData)?;
1090 }
1091 Err(e) => {
1092 msg!("Error harvesting from {}: {}", account_info.key, e);
1093 }
1094 }
1095 }
1096 }
1097
1098 let mut destination_confidential_transfer_account =
1099 destination_account.get_extension_mut::<ConfidentialTransferAccount>()?;
1100 destination_confidential_transfer_account.valid_as_destination()?;
1101
1102 let zkp_instruction =
1105 get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?;
1106 let proof_data = decode_proof_instruction::<WithdrawWithheldTokensData>(
1107 ProofInstruction::VerifyWithdrawWithheldTokens,
1108 &zkp_instruction,
1109 )?;
1110 let confidential_transfer_mint = mint.get_extension_mut::<ConfidentialTransferMint>()?;
1113 if !confidential_transfer_mint
1114 .withdraw_withheld_authority_encryption_pubkey
1115 .equals(&proof_data.withdraw_withheld_authority_pubkey)
1116 {
1117 return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into());
1118 }
1119 if proof_data.destination_pubkey != destination_confidential_transfer_account.encryption_pubkey
1122 {
1123 return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into());
1124 }
1125 if proof_data.withdraw_withheld_authority_ciphertext != aggregate_withheld_amount {
1128 return Err(TokenError::ConfidentialTransferBalanceMismatch.into());
1129 }
1130
1131 destination_confidential_transfer_account.pending_balance_lo = syscall::add(
1134 &destination_confidential_transfer_account.pending_balance_lo,
1135 &proof_data.destination_ciphertext,
1136 )
1137 .ok_or(ProgramError::InvalidInstructionData)?;
1138
1139 destination_confidential_transfer_account.increment_pending_balance_credit_counter()?;
1140
1141 Ok(())
1142}
1143
1144#[cfg(feature = "zk-ops")]
1145fn harvest_from_account<'a, 'b>(
1146 mint_key: &'b Pubkey,
1147 token_account_info: &'b AccountInfo<'a>,
1148) -> Result<EncryptedWithheldAmount, TokenError> {
1149 let mut token_account_data = token_account_info.data.borrow_mut();
1150 let mut token_account = StateWithExtensionsMut::<Account>::unpack(&mut token_account_data)
1151 .map_err(|_| TokenError::InvalidState)?;
1152 if token_account.base.mint != *mint_key {
1153 return Err(TokenError::MintMismatch);
1154 }
1155 check_program_account(token_account_info.owner).map_err(|_| TokenError::InvalidState)?;
1156
1157 let confidential_transfer_token_account = token_account
1158 .get_extension_mut::<ConfidentialTransferAccount>()
1159 .map_err(|_| TokenError::InvalidState)?;
1160
1161 let withheld_amount = confidential_transfer_token_account.withheld_amount;
1162 confidential_transfer_token_account.withheld_amount = EncryptedWithheldAmount::zeroed();
1163
1164 Ok(withheld_amount)
1165}
1166
1167#[cfg(feature = "zk-ops")]
1169fn process_harvest_withheld_tokens_to_mint(accounts: &[AccountInfo]) -> ProgramResult {
1170 let account_info_iter = &mut accounts.iter();
1171 let mint_account_info = next_account_info(account_info_iter)?;
1172 let token_account_infos = account_info_iter.as_slice();
1173
1174 let mut mint_data = mint_account_info.data.borrow_mut();
1175 let mut mint = StateWithExtensionsMut::<Mint>::unpack(&mut mint_data)?;
1176 mint.get_extension::<TransferFeeConfig>()?;
1177 let confidential_transfer_mint = mint.get_extension_mut::<ConfidentialTransferMint>()?;
1178
1179 for token_account_info in token_account_infos {
1180 match harvest_from_account(mint_account_info.key, token_account_info) {
1181 Ok(withheld_amount) => {
1182 let new_mint_withheld_amount = syscall::add(
1183 &confidential_transfer_mint.withheld_amount,
1184 &withheld_amount,
1185 )
1186 .ok_or(ProgramError::InvalidInstructionData)?;
1187
1188 confidential_transfer_mint.withheld_amount = new_mint_withheld_amount;
1189 }
1190 Err(e) => {
1191 msg!("Error harvesting from {}: {}", token_account_info.key, e);
1192 }
1193 }
1194 }
1195 Ok(())
1196}
1197
1198#[allow(dead_code)]
1199pub(crate) fn process_instruction(
1200 program_id: &Pubkey,
1201 accounts: &[AccountInfo],
1202 input: &[u8],
1203) -> ProgramResult {
1204 check_program_account(program_id)?;
1205
1206 match decode_instruction_type(input)? {
1207 ConfidentialTransferInstruction::InitializeMint => {
1208 msg!("ConfidentialTransferInstruction::InitializeMint");
1209 let data = decode_instruction_data::<InitializeMintData>(input)?;
1210 process_initialize_mint(
1211 accounts,
1212 &data.authority,
1213 data.auto_approve_new_accounts,
1214 &data.auditor_encryption_pubkey,
1215 &data.withdraw_withheld_authority_encryption_pubkey,
1216 )
1217 }
1218 ConfidentialTransferInstruction::UpdateMint => {
1219 msg!("ConfidentialTransferInstruction::UpdateMint");
1220 let data = decode_instruction_data::<UpdateMintData>(input)?;
1221 process_update_mint(
1222 accounts,
1223 data.auto_approve_new_accounts,
1224 &data.auditor_encryption_pubkey,
1225 )
1226 }
1227 ConfidentialTransferInstruction::ConfigureAccount => {
1228 msg!("ConfidentialTransferInstruction::ConfigureAccount");
1229 let data = decode_instruction_data::<ConfigureAccountInstructionData>(input)?;
1230 process_configure_account(
1231 program_id,
1232 accounts,
1233 &data.decryptable_zero_balance,
1234 &data.maximum_pending_balance_credit_counter,
1235 data.proof_instruction_offset as i64,
1236 )
1237 }
1238 ConfidentialTransferInstruction::ApproveAccount => {
1239 msg!("ConfidentialTransferInstruction::ApproveAccount");
1240 process_approve_account(accounts)
1241 }
1242 ConfidentialTransferInstruction::EmptyAccount => {
1243 msg!("ConfidentialTransferInstruction::EmptyAccount");
1244 let data = decode_instruction_data::<EmptyAccountInstructionData>(input)?;
1245 process_empty_account(program_id, accounts, data.proof_instruction_offset as i64)
1246 }
1247 ConfidentialTransferInstruction::Deposit => {
1248 msg!("ConfidentialTransferInstruction::Deposit");
1249 #[cfg(feature = "zk-ops")]
1250 {
1251 let data = decode_instruction_data::<DepositInstructionData>(input)?;
1252 process_deposit(program_id, accounts, data.amount.into(), data.decimals)
1253 }
1254 #[cfg(not(feature = "zk-ops"))]
1255 Err(ProgramError::InvalidInstructionData)
1256 }
1257 ConfidentialTransferInstruction::Withdraw => {
1258 msg!("ConfidentialTransferInstruction::Withdraw");
1259 #[cfg(feature = "zk-ops")]
1260 {
1261 let data = decode_instruction_data::<WithdrawInstructionData>(input)?;
1262 process_withdraw(
1263 program_id,
1264 accounts,
1265 data.amount.into(),
1266 data.decimals,
1267 data.new_decryptable_available_balance,
1268 data.proof_instruction_offset as i64,
1269 )
1270 }
1271 #[cfg(not(feature = "zk-ops"))]
1272 Err(ProgramError::InvalidInstructionData)
1273 }
1274 ConfidentialTransferInstruction::Transfer => {
1275 msg!("ConfidentialTransferInstruction::Transfer");
1276 #[cfg(feature = "zk-ops")]
1277 {
1278 let data = decode_instruction_data::<TransferInstructionData>(input)?;
1279 process_transfer(
1280 program_id,
1281 accounts,
1282 data.new_source_decryptable_available_balance,
1283 data.proof_instruction_offset as i64,
1284 )
1285 }
1286 #[cfg(not(feature = "zk-ops"))]
1287 Err(ProgramError::InvalidInstructionData)
1288 }
1289 ConfidentialTransferInstruction::ApplyPendingBalance => {
1290 msg!("ConfidentialTransferInstruction::ApplyPendingBalance");
1291 #[cfg(feature = "zk-ops")]
1292 {
1293 process_apply_pending_balance(
1294 program_id,
1295 accounts,
1296 decode_instruction_data::<ApplyPendingBalanceData>(input)?,
1297 )
1298 }
1299 #[cfg(not(feature = "zk-ops"))]
1300 {
1301 Err(ProgramError::InvalidInstructionData)
1302 }
1303 }
1304 ConfidentialTransferInstruction::DisableConfidentialCredits => {
1305 msg!("ConfidentialTransferInstruction::DisableConfidentialCredits");
1306 process_allow_confidential_credits(program_id, accounts, false)
1307 }
1308 ConfidentialTransferInstruction::EnableConfidentialCredits => {
1309 msg!("ConfidentialTransferInstruction::EnableConfidentialCredits");
1310 process_allow_confidential_credits(program_id, accounts, true)
1311 }
1312 ConfidentialTransferInstruction::DisableNonConfidentialCredits => {
1313 msg!("ConfidentialTransferInstruction::DisableNonConfidentialCredits");
1314 process_allow_non_confidential_credits(program_id, accounts, false)
1315 }
1316 ConfidentialTransferInstruction::EnableNonConfidentialCredits => {
1317 msg!("ConfidentialTransferInstruction::EnableNonConfidentialCredits");
1318 process_allow_non_confidential_credits(program_id, accounts, true)
1319 }
1320 ConfidentialTransferInstruction::WithdrawWithheldTokensFromMint => {
1321 msg!("ConfidentialTransferInstruction::WithdrawWithheldTokensFromMint");
1322 #[cfg(feature = "zk-ops")]
1323 {
1324 let data = decode_instruction_data::<WithdrawWithheldTokensFromMintData>(input)?;
1325 process_withdraw_withheld_tokens_from_mint(
1326 program_id,
1327 accounts,
1328 data.proof_instruction_offset as i64,
1329 )
1330 }
1331 #[cfg(not(feature = "zk-ops"))]
1332 Err(ProgramError::InvalidInstructionData)
1333 }
1334 ConfidentialTransferInstruction::WithdrawWithheldTokensFromAccounts => {
1335 msg!("ConfidentialTransferInstruction::WithdrawWithheldTokensFromAccounts");
1336 #[cfg(feature = "zk-ops")]
1337 {
1338 let data =
1339 decode_instruction_data::<WithdrawWithheldTokensFromAccountsData>(input)?;
1340 process_withdraw_withheld_tokens_from_accounts(
1341 program_id,
1342 accounts,
1343 data.num_token_accounts,
1344 data.proof_instruction_offset as i64,
1345 )
1346 }
1347 #[cfg(not(feature = "zk-ops"))]
1348 Err(ProgramError::InvalidInstructionData)
1349 }
1350 ConfidentialTransferInstruction::HarvestWithheldTokensToMint => {
1351 msg!("ConfidentialTransferInstruction::HarvestWithheldTokensToMint");
1352 #[cfg(feature = "zk-ops")]
1353 {
1354 process_harvest_withheld_tokens_to_mint(accounts)
1355 }
1356 #[cfg(not(feature = "zk-ops"))]
1357 {
1358 Err(ProgramError::InvalidInstructionData)
1359 }
1360 }
1361 }
1362}