hotshot_contract_adapter/
evm.rs1use alloy::{
2 sol_types::SolInterface,
3 transports::{RpcError, TransportErrorKind},
4};
5
6pub trait DecodeRevert<T> {
7 fn maybe_decode_revert<E: SolInterface + std::fmt::Debug>(self) -> anyhow::Result<T>;
8}
9
10impl<T> DecodeRevert<T> for Result<T, alloy::contract::Error> {
11 fn maybe_decode_revert<E: SolInterface + std::fmt::Debug>(self) -> anyhow::Result<T> {
12 match self {
13 Ok(ret) => Ok(ret),
14 Err(err) => {
15 let msg = match err.as_decoded_interface_error::<E>() {
16 Some(e) => format!("{e:?}"),
17 None => format!("{err:?}"),
18 };
19 Err(anyhow::anyhow!(msg))
20 },
21 }
22 }
23}
24
25impl<T> DecodeRevert<T> for Result<T, RpcError<TransportErrorKind>> {
26 fn maybe_decode_revert<E: SolInterface + std::fmt::Debug>(self) -> anyhow::Result<T> {
27 match self {
28 Ok(ret) => Ok(ret),
29 Err(RpcError::ErrorResp(payload)) => match payload.as_decoded_interface_error::<E>() {
30 Some(e) => Err(anyhow::anyhow!("{e:?}")),
31 None => Err(anyhow::anyhow!("{payload}")),
32 },
33 Err(err) => Err(anyhow::anyhow!("{err:?}")),
34 }
35 }
36}
37
38#[cfg(test)]
39mod test {
40 use alloy::{
41 primitives::{Address, U256},
42 providers::{Provider, ProviderBuilder},
43 rpc::types::{TransactionInput, TransactionRequest},
44 sol_types::SolCall,
45 };
46
47 use super::*;
48 use crate::sol_types::EspToken::{self, transferCall, EspTokenErrors};
49
50 #[tokio::test]
51 async fn test_decode_revert_contract_error() -> anyhow::Result<()> {
52 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
53
54 let token = EspToken::deploy(&provider).await?;
55 let err = token
56 .transfer(Address::random(), U256::MAX)
57 .send()
58 .await
59 .maybe_decode_revert::<EspTokenErrors>()
60 .unwrap_err();
61 assert!(err.to_string().contains("ERC20InsufficientBalance"));
62
63 Ok(())
64 }
65
66 #[tokio::test]
67 async fn test_decode_revert_rpc_error() -> anyhow::Result<()> {
68 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
69
70 let token = EspToken::deploy(&provider).await?;
71 let call = transferCall {
72 to: Address::random(),
73 value: U256::MAX,
74 };
75 let tx = TransactionRequest::default()
76 .to(*token.address())
77 .input(TransactionInput::new(call.abi_encode().into()));
78
79 let err = provider
80 .send_transaction(tx)
81 .await
82 .maybe_decode_revert::<EspTokenErrors>()
83 .unwrap_err();
84 assert!(err.to_string().contains("ERC20InsufficientBalance"));
85
86 Ok(())
87 }
88}