1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
//! Error types

use {
    num_derive::FromPrimitive,
    solana_program::{
        decode_error::DecodeError,
        msg,
        program_error::{PrintProgramError, ProgramError},
    },
    thiserror::Error,
};

/// Errors that may be returned by the Token program.
#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]
pub enum TokenError {
    // 0
    /// Lamport balance below rent-exempt threshold.
    #[error("Lamport balance below rent-exempt threshold")]
    NotRentExempt,
    /// Insufficient funds for the operation requested.
    #[error("Insufficient funds")]
    InsufficientFunds,
    /// Invalid Mint.
    #[error("Invalid Mint")]
    InvalidMint,
    /// Account not associated with this Mint.
    #[error("Account not associated with this Mint")]
    MintMismatch,
    /// Owner does not match.
    #[error("Owner does not match")]
    OwnerMismatch,

    // 5
    /// This token's supply is fixed and new tokens cannot be minted.
    #[error("Fixed supply")]
    FixedSupply,
    /// The account cannot be initialized because it is already being used.
    #[error("Already in use")]
    AlreadyInUse,
    /// Invalid number of provided signers.
    #[error("Invalid number of provided signers")]
    InvalidNumberOfProvidedSigners,
    /// Invalid number of required signers.
    #[error("Invalid number of required signers")]
    InvalidNumberOfRequiredSigners,
    /// State is uninitialized.
    #[error("State is uninitialized")]
    UninitializedState,

    // 10
    /// Instruction does not support native tokens
    #[error("Instruction does not support native tokens")]
    NativeNotSupported,
    /// Non-native account can only be closed if its balance is zero
    #[error("Non-native account can only be closed if its balance is zero")]
    NonNativeHasBalance,
    /// Invalid instruction
    #[error("Invalid instruction")]
    InvalidInstruction,
    /// State is invalid for requested operation.
    #[error("State is invalid for requested operation")]
    InvalidState,
    /// Operation overflowed
    #[error("Operation overflowed")]
    Overflow,

    // 15
    /// Account does not support specified authority type.
    #[error("Account does not support specified authority type")]
    AuthorityTypeNotSupported,
    /// This token mint cannot freeze accounts.
    #[error("This token mint cannot freeze accounts")]
    MintCannotFreeze,
    /// Account is frozen; all account operations will fail
    #[error("Account is frozen")]
    AccountFrozen,
    /// Mint decimals mismatch between the client and mint
    #[error("The provided decimals value different from the Mint decimals")]
    MintDecimalsMismatch,
    /// Instruction does not support non-native tokens
    #[error("Instruction does not support non-native tokens")]
    NonNativeNotSupported,

    // 20
    /// Extension type does not match already existing extensions
    #[error("Extension type does not match already existing extensions")]
    ExtensionTypeMismatch,
    /// Extension does not match the base type provided
    #[error("Extension does not match the base type provided")]
    ExtensionBaseMismatch,
    /// Extension already initialized on this account
    #[error("Extension already initialized on this account")]
    ExtensionAlreadyInitialized,
    /// An account can only be closed if its confidential balance is zero
    #[error("An account can only be closed if its confidential balance is zero")]
    ConfidentialTransferAccountHasBalance,
    /// Account not approved for confidential transfers
    #[error("Account not approved for confidential transfers")]
    ConfidentialTransferAccountNotApproved,

    // 25
    /// Account not accepting deposits or transfers
    #[error("Account not accepting deposits or transfers")]
    ConfidentialTransferDepositsAndTransfersDisabled,
    /// ElGamal public key mismatch
    #[error("ElGamal public key mismatch")]
    ConfidentialTransferElGamalPubkeyMismatch,
    /// Balance mismatch
    #[error("Balance mismatch")]
    ConfidentialTransferBalanceMismatch,
    /// Mint has non-zero supply. Burn all tokens before closing the mint.
    #[error("Mint has non-zero supply. Burn all tokens before closing the mint")]
    MintHasSupply,
    /// No authority exists to perform the desired operation
    #[error("No authority exists to perform the desired operation")]
    NoAuthorityExists,

