feat(abigen): support empty events (#2263)
This commit is contained in:
parent
18a049b4c4
commit
72be3376e2
|
@ -1,6 +1,12 @@
|
||||||
//! Helper functions for deriving `EthEvent`
|
//! Helper functions for deriving `EthEvent`
|
||||||
|
|
||||||
|
use crate::{abi_ty, utils};
|
||||||
use ethers_contract_abigen::Source;
|
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 proc_macro2::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{
|
use syn::{
|
||||||
|
@ -8,14 +14,6 @@ use syn::{
|
||||||
NestedMeta,
|
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
|
/// Generates the `EthEvent` trait support
|
||||||
pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> Result<TokenStream, Error> {
|
pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> Result<TokenStream, Error> {
|
||||||
let name = &input.ident;
|
let name = &input.ident;
|
||||||
|
@ -124,10 +122,7 @@ impl EventField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn derive_decode_from_log_impl(
|
fn derive_decode_from_log_impl(input: &DeriveInput, event: &Event) -> Result<TokenStream, Error> {
|
||||||
input: &DeriveInput,
|
|
||||||
event: &Event,
|
|
||||||
) -> Result<proc_macro2::TokenStream, Error> {
|
|
||||||
let ethers_core = ethers_core_crate();
|
let ethers_core = ethers_core_crate();
|
||||||
|
|
||||||
let fields: Vec<_> = match input.data {
|
let fields: Vec<_> = match input.data {
|
||||||
|
@ -159,10 +154,8 @@ fn derive_decode_from_log_impl(
|
||||||
fields.unnamed.iter().collect()
|
fields.unnamed.iter().collect()
|
||||||
}
|
}
|
||||||
Fields::Unit => {
|
Fields::Unit => {
|
||||||
return Err(Error::new(
|
// Empty structs or unit, no fields
|
||||||
input.span(),
|
vec![]
|
||||||
"EthEvent cannot be derived for empty structs and unit",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Data::Enum(_) => {
|
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
|
// decode
|
||||||
let (signature_check, flat_topics_init, topic_tokens_len_check) = if event.anonymous {
|
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
|
// check if indexed are sorted
|
||||||
let tokens_init = if event_fields
|
let tokens_init = if event_fields
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -670,3 +670,31 @@ fn derives_abi_name() {
|
||||||
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef".parse().unwrap()
|
"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()");
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue