1#[cfg(not(target_os = "solana"))]
4use spl_token_confidential_transfer_proof_generation::errors::TokenProofGenerationError;
5use {
6 num_derive::FromPrimitive,
7 solana_decode_error::DecodeError,
8 solana_msg::msg,
9 solana_program_error::{PrintProgramError, ProgramError},
10 spl_token_confidential_transfer_proof_extraction::errors::TokenProofExtractionError,
11 thiserror::Error,
12};
13
14#[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<T> DecodeError<T> for TokenError {
280 fn type_of() -> &'static str {
281 "TokenError"
282 }
283}
284
285impl PrintProgramError for TokenError {
286 fn print<E>(&self)
287 where
288 E: 'static + std::error::Error + DecodeError<E> + num_traits::FromPrimitive,
289 {
290 match self {
291 TokenError::NotRentExempt => msg!("Error: Lamport balance below rent-exempt threshold"),
292 TokenError::InsufficientFunds => msg!("Error: insufficient funds"),
293 TokenError::InvalidMint => msg!("Error: Invalid Mint"),
294 TokenError::MintMismatch => msg!("Error: Account not associated with this Mint"),
295 TokenError::OwnerMismatch => msg!("Error: owner does not match"),
296 TokenError::FixedSupply => msg!("Error: the total supply of this token is fixed"),
297 TokenError::AlreadyInUse => msg!("Error: account or token already in use"),
298 TokenError::InvalidNumberOfProvidedSigners => {
299 msg!("Error: Invalid number of provided signers")
300 }
301 TokenError::InvalidNumberOfRequiredSigners => {
302 msg!("Error: Invalid number of required signers")
303 }
304 TokenError::UninitializedState => msg!("Error: State is uninitialized"),
305 TokenError::NativeNotSupported => {
306 msg!("Error: Instruction does not support native tokens")
307 }
308 TokenError::NonNativeHasBalance => {
309 msg!("Error: Non-native account can only be closed if its balance is zero")
310 }
311 TokenError::InvalidInstruction => msg!("Error: Invalid instruction"),
312 TokenError::InvalidState => msg!("Error: Invalid account state for operation"),
313 TokenError::Overflow => msg!("Error: Operation overflowed"),
314 TokenError::AuthorityTypeNotSupported => {
315 msg!("Error: Account does not support specified authority type")
316 }
317 TokenError::MintCannotFreeze => msg!("Error: This token mint cannot freeze accounts"),
318 TokenError::AccountFrozen => msg!("Error: Account is frozen"),
319 TokenError::MintDecimalsMismatch => {
320 msg!("Error: decimals different from the Mint decimals")
321 }
322 TokenError::NonNativeNotSupported => {
323 msg!("Error: Instruction does not support non-native tokens")
324 }
325 TokenError::ExtensionTypeMismatch => {
326 msg!("Error: New extension type does not match already existing extensions")
327 }
328 TokenError::ExtensionBaseMismatch => {
329 msg!("Error: Extension does not match the base type provided")
330 }
331 TokenError::ExtensionAlreadyInitialized => {
332 msg!("Error: Extension already initialized on this account")
333 }
334 TokenError::ConfidentialTransferAccountHasBalance => {
335 msg!("Error: An account can only be closed if its confidential balance is zero")
336 }
337 TokenError::ConfidentialTransferAccountNotApproved => {
338 msg!("Error: Account not approved for confidential transfers")
339 }
340 TokenError::ConfidentialTransferDepositsAndTransfersDisabled => {
341 msg!("Error: Account not accepting deposits or transfers")
342 }
343 TokenError::ConfidentialTransferElGamalPubkeyMismatch => {
344 msg!("Error: ElGamal public key mismatch")
345 }
346 TokenError::ConfidentialTransferBalanceMismatch => {
347 msg!("Error: Balance mismatch")
348 }
349 TokenError::MintHasSupply => {
350 msg!("Error: Mint has non-zero supply. Burn all tokens before closing the mint")
351 }
352 TokenError::NoAuthorityExists => {
353 msg!("Error: No authority exists to perform the desired operation");
354 }
355 TokenError::TransferFeeExceedsMaximum => {
356 msg!("Error: Transfer fee exceeds maximum of 10,000 basis points");
357 }
358 TokenError::MintRequiredForTransfer => {
359 msg!("Mint required for this account to transfer tokens, use `transfer_checked` or `transfer_checked_with_fee`");
360 }
361 TokenError::FeeMismatch => {
362 msg!("Calculated fee does not match expected fee");
363 }
364 TokenError::FeeParametersMismatch => {
365 msg!("Fee parameters associated with zero-knowledge proofs do not match fee parameters in mint")
366 }
367 TokenError::ImmutableOwner => {
368 msg!("The owner authority cannot be changed");
369 }
370 TokenError::AccountHasWithheldTransferFees => {
371 msg!("Error: An account can only be closed if its withheld fee balance is zero, harvest fees to the mint and try again");
372 }
373 TokenError::NoMemo => {
374 msg!("Error: No memo in previous instruction; required for recipient to receive a transfer");
375 }
376 TokenError::NonTransferable => {
377 msg!("Transfer is disabled for this mint");
378 }
379 TokenError::NonTransferableNeedsImmutableOwnership => {
380 msg!("Non-transferable tokens can't be minted to an account without immutable ownership");
381 }
382 TokenError::MaximumPendingBalanceCreditCounterExceeded => {
383 msg!("The total number of `Deposit` and `Transfer` instructions to an account cannot exceed the associated `maximum_pending_balance_credit_counter`");
384 }
385 TokenError::MaximumDepositAmountExceeded => {
386 msg!("Deposit amount exceeds maximum limit")
387 }
388 TokenError::CpiGuardSettingsLocked => {
389 msg!("CPI Guard status cannot be changed in CPI")
390 }
391 TokenError::CpiGuardTransferBlocked => {
392 msg!("CPI Guard is enabled, and a program attempted to transfer user funds without using a delegate")
393 }
394 TokenError::CpiGuardBurnBlocked => {
395 msg!("CPI Guard is enabled, and a program attempted to burn user funds without using a delegate")
396 }
397 TokenError::CpiGuardCloseAccountBlocked => {
398 msg!("CPI Guard is enabled, and a program attempted to close an account without returning lamports to owner")
399 }
400 TokenError::CpiGuardApproveBlocked => {
401 msg!("CPI Guard is enabled, and a program attempted to approve a delegate")
402 }
403 TokenError::CpiGuardSetAuthorityBlocked => {
404 msg!("CPI Guard is enabled, and a program attempted to add or change an authority")
405 }
406 TokenError::CpiGuardOwnerChangeBlocked => {
407 msg!("Account ownership cannot be changed while CPI Guard is enabled")
408 }
409 TokenError::ExtensionNotFound => {
410 msg!("Extension not found in account data")
411 }
412 TokenError::NonConfidentialTransfersDisabled => {
413 msg!("Non-confidential transfers disabled")
414 }
415 TokenError::ConfidentialTransferFeeAccountHasWithheldFee => {
416 msg!("Account has non-zero confidential withheld fee")
417 }
418 TokenError::InvalidExtensionCombination => {
419 msg!("Mint or account is initialized to an invalid combination of extensions")
420 }
421 TokenError::InvalidLengthForAlloc => {
422 msg!("Extension allocation with overwrite must use the same length")
423 }
424 TokenError::AccountDecryption => {
425 msg!("Failed to decrypt a confidential transfer account")
426 }
427 TokenError::ProofGeneration => {
428 msg!("Failed to generate proof")
429 }
430 TokenError::InvalidProofInstructionOffset => {
431 msg!("An invalid proof instruction offset was provided")
432 }
433 TokenError::HarvestToMintDisabled => {
434 msg!("Harvest of withheld tokens to mint is disabled")
435 }
436 TokenError::SplitProofContextStateAccountsNotSupported => {
437 msg!("Split proof context state accounts not supported for instruction")
438 }
439 TokenError::NotEnoughProofContextStateAccounts => {
440 msg!("Not enough proof context state accounts provided")
441 }
442 TokenError::MalformedCiphertext => {
443 msg!("Ciphertext is malformed")
444 }
445 TokenError::CiphertextArithmeticFailed => {
446 msg!("Ciphertext arithmetic failed")
447 }
448 TokenError::PedersenCommitmentMismatch => {
449 msg!("Pedersen commitments did not match")
450 }
451 TokenError::RangeProofLengthMismatch => {
452 msg!("Range proof lengths did not match")
453 }
454 TokenError::IllegalBitLength => {
455 msg!("Illegal transfer amount bit length")
456 }
457 TokenError::FeeCalculation => {
458 msg!("Transfer fee calculation failed")
459 }
460 TokenError::IllegalMintBurnConversion => {
461 msg!("Conversions from normal to confidential token balance and vice versa are illegal if the confidential-mint-burn extension is enabled")
462 }
463 TokenError::InvalidScale => {
464 msg!("Invalid scale for scaled ui amount")
465 }
466 TokenError::MintPaused => {
467 msg!("Transferring, minting, and burning is paused on this mint")
468 }
469 TokenError::PendingBalanceNonZero => {
470 msg!("Key rotation attempted while pending balance is not zero")
471 }
472 }
473 }
474}
475
476#[cfg(not(target_os = "solana"))]
477impl From<TokenProofGenerationError> for TokenError {
478 fn from(e: TokenProofGenerationError) -> Self {
479 match e {
480 TokenProofGenerationError::ProofGeneration(_) => TokenError::ProofGeneration,
481 TokenProofGenerationError::NotEnoughFunds => TokenError::InsufficientFunds,
482 TokenProofGenerationError::IllegalAmountBitLength => TokenError::IllegalBitLength,
483 TokenProofGenerationError::FeeCalculation => TokenError::FeeCalculation,
484 TokenProofGenerationError::CiphertextExtraction => TokenError::MalformedCiphertext,
485 }
486 }
487}
488
489impl From<TokenProofExtractionError> for TokenError {
490 fn from(e: TokenProofExtractionError) -> Self {
491 match e {
492 TokenProofExtractionError::ElGamalPubkeyMismatch => {
493 TokenError::ConfidentialTransferElGamalPubkeyMismatch
494 }
495 TokenProofExtractionError::PedersenCommitmentMismatch => {
496 TokenError::PedersenCommitmentMismatch
497 }
498 TokenProofExtractionError::RangeProofLengthMismatch => {
499 TokenError::RangeProofLengthMismatch
500 }
501 TokenProofExtractionError::FeeParametersMismatch => TokenError::FeeParametersMismatch,
502 TokenProofExtractionError::CurveArithmetic => TokenError::CiphertextArithmeticFailed,
503 TokenProofExtractionError::CiphertextExtraction => TokenError::MalformedCiphertext,
504 }
505 }
506}