1mod 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#[derive(Debug, Error)]
23pub enum DeployError {
24 #[error("web3 error: {0}")]
26 Web3(#[from] Web3Error),
27
28 #[error("could not find deployed contract for network {0}")]
31 NotFound(String),
32
33 #[error("could not link library {0}")]
35 Link(#[from] LinkError),
36
37 #[error("can not deploy contract with empty bytecode")]
40 EmptyBytecode,
41
42 #[error("error ABI ecoding deployment parameters: {0}")]
44 Abi(#[from] AbiError),
45
46 #[error("error executing contract deployment transaction: {0}")]
48 Tx(#[from] ExecutionError),
49
50 #[error("contract deployment transaction pending: {0}")]
53 Pending(H256),
54}
55
56#[derive(Debug, Error)]
58pub enum ExecutionError {
59 #[error("web3 error: {0}")]
61 Web3(Web3Error),
62
63 #[error("abi decode error: {0}")]
66 AbiDecode(#[from] AbiError),
67
68 #[error("parse chain ID error: {0}")]
70 Parse(#[from] ParseIntError),
71
72 #[error("no local accounts")]
75 NoLocalAccounts,
76
77 #[error("contract call reverted with message: {0:?}")]
79 Revert(Option<String>),
80
81 #[error("contract call executed an invalid opcode")]
83 InvalidOpcode,
84
85 #[error("transaction confirmation timed-out")]
87 ConfirmTimeout(Box<TransactionResult>),
88
89 #[error("transaction failed: {:?}", .0.transaction_hash)]
91 Failure(Box<TransactionReceipt>),
92
93 #[error("missing transaction {0:?}")]
95 MissingTransaction(H256),
96
97 #[error("pending transaction {0:?}, not yet part of a block")]
100 PendingTransaction(H256),
101
102 #[error("unexepected removed log when querying past logs")]
104 RemovedLog(Box<Log>),
105
106 #[error("log stream ended unexpectedly")]
108 StreamEndedUnexpectedly,
109
110 #[error("tokenization error: {0}")]
112 Tokenization(#[from] crate::tokens::Error),
113
114 #[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#[derive(Debug, Error)]
145#[error("method '{signature}' failure: {inner}")]
146pub struct MethodError {
147 pub signature: String,
149
150 #[source]
152 pub inner: ExecutionError,
153}
154
155impl MethodError {
156 pub fn new<I: Into<ExecutionError>>(function: &Function, inner: I) -> Self {
159 MethodError::from_parts(function.signature(), inner.into())
160 }
161
162 pub fn from_parts(signature: String, inner: ExecutionError) -> Self {
164 MethodError { signature, inner }
165 }
166}
167
168#[derive(Debug, Error)]
170#[error("event '{signature}' failure: {inner}")]
171pub struct EventError {
172 pub signature: String,
174
175 #[source]
177 pub inner: ExecutionError,
178}
179
180impl EventError {
181 pub fn new<I: Into<ExecutionError>>(event: &Event, inner: I) -> Self {
184 EventError::from_parts(event.abi_signature(), inner.into())
185 }
186
187 pub fn from_parts(signature: String, inner: ExecutionError) -> Self {
189 EventError { signature, inner }
190 }
191}
192
193#[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 debug_assert!(false, "invalid conversion to InvalidPrivateKey error");
208 }
209 }
210 InvalidPrivateKey
211 }
212}
213
214#[derive(Clone, Copy, Debug, Error)]
217#[error("output of range integer conversion attempted")]
218pub struct TryFromBigIntError;
219
220#[derive(Clone, Copy, Debug, Error)]
222pub enum ParseI256Error {
223 #[error("invalid digit found in string")]
225 InvalidDigit,
226
227 #[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}