From 73b502ed5f47e094ee6b1c9972669957e5a0781f Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Thu, 4 Jun 2020 00:05:05 +0300 Subject: [PATCH] macro: re-enable paths/etherscan and enable more complex tokens --- .circleci/config.yml | 6 ++- .../src/contract/events.rs | 2 +- .../ethers-contract-abigen/src/lib.rs | 3 +- .../ethers-contract-derive/src/abigen.rs | 39 ++++--------------- .../ethers-contract-derive/src/lib.rs | 35 +++++------------ ethers-contract/src/lib.rs | 2 +- ethers-core/src/abi/tokens.rs | 8 ++-- ethers/tests/major_contracts.rs | 35 +++++++++++++++++ 8 files changed, 63 insertions(+), 67 deletions(-) create mode 100644 ethers/tests/major_contracts.rs diff --git a/.circleci/config.yml b/.circleci/config.yml index cc4df9bb..d9291546 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -49,8 +49,10 @@ jobs: - setup-lints - setup-sccache - restore-sccache-cache - # skip these temporarily until we get ganache-cli and solc on CI - - run: cargo test --all -- --skip deploy_and_call_contract --skip send_eth + - run: + 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: name: Check style command: | diff --git a/ethers-contract/ethers-contract-abigen/src/contract/events.rs b/ethers-contract/ethers-contract-abigen/src/contract/events.rs index 0fce108a..29774ef4 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/events.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/events.rs @@ -90,7 +90,7 @@ fn expand_event(event: &Event, event_derives: &[Path]) -> Result { .iter() .map(|(name, _)| { 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::>(); diff --git a/ethers-contract/ethers-contract-abigen/src/lib.rs b/ethers-contract/ethers-contract-abigen/src/lib.rs index ca02e410..c3332d8d 100644 --- a/ethers-contract/ethers-contract-abigen/src/lib.rs +++ b/ethers-contract/ethers-contract-abigen/src/lib.rs @@ -27,8 +27,7 @@ use anyhow::Result; use proc_macro2::TokenStream; use std::{collections::HashMap, fs::File, io::Write, path::Path}; -/// Internal global arguments passed to the generators for each individual -/// component that control expansion. +/// Builder struct for generating type-safe bindings from a contract's ABI pub struct Abigen { /// The source of the ABI JSON for the contract whose bindings /// are being generated. diff --git a/ethers-contract/ethers-contract-derive/src/abigen.rs b/ethers-contract/ethers-contract-derive/src/abigen.rs index 22dfda20..c479bb57 100644 --- a/ethers-contract/ethers-contract-derive/src/abigen.rs +++ b/ethers-contract/ethers-contract-derive/src/abigen.rs @@ -2,16 +2,16 @@ //! ethereum smart contract. use crate::spanned::{ParseInner, Spanned}; -use ethers_contract_abigen::Builder; +use ethers_contract_abigen::Abigen; use ethers_core::abi::{Function, FunctionExt, Param}; use proc_macro2::{Span, TokenStream as TokenStream2}; -use quote::{quote, ToTokens as _}; +use quote::ToTokens; use std::collections::HashSet; use std::error::Error; use syn::ext::IdentExt; 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> { Ok(args.into_builder()?.generate()?.into_tokens()) @@ -20,21 +20,17 @@ pub(crate) fn expand(args: ContractArgs) -> Result> /// Contract procedural macro arguments. #[cfg_attr(test, derive(Debug, Eq, PartialEq))] pub(crate) struct ContractArgs { - visibility: Option, name: String, - path: String, + abi: String, parameters: Vec, } impl ContractArgs { - fn into_builder(self) -> Result> { - let mut builder = - Builder::from_str(&self.name, &self.path).visibility_modifier(self.visibility); + fn into_builder(self) -> Result> { + let mut builder = Abigen::new(&self.name, &self.abi)?; for parameter in self.parameters.into_iter() { 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| { builder.add_method_alias(m.signature, m.alias) }), @@ -50,12 +46,6 @@ impl ContractArgs { impl ParseInner for ContractArgs { fn spanned_parse(input: ParseStream) -> ParseResult<(Span, Self)> { - // read the visibility parameter - let visibility = match input.parse::()? { - Visibility::Inherited => None, - token => Some(quote!(#token).to_string()), - }; - // read the contract name let name = input.parse::()?.to_string(); @@ -67,7 +57,7 @@ impl ParseInner for ContractArgs { // therefore, the path will always be rooted on the cargo manifest // directory. Eventually we can use the `Span::source_file` API to // have a better experience. - let (span, path) = { + let (span, abi) = { let literal = input.parse::()?; (literal.span(), literal.value()) }; @@ -84,9 +74,8 @@ impl ParseInner for ContractArgs { Ok(( span, ContractArgs { - visibility, name, - path, + abi, parameters, }, )) @@ -96,8 +85,6 @@ impl ParseInner for ContractArgs { /// A single procedural macro parameter. #[cfg_attr(test, derive(Debug, Eq, PartialEq))] enum Parameter { - Mod(String), - Crate(String), Methods(Vec), EventDerives(Vec), } @@ -106,16 +93,6 @@ impl Parse for Parameter { fn parse(input: ParseStream) -> ParseResult { let name = input.call(Ident::parse_any)?; let param = match name.to_string().as_str() { - "crate" => { - input.parse::()?; - let name = input.call(Ident::parse_any)?.to_string(); - Parameter::Crate(name) - } - "mod" => { - input.parse::()?; - let name = input.parse::()?.to_string(); - Parameter::Mod(name) - } "methods" => { let content; braced!(content in input); diff --git a/ethers-contract/ethers-contract-derive/src/lib.rs b/ethers-contract/ethers-contract-derive/src/lib.rs index 0ef79630..31badd61 100644 --- a/ethers-contract/ethers-contract-derive/src/lib.rs +++ b/ethers-contract/ethers-contract-derive/src/lib.rs @@ -16,7 +16,7 @@ use syn::{parse::Error, parse_macro_input}; /// the crate's root `CARGO_MANIFEST_DIR`. /// /// ```ignore -/// ethcontract::contract!("build/contracts/MyContract.json"); +/// abigen!(MyContract, "MyContract.json"); /// ``` /// /// Alternatively, other sources may be used, for full details consult the @@ -24,12 +24,12 @@ use syn::{parse::Error, parse_macro_input}; /// /// ```ignore /// // 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 -/// ethcontract::contract!("etherscan:0x0001020304050607080910111213141516171819"); -/// ethcontract::contract!("https://etherscan.io/address/0x0001020304050607080910111213141516171819"); +/// abigen!(MyContract, "etherscan:0x0001020304050607080910111213141516171819"); +/// abigen!(MyContract, "https://etherscan.io/address/0x0001020304050607080910111213141516171819"); /// // 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 @@ -38,15 +38,6 @@ use syn::{parse::Error, parse_macro_input}; /// /// Currently the proc macro accepts additional parameters to configure some /// 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 /// allowing methods names to be explicitely set for contract methods. This /// 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 /// 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 -/// ethcontract::contract!( -/// pub(crate) "build/contracts/MyContract.json", -/// crate = ethcontract_rename, -/// mod = my_contract_instance, -/// contract = MyContractInstance, +/// abigen!( +/// MyContractInstance, +/// "build/contracts/MyContract.json", /// methods { /// 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. #[proc_macro] pub fn abigen(input: TokenStream) -> TokenStream { diff --git a/ethers-contract/src/lib.rs b/ethers-contract/src/lib.rs index 23c8acc2..989a5576 100644 --- a/ethers-contract/src/lib.rs +++ b/ethers-contract/src/lib.rs @@ -11,7 +11,7 @@ mod factory; pub use factory::ContractFactory; #[cfg(feature = "abigen")] -pub use ethers_contract_abigen::Builder; +pub use ethers_contract_abigen::Abigen; #[cfg(feature = "abigen")] pub use ethers_contract_derive::abigen; diff --git a/ethers-core/src/abi/tokens.rs b/ethers-core/src/abi/tokens.rs index 7297a537..525396c2 100644 --- a/ethers-core/src/abi/tokens.rs +++ b/ethers-core/src/abi/tokens.rs @@ -68,7 +68,7 @@ impl_output!(1, A,); impl_output!(2, A, B,); impl_output!(3, A, B, C,); 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!(7, A, B, C, D, E, F, G,); // 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, C:2, ); 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. -// 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, I:8, ); // impl_tokens!(A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7, I:8, J:9, ); diff --git a/ethers/tests/major_contracts.rs b/ethers/tests/major_contracts.rs new file mode 100644 index 00000000..5d67abe9 --- /dev/null +++ b/ethers/tests/major_contracts.rs @@ -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" +// );