feat(abigen): support overloaded events (#1233)

* feat(abigen): support overloaded events

* chore: update CHANGELOG
This commit is contained in:
Matthias Seitz 2022-05-07 18:31:53 +02:00 committed by GitHub
parent 44cbbc769a
commit fd994d7fde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 11 deletions

View File

@ -68,6 +68,8 @@
### Unreleased ### Unreleased
- Support overloaded events
[#1233](https://github.com/gakonst/ethers-rs/pull/1233)
- Relax Clone requirements when Arc<Middleware> is used - Relax Clone requirements when Arc<Middleware> is used
[#1183](https://github.com/gakonst/ethers-rs/pull/1183) [#1183](https://github.com/gakonst/ethers-rs/pull/1183)
- Generate a deploy function if bytecode is provided in the abigen! input (json artifact) - Generate a deploy function if bytecode is provided in the abigen! input (json artifact)

View File

@ -16,7 +16,7 @@ use eyre::{eyre, Context as _, Result};
use crate::contract::methods::MethodAlias; use crate::contract::methods::MethodAlias;
use crate::rawabi::JsonAbi; use crate::rawabi::JsonAbi;
use ethers_core::types::Bytes; use ethers_core::{abi::EventExt, types::Bytes};
use proc_macro2::{Ident, Literal, TokenStream}; use proc_macro2::{Ident, Literal, TokenStream};
use quote::quote; use quote::quote;
use serde::Deserialize; use serde::Deserialize;
@ -227,6 +227,22 @@ impl Context {
event_aliases.insert(signature, alias); 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 let event_derives = args
.event_derives .event_derives
.iter() .iter()

View File

@ -58,7 +58,9 @@ impl Context {
.events .events
.values() .values()
.flatten() .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::<Vec<_>>(); .collect::<Vec<_>>();
let ethers_core = ethers_core_crate(); let ethers_core = ethers_core_crate();
@ -116,7 +118,10 @@ impl Context {
let ty = if iter.next().is_some() { let ty = if iter.next().is_some() {
self.expand_event_enum_name() self.expand_event_enum_name()
} else { } 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! { quote! {
@ -218,7 +223,7 @@ impl Context {
// append `filter` to disambiguate with potentially conflicting // append `filter` to disambiguate with potentially conflicting
// function names // 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)); let doc = util::expand_doc(&format!("Gets the contract's `{}` event", event.name));
quote! { quote! {
@ -237,7 +242,7 @@ impl Context {
let abi_signature = event.abi_signature(); let abi_signature = event.abi_signature();
let event_abi_name = event.name.clone(); 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)?; let params = self.expand_event_params(event)?;
// expand as a tuple if all fields are anonymous // 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. /// Expands an ABI event into an identifier for its event data type.
fn expand_struct_name(event: &Event, alias: Option<Ident>) -> Ident { fn event_struct_name(event_name: &str, alias: Option<Ident>) -> Ident {
// TODO: get rid of `Filter` suffix? // TODO: get rid of `Filter` suffix?
let name = if let Some(id) = alias { let name = if let Some(id) = alias {
format!("{}Filter", id.to_string().to_pascal_case()) format!("{}Filter", id.to_string().to_pascal_case())
} else { } else {
format!("{}Filter", event.name.to_pascal_case()) format!("{}Filter", event_name.to_pascal_case())
}; };
util::ident(&name) 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 /// Expands an event data structure from its name-type parameter pairs. Returns
/// a tuple with the type definition (i.e. the struct declaration) and /// a tuple with the type definition (i.e. the struct declaration) and
/// construction (i.e. code for creating an instance of the event data). /// construction (i.e. code for creating an instance of the event data).
@ -406,7 +416,7 @@ mod tests {
let cx = test_context(); let cx = test_context();
let params = cx.expand_event_params(&event).unwrap(); 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, &params); let definition = expand_data_struct(&name, &params);
assert_quote!(definition, { assert_quote!(definition, {
@ -431,7 +441,7 @@ mod tests {
let cx = test_context_with_alias("Foo(bool,address)", "FooAliased"); let cx = test_context_with_alias("Foo(bool,address)", "FooAliased");
let params = cx.expand_event_params(&event).unwrap(); let params = cx.expand_event_params(&event).unwrap();
let alias = Some(util::ident("FooAliased")); 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, &params); let definition = expand_data_struct(&name, &params);
assert_quote!(definition, { assert_quote!(definition, {
@ -455,7 +465,7 @@ mod tests {
let cx = test_context(); let cx = test_context();
let params = cx.expand_event_params(&event).unwrap(); 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, &params); let definition = expand_data_tuple(&name, &params);
assert_quote!(definition, { assert_quote!(definition, {
@ -477,7 +487,7 @@ mod tests {
let cx = test_context_with_alias("Foo(bool,address)", "FooAliased"); let cx = test_context_with_alias("Foo(bool,address)", "FooAliased");
let params = cx.expand_event_params(&event).unwrap(); let params = cx.expand_event_params(&event).unwrap();
let alias = Some(util::ident("FooAliased")); 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, &params); let definition = expand_data_tuple(&name, &params);
assert_quote!(definition, { assert_quote!(definition, {

View File

@ -548,3 +548,21 @@ fn can_gen_reserved_word_field_names() {
let _foo = Foo { ref_: U256::default() }; 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 };
}