ethcontract/
errors.rs

1//! Module with common error types.
2
3mod ganache;
4mod geth;
5mod hardhat;
6mod nethermind;
7mod parity;
8pub(crate) mod revert;
9
10use crate::transaction::TransactionResult;
11use ethcontract_common::abi::{Error as AbiError, Event, Function};
12use ethcontract_common::abiext::EventExt;
13pub use ethcontract_common::errors::*;
14use secp256k1::Error as Secp256k1Error;
15use std::num::ParseIntError;
16use thiserror::Error;
17use uint::FromDecStrErr;
18use web3::error::Error as Web3Error;
19use web3::types::{Log, TransactionReceipt, H256};
20
21/// Error that can occur while locating a deployed contract.
22#[derive(Debug, Error)]
23pub enum DeployError {
24    /// An error occured while performing a web3 call.
25    #[error("web3 error: {0}")]
26    Web3(#[from] Web3Error),
27
28    /// No previously deployed contract could be found on the network being used
29    /// by the current `web3` provider.
30    #[error("could not find deployed contract for network {0}")]
31    NotFound(String),
32
33    /// Error linking a contract with a deployed library.
34    #[error("could not link library {0}")]
35    Link(#[from] LinkError),
36
37    /// Attempted to deploy a contract when empty bytecode. This can happen when
38    /// attempting to deploy a contract that is actually an interface.
39    #[error("can not deploy contract with empty bytecode")]
40    EmptyBytecode,
41
42    /// An error occured encoding deployment parameters with the contract ABI.
43    #[error("error ABI ecoding deployment parameters: {0}")]
44    Abi(#[from] AbiError),
45
46    /// Error executing contract deployment transaction.
47    #[error("error executing contract deployment transaction: {0}")]
48    Tx(#[from] ExecutionError),
49
50    /// Transaction was unable to confirm and is still pending. The contract
51    /// address cannot be determined.
52    #[error("contract deployment transaction pending: {0}")]
53    Pending(H256),
54}
55
56/// Error that can occur while executing a contract call or transaction.
57#[derive(Debug, Error)]
58pub enum ExecutionError {
59    /// An error occured while performing a web3 call.
60    #[error("web3 error: {0}")]
61    Web3(Web3Error),
62
63    /// An error occured while ABI decoding the result of a contract method
64    /// call.
65    #[error("abi decode error: {0}")]
66    AbiDecode(#[from] AbiError),
67
68    /// An error occured while parsing chain ID received from a Web3 call.
69    #[error("parse chain ID error: {0}")]
70    Parse(#[from] ParseIntError),
71
72    /// An error indicating that an attempt was made to build or send a locally
73    /// signed transaction to a node without any local accounts.
74    #[error("no local accounts")]
75    NoLocalAccounts,
76
77    /// A contract call reverted.
78    #[error("contract call reverted with message: {0:?}")]
79    Revert(Option<String>),
80
81    /// A contract call executed an invalid opcode.
82    #[error("contract call executed an invalid opcode")]
83    InvalidOpcode,
84
85    /// A contract transaction failed to confirm within the block timeout limit.
86    #[error("transaction confirmation timed-out")]
87    ConfirmTimeout(Box<TransactionResult>),
88
89    /// Transaction failure (e.g. out of gas or revert).
90    #[error("transaction failed: {:?}", .0.transaction_hash)]
91    Failure(Box<TransactionReceipt>),
92
93    /// Failed to find a transaction by hash.
94    #[error("missing transaction {0:?}")]
95    MissingTransaction(H256),
96
97    /// Failed to get a block for a pending transaction that has not yet been
98    /// mined.
99    #[error("pending transaction {0:?}, not yet part of a block")]
100    PendingTransaction(H256),
101
102    /// A removed log was received when querying past logs.
103    #[error("unexepected removed log when querying past logs")]
104    RemovedLog(Box<Log>),
105
106    /// A stream ended unexpectedly.
107    #[error("log stream ended unexpectedly")]
108    StreamEndedUnexpectedly,
109
110    /// A tokenization related error.
111    #[error("tokenization error: {0}")]
112    Tokenization(#[from] crate::tokens::Error),
113
114    /// Unexpected transaction hash
115    #[error("transaction hash returned from node when sending raw transaction does not match expected hash")]
116    UnexpectedTransactionHash,
117}
118
119impl From<Web3Error> for ExecutionError {
120    fn from(err: Web3Error) -> Self {
121        if let Web3Error::Rpc(jsonrpc_err) = &err {
122            if let Some(err) = ganache::get_encoded_error(jsonrpc_err) {
123                return err;
124            }
125            if let Some(err) = parity::get_encoded_error(jsonrpc_err) {
126                return err;
127            }
128            if let Some(err) = geth::get_encoded_error(jsonrpc_err) {
129                return err;
130            }
131            if let Some(err) = nethermind::get_encoded_error(jsonrpc_err) {
132                return err;
133            }
134            if let Some(err) = hardhat::get_encoded_error(jsonrpc_err) {
135                return err;
136            }
137        }
138
139        ExecutionError::Web3(err)
140    }
141}
142
143/// Error that can occur while executing a contract call or transaction.
144#[derive(Debug, Error)]
145#[error("method '{signature}' failure: {inner}")]
146pub struct MethodError {
147    /// The signature of the failed method.
148    pub signature: String,
149
150    /// The inner execution error that for the method transaction that failed.
151    #[source]
152    pub inner: ExecutionError,
153}
154
155impl MethodError {
156    /// Create a new `MethodError` from an ABI function specification and an
157    /// inner `ExecutionError`.
158    pub fn new<I: Into<ExecutionError>>(function: &Function, inner: I) -> Self {
159        MethodError::from_parts(function.signature(), inner.into())
160    }
161
162    /// Create a `MethodError` from its signature and inner `ExecutionError`.
163    pub fn from_parts(signature: String, inner: ExecutionError) -> Self {
164        MethodError { signature, inner }
165    }
166}
167
168/// Error that can occur while streaming contract events.
169#[derive(Debug, Error)]
170#[error("event '{signature}' failure: {inner}")]
171pub struct EventError {
172    /// The signature of the failed event.
173    pub signature: String,
174
175    /// The inner execution error that for the method transaction that failed.
176    #[source]
177    pub inner: ExecutionError,
178}
179
180impl EventError {
181    /// Create a new `EventError` from an ABI function specification and an
182    /// inner `ExecutionError`.
183    pub fn new<I: Into<ExecutionError>>(event: &Event, inner: I) -> Self {
184        EventError::from_parts(event.abi_signature(), inner.into())
185    }
186
187    /// Create a `EventError` from its signature and inner `ExecutionError`.
188    pub fn from_parts(signature: String, inner: ExecutionError) -> Self {
189        EventError { signature, inner }
190    }
191}
192
193/// An error indicating an invalid private key. Private keys for secp256k1 must
194/// be exactly 32 bytes and fall within the range `[1, n)` where `n` is the
195/// order of the generator point of the curve.
196#[derive(Debug, Error)]
197#[error("invalid private key")]
198pub struct InvalidPrivateKey;
199
200impl From<Secp256k1Error> for InvalidPrivateKey {
201    fn from(err: Secp256k1Error) -> Self {
202        match err {
203            Secp256k1Error::InvalidSecretKey => {}
204            _ => {
205                // NOTE: Assert that we never try to make this conversion with
206                //   errors not related to `SecretKey`.
207                debug_assert!(false, "invalid conversion to InvalidPrivateKey error");
208            }
209        }
210        InvalidPrivateKey
211    }
212}
213
214/// The error type that is returned when conversion to or from a 256-bit integer
215/// fails.
216#[derive(Clone, Copy, Debug, Error)]
217#[error("output of range integer conversion attempted")]
218pub struct TryFromBigIntError;
219
220/// The error type that is returned when parsing a 256-bit signed integer.
221#[derive(Clone, Copy, Debug, Error)]
222pub enum ParseI256Error {
223    /// Error that occurs when an invalid digit is encountered while parsing.
224    #[error("invalid digit found in string")]
225    InvalidDigit,
226
227    /// Error that occurs when the number is too large or too small (negative)
228    /// and does not fit in a 256-bit signed integer.
229    #[error("number does not fit in 256-bit integer")]
230    IntegerOverflow,
231}
232
233impl From<FromDecStrErr> for ParseI256Error {
234    fn from(err: FromDecStrErr) -> Self {
235        match err {
236            FromDecStrErr::InvalidCharacter => ParseI256Error::InvalidDigit,
237            FromDecStrErr::InvalidLength => ParseI256Error::IntegerOverflow,
238        }
239    }
240}
241
242#[cfg(test)]
243mod tests {
244    use super::*;
245    use std::error::Error;
246
247    #[test]
248    fn from_ganache_encoded_error() {
249        let web3_err = Web3Error::Rpc(ganache::rpc_error("invalid opcode", None));
250        let err = ExecutionError::from(web3_err);
251
252        assert!(
253            matches!(err, ExecutionError::InvalidOpcode),
254            "bad error conversion {:?}",
255            err
256        );
257    }
258
259    #[test]
260    fn from_parity_encoded_error() {
261        let web3_err = Web3Error::Rpc(parity::rpc_error("Bad instruction fd"));
262        let err = ExecutionError::from(web3_err);
263
264        assert!(
265            matches!(err, ExecutionError::InvalidOpcode),
266            "bad error conversion {:?}",
267            err
268        );
269    }
270
271    #[test]
272    fn all_errors_are_boxable_errors() {
273        fn assert_boxable_error<T: Error + Send + Sync + 'static>() {}
274
275        assert_boxable_error::<DeployError>();
276        assert_boxable_error::<ExecutionError>();
277        assert_boxable_error::<MethodError>();
278        assert_boxable_error::<InvalidPrivateKey>();
279    }
280}