Skip to content
This repository was archived by the owner on Oct 19, 2024. It is now read-only.

Commit d704ac7

Browse files
committed
feat(abigen): support empty events
1 parent 18a049b commit d704ac7

File tree

2 files changed

+82
-45
lines changed

2 files changed

+82
-45
lines changed

Diff for: ethers-contract/ethers-contract-derive/src/event.rs

+54-45
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
//! Helper functions for deriving `EthEvent`
22
3+
use crate::{abi_ty, utils};
34
use ethers_contract_abigen::Source;
5+
use ethers_core::{
6+
abi::{Event, EventExt, EventParam, HumanReadableParser},
7+
macros::{ethers_contract_crate, ethers_core_crate},
8+
};
9+
use hex::FromHex;
410
use proc_macro2::{Span, TokenStream};
511
use quote::quote;
612
use syn::{
713
parse::Error, spanned::Spanned, AttrStyle, Data, DeriveInput, Field, Fields, Lit, Meta,
814
NestedMeta,
915
};
1016

11-
use ethers_core::{
12-
abi::{Event, EventExt, EventParam, HumanReadableParser},
13-
macros::{ethers_contract_crate, ethers_core_crate},
14-
};
15-
use hex::FromHex;
16-
17-
use crate::{abi_ty, utils};
18-
1917
/// Generates the `EthEvent` trait support
2018
pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> Result<TokenStream, Error> {
2119
let name = &input.ident;
@@ -124,10 +122,7 @@ impl EventField {
124122
}
125123
}
126124

127-
fn derive_decode_from_log_impl(
128-
input: &DeriveInput,
129-
event: &Event,
130-
) -> Result<proc_macro2::TokenStream, Error> {
125+
fn derive_decode_from_log_impl(input: &DeriveInput, event: &Event) -> Result<TokenStream, Error> {
131126
let ethers_core = ethers_core_crate();
132127

133128
let fields: Vec<_> = match input.data {
@@ -159,10 +154,8 @@ fn derive_decode_from_log_impl(
159154
fields.unnamed.iter().collect()
160155
}
161156
Fields::Unit => {
162-
return Err(Error::new(
163-
input.span(),
164-
"EthEvent cannot be derived for empty structs and unit",
165-
))
157+
// Empty structs or unit, no fields
158+
vec![]
166159
}
167160
},
168161
Data::Enum(_) => {
@@ -173,35 +166,6 @@ fn derive_decode_from_log_impl(
173166
}
174167
};
175168

176-
let mut event_fields = Vec::with_capacity(fields.len());
177-
for (index, field) in fields.iter().enumerate() {
178-
let mut param = event.inputs[index].clone();
179-
180-
let (topic_name, indexed) = parse_field_attributes(field)?;
181-
if indexed {
182-
param.indexed = true;
183-
}
184-
let topic_name =
185-
param.indexed.then(|| topic_name.or_else(|| Some(param.name.clone()))).flatten();
186-
187-
event_fields.push(EventField { topic_name, index, param });
188-
}
189-
190-
// convert fields to params list
191-
let topic_types = event_fields
192-
.iter()
193-
.filter(|f| f.is_indexed())
194-
.map(|f| utils::topic_param_type_quote(&f.param.kind));
195-
196-
let topic_types_init = quote! {let topic_types = ::std::vec![#( #topic_types ),*];};
197-
198-
let data_types = event_fields
199-
.iter()
200-
.filter(|f| !f.is_indexed())
201-
.map(|f| utils::param_type_quote(&f.param.kind));
202-
203-
let data_types_init = quote! {let data_types = [#( #data_types ),*];};
204-
205169
// decode
206170
let (signature_check, flat_topics_init, topic_tokens_len_check) = if event.anonymous {
207171
(
@@ -234,6 +198,51 @@ fn derive_decode_from_log_impl(
234198
)
235199
};
236200

201+
// Event with no fields, can skip decoding
202+
if fields.is_empty() {
203+
return Ok(quote! {
204+
205+
let #ethers_core::abi::RawLog {topics, data} = log;
206+
207+
#signature_check
208+
209+
if topics.len() != 1usize || !data.is_empty() {
210+
return Err(::ethers_core::abi::Error::InvalidData);
211+
}
212+
213+
#ethers_core::abi::Tokenizable::from_token(#ethers_core::abi::Token::Tuple(::std::vec::Vec::new())).map_err(|_|#ethers_core::abi::Error::InvalidData)
214+
})
215+
}
216+
217+
let mut event_fields = Vec::with_capacity(fields.len());
218+
for (index, field) in fields.iter().enumerate() {
219+
let mut param = event.inputs[index].clone();
220+
221+
let (topic_name, indexed) = parse_field_attributes(field)?;
222+
if indexed {
223+
param.indexed = true;
224+
}
225+
let topic_name =
226+
param.indexed.then(|| topic_name.or_else(|| Some(param.name.clone()))).flatten();
227+
228+
event_fields.push(EventField { topic_name, index, param });
229+
}
230+
231+
// convert fields to params list
232+
let topic_types = event_fields
233+
.iter()
234+
.filter(|f| f.is_indexed())
235+
.map(|f| utils::topic_param_type_quote(&f.param.kind));
236+
237+
let topic_types_init = quote! {let topic_types = ::std::vec![#( #topic_types ),*];};
238+
239+
let data_types = event_fields
240+
.iter()
241+
.filter(|f| !f.is_indexed())
242+
.map(|f| utils::param_type_quote(&f.param.kind));
243+
244+
let data_types_init = quote! {let data_types = [#( #data_types ),*];};
245+
237246
// check if indexed are sorted
238247
let tokens_init = if event_fields
239248
.iter()

Diff for: ethers-contract/tests/it/derive.rs

+28
Original file line numberDiff line numberDiff line change
@@ -670,3 +670,31 @@ fn derives_abi_name() {
670670
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef".parse().unwrap()
671671
);
672672
}
673+
674+
// <https://github.com/gakonst/ethers-rs/issues/2261>
675+
#[test]
676+
fn derive_empty_events() {
677+
#[derive(Debug, EthEvent)]
678+
#[ethevent(abi = "EmptyEvent()")]
679+
struct EmptyEvent;
680+
681+
let log = RawLog { topics: vec![EmptyEvent::signature()], data: vec![] };
682+
let event = <EmptyEvent as EthLogDecode>::decode_log(&log).unwrap();
683+
684+
let log = RawLog { topics: vec![EmptyEvent::signature()], data: vec![0] };
685+
assert!(<EmptyEvent as EthLogDecode>::decode_log(&log).is_err());
686+
687+
let log = RawLog { topics: vec![EmptyEvent::signature(), H256::random()], data: vec![0] };
688+
assert!(<EmptyEvent as EthLogDecode>::decode_log(&log).is_err());
689+
690+
assert_eq!(EmptyEvent::abi_signature(), "EmptyEvent()");
691+
692+
abigen!(
693+
DummyContract,
694+
r#"[
695+
event EmptyEvent2()
696+
]"#,
697+
);
698+
699+
assert_eq!(EmptyEvent2Filter::abi_signature(), "EmptyEvent2()");
700+
}

0 commit comments

Comments
 (0)