Skip to main content

evmlib/contract/payment_vault/
error.rs

1use crate::contract::payment_vault::interface::IPaymentVault;
2use crate::retry;
3
4#[derive(thiserror::Error, Debug)]
5pub enum Error {
6    #[error("Contract error: {0}")]
7    Contract(#[from] alloy::contract::Error),
8    #[error("RPC error: {0}")]
9    Rpc(String),
10    #[error(transparent)]
11    Transaction(#[from] retry::TransactionError),
12
13    // Smart contract custom errors
14    #[error("ANT token address is null")]
15    AntTokenNull,
16    #[error("Batch limit exceeded")]
17    BatchLimitExceeded,
18    #[error("Merkle tree depth {depth} exceeds maximum allowed depth {max_depth}")]
19    DepthTooLarge { depth: u8, max_depth: u8 },
20    #[error("Invalid input length")]
21    InvalidInputLength,
22    #[error("Payment already exists for pool hash: {0}")]
23    PaymentAlreadyExists(String),
24    #[error("Payment not found for pool hash: {0}")]
25    PaymentNotFound(String),
26    #[error("Wrong pool count: expected {expected}, got {actual}")]
27    WrongPoolCount { expected: u64, actual: u64 },
28}
29
30impl Error {
31    /// Try to decode a contract error from revert data
32    pub(crate) fn try_decode_revert(data: &[u8]) -> Option<Self> {
33        use alloy::sol_types::SolInterface;
34
35        // The revert data should start with the 4-byte selector followed by the error data
36        if data.len() < 4 {
37            return None;
38        }
39
40        let selector: [u8; 4] = data[..4].try_into().ok()?;
41        let error_data = &data[4..];
42
43        // Try to decode as IPaymentVaultErrors
44        if let Ok(contract_error) =
45            IPaymentVault::IPaymentVaultErrors::abi_decode_raw(selector, error_data)
46        {
47            return Some(Self::from_contract_error(contract_error));
48        }
49
50        None
51    }
52
53    /// Convert a decoded contract error to our Error type
54    fn from_contract_error(error: IPaymentVault::IPaymentVaultErrors) -> Self {
55        use IPaymentVault::IPaymentVaultErrors;
56
57        match error {
58            IPaymentVaultErrors::AntTokenNull(_) => Self::AntTokenNull,
59            IPaymentVaultErrors::BatchLimitExceeded(_) => Self::BatchLimitExceeded,
60            IPaymentVaultErrors::DepthTooLarge(e) => Self::DepthTooLarge {
61                depth: e.depth,
62                max_depth: e.maxDepth,
63            },
64            IPaymentVaultErrors::InvalidInputLength(_) => Self::InvalidInputLength,
65            IPaymentVaultErrors::PaymentAlreadyExists(e) => {
66                Self::PaymentAlreadyExists(hex::encode(e.winnerPoolHash))
67            }
68            IPaymentVaultErrors::WrongPoolCount(e) => Self::WrongPoolCount {
69                expected: e.expected.try_into().unwrap_or(u64::MAX),
70                actual: e.actual.try_into().unwrap_or(u64::MAX),
71            },
72            IPaymentVaultErrors::SafeERC20FailedOperation(e) => {
73                Self::Rpc(format!("SafeERC20 transfer failed for token: {}", e.token))
74            }
75        }
76    }
77}