From 855fd2deb236e1a476433c9f5e00d3080313ce74 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 18 Mar 2021 12:54:39 +0100 Subject: [PATCH] feat: generate EthLogDecode implementations --- .../src/contract/events.rs | 47 +++++++++++++++---- ethers-contract/src/event.rs | 11 ++++- ethers-contract/src/log.rs | 4 +- 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/ethers-contract/ethers-contract-abigen/src/contract/events.rs b/ethers-contract/ethers-contract-abigen/src/contract/events.rs index a12edea3..179f7b7b 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/events.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/events.rs @@ -63,7 +63,7 @@ impl Context { #(#variants(#variants)),* } - impl ethers_core::abi::Tokenizable for #enum_name { + impl ethers::abi::Tokenizable for #enum_name { fn from_token(token: ethers::abi::Token) -> Result where Self: Sized { @@ -83,7 +83,21 @@ impl Context { } } } - impl ethers_core::abi::TokenizableItem for #enum_name { } + impl ethers::abi::TokenizableItem for #enum_name { } + + impl ethers::contract::EthLogDecode for #enum_name { + fn decode_log(log: ðers::abi::RawLog) -> Result + where + Self: Sized, + { + #( + if let Ok(decoded) = #variants::decode_log(log) { + return Ok(#enum_name::#variants(decoded)) + } + )* + Err(ethers::abi::Error::InvalidData) + } + } } } @@ -92,6 +106,24 @@ impl Context { util::ident(&format!("{}Events", self.contract_name.to_string())) } + /// The type that the `events` function should return + /// + /// The generated enum type if more than 1 events are present + /// The event name if only 1 event is present + /// `None` otherwise + fn expand_events_type(&self) -> Option { + let sorted_events: BTreeMap<_, _> = self.abi.events.clone().into_iter().collect(); + + let mut iter = sorted_events.values().flatten(); + let event = iter.next()?; + + if iter.next().is_some() { + Some(self.expand_event_enum_name()) + } else { + Some(expand_struct_name(event)) + } + } + /// Expands an event property type. /// /// Note that this is slightly different than an expanding a Solidity type as @@ -258,17 +290,16 @@ impl Context { } /// Expands an ABI event into an identifier for its event data type. -fn expand_struct_name(event: &Event) -> TokenStream { +fn expand_struct_name(event: &Event) -> Ident { // TODO: get rid of `Filter` suffix? let name = format!("{}Filter", event.name.to_pascal_case()); - let event_name = util::ident(&name); - quote! { #event_name } + util::ident(&name) } /// 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). -fn expand_data_struct(name: &TokenStream, params: &[(TokenStream, TokenStream)]) -> TokenStream { +fn expand_data_struct(name: &Ident, params: &[(TokenStream, TokenStream)]) -> TokenStream { let fields = params .iter() .map(|(name, ty)| quote! { pub #name: #ty }) @@ -279,7 +310,7 @@ fn expand_data_struct(name: &TokenStream, params: &[(TokenStream, TokenStream)]) /// Expands an event data named tuple from its name-type parameter pairs. /// Returns a tuple with the type definition and construction. -fn expand_data_tuple(name: &TokenStream, params: &[(TokenStream, TokenStream)]) -> TokenStream { +fn expand_data_tuple(name: &Ident, params: &[(TokenStream, TokenStream)]) -> TokenStream { let fields = params .iter() .map(|(_, ty)| quote! { pub #ty }) @@ -406,7 +437,7 @@ mod tests { let cx = test_context(); let params = cx.expand_params(&event).unwrap(); let name = expand_struct_name(&event); - let definition = expand_data_tuple(&name, ¶ms); + let definition = expand_data_tuple(name, ¶ms); assert_quote!(definition, { struct FooFilter(pub bool, pub Address); diff --git a/ethers-contract/src/event.rs b/ethers-contract/src/event.rs index c4e9e888..d5a5af1e 100644 --- a/ethers-contract/src/event.rs +++ b/ethers-contract/src/event.rs @@ -1,4 +1,4 @@ -use crate::{base::decode_event, stream::EventStream, ContractError}; +use crate::{base::decode_event, stream::EventStream, ContractError, EthLogDecode}; use ethers_core::{ abi::{Detokenize, Event as AbiEvent, RawLog}, @@ -29,7 +29,16 @@ pub trait EthEvent: Detokenize { /// Returns true if this is an anonymous event fn is_anonymous() -> bool; +} +// Convenience implementation +impl EthLogDecode for T { + fn decode_log(log: &RawLog) -> Result + where + Self: Sized, + { + T::decode_log(log) + } } /// Helper for managing the event filter before querying or streaming its logs diff --git a/ethers-contract/src/log.rs b/ethers-contract/src/log.rs index 53d0edbd..829622e8 100644 --- a/ethers-contract/src/log.rs +++ b/ethers-contract/src/log.rs @@ -5,7 +5,9 @@ use ethers_core::abi::RawLog; /// A trait for types (events) that can be decoded from a `RawLog` pub trait EthLogDecode { /// decode from a `RawLog` - fn decode_log(log: &RawLog) -> Result where Self: Sized; + fn decode_log(log: &RawLog) -> Result + where + Self: Sized; } /// Decodes a series of logs into a vector