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

Prestwich/event no lifetime #2105

Merged
merged 2 commits into from
Feb 14, 2023
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@

### Unreleased

- Abigen now generates events with new `<B, M>` generic pattern [#2103](https://github.com/gakonst/ethers-rs/pull/2103)
- Fix Cargo.toml generation issue that could cause dependency conflicts [#1852](https://github.com/gakonst/ethers-rs/pull/1852)
- Use corresponding rust structs for event fields if they're solidity structs [#1674](https://github.com/gakonst/ethers-rs/pull/1674)
- Add `ContractFilter` to filter contracts in `MultiAbigen` [#1564](https://github.com/gakonst/ethers-rs/pull/1564)
Expand Down Expand Up @@ -300,6 +301,8 @@

### Unreleased

- (Breaking) Make `Event` objects generic over borrow & remove lifetime
[#2105](https://github.com/gakonst/ethers-rs/pull/2105)
- Make `Factory` objects generic over the borrow trait, to allow non-arc mware
[#2103](https://github.com/gakonst/ethers-rs/pull/2103)
- Make `Contract` objects generic over the borrow trait, to allow non-arc mware
Expand Down
10 changes: 6 additions & 4 deletions ethers-contract/ethers-contract-abigen/src/contract/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ impl Context {

quote! {
/// Returns an [`Event`](#ethers_contract::builders::Event) builder for all events of this contract
pub fn events(&self) -> #ethers_contract::builders::Event<M, #ty> {
pub fn events(&self) -> #ethers_contract::builders::Event<Arc<M>, M, #ty> {
self.0.event_with_filter(Default::default())
}
}
Expand Down Expand Up @@ -235,7 +235,7 @@ impl Context {

quote! {
#[doc = #doc_str]
pub fn #function_name(&self) -> #ethers_contract::builders::Event<M, #struct_name> {
pub fn #function_name(&self) -> #ethers_contract::builders::Event<Arc<M>, M, #struct_name> {
self.0.event()
}
}
Expand Down Expand Up @@ -406,7 +406,7 @@ mod tests {
#[doc = "Gets the contract's `Transfer` event"]
pub fn transfer_event_filter(
&self
) -> ::ethers_contract::builders::Event<M, TransferEventFilter> {
) -> ::ethers_contract::builders::Event<Arc<M>, M, TransferEventFilter> {
self.0.event()
}
});
Expand All @@ -425,7 +425,9 @@ mod tests {
let cx = test_context();
assert_quote!(cx.expand_filter(&event), {
#[doc = "Gets the contract's `Transfer` event"]
pub fn transfer_filter(&self) -> ::ethers_contract::builders::Event<M, TransferFilter> {
pub fn transfer_filter(
&self,
) -> ::ethers_contract::builders::Event<Arc<M>, M, TransferFilter> {
self.0.event()
}
});
Expand Down
46 changes: 24 additions & 22 deletions ethers-contract/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,12 @@ where
/// Returns an [`Event`](crate::builders::Event) builder for the provided event.
/// This function operates in a static context, then it does not require a `self`
/// to reference to instantiate an [`Event`](crate::builders::Event) builder.
pub fn event_of_type<D: EthEvent>(client: &M) -> Event<M, D> {
pub fn event_of_type<D: EthEvent>(client: B) -> Event<B, M, D> {
Event {
provider: client,
filter: Filter::new().event(&D::abi_signature()),
datatype: PhantomData,
_m: PhantomData,
}
}
}
Expand All @@ -262,27 +263,6 @@ where
Self { base_contract: abi.into(), client, address: address.into(), _m: PhantomData }
}

/// Returns an [`Event`](crate::builders::Event) builder for the provided event.
pub fn event<D: EthEvent>(&self) -> Event<M, D> {
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<D: EthLogDecode>(&self, filter: Filter) -> Event<M, D> {
Event {
provider: self.client.borrow(),
filter: filter.address(ValueOrArray::Value(self.address)),
datatype: PhantomData,
}
}

/// Returns an [`Event`](crate::builders::Event) builder with the provided name.
pub fn event_for_name<D: EthLogDecode>(&self, name: &str) -> Result<Event<M, D>, Error> {
// get the event's full name
let event = self.base_contract.abi.event(name)?;
Ok(self.event_with_filter(Filter::new().event(&event.abi_signature())))
}

/// Returns a new contract instance using the provided client
///
/// Clones `self` internally
Expand Down Expand Up @@ -321,6 +301,28 @@ where
B: Clone + Borrow<M>,
M: Middleware,
{
/// Returns an [`Event`](crate::builders::Event) builder with the provided filter.
pub fn event_with_filter<D: EthLogDecode>(&self, filter: Filter) -> Event<B, M, D> {
Event {
provider: self.client.clone(),
filter: filter.address(ValueOrArray::Value(self.address)),
datatype: PhantomData,
_m: PhantomData,
}
}

/// Returns an [`Event`](crate::builders::Event) builder for the provided event.
pub fn event<D: EthEvent>(&self) -> Event<B, M, D> {
self.event_with_filter(Filter::new().event(&D::abi_signature()))
}

/// Returns an [`Event`](crate::builders::Event) builder with the provided name.
pub fn event_for_name<D: EthLogDecode>(&self, name: &str) -> Result<Event<B, M, D>, Error> {
// get the event's full name
let event = self.base_contract.abi.event(name)?;
Ok(self.event_with_filter(Filter::new().event(&event.abi_signature())))
}

fn method_func<T: Tokenize, D: Detokenize>(
&self,
function: &Function,
Expand Down
108 changes: 72 additions & 36 deletions ethers-contract/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,22 @@

use crate::{log::LogMeta, stream::EventStream, ContractError, EthLogDecode};
use ethers_core::{
abi::{Address, Detokenize, RawLog},
abi::{Address, Detokenize, Error as AbiError, RawLog},
types::{BlockNumber, Filter, Log, Topic, ValueOrArray, H256},
};
use ethers_providers::{FilterWatcher, Middleware, PubsubClient, SubscriptionStream};
use std::{borrow::Cow, marker::PhantomData};
use std::{
borrow::{Borrow, Cow},
marker::PhantomData,
};

/// Attempt to parse a log into a specific output type.
pub fn parse_log<D>(log: Log) -> std::result::Result<D, AbiError>
where
D: EthLogDecode,
{
D::decode_log(&RawLog { topics: log.topics, data: log.data.to_vec() })
}

/// A trait for implementing event bindings
pub trait EthEvent: Detokenize + Send + Sync {
Expand All @@ -31,12 +42,14 @@ pub trait EthEvent: Detokenize + Send + Sync {
fn is_anonymous() -> bool;

/// Returns an Event builder for the ethereum event represented by this types ABI signature.
fn new<M: Middleware>(filter: Filter, provider: &M) -> Event<M, Self>
fn new<B, M>(filter: Filter, provider: B) -> Event<B, M, Self>
where
Self: Sized,
B: Borrow<M>,
M: Middleware,
{
let filter = filter.event(&Self::abi_signature());
Event { filter, provider, datatype: PhantomData }
Event { filter, provider, datatype: PhantomData, _m: PhantomData }
}
}

Expand All @@ -53,16 +66,22 @@ impl<T: EthEvent> 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 Event<'a, M, D> {
pub struct Event<B, M, D> {
/// The event filter's state
pub filter: Filter,
pub(crate) provider: &'a M,
pub(crate) provider: B,
/// Stores the event datatype
pub(crate) datatype: PhantomData<D>,
pub(crate) _m: PhantomData<M>,
}

// TODO: Improve these functions
impl<M, D: EthLogDecode> Event<'_, M, D> {
impl<B, M, D> Event<B, M, D>
where
B: Borrow<M>,
M: Middleware,
D: EthLogDecode,
{
/// Sets the filter's `from` block
#[allow(clippy::wrong_self_convention)]
pub fn from_block<T: Into<BlockNumber>>(mut self, block: T) -> Self {
Expand Down Expand Up @@ -116,8 +135,9 @@ impl<M, D: EthLogDecode> Event<'_, M, D> {
}
}

impl<'a, M, D> Event<'a, M, D>
impl<B, M, D> Event<B, M, D>
where
B: Borrow<M>,
M: Middleware,
D: EthLogDecode,
{
Expand Down Expand Up @@ -161,40 +181,49 @@ where
/// # }
/// ```
pub async fn stream(
&'a self,
&self,
) -> Result<
// Wraps the FilterWatcher with a mapping to the event
EventStream<'a, FilterWatcher<'a, M::Provider, Log>, D, ContractError<M>>,
EventStream<'_, FilterWatcher<'_, M::Provider, Log>, D, ContractError<M>>,
ContractError<M>,
> {
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))))
let filter = self
.provider
.borrow()
.watch(&self.filter)
.await
.map_err(ContractError::MiddlewareError)?;
Ok(EventStream::new(filter.id, filter, Box::new(move |log| Ok(parse_log(log)?))))
}

/// As [`Self::stream`], but does not discard [`Log`] metadata.
pub async fn stream_with_meta(
&'a self,
&self,
) -> Result<
// Wraps the FilterWatcher with a mapping to the event
EventStream<'a, FilterWatcher<'a, M::Provider, Log>, (D, LogMeta), ContractError<M>>,
EventStream<'_, FilterWatcher<'_, M::Provider, Log>, (D, LogMeta), ContractError<M>>,
ContractError<M>,
> {
let filter =
self.provider.watch(&self.filter).await.map_err(ContractError::MiddlewareError)?;
let filter = self
.provider
.borrow()
.watch(&self.filter)
.await
.map_err(ContractError::MiddlewareError)?;
Ok(EventStream::new(
filter.id,
filter,
Box::new(move |log| {
let meta = LogMeta::from(&log);
Ok((self.parse_log(log)?, meta))
Ok((parse_log(log)?, meta))
}),
))
}
}

impl<'a, M, D> Event<'a, M, D>
impl<B, M, D> Event<B, M, D>
where
B: Borrow<M>,
M: Middleware,
<M as Middleware>::Provider: PubsubClient,
D: EthLogDecode,
Expand All @@ -203,29 +232,31 @@ where
///
/// See also [Self::stream()].
pub async fn subscribe(
&'a self,
&self,
) -> Result<
// Wraps the SubscriptionStream with a mapping to the event
EventStream<'a, SubscriptionStream<'a, M::Provider, Log>, D, ContractError<M>>,
EventStream<'_, SubscriptionStream<'_, M::Provider, Log>, D, ContractError<M>>,
ContractError<M>,
> {
let filter = self
.provider
.borrow()
.subscribe_logs(&self.filter)
.await
.map_err(ContractError::MiddlewareError)?;
Ok(EventStream::new(filter.id, filter, Box::new(move |log| self.parse_log(log))))
Ok(EventStream::new(filter.id, filter, Box::new(move |log| Ok(parse_log(log)?))))
}

pub async fn subscribe_with_meta(
&'a self,
&self,
) -> Result<
// Wraps the SubscriptionStream with a mapping to the event
EventStream<'a, SubscriptionStream<'a, M::Provider, Log>, (D, LogMeta), ContractError<M>>,
EventStream<'_, SubscriptionStream<'_, M::Provider, Log>, (D, LogMeta), ContractError<M>>,
ContractError<M>,
> {
let filter = self
.provider
.borrow()
.subscribe_logs(&self.filter)
.await
.map_err(ContractError::MiddlewareError)?;
Expand All @@ -234,46 +265,51 @@ where
filter,
Box::new(move |log| {
let meta = LogMeta::from(&log);
Ok((self.parse_log(log)?, meta))
Ok((parse_log(log)?, meta))
}),
))
}
}

impl<M, D> Event<'_, M, D>
impl<B, M, D> Event<B, M, D>
where
B: Borrow<M>,
M: Middleware,
D: EthLogDecode,
{
/// Queries the blockchain for the selected filter and returns a vector of matching
/// event logs
pub async fn query(&self) -> Result<Vec<D>, ContractError<M>> {
let logs =
self.provider.get_logs(&self.filter).await.map_err(ContractError::MiddlewareError)?;
let logs = self
.provider
.borrow()
.get_logs(&self.filter)
.await
.map_err(ContractError::MiddlewareError)?;
let events = logs
.into_iter()
.map(|log| self.parse_log(log))
.map(|log| Ok(parse_log(log)?))
.collect::<Result<Vec<_>, ContractError<M>>>()?;
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<Vec<(D, LogMeta)>, ContractError<M>> {
let logs =
self.provider.get_logs(&self.filter).await.map_err(ContractError::MiddlewareError)?;
let logs = self
.provider
.borrow()
.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)?;
let event = parse_log(log)?;
Ok((event, meta))
})
.collect::<Result<_, ContractError<M>>>()?;
Ok(events)
}

pub fn parse_log(&self, log: Log) -> Result<D, ContractError<M>> {
D::decode_log(&RawLog { topics: log.topics, data: log.data.to_vec() }).map_err(From::from)
}
}
2 changes: 1 addition & 1 deletion ethers-contract/tests/it/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ mod eth_tests {

let anvil = Anvil::new().spawn();
let client = connect(&anvil, 0);
let event = ethers_contract::Contract::event_of_type::<AnswerUpdatedFilter>(&client);
let event = ethers_contract::Contract::event_of_type::<AnswerUpdatedFilter>(client);
assert_eq!(event.filter, Filter::new().event(&AnswerUpdatedFilter::abi_signature()));
}

Expand Down
4 changes: 2 additions & 2 deletions ethers-middleware/src/transformer/ds_proxy/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ mod dsproxyfactory_mod {
.expect("method not found (this should never happen)")
}
///Gets the contract's `Created` event
pub fn created_filter(&self) -> Event<M, CreatedFilter> {
pub fn created_filter(&self) -> Event<Arc<M>, M, CreatedFilter> {
self.0.event()
}

/// Returns an [`Event`](ethers_contract::builders::Event) builder for all events of this
/// contract
pub fn events(&self) -> Event<M, CreatedFilter> {
pub fn events(&self) -> Event<Arc<M>, M, CreatedFilter> {
self.0.event_with_filter(Default::default())
}
}
Expand Down
Loading