macro: re-enable paths/etherscan and enable more complex tokens
This commit is contained in:
parent
ba7fedc7d3
commit
73b502ed5f
|
@ -49,8 +49,10 @@ jobs:
|
||||||
- setup-lints
|
- setup-lints
|
||||||
- setup-sccache
|
- setup-sccache
|
||||||
- restore-sccache-cache
|
- restore-sccache-cache
|
||||||
|
- run:
|
||||||
|
name: tests
|
||||||
# skip these temporarily until we get ganache-cli and solc on CI
|
# skip these temporarily until we get ganache-cli and solc on CI
|
||||||
- run: cargo test --all -- --skip deploy_and_call_contract --skip send_eth
|
command: cargo test --all -- --skip deploy_and_call_contract --skip send_eth
|
||||||
- run:
|
- run:
|
||||||
name: Check style
|
name: Check style
|
||||||
command: |
|
command: |
|
||||||
|
|
|
@ -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<_>>();
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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, );
|
||||||
|
|
|
@ -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"
|
||||||
|
// );
|
Loading…
Reference in New Issue