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_decode_error::DecodeError,
13    solana_instruction::error::{
14        InstructionError, ACCOUNT_ALREADY_INITIALIZED, ACCOUNT_BORROW_FAILED,
15        ACCOUNT_DATA_TOO_SMALL, ACCOUNT_NOT_RENT_EXEMPT, ARITHMETIC_OVERFLOW, BORSH_IO_ERROR,
16        BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS, CUSTOM_ZERO, ILLEGAL_OWNER, IMMUTABLE,
17        INCORRECT_AUTHORITY, INCORRECT_PROGRAM_ID, INSUFFICIENT_FUNDS, INVALID_ACCOUNT_DATA,
18        INVALID_ACCOUNT_DATA_REALLOC, INVALID_ACCOUNT_OWNER, INVALID_ARGUMENT,
19        INVALID_INSTRUCTION_DATA, INVALID_SEEDS, MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED,
20        MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED, MAX_SEED_LENGTH_EXCEEDED,
21        MISSING_REQUIRED_SIGNATURES, NOT_ENOUGH_ACCOUNT_KEYS, UNINITIALIZED_ACCOUNT,
22        UNSUPPORTED_SYSVAR,
23    },
24    solana_msg::msg,
25    solana_pubkey::PubkeyError,
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
125pub trait PrintProgramError {
126    fn print<E>(&self)
127    where
128        E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive;
129}
130
131impl PrintProgramError for ProgramError {
132    fn print<E>(&self)
133    where
134        E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
135    {
136        match self {
137            Self::Custom(error) => {
138                if let Some(custom_error) = E::decode_custom_error_to_enum(*error) {
139                    custom_error.print::<E>();
140                } else {
141                    msg!("Error: Unknown");
142                }
143            }
144            Self::InvalidArgument => msg!("Error: InvalidArgument"),
145            Self::InvalidInstructionData => msg!("Error: InvalidInstructionData"),
146            Self::InvalidAccountData => msg!("Error: InvalidAccountData"),
147            Self::AccountDataTooSmall => msg!("Error: AccountDataTooSmall"),
148            Self::InsufficientFunds => msg!("Error: InsufficientFunds"),
149            Self::IncorrectProgramId => msg!("Error: IncorrectProgramId"),
150            Self::MissingRequiredSignature => msg!("Error: MissingRequiredSignature"),
151            Self::AccountAlreadyInitialized => msg!("Error: AccountAlreadyInitialized"),
152            Self::UninitializedAccount => msg!("Error: UninitializedAccount"),
153            Self::NotEnoughAccountKeys => msg!("Error: NotEnoughAccountKeys"),
154            Self::AccountBorrowFailed => msg!("Error: AccountBorrowFailed"),
155            Self::MaxSeedLengthExceeded => msg!("Error: MaxSeedLengthExceeded"),
156            Self::InvalidSeeds => msg!("Error: InvalidSeeds"),
157            Self::BorshIoError(_) => msg!("Error: BorshIoError"),
158            Self::AccountNotRentExempt => msg!("Error: AccountNotRentExempt"),
159            Self::UnsupportedSysvar => msg!("Error: UnsupportedSysvar"),
160            Self::IllegalOwner => msg!("Error: IllegalOwner"),
161            Self::MaxAccountsDataAllocationsExceeded => {
162                msg!("Error: MaxAccountsDataAllocationsExceeded")
163            }
164            Self::InvalidRealloc => msg!("Error: InvalidRealloc"),
165            Self::MaxInstructionTraceLengthExceeded => {
166                msg!("Error: MaxInstructionTraceLengthExceeded")
167            }
168            Self::BuiltinProgramsMustConsumeComputeUnits => {
169                msg!("Error: BuiltinProgramsMustConsumeComputeUnits")
170            }
171            Self::InvalidAccountOwner => msg!("Error: InvalidAccountOwner"),
172            Self::ArithmeticOverflow => msg!("Error: ArithmeticOverflow"),
173            Self::Immutable => msg!("Error: Immutable"),
174            Self::IncorrectAuthority => msg!("Error: IncorrectAuthority"),
175        }
176    }
177}
178
179impl From<ProgramError> for u64 {
180    fn from(error: ProgramError) -> Self {
181        match error {
182            ProgramError::InvalidArgument => INVALID_ARGUMENT,
183            ProgramError::InvalidInstructionData => INVALID_INSTRUCTION_DATA,
184            ProgramError::InvalidAccountData => INVALID_ACCOUNT_DATA,
185            ProgramError::AccountDataTooSmall => ACCOUNT_DATA_TOO_SMALL,
186            ProgramError::InsufficientFunds => INSUFFICIENT_FUNDS,
187            ProgramError::IncorrectProgramId => INCORRECT_PROGRAM_ID,
188            ProgramError::MissingRequiredSignature => MISSING_REQUIRED_SIGNATURES,
189            ProgramError::AccountAlreadyInitialized => ACCOUNT_ALREADY_INITIALIZED,
190            ProgramError::UninitializedAccount => UNINITIALIZED_ACCOUNT,
191            ProgramError::NotEnoughAccountKeys => NOT_ENOUGH_ACCOUNT_KEYS,
192            ProgramError::AccountBorrowFailed => ACCOUNT_BORROW_FAILED,
193            ProgramError::MaxSeedLengthExceeded => MAX_SEED_LENGTH_EXCEEDED,
194            ProgramError::InvalidSeeds => INVALID_SEEDS,
195            ProgramError::BorshIoError(_) => BORSH_IO_ERROR,
196            ProgramError::AccountNotRentExempt => ACCOUNT_NOT_RENT_EXEMPT,
197            ProgramError::UnsupportedSysvar => UNSUPPORTED_SYSVAR,
198            ProgramError::IllegalOwner => ILLEGAL_OWNER,
199            ProgramError::MaxAccountsDataAllocationsExceeded => {
200                MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED
201            }
202            ProgramError::InvalidRealloc => INVALID_ACCOUNT_DATA_REALLOC,
203            ProgramError::MaxInstructionTraceLengthExceeded => {
204                MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED
205            }
206            ProgramError::BuiltinProgramsMustConsumeComputeUnits => {
207                BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS
208            }
209            ProgramError::InvalidAccountOwner => INVALID_ACCOUNT_OWNER,
210            ProgramError::ArithmeticOverflow => ARITHMETIC_OVERFLOW,
211            ProgramError::Immutable => IMMUTABLE,
212            ProgramError::IncorrectAuthority => INCORRECT_AUTHORITY,
213            ProgramError::Custom(error) => {
214                if error == 0 {
215                    CUSTOM_ZERO
216                } else {
217                    error as u64
218                }
219            }
220        }
221    }
222}
223
224impl From<u64> for ProgramError {
225    fn from(error: u64) -> Self {
226        match error {
227            CUSTOM_ZERO => Self::Custom(0),
228            INVALID_ARGUMENT => Self::InvalidArgument,
229            INVALID_INSTRUCTION_DATA => Self::InvalidInstructionData,
230            INVALID_ACCOUNT_DATA => Self::InvalidAccountData,
231            ACCOUNT_DATA_TOO_SMALL => Self::AccountDataTooSmall,
232            INSUFFICIENT_FUNDS => Self::InsufficientFunds,
233            INCORRECT_PROGRAM_ID => Self::IncorrectProgramId,
234            MISSING_REQUIRED_SIGNATURES => Self::MissingRequiredSignature,
235            ACCOUNT_ALREADY_INITIALIZED => Self::AccountAlreadyInitialized,
236            UNINITIALIZED_ACCOUNT => Self::UninitializedAccount,
237            NOT_ENOUGH_ACCOUNT_KEYS => Self::NotEnoughAccountKeys,
238            ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed,
239            MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded,
240            INVALID_SEEDS => Self::InvalidSeeds,
241            BORSH_IO_ERROR => Self::BorshIoError("Unknown".to_string()),
242            ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
243            UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
244            ILLEGAL_OWNER => Self::IllegalOwner,
245            MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED => Self::MaxAccountsDataAllocationsExceeded,
246            INVALID_ACCOUNT_DATA_REALLOC => Self::InvalidRealloc,
247            MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED => Self::MaxInstructionTraceLengthExceeded,
248            BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS => {
249                Self::BuiltinProgramsMustConsumeComputeUnits
250            }
251            INVALID_ACCOUNT_OWNER => Self::InvalidAccountOwner,
252            ARITHMETIC_OVERFLOW => Self::ArithmeticOverflow,
253            IMMUTABLE => Self::Immutable,
254            INCORRECT_AUTHORITY => Self::IncorrectAuthority,
255            _ => Self::Custom(error as u32),
256        }
257    }
258}
259
260impl TryFrom<InstructionError> for ProgramError {
261    type Error = InstructionError;
262
263    fn try_from(error: InstructionError) -> Result<Self, Self::Error> {
264        match error {
265            Self::Error::Custom(err) => Ok(Self::Custom(err)),
266            Self::Error::InvalidArgument => Ok(Self::InvalidArgument),
267            Self::Error::InvalidInstructionData => Ok(Self::InvalidInstructionData),
268            Self::Error::InvalidAccountData => Ok(Self::InvalidAccountData),
269            Self::Error::AccountDataTooSmall => Ok(Self::AccountDataTooSmall),
270            Self::Error::InsufficientFunds => Ok(Self::InsufficientFunds),
271            Self::Error::IncorrectProgramId => Ok(Self::IncorrectProgramId),
272            Self::Error::MissingRequiredSignature => Ok(Self::MissingRequiredSignature),
273            Self::Error::AccountAlreadyInitialized => Ok(Self::AccountAlreadyInitialized),
274            Self::Error::UninitializedAccount => Ok(Self::UninitializedAccount),
275            Self::Error::NotEnoughAccountKeys => Ok(Self::NotEnoughAccountKeys),
276            Self::Error::AccountBorrowFailed => Ok(Self::AccountBorrowFailed),
277            Self::Error::MaxSeedLengthExceeded => Ok(Self::MaxSeedLengthExceeded),
278            Self::Error::InvalidSeeds => Ok(Self::InvalidSeeds),
279            Self::Error::BorshIoError(err) => Ok(Self::BorshIoError(err)),
280            Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt),
281            Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar),
282            Self::Error::IllegalOwner => Ok(Self::IllegalOwner),
283            Self::Error::MaxAccountsDataAllocationsExceeded => {
284                Ok(Self::MaxAccountsDataAllocationsExceeded)
285            }
286            Self::Error::InvalidRealloc => Ok(Self::InvalidRealloc),
287            Self::Error::MaxInstructionTraceLengthExceeded => {
288                Ok(Self::MaxInstructionTraceLengthExceeded)
289            }
290            Self::Error::BuiltinProgramsMustConsumeComputeUnits => {
291                Ok(Self::BuiltinProgramsMustConsumeComputeUnits)
292            }
293            Self::Error::InvalidAccountOwner => Ok(Self::InvalidAccountOwner),
294            Self::Error::ArithmeticOverflow => Ok(Self::ArithmeticOverflow),
295            Self::Error::Immutable => Ok(Self::Immutable),
296            Self::Error::IncorrectAuthority => Ok(Self::IncorrectAuthority),
297            _ => Err(error),
298        }
299    }
300}
301
302impl From<PubkeyError> for ProgramError {
303    fn from(error: PubkeyError) -> Self {
304        match error {
305            PubkeyError::MaxSeedLengthExceeded => Self::MaxSeedLengthExceeded,
306            PubkeyError::InvalidSeeds => Self::InvalidSeeds,
307            PubkeyError::IllegalOwner => Self::IllegalOwner,
308        }
309    }
310}
311
312#[cfg(feature = "borsh")]
313impl From<BorshIoError> for ProgramError {
314    fn from(error: BorshIoError) -> Self {
315        Self::BorshIoError(format!("{error}"))
316    }
317}