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