spl_token_2022_interface/
error.rs

1//! Error types
2
3#[cfg(not(target_os = "solana"))]
4use spl_token_confidential_transfer_proof_generation::errors::TokenProofGenerationError;
5use {
6    num_derive::FromPrimitive,
7    solana_program_error::{ProgramError, ToStr},
8    spl_token_confidential_transfer_proof_extraction::errors::TokenProofExtractionError,
9    thiserror::Error,
10};
11
12/// Errors that may be returned by the Token program.
13#[repr(u32)]
14#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))]
15#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]
16pub enum TokenError {
17    // 0
18    /// Lamport balance below rent-exempt threshold.
19    #[error("Lamport balance below rent-exempt threshold")]
20    NotRentExempt,
21    /// Insufficient funds for the operation requested.
22    #[error("Insufficient funds")]
23    InsufficientFunds,
24    /// Invalid Mint.
25    #[error("Invalid Mint")]
26    InvalidMint,
27    /// Account not associated with this Mint.
28    #[error("Account not associated with this Mint")]
29    MintMismatch,
30    /// Owner does not match.
31    #[error("Owner does not match")]
32    OwnerMismatch,
33
34    // 5
35    /// This token's supply is fixed and new tokens cannot be minted.
36    #[error("Fixed supply")]
37    FixedSupply,
38    /// The account cannot be initialized because it is already being used.
39    #[error("Already in use")]
40    AlreadyInUse,
41    /// Invalid number of provided signers.
42    #[error("Invalid number of provided signers")]
43    InvalidNumberOfProvidedSigners,
44    /// Invalid number of required signers.
45    #[error("Invalid number of required signers")]
46    InvalidNumberOfRequiredSigners,
47    /// State is uninitialized.
48    #[error("State is uninitialized")]
49    UninitializedState,
50
51    // 10
52    /// Instruction does not support native tokens
53    #[error("Instruction does not support native tokens")]
54    NativeNotSupported,
55    /// Non-native account can only be closed if its balance is zero
56    #[error("Non-native account can only be closed if its balance is zero")]
57    NonNativeHasBalance,
58    /// Invalid instruction
59    #[error("Invalid instruction")]
60    InvalidInstruction,
61    /// State is invalid for requested operation.
62    #[error("State is invalid for requested operation")]
63    InvalidState,
64    /// Operation overflowed
65    #[error("Operation overflowed")]
66    Overflow,
67
68    // 15
69    /// Account does not support specified authority type.
70    #[error("Account does not support specified authority type")]
71    AuthorityTypeNotSupported,
72    /// This token mint cannot freeze accounts.
73    #[error("This token mint cannot freeze accounts")]
74    MintCannotFreeze,
75    /// Account is frozen; all account operations will fail
76    #[error("Account is frozen")]
77    AccountFrozen,
78    /// Mint decimals mismatch between the client and mint
79    #[error("The provided decimals value different from the Mint decimals")]
80    MintDecimalsMismatch,
81    /// Instruction does not support non-native tokens
82    #[error("Instruction does not support non-native tokens")]
83    NonNativeNotSupported,
84
85    // 20
86    /// Extension type does not match already existing extensions
87    #[error("Extension type does not match already existing extensions")]
88    ExtensionTypeMismatch,
89    /// Extension does not match the base type provided
90    #[error("Extension does not match the base type provided")]
91    ExtensionBaseMismatch,
92    /// Extension already initialized on this account
93    #[error("Extension already initialized on this account")]
94    ExtensionAlreadyInitialized,
95    /// An account can only be closed if its confidential balance is zero
96    #[error("An account can only be closed if its confidential balance is zero")]
97    ConfidentialTransferAccountHasBalance,
98    /// Account not approved for confidential transfers
99    #[error("Account not approved for confidential transfers")]
100    ConfidentialTransferAccountNotApproved,
101
102    // 25
103    /// Account not accepting deposits or transfers
104    #[error("Account not accepting deposits or transfers")]
105    ConfidentialTransferDepositsAndTransfersDisabled,
106    /// ElGamal public key mismatch
107    #[error("ElGamal public key mismatch")]
108    ConfidentialTransferElGamalPubkeyMismatch,
109    /// Balance mismatch
110    #[error("Balance mismatch")]
111    ConfidentialTransferBalanceMismatch,
112    /// Mint has non-zero supply. Burn all tokens before closing the mint.
113    #[error("Mint has non-zero supply. Burn all tokens before closing the mint")]
114    MintHasSupply,
115    /// No authority exists to perform the desired operation
116    #[error("No authority exists to perform the desired operation")]
117    NoAuthorityExists,
118
119    // 30
120    /// Transfer fee exceeds maximum of 10,000 basis points
121    #[error("Transfer fee exceeds maximum of 10,000 basis points")]
122    TransferFeeExceedsMaximum,
123    /// Mint required for this account to transfer tokens, use
124    /// `transfer_checked` or `transfer_checked_with_fee`
125    #[error("Mint required for this account to transfer tokens, use `transfer_checked` or `transfer_checked_with_fee`")]
126    MintRequiredForTransfer,
127    /// Calculated fee does not match expected fee
128    #[error("Calculated fee does not match expected fee")]
129    FeeMismatch,
130    /// Fee parameters associated with confidential transfer zero-knowledge
131    /// proofs do not match fee parameters in mint
132    #[error(
133        "Fee parameters associated with zero-knowledge proofs do not match fee parameters in mint"
134    )]
135    FeeParametersMismatch,
136    /// The owner authority cannot be changed
137    #[error("The owner authority cannot be changed")]
138    ImmutableOwner,
139
140    // 35
141    /// An account can only be closed if its withheld fee balance is zero,
142    /// harvest fees to the mint and try again
143    #[error("An account can only be closed if its withheld fee balance is zero, harvest fees to the mint and try again")]
144    AccountHasWithheldTransferFees,
145    /// No memo in previous instruction; required for recipient to receive a
146    /// transfer
147    #[error("No memo in previous instruction; required for recipient to receive a transfer")]
148    NoMemo,
149    /// Transfer is disabled for this mint
150    #[error("Transfer is disabled for this mint")]
151    NonTransferable,
152    /// Non-transferable tokens can't be minted to an account without immutable
153    /// ownership
154    #[error("Non-transferable tokens can't be minted to an account without immutable ownership")]
155    NonTransferableNeedsImmutableOwnership,
156    /// The total number of `Deposit` and `Transfer` instructions to an account
157    /// cannot exceed the associated
158    /// `maximum_pending_balance_credit_counter`
159    #[error(
160        "The total number of `Deposit` and `Transfer` instructions to an account cannot exceed
161            the associated `maximum_pending_balance_credit_counter`"
162    )]
163    MaximumPendingBalanceCreditCounterExceeded,
164
165    // 40
166    /// The deposit amount for the confidential extension exceeds the maximum
167    /// limit
168    #[error("Deposit amount exceeds maximum limit")]
169    MaximumDepositAmountExceeded,
170    /// CPI Guard cannot be enabled or disabled in CPI
171    #[error("CPI Guard cannot be enabled or disabled in CPI")]
172    CpiGuardSettingsLocked,
173    /// CPI Guard is enabled, and a program attempted to transfer user funds
174    /// without using a delegate
175    #[error("CPI Guard is enabled, and a program attempted to transfer user funds via CPI without using a delegate")]
176    CpiGuardTransferBlocked,
177    /// CPI Guard is enabled, and a program attempted to burn user funds without
178    /// using a delegate
179    #[error(
180        "CPI Guard is enabled, and a program attempted to burn user funds via CPI without using a delegate"
181    )]
182    CpiGuardBurnBlocked,
183    /// CPI Guard is enabled, and a program attempted to close an account
184    /// without returning lamports to owner
185    #[error("CPI Guard is enabled, and a program attempted to close an account via CPI without returning lamports to owner")]
186    CpiGuardCloseAccountBlocked,
187
188    // 45
189    /// CPI Guard is enabled, and a program attempted to approve a delegate
190    #[error("CPI Guard is enabled, and a program attempted to approve a delegate via CPI")]
191    CpiGuardApproveBlocked,
192    /// CPI Guard is enabled, and a program attempted to add or replace an
193    /// authority
194    #[error(
195        "CPI Guard is enabled, and a program attempted to add or replace an authority via CPI"
196    )]
197    CpiGuardSetAuthorityBlocked,
198    /// Account ownership cannot be changed while CPI Guard is enabled
199    #[error("Account ownership cannot be changed while CPI Guard is enabled")]
200    CpiGuardOwnerChangeBlocked,
201    /// Extension not found in account data
202    #[error("Extension not found in account data")]
203    ExtensionNotFound,
204    /// Account does not accept non-confidential transfers
205    #[error("Non-confidential transfers disabled")]
206    NonConfidentialTransfersDisabled,
207
208    // 50
209    /// An account can only be closed if the confidential withheld fee is zero
210    #[error("An account can only be closed if the confidential withheld fee is zero")]
211    ConfidentialTransferFeeAccountHasWithheldFee,
212    /// A mint or an account is initialized to an invalid combination of
213    /// extensions
214    #[error("A mint or an account is initialized to an invalid combination of extensions")]
215    InvalidExtensionCombination,
216    /// Extension allocation with overwrite must use the same length
217    #[error("Extension allocation with overwrite must use the same length")]
218    InvalidLengthForAlloc,
219    /// Failed to decrypt a confidential transfer account
220    #[error("Failed to decrypt a confidential transfer account")]
221    AccountDecryption,
222    /// Failed to generate a zero-knowledge proof needed for a token instruction
223    #[error("Failed to generate proof")]
224    ProofGeneration,
225
226    // 55
227    /// An invalid proof instruction offset was provided
228    #[error("An invalid proof instruction offset was provided")]
229    InvalidProofInstructionOffset,
230    /// Harvest of withheld tokens to mint is disabled
231    #[error("Harvest of withheld tokens to mint is disabled")]
232    HarvestToMintDisabled,
233    /// Split proof context state accounts not supported for instruction
234    #[error("Split proof context state accounts not supported for instruction")]
235    SplitProofContextStateAccountsNotSupported,
236    /// Not enough proof context state accounts provided
237    #[error("Not enough proof context state accounts provided")]
238    NotEnoughProofContextStateAccounts,
239    /// Ciphertext is malformed
240    #[error("Ciphertext is malformed")]
241    MalformedCiphertext,
242
243    // 60
244    /// Ciphertext arithmetic failed
245    #[error("Ciphertext arithmetic failed")]
246    CiphertextArithmeticFailed,
247    /// Pedersen commitments did not match
248    #[error("Pedersen commitment mismatch")]
249    PedersenCommitmentMismatch,
250    /// Range proof length did not match
251    #[error("Range proof length mismatch")]
252    RangeProofLengthMismatch,
253    /// Illegal transfer amount bit length
254    #[error("Illegal transfer amount bit length")]
255    IllegalBitLength,
256    /// Fee calculation failed
257    #[error("Fee calculation failed")]
258    FeeCalculation,
259
260    //65
261    /// Withdraw / Deposit not allowed for confidential-mint-burn
262    #[error("Withdraw / Deposit not allowed for confidential-mint-burn")]
263    IllegalMintBurnConversion,
264    /// Invalid scale for scaled ui amount
265    #[error("Invalid scale for scaled ui amount")]
266    InvalidScale,
267    /// Transferring, minting, and burning is paused on this mint
268    #[error("Transferring, minting, and burning is paused on this mint")]
269    MintPaused,
270    /// Pending supply is not zero
271    #[error("Key rotation attempted while pending balance is not zero")]
272    PendingBalanceNonZero,
273}
274impl From<TokenError> for ProgramError {
275    fn from(e: TokenError) -> Self {
276        ProgramError::Custom(e as u32)
277    }
278}
279impl TryFrom<u32> for TokenError {
280    type Error = ProgramError;
281    fn try_from(code: u32) -> Result<Self, Self::Error> {
282        num_traits::FromPrimitive::from_u32(code).ok_or(ProgramError::InvalidArgument)
283    }
284}
285
286impl ToStr for TokenError {
287    fn to_str(&self) -> &'static str {
288        match self {
289            TokenError::NotRentExempt => "Error: Lamport balance below rent-exempt threshold",
290            TokenError::InsufficientFunds => "Error: insufficient funds",
291            TokenError::InvalidMint => "Error: Invalid Mint",
292            TokenError::MintMismatch => "Error: Account not associated with this Mint",
293            TokenError::OwnerMismatch => "Error: owner does not match",
294            TokenError::FixedSupply => "Error: the total supply of this token is fixed",
295            TokenError::AlreadyInUse => "Error: account or token already in use",
296            TokenError::InvalidNumberOfProvidedSigners => {
297                "Error: Invalid number of provided signers"
298            }
299            TokenError::InvalidNumberOfRequiredSigners => {
300                "Error: Invalid number of required signers"
301            }
302            TokenError::UninitializedState => "Error: State is uninitialized",
303            TokenError::NativeNotSupported => {
304                "Error: Instruction does not support native tokens"
305            }
306            TokenError::NonNativeHasBalance => {
307                "Error: Non-native account can only be closed if its balance is zero"
308            }
309            TokenError::InvalidInstruction => "Error: Invalid instruction",
310            TokenError::InvalidState => "Error: Invalid account state for operation",
311            TokenError::Overflow => "Error: Operation overflowed",
312            TokenError::AuthorityTypeNotSupported => {
313                "Error: Account does not support specified authority type"
314            }
315            TokenError::MintCannotFreeze => "Error: This token mint cannot freeze accounts",
316            TokenError::AccountFrozen => "Error: Account is frozen",
317            TokenError::MintDecimalsMismatch => {
318                "Error: decimals different from the Mint decimals"
319            }
320            TokenError::NonNativeNotSupported => {
321                "Error: Instruction does not support non-native tokens"
322            }
323            TokenError::ExtensionTypeMismatch => {
324                "Error: New extension type does not match already existing extensions"
325            }
326            TokenError::ExtensionBaseMismatch => {
327                "Error: Extension does not match the base type provided"
328            }
329            TokenError::ExtensionAlreadyInitialized => {
330                "Error: Extension already initialized on this account"
331            }
332            TokenError::ConfidentialTransferAccountHasBalance => {
333                "Error: An account can only be closed if its confidential balance is zero"
334            }
335            TokenError::ConfidentialTransferAccountNotApproved => {
336                "Error: Account not approved for confidential transfers"
337            }
338            TokenError::ConfidentialTransferDepositsAndTransfersDisabled => {
339                "Error: Account not accepting deposits or transfers"
340            }
341            TokenError::ConfidentialTransferElGamalPubkeyMismatch => {
342                "Error: ElGamal public key mismatch"
343            }
344            TokenError::ConfidentialTransferBalanceMismatch => {
345                "Error: Balance mismatch"
346            }
347            TokenError::MintHasSupply => {
348                "Error: Mint has non-zero supply. Burn all tokens before closing the mint"
349            }
350            TokenError::NoAuthorityExists => {
351                "Error: No authority exists to perform the desired operation"
352            }
353            TokenError::TransferFeeExceedsMaximum => {
354                "Error: Transfer fee exceeds maximum of 10,000 basis points"
355            }
356            TokenError::MintRequiredForTransfer => {
357                "Mint required for this account to transfer tokens, use `transfer_checked` or `transfer_checked_with_fee`"
358            }
359            TokenError::FeeMismatch => {
360                "Calculated fee does not match expected fee"
361            }
362            TokenError::FeeParametersMismatch => {
363                "Fee parameters associated with zero-knowledge proofs do not match fee parameters in mint"
364            }
365            TokenError::ImmutableOwner => {
366                "The owner authority cannot be changed"
367            }
368            TokenError::AccountHasWithheldTransferFees => {
369                "Error: An account can only be closed if its withheld fee balance is zero, harvest fees to the mint and try again"
370            }
371            TokenError::NoMemo => {
372                "Error: No memo in previous instruction required for recipient to receive a transfer"
373            }
374            TokenError::NonTransferable => {
375                "Transfer is disabled for this mint"
376            }
377            TokenError::NonTransferableNeedsImmutableOwnership => {
378                "Non-transferable tokens can't be minted to an account without immutable ownership"
379            }
380            TokenError::MaximumPendingBalanceCreditCounterExceeded => {
381                "The total number of `Deposit` and `Transfer` instructions to an account cannot exceed the associated `maximum_pending_balance_credit_counter`"
382            }
383            TokenError::MaximumDepositAmountExceeded => {
384                "Deposit amount exceeds maximum limit"
385            }
386            TokenError::CpiGuardSettingsLocked => {
387                "CPI Guard status cannot be changed in CPI"
388            }
389            TokenError::CpiGuardTransferBlocked => {
390                "CPI Guard is enabled, and a program attempted to transfer user funds without using a delegate"
391            }
392            TokenError::CpiGuardBurnBlocked => {
393                "CPI Guard is enabled, and a program attempted to burn user funds without using a delegate"
394            }
395            TokenError::CpiGuardCloseAccountBlocked => {
396                "CPI Guard is enabled, and a program attempted to close an account without returning lamports to owner"
397            }
398            TokenError::CpiGuardApproveBlocked => {
399                "CPI Guard is enabled, and a program attempted to approve a delegate"
400            }
401            TokenError::CpiGuardSetAuthorityBlocked => {
402                "CPI Guard is enabled, and a program attempted to add or change an authority"
403            }
404            TokenError::CpiGuardOwnerChangeBlocked => {
405                "Account ownership cannot be changed while CPI Guard is enabled"
406            }
407            TokenError::ExtensionNotFound => {
408                "Extension not found in account data"
409            }
410            TokenError::NonConfidentialTransfersDisabled => {
411                "Non-confidential transfers disabled"
412            }
413            TokenError::ConfidentialTransferFeeAccountHasWithheldFee => {
414                "Account has non-zero confidential withheld fee"
415            }
416            TokenError::InvalidExtensionCombination => {
417                "Mint or account is initialized to an invalid combination of extensions"
418            }
419            TokenError::InvalidLengthForAlloc => {
420                "Extension allocation with overwrite must use the same length"
421            }
422            TokenError::AccountDecryption => {
423                "Failed to decrypt a confidential transfer account"
424            }
425            TokenError::ProofGeneration => {
426                "Failed to generate proof"
427            }
428            TokenError::InvalidProofInstructionOffset => {
429                "An invalid proof instruction offset was provided"
430            }
431            TokenError::HarvestToMintDisabled => {
432                "Harvest of withheld tokens to mint is disabled"
433            }
434            TokenError::SplitProofContextStateAccountsNotSupported => {
435                "Split proof context state accounts not supported for instruction"
436            }
437            TokenError::NotEnoughProofContextStateAccounts => {
438                "Not enough proof context state accounts provided"
439            }
440            TokenError::MalformedCiphertext => {
441                "Ciphertext is malformed"
442            }
443            TokenError::CiphertextArithmeticFailed => {
444                "Ciphertext arithmetic failed"
445            }
446            TokenError::PedersenCommitmentMismatch => {
447                "Pedersen commitments did not match"
448            }
449            TokenError::RangeProofLengthMismatch => {
450                "Range proof lengths did not match"
451            }
452            TokenError::IllegalBitLength => {
453                "Illegal transfer amount bit length"
454            }
455            TokenError::FeeCalculation => {
456                "Transfer fee calculation failed"
457            }
458            TokenError::IllegalMintBurnConversion => {
459                "Conversions from normal to confidential token balance and vice versa are illegal if the confidential-mint-burn extension is enabled"
460            }
461            TokenError::InvalidScale => {
462                "Invalid scale for scaled ui amount"
463            }
464            TokenError::MintPaused => {
465                "Transferring, minting, and burning is paused on this mint"
466            }
467            TokenError::PendingBalanceNonZero => {
468                "Key rotation attempted while pending balance is not zero"
469            }
470        }
471    }
472}
473
474#[cfg(not(target_os = "solana"))]
475impl From<TokenProofGenerationError> for TokenError {
476    fn from(e: TokenProofGenerationError) -> Self {
477        match e {
478            TokenProofGenerationError::ProofGeneration(_) => TokenError::ProofGeneration,
479            TokenProofGenerationError::NotEnoughFunds => TokenError::InsufficientFunds,
480            TokenProofGenerationError::IllegalAmountBitLength => TokenError::IllegalBitLength,
481            TokenProofGenerationError::FeeCalculation => TokenError::FeeCalculation,
482            TokenProofGenerationError::CiphertextExtraction => TokenError::MalformedCiphertext,
483        }
484    }
485}
486
487impl From<TokenProofExtractionError> for TokenError {
488    fn from(e: TokenProofExtractionError) -> Self {
489        match e {
490            TokenProofExtractionError::ElGamalPubkeyMismatch => {
491                TokenError::ConfidentialTransferElGamalPubkeyMismatch
492            }
493            TokenProofExtractionError::PedersenCommitmentMismatch => {
494                TokenError::PedersenCommitmentMismatch
495            }
496            TokenProofExtractionError::RangeProofLengthMismatch => {
497                TokenError::RangeProofLengthMismatch
498            }
499            TokenProofExtractionError::FeeParametersMismatch => TokenError::FeeParametersMismatch,
500            TokenProofExtractionError::CurveArithmetic => TokenError::CiphertextArithmeticFailed,
501            TokenProofExtractionError::CiphertextExtraction => TokenError::MalformedCiphertext,
502        }
503    }
504}
505
506#[cfg(test)]
507mod test {
508    use {super::*, strum::IntoEnumIterator};
509    #[test]
510    fn test_parse_error_from_primitive_exhaustive() {
511        for variant in TokenError::iter() {
512            let variant_u32 = variant as u32;
513            assert_eq!(
514                TokenError::from_repr(variant_u32).unwrap(),
515                TokenError::try_from(variant_u32).unwrap()
516            );
517        }
518    }
519}