use {
crate::{
error::TokenError,
extension::{Extension, ExtensionType},
},
bytemuck::{Pod, Zeroable},
solana_address::Address,
solana_nullable::MaybeNull,
solana_program_error::ProgramResult,
solana_zero_copy::unaligned::{Bool, U64},
solana_zk_sdk_pod::encryption::{
auth_encryption::PodAeCiphertext,
elgamal::{PodElGamalCiphertext, PodElGamalPubkey},
},
};
pub const MAXIMUM_DEPOSIT_TRANSFER_AMOUNT: u64 = (u16::MAX as u64) + (1 << 16) * (u32::MAX as u64);
pub const PENDING_BALANCE_LO_BIT_LENGTH: u32 = 16;
pub const DEFAULT_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER: u64 = 65536;
pub mod instruction;
pub type EncryptedBalance = PodElGamalCiphertext;
pub type DecryptableBalance = PodAeCiphertext;
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct ConfidentialTransferMint {
pub authority: MaybeNull<Address>,
pub auto_approve_new_accounts: Bool,
pub auditor_elgamal_pubkey: MaybeNull<PodElGamalPubkey>,
}
impl Extension for ConfidentialTransferMint {
const TYPE: ExtensionType = ExtensionType::ConfidentialTransferMint;
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct ConfidentialTransferAccount {
pub approved: Bool,
pub elgamal_pubkey: PodElGamalPubkey,
pub pending_balance_lo: EncryptedBalance,
pub pending_balance_hi: EncryptedBalance,
pub available_balance: EncryptedBalance,
pub decryptable_available_balance: DecryptableBalance,
pub allow_confidential_credits: Bool,
pub allow_non_confidential_credits: Bool,
pub pending_balance_credit_counter: U64,
pub maximum_pending_balance_credit_counter: U64,
pub expected_pending_balance_credit_counter: U64,
pub actual_pending_balance_credit_counter: U64,
}
impl Extension for ConfidentialTransferAccount {
const TYPE: ExtensionType = ExtensionType::ConfidentialTransferAccount;
}
impl ConfidentialTransferAccount {
pub fn approved(&self) -> ProgramResult {
if bool::from(&self.approved) {
Ok(())
} else {
Err(TokenError::ConfidentialTransferAccountNotApproved.into())
}
}
pub fn closable(&self) -> ProgramResult {
if self.pending_balance_lo == EncryptedBalance::zeroed()
&& self.pending_balance_hi == EncryptedBalance::zeroed()
&& self.available_balance == EncryptedBalance::zeroed()
{
Ok(())
} else {
Err(TokenError::ConfidentialTransferAccountHasBalance.into())
}
}
pub fn non_confidential_transfer_allowed(&self) -> ProgramResult {
if bool::from(&self.allow_non_confidential_credits) {
Ok(())
} else {
Err(TokenError::NonConfidentialTransfersDisabled.into())
}
}
pub fn valid_as_source(&self) -> ProgramResult {
self.approved()
}
pub fn valid_as_destination(&self) -> ProgramResult {
self.approved()?;
if !bool::from(self.allow_confidential_credits) {
return Err(TokenError::ConfidentialTransferDepositsAndTransfersDisabled.into());
}
let new_destination_pending_balance_credit_counter =
u64::from(self.pending_balance_credit_counter)
.checked_add(1)
.ok_or(TokenError::Overflow)?;
if new_destination_pending_balance_credit_counter
> u64::from(self.maximum_pending_balance_credit_counter)
{
return Err(TokenError::MaximumPendingBalanceCreditCounterExceeded.into());
}
Ok(())
}
pub fn valid_as_withheld_amount_destination(&self) -> ProgramResult {
self.approved()?;
if !bool::from(self.allow_confidential_credits) {
return Err(TokenError::ConfidentialTransferDepositsAndTransfersDisabled.into());
}
Ok(())
}
pub fn increment_pending_balance_credit_counter(&mut self) -> ProgramResult {
self.pending_balance_credit_counter = (u64::from(self.pending_balance_credit_counter)
.checked_add(1)
.ok_or(TokenError::Overflow)?)
.into();
Ok(())
}
}