spl_token_2022_interface/
error.rs1use {
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#[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 #[error("Lamport balance below rent-exempt threshold")]
18 NotRentExempt,
19 #[error("Insufficient funds")]
21 InsufficientFunds,
22 #[error("Invalid Mint")]
24 InvalidMint,
25 #[error("Account not associated with this Mint")]
27 MintMismatch,
28 #[error("Owner does not match")]
30 OwnerMismatch,
31
32 #[error("Fixed supply")]
35 FixedSupply,
36 #[error("Already in use")]
38 AlreadyInUse,
39 #[error("Invalid number of provided signers")]
41 InvalidNumberOfProvidedSigners,
42 #[error("Invalid number of required signers")]
44 InvalidNumberOfRequiredSigners,
45 #[error("State is uninitialized")]
47 UninitializedState,
48
49 #[error("Instruction does not support native tokens")]
52 NativeNotSupported,
53 #[error("Non-native account can only be closed if its balance is zero")]
55 NonNativeHasBalance,
56 #[error("Invalid instruction")]
58 InvalidInstruction,
59 #[error("State is invalid for requested operation")]
61 InvalidState,
62 #[error("Operation overflowed")]
64 Overflow,
65
66 #[error("Account does not support specified authority type")]
69 AuthorityTypeNotSupported,
70 #[error("This token mint cannot freeze accounts")]
72 MintCannotFreeze,
73 #[error("Account is frozen")]
75 AccountFrozen,
76 #[error("The provided decimals value different from the Mint decimals")]
78 MintDecimalsMismatch,
79 #[error("Instruction does not support non-native tokens")]
81 NonNativeNotSupported,
82
83 #[error("Extension type does not match already existing extensions")]
86 ExtensionTypeMismatch,
87 #[error("Extension does not match the base type provided")]
89 ExtensionBaseMismatch,
90 #[error("Extension already initialized on this account")]
92 ExtensionAlreadyInitialized,
93 #[error("An account can only be closed if its confidential balance is zero")]
95 ConfidentialTransferAccountHasBalance,
96 #[error("Account not approved for confidential transfers")]
98 ConfidentialTransferAccountNotApproved,
99
100 #[error("Account not accepting deposits or transfers")]
103 ConfidentialTransferDepositsAndTransfersDisabled,
104 #[error("ElGamal public key mismatch")]
106 ConfidentialTransferElGamalPubkeyMismatch,
107 #[error("Balance mismatch")]
109 ConfidentialTransferBalanceMismatch,
110 #[error("Mint has non-zero supply. Burn all tokens before closing the mint")]
112 MintHasSupply,
113 #[error("No authority exists to perform the desired operation")]
115 NoAuthorityExists,
116
117 #[error("Transfer fee exceeds maximum of 10,000 basis points")]
120 TransferFeeExceedsMaximum,
121 #[error("Mint required for this account to transfer tokens, use `transfer_checked` or `transfer_checked_with_fee`")]
124 MintRequiredForTransfer,
125 #[error("Calculated fee does not match expected fee")]
127 FeeMismatch,
128 #[error(
131 "Fee parameters associated with zero-knowledge proofs do not match fee parameters in mint"
132 )]
133 FeeParametersMismatch,
134 #[error("The owner authority cannot be changed")]
136 ImmutableOwner,
137
138 #[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 #[error("No memo in previous instruction; required for recipient to receive a transfer")]
146 NoMemo,
147 #[error("Transfer is disabled for this mint")]
149 NonTransferable,
150 #[error("Non-transferable tokens can't be minted to an account without immutable ownership")]
153 NonTransferableNeedsImmutableOwnership,
154 #[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 #[error("Deposit amount exceeds maximum limit")]
167 MaximumDepositAmountExceeded,
168 #[error("CPI Guard cannot be enabled or disabled in CPI")]
170 CpiGuardSettingsLocked,
171 #[error("CPI Guard is enabled, and a program attempted to transfer user funds via CPI without using a delegate")]
174 CpiGuardTransferBlocked,
175 #[error(
178 "CPI Guard is enabled, and a program attempted to burn user funds via CPI without using a delegate"
179 )]
180 CpiGuardBurnBlocked,
181 #[error("CPI Guard is enabled, and a program attempted to close an account via CPI without returning lamports to owner")]
184 CpiGuardCloseAccountBlocked,
185
186 #[error("CPI Guard is enabled, and a program attempted to approve a delegate via CPI")]
189 CpiGuardApproveBlocked,
190 #[error(
193 "CPI Guard is enabled, and a program attempted to add or replace an authority via CPI"
194 )]
195 CpiGuardSetAuthorityBlocked,
196 #[error("Account ownership cannot be changed while CPI Guard is enabled")]
198 CpiGuardOwnerChangeBlocked,
199 #[error("Extension not found in account data")]
201 ExtensionNotFound,
202 #[error("Non-confidential transfers disabled")]
204 NonConfidentialTransfersDisabled,
205
206 #[error("An account can only be closed if the confidential withheld fee is zero")]
209 ConfidentialTransferFeeAccountHasWithheldFee,
210 #[error("A mint or an account is initialized to an invalid combination of extensions")]
213 InvalidExtensionCombination,
214 #[error("Extension allocation with overwrite must use the same length")]
216 InvalidLengthForAlloc,
217 #[error("Failed to decrypt a confidential transfer account")]
219 AccountDecryption,
220 #[error("Failed to generate proof")]
222 ProofGeneration,
223
224 #[error("An invalid proof instruction offset was provided")]
227 InvalidProofInstructionOffset,
228 #[error("Harvest of withheld tokens to mint is disabled")]
230 HarvestToMintDisabled,
231 #[error("Split proof context state accounts not supported for instruction")]
233 SplitProofContextStateAccountsNotSupported,
234 #[error("Not enough proof context state accounts provided")]
236 NotEnoughProofContextStateAccounts,
237 #[error("Ciphertext is malformed")]
239 MalformedCiphertext,
240
241 #[error("Ciphertext arithmetic failed")]
244 CiphertextArithmeticFailed,
245 #[error("Pedersen commitment mismatch")]
247 PedersenCommitmentMismatch,
248 #[error("Range proof length mismatch")]
250 RangeProofLengthMismatch,
251 #[error("Illegal transfer amount bit length")]
253 IllegalBitLength,
254 #[error("Fee calculation failed")]
256 FeeCalculation,
257
258 #[error("Withdraw / Deposit not allowed for confidential-mint-burn")]
261 IllegalMintBurnConversion,
262 #[error("Invalid scale for scaled ui amount")]
264 InvalidScale,
265 #[error("Transferring, minting, and burning is paused on this mint")]
267 MintPaused,
268 #[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}