fix(abigen): generate correct imports depending on ethers crate usage (#413)
This commit is contained in:
parent
526617fcbd
commit
6cc20688d0
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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: ðers_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 ),*])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)?;
|
||||
|
||||
|
|
|
@ -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 ),*
|
||||
}
|
||||
|
|
|
@ -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 }),
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue