Skip to main content

ledger_sdk_eth_app/
errors.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Error types for Ethereum application
4
5use ledger_sdk_device_base::LedgerAppError;
6use thiserror::Error;
7
8/// Ethereum application specific errors
9#[derive(Debug, Error, Clone, PartialEq)]
10pub enum EthAppError<E: std::error::Error> {
11    /// Error from the underlying transport/device
12    #[error("Transport error: {0}")]
13    Transport(#[from] LedgerAppError<E>),
14
15    /// Invalid BIP32 derivation path
16    #[error("Invalid BIP32 path: {0}")]
17    InvalidBip32Path(String),
18
19    /// Invalid Ethereum address format
20    #[error("Invalid Ethereum address: {0}")]
21    InvalidAddress(String),
22
23    /// Invalid signature format
24    #[error("Invalid signature: {0}")]
25    InvalidSignature(String),
26
27    /// Transaction data too large
28    #[error("Transaction data too large: {size} bytes (max {max})")]
29    TransactionTooLarge { size: usize, max: usize },
30
31    /// Message data too large
32    #[error("Message data too large: {size} bytes (max {max})")]
33    MessageTooLarge { size: usize, max: usize },
34
35    /// Invalid transaction format
36    #[error("Invalid transaction format: {0}")]
37    InvalidTransaction(String),
38
39    /// Invalid message format
40    #[error("Invalid message format: {0}")]
41    InvalidMessage(String),
42
43    /// Hex encoding/decoding error
44    #[error("Hex error: {0}")]
45    HexError(String),
46
47    /// Invalid chain ID
48    #[error("Invalid chain ID: {0}")]
49    InvalidChainId(u64),
50
51    /// Device rejected the operation
52    #[error("Operation rejected by device")]
53    UserRejected,
54
55    /// Application configuration error
56    #[error("App configuration error: {0}")]
57    ConfigurationError(String),
58
59    /// Feature not supported by current app version
60    #[error("Feature not supported: {0}")]
61    FeatureNotSupported(String),
62
63    /// Data chunk error during multi-chunk operations
64    #[error("Chunk error: {0}")]
65    ChunkError(String),
66
67    /// Invalid response data from device
68    #[error("Invalid response data: {0}")]
69    InvalidResponseData(String),
70
71    /// Invalid EIP-712 data
72    #[error("Invalid EIP-712 data: {0}")]
73    InvalidEip712Data(String),
74
75    /// EIP-712 struct definition error
76    #[error("EIP-712 struct error: {0}")]
77    Eip712StructError(String),
78
79    /// EIP-712 filtering error
80    #[error("EIP-712 filter error: {0}")]
81    Eip712FilterError(String),
82
83    /// Unsupported app version
84    #[error("Unsupported version: {0}")]
85    UnsupportedVersion(String),
86
87    /// Device returned a specific status word
88    #[error("Device status 0x{sw:04X}: {description}")]
89    DeviceStatus { sw: u16, description: String },
90}
91
92impl<E: std::error::Error> EthAppError<E> {
93    /// Check if error is due to user rejection
94    pub fn is_user_rejected(&self) -> bool {
95        matches!(self, EthAppError::UserRejected)
96    }
97
98    /// Check if error is due to transport/communication issues
99    pub fn is_transport_error(&self) -> bool {
100        matches!(self, EthAppError::Transport(_))
101    }
102
103    /// Check if error is due to invalid input parameters
104    pub fn is_invalid_input(&self) -> bool {
105        matches!(
106            self,
107            EthAppError::InvalidBip32Path(_)
108                | EthAppError::InvalidAddress(_)
109                | EthAppError::InvalidSignature(_)
110                | EthAppError::InvalidTransaction(_)
111                | EthAppError::InvalidMessage(_)
112                | EthAppError::InvalidChainId(_)
113        )
114    }
115}
116
117/// Result type alias for Ethereum application operations
118pub type EthAppResult<T, E> = Result<T, EthAppError<E>>;
119
120/// Map LedgerAppError to Ethereum app specific error with SW decoding when possible
121pub fn map_ledger_error<E: std::error::Error>(err: LedgerAppError<E>) -> EthAppError<E> {
122    match err {
123        // User cancel / security status not satisfied
124        LedgerAppError::AppSpecific(0x6982, _) => EthAppError::UserRejected,
125        LedgerAppError::Unknown(0x6982) => EthAppError::UserRejected,
126
127        // Map known ETH app status words to descriptions
128        LedgerAppError::AppSpecific(sw, _) | LedgerAppError::Unknown(sw) => {
129            EthAppError::DeviceStatus {
130                sw,
131                description: describe_eth_status(sw).to_string(),
132            }
133        }
134
135        // Fallback: treat as transport-layer app error
136        other => EthAppError::Transport(other),
137    }
138}
139
140/// ETH app specific status word descriptions (subset per spec)
141fn describe_eth_status(sw: u16) -> &'static str {
142    match sw {
143        0x6001 => "Mode check fail",
144        0x6501 => "TransactionType not supported",
145        0x6502 => "Output buffer too small for chainId conversion",
146        0x6982 => "Security status not satisfied (Canceled by user)",
147        0x6983 => "Wrong Data length",
148        0x6984 => "Plugin not installed",
149        0x6985 => "Condition not satisfied",
150        0x6A00 => "Error without info",
151        0x6A80 => "Invalid data",
152        0x6A84 => "Insufficient memory",
153        0x6A88 => "Data not found",
154        0x6B00 => "Incorrect parameter P1 or P2",
155        0x6D00 => "Incorrect parameter INS",
156        0x6E00 => "Incorrect parameter CLA",
157        0x9000 => "Normal ending of the command",
158        0x911C => "Command code not supported (Ledger-PKI not yet available)",
159        _ if (sw & 0xFF00) == 0x6800 => "Internal error (Please report)",
160        _ if (sw & 0xFF00) == 0x6F00 => "Technical problem (Internal error, please report)",
161        _ => "Unknown status",
162    }
163}