1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use ethers_core::{
abi::{AbiDecode, AbiEncode, Tokenizable},
types::Selector,
utils::id,
};
use ethers_providers::JsonRpcError;
use std::borrow::Cow;
pub trait ContractRevert: AbiDecode + AbiEncode + Send + Sync {
fn decode_with_selector(data: &[u8]) -> Option<Self> {
if data.len() < 4 {
return None
}
let selector = data[..4].try_into().expect("checked by len");
if !Self::valid_selector(selector) {
return None
}
<Self as AbiDecode>::decode(&data[4..]).ok()
}
fn valid_selector(selector: Selector) -> bool;
}
pub trait EthError: Tokenizable + AbiDecode + AbiEncode + Send + Sync {
fn from_rpc_response(response: &JsonRpcError) -> Option<Self> {
Self::decode_with_selector(&response.as_revert_data()?)
}
fn decode_with_selector(data: &[u8]) -> Option<Self> {
<Self as AbiDecode>::decode(data.strip_prefix(&Self::selector())?).ok()
}
fn error_name() -> Cow<'static, str>;
fn abi_signature() -> Cow<'static, str>;
fn selector() -> Selector {
id(Self::abi_signature())
}
}
impl EthError for String {
fn error_name() -> Cow<'static, str> {
Cow::Borrowed("Error")
}
fn abi_signature() -> Cow<'static, str> {
Cow::Borrowed("Error(string)")
}
fn selector() -> Selector {
[0x08, 0xc3, 0x79, 0xa0]
}
}
#[cfg(test)]
mod test {
use ethers_core::types::Bytes;
use super::EthError;
#[test]
fn string_error() {
let multicall_revert_string: Bytes = "0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000174d756c746963616c6c333a2063616c6c206661696c6564000000000000000000".parse().unwrap();
assert_eq!(String::selector().as_slice(), &multicall_revert_string[0..4]);
assert_eq!(
String::decode_with_selector(&multicall_revert_string).unwrap().as_str(),
"Multicall3: call failed"
);
}
}