solana_program_error/
lib.rs

1//! The [`ProgramError`] type and related definitions.
2
3#![allow(clippy::arithmetic_side_effects)]
4#![cfg_attr(docsrs, feature(doc_auto_cfg))]
5#[cfg(feature = "borsh")]
6use borsh::io::Error as BorshIoError;
7#[cfg(feature = "serde")]
8use serde_derive::{Deserialize, Serialize};
9use {
10    core::fmt,
11    num_traits::FromPrimitive,
12    solana_instruction::error::{
13        InstructionError, ACCOUNT_ALREADY_INITIALIZED, ACCOUNT_BORROW_FAILED,
14        ACCOUNT_DATA_TOO_SMALL, ACCOUNT_NOT_RENT_EXEMPT, ARITHMETIC_OVERFLOW, BORSH_IO_ERROR,
15        BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS, CUSTOM_ZERO, ILLEGAL_OWNER, IMMUTABLE,
16        INCORRECT_AUTHORITY, INCORRECT_PROGRAM_ID, INSUFFICIENT_FUNDS, INVALID_ACCOUNT_DATA,
17        INVALID_ACCOUNT_DATA_REALLOC, INVALID_ACCOUNT_OWNER, INVALID_ARGUMENT,
18        INVALID_INSTRUCTION_DATA, INVALID_SEEDS, MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED,
19        MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED, MAX_SEED_LENGTH_EXCEEDED,
20        MISSING_REQUIRED_SIGNATURES, NOT_ENOUGH_ACCOUNT_KEYS, UNINITIALIZED_ACCOUNT,
21        UNSUPPORTED_SYSVAR,
22    },
23    solana_msg::msg,
24    solana_pubkey::PubkeyError,
25    std::convert::TryFrom,
26};
27
28pub type ProgramResult = std::result::Result<(), ProgramError>;
29
30/// Reasons the program may fail
31#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
32#[derive(Clone, Debug, Eq, PartialEq)]
33pub enum ProgramError {
34    /// Allows on-chain programs to implement program-specific error types and see them returned
35    /// by the Solana runtime. A program-specific error may be any type that is represented as
36    /// or serialized to a u32 integer.
37    Custom(u32),
38    InvalidArgument,
39    InvalidInstructionData,
40    InvalidAccountData,
41    AccountDataTooSmall,
42    InsufficientFunds,
43    IncorrectProgramId,
44    MissingRequiredSignature,
45    AccountAlreadyInitialized,
46    UninitializedAccount,
47    NotEnoughAccountKeys,
48    AccountBorrowFailed,
49    MaxSeedLengthExceeded,
50    InvalidSeeds,
51    BorshIoError(String),
52    AccountNotRentExempt,
53    UnsupportedSysvar,
54    IllegalOwner,
55    MaxAccountsDataAllocationsExceeded,
56    InvalidRealloc,
57    MaxInstructionTraceLengthExceeded,
58    BuiltinProgramsMustConsumeComputeUnits,
59    InvalidAccountOwner,
60    ArithmeticOverflow,
61    Immutable,
62    IncorrectAuthority,
63}
64
65impl std::error::Error for ProgramError {}
66
67impl fmt::Display for ProgramError {
68    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69        match self {
70            ProgramError::Custom(num) => write!(f,"Custom program error: {num:#x}"),
71            ProgramError::InvalidArgument
72             => f.write_str("The arguments provided to a program instruction were invalid"),
73            ProgramError::InvalidInstructionData
74             => f.write_str("An instruction's data contents was invalid"),
75            ProgramError::InvalidAccountData
76             => f.write_str("An account's data contents was invalid"),
77            ProgramError::AccountDataTooSmall
78             => f.write_str("An account's data was too small"),
79            ProgramError::InsufficientFunds
80             => f.write_str("An account's balance was too small to complete the instruction"),
81            ProgramError::IncorrectProgramId
82             => f.write_str("The account did not have the expected program id"),
83            ProgramError::MissingRequiredSignature
84             => f.write_str("A signature was required but not found"),
85            ProgramError::AccountAlreadyInitialized
86             => f.write_str("An initialize instruction was sent to an account that has already been initialized"),
87            ProgramError::UninitializedAccount
88             => f.write_str("An attempt to operate on an account that hasn't been initialized"),
89            ProgramError::NotEnoughAccountKeys
90             => f.write_str("The instruction expected additional account keys"),
91            ProgramError::AccountBorrowFailed
92             => f.write_str("Failed to borrow a reference to account data, already borrowed"),
93            ProgramError::MaxSeedLengthExceeded
94             => f.write_str("Length of the seed is too long for address generation"),
95            ProgramError::InvalidSeeds
96             => f.write_str("Provided seeds do not result in a valid address"),
97            ProgramError::BorshIoError(s) =>  write!(f, "IO Error: {s}"),
98            ProgramError::AccountNotRentExempt
99             => f.write_str("An account does not have enough lamports to be rent-exempt"),
100            ProgramError::UnsupportedSysvar
101             => f.write_str("Unsupported sysvar"),
102            ProgramError::IllegalOwner
103             => f.write_str("Provided owner is not allowed"),
104            ProgramError::MaxAccountsDataAllocationsExceeded
105             => f.write_str("Accounts data allocations exceeded the maximum allowed per transaction"),
106            ProgramError::InvalidRealloc
107             => f.write_str("Account data reallocation was invalid"),
108            ProgramError::MaxInstructionTraceLengthExceeded
109             => f.write_str("Instruction trace length exceeded the maximum allowed per transaction"),
110            ProgramError::BuiltinProgramsMustConsumeComputeUnits
111             => f.write_str("Builtin programs must consume compute units"),
112            ProgramError::InvalidAccountOwner
113             => f.write_str("Invalid account owner"),
114            ProgramError::ArithmeticOverflow
115             => f.write_str("Program arithmetic overflowed"),
116            ProgramError::Immutable
117             => f.write_str("Account is immutable"),
118            ProgramError::IncorrectAuthority
119             => f.write_str("Incorrect authority provided"),
120        }
121    }
122}
123
124#[deprecated(
125    since = "2.2.2",
126    note = "Use `ToStr` instead with `solana_msg::msg!` or any other logging"
127)]
128#[allow(deprecated)]
129pub trait PrintProgramError {
130    fn print<E>(&self)
131    where
132        E: 'static
133            + std::error::Error
134            + solana_decode_error::DecodeError<E>
135            + PrintProgramError
136            + FromPrimitive;
137}
138
139#[allow(deprecated)]
140impl PrintProgramError for ProgramError {
141    fn print<E>(&self)
142    where
143        E: 'static
144            + std::error::Error
145            + solana_decode_error::DecodeError<E>
146            + PrintProgramError
147            + FromPrimitive,
148    {
149        match self {
150            Self::Custom(error) => {
151                if let Some(custom_error) = E::decode_custom_error_to_enum(*error) {
152                    custom_error.print::<E>();
153                } else {
154                    msg!("Error: Unknown");
155                }
156            }
157            Self::InvalidArgument => msg!("Error: InvalidArgument"),
158            Self::InvalidInstructionData => msg!("Error: InvalidInstructionData"),
159            Self::InvalidAccountData => msg!("Error: InvalidAccountData"),
160            Self::AccountDataTooSmall => msg!("Error: AccountDataTooSmall"),
161            Self::InsufficientFunds => msg!("Error: InsufficientFunds"),
162            Self::IncorrectProgramId => msg!("Error: IncorrectProgramId"),
163            Self::MissingRequiredSignature => msg!("Error: MissingRequiredSignature"),
164            Self::AccountAlreadyInitialized => msg!("Error: AccountAlreadyInitialized"),
165            Self::UninitializedAccount => msg!("Error: UninitializedAccount"),
166            Self::NotEnoughAccountKeys => msg!("Error: NotEnoughAccountKeys"),
167            Self::AccountBorrowFailed => msg!("Error: AccountBorrowFailed"),
168            Self::MaxSeedLengthExceeded => msg!("Error: MaxSeedLengthExceeded"),
169            Self::InvalidSeeds => msg!("Error: InvalidSeeds"),
170            Self::BorshIoError(_) => msg!("Error: BorshIoError"),
171            Self::AccountNotRentExempt => msg!("Error: AccountNotRentExempt"),
172            Self::UnsupportedSysvar => msg!("Error: UnsupportedSysvar"),
173            Self::IllegalOwner => msg!("Error: IllegalOwner"),
174            Self::MaxAccountsDataAllocationsExceeded => {
175                msg!("Error: MaxAccountsDataAllocationsExceeded")
176            }
177            Self::InvalidRealloc => msg!("Error: InvalidRealloc"),
178            Self::MaxInstructionTraceLengthExceeded => {
179                msg!("Error: MaxInstructionTraceLengthExceeded")
180            }
181            Self::BuiltinProgramsMustConsumeComputeUnits => {
182                msg!("Error: BuiltinProgramsMustConsumeComputeUnits")
183            }
184            Self::InvalidAccountOwner => msg!("Error: InvalidAccountOwner"),
185            Self::ArithmeticOverflow => msg!("Error: ArithmeticOverflow"),
186            Self::Immutable => msg!("Error: Immutable"),
187            Self::IncorrectAuthority => msg!("Error: IncorrectAuthority"),
188        }
189    }
190}
191
192/// A trait for converting a program error to a `&str`.
193pub trait ToStr {
194    fn to_str<E>(&self) -> &'static str
195    where
196        E: 'static + ToStr + TryFrom<u32>;
197}
198
199impl ToStr for ProgramError {
200    fn to_str<E>(&self) -> &'static str
201    where
202        E: 'static + ToStr + TryFrom<u32>,
203    {
204        match self {
205            Self::Custom(error) => {
206                if let Ok(custom_error) = E::try_from(*error) {
207                    custom_error.to_str::<E>()
208                } else {
209                    "Error: Unknown"
210                }
211            }
212            Self::InvalidArgument => "Error: InvalidArgument",
213            Self::InvalidInstructionData => "Error: InvalidInstructionData",
214            Self::InvalidAccountData => "Error: InvalidAccountData",
215            Self::AccountDataTooSmall => "Error: AccountDataTooSmall",
216            Self::InsufficientFunds => "Error: InsufficientFunds",
217            Self::IncorrectProgramId => "Error: IncorrectProgramId",
218            Self::MissingRequiredSignature => "Error: MissingRequiredSignature",
219            Self::AccountAlreadyInitialized => "Error: AccountAlreadyInitialized",
220            Self::UninitializedAccount => "Error: UninitializedAccount",
221            Self::NotEnoughAccountKeys => "Error: NotEnoughAccountKeys",
222            Self::AccountBorrowFailed => "Error: AccountBorrowFailed",
223            Self::MaxSeedLengthExceeded => "Error: MaxSeedLengthExceeded",
224            Self::InvalidSeeds => "Error: InvalidSeeds",
225            Self::BorshIoError(_) => "Error: BorshIoError",
226            Self::AccountNotRentExempt => "Error: AccountNotRentExempt",
227            Self::UnsupportedSysvar => "Error: UnsupportedSysvar",
228            Self::IllegalOwner => "Error: IllegalOwner",
229            Self::MaxAccountsDataAllocationsExceeded => "Error: MaxAccountsDataAllocationsExceeded",
230            Self::InvalidRealloc => "Error: InvalidRealloc",
231            Self::MaxInstructionTraceLengthExceeded => "Error: MaxInstructionTraceLengthExceeded",
232            Self::BuiltinProgramsMustConsumeComputeUnits => {
233                "Error: BuiltinProgramsMustConsumeComputeUnits"
234            }
235            Self::InvalidAccountOwner => "Error: InvalidAccountOwner",
236            Self::ArithmeticOverflow => "Error: ArithmeticOverflow",
237            Self::Immutable => "Error: Immutable",
238            Self::IncorrectAuthority => "Error: IncorrectAuthority",
239        }
240    }
241}
242
243impl From<ProgramError> for u64 {
244    fn from(error: ProgramError) -> Self {
245        match error {
246            ProgramError::InvalidArgument => INVALID_ARGUMENT,
247            ProgramError::InvalidInstructionData => INVALID_INSTRUCTION_DATA,
248            ProgramError::InvalidAccountData => INVALID_ACCOUNT_DATA,
249            ProgramError::AccountDataTooSmall => ACCOUNT_DATA_TOO_SMALL,
250            ProgramError::InsufficientFunds => INSUFFICIENT_FUNDS,
251            ProgramError::IncorrectProgramId => INCORRECT_PROGRAM_ID,
252            ProgramError::MissingRequiredSignature => MISSING_REQUIRED_SIGNATURES,
253            ProgramError::AccountAlreadyInitialized => ACCOUNT_ALREADY_INITIALIZED,
254            ProgramError::UninitializedAccount => UNINITIALIZED_ACCOUNT,
255            ProgramError::NotEnoughAccountKeys => NOT_ENOUGH_ACCOUNT_KEYS,
256            ProgramError::AccountBorrowFailed => ACCOUNT_BORROW_FAILED,
257            ProgramError::MaxSeedLengthExceeded => MAX_SEED_LENGTH_EXCEEDED,
258            ProgramError::InvalidSeeds => INVALID_SEEDS,
259            ProgramError::BorshIoError(_) => BORSH_IO_ERROR,
260            ProgramError::AccountNotRentExempt => ACCOUNT_NOT_RENT_EXEMPT,
261            ProgramError::UnsupportedSysvar => UNSUPPORTED_SYSVAR,
262            ProgramError::IllegalOwner => ILLEGAL_OWNER,
263            ProgramError::MaxAccountsDataAllocationsExceeded => {
264                MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED
265            }
266            ProgramError::InvalidRealloc => INVALID_ACCOUNT_DATA_REALLOC,
267            ProgramError::MaxInstructionTraceLengthExceeded => {
268                MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED
269            }
270            ProgramError::BuiltinProgramsMustConsumeComputeUnits => {
271                BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS
272            }
273            ProgramError::InvalidAccountOwner => INVALID_ACCOUNT_OWNER,
274            ProgramError::ArithmeticOverflow => ARITHMETIC_OVERFLOW,
275            ProgramError::Immutable => IMMUTABLE,
276            ProgramError::IncorrectAuthority => INCORRECT_AUTHORITY,
277            ProgramError::Custom(error) => {
278                if error == 0 {
279                    CUSTOM_ZERO
280                } else {
281                    error as u64
282                }
283            }
284        }
285    }
286}
287
288impl From<u64> for ProgramError {
289    fn from(error: u64) -> Self {
290        match error {
291            CUSTOM_ZERO => Self::Custom(0),
292            INVALID_ARGUMENT => Self::InvalidArgument,
293            INVALID_INSTRUCTION_DATA => Self::InvalidInstructionData,
294            INVALID_ACCOUNT_DATA => Self::InvalidAccountData,
295            ACCOUNT_DATA_TOO_SMALL => Self::AccountDataTooSmall,
296            INSUFFICIENT_FUNDS => Self::InsufficientFunds,
297            INCORRECT_PROGRAM_ID => Self::IncorrectProgramId,
298            MISSING_REQUIRED_SIGNATURES => Self::MissingRequiredSignature,
299            ACCOUNT_ALREADY_INITIALIZED => Self::AccountAlreadyInitialized,
300            UNINITIALIZED_ACCOUNT => Self::UninitializedAccount,
301            NOT_ENOUGH_ACCOUNT_KEYS => Self::NotEnoughAccountKeys,
302            ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed,
303            MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded,
304            INVALID_SEEDS => Self::InvalidSeeds,
305            BORSH_IO_ERROR => Self::BorshIoError("Unknown".to_string()),
306            ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
307            UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
308            ILLEGAL_OWNER => Self::IllegalOwner,
309            MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED => Self::MaxAccountsDataAllocationsExceeded,
310            INVALID_ACCOUNT_DATA_REALLOC => Self::InvalidRealloc,
311            MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED => Self::MaxInstructionTraceLengthExceeded,
312            BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS => {
313                Self::BuiltinProgramsMustConsumeComputeUnits
314            }
315            INVALID_ACCOUNT_OWNER => Self::InvalidAccountOwner,
316            ARITHMETIC_OVERFLOW => Self::ArithmeticOverflow,
317            IMMUTABLE => Self::Immutable,
318            INCORRECT_AUTHORITY => Self::IncorrectAuthority,
319            _ => Self::Custom(error as u32),
320        }
321    }
322}
323
324impl TryFrom<InstructionError> for ProgramError {
325    type Error = InstructionError;
326
327    fn try_from(error: InstructionError) -> Result<Self, Self::Error> {
328        match error {
329            Self::Error::Custom(err) => Ok(Self::Custom(err)),
330            Self::Error::InvalidArgument => Ok(Self::InvalidArgument),
331            Self::Error::InvalidInstructionData => Ok(Self::InvalidInstructionData),
332            Self::Error::InvalidAccountData => Ok(Self::InvalidAccountData),
333            Self::Error::AccountDataTooSmall => Ok(Self::AccountDataTooSmall),
334            Self::Error::InsufficientFunds => Ok(Self::InsufficientFunds),
335            Self::Error::IncorrectProgramId => Ok(Self::IncorrectProgramId),
336            Self::Error::MissingRequiredSignature => Ok(Self::MissingRequiredSignature),
337            Self::Error::AccountAlreadyInitialized => Ok(Self::AccountAlreadyInitialized),
338            Self::Error::UninitializedAccount => Ok(Self::UninitializedAccount),
339            Self::Error::NotEnoughAccountKeys => Ok(Self::NotEnoughAccountKeys),
340            Self::Error::AccountBorrowFailed => Ok(Self::AccountBorrowFailed),
341            Self::Error::MaxSeedLengthExceeded => Ok(Self::MaxSeedLengthExceeded),
342            Self::Error::InvalidSeeds => Ok(Self::InvalidSeeds),
343            Self::Error::BorshIoError(err) => Ok(Self::BorshIoError(err)),
344            Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt),
345            Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar),
346            Self::Error::IllegalOwner => Ok(Self::IllegalOwner),
347            Self::Error::MaxAccountsDataAllocationsExceeded => {
348                Ok(Self::MaxAccountsDataAllocationsExceeded)
349            }
350            Self::Error::InvalidRealloc => Ok(Self::InvalidRealloc),
351            Self::Error::MaxInstructionTraceLengthExceeded => {
352                Ok(Self::MaxInstructionTraceLengthExceeded)
353            }
354            Self::Error::BuiltinProgramsMustConsumeComputeUnits => {
355                Ok(Self::BuiltinProgramsMustConsumeComputeUnits)
356            }
357            Self::Error::InvalidAccountOwner => Ok(Self::InvalidAccountOwner),
358            Self::Error::ArithmeticOverflow => Ok(Self::ArithmeticOverflow),
359            Self::Error::Immutable => Ok(Self::Immutable),
360            Self::Error::IncorrectAuthority => Ok(Self::IncorrectAuthority),
361            _ => Err(error),
362        }
363    }
364}
365
366impl From<PubkeyError> for ProgramError {
367    fn from(error: PubkeyError) -> Self {
368        match error {
369            PubkeyError::MaxSeedLengthExceeded => Self::MaxSeedLengthExceeded,
370            PubkeyError::InvalidSeeds => Self::InvalidSeeds,
371            PubkeyError::IllegalOwner => Self::IllegalOwner,
372        }
373    }
374}
375
376#[cfg(feature = "borsh")]
377impl From<BorshIoError> for ProgramError {
378    fn from(error: BorshIoError) -> Self {
379        Self::BorshIoError(format!("{error}"))
380    }
381}