1#[cfg(feature = "pyo3")]
9use pyo3::{exceptions::PyValueError, PyErr};
10
11#[derive(Debug, thiserror::Error)]
13#[non_exhaustive]
14pub enum TickMathError {
15 #[error("Invalid tick value: {0}. Must be in range [-887272, 887272]")]
17 InvalidTick(i32),
18 #[error("Sqrt ratio {actual} out of bounds [{min}, {max})")]
20 SqrtRatioOutOfBounds {
21 actual: alloy::primitives::aliases::U160,
23 min: alloy::primitives::aliases::U160,
25 max: alloy::primitives::aliases::U160,
27 },
28}
29
30#[derive(Debug, thiserror::Error, PartialEq)]
32#[non_exhaustive]
33pub enum ClMathError {
34 #[error("DIVISION BY ZERO")]
36 DivisionByZero,
37 #[error("product > MAX_UINT256")]
39 Uint256Overflow,
40 #[error("Zero input")]
42 ZeroInput,
43 #[error("InvalidPrice")]
45 InvalidPrice,
46 #[error("InvalidPriceOrLiquidity")]
48 InvalidPriceOrLiquidity,
49 #[error("PriceOverflow")]
51 PriceOverflow,
52 #[error("Result overflowed MAX_UINT160")]
54 ResultOverflowedUint160,
55 #[error("NotEnoughLiquidity")]
57 InsufficientLiquidity,
58 #[error("InvalidLiquidity")]
60 InvalidLiquidity,
61 #[error("SafeCastOverflow")]
63 SafeCastOverflow,
64}
65
66#[cfg(feature = "pyo3")]
67impl From<ClMathError> for PyErr {
68 fn from(err: ClMathError) -> Self {
69 Self::new::<PyValueError, _>(err.to_string())
70 }
71}
72
73impl From<TickMathError> for ClMathError {
74 fn from(err: TickMathError) -> Self {
75 match err {
76 TickMathError::InvalidTick(_) | TickMathError::SqrtRatioOutOfBounds { .. } => {
77 Self::InvalidPrice
78 }
79 }
80 }
81}
82
83#[cfg(feature = "pyo3")]
84impl From<TickMathError> for PyErr {
85 fn from(err: TickMathError) -> Self {
86 Self::new::<PyValueError, _>(err.to_string())
89 }
90}
91
92#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
94#[non_exhaustive]
95pub enum AbiDecodeError {
96 #[error("Types list cannot be empty")]
98 EmptyTypesList,
99 #[error("Data cannot be empty")]
101 EmptyData,
102 #[error("Invalid array size in type: {0}")]
104 InvalidArraySize(String),
105 #[error("Insufficient data: need {needed} bytes at offset {offset}, have {have} bytes")]
107 InsufficientData {
108 needed: usize,
110 have: usize,
112 offset: usize,
114 },
115 #[error("Invalid offset: {0}")]
117 InvalidOffset(String),
118 #[error("Invalid length: {0}")]
120 InvalidLength(String),
121 #[error("Unsupported type: {0}")]
123 UnsupportedType(String),
124 #[error("Fixed-point types are not yet implemented")]
126 FixedPointNotImplemented,
127 #[error("Non-strict decoding mode is not yet implemented")]
129 NonStrictNotImplemented,
130}
131
132#[cfg(feature = "pyo3")]
133impl From<AbiDecodeError> for PyErr {
134 fn from(err: AbiDecodeError) -> Self {
135 Self::new::<PyValueError, _>(err.to_string())
137 }
138}
139
140#[derive(Debug, thiserror::Error)]
142#[non_exhaustive]
143pub enum AddressError {
144 #[error("Invalid address string: {0}")]
146 InvalidAddress(String),
147 #[error("Address must be exactly 20 bytes, got {0} bytes")]
149 InvalidByteLength(usize),
150}
151
152#[cfg(feature = "pyo3")]
153impl From<AddressError> for PyErr {
154 fn from(err: AddressError) -> Self {
155 Self::new::<PyValueError, _>(err.to_string())
157 }
158}
159
160#[derive(Debug, thiserror::Error, Clone)]
162#[non_exhaustive]
163pub enum ProviderError {
164 #[error("Request timeout: {message}")]
166 Timeout { message: String },
167
168 #[error("Rate limited: {message}")]
170 RateLimited { message: String },
171
172 #[error("Connection failed: {message}")]
174 ConnectionFailed { message: String },
175
176 #[error("Invalid response: {message}")]
178 InvalidResponse { message: String },
179
180 #[error("RPC error: {code} - {message}")]
182 RpcError { code: i64, message: String },
183
184 #[error("execution reverted: {message}")]
191 ExecutionReverted { code: i64, message: String },
192
193 #[error("Invalid block range: {from} to {to}")]
195 InvalidBlockRange { from: u64, to: u64 },
196
197 #[error("Invalid address format: {address} - {reason}")]
199 InvalidAddress { address: String, reason: String },
200
201 #[error("Invalid topic format: {topic} - {reason}")]
203 InvalidTopic { topic: String, reason: String },
204
205 #[error("Invalid parameters: {message}")]
207 InvalidParams { message: String },
208
209 #[error("Anvil error: {message}")]
211 AnvilError { message: String },
212
213 #[error("Invalid ABI: {message}")]
215 InvalidAbi { message: String },
216
217 #[error("Encoding error: {message}")]
219 EncodingError { message: String },
220
221 #[error("Decoding error: {message}")]
223 DecodingError { message: String },
224
225 #[error("Serialization error: {message}")]
227 SerializationError { message: String },
228
229 #[error("Subscription not supported: {message}")]
231 SubscriptionNotSupported { message: String },
232
233 #[error("endpoint {endpoint} reports chain id {actual}, not the expected {expected}")]
240 ChainMismatch {
241 expected: u64,
243 actual: u64,
245 endpoint: String,
247 },
248
249 #[error("{message}")]
251 Other { message: String },
252}
253
254impl ProviderError {
255 #[must_use]
260 pub const fn is_retryable(&self) -> bool {
261 matches!(
262 self,
263 Self::RateLimited { .. } | Self::Timeout { .. } | Self::ConnectionFailed { .. }
264 )
265 }
266}
267
268#[cfg(feature = "pyo3")]
269impl From<ProviderError> for PyErr {
270 fn from(err: ProviderError) -> Self {
271 let msg = format!("Provider error: {err}");
272 match err {
273 ProviderError::Timeout { .. } => Self::new::<pyo3::exceptions::PyTimeoutError, _>(msg),
274 ProviderError::ConnectionFailed { .. } => {
275 Self::new::<pyo3::exceptions::PyConnectionError, _>(msg)
276 }
277 ProviderError::ChainMismatch { .. } => {
281 Self::new::<pyo3::exceptions::PyValueError, _>(msg)
282 }
283 ProviderError::SubscriptionNotSupported { .. }
284 | ProviderError::RateLimited { .. }
285 | ProviderError::RpcError { .. }
286 | ProviderError::ExecutionReverted { .. }
287 | ProviderError::SerializationError { .. }
288 | ProviderError::InvalidResponse { .. }
289 | ProviderError::AnvilError { .. }
290 | ProviderError::Other { .. }
291 | ProviderError::InvalidBlockRange { .. }
292 | ProviderError::InvalidAddress { .. }
293 | ProviderError::InvalidTopic { .. }
294 | ProviderError::InvalidParams { .. }
295 | ProviderError::InvalidAbi { .. }
296 | ProviderError::EncodingError { .. }
297 | ProviderError::DecodingError { .. } => {
298 Self::new::<pyo3::exceptions::PyRuntimeError, _>(msg)
299 }
300 }
301 }
302}
303
304pub type ProviderResult<T> = Result<T, ProviderError>;
306
307#[derive(Debug, thiserror::Error, Clone)]
309#[non_exhaustive]
310pub enum ContractError {
311 #[error("Invalid ABI: {message}")]
313 InvalidAbi {
314 message: String,
316 },
317 #[error("Invalid address: {address} - {reason}")]
319 InvalidAddress {
320 address: String,
322 reason: String,
324 },
325 #[error("Encoding error: {message}")]
327 EncodingError {
328 message: String,
330 },
331 #[error("Decoding error: {message}")]
333 DecodingError {
334 message: String,
336 },
337}
338
339impl From<ContractError> for ProviderError {
340 fn from(err: ContractError) -> Self {
341 match err {
342 ContractError::InvalidAbi { message } => Self::InvalidAbi { message },
343 ContractError::InvalidAddress { address, reason } => {
344 Self::InvalidAddress { address, reason }
345 }
346 ContractError::EncodingError { message } => Self::EncodingError { message },
347 ContractError::DecodingError { message } => Self::DecodingError { message },
348 }
349 }
350}
351
352#[cfg(feature = "pyo3")]
353impl From<ContractError> for PyErr {
354 fn from(err: ContractError) -> Self {
355 Self::new::<PyValueError, _>(format!("{err}"))
356 }
357}
358
359impl From<crate::errors::AbiDecodeError> for ContractError {
360 fn from(err: crate::errors::AbiDecodeError) -> Self {
361 match err {
362 crate::errors::AbiDecodeError::UnsupportedType(msg) => {
363 Self::InvalidAbi { message: msg }
364 }
365 crate::errors::AbiDecodeError::InvalidLength(msg)
366 | crate::errors::AbiDecodeError::InvalidOffset(msg) => {
367 Self::DecodingError { message: msg }
368 }
369 crate::errors::AbiDecodeError::InsufficientData { .. } => Self::DecodingError {
370 message: err.to_string(),
371 },
372 other => Self::DecodingError {
373 message: other.to_string(),
374 },
375 }
376 }
377}
378
379pub type ContractResult<T> = Result<T, ContractError>;