1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright (c) 2025-2026 R3E Network
// Licensed under the MIT License
use std::fmt;
use std::string::String;
/// Neo N3 Error type
///
/// Represents all possible error conditions that can occur during
/// Neo N3 smart contract execution.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NeoError {
/// Operation is not valid in the current context
InvalidOperation,
/// Argument has an invalid value or type
InvalidArgument,
/// Type mismatch encountered during execution
InvalidType,
/// Index or offset is out of valid bounds
OutOfBounds,
/// Attempted division by zero
DivisionByZero,
/// Arithmetic overflow occurred
Overflow,
/// Arithmetic underflow occurred
Underflow,
/// Dereferenced a null or invalid reference
NullReference,
/// Internal state is invalid or corrupted
InvalidState,
/// Application-specific error with custom message
Custom(String),
/// L6: a wasm32 cross-call syscall (System.Contract.Call,
/// System.Runtime.LoadScript, System.Contract.CallNative) was
/// invoked, but the wasm32 cross-call executor is not yet
/// implemented. Replaces the v0.8.0 B4 panic-loud sites with
/// a structured error.
Wasm32CrossCallUnavailable { syscall: &'static str },
}
impl fmt::Display for NeoError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NeoError::InvalidOperation => write!(f, "Invalid operation: the requested operation cannot be performed in the current context"),
NeoError::InvalidArgument => write!(f, "Invalid argument: one or more arguments have invalid values or types"),
NeoError::InvalidType => write!(f, "Invalid type: type mismatch encountered during execution"),
NeoError::OutOfBounds => write!(f, "Out of bounds: index or offset exceeds valid range"),
NeoError::DivisionByZero => write!(f, "Division by zero: cannot divide by zero"),
NeoError::Overflow => write!(f, "Overflow: arithmetic operation resulted in overflow"),
NeoError::Underflow => write!(f, "Underflow: arithmetic operation resulted in underflow"),
NeoError::NullReference => write!(f, "Null reference: attempted to access a null or invalid reference"),
NeoError::InvalidState => write!(f, "Invalid state: internal state is invalid or corrupted"),
NeoError::Custom(msg) => write!(f, "{}", msg),
NeoError::Wasm32CrossCallUnavailable { syscall } => {
write!(f, "wasm32 cross-call unavailable: {syscall}")
}
}
}
}
impl NeoError {
/// Creates a new custom error with the given message.
///
/// # Examples
///
/// ```
/// use neo_types::NeoError;
///
/// let err = NeoError::new("Custom error message");
/// ```
pub fn new(message: &str) -> Self {
NeoError::Custom(message.to_string())
}
/// Returns a stable status code for this error variant.
///
/// These codes are used by generated export wrappers to avoid silently
/// treating failures as successful `0` values.
pub fn status_code(&self) -> i64 {
match self {
NeoError::InvalidOperation => 1,
NeoError::InvalidArgument => 2,
NeoError::InvalidType => 3,
NeoError::OutOfBounds => 4,
NeoError::DivisionByZero => 5,
NeoError::Overflow => 6,
NeoError::Underflow => 7,
NeoError::NullReference => 8,
NeoError::InvalidState => 9,
NeoError::Custom(_) => 10,
NeoError::Wasm32CrossCallUnavailable { .. } => 11,
}
}
/// Returns the error message if this is a custom error, otherwise returns a generic description.
pub fn message(&self) -> &str {
match self {
NeoError::Custom(msg) => msg,
NeoError::Wasm32CrossCallUnavailable { syscall } => {
// `message()` returns `&str`, so it cannot allocate the
// "<prefix>: <syscall>" form that `Display` builds. Since the
// syscall is a `&'static str` from a closed set, return a
// matching static literal per known syscall.
match *syscall {
"System.Contract.Call" => "wasm32 cross-call unavailable: System.Contract.Call",
"System.Runtime.LoadScript" => {
"wasm32 cross-call unavailable: System.Runtime.LoadScript"
}
"System.Contract.CallNative" => {
"wasm32 cross-call unavailable: System.Contract.CallNative"
}
_ => "wasm32 cross-call unavailable",
}
}
_ => self.as_str(),
}
}
/// Returns a static string description of the error variant.
pub fn as_str(&self) -> &'static str {
match self {
NeoError::InvalidOperation => "InvalidOperation",
NeoError::InvalidArgument => "InvalidArgument",
NeoError::InvalidType => "InvalidType",
NeoError::OutOfBounds => "OutOfBounds",
NeoError::DivisionByZero => "DivisionByZero",
NeoError::Overflow => "Overflow",
NeoError::Underflow => "Underflow",
NeoError::NullReference => "NullReference",
NeoError::InvalidState => "InvalidState",
NeoError::Custom(_) => "Custom",
NeoError::Wasm32CrossCallUnavailable { .. } => "Wasm32CrossCallUnavailable",
}
}
}
impl std::error::Error for NeoError {}
// D16: ergonomic `From` conversions so `?` works across common stdlib error
// types encountered when parsing integers/bytes in contract code.
impl From<std::num::TryFromIntError> for NeoError {
fn from(_: std::num::TryFromIntError) -> Self {
NeoError::Overflow
}
}
impl From<std::num::ParseIntError> for NeoError {
fn from(_: std::num::ParseIntError) -> Self {
NeoError::InvalidArgument
}
}
/// Neo N3 Result type
pub type NeoResult<T> = Result<T, NeoError>;