spl_token_2022_interface/extension/confidential_transfer/
mod.rs1use {
2 crate::{
3 error::TokenError,
4 extension::{Extension, ExtensionType},
5 },
6 bytemuck::{Pod, Zeroable},
7 solana_program_error::ProgramResult,
8 solana_zk_sdk::encryption::pod::{
9 auth_encryption::PodAeCiphertext,
10 elgamal::{PodElGamalCiphertext, PodElGamalPubkey},
11 },
12 spl_pod::{
13 optional_keys::{OptionalNonZeroElGamalPubkey, OptionalNonZeroPubkey},
14 primitives::{PodBool, PodU64},
15 },
16};
17
18pub const MAXIMUM_DEPOSIT_TRANSFER_AMOUNT: u64 = (u16::MAX as u64) + (1 << 16) * (u32::MAX as u64);
22
23pub const PENDING_BALANCE_LO_BIT_LENGTH: u32 = 16;
25
26pub const DEFAULT_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER: u64 = 65536;
28
29pub mod instruction;
31
32pub type EncryptedBalance = PodElGamalCiphertext;
34pub type DecryptableBalance = PodAeCiphertext;
36
37#[repr(C)]
39#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
40pub struct ConfidentialTransferMint {
41 pub authority: OptionalNonZeroPubkey,
46
47 pub auto_approve_new_accounts: PodBool,
55
56 pub auditor_elgamal_pubkey: OptionalNonZeroElGamalPubkey,
58}
59
60impl Extension for ConfidentialTransferMint {
61 const TYPE: ExtensionType = ExtensionType::ConfidentialTransferMint;
62}
63
64#[repr(C)]
66#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
67pub struct ConfidentialTransferAccount {
68 pub approved: PodBool,
72
73 pub elgamal_pubkey: PodElGamalPubkey,
75
76 pub pending_balance_lo: EncryptedBalance,
78
79 pub pending_balance_hi: EncryptedBalance,
81
82 pub available_balance: EncryptedBalance,
84
85 pub decryptable_available_balance: DecryptableBalance,
87
88 pub allow_confidential_credits: PodBool,
91
92 pub allow_non_confidential_credits: PodBool,
94
95 pub pending_balance_credit_counter: PodU64,
98
99 pub maximum_pending_balance_credit_counter: PodU64,
103
104 pub expected_pending_balance_credit_counter: PodU64,
107
108 pub actual_pending_balance_credit_counter: PodU64,
111}
112
113impl Extension for ConfidentialTransferAccount {
114 const TYPE: ExtensionType = ExtensionType::ConfidentialTransferAccount;
115}
116
117impl ConfidentialTransferAccount {
118 pub fn approved(&self) -> ProgramResult {
120 if bool::from(&self.approved) {
121 Ok(())
122 } else {
123 Err(TokenError::ConfidentialTransferAccountNotApproved.into())
124 }
125 }
126
127 pub fn closable(&self) -> ProgramResult {
129 if self.pending_balance_lo == EncryptedBalance::zeroed()
130 && self.pending_balance_hi == EncryptedBalance::zeroed()
131 && self.available_balance == EncryptedBalance::zeroed()
132 {
133 Ok(())
134 } else {
135 Err(TokenError::ConfidentialTransferAccountHasBalance.into())
136 }
137 }
138
139 pub fn non_confidential_transfer_allowed(&self) -> ProgramResult {
142 if bool::from(&self.allow_non_confidential_credits) {
143 Ok(())
144 } else {
145 Err(TokenError::NonConfidentialTransfersDisabled.into())
146 }
147 }
148
149 pub fn valid_as_source(&self) -> ProgramResult {
151 self.approved()
152 }
153
154 pub fn valid_as_destination(&self) -> ProgramResult {
163 self.approved()?;
164
165 if !bool::from(self.allow_confidential_credits) {
166 return Err(TokenError::ConfidentialTransferDepositsAndTransfersDisabled.into());
167 }
168
169 let new_destination_pending_balance_credit_counter =
170 u64::from(self.pending_balance_credit_counter)
171 .checked_add(1)
172 .ok_or(TokenError::Overflow)?;
173 if new_destination_pending_balance_credit_counter
174 > u64::from(self.maximum_pending_balance_credit_counter)
175 {
176 return Err(TokenError::MaximumPendingBalanceCreditCounterExceeded.into());
177 }
178
179 Ok(())
180 }
181
182 pub fn increment_pending_balance_credit_counter(&mut self) -> ProgramResult {
184 self.pending_balance_credit_counter = (u64::from(self.pending_balance_credit_counter)
185 .checked_add(1)
186 .ok_or(TokenError::Overflow)?)
187 .into();
188 Ok(())
189 }
190}