diff --git a/ethers-contract/ethers-contract-abigen/src/contract.rs b/ethers-contract/ethers-contract-abigen/src/contract.rs index 02a3a582..bf0be544 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract.rs @@ -79,15 +79,14 @@ impl Context { #[allow(clippy::too_many_arguments)] mod #name_mod { #imports - #struct_decl - impl<'a, M: Middleware> #name { + impl<'a, M: ethers_providers::Middleware> #name { /// Creates a new contract instance with the specified `ethers` /// client at the given `Address`. The contract derefs to a `ethers::Contract` /// object - pub fn new>(address: T, client: Arc) -> Self { - let contract = Contract::new(address.into(), #abi_name.clone(), client); + pub fn new>(address: T, client: ::std::sync::Arc) -> Self { + let contract = ethers_contract::Contract::new(address.into(), #abi_name.clone(), client); Self(contract) } diff --git a/ethers-contract/ethers-contract-abigen/src/contract/common.rs b/ethers-contract/ethers-contract-abigen/src/contract/common.rs index 49da608f..4efb03ce 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/common.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/common.rs @@ -31,12 +31,12 @@ pub(crate) fn struct_declaration(cx: &Context, abi_name: &proc_macro2::Ident) -> let abi_parse = if !cx.human_readable { quote! { - pub static #abi_name: Lazy = Lazy::new(|| serde_json::from_str(#abi) + pub static #abi_name: ethers_contract::Lazy = ethers_contract::Lazy::new(|| serde_json::from_str(#abi) .expect("invalid abi")); } } else { quote! { - pub static #abi_name: Lazy = Lazy::new(|| ethers::core::abi::parse_abi_str(#abi) + pub static #abi_name: ethers_contract::Lazy = ethers_contract::Lazy::new(|| ethers::core::abi::parse_abi_str(#abi) .expect("invalid abi")); } }; @@ -47,17 +47,17 @@ pub(crate) fn struct_declaration(cx: &Context, abi_name: &proc_macro2::Ident) -> // Struct declaration #[derive(Clone)] - pub struct #name(Contract); + pub struct #name(ethers_contract::Contract); // Deref to the inner contract in order to access more specific functions functions impl std::ops::Deref for #name { - type Target = Contract; + type Target = ethers_contract::Contract; fn deref(&self) -> &Self::Target { &self.0 } } - impl std::fmt::Debug for #name { + impl std::fmt::Debug for #name { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_tuple(stringify!(#name)) .field(&self.address()) diff --git a/ethers-contract/ethers-contract-abigen/src/contract/events.rs b/ethers-contract/ethers-contract-abigen/src/contract/events.rs index 179f7b7b..7c5d2f2f 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/events.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/events.rs @@ -63,19 +63,19 @@ impl Context { #(#variants(#variants)),* } - impl ethers::abi::Tokenizable for #enum_name { + impl ethers_core::abi::Tokenizable for #enum_name { - fn from_token(token: ethers::abi::Token) -> Result where + fn from_token(token: ethers_core::abi::Token) -> Result where Self: Sized { #( if let Ok(decoded) = #variants::from_token(token.clone()) { return Ok(#enum_name::#variants(decoded)) } )* - Err(ethers::abi::InvalidOutputType("Failed to decode all event variants".to_string())) + Err(ethers_core::abi::InvalidOutputType("Failed to decode all event variants".to_string())) } - fn into_token(self) -> ethers::abi::Token { + fn into_token(self) -> ethers_core::abi::Token { match self { #( #enum_name::#variants(element) => element.into_token() @@ -83,10 +83,10 @@ impl Context { } } } - impl ethers::abi::TokenizableItem for #enum_name { } + impl ethers_core::abi::TokenizableItem for #enum_name { } - impl ethers::contract::EthLogDecode for #enum_name { - fn decode_log(log: ðers::abi::RawLog) -> Result + impl ethers_contract::EthLogDecode for #enum_name { + fn decode_log(log: ðers_core::abi::RawLog) -> Result where Self: Sized, { @@ -95,7 +95,7 @@ impl Context { return Ok(#enum_name::#variants(decoded)) } )* - Err(ethers::abi::Error::InvalidData) + Err(ethers_core::abi::Error::InvalidData) } } } @@ -146,7 +146,7 @@ impl Context { return Ok(quote! {::std::vec::Vec<#ty>}); } } - quote! { H256 } + quote! { ethers_core::types::H256 } } (ParamType::FixedArray(ty, size), true) => { if let ParamType::Tuple(..) = **ty { @@ -162,7 +162,7 @@ impl Context { return Ok(quote! {[#ty; #size]}); } } - quote! { H256 } + quote! { ethers_core::types::H256 } } (ParamType::Tuple(..), true) => { // represents an struct @@ -175,11 +175,11 @@ impl Context { { quote! {#ty} } else { - quote! { H256 } + quote! { ethers_core::types::H256 } } } (ParamType::Bytes, true) | (ParamType::String, true) => { - quote! { H256 } + quote! { ethers_core::types::H256 } } (kind, _) => types::expand(kind)?, }) @@ -213,7 +213,7 @@ impl Context { let doc = util::expand_doc(&format!("Gets the contract's `{}` event", event.name)); quote! { #doc - pub fn #name(&self) -> Event { + pub fn #name(&self) -> ethers_contract::builders::Event { self.0.event(#ev_name).expect("event not found (this should never happen)") } } @@ -239,7 +239,7 @@ impl Context { let event_abi_name = &event.name; Ok(quote! { - #[derive(Clone, Debug, Default, Eq, PartialEq, ethers::contract::EthEvent, #derives)] + #[derive(Clone, Debug, Default, Eq, PartialEq, ethers_contract::EthEvent, #derives)] #[ethevent( name = #event_abi_name, abi = #abi_signature )] pub #data_type_definition }) @@ -335,7 +335,7 @@ fn expand_hash(hash: Hash) -> TokenStream { let bytes = hash.as_bytes().iter().copied().map(Literal::u8_unsuffixed); quote! { - H256([#( #bytes ),*]) + ethers_core::types::H256([#( #bytes ),*]) } } @@ -452,7 +452,7 @@ mod tests { "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f".parse().unwrap() ), { - H256([ + ethers_core::types::H256([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 ]) diff --git a/ethers-contract/ethers-contract-abigen/src/contract/methods.rs b/ethers-contract/ethers-contract-abigen/src/contract/methods.rs index 4c15d6d7..141c2b3b 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/methods.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/methods.rs @@ -39,7 +39,7 @@ fn expand_function(function: &Function, alias: Option) -> Result }; + let result = quote! { ethers_contract::builders::ContractCall }; let arg = expand_inputs_call_arg(&function.inputs); let doc = util::expand_doc(&format!( diff --git a/ethers-contract/ethers-contract-abigen/src/contract/types.rs b/ethers-contract/ethers-contract-abigen/src/contract/types.rs index 30efaac2..a1500c73 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/types.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/types.rs @@ -5,7 +5,7 @@ use quote::quote; pub(crate) fn expand(kind: &ParamType) -> Result { match kind { - ParamType::Address => Ok(quote! { Address }), + ParamType::Address => Ok(quote! { ethers_core::types::Address }), ParamType::Bytes => Ok(quote! { Vec }), ParamType::Int(n) => match n / 8 { 1 => Ok(quote! { i8 }), @@ -22,7 +22,7 @@ pub(crate) fn expand(kind: &ParamType) -> Result { 3..=4 => Ok(quote! { u32 }), 5..=8 => Ok(quote! { u64 }), 9..=16 => Ok(quote! { u128 }), - 17..=32 => Ok(quote! { U256 }), + 17..=32 => Ok(quote! { ethers_core::types::U256 }), _ => Err(anyhow!("unsupported solidity type uint{}", n)), }, ParamType::Bool => Ok(quote! { bool }), diff --git a/ethers-contract/ethers-contract-derive/src/lib.rs b/ethers-contract/ethers-contract-derive/src/lib.rs index 5a056528..6f9e5f2c 100644 --- a/ethers-contract/ethers-contract-derive/src/lib.rs +++ b/ethers-contract/ethers-contract-derive/src/lib.rs @@ -206,13 +206,13 @@ pub fn derive_abi_event(input: TokenStream) -> TokenStream { let anon = attributes.anonymous.map(|(b, _)| b).unwrap_or_default(); let ethevent_impl = quote! { - impl ethers::contract::EthEvent for #name { + impl ethers_contract::EthEvent for #name { fn name() -> ::std::borrow::Cow<'static, str> { #event_name.into() } - fn signature() -> ethers::types::H256 { + fn signature() -> ethers_core::types::H256 { #signature } @@ -220,7 +220,7 @@ pub fn derive_abi_event(input: TokenStream) -> TokenStream { #abi.into() } - fn decode_log(log: ðers::abi::RawLog) -> Result where Self: Sized { + fn decode_log(log: ðers_core::abi::RawLog) -> Result where Self: Sized { #decode_log_impl } @@ -261,7 +261,7 @@ fn topic_param_type_quote(kind: &ParamType) -> proc_macro2::TokenStream { | ParamType::Bytes | ParamType::Array(_) | ParamType::FixedArray(_, _) - | ParamType::Tuple(_) => quote! {ethers::abi::ParamType::FixedBytes(32)}, + | ParamType::Tuple(_) => quote! {ethers_core::abi::ParamType::FixedBytes(32)}, ty => param_type_quote(ty), } } @@ -269,42 +269,42 @@ fn topic_param_type_quote(kind: &ParamType) -> proc_macro2::TokenStream { fn param_type_quote(kind: &ParamType) -> proc_macro2::TokenStream { match kind { ParamType::Address => { - quote! {ethers::abi::ParamType::Address} + quote! {ethers_core::abi::ParamType::Address} } ParamType::Bytes => { - quote! {ethers::abi::ParamType::Bytes} + quote! {ethers_core::abi::ParamType::Bytes} } ParamType::Int(size) => { let size = Literal::usize_suffixed(*size); - quote! {ethers::abi::ParamType::Int(#size)} + quote! {ethers_core::abi::ParamType::Int(#size)} } ParamType::Uint(size) => { let size = Literal::usize_suffixed(*size); - quote! {ethers::abi::ParamType::Uint(#size)} + quote! {ethers_core::abi::ParamType::Uint(#size)} } ParamType::Bool => { - quote! {ethers::abi::ParamType::Bool} + quote! {ethers_core::abi::ParamType::Bool} } ParamType::String => { - quote! {ethers::abi::ParamType::String} + quote! {ethers_core::abi::ParamType::String} } ParamType::Array(ty) => { let ty = param_type_quote(&*ty); - quote! {ethers::abi::ParamType::Array(Box::new(#ty))} + quote! {ethers_core::abi::ParamType::Array(Box::new(#ty))} } ParamType::FixedBytes(size) => { let size = Literal::usize_suffixed(*size); - quote! {ethers::abi::ParamType::FixedBytes(#size)} + quote! {ethers_core::abi::ParamType::FixedBytes(#size)} } ParamType::FixedArray(ty, size) => { let ty = param_type_quote(&*ty); let size = Literal::usize_suffixed(*size); - quote! {ethers::abi::ParamType::FixedArray(Box::new(#ty),#size)} + quote! {ethers_core::abi::ParamType::FixedArray(Box::new(#ty),#size)} } ParamType::Tuple(tuple) => { let elements = tuple.iter().map(param_type_quote); quote! { - ethers::abi::ParamType::Tuple( + ethers_core::abi::ParamType::Tuple( vec![ #( #elements ),* ] @@ -424,16 +424,16 @@ fn derive_decode_from_log_impl( }, quote! { if topic_tokens.len() != topics.len() { - return Err(ethers::abi::Error::InvalidData); + return Err(ethers_core::abi::Error::InvalidData); } }, ) } else { ( quote! { - let event_signature = topics.get(0).ok_or(ethers::abi::Error::InvalidData)?; + let event_signature = topics.get(0).ok_or(ethers_core::abi::Error::InvalidData)?; if event_signature != &Self::signature() { - return Err(ethers::abi::Error::InvalidData); + return Err(ethers_core::abi::Error::InvalidData); } }, quote! { @@ -441,7 +441,7 @@ fn derive_decode_from_log_impl( }, quote! { if topic_tokens.is_empty() || topic_tokens.len() != topics.len() - 1 { - return Err(ethers::abi::Error::InvalidData); + return Err(ethers_core::abi::Error::InvalidData); } }, ) @@ -455,9 +455,9 @@ fn derive_decode_from_log_impl( .all(|(idx, f)| f.index == idx) { quote! { - let topic_tokens = ethers::abi::decode(&topic_types, &flat_topics)?; + let topic_tokens = ethers_core::abi::decode(&topic_types, &flat_topics)?; #topic_tokens_len_check - let data_tokens = ethers::abi::decode(&data_types, &data)?; + let data_tokens = ethers_core::abi::decode(&data_types, &data)?; let tokens:Vec<_> = topic_tokens.into_iter().chain(data_tokens.into_iter()).collect(); } } else { @@ -470,9 +470,9 @@ fn derive_decode_from_log_impl( }); quote! { - let mut topic_tokens = ethers::abi::decode(&topic_types, &flat_topics)?; + let mut topic_tokens = ethers_core::abi::decode(&topic_types, &flat_topics)?; #topic_tokens_len_check - let mut data_tokens = ethers::abi::decode(&data_types, &data)?; + let mut data_tokens = ethers_core::abi::decode(&data_types, &data)?; let mut tokens = Vec::with_capacity(topics.len() + data_tokens.len()); #( tokens.push(#swap_tokens); )* } @@ -480,7 +480,7 @@ fn derive_decode_from_log_impl( Ok(quote! { - let ethers::abi::RawLog {data, topics} = log; + let ethers_core::abi::RawLog {data, topics} = log; #signature_check @@ -491,7 +491,7 @@ fn derive_decode_from_log_impl( #tokens_init - ethers::abi::Detokenize::from_tokens(tokens).map_err(|_|ethers::abi::Error::InvalidData) + ethers_core::abi::Detokenize::from_tokens(tokens).map_err(|_|ethers_core::abi::Error::InvalidData) }) } @@ -678,7 +678,7 @@ fn parse_int_param_type(s: &str) -> Option { fn signature(hash: &[u8]) -> proc_macro2::TokenStream { let bytes = hash.iter().copied().map(Literal::u8_unsuffixed); - quote! {ethers::types::H256([#( #bytes ),*])} + quote! {ethers_core::types::H256([#( #bytes ),*])} } fn parse_event(abi: &str) -> Result { @@ -727,13 +727,13 @@ fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream { Fields::Named(ref fields) => { let tokenize_predicates = fields.named.iter().map(|f| { let ty = &f.ty; - quote_spanned! { f.span() => #ty: ethers::abi::Tokenize } + quote_spanned! { f.span() => #ty: ethers_core::abi::Tokenize } }); let tokenize_predicates = quote! { #(#tokenize_predicates,)* }; let assignments = fields.named.iter().map(|f| { let name = f.ident.as_ref().expect("Named fields have names"); - quote_spanned! { f.span() => #name: ethers::abi::Tokenizable::from_token(iter.next().expect("tokens size is sufficient qed").into_token())? } + quote_spanned! { f.span() => #name: ethers_core::abi::Tokenizable::from_token(iter.next().expect("tokens size is sufficient qed").into_token())? } }); let init_struct_impl = quote! { Self { #(#assignments,)* } }; @@ -753,12 +753,12 @@ fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream { Fields::Unnamed(ref fields) => { let tokenize_predicates = fields.unnamed.iter().map(|f| { let ty = &f.ty; - quote_spanned! { f.span() => #ty: ethers::abi::Tokenize } + quote_spanned! { f.span() => #ty: ethers_core::abi::Tokenize } }); let tokenize_predicates = quote! { #(#tokenize_predicates,)* }; let assignments = fields.unnamed.iter().map(|f| { - quote_spanned! { f.span() => ethers::abi::Tokenizable::from_token(iter.next().expect("tokens size is sufficient qed").into_token())? } + quote_spanned! { f.span() => ethers_core::abi::Tokenizable::from_token(iter.next().expect("tokens size is sufficient qed").into_token())? } }); let init_struct_impl = quote! { Self(#(#assignments,)* ) }; @@ -794,17 +794,17 @@ fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream { }; quote! { - impl<#generic_params> ethers::abi::Tokenizable for #name<#generic_args> + impl<#generic_params> ethers_core::abi::Tokenizable for #name<#generic_args> where #generic_predicates #tokenize_predicates { - fn from_token(token: ethers::abi::Token) -> Result where + fn from_token(token: ethers_core::abi::Token) -> Result where Self: Sized { - if let ethers::abi::Token::Tuple(tokens) = token { + if let ethers_core::abi::Token::Tuple(tokens) = token { if tokens.len() != #params_len { - return Err(ethers::abi::InvalidOutputType(format!( + return Err(ethers_core::abi::InvalidOutputType(format!( "Expected {} tokens, got {}: {:?}", #params_len, tokens.len(), @@ -816,15 +816,15 @@ fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream { Ok(#init_struct_impl) } else { - Err(ethers::abi::InvalidOutputType(format!( + Err(ethers_core::abi::InvalidOutputType(format!( "Expected Tuple, got {:?}", token ))) } } - fn into_token(self) -> ethers::abi::Token { - ethers::abi::Token::Tuple( + fn into_token(self) -> ethers_core::abi::Token { + ethers_core::abi::Token::Tuple( vec![ #into_token_impl ] @@ -832,7 +832,7 @@ fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream { } } - impl<#generic_params> ethers::abi::TokenizableItem for #name<#generic_args> + impl<#generic_params> ethers_core::abi::TokenizableItem for #name<#generic_args> where #generic_predicates #tokenize_predicates diff --git a/ethers-contract/src/contract.rs b/ethers-contract/src/contract.rs index 8aa3fddd..878ce4c1 100644 --- a/ethers-contract/src/contract.rs +++ b/ethers-contract/src/contract.rs @@ -180,6 +180,7 @@ 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> { // get the event's full name let event = self.base_contract.abi.event(name)?; diff --git a/ethers-contract/src/event.rs b/ethers-contract/src/event.rs index d5a5af1e..5fcbb1d2 100644 --- a/ethers-contract/src/event.rs +++ b/ethers-contract/src/event.rs @@ -29,6 +29,19 @@ pub trait EthEvent: Detokenize { /// Returns true if this is an anonymous event 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 + where + Self: Sized, + { + let filter = filter.event(&Self::abi_signature()); + Event2 { + filter, + provider, + datatype: PhantomData, + } + } } // Convenience implementation @@ -41,6 +54,167 @@ 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> { + /// The event filter's state + pub filter: Filter, + pub(crate) provider: &'a M, + /// Stores the event datatype + pub(crate) datatype: PhantomData, +} + +// TODO: Improve these functions +impl Event2<'_, 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, M, D> Event2<'a, M, D> +where + M: Middleware, + D: EthLogDecode, +{ + /// 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, M, D> Event2<'a, M, D> +where + M: Middleware, + ::Provider: PubsubClient, + D: EthLogDecode, +{ + /// 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 Event2<'_, M, D> +where + 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, 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> { + D::decode_log(&RawLog { + topics: log.topics, + data: log.data.to_vec(), + }) + .map_err(From::from) + } +} + /// 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"]