Skip to main content

chainerrors_core/
decoder.rs

1//! The `ErrorDecoder` trait — implemented by each chain-specific crate.
2
3use crate::types::{DecodedError, ErrorContext};
4use thiserror::Error;
5
6/// Errors that can occur during error decoding.
7#[derive(Debug, Error)]
8pub enum DecodeErrorError {
9    #[error("ABI decode failed: {reason}")]
10    AbiDecodeFailed { reason: String },
11
12    #[error("Invalid revert data: {reason}")]
13    InvalidData { reason: String },
14
15    #[error("Unsupported chain: {chain}")]
16    UnsupportedChain { chain: String },
17
18    #[error("Registry lookup failed: {reason}")]
19    RegistryError { reason: String },
20
21    #[error("{0}")]
22    Other(String),
23}
24
25/// A chain-specific error decoder.
26///
27/// Each chain family (EVM, Solana, …) provides its own implementation.
28/// Implementations must be `Send + Sync` for use in async contexts.
29pub trait ErrorDecoder: Send + Sync {
30    /// Returns the chain family name this decoder handles (e.g. `"evm"`).
31    fn chain_family(&self) -> &'static str;
32
33    /// Decode raw revert/error data from a failed transaction.
34    ///
35    /// `revert_data` is the raw bytes returned by the node in the
36    /// `revert` field (EVM) or equivalent.
37    ///
38    /// On success returns a `DecodedError` with the highest-confidence
39    /// interpretation available. Never returns `Err` for unknown selectors —
40    /// instead returns a `RawRevert` or `Empty` kind.
41    fn decode(
42        &self,
43        revert_data: &[u8],
44        ctx: Option<ErrorContext>,
45    ) -> Result<DecodedError, DecodeErrorError>;
46
47    /// Convenience: decode from a hex string (with or without `0x` prefix).
48    fn decode_hex(
49        &self,
50        hex_str: &str,
51        ctx: Option<ErrorContext>,
52    ) -> Result<DecodedError, DecodeErrorError> {
53        let stripped = hex_str.strip_prefix("0x").unwrap_or(hex_str);
54        let bytes = hex::decode(stripped).map_err(|e| DecodeErrorError::InvalidData {
55            reason: format!("invalid hex: {e}"),
56        })?;
57        self.decode(&bytes, ctx)
58    }
59}