fix(abigen): generate correct imports depending on ethers crate usage (#413)

This commit is contained in:
Georgios Konstantopoulos 2021-08-28 21:53:01 +03:00 committed by GitHub
parent 526617fcbd
commit 6cc20688d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 88 additions and 50 deletions

View File

@ -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<M> {
impl<'a, M: #ethers_providers::Middleware> #name<M> {
/// 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<T: Into<ethers_core::types::Address>>(address: T, client: ::std::sync::Arc<M>) -> Self {
let contract = ethers_contract::Contract::new(address.into(), #abi_name.clone(), client);
pub fn new<T: Into<#ethers_core::types::Address>>(address: T, client: ::std::sync::Arc<M>) -> Self {
let contract = #ethers_contract::Contract::new(address.into(), #abi_name.clone(), client);
Self(contract)
}

View File

@ -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_core::abi::Abi> = 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_core::abi::Abi> = 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<M>(ethers_contract::Contract<M>);
pub struct #name<M>(#ethers_contract::Contract<M>);
// Deref to the inner contract in order to access more specific functions functions
impl<M> std::ops::Deref for #name<M> {
type Target = ethers_contract::Contract<M>;
type Target = #ethers_contract::Contract<M>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl<M: ethers_providers::Middleware> std::fmt::Debug for #name<M> {
impl<M: #ethers_providers::Middleware> std::fmt::Debug for #name<M> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_tuple(stringify!(#name))
.field(&self.address())

View File

@ -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<Self, ethers_core::abi::InvalidOutputType> where
fn from_token(token: #ethers_core::abi::Token) -> Result<Self, #ethers_core::abi::InvalidOutputType> 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: &ethers_core::abi::RawLog) -> Result<Self, ethers_core::abi::Error>
impl #ethers_contract::EthLogDecode for #enum_name {
fn decode_log(log: &#ethers_core::abi::RawLog) -> Result<Self, #ethers_core::abi::Error>
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<M, #ty> {
/// Returns an [`Event`](#ethers_contract::builders::Event) builder for all events of this contract
pub fn events(&self) -> #ethers_contract::builders::Event<M, #ty> {
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<TokenStream> {
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<M, #result> {
pub fn #name(&self) -> #ethers_contract::builders::Event<M, #result> {
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 ),*])
}
}

View File

@ -105,7 +105,11 @@ impl Context {
// TODO use structs
let outputs = expand_fn_outputs(&function.outputs)?;
let result = quote! { ethers_contract::builders::ContractCall<M, #outputs> };
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<M, #outputs> };
let (input, arg) = self.expand_inputs_call_arg_with_structs(function)?;

View File

@ -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 ),*
}

View File

@ -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<TokenStream> {
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<u8> }),
ParamType::Int(n) => match n / 8 {
1 => Ok(quote! { i8 }),
@ -22,7 +26,7 @@ pub(crate) fn expand(kind: &ParamType) -> Result<TokenStream> {
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 }),

View File

@ -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.