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