#[cfg(not(target_os = "solana"))]
use spl_token_confidential_transfer_proof_generation::errors::TokenProofGenerationError;
use {
num_derive::FromPrimitive,
solana_program::{
decode_error::DecodeError,
msg,
program_error::{PrintProgramError, ProgramError},
},
spl_token_confidential_transfer_proof_extraction::errors::TokenProofExtractionError,
thiserror::Error,
};
#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]
pub enum TokenError {
#[error("Lamport balance below rent-exempt threshold")]
NotRentExempt,
#[error("Insufficient funds")]
InsufficientFunds,
#[error("Invalid Mint")]
InvalidMint,
#[error("Account not associated with this Mint")]
MintMismatch,
#[error("Owner does not match")]
OwnerMismatch,
#[error("Fixed supply")]
FixedSupply,
#[error("Already in use")]
AlreadyInUse,
#[error("Invalid number of provided signers")]
InvalidNumberOfProvidedSigners,
#[error("Invalid number of required signers")]
InvalidNumberOfRequiredSigners,
#[error("State is uninitialized")]
UninitializedState,
#[error("Instruction does not support native tokens")]
NativeNotSupported,
#[error("Non-native account can only be closed if its balance is zero")]
NonNativeHasBalance,
#[error("Invalid instruction")]
InvalidInstruction,
#[error("State is invalid for requested operation")]
InvalidState,
#[error("Operation overflowed")]
Overflow,
#[error("Account does not support specified authority type")]
AuthorityTypeNotSupported,
#[error("This token mint cannot freeze accounts")]
MintCannotFreeze,
#[error("Account is frozen")]
AccountFrozen,
#[error("The provided decimals value different from the Mint decimals")]
MintDecimalsMismatch,
#[error("Instruction does not support non-native tokens")]
NonNativeNotSupported,
#[error("Extension type does not match already existing extensions")]
ExtensionTypeMismatch,
#[error("Extension does not match the base type provided")]
ExtensionBaseMismatch,
#[error("Extension already initialized on this account")]
ExtensionAlreadyInitialized,
#[error("An account can only be closed if its confidential balance is zero")]
ConfidentialTransferAccountHasBalance,
#[error("Account not approved for confidential transfers")]
ConfidentialTransferAccountNotApproved,
#[error("Account not accepting deposits or transfers")]
ConfidentialTransferDepositsAndTransfersDisabled,
#[error("ElGamal public key mismatch")]
ConfidentialTransferElGamalPubkeyMismatch,
#[error("Balance mismatch")]
ConfidentialTransferBalanceMismatch,
#[error("Mint has non-zero supply. Burn all tokens before closing the mint")]
MintHasSupply,
#[error("No authority exists to perform the desired operation")]
NoAuthorityExists,
#[error("Transfer fee exceeds maximum of 10,000 basis points")]
TransferFeeExceedsMaximum,
#[error("Mint required for this account to transfer tokens, use `transfer_checked` or `transfer_checked_with_fee`")]
MintRequiredForTransfer,
#[error("Calculated fee does not match expected fee")]
FeeMismatch,
#[error(
"Fee parameters associated with zero-knowledge proofs do not match fee parameters in mint"
)]
FeeParametersMismatch,
#[error("The owner authority cannot be changed")]
ImmutableOwner,
#[error("An account can only be closed if its withheld fee balance is zero, harvest fees to the mint and try again")]
AccountHasWithheldTransferFees,
#[error("No memo in previous instruction; required for recipient to receive a transfer")]
NoMemo,
#[error("Transfer is disabled for this mint")]
NonTransferable,
#[error("Non-transferable tokens can't be minted to an account without immutable ownership")]
NonTransferableNeedsImmutableOwnership,
#[error(
"The total number of `Deposit` and `Transfer` instructions to an account cannot exceed
the associated `maximum_pending_balance_credit_counter`"
)]
MaximumPendingBalanceCreditCounterExceeded,
#[error("Deposit amount exceeds maximum limit")]
MaximumDepositAmountExceeded,
#[error("CPI Guard cannot be enabled or disabled in CPI")]
CpiGuardSettingsLocked,
#[error("CPI Guard is enabled, and a program attempted to transfer user funds via CPI without using a delegate")]
CpiGuardTransferBlocked,
#[error(
"CPI Guard is enabled, and a program attempted to burn user funds via CPI without using a delegate"
)]
CpiGuardBurnBlocked,
#[error("CPI Guard is enabled, and a program attempted to close an account via CPI without returning lamports to owner")]
CpiGuardCloseAccountBlocked,
#[error("CPI Guard is enabled, and a program attempted to approve a delegate via CPI")]
CpiGuardApproveBlocked,
#[error(
"CPI Guard is enabled, and a program attempted to add or replace an authority via CPI"
)]
CpiGuardSetAuthorityBlocked,
#[error("Account ownership cannot be changed while CPI Guard is enabled")]
CpiGuardOwnerChangeBlocked,
#[error("Extension not found in account data")]
ExtensionNotFound,
#[error("Non-confidential transfers disabled")]
NonConfidentialTransfersDisabled,
#[error("An account can only be closed if the confidential withheld fee is zero")]
ConfidentialTransferFeeAccountHasWithheldFee,
#[error("A mint or an account is initialized to an invalid combination of extensions")]
InvalidExtensionCombination,
#[error("Extension allocation with overwrite must use the same length")]
InvalidLengthForAlloc,
#[error("Failed to decrypt a confidential transfer account")]
AccountDecryption,
#[error("Failed to generate proof")]
ProofGeneration,
#[error("An invalid proof instruction offset was provided")]
InvalidProofInstructionOffset,
#[error("Harvest of withheld tokens to mint is disabled")]
HarvestToMintDisabled,
#[error("Split proof context state accounts not supported for instruction")]
SplitProofContextStateAccountsNotSupported,
#[error("Not enough proof context state accounts provided")]
NotEnoughProofContextStateAccounts,
#[error("Ciphertext is malformed")]
MalformedCiphertext,
#[error("Ciphertext arithmetic failed")]
CiphertextArithmeticFailed,
#[error("Pedersen commitment mismatch")]
PedersenCommitmentMismatch,
#[error("Range proof length mismatch")]
RangeProofLengthMismatch,
#[error("Illegal transfer amount bit length")]
IllegalBitLength,
#[error("Fee calculation failed")]
FeeCalculation,
#[error("Withdraw / Deposit not allowed for confidential-mint-burn")]
IllegalMintBurnConversion,
}
impl From<TokenError> for ProgramError {
fn from(e: TokenError) -> Self {
ProgramError::Custom(e as u32)
}
}
impl<T> DecodeError<T> for TokenError {
fn type_of() -> &'static str {
"TokenError"
}
}
impl PrintProgramError for TokenError {
fn print<E>(&self)
where
E: 'static + std::error::Error + DecodeError<E> + num_traits::FromPrimitive,
{
match self {
TokenError::NotRentExempt => msg!("Error: Lamport balance below rent-exempt threshold"),
TokenError::InsufficientFunds => msg!("Error: insufficient funds"),
TokenError::InvalidMint => msg!("Error: Invalid Mint"),
TokenError::MintMismatch => msg!("Error: Account not associated with this Mint"),
TokenError::OwnerMismatch => msg!("Error: owner does not match"),
TokenError::FixedSupply => msg!("Error: the total supply of this token is fixed"),
TokenError::AlreadyInUse => msg!("Error: account or token already in use"),
TokenError::InvalidNumberOfProvidedSigners => {
msg!("Error: Invalid number of provided signers")
}
TokenError::InvalidNumberOfRequiredSigners => {
msg!("Error: Invalid number of required signers")
}
TokenError::UninitializedState => msg!("Error: State is uninitialized"),
TokenError::NativeNotSupported => {
msg!("Error: Instruction does not support native tokens")
}
TokenError::NonNativeHasBalance => {
msg!("Error: Non-native account can only be closed if its balance is zero")
}
TokenError::InvalidInstruction => msg!("Error: Invalid instruction"),
TokenError::InvalidState => msg!("Error: Invalid account state for operation"),
TokenError::Overflow => msg!("Error: Operation overflowed"),
TokenError::AuthorityTypeNotSupported => {
msg!("Error: Account does not support specified authority type")
}
TokenError::MintCannotFreeze => msg!("Error: This token mint cannot freeze accounts"),
TokenError::AccountFrozen => msg!("Error: Account is frozen"),
TokenError::MintDecimalsMismatch => {
msg!("Error: decimals different from the Mint decimals")
}
TokenError::NonNativeNotSupported => {
msg!("Error: Instruction does not support non-native tokens")
}
TokenError::ExtensionTypeMismatch => {
msg!("Error: New extension type does not match already existing extensions")
}
TokenError::ExtensionBaseMismatch => {
msg!("Error: Extension does not match the base type provided")
}
TokenError::ExtensionAlreadyInitialized => {
msg!("Error: Extension already initialized on this account")
}
TokenError::ConfidentialTransferAccountHasBalance => {
msg!("Error: An account can only be closed if its confidential balance is zero")
}
TokenError::ConfidentialTransferAccountNotApproved => {
msg!("Error: Account not approved for confidential transfers")
}
TokenError::ConfidentialTransferDepositsAndTransfersDisabled => {
msg!("Error: Account not accepting deposits or transfers")
}
TokenError::ConfidentialTransferElGamalPubkeyMismatch => {
msg!("Error: ElGamal public key mismatch")
}
TokenError::ConfidentialTransferBalanceMismatch => {
msg!("Error: Balance mismatch")
}
TokenError::MintHasSupply => {
msg!("Error: Mint has non-zero supply. Burn all tokens before closing the mint")
}
TokenError::NoAuthorityExists => {
msg!("Error: No authority exists to perform the desired operation");
}
TokenError::TransferFeeExceedsMaximum => {
msg!("Error: Transfer fee exceeds maximum of 10,000 basis points");
}
TokenError::MintRequiredForTransfer => {
msg!("Mint required for this account to transfer tokens, use `transfer_checked` or `transfer_checked_with_fee`");
}
TokenError::FeeMismatch => {
msg!("Calculated fee does not match expected fee");
}
TokenError::FeeParametersMismatch => {
msg!("Fee parameters associated with zero-knowledge proofs do not match fee parameters in mint")
}
TokenError::ImmutableOwner => {
msg!("The owner authority cannot be changed");
}
TokenError::AccountHasWithheldTransferFees => {
msg!("Error: An account can only be closed if its withheld fee balance is zero, harvest fees to the mint and try again");
}
TokenError::NoMemo => {
msg!("Error: No memo in previous instruction; required for recipient to receive a transfer");
}
TokenError::NonTransferable => {
msg!("Transfer is disabled for this mint");
}
TokenError::NonTransferableNeedsImmutableOwnership => {
msg!("Non-transferable tokens can't be minted to an account without immutable ownership");
}
TokenError::MaximumPendingBalanceCreditCounterExceeded => {
msg!("The total number of `Deposit` and `Transfer` instructions to an account cannot exceed the associated `maximum_pending_balance_credit_counter`");
}
TokenError::MaximumDepositAmountExceeded => {
msg!("Deposit amount exceeds maximum limit")
}
TokenError::CpiGuardSettingsLocked => {
msg!("CPI Guard status cannot be changed in CPI")
}
TokenError::CpiGuardTransferBlocked => {
msg!("CPI Guard is enabled, and a program attempted to transfer user funds without using a delegate")
}
TokenError::CpiGuardBurnBlocked => {
msg!("CPI Guard is enabled, and a program attempted to burn user funds without using a delegate")
}
TokenError::CpiGuardCloseAccountBlocked => {
msg!("CPI Guard is enabled, and a program attempted to close an account without returning lamports to owner")
}
TokenError::CpiGuardApproveBlocked => {
msg!("CPI Guard is enabled, and a program attempted to approve a delegate")
}
TokenError::CpiGuardSetAuthorityBlocked => {
msg!("CPI Guard is enabled, and a program attempted to add or change an authority")
}
TokenError::CpiGuardOwnerChangeBlocked => {
msg!("Account ownership cannot be changed while CPI Guard is enabled")
}
TokenError::ExtensionNotFound => {
msg!("Extension not found in account data")
}
TokenError::NonConfidentialTransfersDisabled => {
msg!("Non-confidential transfers disabled")
}
TokenError::ConfidentialTransferFeeAccountHasWithheldFee => {
msg!("Account has non-zero confidential withheld fee")
}
TokenError::InvalidExtensionCombination => {
msg!("Mint or account is initialized to an invalid combination of extensions")
}
TokenError::InvalidLengthForAlloc => {
msg!("Extension allocation with overwrite must use the same length")
}
TokenError::AccountDecryption => {
msg!("Failed to decrypt a confidential transfer account")
}
TokenError::ProofGeneration => {
msg!("Failed to generate proof")
}
TokenError::InvalidProofInstructionOffset => {
msg!("An invalid proof instruction offset was provided")
}
TokenError::HarvestToMintDisabled => {
msg!("Harvest of withheld tokens to mint is disabled")
}
TokenError::SplitProofContextStateAccountsNotSupported => {
msg!("Split proof context state accounts not supported for instruction")
}
TokenError::NotEnoughProofContextStateAccounts => {
msg!("Not enough proof context state accounts provided")
}
TokenError::MalformedCiphertext => {
msg!("Ciphertext is malformed")
}
TokenError::CiphertextArithmeticFailed => {
msg!("Ciphertext arithmetic failed")
}
TokenError::PedersenCommitmentMismatch => {
msg!("Pedersen commitments did not match")
}
TokenError::RangeProofLengthMismatch => {
msg!("Range proof lengths did not match")
}
TokenError::IllegalBitLength => {
msg!("Illegal transfer amount bit length")
}
TokenError::FeeCalculation => {
msg!("Transfer fee calculation failed")
}
TokenError::IllegalMintBurnConversion => {
msg!("Conversions from normal to confidential token balance and vice versa are illegal if the confidential-mint-burn extension is enabled")
}
}
}
}
#[cfg(not(target_os = "solana"))]
impl From<TokenProofGenerationError> for TokenError {
fn from(e: TokenProofGenerationError) -> Self {
match e {
TokenProofGenerationError::ProofGeneration(_) => TokenError::ProofGeneration,
TokenProofGenerationError::NotEnoughFunds => TokenError::InsufficientFunds,
TokenProofGenerationError::IllegalAmountBitLength => TokenError::IllegalBitLength,
TokenProofGenerationError::FeeCalculation => TokenError::FeeCalculation,
}
}
}
impl From<TokenProofExtractionError> for TokenError {
fn from(e: TokenProofExtractionError) -> Self {
match e {
TokenProofExtractionError::ElGamalPubkeyMismatch => {
TokenError::ConfidentialTransferElGamalPubkeyMismatch
}
TokenProofExtractionError::PedersenCommitmentMismatch => {
TokenError::PedersenCommitmentMismatch
}
TokenProofExtractionError::RangeProofLengthMismatch => {
TokenError::RangeProofLengthMismatch
}
TokenProofExtractionError::FeeParametersMismatch => TokenError::FeeParametersMismatch,
TokenProofExtractionError::CurveArithmetic => TokenError::CiphertextArithmeticFailed,
TokenProofExtractionError::CiphertextExtraction => TokenError::MalformedCiphertext,
}
}
}