    // 30
    /// Transfer fee exceeds maximum of 10,000 basis points
    #[error("Transfer fee exceeds maximum of 10,000 basis points")]
    TransferFeeExceedsMaximum,
    /// Mint required for this account to transfer tokens, use `transfer_checked` or `transfer_checked_with_fee`
    #[error("Mint required for this account to transfer tokens, use `transfer_checked` or `transfer_checked_with_fee`")]
    MintRequiredForTransfer,
    /// Calculated fee does not match expected fee
    #[error("Calculated fee does not match expected fee")]
    FeeMismatch,
    /// Fee parameters associated with confidential transfer zero-knowledge proofs do not match fee parameters in mint
    #[error(
        "Fee parameters associated with zero-knowledge proofs do not match fee parameters in mint"
    )]
    FeeParametersMismatch,
    /// The owner authority cannot be changed
    #[error("The owner authority cannot be changed")]
    ImmutableOwner,

    // 35
    /// An account can only be closed if its withheld fee balance is zero, harvest fees to the
    /// mint and try again
    #[error("An account can only be closed if its withheld fee balance is zero, harvest fees to the mint and try again")]
    AccountHasWithheldTransferFees,
    /// No memo in previous instruction; required for recipient to receive a transfer
    #[error("No memo in previous instruction; required for recipient to receive a transfer")]
    NoMemo,
    /// Transfer is disabled for this mint
    #[error("Transfer is disabled for this mint")]
    NonTransferable,
    /// Non-transferable tokens can't be minted to an account without immutable ownership
    #[error("Non-transferable tokens can't be minted to an account without immutable ownership")]
    NonTransferableNeedsImmutableOwnership,
    /// The total number of `Deposit` and `Transfer` instructions to an account cannot exceed the
    /// associated `maximum_pending_balance_credit_counter`
    #[error(
        "The total number of `Deposit` and `Transfer` instructions to an account cannot exceed
            the associated `maximum_pending_balance_credit_counter`"
    )]
    MaximumPendingBalanceCreditCounterExceeded,

    // 40
    /// The deposit amount for the confidential extension exceeds the maximum limit
    #[error("Deposit amount exceeds maximum limit")]
    MaximumDepositAmountExceeded,
    /// CPI Guard cannot be enabled or disabled in CPI
    #[error("CPI Guard cannot be enabled or disabled in CPI")]
    CpiGuardSettingsLocked,
    /// CPI Guard is enabled, and a program attempted to transfer user funds without using a delegate
    #[error("CPI Guard is enabled, and a program attempted to transfer user funds via CPI without using a delegate")]
    CpiGuardTransferBlocked,
    /// CPI Guard is enabled, and a program attempted to burn user funds without using a delegate
    #[error(
        "CPI Guard is enabled, and a program attempted to burn user funds via CPI without using a delegate"
    )]
    CpiGuardBurnBlocked,
    /// CPI Guard is enabled, and a program attempted to close an account without returning lamports to owner
    #[error("CPI Guard is enabled, and a program attempted to close an account via CPI without returning lamports to owner")]
    CpiGuardCloseAccountBlocked,

    // 45
    /// CPI Guard is enabled, and a program attempted to approve a delegate
    #[error("CPI Guard is enabled, and a program attempted to approve a delegate via CPI")]
    CpiGuardApproveBlocked,
    /// CPI Guard is enabled, and a program attempted to add or replace an authority
    #[error(
        "CPI Guard is enabled, and a program attempted to add or replace an authority via CPI"
    )]
    CpiGuardSetAuthorityBlocked,
    /// Account ownership cannot be changed while CPI Guard is enabled
    #[error("Account ownership cannot be changed while CPI Guard is enabled")]
    CpiGuardOwnerChangeBlocked,
    /// Extension not found in account data
    #[error("Extension not found in account data")]
    ExtensionNotFound,
    /// Account does not accept non-confidential transfers
    #[error("Non-confidential transfers disabled")]
    NonConfidentialTransfersDisabled,
}
impl From<TokenError> for ProgramError {
    fn from(e: TokenError) -> Self {
        ProgramError::Custom(e as u32)
    }
}
impl<T> DecodeError<T> for TokenError {
    fn type_of() -> &'static str {
        "TokenError"
    }
}

