1#[derive(Clone, Debug, Eq, PartialEq)]
19#[repr(u64)]
20pub enum ProgramError {
21 Custom(u32) = 0,
23 InvalidArgument = 1,
24 InvalidInstructionData = 2,
25 InvalidAccountData = 3,
26 AccountDataTooSmall = 4,
27 InsufficientFunds = 5,
28 IncorrectProgramId = 6,
29 MissingRequiredSignature = 7,
30 AccountAlreadyInitialized = 8,
31 UninitializedAccount = 9,
32 NotEnoughAccountKeys = 10,
33 AccountBorrowFailed = 11,
34 MaxSeedLengthExceeded = 12,
35 InvalidSeeds = 13,
36 BorshIoError = 14,
37 AccountNotRentExempt = 15,
38 UnsupportedSysvar = 16,
39 IllegalOwner = 17,
40 MaxAccountsDataAllocationsExceeded = 18,
41 InvalidRealloc = 19,
42 MaxInstructionTraceLengthExceeded = 20,
43 BuiltinProgramsMustConsumeComputeUnits = 21,
44 InvalidAccountOwner = 22,
45 ArithmeticOverflow = 23,
46 Immutable = 24,
47 IncorrectAuthority = 25,
48}
49
50impl From<ProgramError> for u64 {
53 #[inline]
54 fn from(err: ProgramError) -> u64 {
55 match err {
56 ProgramError::Custom(0) => CUSTOM_ZERO,
57 ProgramError::Custom(code) => code as u64,
58 builtin => {
59 let tag = unsafe { *(&builtin as *const ProgramError as *const u64) };
66 (tag + 1) << BUILTIN_BIT_SHIFT
71 }
72 }
73 }
74}
75
76impl From<u64> for ProgramError {
77 fn from(code: u64) -> Self {
78 if code == CUSTOM_ZERO {
79 return ProgramError::Custom(0);
80 }
81 let builtin = code >> BUILTIN_BIT_SHIFT;
82 if code & BUILTIN_LOW_MASK == 0 && builtin >= 2 {
83 match builtin - 2 {
84 0 => return ProgramError::InvalidArgument,
85 1 => return ProgramError::InvalidInstructionData,
86 2 => return ProgramError::InvalidAccountData,
87 3 => return ProgramError::AccountDataTooSmall,
88 4 => return ProgramError::InsufficientFunds,
89 5 => return ProgramError::IncorrectProgramId,
90 6 => return ProgramError::MissingRequiredSignature,
91 7 => return ProgramError::AccountAlreadyInitialized,
92 8 => return ProgramError::UninitializedAccount,
93 9 => return ProgramError::NotEnoughAccountKeys,
94 10 => return ProgramError::AccountBorrowFailed,
95 11 => return ProgramError::MaxSeedLengthExceeded,
96 12 => return ProgramError::InvalidSeeds,
97 13 => return ProgramError::BorshIoError,
98 14 => return ProgramError::AccountNotRentExempt,
99 15 => return ProgramError::UnsupportedSysvar,
100 16 => return ProgramError::IllegalOwner,
101 17 => return ProgramError::MaxAccountsDataAllocationsExceeded,
102 18 => return ProgramError::InvalidRealloc,
103 19 => return ProgramError::MaxInstructionTraceLengthExceeded,
104 20 => return ProgramError::BuiltinProgramsMustConsumeComputeUnits,
105 21 => return ProgramError::InvalidAccountOwner,
106 22 => return ProgramError::ArithmeticOverflow,
107 23 => return ProgramError::Immutable,
108 24 => return ProgramError::IncorrectAuthority,
109 _ => {}
110 }
111 }
112 ProgramError::Custom(code as u32)
113 }
114}
115
116const BUILTIN_BIT_SHIFT: usize = 32;
122const CUSTOM_ZERO: u64 = 1_u64 << BUILTIN_BIT_SHIFT;
123const BUILTIN_LOW_MASK: u64 = (1_u64 << BUILTIN_BIT_SHIFT) - 1;
124
125#[cfg(test)]
130#[inline(always)]
131const fn to_builtin(index: u64) -> u64 {
132 (index + 2) << BUILTIN_BIT_SHIFT
133}
134
135impl core::fmt::Display for ProgramError {
136 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
137 match self {
138 ProgramError::Custom(code) => write!(f, "Custom({code})"),
139 ProgramError::InvalidArgument => write!(f, "InvalidArgument"),
140 ProgramError::InvalidInstructionData => write!(f, "InvalidInstructionData"),
141 ProgramError::InvalidAccountData => write!(f, "InvalidAccountData"),
142 ProgramError::AccountDataTooSmall => write!(f, "AccountDataTooSmall"),
143 ProgramError::InsufficientFunds => write!(f, "InsufficientFunds"),
144 ProgramError::IncorrectProgramId => write!(f, "IncorrectProgramId"),
145 ProgramError::MissingRequiredSignature => write!(f, "MissingRequiredSignature"),
146 ProgramError::AccountAlreadyInitialized => write!(f, "AccountAlreadyInitialized"),
147 ProgramError::UninitializedAccount => write!(f, "UninitializedAccount"),
148 ProgramError::NotEnoughAccountKeys => write!(f, "NotEnoughAccountKeys"),
149 ProgramError::AccountBorrowFailed => write!(f, "AccountBorrowFailed"),
150 ProgramError::MaxSeedLengthExceeded => write!(f, "MaxSeedLengthExceeded"),
151 ProgramError::InvalidSeeds => write!(f, "InvalidSeeds"),
152 ProgramError::BorshIoError => write!(f, "BorshIoError"),
153 ProgramError::AccountNotRentExempt => write!(f, "AccountNotRentExempt"),
154 ProgramError::UnsupportedSysvar => write!(f, "UnsupportedSysvar"),
155 ProgramError::IllegalOwner => write!(f, "IllegalOwner"),
156 ProgramError::MaxAccountsDataAllocationsExceeded => {
157 write!(f, "MaxAccountsDataAllocationsExceeded")
158 }
159 ProgramError::InvalidRealloc => write!(f, "InvalidRealloc"),
160 ProgramError::MaxInstructionTraceLengthExceeded => {
161 write!(f, "MaxInstructionTraceLengthExceeded")
162 }
163 ProgramError::BuiltinProgramsMustConsumeComputeUnits => {
164 write!(f, "BuiltinProgramsMustConsumeComputeUnits")
165 }
166 ProgramError::InvalidAccountOwner => write!(f, "InvalidAccountOwner"),
167 ProgramError::ArithmeticOverflow => write!(f, "ArithmeticOverflow"),
168 ProgramError::Immutable => write!(f, "Immutable"),
169 ProgramError::IncorrectAuthority => write!(f, "IncorrectAuthority"),
170 }
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
184 fn u64_conversion_matches_reference_for_every_variant() {
185 fn reference(err: &ProgramError) -> u64 {
186 match err {
187 ProgramError::Custom(0) => CUSTOM_ZERO,
188 ProgramError::Custom(code) => *code as u64,
189 ProgramError::InvalidArgument => to_builtin(0),
190 ProgramError::InvalidInstructionData => to_builtin(1),
191 ProgramError::InvalidAccountData => to_builtin(2),
192 ProgramError::AccountDataTooSmall => to_builtin(3),
193 ProgramError::InsufficientFunds => to_builtin(4),
194 ProgramError::IncorrectProgramId => to_builtin(5),
195 ProgramError::MissingRequiredSignature => to_builtin(6),
196 ProgramError::AccountAlreadyInitialized => to_builtin(7),
197 ProgramError::UninitializedAccount => to_builtin(8),
198 ProgramError::NotEnoughAccountKeys => to_builtin(9),
199 ProgramError::AccountBorrowFailed => to_builtin(10),
200 ProgramError::MaxSeedLengthExceeded => to_builtin(11),
201 ProgramError::InvalidSeeds => to_builtin(12),
202 ProgramError::BorshIoError => to_builtin(13),
203 ProgramError::AccountNotRentExempt => to_builtin(14),
204 ProgramError::UnsupportedSysvar => to_builtin(15),
205 ProgramError::IllegalOwner => to_builtin(16),
206 ProgramError::MaxAccountsDataAllocationsExceeded => to_builtin(17),
207 ProgramError::InvalidRealloc => to_builtin(18),
208 ProgramError::MaxInstructionTraceLengthExceeded => to_builtin(19),
209 ProgramError::BuiltinProgramsMustConsumeComputeUnits => to_builtin(20),
210 ProgramError::InvalidAccountOwner => to_builtin(21),
211 ProgramError::ArithmeticOverflow => to_builtin(22),
212 ProgramError::Immutable => to_builtin(23),
213 ProgramError::IncorrectAuthority => to_builtin(24),
214 }
215 }
216
217 let all = [
218 ProgramError::Custom(0),
219 ProgramError::Custom(1),
220 ProgramError::Custom(0xD003),
221 ProgramError::Custom(u32::MAX),
222 ProgramError::InvalidArgument,
223 ProgramError::InvalidInstructionData,
224 ProgramError::InvalidAccountData,
225 ProgramError::AccountDataTooSmall,
226 ProgramError::InsufficientFunds,
227 ProgramError::IncorrectProgramId,
228 ProgramError::MissingRequiredSignature,
229 ProgramError::AccountAlreadyInitialized,
230 ProgramError::UninitializedAccount,
231 ProgramError::NotEnoughAccountKeys,
232 ProgramError::AccountBorrowFailed,
233 ProgramError::MaxSeedLengthExceeded,
234 ProgramError::InvalidSeeds,
235 ProgramError::BorshIoError,
236 ProgramError::AccountNotRentExempt,
237 ProgramError::UnsupportedSysvar,
238 ProgramError::IllegalOwner,
239 ProgramError::MaxAccountsDataAllocationsExceeded,
240 ProgramError::InvalidRealloc,
241 ProgramError::MaxInstructionTraceLengthExceeded,
242 ProgramError::BuiltinProgramsMustConsumeComputeUnits,
243 ProgramError::InvalidAccountOwner,
244 ProgramError::ArithmeticOverflow,
245 ProgramError::Immutable,
246 ProgramError::IncorrectAuthority,
247 ];
248 for err in all {
249 let want = reference(&err);
250 let got: u64 = err.clone().into();
251 assert_eq!(got, want, "u64 code diverged for {err:?}");
252 assert_eq!(ProgramError::from(want), err, "roundtrip for {err:?}");
255 }
256 }
257}