Skip to main content

spl_token_2022_interface/
error.rs

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