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