use crate::{
Result, SolType, Word,
abi::token::{Token, TokenSeq, WordToken},
};
use alloc::vec::Vec;
use alloy_primitives::{B256, FixedBytes, Log, LogData};
mod topic;
pub use topic::EventTopic;
mod topic_list;
pub use topic_list::TopicList;
pub trait SolEvent: Sized {
type DataTuple<'a>: SolType<Token<'a> = Self::DataToken<'a>>;
type DataToken<'a>: TokenSeq<'a>;
type TopicList: TopicList;
const SIGNATURE: &'static str;
#[doc(alias = "SELECTOR")]
const SIGNATURE_HASH: FixedBytes<32>;
const ANONYMOUS: bool;
fn new(
topics: <Self::TopicList as SolType>::RustType,
data: <Self::DataTuple<'_> as SolType>::RustType,
) -> Self;
#[inline]
fn new_checked(
topics: <Self::TopicList as SolType>::RustType,
data: <Self::DataTuple<'_> as SolType>::RustType,
) -> Result<Self> {
Self::check_signature(&topics).map(|()| Self::new(topics, data))
}
#[inline]
fn check_signature(topics: &<Self::TopicList as SolType>::RustType) -> Result<()> {
let _ = topics;
Ok(())
}
fn tokenize_body(&self) -> Self::DataToken<'_>;
fn topics(&self) -> <Self::TopicList as SolType>::RustType;
#[inline]
fn abi_encoded_size(&self) -> usize {
if let Some(size) = <Self::DataTuple<'_> as SolType>::ENCODED_SIZE {
return size;
}
self.tokenize_body().total_words() * Word::len_bytes()
}
#[inline]
fn encode_data_to(&self, out: &mut Vec<u8>) {
out.reserve(self.abi_encoded_size());
out.extend(crate::abi::encode_sequence(&self.tokenize_body()));
}
#[inline]
fn encode_data(&self) -> Vec<u8> {
let mut out = Vec::new();
self.encode_data_to(&mut out);
out
}
fn encode_topics_raw(&self, out: &mut [WordToken]) -> Result<()>;
#[inline]
fn encode_topics(&self) -> Vec<WordToken> {
let mut out = vec![WordToken(B256::ZERO); Self::TopicList::COUNT];
self.encode_topics_raw(&mut out).unwrap();
out
}
#[inline]
fn encode_topics_array<const LEN: usize>(&self) -> [WordToken; LEN] {
const { assert!(LEN == Self::TopicList::COUNT, "topic list length mismatch") };
let mut out = [WordToken(B256::ZERO); LEN];
self.encode_topics_raw(&mut out).unwrap();
out
}
fn encode_log_data(&self) -> LogData {
LogData::new_unchecked(
self.encode_topics().into_iter().map(Into::into).collect(),
self.encode_data().into(),
)
}
fn encode_log(log: &Log<Self>) -> Log<LogData> {
Log { address: log.address, data: log.data.encode_log_data() }
}
#[inline]
fn decode_topics<I, D>(topics: I) -> Result<<Self::TopicList as SolType>::RustType>
where
I: IntoIterator<Item = D>,
D: Into<WordToken>,
{
<Self::TopicList as TopicList>::detokenize(topics)
}
#[inline]
fn abi_decode_data<'a>(data: &'a [u8]) -> Result<<Self::DataTuple<'a> as SolType>::RustType> {
<Self::DataTuple<'a> as SolType>::abi_decode_sequence(data)
}
#[inline]
fn abi_decode_data_validate<'a>(
data: &'a [u8],
) -> Result<<Self::DataTuple<'a> as SolType>::RustType> {
<Self::DataTuple<'a> as SolType>::abi_decode_sequence_validate(data)
}
fn decode_raw_log<I, D>(topics: I, data: &[u8]) -> Result<Self>
where
I: IntoIterator<Item = D>,
D: Into<WordToken>,
{
let topics = Self::decode_topics(topics)?;
Self::check_signature(&topics)?;
let body = Self::abi_decode_data(data)?;
Ok(Self::new(topics, body))
}
fn decode_raw_log_validate<I, D>(topics: I, data: &[u8]) -> Result<Self>
where
I: IntoIterator<Item = D>,
D: Into<WordToken>,
{
let topics = Self::decode_topics(topics)?;
Self::check_signature(&topics)?;
let body = Self::abi_decode_data_validate(data)?;
Ok(Self::new(topics, body))
}
fn decode_log_data(log: &LogData) -> Result<Self> {
Self::decode_raw_log(log.topics(), &log.data)
}
fn decode_log_data_validate(log: &LogData) -> Result<Self> {
Self::decode_raw_log_validate(log.topics(), &log.data)
}
fn decode_log(log: &Log) -> Result<Log<Self>> {
Self::decode_log_data(&log.data).map(|data| Log { address: log.address, data })
}
fn decode_log_validate(log: &Log) -> Result<Log<Self>> {
Self::decode_log_data_validate(&log.data).map(|data| Log { address: log.address, data })
}
}