feat(abigen): support empty events (#2263)

This commit is contained in:
Matthias Seitz 2023-03-16 00:12:12 +01:00 committed by GitHub
parent 18a049b4c4
commit 72be3376e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 82 additions and 45 deletions

View File

@ -1,6 +1,12 @@
//! Helper functions for deriving `EthEvent`
use crate::{abi_ty, utils};
use ethers_contract_abigen::Source;
use ethers_core::{
abi::{Event, EventExt, EventParam, HumanReadableParser},
macros::{ethers_contract_crate, ethers_core_crate},
};
use hex::FromHex;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{
@ -8,14 +14,6 @@ use syn::{
NestedMeta,
};
use ethers_core::{
abi::{Event, EventExt, EventParam, HumanReadableParser},
macros::{ethers_contract_crate, ethers_core_crate},
};
use hex::FromHex;
use crate::{abi_ty, utils};
/// Generates the `EthEvent` trait support
pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> Result<TokenStream, Error> {
let name = &input.ident;
@ -124,10 +122,7 @@ impl EventField {
}
}
fn derive_decode_from_log_impl(
input: &DeriveInput,
event: &Event,
) -> Result<proc_macro2::TokenStream, Error> {
fn derive_decode_from_log_impl(input: &DeriveInput, event: &Event) -> Result<TokenStream, Error> {
let ethers_core = ethers_core_crate();
let fields: Vec<_> = match input.data {
@ -159,10 +154,8 @@ fn derive_decode_from_log_impl(
fields.unnamed.iter().collect()
}
Fields::Unit => {
return Err(Error::new(
input.span(),
"EthEvent cannot be derived for empty structs and unit",
))
// Empty structs or unit, no fields
vec![]
}
},
Data::Enum(_) => {
@ -173,35 +166,6 @@ fn derive_decode_from_log_impl(
}
};
let mut event_fields = Vec::with_capacity(fields.len());
for (index, field) in fields.iter().enumerate() {
let mut param = event.inputs[index].clone();
let (topic_name, indexed) = parse_field_attributes(field)?;
if indexed {
param.indexed = true;
}
let topic_name =
param.indexed.then(|| topic_name.or_else(|| Some(param.name.clone()))).flatten();
event_fields.push(EventField { topic_name, index, param });
}
// convert fields to params list
let topic_types = event_fields
.iter()
.filter(|f| f.is_indexed())
.map(|f| utils::topic_param_type_quote(&f.param.kind));
let topic_types_init = quote! {let topic_types = ::std::vec![#( #topic_types ),*];};
let data_types = event_fields
.iter()
.filter(|f| !f.is_indexed())
.map(|f| utils::param_type_quote(&f.param.kind));
let data_types_init = quote! {let data_types = [#( #data_types ),*];};
// decode
let (signature_check, flat_topics_init, topic_tokens_len_check) = if event.anonymous {
(
@ -234,6 +198,51 @@ fn derive_decode_from_log_impl(
)
};
// Event with no fields, can skip decoding
if fields.is_empty() {
return Ok(quote! {
let #ethers_core::abi::RawLog {topics, data} = log;
#signature_check
if topics.len() != 1usize || !data.is_empty() {
return Err(::ethers_core::abi::Error::InvalidData);
}
#ethers_core::abi::Tokenizable::from_token(#ethers_core::abi::Token::Tuple(::std::vec::Vec::new())).map_err(|_|#ethers_core::abi::Error::InvalidData)
})
}
let mut event_fields = Vec::with_capacity(fields.len());
for (index, field) in fields.iter().enumerate() {
let mut param = event.inputs[index].clone();
let (topic_name, indexed) = parse_field_attributes(field)?;
if indexed {
param.indexed = true;
}
let topic_name =
param.indexed.then(|| topic_name.or_else(|| Some(param.name.clone()))).flatten();
event_fields.push(EventField { topic_name, index, param });
}
// convert fields to params list
let topic_types = event_fields
.iter()
.filter(|f| f.is_indexed())
.map(|f| utils::topic_param_type_quote(&f.param.kind));
let topic_types_init = quote! {let topic_types = ::std::vec![#( #topic_types ),*];};
let data_types = event_fields
.iter()
.filter(|f| !f.is_indexed())
.map(|f| utils::param_type_quote(&f.param.kind));
let data_types_init = quote! {let data_types = [#( #data_types ),*];};
// check if indexed are sorted
let tokens_init = if event_fields
.iter()

View File

@ -670,3 +670,31 @@ fn derives_abi_name() {
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef".parse().unwrap()
);
}
// <https://github.com/gakonst/ethers-rs/issues/2261>
#[test]
fn derive_empty_events() {
#[derive(Debug, EthEvent)]
#[ethevent(abi = "EmptyEvent()")]
struct EmptyEvent;
let log = RawLog { topics: vec![EmptyEvent::signature()], data: vec![] };
let _event = <EmptyEvent as EthLogDecode>::decode_log(&log).unwrap();
let log = RawLog { topics: vec![EmptyEvent::signature()], data: vec![0] };
assert!(<EmptyEvent as EthLogDecode>::decode_log(&log).is_err());
let log = RawLog { topics: vec![EmptyEvent::signature(), H256::random()], data: vec![0] };
assert!(<EmptyEvent as EthLogDecode>::decode_log(&log).is_err());
assert_eq!(EmptyEvent::abi_signature(), "EmptyEvent()");
abigen!(
DummyContract,
r#"[
event EmptyEvent2()
]"#,
);
assert_eq!(EmptyEvent2Filter::abi_signature(), "EmptyEvent2()");
}