1pub use solana_zk_sdk::zk_elgamal_proof_program::{
2 instruction::ProofInstruction, proof_data::*, state::ProofContextState,
3};
4#[cfg(feature = "serde")]
5use {
6 crate::serialization::{aeciphertext_fromstr, elgamalciphertext_fromstr},
7 serde::{Deserialize, Serialize},
8};
9use {
10 crate::{
11 check_program_account,
12 extension::confidential_transfer::*,
13 instruction::{encode_instruction, TokenInstruction},
14 },
15 bytemuck::Zeroable,
16 num_enum::{IntoPrimitive, TryFromPrimitive},
17 solana_instruction::{AccountMeta, Instruction},
18 solana_program_error::ProgramError,
19 solana_pubkey::Pubkey,
20 solana_sdk_ids::{system_program, sysvar},
21 spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation,
22};
23
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
26#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
27#[derive(Clone, Copy, Debug, TryFromPrimitive, IntoPrimitive)]
28#[repr(u8)]
29pub enum ConfidentialTransferInstruction {
30 InitializeMint,
47
48 UpdateMint,
61
62 ConfigureAccount,
105
106 ApproveAccount,
123
124 EmptyAccount,
165
166 Deposit,
195
196 Withdraw,
242
243 Transfer,
288
289 ApplyPendingBalance,
316
317 EnableConfidentialCredits,
335
336 DisableConfidentialCredits,
360
361 EnableNonConfidentialCredits,
379
380 DisableNonConfidentialCredits,
401
402 TransferWithFee,
460
461 ConfigureAccountWithRegistry,
491}
492
493#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
495#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
496#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
497#[repr(C)]
498pub struct InitializeMintData {
499 pub authority: OptionalNonZeroPubkey,
502 pub auto_approve_new_accounts: PodBool,
505 pub auditor_elgamal_pubkey: OptionalNonZeroElGamalPubkey,
507}
508
509#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
511#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
512#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
513#[repr(C)]
514pub struct UpdateMintData {
515 pub auto_approve_new_accounts: PodBool,
518 pub auditor_elgamal_pubkey: OptionalNonZeroElGamalPubkey,
520}
521
522#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
524#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
525#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
526#[repr(C)]
527pub struct ConfigureAccountInstructionData {
528 #[cfg_attr(feature = "serde", serde(with = "aeciphertext_fromstr"))]
530 pub decryptable_zero_balance: DecryptableBalance,
531 pub maximum_pending_balance_credit_counter: PodU64,
534 pub proof_instruction_offset: i8,
539}
540
541#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
543#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
544#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
545#[repr(C)]
546pub struct EmptyAccountInstructionData {
547 pub proof_instruction_offset: i8,
551}
552
553#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
555#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
556#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
557#[repr(C)]
558pub struct DepositInstructionData {
559 pub amount: PodU64,
561 pub decimals: u8,
563}
564
565#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
567#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
568#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
569#[repr(C)]
570pub struct WithdrawInstructionData {
571 pub amount: PodU64,
573 pub decimals: u8,
575 #[cfg_attr(feature = "serde", serde(with = "aeciphertext_fromstr"))]
577 pub new_decryptable_available_balance: DecryptableBalance,
578 pub equality_proof_instruction_offset: i8,
583 pub range_proof_instruction_offset: i8,
587}
588
589#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
591#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
592#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
593#[repr(C)]
594pub struct TransferInstructionData {
595 #[cfg_attr(feature = "serde", serde(with = "aeciphertext_fromstr"))]
597 pub new_source_decryptable_available_balance: DecryptableBalance,
598 #[cfg_attr(feature = "serde", serde(with = "elgamalciphertext_fromstr"))]
600 pub transfer_amount_auditor_ciphertext_lo: PodElGamalCiphertext,
601 #[cfg_attr(feature = "serde", serde(with = "elgamalciphertext_fromstr"))]
603 pub transfer_amount_auditor_ciphertext_hi: PodElGamalCiphertext,
604 pub equality_proof_instruction_offset: i8,
609 pub ciphertext_validity_proof_instruction_offset: i8,
614 pub range_proof_instruction_offset: i8,
618}
619
620#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
622#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
623#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
624#[repr(C)]
625pub struct ApplyPendingBalanceData {
626 pub expected_pending_balance_credit_counter: PodU64,
629 #[cfg_attr(feature = "serde", serde(with = "aeciphertext_fromstr"))]
632 pub new_decryptable_available_balance: DecryptableBalance,
633}
634
635#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
637#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
638#[repr(C)]
639pub struct TransferWithFeeInstructionData {
640 #[cfg_attr(feature = "serde", serde(with = "aeciphertext_fromstr"))]
642 pub new_source_decryptable_available_balance: DecryptableBalance,
643 #[cfg_attr(feature = "serde", serde(with = "elgamalciphertext_fromstr"))]
645 pub transfer_amount_auditor_ciphertext_lo: PodElGamalCiphertext,
646 #[cfg_attr(feature = "serde", serde(with = "elgamalciphertext_fromstr"))]
648 pub transfer_amount_auditor_ciphertext_hi: PodElGamalCiphertext,
649 pub equality_proof_instruction_offset: i8,
654 pub transfer_amount_ciphertext_validity_proof_instruction_offset: i8,
660 pub fee_sigma_proof_instruction_offset: i8,
665 pub fee_ciphertext_validity_proof_instruction_offset: i8,
671 pub range_proof_instruction_offset: i8,
676}
677
678pub fn initialize_mint(
680 token_program_id: &Pubkey,
681 mint: &Pubkey,
682 authority: Option<Pubkey>,
683 auto_approve_new_accounts: bool,
684 auditor_elgamal_pubkey: Option<PodElGamalPubkey>,
685) -> Result<Instruction, ProgramError> {
686 check_program_account(token_program_id)?;
687 let accounts = vec![AccountMeta::new(*mint, false)];
688
689 Ok(encode_instruction(
690 token_program_id,
691 accounts,
692 TokenInstruction::ConfidentialTransferExtension,
693 ConfidentialTransferInstruction::InitializeMint,
694 &InitializeMintData {
695 authority: authority.try_into()?,
696 auto_approve_new_accounts: auto_approve_new_accounts.into(),
697 auditor_elgamal_pubkey: auditor_elgamal_pubkey.try_into()?,
698 },
699 ))
700}
701
702pub fn update_mint(
704 token_program_id: &Pubkey,
705 mint: &Pubkey,
706 authority: &Pubkey,
707 multisig_signers: &[&Pubkey],
708 auto_approve_new_accounts: bool,
709 auditor_elgamal_pubkey: Option<PodElGamalPubkey>,
710) -> Result<Instruction, ProgramError> {
711 check_program_account(token_program_id)?;
712 let mut accounts = vec![
713 AccountMeta::new(*mint, false),
714 AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
715 ];
716 for multisig_signer in multisig_signers.iter() {
717 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
718 }
719 Ok(encode_instruction(
720 token_program_id,
721 accounts,
722 TokenInstruction::ConfidentialTransferExtension,
723 ConfidentialTransferInstruction::UpdateMint,
724 &UpdateMintData {
725 auto_approve_new_accounts: auto_approve_new_accounts.into(),
726 auditor_elgamal_pubkey: auditor_elgamal_pubkey.try_into()?,
727 },
728 ))
729}
730
731#[allow(clippy::too_many_arguments)]
735pub fn inner_configure_account(
736 token_program_id: &Pubkey,
737 token_account: &Pubkey,
738 mint: &Pubkey,
739 decryptable_zero_balance: &DecryptableBalance,
740 maximum_pending_balance_credit_counter: u64,
741 authority: &Pubkey,
742 multisig_signers: &[&Pubkey],
743 proof_data_location: ProofLocation<PubkeyValidityProofData>,
744) -> Result<Instruction, ProgramError> {
745 check_program_account(token_program_id)?;
746
747 let mut accounts = vec![
748 AccountMeta::new(*token_account, false),
749 AccountMeta::new_readonly(*mint, false),
750 ];
751
752 let proof_instruction_offset = match proof_data_location {
753 ProofLocation::InstructionOffset(proof_instruction_offset, _) => {
754 accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false));
755 proof_instruction_offset.into()
756 }
757 ProofLocation::ContextStateAccount(context_state_account) => {
758 accounts.push(AccountMeta::new_readonly(*context_state_account, false));
759 0
760 }
761 };
762
763 accounts.push(AccountMeta::new_readonly(
764 *authority,
765 multisig_signers.is_empty(),
766 ));
767
768 for multisig_signer in multisig_signers.iter() {
769 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
770 }
771
772 Ok(encode_instruction(
773 token_program_id,
774 accounts,
775 TokenInstruction::ConfidentialTransferExtension,
776 ConfidentialTransferInstruction::ConfigureAccount,
777 &ConfigureAccountInstructionData {
778 decryptable_zero_balance: *decryptable_zero_balance,
779 maximum_pending_balance_credit_counter: maximum_pending_balance_credit_counter.into(),
780 proof_instruction_offset,
781 },
782 ))
783}
784
785#[allow(clippy::too_many_arguments)]
787pub fn configure_account(
788 token_program_id: &Pubkey,
789 token_account: &Pubkey,
790 mint: &Pubkey,
791 decryptable_zero_balance: &DecryptableBalance,
792 maximum_pending_balance_credit_counter: u64,
793 authority: &Pubkey,
794 multisig_signers: &[&Pubkey],
795 proof_data_location: ProofLocation<PubkeyValidityProofData>,
796) -> Result<Vec<Instruction>, ProgramError> {
797 let mut instructions = vec![inner_configure_account(
798 token_program_id,
799 token_account,
800 mint,
801 decryptable_zero_balance,
802 maximum_pending_balance_credit_counter,
803 authority,
804 multisig_signers,
805 proof_data_location,
806 )?];
807
808 if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) =
809 proof_data_location
810 {
811 let proof_instruction_offset: i8 = proof_instruction_offset.into();
816 if proof_instruction_offset != 1 {
817 return Err(TokenError::InvalidProofInstructionOffset.into());
818 }
819 instructions
820 .push(ProofInstruction::VerifyPubkeyValidity.encode_verify_proof(None, proof_data));
821 }
822
823 Ok(instructions)
824}
825
826pub fn approve_account(
828 token_program_id: &Pubkey,
829 account_to_approve: &Pubkey,
830 mint: &Pubkey,
831 authority: &Pubkey,
832 multisig_signers: &[&Pubkey],
833) -> Result<Instruction, ProgramError> {
834 check_program_account(token_program_id)?;
835 let mut accounts = vec![
836 AccountMeta::new(*account_to_approve, false),
837 AccountMeta::new_readonly(*mint, false),
838 AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
839 ];
840 for multisig_signer in multisig_signers.iter() {
841 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
842 }
843 Ok(encode_instruction(
844 token_program_id,
845 accounts,
846 TokenInstruction::ConfidentialTransferExtension,
847 ConfidentialTransferInstruction::ApproveAccount,
848 &(),
849 ))
850}
851
852pub fn inner_empty_account(
856 token_program_id: &Pubkey,
857 token_account: &Pubkey,
858 authority: &Pubkey,
859 multisig_signers: &[&Pubkey],
860 proof_data_location: ProofLocation<ZeroCiphertextProofData>,
861) -> Result<Instruction, ProgramError> {
862 check_program_account(token_program_id)?;
863 let mut accounts = vec![AccountMeta::new(*token_account, false)];
864
865 let proof_instruction_offset = match proof_data_location {
866 ProofLocation::InstructionOffset(proof_instruction_offset, _) => {
867 accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false));
868 proof_instruction_offset.into()
869 }
870 ProofLocation::ContextStateAccount(context_state_account) => {
871 accounts.push(AccountMeta::new_readonly(*context_state_account, false));
872 0
873 }
874 };
875
876 accounts.push(AccountMeta::new_readonly(
877 *authority,
878 multisig_signers.is_empty(),
879 ));
880
881 for multisig_signer in multisig_signers.iter() {
882 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
883 }
884
885 Ok(encode_instruction(
886 token_program_id,
887 accounts,
888 TokenInstruction::ConfidentialTransferExtension,
889 ConfidentialTransferInstruction::EmptyAccount,
890 &EmptyAccountInstructionData {
891 proof_instruction_offset,
892 },
893 ))
894}
895
896pub fn empty_account(
898 token_program_id: &Pubkey,
899 token_account: &Pubkey,
900 authority: &Pubkey,
901 multisig_signers: &[&Pubkey],
902 proof_data_location: ProofLocation<ZeroCiphertextProofData>,
903) -> Result<Vec<Instruction>, ProgramError> {
904 let mut instructions = vec![inner_empty_account(
905 token_program_id,
906 token_account,
907 authority,
908 multisig_signers,
909 proof_data_location,
910 )?];
911
912 if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) =
913 proof_data_location
914 {
915 let proof_instruction_offset: i8 = proof_instruction_offset.into();
920 if proof_instruction_offset != 1 {
921 return Err(TokenError::InvalidProofInstructionOffset.into());
922 }
923 instructions
924 .push(ProofInstruction::VerifyZeroCiphertext.encode_verify_proof(None, proof_data));
925 };
926
927 Ok(instructions)
928}
929
930#[allow(clippy::too_many_arguments)]
932pub fn deposit(
933 token_program_id: &Pubkey,
934 token_account: &Pubkey,
935 mint: &Pubkey,
936 amount: u64,
937 decimals: u8,
938 authority: &Pubkey,
939 multisig_signers: &[&Pubkey],
940) -> Result<Instruction, ProgramError> {
941 check_program_account(token_program_id)?;
942 let mut accounts = vec![
943 AccountMeta::new(*token_account, false),
944 AccountMeta::new_readonly(*mint, false),
945 AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
946 ];
947
948 for multisig_signer in multisig_signers.iter() {
949 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
950 }
951
952 Ok(encode_instruction(
953 token_program_id,
954 accounts,
955 TokenInstruction::ConfidentialTransferExtension,
956 ConfidentialTransferInstruction::Deposit,
957 &DepositInstructionData {
958 amount: amount.into(),
959 decimals,
960 },
961 ))
962}
963
964#[allow(clippy::too_many_arguments)]
968pub fn inner_withdraw(
969 token_program_id: &Pubkey,
970 token_account: &Pubkey,
971 mint: &Pubkey,
972 amount: u64,
973 decimals: u8,
974 new_decryptable_available_balance: &DecryptableBalance,
975 authority: &Pubkey,
976 multisig_signers: &[&Pubkey],
977 equality_proof_data_location: ProofLocation<CiphertextCommitmentEqualityProofData>,
978 range_proof_data_location: ProofLocation<BatchedRangeProofU64Data>,
979) -> Result<Instruction, ProgramError> {
980 check_program_account(token_program_id)?;
981 let mut accounts = vec![
982 AccountMeta::new(*token_account, false),
983 AccountMeta::new_readonly(*mint, false),
984 ];
985
986 if equality_proof_data_location.is_instruction_offset()
989 || range_proof_data_location.is_instruction_offset()
990 {
991 accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false));
992 }
993
994 let equality_proof_instruction_offset = match equality_proof_data_location {
995 ProofLocation::InstructionOffset(proof_instruction_offset, _) => {
996 proof_instruction_offset.into()
997 }
998 ProofLocation::ContextStateAccount(context_state_account) => {
999 accounts.push(AccountMeta::new_readonly(*context_state_account, false));
1000 0
1001 }
1002 };
1003
1004 let range_proof_instruction_offset = match range_proof_data_location {
1005 ProofLocation::InstructionOffset(proof_instruction_offset, _) => {
1006 proof_instruction_offset.into()
1007 }
1008 ProofLocation::ContextStateAccount(context_state_account) => {
1009 accounts.push(AccountMeta::new_readonly(*context_state_account, false));
1010 0
1011 }
1012 };
1013
1014 accounts.push(AccountMeta::new_readonly(
1015 *authority,
1016 multisig_signers.is_empty(),
1017 ));
1018
1019 for multisig_signer in multisig_signers.iter() {
1020 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
1021 }
1022
1023 Ok(encode_instruction(
1024 token_program_id,
1025 accounts,
1026 TokenInstruction::ConfidentialTransferExtension,
1027 ConfidentialTransferInstruction::Withdraw,
1028 &WithdrawInstructionData {
1029 amount: amount.into(),
1030 decimals,
1031 new_decryptable_available_balance: *new_decryptable_available_balance,
1032 equality_proof_instruction_offset,
1033 range_proof_instruction_offset,
1034 },
1035 ))
1036}
1037
1038#[allow(clippy::too_many_arguments)]
1040pub fn withdraw(
1041 token_program_id: &Pubkey,
1042 token_account: &Pubkey,
1043 mint: &Pubkey,
1044 amount: u64,
1045 decimals: u8,
1046 new_decryptable_available_balance: &DecryptableBalance,
1047 authority: &Pubkey,
1048 multisig_signers: &[&Pubkey],
1049 equality_proof_data_location: ProofLocation<CiphertextCommitmentEqualityProofData>,
1050 range_proof_data_location: ProofLocation<BatchedRangeProofU64Data>,
1051) -> Result<Vec<Instruction>, ProgramError> {
1052 let mut instructions = vec![inner_withdraw(
1053 token_program_id,
1054 token_account,
1055 mint,
1056 amount,
1057 decimals,
1058 new_decryptable_available_balance,
1059 authority,
1060 multisig_signers,
1061 equality_proof_data_location,
1062 range_proof_data_location,
1063 )?];
1064
1065 let mut expected_instruction_offset = 1;
1066
1067 if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) =
1068 equality_proof_data_location
1069 {
1070 let proof_instruction_offset: i8 = proof_instruction_offset.into();
1071 if proof_instruction_offset != expected_instruction_offset {
1072 return Err(TokenError::InvalidProofInstructionOffset.into());
1073 }
1074 instructions.push(
1075 ProofInstruction::VerifyCiphertextCommitmentEquality
1076 .encode_verify_proof(None, proof_data),
1077 );
1078 expected_instruction_offset += 1;
1079 };
1080
1081 if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) =
1082 range_proof_data_location
1083 {
1084 let proof_instruction_offset: i8 = proof_instruction_offset.into();
1085 if proof_instruction_offset != expected_instruction_offset {
1086 return Err(TokenError::InvalidProofInstructionOffset.into());
1087 }
1088 instructions.push(
1089 ProofInstruction::VerifyBatchedRangeProofU64.encode_verify_proof(None, proof_data),
1090 );
1091 };
1092
1093 Ok(instructions)
1094}
1095
1096#[allow(clippy::too_many_arguments)]
1100pub fn inner_transfer(
1101 token_program_id: &Pubkey,
1102 source_token_account: &Pubkey,
1103 mint: &Pubkey,
1104 destination_token_account: &Pubkey,
1105 new_source_decryptable_available_balance: &DecryptableBalance,
1106 transfer_amount_auditor_ciphertext_lo: &PodElGamalCiphertext,
1107 transfer_amount_auditor_ciphertext_hi: &PodElGamalCiphertext,
1108 authority: &Pubkey,
1109 multisig_signers: &[&Pubkey],
1110 equality_proof_data_location: ProofLocation<CiphertextCommitmentEqualityProofData>,
1111 ciphertext_validity_proof_data_location: ProofLocation<
1112 BatchedGroupedCiphertext3HandlesValidityProofData,
1113 >,
1114 range_proof_data_location: ProofLocation<BatchedRangeProofU128Data>,
1115) -> Result<Instruction, ProgramError> {
1116 check_program_account(token_program_id)?;
1117 let mut accounts = vec![
1118 AccountMeta::new(*source_token_account, false),
1119 AccountMeta::new_readonly(*mint, false),
1120 AccountMeta::new(*destination_token_account, false),
1121 ];
1122
1123 if equality_proof_data_location.is_instruction_offset()
1126 || ciphertext_validity_proof_data_location.is_instruction_offset()
1127 || range_proof_data_location.is_instruction_offset()
1128 {
1129 accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false));
1130 }
1131
1132 let equality_proof_instruction_offset = match equality_proof_data_location {
1133 ProofLocation::InstructionOffset(proof_instruction_offset, _) => {
1134 proof_instruction_offset.into()
1135 }
1136 ProofLocation::ContextStateAccount(context_state_account) => {
1137 accounts.push(AccountMeta::new_readonly(*context_state_account, false));
1138 0
1139 }
1140 };
1141
1142 let ciphertext_validity_proof_instruction_offset = match ciphertext_validity_proof_data_location
1143 {
1144 ProofLocation::InstructionOffset(proof_instruction_offset, _) => {
1145 proof_instruction_offset.into()
1146 }
1147 ProofLocation::ContextStateAccount(context_state_account) => {
1148 accounts.push(AccountMeta::new_readonly(*context_state_account, false));
1149 0
1150 }
1151 };
1152
1153 let range_proof_instruction_offset = match range_proof_data_location {
1154 ProofLocation::InstructionOffset(proof_instruction_offset, _) => {
1155 proof_instruction_offset.into()
1156 }
1157 ProofLocation::ContextStateAccount(context_state_account) => {
1158 accounts.push(AccountMeta::new_readonly(*context_state_account, false));
1159 0
1160 }
1161 };
1162
1163 accounts.push(AccountMeta::new_readonly(
1164 *authority,
1165 multisig_signers.is_empty(),
1166 ));
1167
1168 for multisig_signer in multisig_signers.iter() {
1169 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
1170 }
1171
1172 Ok(encode_instruction(
1173 token_program_id,
1174 accounts,
1175 TokenInstruction::ConfidentialTransferExtension,
1176 ConfidentialTransferInstruction::Transfer,
1177 &TransferInstructionData {
1178 new_source_decryptable_available_balance: *new_source_decryptable_available_balance,
1179 transfer_amount_auditor_ciphertext_lo: *transfer_amount_auditor_ciphertext_lo,
1180 transfer_amount_auditor_ciphertext_hi: *transfer_amount_auditor_ciphertext_hi,
1181 equality_proof_instruction_offset,
1182 ciphertext_validity_proof_instruction_offset,
1183 range_proof_instruction_offset,
1184 },
1185 ))
1186}
1187
1188#[allow(clippy::too_many_arguments)]
1190pub fn transfer(
1191 token_program_id: &Pubkey,
1192 source_token_account: &Pubkey,
1193 mint: &Pubkey,
1194 destination_token_account: &Pubkey,
1195 new_source_decryptable_available_balance: &DecryptableBalance,
1196 transfer_amount_auditor_ciphertext_lo: &PodElGamalCiphertext,
1197 transfer_amount_auditor_ciphertext_hi: &PodElGamalCiphertext,
1198 authority: &Pubkey,
1199 multisig_signers: &[&Pubkey],
1200 equality_proof_data_location: ProofLocation<CiphertextCommitmentEqualityProofData>,
1201 ciphertext_validity_proof_data_location: ProofLocation<
1202 BatchedGroupedCiphertext3HandlesValidityProofData,
1203 >,
1204 range_proof_data_location: ProofLocation<BatchedRangeProofU128Data>,
1205) -> Result<Vec<Instruction>, ProgramError> {
1206 let mut instructions = vec![inner_transfer(
1207 token_program_id,
1208 source_token_account,
1209 mint,
1210 destination_token_account,
1211 new_source_decryptable_available_balance,
1212 transfer_amount_auditor_ciphertext_lo,
1213 transfer_amount_auditor_ciphertext_hi,
1214 authority,
1215 multisig_signers,
1216 equality_proof_data_location,
1217 ciphertext_validity_proof_data_location,
1218 range_proof_data_location,
1219 )?];
1220
1221 let mut expected_instruction_offset = 1;
1222
1223 if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) =
1224 equality_proof_data_location
1225 {
1226 let proof_instruction_offset: i8 = proof_instruction_offset.into();
1227 if proof_instruction_offset != expected_instruction_offset {
1228 return Err(TokenError::InvalidProofInstructionOffset.into());
1229 }
1230 instructions.push(
1231 ProofInstruction::VerifyCiphertextCommitmentEquality
1232 .encode_verify_proof(None, proof_data),
1233 );
1234 expected_instruction_offset += 1;
1235 }
1236
1237 if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) =
1238 ciphertext_validity_proof_data_location
1239 {
1240 let proof_instruction_offset: i8 = proof_instruction_offset.into();
1241 if proof_instruction_offset != expected_instruction_offset {
1242 return Err(TokenError::InvalidProofInstructionOffset.into());
1243 }
1244 instructions.push(
1245 ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity
1246 .encode_verify_proof(None, proof_data),
1247 );
1248 expected_instruction_offset += 1;
1249 }
1250
1251 if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) =
1252 range_proof_data_location
1253 {
1254 let proof_instruction_offset: i8 = proof_instruction_offset.into();
1255 if proof_instruction_offset != expected_instruction_offset {
1256 return Err(TokenError::InvalidProofInstructionOffset.into());
1257 }
1258 instructions.push(
1259 ProofInstruction::VerifyBatchedRangeProofU128.encode_verify_proof(None, proof_data),
1260 );
1261 }
1262
1263 Ok(instructions)
1264}
1265
1266pub fn inner_apply_pending_balance(
1270 token_program_id: &Pubkey,
1271 token_account: &Pubkey,
1272 expected_pending_balance_credit_counter: u64,
1273 new_decryptable_available_balance: &DecryptableBalance,
1274 authority: &Pubkey,
1275 multisig_signers: &[&Pubkey],
1276) -> Result<Instruction, ProgramError> {
1277 check_program_account(token_program_id)?;
1278 let mut accounts = vec![
1279 AccountMeta::new(*token_account, false),
1280 AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
1281 ];
1282
1283 for multisig_signer in multisig_signers.iter() {
1284 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
1285 }
1286
1287 Ok(encode_instruction(
1288 token_program_id,
1289 accounts,
1290 TokenInstruction::ConfidentialTransferExtension,
1291 ConfidentialTransferInstruction::ApplyPendingBalance,
1292 &ApplyPendingBalanceData {
1293 expected_pending_balance_credit_counter: expected_pending_balance_credit_counter.into(),
1294 new_decryptable_available_balance: *new_decryptable_available_balance,
1295 },
1296 ))
1297}
1298
1299pub fn apply_pending_balance(
1301 token_program_id: &Pubkey,
1302 token_account: &Pubkey,
1303 pending_balance_instructions: u64,
1304 new_decryptable_available_balance: &DecryptableBalance,
1305 authority: &Pubkey,
1306 multisig_signers: &[&Pubkey],
1307) -> Result<Instruction, ProgramError> {
1308 inner_apply_pending_balance(
1309 token_program_id,
1310 token_account,
1311 pending_balance_instructions,
1312 new_decryptable_available_balance,
1313 authority,
1314 multisig_signers,
1315 ) }
1317
1318fn enable_or_disable_balance_credits(
1319 instruction: ConfidentialTransferInstruction,
1320 token_program_id: &Pubkey,
1321 token_account: &Pubkey,
1322 authority: &Pubkey,
1323 multisig_signers: &[&Pubkey],
1324) -> Result<Instruction, ProgramError> {
1325 check_program_account(token_program_id)?;
1326 let mut accounts = vec![
1327 AccountMeta::new(*token_account, false),
1328 AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
1329 ];
1330
1331 for multisig_signer in multisig_signers.iter() {
1332 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
1333 }
1334
1335 Ok(encode_instruction(
1336 token_program_id,
1337 accounts,
1338 TokenInstruction::ConfidentialTransferExtension,
1339 instruction,
1340 &(),
1341 ))
1342}
1343
1344pub fn enable_confidential_credits(
1346 token_program_id: &Pubkey,
1347 token_account: &Pubkey,
1348 authority: &Pubkey,
1349 multisig_signers: &[&Pubkey],
1350) -> Result<Instruction, ProgramError> {
1351 enable_or_disable_balance_credits(
1352 ConfidentialTransferInstruction::EnableConfidentialCredits,
1353 token_program_id,
1354 token_account,
1355 authority,
1356 multisig_signers,
1357 )
1358}
1359
1360pub fn disable_confidential_credits(
1362 token_program_id: &Pubkey,
1363 token_account: &Pubkey,
1364 authority: &Pubkey,
1365 multisig_signers: &[&Pubkey],
1366) -> Result<Instruction, ProgramError> {
1367 enable_or_disable_balance_credits(
1368 ConfidentialTransferInstruction::DisableConfidentialCredits,
1369 token_program_id,
1370 token_account,
1371 authority,
1372 multisig_signers,
1373 )
1374}
1375
1376pub fn enable_non_confidential_credits(
1378 token_program_id: &Pubkey,
1379 token_account: &Pubkey,
1380 authority: &Pubkey,
1381 multisig_signers: &[&Pubkey],
1382) -> Result<Instruction, ProgramError> {
1383 enable_or_disable_balance_credits(
1384 ConfidentialTransferInstruction::EnableNonConfidentialCredits,
1385 token_program_id,
1386 token_account,
1387 authority,
1388 multisig_signers,
1389 )
1390}
1391
1392pub fn disable_non_confidential_credits(
1394 token_program_id: &Pubkey,
1395 token_account: &Pubkey,
1396 authority: &Pubkey,
1397 multisig_signers: &[&Pubkey],
1398) -> Result<Instruction, ProgramError> {
1399 enable_or_disable_balance_credits(
1400 ConfidentialTransferInstruction::DisableNonConfidentialCredits,
1401 token_program_id,
1402 token_account,
1403 authority,
1404 multisig_signers,
1405 )
1406}
1407
1408#[allow(clippy::too_many_arguments)]
1412pub fn inner_transfer_with_fee(
1413 token_program_id: &Pubkey,
1414 source_token_account: &Pubkey,
1415 mint: &Pubkey,
1416 destination_token_account: &Pubkey,
1417 new_source_decryptable_available_balance: &DecryptableBalance,
1418 transfer_amount_auditor_ciphertext_lo: &PodElGamalCiphertext,
1419 transfer_amount_auditor_ciphertext_hi: &PodElGamalCiphertext,
1420 authority: &Pubkey,
1421 multisig_signers: &[&Pubkey],
1422 equality_proof_data_location: ProofLocation<CiphertextCommitmentEqualityProofData>,
1423 transfer_amount_ciphertext_validity_proof_data_location: ProofLocation<
1424 BatchedGroupedCiphertext3HandlesValidityProofData,
1425 >,
1426 fee_sigma_proof_data_location: ProofLocation<PercentageWithCapProofData>,
1427 fee_ciphertext_validity_proof_data_location: ProofLocation<
1428 BatchedGroupedCiphertext2HandlesValidityProofData,
1429 >,
1430 range_proof_data_location: ProofLocation<BatchedRangeProofU256Data>,
1431) -> Result<Instruction, ProgramError> {
1432 check_program_account(token_program_id)?;
1433 let mut accounts = vec![
1434 AccountMeta::new(*source_token_account, false),
1435 AccountMeta::new_readonly(*mint, false),
1436 AccountMeta::new(*destination_token_account, false),
1437 ];
1438
1439 if equality_proof_data_location.is_instruction_offset()
1442 || transfer_amount_ciphertext_validity_proof_data_location.is_instruction_offset()
1443 || fee_sigma_proof_data_location.is_instruction_offset()
1444 || fee_ciphertext_validity_proof_data_location.is_instruction_offset()
1445 || range_proof_data_location.is_instruction_offset()
1446 {
1447 accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false));
1448 }
1449
1450 let equality_proof_instruction_offset = match equality_proof_data_location {
1451 ProofLocation::InstructionOffset(proof_instruction_offset, _) => {
1452 proof_instruction_offset.into()
1453 }
1454 ProofLocation::ContextStateAccount(context_state_account) => {
1455 accounts.push(AccountMeta::new_readonly(*context_state_account, false));
1456 0
1457 }
1458 };
1459
1460 let transfer_amount_ciphertext_validity_proof_instruction_offset =
1461 match transfer_amount_ciphertext_validity_proof_data_location {
1462 ProofLocation::InstructionOffset(proof_instruction_offset, _) => {
1463 proof_instruction_offset.into()
1464 }
1465 ProofLocation::ContextStateAccount(context_state_account) => {
1466 accounts.push(AccountMeta::new_readonly(*context_state_account, false));
1467 0
1468 }
1469 };
1470
1471 let fee_sigma_proof_instruction_offset = match fee_sigma_proof_data_location {
1472 ProofLocation::InstructionOffset(proof_instruction_offset, _) => {
1473 proof_instruction_offset.into()
1474 }
1475 ProofLocation::ContextStateAccount(context_state_account) => {
1476 accounts.push(AccountMeta::new_readonly(*context_state_account, false));
1477 0
1478 }
1479 };
1480
1481 let fee_ciphertext_validity_proof_instruction_offset =
1482 match fee_ciphertext_validity_proof_data_location {
1483 ProofLocation::InstructionOffset(proof_instruction_offset, _) => {
1484 proof_instruction_offset.into()
1485 }
1486 ProofLocation::ContextStateAccount(context_state_account) => {
1487 accounts.push(AccountMeta::new_readonly(*context_state_account, false));
1488 0
1489 }
1490 };
1491
1492 let range_proof_instruction_offset = match range_proof_data_location {
1493 ProofLocation::InstructionOffset(proof_instruction_offset, _) => {
1494 proof_instruction_offset.into()
1495 }
1496 ProofLocation::ContextStateAccount(context_state_account) => {
1497 accounts.push(AccountMeta::new_readonly(*context_state_account, false));
1498 0
1499 }
1500 };
1501
1502 accounts.push(AccountMeta::new_readonly(
1503 *authority,
1504 multisig_signers.is_empty(),
1505 ));
1506
1507 for multisig_signer in multisig_signers.iter() {
1508 accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
1509 }
1510
1511 Ok(encode_instruction(
1512 token_program_id,
1513 accounts,
1514 TokenInstruction::ConfidentialTransferExtension,
1515 ConfidentialTransferInstruction::TransferWithFee,
1516 &TransferWithFeeInstructionData {
1517 new_source_decryptable_available_balance: *new_source_decryptable_available_balance,
1518 transfer_amount_auditor_ciphertext_lo: *transfer_amount_auditor_ciphertext_lo,
1519 transfer_amount_auditor_ciphertext_hi: *transfer_amount_auditor_ciphertext_hi,
1520 equality_proof_instruction_offset,
1521 transfer_amount_ciphertext_validity_proof_instruction_offset,
1522 fee_sigma_proof_instruction_offset,
1523 fee_ciphertext_validity_proof_instruction_offset,
1524 range_proof_instruction_offset,
1525 },
1526 ))
1527}
1528
1529#[allow(clippy::too_many_arguments)]
1531pub fn transfer_with_fee(
1532 token_program_id: &Pubkey,
1533 source_token_account: &Pubkey,
1534 mint: &Pubkey,
1535 destination_token_account: &Pubkey,
1536 new_source_decryptable_available_balance: &DecryptableBalance,
1537 transfer_amount_auditor_ciphertext_lo: &PodElGamalCiphertext,
1538 transfer_amount_auditor_ciphertext_hi: &PodElGamalCiphertext,
1539 authority: &Pubkey,
1540 multisig_signers: &[&Pubkey],
1541 equality_proof_data_location: ProofLocation<CiphertextCommitmentEqualityProofData>,
1542 transfer_amount_ciphertext_validity_proof_data_location: ProofLocation<
1543 BatchedGroupedCiphertext3HandlesValidityProofData,
1544 >,
1545 fee_sigma_proof_data_location: ProofLocation<PercentageWithCapProofData>,
1546 fee_ciphertext_validity_proof_data_location: ProofLocation<
1547 BatchedGroupedCiphertext2HandlesValidityProofData,
1548 >,
1549 range_proof_data_location: ProofLocation<BatchedRangeProofU256Data>,
1550) -> Result<Vec<Instruction>, ProgramError> {
1551 let mut instructions = vec![inner_transfer_with_fee(
1552 token_program_id,
1553 source_token_account,
1554 mint,
1555 destination_token_account,
1556 new_source_decryptable_available_balance,
1557 transfer_amount_auditor_ciphertext_lo,
1558 transfer_amount_auditor_ciphertext_hi,
1559 authority,
1560 multisig_signers,
1561 equality_proof_data_location,
1562 transfer_amount_ciphertext_validity_proof_data_location,
1563 fee_sigma_proof_data_location,
1564 fee_ciphertext_validity_proof_data_location,
1565 range_proof_data_location,
1566 )?];
1567
1568 let mut expected_instruction_offset = 1;
1569
1570 if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) =
1571 equality_proof_data_location
1572 {
1573 let proof_instruction_offset: i8 = proof_instruction_offset.into();
1574 if proof_instruction_offset != expected_instruction_offset {
1575 return Err(TokenError::InvalidProofInstructionOffset.into());
1576 }
1577 instructions.push(
1578 ProofInstruction::VerifyCiphertextCommitmentEquality
1579 .encode_verify_proof(None, proof_data),
1580 );
1581 expected_instruction_offset += 1;
1582 }
1583
1584 if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) =
1585 transfer_amount_ciphertext_validity_proof_data_location
1586 {
1587 let proof_instruction_offset: i8 = proof_instruction_offset.into();
1588 if proof_instruction_offset != expected_instruction_offset {
1589 return Err(TokenError::InvalidProofInstructionOffset.into());
1590 }
1591 instructions.push(
1592 ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity
1593 .encode_verify_proof(None, proof_data),
1594 );
1595 expected_instruction_offset += 1;
1596 }
1597
1598 if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) =
1599 fee_sigma_proof_data_location
1600 {
1601 let proof_instruction_offset: i8 = proof_instruction_offset.into();
1602 if proof_instruction_offset != expected_instruction_offset {
1603 return Err(TokenError::InvalidProofInstructionOffset.into());
1604 }
1605 instructions
1606 .push(ProofInstruction::VerifyPercentageWithCap.encode_verify_proof(None, proof_data));
1607 expected_instruction_offset += 1;
1608 }
1609
1610 if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) =
1611 fee_ciphertext_validity_proof_data_location
1612 {
1613 let proof_instruction_offset: i8 = proof_instruction_offset.into();
1614 if proof_instruction_offset != expected_instruction_offset {
1615 return Err(TokenError::InvalidProofInstructionOffset.into());
1616 }
1617 instructions.push(
1618 ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity
1619 .encode_verify_proof(None, proof_data),
1620 );
1621 expected_instruction_offset += 1;
1622 }
1623
1624 if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) =
1625 range_proof_data_location
1626 {
1627 let proof_instruction_offset: i8 = proof_instruction_offset.into();
1628 if proof_instruction_offset != expected_instruction_offset {
1629 return Err(TokenError::InvalidProofInstructionOffset.into());
1630 }
1631 instructions.push(
1632 ProofInstruction::VerifyBatchedRangeProofU256.encode_verify_proof(None, proof_data),
1633 );
1634 }
1635
1636 Ok(instructions)
1637}
1638
1639pub fn configure_account_with_registry(
1641 token_program_id: &Pubkey,
1642 token_account: &Pubkey,
1643 mint: &Pubkey,
1644 elgamal_registry_account: &Pubkey,
1645 payer: Option<&Pubkey>,
1646) -> Result<Instruction, ProgramError> {
1647 check_program_account(token_program_id)?;
1648 let mut accounts = vec![
1649 AccountMeta::new(*token_account, false),
1650 AccountMeta::new_readonly(*mint, false),
1651 AccountMeta::new_readonly(*elgamal_registry_account, false),
1652 ];
1653 if let Some(payer) = payer {
1654 accounts.push(AccountMeta::new(*payer, true));
1655 accounts.push(AccountMeta::new_readonly(system_program::id(), false));
1656 }
1657
1658 Ok(encode_instruction(
1659 token_program_id,
1660 accounts,
1661 TokenInstruction::ConfidentialTransferExtension,
1662 ConfidentialTransferInstruction::ConfigureAccountWithRegistry,
1663 &(),
1664 ))
1665}