use std::collections::HashMap;
use alloy_dyn_abi::{DecodedEvent, DynSolValue, EventExt as _, FunctionExt as _, JsonAbiExt as _};
use alloy_json_abi::{Event, Function, JsonAbi};
use alloy_primitives::{B256, Selector};
use tronz_primitives::{Address, Bytes};
use tronz_provider::types::Log;
use crate::{
error::{ContractError, Result},
instance::ContractInstance,
};
#[derive(Clone, Debug, Default)]
pub struct Interface {
abi: JsonAbi,
functions: HashMap<Selector, (String, usize)>,
events: HashMap<B256, (String, usize)>,
}
impl Interface {
pub fn new(abi: JsonAbi) -> Self {
let functions = build_selector_map(&abi);
let events = build_event_map(&abi);
Self { abi, functions, events }
}
pub fn empty() -> Self {
Self::default()
}
pub fn abi(&self) -> &JsonAbi {
&self.abi
}
pub fn into_abi(self) -> JsonAbi {
self.abi
}
pub fn encode_input(&self, fn_name: &str, args: &[DynSolValue]) -> Result<Bytes> {
Ok(self.get_by_name(fn_name)?.abi_encode_input(args)?.into())
}
pub fn encode_input_with_selector(
&self,
selector: &Selector,
args: &[DynSolValue],
) -> Result<Bytes> {
Ok(self.get_by_selector(selector)?.abi_encode_input(args)?.into())
}
pub fn decode_input(&self, fn_name: &str, data: &[u8]) -> Result<Vec<DynSolValue>> {
Ok(self.get_by_name(fn_name)?.abi_decode_input(data)?)
}
pub fn decode_input_with_selector(
&self,
selector: &Selector,
data: &[u8],
) -> Result<Vec<DynSolValue>> {
Ok(self.get_by_selector(selector)?.abi_decode_input(data)?)
}
pub fn decode_output(&self, fn_name: &str, data: &[u8]) -> Result<Vec<DynSolValue>> {
self.get_by_name(fn_name)?
.abi_decode_output(data)
.map_err(|e| ContractError::decode_err(fn_name, data, e))
}
pub fn decode_output_with_selector(
&self,
selector: &Selector,
data: &[u8],
) -> Result<Vec<DynSolValue>> {
let f = self.get_by_selector(selector)?;
let name = f.name.as_str();
f.abi_decode_output(data).map_err(|e| ContractError::decode_err(name, data, e))
}
pub fn decode_log(&self, log: &Log) -> Result<(String, DecodedEvent)> {
let topic0 = log.topics.first().copied().unwrap_or_default();
let event = self.get_event_by_topic(&topic0)?;
let name = event.name.clone();
let decoded = event.decode_log_parts(log.topics.iter().copied(), &log.data)?;
Ok((name, decoded))
}
pub fn decode_logs<'a>(
&'a self,
logs: &'a [Log],
) -> impl Iterator<Item = Result<(String, DecodedEvent)>> + 'a {
logs.iter().filter_map(move |log| {
let topic0 = log.topics.first().copied()?;
if !self.events.contains_key(&topic0) {
return None;
}
Some(self.decode_log(log))
})
}
pub fn connect<P>(self, address: Address, provider: P) -> ContractInstance<P>
where
P: tronz_provider::TronProvider,
{
ContractInstance::new(address, provider, self)
}
pub(crate) fn get_by_name(&self, name: &str) -> Result<&Function> {
self.abi
.function(name)
.and_then(|fs| fs.first())
.ok_or_else(|| ContractError::UnknownFunction(name.to_owned()))
}
pub(crate) fn get_by_selector(&self, selector: &Selector) -> Result<&Function> {
self.functions
.get(selector)
.and_then(|(name, idx)| self.abi.functions.get(name)?.get(*idx))
.ok_or_else(|| ContractError::UnknownSelector(*selector))
}
fn get_event_by_topic(&self, topic0: &B256) -> Result<&Event> {
self.events
.get(topic0)
.and_then(|(name, idx)| self.abi.events.get(name)?.get(*idx))
.ok_or_else(|| ContractError::UnknownEvent(*topic0))
}
}
impl From<JsonAbi> for Interface {
fn from(abi: JsonAbi) -> Self {
Self::new(abi)
}
}
fn build_selector_map(abi: &JsonAbi) -> HashMap<Selector, (String, usize)> {
abi.functions
.iter()
.flat_map(|(name, overloads)| {
overloads.iter().enumerate().map(move |(idx, f)| (f.selector(), (name.clone(), idx)))
})
.collect()
}
fn build_event_map(abi: &JsonAbi) -> HashMap<B256, (String, usize)> {
abi.events
.iter()
.flat_map(|(name, overloads)| {
overloads.iter().enumerate().filter_map(move |(idx, e)| {
if e.anonymous {
return None;
}
Some((e.selector(), (name.clone(), idx)))
})
})
.collect()
}