Skip to main content

hopper_native/
error.rs

1//! Program error type for Solana on-chain programs.
2//!
3//! Wire-compatible with pinocchio/solana-program ProgramError.
4//! Each variant maps to a fixed u64 error code returned to the runtime.
5
6/// Errors that a Solana program can return.
7#[derive(Clone, Debug, Eq, PartialEq)]
8pub enum ProgramError {
9    /// Custom program error with a u32 code.
10    Custom(u32),
11    InvalidArgument,
12    InvalidInstructionData,
13    InvalidAccountData,
14    AccountDataTooSmall,
15    InsufficientFunds,
16    IncorrectProgramId,
17    MissingRequiredSignature,
18    AccountAlreadyInitialized,
19    UninitializedAccount,
20    NotEnoughAccountKeys,
21    AccountBorrowFailed,
22    MaxSeedLengthExceeded,
23    InvalidSeeds,
24    BorshIoError,
25    AccountNotRentExempt,
26    UnsupportedSysvar,
27    IllegalOwner,
28    MaxAccountsDataAllocationsExceeded,
29    InvalidRealloc,
30    MaxInstructionTraceLengthExceeded,
31    BuiltinProgramsMustConsumeComputeUnits,
32    InvalidAccountOwner,
33    ArithmeticOverflow,
34    Immutable,
35    IncorrectAuthority,
36}
37
38// ── u64 conversion (Solana runtime ABI) ──────────────────────────────
39
40impl From<ProgramError> for u64 {
41    fn from(err: ProgramError) -> u64 {
42        match err {
43            ProgramError::Custom(0) => CUSTOM_ZERO,
44            ProgramError::Custom(code) => code as u64,
45            ProgramError::InvalidArgument => to_builtin(0),
46            ProgramError::InvalidInstructionData => to_builtin(1),
47            ProgramError::InvalidAccountData => to_builtin(2),
48            ProgramError::AccountDataTooSmall => to_builtin(3),
49            ProgramError::InsufficientFunds => to_builtin(4),
50            ProgramError::IncorrectProgramId => to_builtin(5),
51            ProgramError::MissingRequiredSignature => to_builtin(6),
52            ProgramError::AccountAlreadyInitialized => to_builtin(7),
53            ProgramError::UninitializedAccount => to_builtin(8),
54            ProgramError::NotEnoughAccountKeys => to_builtin(9),
55            ProgramError::AccountBorrowFailed => to_builtin(10),
56            ProgramError::MaxSeedLengthExceeded => to_builtin(11),
57            ProgramError::InvalidSeeds => to_builtin(12),
58            ProgramError::BorshIoError => to_builtin(13),
59            ProgramError::AccountNotRentExempt => to_builtin(14),
60            ProgramError::UnsupportedSysvar => to_builtin(15),
61            ProgramError::IllegalOwner => to_builtin(16),
62            ProgramError::MaxAccountsDataAllocationsExceeded => to_builtin(17),
63            ProgramError::InvalidRealloc => to_builtin(18),
64            ProgramError::MaxInstructionTraceLengthExceeded => to_builtin(19),
65            ProgramError::BuiltinProgramsMustConsumeComputeUnits => to_builtin(20),
66            ProgramError::InvalidAccountOwner => to_builtin(21),
67            ProgramError::ArithmeticOverflow => to_builtin(22),
68            ProgramError::Immutable => to_builtin(23),
69            ProgramError::IncorrectAuthority => to_builtin(24),
70        }
71    }
72}
73
74impl From<u64> for ProgramError {
75    fn from(code: u64) -> Self {
76        match code {
77            CUSTOM_ZERO => ProgramError::Custom(0),
78            c if c == to_builtin(0) => ProgramError::InvalidArgument,
79            c if c == to_builtin(1) => ProgramError::InvalidInstructionData,
80            c if c == to_builtin(2) => ProgramError::InvalidAccountData,
81            c if c == to_builtin(3) => ProgramError::AccountDataTooSmall,
82            c if c == to_builtin(4) => ProgramError::InsufficientFunds,
83            c if c == to_builtin(5) => ProgramError::IncorrectProgramId,
84            c if c == to_builtin(6) => ProgramError::MissingRequiredSignature,
85            c if c == to_builtin(7) => ProgramError::AccountAlreadyInitialized,
86            c if c == to_builtin(8) => ProgramError::UninitializedAccount,
87            c if c == to_builtin(9) => ProgramError::NotEnoughAccountKeys,
88            c if c == to_builtin(10) => ProgramError::AccountBorrowFailed,
89            c if c == to_builtin(11) => ProgramError::MaxSeedLengthExceeded,
90            c if c == to_builtin(12) => ProgramError::InvalidSeeds,
91            c if c == to_builtin(13) => ProgramError::BorshIoError,
92            c if c == to_builtin(14) => ProgramError::AccountNotRentExempt,
93            c if c == to_builtin(15) => ProgramError::UnsupportedSysvar,
94            c if c == to_builtin(16) => ProgramError::IllegalOwner,
95            c if c == to_builtin(17) => ProgramError::MaxAccountsDataAllocationsExceeded,
96            c if c == to_builtin(18) => ProgramError::InvalidRealloc,
97            c if c == to_builtin(19) => ProgramError::MaxInstructionTraceLengthExceeded,
98            c if c == to_builtin(20) => ProgramError::BuiltinProgramsMustConsumeComputeUnits,
99            c if c == to_builtin(21) => ProgramError::InvalidAccountOwner,
100            c if c == to_builtin(22) => ProgramError::ArithmeticOverflow,
101            c if c == to_builtin(23) => ProgramError::Immutable,
102            c if c == to_builtin(24) => ProgramError::IncorrectAuthority,
103            other => ProgramError::Custom(other as u32),
104        }
105    }
106}
107
108/// Map a builtin error index to its runtime u64 code.
109///
110/// The Solana runtime uses a specific encoding for builtin errors:
111/// - `Custom(0)` occupies `1 << 32`
112/// - builtin errors start at `2 << 32`
113const BUILTIN_BIT_SHIFT: usize = 32;
114const CUSTOM_ZERO: u64 = 1_u64 << BUILTIN_BIT_SHIFT;
115
116#[inline(always)]
117const fn to_builtin(index: u64) -> u64 {
118    (index + 2) << BUILTIN_BIT_SHIFT
119}
120
121impl core::fmt::Display for ProgramError {
122    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
123        match self {
124            ProgramError::Custom(code) => write!(f, "Custom({code})"),
125            ProgramError::InvalidArgument => write!(f, "InvalidArgument"),
126            ProgramError::InvalidInstructionData => write!(f, "InvalidInstructionData"),
127            ProgramError::InvalidAccountData => write!(f, "InvalidAccountData"),
128            ProgramError::AccountDataTooSmall => write!(f, "AccountDataTooSmall"),
129            ProgramError::InsufficientFunds => write!(f, "InsufficientFunds"),
130            ProgramError::IncorrectProgramId => write!(f, "IncorrectProgramId"),
131            ProgramError::MissingRequiredSignature => write!(f, "MissingRequiredSignature"),
132            ProgramError::AccountAlreadyInitialized => write!(f, "AccountAlreadyInitialized"),
133            ProgramError::UninitializedAccount => write!(f, "UninitializedAccount"),
134            ProgramError::NotEnoughAccountKeys => write!(f, "NotEnoughAccountKeys"),
135            ProgramError::AccountBorrowFailed => write!(f, "AccountBorrowFailed"),
136            ProgramError::MaxSeedLengthExceeded => write!(f, "MaxSeedLengthExceeded"),
137            ProgramError::InvalidSeeds => write!(f, "InvalidSeeds"),
138            ProgramError::BorshIoError => write!(f, "BorshIoError"),
139            ProgramError::AccountNotRentExempt => write!(f, "AccountNotRentExempt"),
140            ProgramError::UnsupportedSysvar => write!(f, "UnsupportedSysvar"),
141            ProgramError::IllegalOwner => write!(f, "IllegalOwner"),
142            ProgramError::MaxAccountsDataAllocationsExceeded => {
143                write!(f, "MaxAccountsDataAllocationsExceeded")
144            }
145            ProgramError::InvalidRealloc => write!(f, "InvalidRealloc"),
146            ProgramError::MaxInstructionTraceLengthExceeded => {
147                write!(f, "MaxInstructionTraceLengthExceeded")
148            }
149            ProgramError::BuiltinProgramsMustConsumeComputeUnits => {
150                write!(f, "BuiltinProgramsMustConsumeComputeUnits")
151            }
152            ProgramError::InvalidAccountOwner => write!(f, "InvalidAccountOwner"),
153            ProgramError::ArithmeticOverflow => write!(f, "ArithmeticOverflow"),
154            ProgramError::Immutable => write!(f, "Immutable"),
155            ProgramError::IncorrectAuthority => write!(f, "IncorrectAuthority"),
156        }
157    }
158}