Skip to main content

solana_instruction_error/
lib.rs

1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
4#[cfg(feature = "num-traits")]
5use num_traits::ToPrimitive;
6#[cfg(feature = "frozen-abi")]
7extern crate std;
8use {core::fmt, solana_program_error::ProgramError};
9pub use {
10    instruction_error_module::*,
11    solana_program_error::{
12        ACCOUNT_ALREADY_INITIALIZED, ACCOUNT_BORROW_FAILED, ACCOUNT_DATA_TOO_SMALL,
13        ACCOUNT_NOT_RENT_EXEMPT, ARITHMETIC_OVERFLOW, BORSH_IO_ERROR,
14        BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS, CUSTOM_ZERO, ILLEGAL_OWNER, IMMUTABLE,
15        INCORRECT_AUTHORITY, INCORRECT_PROGRAM_ID, INSUFFICIENT_FUNDS, INVALID_ACCOUNT_DATA,
16        INVALID_ACCOUNT_DATA_REALLOC, INVALID_ACCOUNT_OWNER, INVALID_ARGUMENT,
17        INVALID_INSTRUCTION_DATA, INVALID_SEEDS, MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED,
18        MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED, MAX_SEED_LENGTH_EXCEEDED,
19        MISSING_REQUIRED_SIGNATURES, NOT_ENOUGH_ACCOUNT_KEYS, UNINITIALIZED_ACCOUNT,
20        UNSUPPORTED_SYSVAR,
21    },
22};
23
24#[allow(deprecated)]
25mod instruction_error_module {
26    #[cfg(feature = "frozen-abi")]
27    use solana_frozen_abi_macro::{AbiEnumVisitor, AbiExample};
28
29    /// Reasons the runtime might have rejected an instruction.
30    ///
31    /// Members of this enum must not be removed, but new ones can be added.
32    /// Also, it is crucial that meta-information if any that comes along with
33    /// an error be consistent across software versions.  For example, it is
34    /// dangerous to include error strings from 3rd party crates because they could
35    /// change at any time and changes to them are difficult to detect.
36    #[cfg_attr(feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor))]
37    #[cfg_attr(
38        feature = "serde",
39        derive(serde_derive::Serialize, serde_derive::Deserialize)
40    )]
41    #[cfg_attr(feature = "wincode", derive(wincode::SchemaWrite, wincode::SchemaRead))]
42    #[derive(Debug, PartialEq, Eq, Clone)]
43    pub enum InstructionError {
44        /// Deprecated! Use CustomError instead!
45        /// The program instruction returned an error
46        GenericError,
47
48        /// The arguments provided to a program were invalid
49        InvalidArgument,
50
51        /// An instruction's data contents were invalid
52        InvalidInstructionData,
53
54        /// An account's data contents was invalid
55        InvalidAccountData,
56
57        /// An account's data was too small
58        AccountDataTooSmall,
59
60        /// An account's balance was too small to complete the instruction
61        InsufficientFunds,
62
63        /// The account did not have the expected program id
64        IncorrectProgramId,
65
66        /// A signature was required but not found
67        MissingRequiredSignature,
68
69        /// An initialize instruction was sent to an account that has already been initialized.
70        AccountAlreadyInitialized,
71
72        /// An attempt to operate on an account that hasn't been initialized.
73        UninitializedAccount,
74
75        /// Program's instruction lamport balance does not equal the balance after the instruction
76        UnbalancedInstruction,
77
78        /// Program illegally modified an account's program id
79        ModifiedProgramId,
80
81        /// Program spent the lamports of an account that doesn't belong to it
82        ExternalAccountLamportSpend,
83
84        /// Program modified the data of an account that doesn't belong to it
85        ExternalAccountDataModified,
86
87        /// Read-only account's lamports modified
88        ReadonlyLamportChange,
89
90        /// Read-only account's data was modified
91        ReadonlyDataModified,
92
93        /// An account was referenced more than once in a single instruction
94        // Deprecated, instructions can now contain duplicate accounts
95        DuplicateAccountIndex,
96
97        /// Executable bit on account changed, but shouldn't have
98        ExecutableModified,
99
100        /// Rent_epoch account changed, but shouldn't have
101        RentEpochModified,
102
103        /// The instruction expected additional account keys
104        #[deprecated(since = "2.1.0", note = "Use InstructionError::MissingAccount instead")]
105        NotEnoughAccountKeys,
106
107        /// Program other than the account's owner changed the size of the account data
108        AccountDataSizeChanged,
109
110        /// The instruction expected an executable account
111        AccountNotExecutable,
112
113        /// Failed to borrow a reference to account data, already borrowed
114        AccountBorrowFailed,
115
116        /// Account data has an outstanding reference after a program's execution
117        AccountBorrowOutstanding,
118
119        /// The same account was multiply passed to an on-chain program's entrypoint, but the program
120        /// modified them differently.  A program can only modify one instance of the account because
121        /// the runtime cannot determine which changes to pick or how to merge them if both are modified
122        DuplicateAccountOutOfSync,
123
124        /// Allows on-chain programs to implement program-specific error types and see them returned
125        /// by the Solana runtime. A program-specific error may be any type that is represented as
126        /// or serialized to a u32 integer.
127        Custom(u32),
128
129        /// The return value from the program was invalid.  Valid errors are either a defined builtin
130        /// error value or a user-defined error in the lower 32 bits.
131        InvalidError,
132
133        /// Executable account's data was modified
134        ExecutableDataModified,
135
136        /// Executable account's lamports modified
137        ExecutableLamportChange,
138
139        /// Executable accounts must be rent exempt
140        ExecutableAccountNotRentExempt,
141
142        /// Unsupported program id
143        UnsupportedProgramId,
144
145        /// Cross-program invocation call depth too deep
146        CallDepth,
147
148        /// An account required by the instruction is missing
149        MissingAccount,
150
151        /// Cross-program invocation reentrancy not allowed for this instruction
152        ReentrancyNotAllowed,
153
154        /// Length of the seed is too long for address generation
155        MaxSeedLengthExceeded,
156
157        /// Provided seeds do not result in a valid address
158        InvalidSeeds,
159
160        /// Failed to reallocate account data of this length
161        InvalidRealloc,
162
163        /// Computational budget exceeded
164        ComputationalBudgetExceeded,
165
166        /// Cross-program invocation with unauthorized signer or writable account
167        PrivilegeEscalation,
168
169        /// Failed to create program execution environment
170        ProgramEnvironmentSetupFailure,
171
172        /// Program failed to complete
173        ProgramFailedToComplete,
174
175        /// Program failed to compile
176        ProgramFailedToCompile,
177
178        /// Account is immutable
179        Immutable,
180
181        /// Incorrect authority provided
182        IncorrectAuthority,
183
184        /// Failed to serialize or deserialize account data
185        BorshIoError,
186
187        /// An account does not have enough lamports to be rent-exempt
188        AccountNotRentExempt,
189
190        /// Invalid account owner
191        InvalidAccountOwner,
192
193        /// Program arithmetic overflowed
194        ArithmeticOverflow,
195
196        /// Unsupported sysvar
197        UnsupportedSysvar,
198
199        /// Illegal account owner
200        IllegalOwner,
201
202        /// Accounts data allocations exceeded the maximum allowed per transaction
203        MaxAccountsDataAllocationsExceeded,
204
205        /// Max accounts exceeded
206        MaxAccountsExceeded,
207
208        /// Max instruction trace length exceeded
209        MaxInstructionTraceLengthExceeded,
210
211        /// Builtin programs must consume compute units
212        BuiltinProgramsMustConsumeComputeUnits,
213        // Note: For any new error added here an equivalent ProgramError and its
214        // conversions must also be added
215    }
216}
217
218impl core::error::Error for InstructionError {}
219
220impl fmt::Display for InstructionError {
221    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
222        match self {
223            InstructionError::GenericError => f.write_str("generic instruction error"),
224            InstructionError::InvalidArgument => f.write_str("invalid program argument"),
225            InstructionError::InvalidInstructionData => f.write_str("invalid instruction data"),
226            InstructionError::InvalidAccountData => {
227                f.write_str("invalid account data for instruction")
228            }
229            InstructionError::AccountDataTooSmall => {
230                f.write_str("account data too small for instruction")
231            }
232            InstructionError::InsufficientFunds => {
233                f.write_str("insufficient funds for instruction")
234            }
235            InstructionError::IncorrectProgramId => {
236                f.write_str("incorrect program id for instruction")
237            }
238            InstructionError::MissingRequiredSignature => {
239                f.write_str("missing required signature for instruction")
240            }
241            InstructionError::AccountAlreadyInitialized => {
242                f.write_str("instruction requires an uninitialized account")
243            }
244            InstructionError::UninitializedAccount => {
245                f.write_str("instruction requires an initialized account")
246            }
247            InstructionError::UnbalancedInstruction => {
248                f.write_str("sum of account balances before and after instruction do not match")
249            }
250            InstructionError::ModifiedProgramId => {
251                f.write_str("instruction illegally modified the program id of an account")
252            }
253            InstructionError::ExternalAccountLamportSpend => {
254                f.write_str("instruction spent from the balance of an account it does not own")
255            }
256            InstructionError::ExternalAccountDataModified => {
257                f.write_str("instruction modified data of an account it does not own")
258            }
259            InstructionError::ReadonlyLamportChange => {
260                f.write_str("instruction changed the balance of a read-only account")
261            }
262            InstructionError::ReadonlyDataModified => {
263                f.write_str("instruction modified data of a read-only account")
264            }
265            InstructionError::DuplicateAccountIndex => {
266                f.write_str("instruction contains duplicate accounts")
267            }
268            InstructionError::ExecutableModified => {
269                f.write_str("instruction changed executable bit of an account")
270            }
271            InstructionError::RentEpochModified => {
272                f.write_str("instruction modified rent epoch of an account")
273            }
274            #[allow(deprecated)]
275            InstructionError::NotEnoughAccountKeys => {
276                f.write_str("insufficient account keys for instruction")
277            }
278            InstructionError::AccountDataSizeChanged => f.write_str(
279                "program other than the account's owner changed the size of the account data",
280            ),
281            InstructionError::AccountNotExecutable => {
282                f.write_str("instruction expected an executable account")
283            }
284            InstructionError::AccountBorrowFailed => f.write_str(
285                "instruction tries to borrow reference for an account which is already borrowed",
286            ),
287            InstructionError::AccountBorrowOutstanding => {
288                f.write_str("instruction left account with an outstanding borrowed reference")
289            }
290            InstructionError::DuplicateAccountOutOfSync => {
291                f.write_str("instruction modifications of multiply-passed account differ")
292            }
293            InstructionError::Custom(num) => {
294                write!(f, "custom program error: {num:#x}")
295            }
296            InstructionError::InvalidError => f.write_str("program returned invalid error code"),
297            InstructionError::ExecutableDataModified => {
298                f.write_str("instruction changed executable accounts data")
299            }
300            InstructionError::ExecutableLamportChange => {
301                f.write_str("instruction changed the balance of an executable account")
302            }
303            InstructionError::ExecutableAccountNotRentExempt => {
304                f.write_str("executable accounts must be rent exempt")
305            }
306            InstructionError::UnsupportedProgramId => f.write_str("Unsupported program id"),
307            InstructionError::CallDepth => {
308                f.write_str("Cross-program invocation call depth too deep")
309            }
310            InstructionError::MissingAccount => {
311                f.write_str("An account required by the instruction is missing")
312            }
313            InstructionError::ReentrancyNotAllowed => {
314                f.write_str("Cross-program invocation reentrancy not allowed for this instruction")
315            }
316            InstructionError::MaxSeedLengthExceeded => {
317                f.write_str("Length of the seed is too long for address generation")
318            }
319            InstructionError::InvalidSeeds => {
320                f.write_str("Provided seeds do not result in a valid address")
321            }
322            InstructionError::InvalidRealloc => f.write_str("Failed to reallocate account data"),
323            InstructionError::ComputationalBudgetExceeded => {
324                f.write_str("Computational budget exceeded")
325            }
326            InstructionError::PrivilegeEscalation => {
327                f.write_str("Cross-program invocation with unauthorized signer or writable account")
328            }
329            InstructionError::ProgramEnvironmentSetupFailure => {
330                f.write_str("Failed to create program execution environment")
331            }
332            InstructionError::ProgramFailedToComplete => f.write_str("Program failed to complete"),
333            InstructionError::ProgramFailedToCompile => f.write_str("Program failed to compile"),
334            InstructionError::Immutable => f.write_str("Account is immutable"),
335            InstructionError::IncorrectAuthority => f.write_str("Incorrect authority provided"),
336            InstructionError::BorshIoError => {
337                f.write_str("Failed to serialize or deserialize account data")
338            }
339            InstructionError::AccountNotRentExempt => {
340                f.write_str("An account does not have enough lamports to be rent-exempt")
341            }
342            InstructionError::InvalidAccountOwner => f.write_str("Invalid account owner"),
343            InstructionError::ArithmeticOverflow => f.write_str("Program arithmetic overflowed"),
344            InstructionError::UnsupportedSysvar => f.write_str("Unsupported sysvar"),
345            InstructionError::IllegalOwner => f.write_str("Provided owner is not allowed"),
346            InstructionError::MaxAccountsDataAllocationsExceeded => f.write_str(
347                "Accounts data allocations exceeded the maximum allowed per transaction",
348            ),
349            InstructionError::MaxAccountsExceeded => f.write_str("Max accounts exceeded"),
350            InstructionError::MaxInstructionTraceLengthExceeded => {
351                f.write_str("Max instruction trace length exceeded")
352            }
353            InstructionError::BuiltinProgramsMustConsumeComputeUnits => {
354                f.write_str("Builtin programs must consume compute units")
355            }
356        }
357    }
358}
359
360#[cfg(feature = "num-traits")]
361impl<T> From<T> for InstructionError
362where
363    T: ToPrimitive,
364{
365    fn from(error: T) -> Self {
366        let error = error.to_u64().unwrap_or(0xbad_c0de);
367        match error {
368            CUSTOM_ZERO => Self::Custom(0),
369            INVALID_ARGUMENT => Self::InvalidArgument,
370            INVALID_INSTRUCTION_DATA => Self::InvalidInstructionData,
371            INVALID_ACCOUNT_DATA => Self::InvalidAccountData,
372            ACCOUNT_DATA_TOO_SMALL => Self::AccountDataTooSmall,
373            INSUFFICIENT_FUNDS => Self::InsufficientFunds,
374            INCORRECT_PROGRAM_ID => Self::IncorrectProgramId,
375            MISSING_REQUIRED_SIGNATURES => Self::MissingRequiredSignature,
376            ACCOUNT_ALREADY_INITIALIZED => Self::AccountAlreadyInitialized,
377            UNINITIALIZED_ACCOUNT => Self::UninitializedAccount,
378            #[allow(deprecated)]
379            NOT_ENOUGH_ACCOUNT_KEYS => Self::NotEnoughAccountKeys,
380            ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed,
381            MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded,
382            INVALID_SEEDS => Self::InvalidSeeds,
383            BORSH_IO_ERROR => Self::BorshIoError,
384            ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
385            UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
386            ILLEGAL_OWNER => Self::IllegalOwner,
387            MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED => Self::MaxAccountsDataAllocationsExceeded,
388            INVALID_ACCOUNT_DATA_REALLOC => Self::InvalidRealloc,
389            MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED => Self::MaxInstructionTraceLengthExceeded,
390            BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS => {
391                Self::BuiltinProgramsMustConsumeComputeUnits
392            }
393            INVALID_ACCOUNT_OWNER => Self::InvalidAccountOwner,
394            ARITHMETIC_OVERFLOW => Self::ArithmeticOverflow,
395            IMMUTABLE => Self::Immutable,
396            INCORRECT_AUTHORITY => Self::IncorrectAuthority,
397            _ => {
398                // A valid custom error has no bits set in the upper 32
399                if error >> solana_program_error::BUILTIN_BIT_SHIFT == 0 {
400                    Self::Custom(error as u32)
401                } else {
402                    Self::InvalidError
403                }
404            }
405        }
406    }
407}
408
409#[derive(Debug)]
410pub enum LamportsError {
411    /// arithmetic underflowed
412    ArithmeticUnderflow,
413    /// arithmetic overflowed
414    ArithmeticOverflow,
415}
416
417impl core::error::Error for LamportsError {}
418
419impl fmt::Display for LamportsError {
420    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
421        match self {
422            Self::ArithmeticUnderflow => f.write_str("Arithmetic underflowed"),
423            Self::ArithmeticOverflow => f.write_str("Arithmetic overflowed"),
424        }
425    }
426}
427
428impl From<LamportsError> for InstructionError {
429    fn from(error: LamportsError) -> Self {
430        match error {
431            LamportsError::ArithmeticOverflow => InstructionError::ArithmeticOverflow,
432            LamportsError::ArithmeticUnderflow => InstructionError::ArithmeticOverflow,
433        }
434    }
435}
436
437impl TryFrom<InstructionError> for ProgramError {
438    type Error = InstructionError;
439
440    fn try_from(error: InstructionError) -> Result<Self, Self::Error> {
441        match error {
442            Self::Error::Custom(err) => Ok(Self::Custom(err)),
443            Self::Error::InvalidArgument => Ok(Self::InvalidArgument),
444            Self::Error::InvalidInstructionData => Ok(Self::InvalidInstructionData),
445            Self::Error::InvalidAccountData => Ok(Self::InvalidAccountData),
446            Self::Error::AccountDataTooSmall => Ok(Self::AccountDataTooSmall),
447            Self::Error::InsufficientFunds => Ok(Self::InsufficientFunds),
448            Self::Error::IncorrectProgramId => Ok(Self::IncorrectProgramId),
449            Self::Error::MissingRequiredSignature => Ok(Self::MissingRequiredSignature),
450            Self::Error::AccountAlreadyInitialized => Ok(Self::AccountAlreadyInitialized),
451            Self::Error::UninitializedAccount => Ok(Self::UninitializedAccount),
452            #[allow(deprecated)]
453            Self::Error::NotEnoughAccountKeys => Ok(Self::NotEnoughAccountKeys),
454            Self::Error::MissingAccount => Ok(Self::NotEnoughAccountKeys),
455            Self::Error::AccountBorrowFailed => Ok(Self::AccountBorrowFailed),
456            Self::Error::MaxSeedLengthExceeded => Ok(Self::MaxSeedLengthExceeded),
457            Self::Error::InvalidSeeds => Ok(Self::InvalidSeeds),
458            Self::Error::BorshIoError => Ok(Self::BorshIoError),
459            Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt),
460            Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar),
461            Self::Error::IllegalOwner => Ok(Self::IllegalOwner),
462            Self::Error::MaxAccountsDataAllocationsExceeded => {
463                Ok(Self::MaxAccountsDataAllocationsExceeded)
464            }
465            Self::Error::InvalidRealloc => Ok(Self::InvalidRealloc),
466            Self::Error::MaxInstructionTraceLengthExceeded => {
467                Ok(Self::MaxInstructionTraceLengthExceeded)
468            }
469            Self::Error::BuiltinProgramsMustConsumeComputeUnits => {
470                Ok(Self::BuiltinProgramsMustConsumeComputeUnits)
471            }
472            Self::Error::InvalidAccountOwner => Ok(Self::InvalidAccountOwner),
473            Self::Error::ArithmeticOverflow => Ok(Self::ArithmeticOverflow),
474            Self::Error::Immutable => Ok(Self::Immutable),
475            Self::Error::IncorrectAuthority => Ok(Self::IncorrectAuthority),
476            _ => Err(error),
477        }
478    }
479}