1use thiserror::Error;
2
3#[derive(Error, Debug)]
5pub enum Neo3Error {
6 #[error("Cryptographic error: {0}")]
8 Crypto(#[from] CryptoError),
9
10 #[error("Wallet error: {0}")]
12 Wallet(#[from] WalletError),
13
14 #[error("Network error: {0}")]
16 Network(#[from] NetworkError),
17
18 #[error("Transaction error: {0}")]
20 Transaction(#[from] TransactionError),
21
22 #[error("Contract error: {0}")]
24 Contract(#[from] ContractError),
25
26 #[error("Serialization error: {0}")]
28 Serialization(#[from] SerializationError),
29
30 #[error("Configuration error: {0}")]
32 Config(String),
33
34 #[error("Error: {message}")]
36 Generic { message: String },
37
38 #[error("Unsupported operation: {0}")]
40 UnsupportedOperation(String),
41}
42
43#[derive(Error, Debug)]
44pub enum CryptoError {
45 #[error("Invalid private key: {0}")]
46 InvalidPrivateKey(String),
47
48 #[error("Invalid public key: {0}")]
49 InvalidPublicKey(String),
50
51 #[error("Signature verification failed")]
52 SignatureVerificationFailed,
53
54 #[error("Key generation failed: {0}")]
55 KeyGenerationFailed(String),
56
57 #[error("Hash operation failed: {0}")]
58 HashFailed(String),
59
60 #[error("Encryption failed: {0}")]
61 EncryptionFailed(String),
62
63 #[error("Decryption failed: {0}")]
64 DecryptionFailed(String),
65}
66
67#[derive(Error, Debug)]
68pub enum WalletError {
69 #[error("Wallet not found: {0}")]
70 NotFound(String),
71
72 #[error("Invalid password")]
73 InvalidPassword,
74
75 #[error("Account not found: {0}")]
76 AccountNotFound(String),
77
78 #[error("Wallet is locked")]
79 WalletLocked,
80
81 #[error("Backup operation failed: {0}")]
82 BackupFailed(String),
83
84 #[error("Recovery operation failed: {0}")]
85 RecoveryFailed(String),
86
87 #[error("Invalid wallet format: {0}")]
88 InvalidFormat(String),
89
90 #[error("IO error: {0}")]
91 Io(#[from] std::io::Error),
92}
93
94#[derive(Error, Debug)]
95pub enum NetworkError {
96 #[error("Connection failed: {0}")]
97 ConnectionFailed(String),
98
99 #[error("Request timeout")]
100 Timeout,
101
102 #[error("Invalid response: {0}")]
103 InvalidResponse(String),
104
105 #[error("RPC error: {code} - {message}")]
106 RpcError { code: i32, message: String },
107
108 #[error("Network unreachable: {0}")]
109 NetworkUnreachable(String),
110
111 #[error("Rate limit exceeded")]
112 RateLimitExceeded,
113
114 #[error("HTTP error: {0}")]
115 Http(#[from] reqwest::Error),
116}
117
118#[derive(Error, Debug)]
119pub enum TransactionError {
120 #[error("Invalid transaction: {0}")]
121 Invalid(String),
122
123 #[error("Insufficient funds: required {required}, available {available}")]
124 InsufficientFunds { required: u64, available: u64 },
125
126 #[error("Transaction too large: {size} bytes (max: {max})")]
127 TooLarge { size: usize, max: usize },
128
129 #[error("Invalid signature")]
130 InvalidSignature,
131
132 #[error("Transaction expired")]
133 Expired,
134
135 #[error("Nonce too low: {provided} (expected: {expected})")]
136 NonceTooLow { provided: u64, expected: u64 },
137
138 #[error("Gas limit exceeded: {used} (limit: {limit})")]
139 GasLimitExceeded { used: u64, limit: u64 },
140}
141
142#[derive(Error, Debug)]
143pub enum ContractError {
144 #[error("Contract not found: {0}")]
145 NotFound(String),
146
147 #[error("Method not found: {0}")]
148 MethodNotFound(String),
149
150 #[error("Invalid parameters: {0}")]
151 InvalidParameters(String),
152
153 #[error("Execution failed: {0}")]
154 ExecutionFailed(String),
155
156 #[error("Insufficient gas: {0}")]
157 InsufficientGas(String),
158
159 #[error("Contract deployment failed: {0}")]
160 DeploymentFailed(String),
161}
162
163#[derive(Error, Debug)]
164pub enum SerializationError {
165 #[error("JSON error: {0}")]
166 Json(#[from] serde_json::Error),
167
168 #[error("Invalid format: {0}")]
169 InvalidFormat(String),
170
171 #[error("Encoding error: {0}")]
172 Encoding(String),
173
174 #[error("Decoding error: {0}")]
175 Decoding(String),
176}
177
178pub type Neo3Result<T> = Result<T, Neo3Error>;
180
181pub type NeoError = Neo3Error;
183
184impl From<std::io::Error> for Neo3Error {
186 fn from(err: std::io::Error) -> Self {
187 Neo3Error::Wallet(WalletError::Io(err))
188 }
189}
190
191impl From<serde_json::Error> for Neo3Error {
192 fn from(err: serde_json::Error) -> Self {
193 Neo3Error::Serialization(SerializationError::Json(err))
194 }
195}
196
197pub trait ErrorContext<T> {
199 fn with_context<F>(self, f: F) -> Neo3Result<T>
200 where
201 F: FnOnce() -> String;
202}
203
204impl<T, E> ErrorContext<T> for Result<T, E>
205where
206 E: Into<Neo3Error>,
207{
208 fn with_context<F>(self, f: F) -> Neo3Result<T>
209 where
210 F: FnOnce() -> String,
211 {
212 self.map_err(|e| {
213 let base_error = e.into();
214 Neo3Error::Generic { message: format!("{}: {}", f(), base_error) }
215 })
216 }
217}
218
219#[macro_export]
221macro_rules! neo3_error {
222 ($msg:expr) => {
223 Neo3Error::Generic {
224 message: $msg.to_string(),
225 }
226 };
227 ($fmt:expr, $($arg:tt)*) => {
228 Neo3Error::Generic {
229 message: format!($fmt, $($arg)*),
230 }
231 };
232}
233
234#[macro_export]
236macro_rules! ensure {
237 ($cond:expr, $msg:expr) => {
238 if !$cond {
239 return Err(neo3_error!($msg));
240 }
241 };
242 ($cond:expr, $fmt:expr, $($arg:tt)*) => {
243 if !$cond {
244 return Err(neo3_error!($fmt, $($arg)*));
245 }
246 };
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252
253 #[test]
254 fn test_error_context() {
255 let result: Result<(), std::io::Error> =
256 Err(std::io::Error::new(std::io::ErrorKind::NotFound, "file not found"));
257
258 let with_context = result.with_context(|| "Failed to read configuration file".to_string());
259
260 assert!(with_context.is_err());
261 let error_msg = with_context.unwrap_err().to_string();
262 assert!(error_msg.contains("Failed to read configuration file"));
263 assert!(error_msg.contains("file not found"));
264 }
265
266 #[test]
267 fn test_error_macros() {
268 let error = neo3_error!("Something went wrong");
269 assert_eq!(error.to_string(), "Error: Something went wrong");
270
271 let error = neo3_error!("Value {} is invalid", 42);
272 assert_eq!(error.to_string(), "Error: Value 42 is invalid");
273 }
274}