1use core::fmt;
2#[cfg(feature = "frozen-abi")]
3use solana_frozen_abi_macro::{AbiEnumVisitor, AbiExample};
4#[cfg(feature = "std")]
5use {
6 num_traits::ToPrimitive,
7 std::string::{String, ToString},
8};
9
10const BUILTIN_BIT_SHIFT: usize = 32;
12macro_rules! to_builtin {
13 ($error:expr) => {
14 ($error as u64) << BUILTIN_BIT_SHIFT
15 };
16}
17
18pub const CUSTOM_ZERO: u64 = to_builtin!(1);
19pub const INVALID_ARGUMENT: u64 = to_builtin!(2);
20pub const INVALID_INSTRUCTION_DATA: u64 = to_builtin!(3);
21pub const INVALID_ACCOUNT_DATA: u64 = to_builtin!(4);
22pub const ACCOUNT_DATA_TOO_SMALL: u64 = to_builtin!(5);
23pub const INSUFFICIENT_FUNDS: u64 = to_builtin!(6);
24pub const INCORRECT_PROGRAM_ID: u64 = to_builtin!(7);
25pub const MISSING_REQUIRED_SIGNATURES: u64 = to_builtin!(8);
26pub const ACCOUNT_ALREADY_INITIALIZED: u64 = to_builtin!(9);
27pub const UNINITIALIZED_ACCOUNT: u64 = to_builtin!(10);
28pub const NOT_ENOUGH_ACCOUNT_KEYS: u64 = to_builtin!(11);
29pub const ACCOUNT_BORROW_FAILED: u64 = to_builtin!(12);
30pub const MAX_SEED_LENGTH_EXCEEDED: u64 = to_builtin!(13);
31pub const INVALID_SEEDS: u64 = to_builtin!(14);
32pub const BORSH_IO_ERROR: u64 = to_builtin!(15);
33pub const ACCOUNT_NOT_RENT_EXEMPT: u64 = to_builtin!(16);
34pub const UNSUPPORTED_SYSVAR: u64 = to_builtin!(17);
35pub const ILLEGAL_OWNER: u64 = to_builtin!(18);
36pub const MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED: u64 = to_builtin!(19);
37pub const INVALID_ACCOUNT_DATA_REALLOC: u64 = to_builtin!(20);
38pub const MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED: u64 = to_builtin!(21);
39pub const BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS: u64 = to_builtin!(22);
40pub const INVALID_ACCOUNT_OWNER: u64 = to_builtin!(23);
41pub const ARITHMETIC_OVERFLOW: u64 = to_builtin!(24);
42pub const IMMUTABLE: u64 = to_builtin!(25);
43pub const INCORRECT_AUTHORITY: u64 = to_builtin!(26);
44#[cfg(feature = "std")]
58#[cfg_attr(feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor))]
59#[cfg_attr(
60 feature = "serde",
61 derive(serde_derive::Serialize, serde_derive::Deserialize)
62)]
63#[derive(Debug, PartialEq, Eq, Clone)]
64pub enum InstructionError {
65 GenericError,
68
69 InvalidArgument,
71
72 InvalidInstructionData,
74
75 InvalidAccountData,
77
78 AccountDataTooSmall,
80
81 InsufficientFunds,
83
84 IncorrectProgramId,
86
87 MissingRequiredSignature,
89
90 AccountAlreadyInitialized,
92
93 UninitializedAccount,
95
96 UnbalancedInstruction,
98
99 ModifiedProgramId,
101
102 ExternalAccountLamportSpend,
104
105 ExternalAccountDataModified,
107
108 ReadonlyLamportChange,
110
111 ReadonlyDataModified,
113
114 DuplicateAccountIndex,
117
118 ExecutableModified,
120
121 RentEpochModified,
123
124 NotEnoughAccountKeys,
126
127 AccountDataSizeChanged,
129
130 AccountNotExecutable,
132
133 AccountBorrowFailed,
135
136 AccountBorrowOutstanding,
138
139 DuplicateAccountOutOfSync,
143
144 Custom(u32),
148
149 InvalidError,
152
153 ExecutableDataModified,
155
156 ExecutableLamportChange,
158
159 ExecutableAccountNotRentExempt,
161
162 UnsupportedProgramId,
164
165 CallDepth,
167
168 MissingAccount,
170
171 ReentrancyNotAllowed,
173
174 MaxSeedLengthExceeded,
176
177 InvalidSeeds,
179
180 InvalidRealloc,
182
183 ComputationalBudgetExceeded,
185
186 PrivilegeEscalation,
188
189 ProgramEnvironmentSetupFailure,
191
192 ProgramFailedToComplete,
194
195 ProgramFailedToCompile,
197
198 Immutable,
200
201 IncorrectAuthority,
203
204 BorshIoError(String),
214
215 AccountNotRentExempt,
217
218 InvalidAccountOwner,
220
221 ArithmeticOverflow,
223
224 UnsupportedSysvar,
226
227 IllegalOwner,
229
230 MaxAccountsDataAllocationsExceeded,
232
233 MaxAccountsExceeded,
235
236 MaxInstructionTraceLengthExceeded,
238
239 BuiltinProgramsMustConsumeComputeUnits,
241 }
244
245#[cfg(feature = "std")]
246impl std::error::Error for InstructionError {}
247
248#[cfg(feature = "std")]
249impl fmt::Display for InstructionError {
250 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
251 match self {
252 InstructionError::GenericError => f.write_str("generic instruction error"),
253 InstructionError::InvalidArgument => f.write_str("invalid program argument"),
254 InstructionError::InvalidInstructionData => f.write_str("invalid instruction data"),
255 InstructionError::InvalidAccountData => {
256 f.write_str("invalid account data for instruction")
257 }
258 InstructionError::AccountDataTooSmall => {
259 f.write_str("account data too small for instruction")
260 }
261 InstructionError::InsufficientFunds => {
262 f.write_str("insufficient funds for instruction")
263 }
264 InstructionError::IncorrectProgramId => {
265 f.write_str("incorrect program id for instruction")
266 }
267 InstructionError::MissingRequiredSignature => {
268 f.write_str("missing required signature for instruction")
269 }
270 InstructionError::AccountAlreadyInitialized => {
271 f.write_str("instruction requires an uninitialized account")
272 }
273 InstructionError::UninitializedAccount => {
274 f.write_str("instruction requires an initialized account")
275 }
276 InstructionError::UnbalancedInstruction => {
277 f.write_str("sum of account balances before and after instruction do not match")
278 }
279 InstructionError::ModifiedProgramId => {
280 f.write_str("instruction illegally modified the program id of an account")
281 }
282 InstructionError::ExternalAccountLamportSpend => {
283 f.write_str("instruction spent from the balance of an account it does not own")
284 }
285 InstructionError::ExternalAccountDataModified => {
286 f.write_str("instruction modified data of an account it does not own")
287 }
288 InstructionError::ReadonlyLamportChange => {
289 f.write_str("instruction changed the balance of a read-only account")
290 }
291 InstructionError::ReadonlyDataModified => {
292 f.write_str("instruction modified data of a read-only account")
293 }
294 InstructionError::DuplicateAccountIndex => {
295 f.write_str("instruction contains duplicate accounts")
296 }
297 InstructionError::ExecutableModified => {
298 f.write_str("instruction changed executable bit of an account")
299 }
300 InstructionError::RentEpochModified => {
301 f.write_str("instruction modified rent epoch of an account")
302 }
303 InstructionError::NotEnoughAccountKeys => {
304 f.write_str("insufficient account keys for instruction")
305 }
306 InstructionError::AccountDataSizeChanged => f.write_str(
307 "program other than the account's owner changed the size of the account data",
308 ),
309 InstructionError::AccountNotExecutable => {
310 f.write_str("instruction expected an executable account")
311 }
312 InstructionError::AccountBorrowFailed => f.write_str(
313 "instruction tries to borrow reference for an account which is already borrowed",
314 ),
315 InstructionError::AccountBorrowOutstanding => {
316 f.write_str("instruction left account with an outstanding borrowed reference")
317 }
318 InstructionError::DuplicateAccountOutOfSync => {
319 f.write_str("instruction modifications of multiply-passed account differ")
320 }
321 InstructionError::Custom(num) => {
322 write!(f, "custom program error: {num:#x}")
323 }
324 InstructionError::InvalidError => f.write_str("program returned invalid error code"),
325 InstructionError::ExecutableDataModified => {
326 f.write_str("instruction changed executable accounts data")
327 }
328 InstructionError::ExecutableLamportChange => {
329 f.write_str("instruction changed the balance of an executable account")
330 }
331 InstructionError::ExecutableAccountNotRentExempt => {
332 f.write_str("executable accounts must be rent exempt")
333 }
334 InstructionError::UnsupportedProgramId => f.write_str("Unsupported program id"),
335 InstructionError::CallDepth => {
336 f.write_str("Cross-program invocation call depth too deep")
337 }
338 InstructionError::MissingAccount => {
339 f.write_str("An account required by the instruction is missing")
340 }
341 InstructionError::ReentrancyNotAllowed => {
342 f.write_str("Cross-program invocation reentrancy not allowed for this instruction")
343 }
344 InstructionError::MaxSeedLengthExceeded => {
345 f.write_str("Length of the seed is too long for address generation")
346 }
347 InstructionError::InvalidSeeds => {
348 f.write_str("Provided seeds do not result in a valid address")
349 }
350 InstructionError::InvalidRealloc => f.write_str("Failed to reallocate account data"),
351 InstructionError::ComputationalBudgetExceeded => {
352 f.write_str("Computational budget exceeded")
353 }
354 InstructionError::PrivilegeEscalation => {
355 f.write_str("Cross-program invocation with unauthorized signer or writable account")
356 }
357 InstructionError::ProgramEnvironmentSetupFailure => {
358 f.write_str("Failed to create program execution environment")
359 }
360 InstructionError::ProgramFailedToComplete => f.write_str("Program failed to complete"),
361 InstructionError::ProgramFailedToCompile => f.write_str("Program failed to compile"),
362 InstructionError::Immutable => f.write_str("Account is immutable"),
363 InstructionError::IncorrectAuthority => f.write_str("Incorrect authority provided"),
364 InstructionError::BorshIoError(s) => {
365 write!(f, "Failed to serialize or deserialize account data: {s}",)
366 }
367 InstructionError::AccountNotRentExempt => {
368 f.write_str("An account does not have enough lamports to be rent-exempt")
369 }
370 InstructionError::InvalidAccountOwner => f.write_str("Invalid account owner"),
371 InstructionError::ArithmeticOverflow => f.write_str("Program arithmetic overflowed"),
372 InstructionError::UnsupportedSysvar => f.write_str("Unsupported sysvar"),
373 InstructionError::IllegalOwner => f.write_str("Provided owner is not allowed"),
374 InstructionError::MaxAccountsDataAllocationsExceeded => f.write_str(
375 "Accounts data allocations exceeded the maximum allowed per transaction",
376 ),
377 InstructionError::MaxAccountsExceeded => f.write_str("Max accounts exceeded"),
378 InstructionError::MaxInstructionTraceLengthExceeded => {
379 f.write_str("Max instruction trace length exceeded")
380 }
381 InstructionError::BuiltinProgramsMustConsumeComputeUnits => {
382 f.write_str("Builtin programs must consume compute units")
383 }
384 }
385 }
386}
387
388#[cfg(feature = "std")]
389impl<T> From<T> for InstructionError
390where
391 T: ToPrimitive,
392{
393 fn from(error: T) -> Self {
394 let error = error.to_u64().unwrap_or(0xbad_c0de);
395 match error {
396 CUSTOM_ZERO => Self::Custom(0),
397 INVALID_ARGUMENT => Self::InvalidArgument,
398 INVALID_INSTRUCTION_DATA => Self::InvalidInstructionData,
399 INVALID_ACCOUNT_DATA => Self::InvalidAccountData,
400 ACCOUNT_DATA_TOO_SMALL => Self::AccountDataTooSmall,
401 INSUFFICIENT_FUNDS => Self::InsufficientFunds,
402 INCORRECT_PROGRAM_ID => Self::IncorrectProgramId,
403 MISSING_REQUIRED_SIGNATURES => Self::MissingRequiredSignature,
404 ACCOUNT_ALREADY_INITIALIZED => Self::AccountAlreadyInitialized,
405 UNINITIALIZED_ACCOUNT => Self::UninitializedAccount,
406 NOT_ENOUGH_ACCOUNT_KEYS => Self::NotEnoughAccountKeys,
407 ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed,
408 MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded,
409 INVALID_SEEDS => Self::InvalidSeeds,
410 BORSH_IO_ERROR => Self::BorshIoError("Unknown".to_string()),
411 ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
412 UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
413 ILLEGAL_OWNER => Self::IllegalOwner,
414 MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED => Self::MaxAccountsDataAllocationsExceeded,
415 INVALID_ACCOUNT_DATA_REALLOC => Self::InvalidRealloc,
416 MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED => Self::MaxInstructionTraceLengthExceeded,
417 BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS => {
418 Self::BuiltinProgramsMustConsumeComputeUnits
419 }
420 INVALID_ACCOUNT_OWNER => Self::InvalidAccountOwner,
421 ARITHMETIC_OVERFLOW => Self::ArithmeticOverflow,
422 IMMUTABLE => Self::Immutable,
423 INCORRECT_AUTHORITY => Self::IncorrectAuthority,
424 _ => {
425 if error >> BUILTIN_BIT_SHIFT == 0 {
427 Self::Custom(error as u32)
428 } else {
429 Self::InvalidError
430 }
431 }
432 }
433 }
434}
435
436#[derive(Debug)]
437pub enum LamportsError {
438 ArithmeticUnderflow,
440 ArithmeticOverflow,
442}
443
444#[cfg(feature = "std")]
445impl std::error::Error for LamportsError {}
446
447impl fmt::Display for LamportsError {
448 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
449 match self {
450 Self::ArithmeticUnderflow => f.write_str("Arithmetic underflowed"),
451 Self::ArithmeticOverflow => f.write_str("Arithmetic overflowed"),
452 }
453 }
454}
455
456#[cfg(feature = "std")]
457impl From<LamportsError> for InstructionError {
458 fn from(error: LamportsError) -> Self {
459 match error {
460 LamportsError::ArithmeticOverflow => InstructionError::ArithmeticOverflow,
461 LamportsError::ArithmeticUnderflow => InstructionError::ArithmeticOverflow,
462 }
463 }
464}