alloy_primitives/log/
mod.rsuse crate::{Address, Bloom, Bytes, B256};
use alloc::vec::Vec;
#[cfg(feature = "serde")]
mod serde;
pub fn logs_bloom<'a>(logs: impl IntoIterator<Item = &'a Log>) -> Bloom {
let mut bloom = Bloom::ZERO;
for log in logs {
bloom.accrue_log(log);
}
bloom
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(derive_arbitrary::Arbitrary, proptest_derive::Arbitrary))]
pub struct LogData {
topics: Vec<B256>,
pub data: Bytes,
}
impl LogData {
#[inline]
pub const fn new_unchecked(topics: Vec<B256>, data: Bytes) -> Self {
Self { topics, data }
}
#[inline]
pub fn new(topics: Vec<B256>, data: Bytes) -> Option<Self> {
let this = Self::new_unchecked(topics, data);
this.is_valid().then_some(this)
}
#[inline]
pub const fn empty() -> Self {
Self { topics: Vec::new(), data: Bytes::new() }
}
#[inline]
pub fn is_valid(&self) -> bool {
self.topics.len() <= 4
}
#[inline]
pub fn topics(&self) -> &[B256] {
&self.topics
}
#[inline]
pub fn topics_mut(&mut self) -> &mut [B256] {
&mut self.topics
}
#[inline]
pub fn topics_mut_unchecked(&mut self) -> &mut Vec<B256> {
&mut self.topics
}
#[inline]
pub fn set_topics_unchecked(&mut self, topics: Vec<B256>) {
self.topics = topics;
}
#[inline]
pub fn set_topics_truncating(&mut self, mut topics: Vec<B256>) {
topics.truncate(4);
self.set_topics_unchecked(topics);
}
#[inline]
pub fn split(self) -> (Vec<B256>, Bytes) {
(self.topics, self.data)
}
}
pub trait IntoLogData {
fn to_log_data(&self) -> LogData;
fn into_log_data(self) -> LogData;
}
impl IntoLogData for LogData {
#[inline]
fn to_log_data(&self) -> LogData {
self.clone()
}
#[inline]
fn into_log_data(self) -> LogData {
self
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "arbitrary", derive(derive_arbitrary::Arbitrary, proptest_derive::Arbitrary))]
pub struct Log<T = LogData> {
pub address: Address,
pub data: T,
}
impl<T> core::ops::Deref for Log<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T> core::ops::DerefMut for Log<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
impl Log {
#[inline]
pub fn new(address: Address, topics: Vec<B256>, data: Bytes) -> Option<Self> {
LogData::new(topics, data).map(|data| Self { address, data })
}
#[inline]
pub const fn new_unchecked(address: Address, topics: Vec<B256>, data: Bytes) -> Self {
Self { address, data: LogData::new_unchecked(topics, data) }
}
#[inline]
pub const fn empty() -> Self {
Self { address: Address::ZERO, data: LogData::empty() }
}
}
impl<T> Log<T>
where
for<'a> &'a T: Into<LogData>,
{
#[inline]
pub const fn new_from_event_unchecked(address: Address, data: T) -> Self {
Self { address, data }
}
pub fn new_from_event(address: Address, data: T) -> Option<Self> {
let this = Self::new_from_event_unchecked(address, data);
(&this.data).into().is_valid().then_some(this)
}
#[inline]
pub fn reserialize(&self) -> Log<LogData> {
Log { address: self.address, data: (&self.data).into() }
}
}
#[cfg(feature = "rlp")]
impl alloy_rlp::Encodable for Log {
fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
let payload_length =
self.address.length() + self.data.data.length() + self.data.topics.length();
alloy_rlp::Header { list: true, payload_length }.encode(out);
self.address.encode(out);
self.data.topics.encode(out);
self.data.data.encode(out);
}
fn length(&self) -> usize {
let payload_length =
self.address.length() + self.data.data.length() + self.data.topics.length();
payload_length + alloy_rlp::length_of_length(payload_length)
}
}
#[cfg(feature = "rlp")]
impl<T> alloy_rlp::Encodable for Log<T>
where
for<'a> &'a T: Into<LogData>,
{
fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
self.reserialize().encode(out)
}
fn length(&self) -> usize {
self.reserialize().length()
}
}
#[cfg(feature = "rlp")]
impl alloy_rlp::Decodable for Log {
fn decode(buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
let h = alloy_rlp::Header::decode(buf)?;
let pre = buf.len();
let address = alloy_rlp::Decodable::decode(buf)?;
let topics = alloy_rlp::Decodable::decode(buf)?;
let data = alloy_rlp::Decodable::decode(buf)?;
if h.payload_length != pre - buf.len() {
return Err(alloy_rlp::Error::Custom("did not consume exact payload"));
}
Ok(Self { address, data: LogData { topics, data } })
}
}
#[cfg(feature = "rlp")]
#[cfg(test)]
mod tests {
use super::*;
use alloy_rlp::{Decodable, Encodable};
#[test]
fn test_roundtrip_rlp_log_data() {
let log = Log::<LogData>::default();
let mut buf = Vec::<u8>::new();
log.encode(&mut buf);
assert_eq!(Log::decode(&mut &buf[..]).unwrap(), log);
}
}