1#![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#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
32#[derive(Clone, Debug, Eq, PartialEq)]
33pub enum ProgramError {
34 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
192pub 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}