diff --git a/ethers-contract/ethers-contract-abigen/src/contract.rs b/ethers-contract/ethers-contract-abigen/src/contract.rs index 7ea18626..15d93190 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract.rs @@ -77,6 +77,10 @@ impl Context { // 5. Declare the structs parsed from the human readable abi let abi_structs_decl = cx.abi_structs()?; + let ethers_core = util::ethers_core_crate(); + let ethers_contract = util::ethers_contract_crate(); + let ethers_providers = util::ethers_providers_crate(); + Ok(quote! { // export all the created data types pub use #name_mod::*; @@ -86,12 +90,12 @@ impl Context { #imports #struct_decl - impl<'a, M: ethers_providers::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: ::std::sync::Arc) -> Self { - let contract = ethers_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 4529b1b3..a68fdc81 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/common.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/common.rs @@ -4,9 +4,15 @@ use ethers_core::types::Address; use proc_macro2::{Literal, TokenStream}; use quote::quote; +use super::util::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate}; + pub(crate) fn imports(name: &str) -> TokenStream { let doc = util::expand_doc(&format!("{} was auto-generated with ethers-rs Abigen. More information at: https://github.com/gakonst/ethers-rs", name)); + let ethers_core = ethers_core_crate(); + let ethers_providers = ethers_providers_crate(); + let ethers_contract = ethers_contract_crate(); + quote! { #![allow(clippy::enum_variant_names)] #![allow(dead_code)] @@ -14,15 +20,12 @@ pub(crate) fn imports(name: &str) -> TokenStream { #doc use std::sync::Arc; - use ethers::{ - core::{ - self as ethers_core, - abi::{Abi, Token, Detokenize, InvalidOutputType, Tokenizable}, - types::*, // import all the types so that we can codegen for everything - }, - contract::{self as ethers_contract, Contract, builders::{ContractCall, Event}, Lazy}, - providers::{self as ethers_providers,Middleware}, + use #ethers_core::{ + abi::{Abi, Token, Detokenize, InvalidOutputType, Tokenizable}, + types::*, // import all the types so that we can codegen for everything }; + use #ethers_contract::{Contract, builders::{ContractCall, Event}, Lazy}; + use #ethers_providers::Middleware; } } @@ -31,14 +34,18 @@ pub(crate) fn struct_declaration(cx: &Context, abi_name: &proc_macro2::Ident) -> let name = &cx.contract_name; let abi = &cx.abi_str; + let ethers_core = ethers_core_crate(); + let ethers_providers = ethers_providers_crate(); + let ethers_contract = ethers_contract_crate(); + let abi_parse = if !cx.human_readable { quote! { - pub static #abi_name: ethers_contract::Lazy = ethers_contract::Lazy::new(|| serde_json::from_str(#abi) + pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> = #ethers_contract::Lazy::new(|| serde_json::from_str(#abi) .expect("invalid abi")); } } else { quote! { - pub static #abi_name: ethers_contract::Lazy = ethers_contract::Lazy::new(|| ethers::core::abi::parse_abi_str(#abi) + pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> = #ethers_contract::Lazy::new(|| #ethers_core::abi::parse_abi_str(#abi) .expect("invalid abi")); } }; @@ -49,17 +56,17 @@ pub(crate) fn struct_declaration(cx: &Context, abi_name: &proc_macro2::Ident) -> // Struct declaration #[derive(Clone)] - pub struct #name(ethers_contract::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 = ethers_contract::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 6b5817b6..341bae4a 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/events.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/events.rs @@ -61,25 +61,28 @@ impl Context { let enum_name = self.expand_event_enum_name(); + let ethers_core = util::ethers_core_crate(); + let ethers_contract = util::ethers_contract_crate(); + quote! { #[derive(Debug, Clone, PartialEq, Eq)] pub enum #enum_name { #(#variants(#variants)),* } - impl ethers_core::abi::Tokenizable for #enum_name { + impl #ethers_core::abi::Tokenizable for #enum_name { - fn from_token(token: ethers_core::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_core::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_core::abi::Token { + fn into_token(self) -> #ethers_core::abi::Token { match self { #( #enum_name::#variants(element) => element.into_token() @@ -87,10 +90,10 @@ impl Context { } } } - impl ethers_core::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_core::abi::RawLog) -> Result + impl #ethers_contract::EthLogDecode for #enum_name { + fn decode_log(log: &#ethers_core::abi::RawLog) -> Result where Self: Sized, { @@ -99,7 +102,7 @@ impl Context { return Ok(#enum_name::#variants(decoded)) } )* - Err(ethers_core::abi::Error::InvalidData) + Err(#ethers_core::abi::Error::InvalidData) } } } @@ -115,6 +118,7 @@ impl Context { let sorted_events: BTreeMap<_, _> = self.abi.events.clone().into_iter().collect(); let mut iter = sorted_events.values().flatten(); + let ethers_contract = util::ethers_contract_crate(); if let Some(event) = iter.next() { let ty = if iter.next().is_some() { @@ -124,8 +128,8 @@ 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 { + /// 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()) } } @@ -142,6 +146,8 @@ impl Context { /// If a complex types matches with a struct previously parsed by the AbiParser, /// we can replace it fn expand_input_type(&self, input: &EventParam) -> Result { + let ethers_core = util::ethers_core_crate(); + Ok(match (&input.kind, input.indexed) { (ParamType::Array(ty), true) => { if let ParamType::Tuple(..) = **ty { @@ -156,7 +162,7 @@ impl Context { return Ok(quote! {::std::vec::Vec<#ty>}); } } - quote! { ethers_core::types::H256 } + quote! { #ethers_core::types::H256 } } (ParamType::FixedArray(ty, size), true) => { if let ParamType::Tuple(..) = **ty { @@ -172,7 +178,7 @@ impl Context { return Ok(quote! {[#ty; #size]}); } } - quote! { ethers_core::types::H256 } + quote! { #ethers_core::types::H256 } } (ParamType::Tuple(..), true) => { // represents an struct @@ -185,11 +191,11 @@ impl Context { { quote! {#ty} } else { - quote! { ethers_core::types::H256 } + quote! { #ethers_core::types::H256 } } } (ParamType::Bytes, true) | (ParamType::String, true) => { - quote! { ethers_core::types::H256 } + quote! { #ethers_core::types::H256 } } (kind, _) => types::expand(kind)?, }) @@ -213,6 +219,8 @@ impl Context { /// Expands into a single method for contracting an event stream. fn expand_filter(&self, event: &Event) -> TokenStream { + let ethers_contract = util::ethers_contract_crate(); + // append `filter` to disambiguate with potentially conflicting // function names let name = util::safe_ident(&format!("{}_filter", event.name.to_snake_case())); @@ -222,7 +230,7 @@ impl Context { let doc = util::expand_doc(&format!("Gets the contract's `{}` event", event.name)); quote! { #doc - pub fn #name(&self) -> ethers_contract::builders::Event { + pub fn #name(&self) -> #ethers_contract::builders::Event { self.0.event() } } @@ -247,8 +255,10 @@ impl Context { let abi_signature = event.abi_signature(); let event_abi_name = &event.name; + let ethers_contract = util::ethers_contract_crate(); + 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 }) @@ -351,9 +361,10 @@ fn expand_derives(derives: &[Path]) -> TokenStream { /// quasi-quoting for code generation. We do this to avoid allocating at runtime fn expand_hash(hash: Hash) -> TokenStream { let bytes = hash.as_bytes().iter().copied().map(Literal::u8_unsuffixed); + let ethers_core = util::ethers_core_crate(); quote! { - ethers_core::types::H256([#( #bytes ),*]) + #ethers_core::types::H256([#( #bytes ),*]) } } diff --git a/ethers-contract/ethers-contract-abigen/src/contract/methods.rs b/ethers-contract/ethers-contract-abigen/src/contract/methods.rs index f328b71d..608778e1 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/methods.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/methods.rs @@ -105,7 +105,11 @@ impl Context { // TODO use structs let outputs = expand_fn_outputs(&function.outputs)?; - let result = quote! { ethers_contract::builders::ContractCall }; + let ethers_core = util::ethers_core_crate(); + let ethers_providers = util::ethers_providers_crate(); + let ethers_contract = util::ethers_contract_crate(); + + let result = quote! { #ethers_contract::builders::ContractCall }; let (input, arg) = self.expand_inputs_call_arg_with_structs(function)?; diff --git a/ethers-contract/ethers-contract-abigen/src/contract/structs.rs b/ethers-contract/ethers-contract-abigen/src/contract/structs.rs index cc6bb164..8a1b4a1c 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/structs.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/structs.rs @@ -104,9 +104,10 @@ impl Context { let derives = &self.event_derives; let derives = quote! {#(#derives),*}; + let ethers_contract = util::ethers_contract_crate(); Ok(quote! { #abi_signature_doc - #[derive(Clone, Debug, Default, Eq, PartialEq, ethers::contract::EthAbiType, #derives)] + #[derive(Clone, Debug, Default, Eq, PartialEq, #ethers_contract::EthAbiType, #derives)] pub struct #name { #( #fields ),* } @@ -173,9 +174,11 @@ impl Context { let derives = &self.event_derives; let derives = quote! {#(#derives),*}; + let ethers_contract = util::ethers_contract_crate(); + structs.extend(quote! { #abi_signature_doc - #[derive(Clone, Debug, Default, Eq, PartialEq, ethers::contract::EthAbiType, #derives)] + #[derive(Clone, Debug, Default, Eq, PartialEq, #ethers_contract::EthAbiType, #derives)] pub struct #name { #( #fields ),* } diff --git a/ethers-contract/ethers-contract-abigen/src/contract/types.rs b/ethers-contract/ethers-contract-abigen/src/contract/types.rs index a1500c73..53679e6a 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/types.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/types.rs @@ -3,9 +3,13 @@ use ethers_core::abi::ParamType; use proc_macro2::{Literal, TokenStream}; use quote::quote; +use super::util; + pub(crate) fn expand(kind: &ParamType) -> Result { + let ethers_core = util::ethers_core_crate(); + match kind { - ParamType::Address => Ok(quote! { ethers_core::types::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 +26,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! { ethers_core::types::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-abigen/src/util.rs b/ethers-contract/ethers-contract-abigen/src/util.rs index 21db5948..33b61a31 100644 --- a/ethers-contract/ethers-contract-abigen/src/util.rs +++ b/ethers-contract/ethers-contract-abigen/src/util.rs @@ -12,7 +12,8 @@ use syn::{Ident as SynIdent, Path}; /// See `determine_ethers_crates` /// /// This ensures that the `MetadataCommand` is only run once -static ETHERS_CRATES: Lazy<(&'static str, &'static str)> = Lazy::new(determine_ethers_crates); +static ETHERS_CRATES: Lazy<(&'static str, &'static str, &'static str)> = + Lazy::new(determine_ethers_crates); /// Convenience function to turn the `ethers_core` name in `ETHERS_CRATE` into a `Path` pub fn ethers_core_crate() -> Path { @@ -22,6 +23,9 @@ pub fn ethers_core_crate() -> Path { pub fn ethers_contract_crate() -> Path { syn::parse_str(ETHERS_CRATES.1).expect("valid path; qed") } +pub fn ethers_providers_crate() -> Path { + syn::parse_str(ETHERS_CRATES.2).expect("valid path; qed") +} /// The crates name to use when deriving macros: (`core`, `contract`) /// @@ -34,8 +38,8 @@ pub fn ethers_contract_crate() -> Path { /// | ethers_contract`, we need to use the fitting crate ident when expand the /// macros This will attempt to parse the current `Cargo.toml` and check the /// ethers related dependencies. -pub fn determine_ethers_crates() -> (&'static str, &'static str) { - MetadataCommand::new() +pub fn determine_ethers_crates() -> (&'static str, &'static str, &'static str) { + let res = MetadataCommand::new() .manifest_path(&format!( "{}/Cargo.toml", std::env::var("CARGO_MANIFEST_DIR").expect("No Manifest found") @@ -44,16 +48,17 @@ pub fn determine_ethers_crates() -> (&'static str, &'static str) { .exec() .ok() .and_then(|metadata| { - metadata.root_package().and_then(|pkg| { - pkg.dependencies - .iter() - .filter(|dep| dep.kind == DependencyKind::Normal) - .find_map(|dep| { - (dep.name == "ethers").then(|| ("ethers::core", "ethers::contract")) - }) - }) + metadata.packages[0] + .dependencies + .iter() + .filter(|dep| dep.kind == DependencyKind::Normal) + .find_map(|dep| { + (dep.name == "ethers") + .then(|| ("ethers::core", "ethers::contract", "ethers::providers")) + }) }) - .unwrap_or(("ethers_core", "ethers_contract")) + .unwrap_or(("ethers_core", "ethers_contract", "ethers_providers")); + res } /// Expands a identifier string into an token.