diff --git a/ethers-contract/ethers-contract-abigen/src/contract/events.rs b/ethers-contract/ethers-contract-abigen/src/contract/events.rs index 7c5d2f2f..eea9d52b 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/events.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/events.rs @@ -34,14 +34,18 @@ impl Context { /// Generate the event filter methods for the contract pub fn event_methods(&self) -> Result { let sorted_events: BTreeMap<_, _> = self.abi.events.clone().into_iter().collect(); - let data_types = sorted_events + let filter_methods = sorted_events .values() .flatten() .map(|event| self.expand_filter(event)) .collect::>(); + let events_method = self.expand_events_method(); + Ok(quote! { - #( #data_types )* + #( #filter_methods )* + + #events_method }) } @@ -106,21 +110,27 @@ 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 { + /// Expands the `events` function that bundles all declared events of this contract + fn expand_events_method(&self) -> TokenStream { 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()) + if let Some(event) = iter.next() { + let ty = if iter.next().is_some() { + self.expand_event_enum_name() + } else { + expand_struct_name(event) + }; + + quote! { + /// Returns an [`Event`](ethers_contract::builders::Event) builder for all events of this contract + pub fn events(&self) -> ethers_contract::builders::Event { + self.0.event_with_filter(Default::default()) + } + } } else { - Some(expand_struct_name(event)) + quote! {} } } @@ -208,13 +218,12 @@ impl Context { let name = util::safe_ident(&format!("{}_filter", event.name.to_snake_case())); // let result = util::ident(&event.name.to_pascal_case()); let result = expand_struct_name(event); - let ev_name = Literal::string(&event.name); let doc = util::expand_doc(&format!("Gets the contract's `{}` event", event.name)); quote! { #doc pub fn #name(&self) -> ethers_contract::builders::Event { - self.0.event(#ev_name).expect("event not found (this should never happen)") + self.0.event() } } } diff --git a/ethers-contract/src/contract.rs b/ethers-contract/src/contract.rs index 878ce4c1..f8b4bf8f 100644 --- a/ethers-contract/src/contract.rs +++ b/ethers-contract/src/contract.rs @@ -1,7 +1,8 @@ -use super::{ +use crate::{ base::{encode_function_data, AbiError, BaseContract}, call::ContractCall, - event::Event, + event::{EthEvent, Event}, + EthLogDecode, }; use ethers_core::{ @@ -179,19 +180,25 @@ impl Contract { } } - /// Returns an [`Event`](crate::builders::Event) builder for the provided event name. - /// TODO(mattsse) keep this but remove event - pub fn event(&self, name: &str) -> Result, Error> { + /// Returns an [`Event`](crate::builders::Event) builder for the provided event. + pub fn event(&self) -> Event { + self.event_with_filter(Filter::new().event(&D::abi_signature())) + } + + /// Returns an [`Event`](crate::builders::Event) builder with the provided filter. + pub fn event_with_filter(&self, filter: Filter) -> Event { + Event { + provider: &self.client, + filter: filter.address(self.address), + datatype: PhantomData, + } + } + + /// Returns an [`Event`](crate::builders::Event) builder with the provided name. + pub fn event_by_name(&self, name: &str) -> Result, Error> { // get the event's full name let event = self.base_contract.abi.event(name)?; - Ok(Event { - provider: &self.client, - filter: Filter::new() - .event(&event.abi_signature()) - .address(self.address), - event: &event, - datatype: PhantomData, - }) + Ok(self.event_with_filter(Filter::new().event(&event.abi_signature()))) } /// Returns a transaction builder for the provided function name. If there are diff --git a/ethers-contract/src/event.rs b/ethers-contract/src/event.rs index 5fcbb1d2..4e88d608 100644 --- a/ethers-contract/src/event.rs +++ b/ethers-contract/src/event.rs @@ -1,7 +1,7 @@ -use crate::{base::decode_event, stream::EventStream, ContractError, EthLogDecode}; +use crate::{stream::EventStream, ContractError, EthLogDecode}; use ethers_core::{ - abi::{Detokenize, Event as AbiEvent, RawLog}, + abi::{Detokenize, RawLog}, types::{BlockNumber, Filter, Log, TxHash, ValueOrArray, H256, U64}, }; use ethers_providers::{FilterWatcher, Middleware, PubsubClient, SubscriptionStream}; @@ -31,12 +31,12 @@ pub trait EthEvent: Detokenize { fn is_anonymous() -> bool; /// Returns an Event builder for the ethereum event represented by this types ABI signature. - fn new(filter: Filter, provider: &M) -> Event2 + fn new(filter: Filter, provider: &M) -> Event where Self: Sized, { let filter = filter.event(&Self::abi_signature()); - Event2 { + Event { filter, provider, datatype: PhantomData, @@ -57,7 +57,7 @@ impl EthLogDecode for T { /// Helper for managing the event filter before querying or streaming its logs #[derive(Debug)] #[must_use = "event filters do nothing unless you `query` or `stream` them"] -pub struct Event2<'a, M, D> { +pub struct Event<'a, M, D> { /// The event filter's state pub filter: Filter, pub(crate) provider: &'a M, @@ -66,7 +66,7 @@ pub struct Event2<'a, M, D> { } // TODO: Improve these functions -impl Event2<'_, M, D> { +impl Event<'_, M, D> { /// Sets the filter's `from` block #[allow(clippy::wrong_self_convention)] pub fn from_block>(mut self, block: T) -> Self { @@ -114,7 +114,7 @@ impl Event2<'_, M, D> { } } -impl<'a, M, D> Event2<'a, M, D> +impl<'a, M, D> Event<'a, M, D> where M: Middleware, D: EthLogDecode, @@ -140,7 +140,7 @@ where } } -impl<'a, M, D> Event2<'a, M, D> +impl<'a, M, D> Event<'a, M, D> where M: Middleware, ::Provider: PubsubClient, @@ -167,7 +167,7 @@ where } } -impl Event2<'_, M, D> +impl Event<'_, M, D> where M: Middleware, D: EthLogDecode, @@ -215,164 +215,6 @@ where } } -/// Helper for managing the event filter before querying or streaming its logs -#[derive(Debug)] -#[must_use = "event filters do nothing unless you `query` or `stream` them"] -pub struct Event<'a: 'b, 'b, M, D> { - /// The event filter's state - pub filter: Filter, - /// The ABI of the event which is being filtered - pub event: &'b AbiEvent, - pub(crate) provider: &'a M, - pub(crate) datatype: PhantomData, -} - -// TODO: Improve these functions -impl Event<'_, '_, M, D> { - /// Sets the filter's `from` block - #[allow(clippy::wrong_self_convention)] - pub fn from_block>(mut self, block: T) -> Self { - self.filter = self.filter.from_block(block); - self - } - - /// Sets the filter's `to` block - #[allow(clippy::wrong_self_convention)] - pub fn to_block>(mut self, block: T) -> Self { - self.filter = self.filter.to_block(block); - self - } - - /// Sets the filter's `blockHash`. Setting this will override previously - /// set `from_block` and `to_block` fields. - #[allow(clippy::wrong_self_convention)] - pub fn at_block_hash>(mut self, hash: T) -> Self { - self.filter = self.filter.at_block_hash(hash); - self - } - - /// Sets the filter's 0th topic (typically the event name for non-anonymous events) - pub fn topic0>>(mut self, topic: T) -> Self { - self.filter.topics[0] = Some(topic.into()); - self - } - - /// Sets the filter's 1st topic - pub fn topic1>>(mut self, topic: T) -> Self { - self.filter.topics[1] = Some(topic.into()); - self - } - - /// Sets the filter's 2nd topic - pub fn topic2>>(mut self, topic: T) -> Self { - self.filter.topics[2] = Some(topic.into()); - self - } - - /// Sets the filter's 3rd topic - pub fn topic3>>(mut self, topic: T) -> Self { - self.filter.topics[3] = Some(topic.into()); - self - } -} - -impl<'a, 'b, M, D> Event<'a, 'b, M, D> -where - M: Middleware, - D: 'b + Detokenize + Clone, -{ - /// Returns a stream for the event - pub async fn stream( - &'a self, - ) -> Result< - // Wraps the FilterWatcher with a mapping to the event - EventStream<'a, FilterWatcher<'a, M::Provider, Log>, D, ContractError>, - ContractError, - > { - let filter = self - .provider - .watch(&self.filter) - .await - .map_err(ContractError::MiddlewareError)?; - Ok(EventStream::new( - filter.id, - filter, - Box::new(move |log| self.parse_log(log)), - )) - } -} - -impl<'a, 'b, M, D> Event<'a, 'b, M, D> -where - M: Middleware, - ::Provider: PubsubClient, - D: 'b + Detokenize + Clone, -{ - /// Returns a subscription for the event - pub async fn subscribe( - &'a self, - ) -> Result< - // Wraps the SubscriptionStream with a mapping to the event - EventStream<'a, SubscriptionStream<'a, M::Provider, Log>, D, ContractError>, - ContractError, - > { - let filter = self - .provider - .subscribe_logs(&self.filter) - .await - .map_err(ContractError::MiddlewareError)?; - Ok(EventStream::new( - filter.id, - filter, - Box::new(move |log| self.parse_log(log)), - )) - } -} - -impl Event<'_, '_, M, D> -where - M: Middleware, - D: Detokenize + Clone, -{ - /// Queries the blockchain for the selected filter and returns a vector of matching - /// event logs - pub async fn query(&self) -> Result, ContractError> { - let logs = self - .provider - .get_logs(&self.filter) - .await - .map_err(ContractError::MiddlewareError)?; - let events = logs - .into_iter() - .map(|log| self.parse_log(log)) - .collect::, ContractError>>()?; - Ok(events) - } - - /// Queries the blockchain for the selected filter and returns a vector of logs - /// along with their metadata - pub async fn query_with_meta(&self) -> Result, ContractError> { - let logs = self - .provider - .get_logs(&self.filter) - .await - .map_err(ContractError::MiddlewareError)?; - let events = logs - .into_iter() - .map(|log| { - let meta = LogMeta::from(&log); - let event = self.parse_log(log)?; - Ok((event, meta)) - }) - .collect::>>()?; - Ok(events) - } - - fn parse_log(&self, log: Log) -> Result> { - Ok(decode_event(self.event, log.topics, log.data)?) - } -} - /// Metadata inside a log #[derive(Clone, Debug, PartialEq)] pub struct LogMeta {