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-sccache
|
||||
- restore-sccache-cache
|
||||
- run:
|
||||
name: tests
|
||||
# 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:
|
||||
name: Check style
|
||||
command: |
|
||||
|
|
|
@ -90,7 +90,7 @@ fn expand_event(event: &Event, event_derives: &[Path]) -> Result<TokenStream> {
|
|||
.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::<Vec<_>>();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<TokenStream2, Box<dyn Error>> {
|
||||
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.
|
||||
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
|
||||
pub(crate) struct ContractArgs {
|
||||
visibility: Option<String>,
|
||||
name: String,
|
||||
path: String,
|
||||
abi: String,
|
||||
parameters: Vec<Parameter>,
|
||||
}
|
||||
|
||||
impl ContractArgs {
|
||||
fn into_builder(self) -> Result<Builder, Box<dyn Error>> {
|
||||
let mut builder =
|
||||
Builder::from_str(&self.name, &self.path).visibility_modifier(self.visibility);
|
||||
fn into_builder(self) -> Result<Abigen, Box<dyn Error>> {
|
||||
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>()? {
|
||||
Visibility::Inherited => None,
|
||||
token => Some(quote!(#token).to_string()),
|
||||
};
|
||||
|
||||
// read the contract name
|
||||
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
|
||||
// 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::<LitStr>()?;
|
||||
(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<Method>),
|
||||
EventDerives(Vec<String>),
|
||||
}
|
||||
|
@ -106,16 +93,6 @@ impl Parse for Parameter {
|
|||
fn parse(input: ParseStream) -> ParseResult<Self> {
|
||||
let name = input.call(Ident::parse_any)?;
|
||||
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" => {
|
||||
let content;
|
||||
braced!(content in input);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, );
|
||||
|
|
|
@ -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