use alloy_primitives::{B256, Selector};
use alloy_sol_types::{SolError, SolInterface};
use thiserror::Error;
use tronz_primitives::Bytes;
use tronz_provider::Error as ProviderError;
pub type Result<T, E = ContractError> = std::result::Result<T, E>;
#[derive(Debug, Error)]
pub enum ContractError {
#[error(transparent)]
Provider(#[from] ProviderError),
#[error("ABI error: {0}")]
Abi(#[from] alloy_dyn_abi::Error),
#[error("unknown function: `{0}`")]
UnknownFunction(String),
#[error("unknown function selector: {0}")]
UnknownSelector(Selector),
#[error("no signer attached")]
NoSigner,
#[error("contract call to `{0}` returned no data; the address might not be a contract")]
ZeroData(String, #[source] alloy_dyn_abi::Error),
#[error("contract call reverted")]
ContractRevert(Bytes),
#[error("unknown event topic: {0}")]
UnknownEvent(B256),
}
impl From<alloy_sol_types::Error> for ContractError {
fn from(e: alloy_sol_types::Error) -> Self {
Self::Abi(e.into())
}
}
impl ContractError {
pub fn as_revert_data(&self) -> Option<&Bytes> {
if let Self::ContractRevert(data) = self {
Some(data)
} else {
None
}
}
pub fn as_decoded_error<E: SolError>(&self) -> Option<E> {
self.as_revert_data()
.and_then(|data| E::abi_decode(data).ok())
}
pub fn as_decoded_interface_error<I: SolInterface>(&self) -> Option<I> {
self.as_revert_data()
.and_then(|data| I::abi_decode(data).ok())
}
pub(crate) fn decode_err(name: &str, data: &[u8], error: alloy_dyn_abi::Error) -> Self {
if data.is_empty() {
let short = name.split('(').next().unwrap_or(name);
return Self::ZeroData(short.to_string(), error);
}
Self::Abi(error)
}
}