diff --git a/CHANGELOG.md b/CHANGELOG.md index 810d78da..6d20c21b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,8 @@ ### Unreleased +- Support overloaded events + [#1233](https://github.com/gakonst/ethers-rs/pull/1233) - Relax Clone requirements when Arc is used [#1183](https://github.com/gakonst/ethers-rs/pull/1183) - Generate a deploy function if bytecode is provided in the abigen! input (json artifact) diff --git a/ethers-contract/ethers-contract-abigen/src/contract.rs b/ethers-contract/ethers-contract-abigen/src/contract.rs index 288af663..ad4306d6 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract.rs @@ -16,7 +16,7 @@ use eyre::{eyre, Context as _, Result}; use crate::contract::methods::MethodAlias; use crate::rawabi::JsonAbi; -use ethers_core::types::Bytes; +use ethers_core::{abi::EventExt, types::Bytes}; use proc_macro2::{Ident, Literal, TokenStream}; use quote::quote; use serde::Deserialize; @@ -227,6 +227,22 @@ impl Context { event_aliases.insert(signature, alias); } + // also check for overloaded functions not covered by aliases, in which case we simply + // numerate them + for events in abi.events.values() { + let not_aliased = + events.iter().filter(|ev| !event_aliases.contains_key(&ev.abi_signature())); + if not_aliased.clone().count() > 1 { + let mut aliases = Vec::new(); + // overloaded events + for (idx, event) in not_aliased.enumerate() { + let event_name = format!("{}{}", event.name, idx + 1); + aliases.push((event.abi_signature(), events::event_struct_alias(&event_name))); + } + event_aliases.extend(aliases); + } + } + let event_derives = args .event_derives .iter() diff --git a/ethers-contract/ethers-contract-abigen/src/contract/events.rs b/ethers-contract/ethers-contract-abigen/src/contract/events.rs index 96946af8..d7fb7b5e 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/events.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/events.rs @@ -58,7 +58,9 @@ impl Context { .events .values() .flatten() - .map(|e| expand_struct_name(e, self.event_aliases.get(&e.abi_signature()).cloned())) + .map(|e| { + event_struct_name(&e.name, self.event_aliases.get(&e.abi_signature()).cloned()) + }) .collect::>(); let ethers_core = ethers_core_crate(); @@ -116,7 +118,10 @@ impl Context { let ty = if iter.next().is_some() { self.expand_event_enum_name() } else { - expand_struct_name(event, self.event_aliases.get(&event.abi_signature()).cloned()) + event_struct_name( + &event.name, + self.event_aliases.get(&event.abi_signature()).cloned(), + ) }; quote! { @@ -218,7 +223,7 @@ impl Context { // append `filter` to disambiguate with potentially conflicting // function names - let result = expand_struct_name(event, alias); + let result = event_struct_name(&event.name, alias); let doc = util::expand_doc(&format!("Gets the contract's `{}` event", event.name)); quote! { @@ -237,7 +242,7 @@ impl Context { let abi_signature = event.abi_signature(); let event_abi_name = event.name.clone(); - let event_name = expand_struct_name(event, sig); + let event_name = event_struct_name(&event.name, sig); let params = self.expand_event_params(event)?; // expand as a tuple if all fields are anonymous @@ -261,17 +266,22 @@ impl Context { } /// Expands an ABI event into an identifier for its event data type. -fn expand_struct_name(event: &Event, alias: Option) -> Ident { +fn event_struct_name(event_name: &str, alias: Option) -> Ident { // TODO: get rid of `Filter` suffix? let name = if let Some(id) = alias { format!("{}Filter", id.to_string().to_pascal_case()) } else { - format!("{}Filter", event.name.to_pascal_case()) + format!("{}Filter", event_name.to_pascal_case()) }; util::ident(&name) } +/// Returns the alias name for an event +pub(crate) fn event_struct_alias(event_name: &str) -> Ident { + util::ident(&event_name.to_pascal_case()) +} + /// Expands an event data structure from its name-type parameter pairs. Returns /// a tuple with the type definition (i.e. the struct declaration) and /// construction (i.e. code for creating an instance of the event data). @@ -406,7 +416,7 @@ mod tests { let cx = test_context(); let params = cx.expand_event_params(&event).unwrap(); - let name = expand_struct_name(&event, None); + let name = event_struct_name(&event.name, None); let definition = expand_data_struct(&name, ¶ms); assert_quote!(definition, { @@ -431,7 +441,7 @@ mod tests { let cx = test_context_with_alias("Foo(bool,address)", "FooAliased"); let params = cx.expand_event_params(&event).unwrap(); let alias = Some(util::ident("FooAliased")); - let name = expand_struct_name(&event, alias); + let name = event_struct_name(&event.name, alias); let definition = expand_data_struct(&name, ¶ms); assert_quote!(definition, { @@ -455,7 +465,7 @@ mod tests { let cx = test_context(); let params = cx.expand_event_params(&event).unwrap(); - let name = expand_struct_name(&event, None); + let name = event_struct_name(&event.name, None); let definition = expand_data_tuple(&name, ¶ms); assert_quote!(definition, { @@ -477,7 +487,7 @@ mod tests { let cx = test_context_with_alias("Foo(bool,address)", "FooAliased"); let params = cx.expand_event_params(&event).unwrap(); let alias = Some(util::ident("FooAliased")); - let name = expand_struct_name(&event, alias); + let name = event_struct_name(&event.name, alias); let definition = expand_data_tuple(&name, ¶ms); assert_quote!(definition, { diff --git a/ethers-contract/tests/abigen.rs b/ethers-contract/tests/abigen.rs index 63faf133..1c5cd110 100644 --- a/ethers-contract/tests/abigen.rs +++ b/ethers-contract/tests/abigen.rs @@ -548,3 +548,21 @@ fn can_gen_reserved_word_field_names() { let _foo = Foo { ref_: U256::default() }; } + +#[test] +fn can_handle_overloaded_events() { + abigen!( + SimpleContract, + r#"[ + event ActionPaused(string cToken, string action, bool pauseState) + event ActionPaused(string action, bool pauseState) + ]"# + ); + + let _ev1 = ActionPaused1Filter { + c_token: "ctoken".to_string(), + action: "action".to_string(), + pause_state: false, + }; + let _ev2 = ActionPaused2Filter { action: "action".to_string(), pause_state: false }; +}