macro: re-enable paths/etherscan and enable more complex tokens

This commit is contained in:
Georgios Konstantopoulos 2020-06-04 00:05:05 +03:00
parent ba7fedc7d3
commit 73b502ed5f
No known key found for this signature in database
GPG Key ID: FA607837CD26EDBC
8 changed files with 63 additions and 67 deletions

View File

@ -49,8 +49,10 @@ jobs:
- setup-lints - setup-lints
- setup-sccache - setup-sccache
- restore-sccache-cache - restore-sccache-cache
# skip these temporarily until we get ganache-cli and solc on CI - run:
- run: cargo test --all -- --skip deploy_and_call_contract --skip send_eth name: tests
# skip these temporarily until we get ganache-cli and solc on CI
command: cargo test --all -- --skip deploy_and_call_contract --skip send_eth
- run: - run:
name: Check style name: Check style
command: | command: |

View File

@ -90,7 +90,7 @@ fn expand_event(event: &Event, event_derives: &[Path]) -> Result<TokenStream> {
.iter() .iter()
.map(|(name, _)| { .map(|(name, _)| {
quote! { quote! {
let #name = Detokenize::from_token(tokens.next().expect("this should never happen"))?; let #name = Tokenizable::from_token(tokens.next().expect("this should never happen"))?;
} }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View File

@ -27,8 +27,7 @@ use anyhow::Result;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use std::{collections::HashMap, fs::File, io::Write, path::Path}; use std::{collections::HashMap, fs::File, io::Write, path::Path};
/// Internal global arguments passed to the generators for each individual /// Builder struct for generating type-safe bindings from a contract's ABI
/// component that control expansion.
pub struct Abigen { pub struct Abigen {
/// The source of the ABI JSON for the contract whose bindings /// The source of the ABI JSON for the contract whose bindings
/// are being generated. /// are being generated.

View File

@ -2,16 +2,16 @@
//! ethereum smart contract. //! ethereum smart contract.
use crate::spanned::{ParseInner, Spanned}; use crate::spanned::{ParseInner, Spanned};
use ethers_contract_abigen::Builder; use ethers_contract_abigen::Abigen;
use ethers_core::abi::{Function, FunctionExt, Param}; use ethers_core::abi::{Function, FunctionExt, Param};
use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens as _}; use quote::ToTokens;
use std::collections::HashSet; use std::collections::HashSet;
use std::error::Error; use std::error::Error;
use syn::ext::IdentExt; use syn::ext::IdentExt;
use syn::parse::{Error as ParseError, Parse, ParseStream, Result as ParseResult}; use syn::parse::{Error as ParseError, Parse, ParseStream, Result as ParseResult};
use syn::{braced, parenthesized, Ident, LitStr, Path, Token, Visibility}; use syn::{braced, parenthesized, Ident, LitStr, Path, Token};
pub(crate) fn expand(args: ContractArgs) -> Result<TokenStream2, Box<dyn Error>> { pub(crate) fn expand(args: ContractArgs) -> Result<TokenStream2, Box<dyn Error>> {
Ok(args.into_builder()?.generate()?.into_tokens()) Ok(args.into_builder()?.generate()?.into_tokens())
@ -20,21 +20,17 @@ pub(crate) fn expand(args: ContractArgs) -> Result<TokenStream2, Box<dyn Error>>
/// Contract procedural macro arguments. /// Contract procedural macro arguments.
#[cfg_attr(test, derive(Debug, Eq, PartialEq))] #[cfg_attr(test, derive(Debug, Eq, PartialEq))]
pub(crate) struct ContractArgs { pub(crate) struct ContractArgs {
visibility: Option<String>,
name: String, name: String,
path: String, abi: String,
parameters: Vec<Parameter>, parameters: Vec<Parameter>,
} }
impl ContractArgs { impl ContractArgs {
fn into_builder(self) -> Result<Builder, Box<dyn Error>> { fn into_builder(self) -> Result<Abigen, Box<dyn Error>> {
let mut builder = let mut builder = Abigen::new(&self.name, &self.abi)?;
Builder::from_str(&self.name, &self.path).visibility_modifier(self.visibility);
for parameter in self.parameters.into_iter() { for parameter in self.parameters.into_iter() {
builder = match parameter { builder = match parameter {
Parameter::Mod(name) => builder.contract_mod_override(Some(name)),
Parameter::Crate(name) => builder.runtime_crate_name(name),
Parameter::Methods(methods) => methods.into_iter().fold(builder, |builder, m| { Parameter::Methods(methods) => methods.into_iter().fold(builder, |builder, m| {
builder.add_method_alias(m.signature, m.alias) builder.add_method_alias(m.signature, m.alias)
}), }),
@ -50,12 +46,6 @@ impl ContractArgs {
impl ParseInner for ContractArgs { impl ParseInner for ContractArgs {
fn spanned_parse(input: ParseStream) -> ParseResult<(Span, Self)> { fn spanned_parse(input: ParseStream) -> ParseResult<(Span, Self)> {
// read the visibility parameter
let visibility = match input.parse::<Visibility>()? {
Visibility::Inherited => None,
token => Some(quote!(#token).to_string()),
};
// read the contract name // read the contract name
let name = input.parse::<Ident>()?.to_string(); let name = input.parse::<Ident>()?.to_string();
@ -67,7 +57,7 @@ impl ParseInner for ContractArgs {
// therefore, the path will always be rooted on the cargo manifest // therefore, the path will always be rooted on the cargo manifest
// directory. Eventually we can use the `Span::source_file` API to // directory. Eventually we can use the `Span::source_file` API to
// have a better experience. // have a better experience.
let (span, path) = { let (span, abi) = {
let literal = input.parse::<LitStr>()?; let literal = input.parse::<LitStr>()?;
(literal.span(), literal.value()) (literal.span(), literal.value())
}; };
@ -84,9 +74,8 @@ impl ParseInner for ContractArgs {
Ok(( Ok((
span, span,
ContractArgs { ContractArgs {
visibility,
name, name,
path, abi,
parameters, parameters,
}, },
)) ))
@ -96,8 +85,6 @@ impl ParseInner for ContractArgs {
/// A single procedural macro parameter. /// A single procedural macro parameter.
#[cfg_attr(test, derive(Debug, Eq, PartialEq))] #[cfg_attr(test, derive(Debug, Eq, PartialEq))]
enum Parameter { enum Parameter {
Mod(String),
Crate(String),
Methods(Vec<Method>), Methods(Vec<Method>),
EventDerives(Vec<String>), EventDerives(Vec<String>),
} }
@ -106,16 +93,6 @@ impl Parse for Parameter {
fn parse(input: ParseStream) -> ParseResult<Self> { fn parse(input: ParseStream) -> ParseResult<Self> {
let name = input.call(Ident::parse_any)?; let name = input.call(Ident::parse_any)?;
let param = match name.to_string().as_str() { let param = match name.to_string().as_str() {
"crate" => {
input.parse::<Token![=]>()?;
let name = input.call(Ident::parse_any)?.to_string();
Parameter::Crate(name)
}
"mod" => {
input.parse::<Token![=]>()?;
let name = input.parse::<Ident>()?.to_string();
Parameter::Mod(name)
}
"methods" => { "methods" => {
let content; let content;
braced!(content in input); braced!(content in input);

View File

@ -16,7 +16,7 @@ use syn::{parse::Error, parse_macro_input};
/// the crate's root `CARGO_MANIFEST_DIR`. /// the crate's root `CARGO_MANIFEST_DIR`.
/// ///
/// ```ignore /// ```ignore
/// ethcontract::contract!("build/contracts/MyContract.json"); /// abigen!(MyContract, "MyContract.json");
/// ``` /// ```
/// ///
/// Alternatively, other sources may be used, for full details consult the /// Alternatively, other sources may be used, for full details consult the
@ -24,12 +24,12 @@ use syn::{parse::Error, parse_macro_input};
/// ///
/// ```ignore /// ```ignore
/// // HTTP(S) source /// // HTTP(S) source
/// ethcontract::contract!("https://my.domain.local/path/to/contract.json") /// abigen!(MyContract, "https://my.domain.local/path/to/contract.json")
/// // Etherscan.io /// // Etherscan.io
/// ethcontract::contract!("etherscan:0x0001020304050607080910111213141516171819"); /// abigen!(MyContract, "etherscan:0x0001020304050607080910111213141516171819");
/// ethcontract::contract!("https://etherscan.io/address/0x0001020304050607080910111213141516171819"); /// abigen!(MyContract, "https://etherscan.io/address/0x0001020304050607080910111213141516171819");
/// // npmjs /// // npmjs
/// ethcontract::contract!("npm:@org/package@1.0.0/path/to/contract.json") /// abigen!(MyContract, "npm:@org/package@1.0.0/path/to/contract.json")
/// ``` /// ```
/// ///
/// Note that Etherscan rate-limits requests to their API, to avoid this an /// Note that Etherscan rate-limits requests to their API, to avoid this an
@ -38,15 +38,6 @@ use syn::{parse::Error, parse_macro_input};
/// ///
/// Currently the proc macro accepts additional parameters to configure some /// Currently the proc macro accepts additional parameters to configure some
/// aspects of the code generation. Specifically it accepts: /// aspects of the code generation. Specifically it accepts:
/// - `crate`: The name of the `ethcontract` crate. This is useful if the crate
/// was renamed in the `Cargo.toml` for whatever reason.
/// - `contract`: Override the contract name that is used for the generated
/// type. This is required when using sources that do not provide the contract
/// name in the artifact JSON such as Etherscan.
/// - `mod`: The name of the contract module to place generated code in. Note
/// that the root contract type gets re-exported in the context where the
/// macro was invoked. This defaults to the contract name converted into snake
/// case.
/// - `methods`: A list of mappings from method signatures to method names /// - `methods`: A list of mappings from method signatures to method names
/// allowing methods names to be explicitely set for contract methods. This /// allowing methods names to be explicitely set for contract methods. This
/// also provides a workaround for generating code for contracts with multiple /// also provides a workaround for generating code for contracts with multiple
@ -54,18 +45,10 @@ use syn::{parse::Error, parse_macro_input};
/// - `event_derives`: A list of additional derives that should be added to /// - `event_derives`: A list of additional derives that should be added to
/// contract event structs and enums. /// contract event structs and enums.
/// ///
/// Additionally, the ABI source can be preceeded by a visibility modifier such
/// as `pub` or `pub(crate)`. This visibility modifier is applied to both the
/// generated module and contract re-export. If no visibility modifier is
/// provided, then none is used for the generated code as well, making the
/// module and contract private to the scope where the macro was invoked.
///
/// ```ignore /// ```ignore
/// ethcontract::contract!( /// abigen!(
/// pub(crate) "build/contracts/MyContract.json", /// MyContractInstance,
/// crate = ethcontract_rename, /// "build/contracts/MyContract.json",
/// mod = my_contract_instance,
/// contract = MyContractInstance,
/// methods { /// methods {
/// myMethod(uint256,bool) as my_renamed_method; /// myMethod(uint256,bool) as my_renamed_method;
/// }, /// },
@ -73,7 +56,7 @@ use syn::{parse::Error, parse_macro_input};
/// ); /// );
/// ``` /// ```
/// ///
/// See [`ethcontract`](ethcontract) module level documentation for additional /// See [`ethers-contract-abigen`](ethers-contract-abigen) module level documentation for additional
/// information. /// information.
#[proc_macro] #[proc_macro]
pub fn abigen(input: TokenStream) -> TokenStream { pub fn abigen(input: TokenStream) -> TokenStream {

View File

@ -11,7 +11,7 @@ mod factory;
pub use factory::ContractFactory; pub use factory::ContractFactory;
#[cfg(feature = "abigen")] #[cfg(feature = "abigen")]
pub use ethers_contract_abigen::Builder; pub use ethers_contract_abigen::Abigen;
#[cfg(feature = "abigen")] #[cfg(feature = "abigen")]
pub use ethers_contract_derive::abigen; pub use ethers_contract_derive::abigen;

View File

@ -68,7 +68,7 @@ impl_output!(1, A,);
impl_output!(2, A, B,); impl_output!(2, A, B,);
impl_output!(3, A, B, C,); impl_output!(3, A, B, C,);
impl_output!(4, A, B, C, D,); impl_output!(4, A, B, C, D,);
// impl_output!(5, A, B, C, D, E,); impl_output!(5, A, B, C, D, E,);
// impl_output!(6, A, B, C, D, E, F,); // impl_output!(6, A, B, C, D, E, F,);
// impl_output!(7, A, B, C, D, E, F, G,); // impl_output!(7, A, B, C, D, E, F, G,);
// impl_output!(8, A, B, C, D, E, F, G, H,); // impl_output!(8, A, B, C, D, E, F, G, H,);
@ -125,11 +125,11 @@ impl_tokens!(A:0, );
impl_tokens!(A:0, B:1, ); impl_tokens!(A:0, B:1, );
impl_tokens!(A:0, B:1, C:2, ); impl_tokens!(A:0, B:1, C:2, );
impl_tokens!(A:0, B:1, C:2, D:3, ); impl_tokens!(A:0, B:1, C:2, D:3, );
impl_tokens!(A:0, B:1, C:2, D:3, E:4, );
impl_tokens!(A:0, B:1, C:2, D:3, E:4, F:5, );
impl_tokens!(A:0, B:1, C:2, D:3, E:4, F:5, G:6, );
// Commented out macros to reduce codegen time. Re-enable if needed. // Commented out macros to reduce codegen time. Re-enable if needed.
// impl_tokens!(A:0, B:1, C:2, D:3, E:4, );
// impl_tokens!(A:0, B:1, C:2, D:3, E:4, F:5, );
// impl_tokens!(A:0, B:1, C:2, D:3, E:4, F:5, G:6, );
// impl_tokens!(A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7, ); // impl_tokens!(A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7, );
// impl_tokens!(A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7, I:8, ); // impl_tokens!(A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7, I:8, );
// impl_tokens!(A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7, I:8, J:9, ); // impl_tokens!(A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7, I:8, J:9, );

View File

@ -0,0 +1,35 @@
// This test exists to ensure that the abigen macro works "reasonably" well with popular contracts
use ethers::contract::abigen;
abigen!(
KeepBonding,
"etherscan:0x7137701e90C6a80B0dA36922cd83942b32A8fc95"
);
abigen!(cDAI, "etherscan:0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643");
abigen!(
Comptroller,
"etherscan:0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b"
);
abigen!(
Curve,
"etherscan:0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56"
);
abigen!(
UmaAdmin,
"etherscan:0x4E6CCB1dA3C7844887F9A5aF4e8450d9fd90317A"
);
// e.g. aave's `initialize` methods exist multiple times, so we should rename it
abigen!(
AavePoolCore,
"etherscan:0x3dfd23a6c5e8bbcfc9581d2e864a68feb6a076d3",
methods {
initialize(address,bytes) as initialize_proxy;
}
);
// Abi Encoder v2 not yet supported :(
// abigen!(
// DyDxLimitOrders,
// "etherscan:0xDEf136D9884528e1EB302f39457af0E4d3AD24EB"
// );