Skip to content

Commit

Permalink
feat: function call enums EthCall macro and more (#517)
Browse files Browse the repository at this point in the history
* fix: do not sort event variants

* style: use deref over clone

* style: refactor some stuff

* feat: add ethcall trait

* feat: include in abigen

* feat: add bare bones eth call derive

* feat: impl EthCall derive

* feat: support enums

* feat: use abigen enum derive

* fix: concrete abi and map errors

* test: first call test

* rustfmt

* chore: use correct trait name on error

* feat: derive display for call structs

* feat: add from conversion

* test: add convert test

* chore: docs and test

* chore: update changelog

* cargo fix

* feat: add unit type derive support and more test

* chore: patch ethabi

* chore: rm ethabi patch

* feat: add encode/decode trait impls

* style: use AsRef<[u8]>

* Update ethers-contract/ethers-contract-abigen/src/contract/methods.rs

Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>

* style: reindent macro body

* test: add tuple event test

Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
  • Loading branch information
mattsse and gakonst committed Oct 18, 2021
1 parent 071a416 commit fb4d9a9
Show file tree
Hide file tree
Showing 19 changed files with 950 additions and 134 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Unreleased

- add `EthCall` trait and derive macro which generates matching structs for contract calls [#517](https://github.com/gakonst/ethers-rs/pull/517)
- `abigen!` now generates `Display` for all events using the new `EthDisplay` macro [#513](https://github.com/gakonst/ethers-rs/pull/513)
- `abigen!` now supports overloaded functions natively [#501](https://github.com/gakonst/ethers-rs/pull/501)
- `abigen!` now supports multiple contracts [#498](https://github.com/gakonst/ethers-rs/pull/498)
Expand Down
9 changes: 7 additions & 2 deletions ethers-contract/ethers-contract-abigen/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub struct ExpandedContract {
pub contract: TokenStream,
/// All event impls of the contract
pub events: TokenStream,
/// All contract call struct related types
pub call_structs: TokenStream,
/// The contract's internal structs
pub abi_structs: TokenStream,
}
Expand All @@ -41,6 +43,7 @@ impl ExpandedContract {
imports,
contract,
events,
call_structs,
abi_structs,
} = self;
quote! {
Expand All @@ -52,6 +55,7 @@ impl ExpandedContract {
#imports
#contract
#events
#call_structs
#abi_structs
}
}
Expand Down Expand Up @@ -111,8 +115,8 @@ impl Context {
// 3. impl block for the event functions
let contract_events = self.event_methods()?;

// 4. impl block for the contract methods
let contract_methods = self.methods()?;
// 4. impl block for the contract methods and their corresponding types
let (contract_methods, call_structs) = self.methods_and_call_structs()?;

// 5. Declare the structs parsed from the human readable abi
let abi_structs_decl = self.abi_structs()?;
Expand Down Expand Up @@ -146,6 +150,7 @@ impl Context {
imports,
contract,
events: events_decl,
call_structs,
abi_structs: abi_structs_decl,
})
}
Expand Down
68 changes: 25 additions & 43 deletions ethers-contract/ethers-contract-abigen/src/contract/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use inflector::Inflector;
use proc_macro2::{Ident, Literal, TokenStream};
use quote::quote;
use std::collections::BTreeMap;
use syn::Path;

impl Context {
/// Expands each event to a struct + its impl Detokenize block
Expand Down Expand Up @@ -33,9 +32,10 @@ impl Context {

/// Generate the event filter methods for the contract
pub fn event_methods(&self) -> Result<TokenStream> {
let sorted_events: BTreeMap<_, _> = self.abi.events.clone().into_iter().collect();
let sorted_events: BTreeMap<_, _> = self.abi.events.iter().collect();
let filter_methods = sorted_events
.values()
.map(std::ops::Deref::deref)
.flatten()
.map(|event| self.expand_filter(event))
.collect::<Vec<_>>();
Expand All @@ -51,9 +51,9 @@ impl Context {

/// Generate an enum with a variant for each event
fn expand_events_enum(&self) -> TokenStream {
let sorted_events: BTreeMap<_, _> = self.abi.events.clone().into_iter().collect();

let variants = sorted_events
let variants = self
.abi
.events
.values()
.flatten()
.map(|e| expand_struct_name(e, self.event_aliases.get(&e.abi_signature()).cloned()))
Expand All @@ -65,33 +65,11 @@ impl Context {
let ethers_contract = util::ethers_contract_crate();

quote! {
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, #ethers_contract::EthAbiType)]
pub enum #enum_name {
#(#variants(#variants)),*
}

impl #ethers_core::abi::Tokenizable for #enum_name {

fn from_token(token: #ethers_core::abi::Token) -> Result<Self, #ethers_core::abi::InvalidOutputType> where
Self: Sized {
#(
if let Ok(decoded) = #variants::from_token(token.clone()) {
return Ok(#enum_name::#variants(decoded))
}
)*
Err(#ethers_core::abi::InvalidOutputType("Failed to decode all event variants".to_string()))
}

fn into_token(self) -> #ethers_core::abi::Token {
match self {
#(
#enum_name::#variants(element) => element.into_token()
),*
}
}
}
impl #ethers_core::abi::TokenizableItem for #enum_name { }

impl #ethers_contract::EthLogDecode for #enum_name {
fn decode_log(log: &#ethers_core::abi::RawLog) -> Result<Self, #ethers_core::abi::Error>
where
Expand Down Expand Up @@ -153,8 +131,8 @@ impl Context {

/// Expands an event property type.
///
/// Note that this is slightly different than an expanding a Solidity type as
/// complex types like arrays and strings get emited as hashes when they are
/// Note that this is slightly different from expanding a Solidity type as
/// complex types like arrays and strings get emitted as hashes when they are
/// indexed.
/// If a complex types matches with a struct previously parsed by the AbiParser,
/// we can replace it
Expand Down Expand Up @@ -213,8 +191,8 @@ impl Context {
})
}

/// Expands an ABI event into name-type pairs for each of its parameters.
fn expand_params(&self, event: &Event) -> Result<Vec<(TokenStream, TokenStream, bool)>> {
/// Expands the name-type pairs for the given inputs
fn expand_event_params(&self, event: &Event) -> Result<Vec<(TokenStream, TokenStream, bool)>> {
event
.inputs
.iter()
Expand Down Expand Up @@ -264,7 +242,7 @@ impl Context {

let event_name = expand_struct_name(event, sig);

let params = self.expand_params(event)?;
let params = self.expand_event_params(event)?;
// expand as a tuple if all fields are anonymous
let all_anonymous_fields = event.inputs.iter().all(|input| input.name.is_empty());
let data_type_definition = if all_anonymous_fields {
Expand All @@ -273,7 +251,7 @@ impl Context {
expand_data_struct(&event_name, &params)
};

let derives = expand_derives(&self.event_derives);
let derives = util::expand_derives(&self.event_derives);

let ethers_contract = util::ethers_contract_crate();

Expand Down Expand Up @@ -323,16 +301,20 @@ fn expand_data_struct(name: &Ident, params: &[(TokenStream, TokenStream, bool)])
fn expand_data_tuple(name: &Ident, params: &[(TokenStream, TokenStream, bool)]) -> TokenStream {
let fields = params
.iter()
.map(|(_, ty, _)| quote! { pub #ty })
.map(|(_, ty, indexed)| {
if *indexed {
quote! {
#[ethevent(indexed)] pub #ty }
} else {
quote! {
pub #ty }
}
})
.collect::<Vec<_>>();

quote! { struct #name( #( #fields ),* ); }
}

fn expand_derives(derives: &[Path]) -> TokenStream {
quote! {#(#derives),*}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -450,7 +432,7 @@ mod tests {
};

let cx = test_context();
let params = cx.expand_params(&event).unwrap();
let params = cx.expand_event_params(&event).unwrap();
let name = expand_struct_name(&event, None);
let definition = expand_data_struct(&name, &params);

Expand Down Expand Up @@ -482,7 +464,7 @@ mod tests {
};

let cx = test_context_with_alias("Foo(bool,address)", "FooAliased");
let params = cx.expand_params(&event).unwrap();
let params = cx.expand_event_params(&event).unwrap();
let alias = Some(util::ident("FooAliased"));
let name = expand_struct_name(&event, alias);
let definition = expand_data_struct(&name, &params);
Expand Down Expand Up @@ -515,7 +497,7 @@ mod tests {
};

let cx = test_context();
let params = cx.expand_params(&event).unwrap();
let params = cx.expand_event_params(&event).unwrap();
let name = expand_struct_name(&event, None);
let definition = expand_data_tuple(&name, &params);

Expand Down Expand Up @@ -544,7 +526,7 @@ mod tests {
};

let cx = test_context_with_alias("Foo(bool,address)", "FooAliased");
let params = cx.expand_params(&event).unwrap();
let params = cx.expand_event_params(&event).unwrap();
let alias = Some(util::ident("FooAliased"));
let name = expand_struct_name(&event, alias);
let definition = expand_data_tuple(&name, &params);
Expand Down
Loading

0 comments on commit fb4d9a9

Please sign in to comment.