impl PrintProgramError for TokenError {
    fn print<E>(&self)
    where
        E: 'static + std::error::Error + DecodeError<E> + num_traits::FromPrimitive,
    {
        match self {
            TokenError::NotRentExempt => msg!("Error: Lamport balance below rent-exempt threshold"),
            TokenError::InsufficientFunds => msg!("Error: insufficient funds"),
            TokenError::InvalidMint => msg!("Error: Invalid Mint"),
            TokenError::MintMismatch => msg!("Error: Account not associated with this Mint"),
            TokenError::OwnerMismatch => msg!("Error: owner does not match"),
            TokenError::FixedSupply => msg!("Error: the total supply of this token is fixed"),
            TokenError::AlreadyInUse => msg!("Error: account or token already in use"),
            TokenError::InvalidNumberOfProvidedSigners => {
                msg!("Error: Invalid number of provided signers")
            }
            TokenError::InvalidNumberOfRequiredSigners => {
                msg!("Error: Invalid number of required signers")
            }
            TokenError::UninitializedState => msg!("Error: State is uninitialized"),
            TokenError::NativeNotSupported => {
                msg!("Error: Instruction does not support native tokens")
            }
            TokenError::NonNativeHasBalance => {
                msg!("Error: Non-native account can only be closed if its balance is zero")
            }
            TokenError::InvalidInstruction => msg!("Error: Invalid instruction"),
            TokenError::InvalidState => msg!("Error: Invalid account state for operation"),
            TokenError::Overflow => msg!("Error: Operation overflowed"),
            TokenError::AuthorityTypeNotSupported => {
                msg!("Error: Account does not support specified authority type")
            }
            TokenError::MintCannotFreeze => msg!("Error: This token mint cannot freeze accounts"),
            TokenError::AccountFrozen => msg!("Error: Account is frozen"),
            TokenError::MintDecimalsMismatch => {
                msg!("Error: decimals different from the Mint decimals")
            }
            TokenError::NonNativeNotSupported => {
                msg!("Error: Instruction does not support non-native tokens")
            }
            TokenError::ExtensionTypeMismatch => {
                msg!("Error: New extension type does not match already existing extensions")
            }
            TokenError::ExtensionBaseMismatch => {
                msg!("Error: Extension does not match the base type provided")
            }
            TokenError::ExtensionAlreadyInitialized => {
                msg!("Error: Extension already initialized on this account")
            }
            TokenError::ConfidentialTransferAccountHasBalance => {
                msg!("Error: An account can only be closed if its confidential balance is zero")
            }
            TokenError::ConfidentialTransferAccountNotApproved => {
                msg!("Error: Account not approved for confidential transfers")
            }
            TokenError::ConfidentialTransferDepositsAndTransfersDisabled => {
                msg!("Error: Account not accepting deposits or transfers")
            }
            TokenError::ConfidentialTransferElGamalPubkeyMismatch => {
                msg!("Error: ElGamal public key mismatch")
            }
            TokenError::ConfidentialTransferBalanceMismatch => {
                msg!("Error: Balance mismatch")
            }
            TokenError::MintHasSupply => {
                msg!("Error: Mint has non-zero supply. Burn all tokens before closing the mint")
            }
            TokenError::NoAuthorityExists => {
                msg!("Error: No authority exists to perform the desired operation");
            }
            TokenError::TransferFeeExceedsMaximum => {
                msg!("Error: Transfer fee exceeds maximum of 10,000 basis points");
            }
            TokenError::MintRequiredForTransfer => {
                msg!("Mint required for this account to transfer tokens, use `transfer_checked` or `transfer_checked_with_fee`");
            }
            TokenError::FeeMismatch => {
                msg!("Calculated fee does not match expected fee");
            }
            TokenError::FeeParametersMismatch => {
                msg!("Fee parameters associated with zero-knowledge proofs do not match fee parameters in mint")
            }
            TokenError::ImmutableOwner => {
                msg!("The owner authority cannot be changed");
            }
            TokenError::AccountHasWithheldTransferFees => {
                msg!("Error: An account can only be closed if its withheld fee balance is zero, harvest fees to the mint and try again");
            }
            TokenError::NoMemo => {
                msg!("Error: No memo in previous instruction; required for recipient to receive a transfer");
            }
            TokenError::NonTransferable => {
                msg!("Transfer is disabled for this mint");
            }
            TokenError::NonTransferableNeedsImmutableOwnership => {
                msg!("Non-transferable tokens can't be minted to an account without immutable ownership");
            }
            TokenError::MaximumPendingBalanceCreditCounterExceeded => {
                msg!("The total number of `Deposit` and `Transfer` instructions to an account cannot exceed the associated `maximum_pending_balance_credit_counter`");
            }
            TokenError::MaximumDepositAmountExceeded => {
                msg!("Deposit amount exceeds maximum limit")
            }
            TokenError::CpiGuardSettingsLocked => {
                msg!("CPI Guard status cannot be changed in CPI")
            }
            TokenError::CpiGuardTransferBlocked => {
                msg!("CPI Guard is enabled, and a program attempted to transfer user funds without using a delegate")
            }
            TokenError::CpiGuardBurnBlocked => {
                msg!("CPI Guard is enabled, and a program attempted to burn user funds without using a delegate")
            }
            TokenError::CpiGuardCloseAccountBlocked => {
                msg!("CPI Guard is enabled, and a program attempted to close an account without returning lamports to owner")
            }
            TokenError::CpiGuardApproveBlocked => {
                msg!("CPI Guard is enabled, and a program attempted to approve a delegate")
            }
            TokenError::CpiGuardSetAuthorityBlocked => {
                msg!("CPI Guard is enabled, and a program attempted to add or change an authority")
            }
            TokenError::CpiGuardOwnerChangeBlocked => {
                msg!("Account ownership cannot be changed while CPI Guard is enabled")
            }
            TokenError::ExtensionNotFound => {
                msg!("Extension not found in account data")
            }
            TokenError::NonConfidentialTransfersDisabled => {
                msg!("Non-confidential transfers disabled")
            }
        }
    }
}