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

feat(abigen): support overloaded events #1233

Merged
merged 2 commits into from
May 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@

### Unreleased

- Support overloaded events
[#1233](https://github.com/gakonst/ethers-rs/pull/1233)
- Relax Clone requirements when Arc<Middleware> 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)
Expand Down
18 changes: 17 additions & 1 deletion ethers-contract/ethers-contract-abigen/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()
Expand Down
30 changes: 20 additions & 10 deletions ethers-contract/ethers-contract-abigen/src/contract/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<_>>();

let ethers_core = ethers_core_crate();
Expand Down Expand Up @@ -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! {
Expand Down Expand Up @@ -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! {
Expand All @@ -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
Expand All @@ -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>) -> Ident {
fn event_struct_name(event_name: &str, alias: Option<Ident>) -> 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).
Expand Down Expand Up @@ -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, &params);

assert_quote!(definition, {
Expand All @@ -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, &params);

assert_quote!(definition, {
Expand All @@ -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, &params);

assert_quote!(definition, {
Expand All @@ -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, &params);

assert_quote!(definition, {
Expand Down
18 changes: 18 additions & 0 deletions ethers-contract/tests/abigen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}