spl_token_2022_interface/
error.rs1#[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#[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 #[error("Lamport balance below rent-exempt threshold")]
20 NotRentExempt,
21 #[error("Insufficient funds")]
23 InsufficientFunds,
24 #[error("Invalid Mint")]
26 InvalidMint,
27 #[error("Account not associated with this Mint")]
29 MintMismatch,
30 #[error("Owner does not match")]
32 OwnerMismatch,
33
34 #[error("Fixed supply")]
37 FixedSupply,
38 #[error("Already in use")]
40 AlreadyInUse,
41 #[error("Invalid number of provided signers")]
43 InvalidNumberOfProvidedSigners,
44 #[error("Invalid number of required signers")]
46 InvalidNumberOfRequiredSigners,
47 #[error("State is uninitialized")]
49 UninitializedState,
50
51 #[error("Instruction does not support native tokens")]
54 NativeNotSupported,
55 #[error("Non-native account can only be closed if its balance is zero")]
57 NonNativeHasBalance,
58 #[error("Invalid instruction")]
60 InvalidInstruction,
61 #[error("State is invalid for requested operation")]
63 InvalidState,
64 #[error("Operation overflowed")]
66 Overflow,
67
68 #[error("Account does not support specified authority type")]
71 AuthorityTypeNotSupported,
72 #[error("This token mint cannot freeze accounts")]
74 MintCannotFreeze,
75 #[error("Account is frozen")]
77 AccountFrozen,
78 #[error("The provided decimals value different from the Mint decimals")]
80 MintDecimalsMismatch,
81 #[error("Instruction does not support non-native tokens")]
83 NonNativeNotSupported,
84
85 #[error("Extension type does not match already existing extensions")]
88 ExtensionTypeMismatch,
89 #[error("Extension does not match the base type provided")]
91 ExtensionBaseMismatch,
92 #[error("Extension already initialized on this account")]
94 ExtensionAlreadyInitialized,
95 #[error("An account can only be closed if its confidential balance is zero")]
97 ConfidentialTransferAccountHasBalance,
98 #[error("Account not approved for confidential transfers")]
100 ConfidentialTransferAccountNotApproved,
101
102 #[error("Account not accepting deposits or transfers")]
105 ConfidentialTransferDepositsAndTransfersDisabled,
106 #[error("ElGamal public key mismatch")]
108 ConfidentialTransferElGamalPubkeyMismatch,
109 #[error("Balance mismatch")]
111 ConfidentialTransferBalanceMismatch,
112 #[error("Mint has non-zero supply. Burn all tokens before closing the mint")]
114 MintHasSupply,
115 #[error("No authority exists to perform the desired operation")]
117 NoAuthorityExists,
118
119 #[error("Transfer fee exceeds maximum of 10,000 basis points")]
122 TransferFeeExceedsMaximum,
123 #[error("Mint required for this account to transfer tokens, use `transfer_checked` or `transfer_checked_with_fee`")]
126 MintRequiredForTransfer,
127 #[error("Calculated fee does not match expected fee")]
129 FeeMismatch,
130 #[error(
133 "Fee parameters associated with zero-knowledge proofs do not match fee parameters in mint"
134 )]
135 FeeParametersMismatch,
136 #[error("The owner authority cannot be changed")]
138 ImmutableOwner,
139
140 #[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 #[error("No memo in previous instruction; required for recipient to receive a transfer")]
148 NoMemo,
149 #[error("Transfer is disabled for this mint")]
151 NonTransferable,
152 #[error("Non-transferable tokens can't be minted to an account without immutable ownership")]
155 NonTransferableNeedsImmutableOwnership,
156 #[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 #[error("Deposit amount exceeds maximum limit")]
169 MaximumDepositAmountExceeded,
170 #[error("CPI Guard cannot be enabled or disabled in CPI")]
172 CpiGuardSettingsLocked,
173 #[error("CPI Guard is enabled, and a program attempted to transfer user funds via CPI without using a delegate")]
176 CpiGuardTransferBlocked,
177 #[error(
180 "CPI Guard is enabled, and a program attempted to burn user funds via CPI without using a delegate"
181 )]
182 CpiGuardBurnBlocked,
183 #[error("CPI Guard is enabled, and a program attempted to close an account via CPI without returning lamports to owner")]
186 CpiGuardCloseAccountBlocked,
187
188 #[error("CPI Guard is enabled, and a program attempted to approve a delegate via CPI")]
191 CpiGuardApproveBlocked,
192 #[error(
195 "CPI Guard is enabled, and a program attempted to add or replace an authority via CPI"
196 )]
197 CpiGuardSetAuthorityBlocked,
198 #[error("Account ownership cannot be changed while CPI Guard is enabled")]
200 CpiGuardOwnerChangeBlocked,
201 #[error("Extension not found in account data")]
203 ExtensionNotFound,
204 #[error("Non-confidential transfers disabled")]
206 NonConfidentialTransfersDisabled,
207
208 #[error("An account can only be closed if the confidential withheld fee is zero")]
211 ConfidentialTransferFeeAccountHasWithheldFee,
212 #[error("A mint or an account is initialized to an invalid combination of extensions")]
215 InvalidExtensionCombination,
216 #[error("Extension allocation with overwrite must use the same length")]
218 InvalidLengthForAlloc,
219 #[error("Failed to decrypt a confidential transfer account")]
221 AccountDecryption,
222 #[error("Failed to generate proof")]
224 ProofGeneration,
225
226 #[error("An invalid proof instruction offset was provided")]
229 InvalidProofInstructionOffset,
230 #[error("Harvest of withheld tokens to mint is disabled")]
232 HarvestToMintDisabled,
233 #[error("Split proof context state accounts not supported for instruction")]
235 SplitProofContextStateAccountsNotSupported,
236 #[error("Not enough proof context state accounts provided")]
238 NotEnoughProofContextStateAccounts,
239 #[error("Ciphertext is malformed")]
241 MalformedCiphertext,
242
243 #[error("Ciphertext arithmetic failed")]
246 CiphertextArithmeticFailed,
247 #[error("Pedersen commitment mismatch")]
249 PedersenCommitmentMismatch,
250 #[error("Range proof length mismatch")]
252 RangeProofLengthMismatch,
253 #[error("Illegal transfer amount bit length")]
255 IllegalBitLength,
256 #[error("Fee calculation failed")]
258 FeeCalculation,
259
260 #[error("Withdraw / Deposit not allowed for confidential-mint-burn")]
263 IllegalMintBurnConversion,
264 #[error("Invalid scale for scaled ui amount")]
266 InvalidScale,
267 #[error("Transferring, minting, and burning is paused on this mint")]
269 MintPaused,
270 #[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}