Refactor crate determination in new ethers-macro crate (#555)
* fix: compute ethers-core path in derive eip712 Co-authored-by: Ryan <ryan.tate@fieldresponder.io> * refactor: move crate determination to ethers-macro * docs: update fmt command * fix: change cargo_metadata dep to optional Co-authored-by: Ryan <ryan.tate@fieldresponder.io>
This commit is contained in:
parent
6cd5625787
commit
e72636210c
|
@ -118,7 +118,7 @@ This section lists some commonly needed commands.
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo check --all-features
|
cargo check --all-features
|
||||||
cargo fmt --all
|
cargo +nightly fmt --all
|
||||||
cargo build --all-features
|
cargo build --all-features
|
||||||
cargo test --all-features
|
cargo test --all-features
|
||||||
```
|
```
|
||||||
|
|
|
@ -950,7 +950,6 @@ version = "0.5.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"Inflector",
|
"Inflector",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cargo_metadata",
|
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"ethers-core",
|
"ethers-core",
|
||||||
"getrandom 0.2.3",
|
"getrandom 0.2.3",
|
||||||
|
@ -985,6 +984,7 @@ dependencies = [
|
||||||
"arrayvec 0.7.2",
|
"arrayvec 0.7.2",
|
||||||
"bincode",
|
"bincode",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"cargo_metadata",
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"ecdsa",
|
"ecdsa",
|
||||||
"elliptic-curve",
|
"elliptic-curve",
|
||||||
|
|
|
@ -10,7 +10,7 @@ repository = "https://github.com/gakonst/ethers-rs"
|
||||||
keywords = ["ethereum", "web3", "celo", "ethers"]
|
keywords = ["ethereum", "web3", "celo", "ethers"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ethers-core = { version = "^0.5.0", path = "../../ethers-core" }
|
ethers-core = { version = "^0.5.0", path = "../../ethers-core", features = ["macros"] }
|
||||||
|
|
||||||
anyhow = "1.0.37"
|
anyhow = "1.0.37"
|
||||||
Inflector = "0.11"
|
Inflector = "0.11"
|
||||||
|
@ -23,7 +23,6 @@ serde = { version = "1.0.124", features = ["derive"] }
|
||||||
hex = { version = "0.4.2", default-features = false, features = ["std"] }
|
hex = { version = "0.4.2", default-features = false, features = ["std"] }
|
||||||
reqwest = { version = "0.11.3", features = ["blocking"] }
|
reqwest = { version = "0.11.3", features = ["blocking"] }
|
||||||
once_cell = "1.8.0"
|
once_cell = "1.8.0"
|
||||||
cargo_metadata = "0.14.0"
|
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
|
|
@ -8,7 +8,10 @@ mod types;
|
||||||
use super::{util, Abigen};
|
use super::{util, Abigen};
|
||||||
use crate::{contract::structs::InternalStructs, rawabi::RawAbi};
|
use crate::{contract::structs::InternalStructs, rawabi::RawAbi};
|
||||||
use anyhow::{anyhow, Context as _, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
use ethers_core::abi::{Abi, AbiParser};
|
use ethers_core::{
|
||||||
|
abi::{Abi, AbiParser},
|
||||||
|
macros::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate},
|
||||||
|
};
|
||||||
|
|
||||||
use proc_macro2::{Ident, Literal, TokenStream};
|
use proc_macro2::{Ident, Literal, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
@ -111,9 +114,9 @@ impl Context {
|
||||||
// 5. Declare the structs parsed from the human readable abi
|
// 5. Declare the structs parsed from the human readable abi
|
||||||
let abi_structs_decl = self.abi_structs()?;
|
let abi_structs_decl = self.abi_structs()?;
|
||||||
|
|
||||||
let ethers_core = util::ethers_core_crate();
|
let ethers_core = ethers_core_crate();
|
||||||
let ethers_contract = util::ethers_contract_crate();
|
let ethers_contract = ethers_contract_crate();
|
||||||
let ethers_providers = util::ethers_providers_crate();
|
let ethers_providers = ethers_providers_crate();
|
||||||
|
|
||||||
let contract = quote! {
|
let contract = quote! {
|
||||||
#struct_decl
|
#struct_decl
|
||||||
|
|
|
@ -3,7 +3,7 @@ use super::{util, Context};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
||||||
use super::util::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate};
|
use ethers_core::macros::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate};
|
||||||
|
|
||||||
pub(crate) fn imports(name: &str) -> TokenStream {
|
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 doc = util::expand_doc(&format!("{} was auto-generated with ethers-rs Abigen. More information at: https://github.com/gakonst/ethers-rs", name));
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use super::{types, util, Context};
|
use super::{types, util, Context};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use ethers_core::abi::{Event, EventExt, EventParam, ParamType, SolStruct};
|
use ethers_core::{
|
||||||
|
abi::{Event, EventExt, EventParam, ParamType, SolStruct},
|
||||||
|
macros::{ethers_contract_crate, ethers_core_crate},
|
||||||
|
};
|
||||||
use inflector::Inflector;
|
use inflector::Inflector;
|
||||||
use proc_macro2::{Ident, Literal, TokenStream};
|
use proc_macro2::{Ident, Literal, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
@ -61,8 +64,8 @@ impl Context {
|
||||||
|
|
||||||
let enum_name = self.expand_event_enum_name();
|
let enum_name = self.expand_event_enum_name();
|
||||||
|
|
||||||
let ethers_core = util::ethers_core_crate();
|
let ethers_core = ethers_core_crate();
|
||||||
let ethers_contract = util::ethers_contract_crate();
|
let ethers_contract = ethers_contract_crate();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, #ethers_contract::EthAbiType)]
|
#[derive(Debug, Clone, PartialEq, Eq, #ethers_contract::EthAbiType)]
|
||||||
|
@ -106,7 +109,7 @@ impl Context {
|
||||||
let sorted_events: BTreeMap<_, _> = self.abi.events.clone().into_iter().collect();
|
let sorted_events: BTreeMap<_, _> = self.abi.events.clone().into_iter().collect();
|
||||||
|
|
||||||
let mut iter = sorted_events.values().flatten();
|
let mut iter = sorted_events.values().flatten();
|
||||||
let ethers_contract = util::ethers_contract_crate();
|
let ethers_contract = ethers_contract_crate();
|
||||||
|
|
||||||
if let Some(event) = iter.next() {
|
if let Some(event) = iter.next() {
|
||||||
let ty = if iter.next().is_some() {
|
let ty = if iter.next().is_some() {
|
||||||
|
@ -134,7 +137,7 @@ impl Context {
|
||||||
/// If a complex types matches with a struct previously parsed by the AbiParser,
|
/// If a complex types matches with a struct previously parsed by the AbiParser,
|
||||||
/// we can replace it
|
/// we can replace it
|
||||||
fn expand_input_type(&self, input: &EventParam) -> Result<TokenStream> {
|
fn expand_input_type(&self, input: &EventParam) -> Result<TokenStream> {
|
||||||
let ethers_core = util::ethers_core_crate();
|
let ethers_core = ethers_core_crate();
|
||||||
Ok(match (&input.kind, input.indexed) {
|
Ok(match (&input.kind, input.indexed) {
|
||||||
(ParamType::Array(ty), true) => {
|
(ParamType::Array(ty), true) => {
|
||||||
if let ParamType::Tuple(..) = **ty {
|
if let ParamType::Tuple(..) = **ty {
|
||||||
|
@ -202,7 +205,7 @@ impl Context {
|
||||||
|
|
||||||
/// Expands into a single method for contracting an event stream.
|
/// Expands into a single method for contracting an event stream.
|
||||||
fn expand_filter(&self, event: &Event) -> TokenStream {
|
fn expand_filter(&self, event: &Event) -> TokenStream {
|
||||||
let ethers_contract = util::ethers_contract_crate();
|
let ethers_contract = ethers_contract_crate();
|
||||||
let alias = self.event_aliases.get(&event.abi_signature()).cloned();
|
let alias = self.event_aliases.get(&event.abi_signature()).cloned();
|
||||||
|
|
||||||
let name = if let Some(id) = alias.clone() {
|
let name = if let Some(id) = alias.clone() {
|
||||||
|
@ -246,7 +249,7 @@ impl Context {
|
||||||
|
|
||||||
let derives = util::expand_derives(&self.event_derives);
|
let derives = util::expand_derives(&self.event_derives);
|
||||||
|
|
||||||
let ethers_contract = util::ethers_contract_crate();
|
let ethers_contract = ethers_contract_crate();
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, #ethers_contract::EthEvent, #ethers_contract::EthDisplay, #derives)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, #ethers_contract::EthEvent, #ethers_contract::EthDisplay, #derives)]
|
||||||
|
@ -318,7 +321,7 @@ mod tests {
|
||||||
/// quasi-quoting for code generation. We do this to avoid allocating at runtime
|
/// quasi-quoting for code generation. We do this to avoid allocating at runtime
|
||||||
fn expand_hash(hash: Hash) -> TokenStream {
|
fn expand_hash(hash: Hash) -> TokenStream {
|
||||||
let bytes = hash.as_bytes().iter().copied().map(Literal::u8_unsuffixed);
|
let bytes = hash.as_bytes().iter().copied().map(Literal::u8_unsuffixed);
|
||||||
let ethers_core = util::ethers_core_crate();
|
let ethers_core = ethers_core_crate();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#ethers_core::types::H256([#( #bytes ),*])
|
#ethers_core::types::H256([#( #bytes ),*])
|
||||||
|
|
|
@ -8,6 +8,7 @@ use syn::Ident;
|
||||||
|
|
||||||
use ethers_core::{
|
use ethers_core::{
|
||||||
abi::{Function, FunctionExt, Param, ParamType},
|
abi::{Function, FunctionExt, Param, ParamType},
|
||||||
|
macros::{ethers_contract_crate, ethers_core_crate},
|
||||||
types::Selector,
|
types::Selector,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,7 +64,7 @@ impl Context {
|
||||||
function.selector()
|
function.selector()
|
||||||
);
|
);
|
||||||
let abi_signature_doc = util::expand_doc(&doc);
|
let abi_signature_doc = util::expand_doc(&doc);
|
||||||
let ethers_contract = util::ethers_contract_crate();
|
let ethers_contract = ethers_contract_crate();
|
||||||
// use the same derives as for events
|
// use the same derives as for events
|
||||||
let derives = util::expand_derives(&self.event_derives);
|
let derives = util::expand_derives(&self.event_derives);
|
||||||
|
|
||||||
|
@ -98,8 +99,8 @@ impl Context {
|
||||||
return Ok(struct_def_tokens)
|
return Ok(struct_def_tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
let ethers_core = util::ethers_core_crate();
|
let ethers_core = ethers_core_crate();
|
||||||
let ethers_contract = util::ethers_contract_crate();
|
let ethers_contract = ethers_contract_crate();
|
||||||
|
|
||||||
let enum_name = self.expand_calls_enum_name();
|
let enum_name = self.expand_calls_enum_name();
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
|
@ -238,7 +239,7 @@ impl Context {
|
||||||
// TODO use structs
|
// TODO use structs
|
||||||
let outputs = expand_fn_outputs(&function.outputs)?;
|
let outputs = expand_fn_outputs(&function.outputs)?;
|
||||||
|
|
||||||
let ethers_contract = util::ethers_contract_crate();
|
let ethers_contract = ethers_contract_crate();
|
||||||
|
|
||||||
let result = quote! { #ethers_contract::builders::ContractCall<M, #outputs> };
|
let result = quote! { #ethers_contract::builders::ContractCall<M, #outputs> };
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,13 @@ use inflector::Inflector;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
||||||
use ethers_core::abi::{
|
use ethers_core::{
|
||||||
|
abi::{
|
||||||
param_type::Reader,
|
param_type::Reader,
|
||||||
struct_def::{FieldDeclaration, FieldType, StructFieldType, StructType},
|
struct_def::{FieldDeclaration, FieldType, StructFieldType, StructType},
|
||||||
ParamType, SolStruct,
|
ParamType, SolStruct,
|
||||||
|
},
|
||||||
|
macros::ethers_contract_crate,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -123,7 +126,7 @@ impl Context {
|
||||||
// use the same derives as for events
|
// use the same derives as for events
|
||||||
let derives = util::expand_derives(&self.event_derives);
|
let derives = util::expand_derives(&self.event_derives);
|
||||||
|
|
||||||
let ethers_contract = util::ethers_contract_crate();
|
let ethers_contract = ethers_contract_crate();
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#abi_signature_doc
|
#abi_signature_doc
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, #ethers_contract::EthAbiType, #derives)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, #ethers_contract::EthAbiType, #derives)]
|
||||||
|
@ -184,7 +187,7 @@ impl Context {
|
||||||
let derives = &self.event_derives;
|
let derives = &self.event_derives;
|
||||||
let derives = quote! {#(#derives),*};
|
let derives = quote! {#(#derives),*};
|
||||||
|
|
||||||
let ethers_contract = util::ethers_contract_crate();
|
let ethers_contract = ethers_contract_crate();
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#abi_signature_doc
|
#abi_signature_doc
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use ethers_core::abi::ParamType;
|
use ethers_core::{abi::ParamType, macros::ethers_core_crate};
|
||||||
use proc_macro2::{Literal, TokenStream};
|
use proc_macro2::{Literal, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
||||||
use super::util;
|
|
||||||
|
|
||||||
pub(crate) fn expand(kind: &ParamType) -> Result<TokenStream> {
|
pub(crate) fn expand(kind: &ParamType) -> Result<TokenStream> {
|
||||||
let ethers_core = util::ethers_core_crate();
|
let ethers_core = ethers_core_crate();
|
||||||
|
|
||||||
match kind {
|
match kind {
|
||||||
ParamType::Address => Ok(quote! { #ethers_core::types::Address }),
|
ParamType::Address => Ok(quote! { #ethers_core::types::Address }),
|
||||||
|
|
|
@ -21,7 +21,7 @@ mod util;
|
||||||
|
|
||||||
pub use ethers_core::types::Address;
|
pub use ethers_core::types::Address;
|
||||||
pub use source::Source;
|
pub use source::Source;
|
||||||
pub use util::{ethers_contract_crate, ethers_core_crate, parse_address};
|
pub use util::parse_address;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
|
|
|
@ -1,86 +1,12 @@
|
||||||
use ethers_core::types::Address;
|
use ethers_core::types::Address;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use cargo_metadata::{DependencyKind, MetadataCommand};
|
|
||||||
use inflector::Inflector;
|
use inflector::Inflector;
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use proc_macro2::{Ident, Literal, Span, TokenStream};
|
use proc_macro2::{Ident, Literal, Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
||||||
use syn::{Ident as SynIdent, Path};
|
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, &'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 {
|
|
||||||
syn::parse_str(ETHERS_CRATES.0).expect("valid path; qed")
|
|
||||||
}
|
|
||||||
/// Convenience function to turn the `ethers_contract` name in `ETHERS_CRATE` into an `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`)
|
|
||||||
///
|
|
||||||
/// We try to determine which crate ident to use based on the dependencies of
|
|
||||||
/// the project in which the macro is used. This is useful because the macros,
|
|
||||||
/// like `EthEvent` are provided by the `ethers-contract` crate which depends on
|
|
||||||
/// `ethers_core`. Most commonly `ethers` will be used as dependency which
|
|
||||||
/// reexports all the different crates, essentially `ethers::core` is
|
|
||||||
/// `ethers_core` So depending on the dependency used `ethers` ors `ethers_core
|
|
||||||
/// | 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.
|
|
||||||
///
|
|
||||||
/// This process is a bit hacky, we run `cargo metadata` internally which
|
|
||||||
/// resolves the current package but creates a new `Cargo.lock` file in the
|
|
||||||
/// process. This is not a problem for regular workspaces but becomes an issue
|
|
||||||
/// during publishing with `cargo publish` if the project does not ignore
|
|
||||||
/// `Cargo.lock` in `.gitignore`, because then cargo can't proceed with
|
|
||||||
/// publishing the crate because the created `Cargo.lock` leads to a modified
|
|
||||||
/// workspace, not the `CARGO_MANIFEST_DIR` but the workspace `cargo publish`
|
|
||||||
/// created in `./target/package/..`. Therefore we check prior to executing
|
|
||||||
/// `cargo metadata` if a `Cargo.lock` file exists and delete it afterwards if
|
|
||||||
/// it was created by `cargo metadata`.
|
|
||||||
pub fn determine_ethers_crates() -> (&'static str, &'static str, &'static str) {
|
|
||||||
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("No Manifest found");
|
|
||||||
|
|
||||||
// check if the lock file exists, if it's missing we need to clean up afterward
|
|
||||||
let lock_file = format!("{}/Cargo.lock", manifest_dir);
|
|
||||||
let needs_lock_file_cleanup = !std::path::Path::new(&lock_file).exists();
|
|
||||||
|
|
||||||
let res = MetadataCommand::new()
|
|
||||||
.manifest_path(&format!("{}/Cargo.toml", manifest_dir))
|
|
||||||
.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", "ethers::providers"))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap_or(("ethers_core", "ethers_contract", "ethers_providers"));
|
|
||||||
|
|
||||||
if needs_lock_file_cleanup {
|
|
||||||
// delete the `Cargo.lock` file that was created by `cargo metadata`
|
|
||||||
// if the package is not part of a workspace
|
|
||||||
let _ = std::fs::remove_file(lock_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Expands a identifier string into an token.
|
/// Expands a identifier string into an token.
|
||||||
pub fn ident(name: &str) -> Ident {
|
pub fn ident(name: &str) -> Ident {
|
||||||
Ident::new(name, Span::call_site())
|
Ident::new(name, Span::call_site())
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Helper functions for deriving `EthAbiType`
|
//! Helper functions for deriving `EthAbiType`
|
||||||
|
|
||||||
use ethers_contract_abigen::ethers_core_crate;
|
use ethers_core::macros::ethers_core_crate;
|
||||||
use proc_macro2::{Ident, Literal, TokenStream};
|
use proc_macro2::{Ident, Literal, TokenStream};
|
||||||
use quote::{quote, quote_spanned};
|
use quote::{quote, quote_spanned};
|
||||||
use syn::{parse::Error, spanned::Spanned as _, Data, DeriveInput, Fields, Variant};
|
use syn::{parse::Error, spanned::Spanned as _, Data, DeriveInput, Fields, Variant};
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
//! Helper functions for deriving `EthCall`
|
//! Helper functions for deriving `EthCall`
|
||||||
|
|
||||||
use ethers_contract_abigen::{ethers_contract_crate, ethers_core_crate};
|
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{parse::Error, spanned::Spanned as _, AttrStyle, DeriveInput, Lit, Meta, NestedMeta};
|
use syn::{parse::Error, spanned::Spanned as _, AttrStyle, DeriveInput, Lit, Meta, NestedMeta};
|
||||||
|
|
||||||
use ethers_core::abi::{param_type::Reader, AbiParser, Function, FunctionExt, Param, ParamType};
|
use ethers_core::{
|
||||||
|
abi::{param_type::Reader, AbiParser, Function, FunctionExt, Param, ParamType},
|
||||||
|
macros::{ethers_contract_crate, ethers_core_crate},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{abi_ty, utils};
|
use crate::{abi_ty, utils};
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,7 @@ use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{parse::Error, spanned::Spanned as _, Data, DeriveInput, Fields, Index};
|
use syn::{parse::Error, spanned::Spanned as _, Data, DeriveInput, Fields, Index};
|
||||||
|
|
||||||
use ethers_contract_abigen::ethers_core_crate;
|
use ethers_core::{abi::ParamType, macros::ethers_core_crate};
|
||||||
use ethers_core::abi::ParamType;
|
|
||||||
|
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Helper functions for deriving `EthEvent`
|
//! Helper functions for deriving `EthEvent`
|
||||||
|
|
||||||
use ethers_contract_abigen::{ethers_contract_crate, ethers_core_crate, Source};
|
use ethers_contract_abigen::Source;
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{
|
use syn::{
|
||||||
|
@ -8,7 +8,10 @@ use syn::{
|
||||||
NestedMeta,
|
NestedMeta,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ethers_core::abi::{param_type::Reader, AbiParser, Event, EventExt, EventParam, ParamType};
|
use ethers_core::{
|
||||||
|
abi::{param_type::Reader, AbiParser, Event, EventExt, EventParam, ParamType},
|
||||||
|
macros::{ethers_contract_crate, ethers_core_crate},
|
||||||
|
};
|
||||||
use hex::FromHex;
|
use hex::FromHex;
|
||||||
|
|
||||||
use crate::{abi_ty, utils};
|
use crate::{abi_ty, utils};
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use ethers_contract_abigen::ethers_core_crate;
|
use ethers_core::{abi::ParamType, macros::ethers_core_crate, types::Selector};
|
||||||
use ethers_core::{abi::ParamType, types::Selector};
|
|
||||||
use proc_macro2::Literal;
|
use proc_macro2::Literal;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{
|
use syn::{
|
||||||
|
|
|
@ -31,6 +31,9 @@ bytes = { version = "1.1.0", features = ["serde"] }
|
||||||
hex = { version = "0.4.3", default-features = false, features = ["std"] }
|
hex = { version = "0.4.3", default-features = false, features = ["std"] }
|
||||||
once_cell = "1.8.0"
|
once_cell = "1.8.0"
|
||||||
|
|
||||||
|
# macros feature enabled dependencies
|
||||||
|
cargo_metadata = { version = "0.14.0", optional = true }
|
||||||
|
|
||||||
# eip712 feature enabled dependencies
|
# eip712 feature enabled dependencies
|
||||||
convert_case = { version = "0.4.0", optional = true }
|
convert_case = { version = "0.4.0", optional = true }
|
||||||
syn = { version = "1.0.81", optional = true }
|
syn = { version = "1.0.81", optional = true }
|
||||||
|
@ -55,6 +58,7 @@ celo = ["legacy"] # celo support extends the transaction format with extra field
|
||||||
setup = ["tokio", "futures-util"] # async support for concurrent setup
|
setup = ["tokio", "futures-util"] # async support for concurrent setup
|
||||||
legacy = []
|
legacy = []
|
||||||
eip712 = ["convert_case", "syn", "quote", "proc-macro2"]
|
eip712 = ["convert_case", "syn", "quote", "proc-macro2"]
|
||||||
|
macros = ["syn", "cargo_metadata"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|
|
@ -10,7 +10,7 @@ proc-macro = true
|
||||||
[dependencies]
|
[dependencies]
|
||||||
quote = "1.0.9"
|
quote = "1.0.9"
|
||||||
syn = "1.0.77"
|
syn = "1.0.77"
|
||||||
ethers-core = { version = "^0.5.0", path = "../", default-features = false, features = ["eip712"] }
|
ethers-core = { version = "^0.5.0", path = "../", default-features = false, features = ["eip712", "macros"] }
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
serde = "1.0.130"
|
serde = "1.0.130"
|
||||||
serde_json = "1.0.68"
|
serde_json = "1.0.68"
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
//! determine if there is a nested eip712 struct. However, this work is not yet complete.
|
//! determine if there is a nested eip712 struct. However, this work is not yet complete.
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use ethers_core::types::transaction::eip712;
|
use ethers_core::{macros::ethers_core_crate, types::transaction::eip712};
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
@ -104,13 +104,16 @@ fn impl_eip_712_macro(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
Err(e) => return TokenStream::from(e),
|
Err(e) => return TokenStream::from(e),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compute the type hash for the derived struct using the parsed fields from above;
|
// Compute the type hash for the derived struct using the parsed fields from above.
|
||||||
let type_hash =
|
let type_hash =
|
||||||
hex::encode(eip712::make_type_hash(primary_type.clone().to_string(), &parsed_fields));
|
hex::encode(eip712::make_type_hash(primary_type.clone().to_string(), &parsed_fields));
|
||||||
|
|
||||||
|
// Use reference to ethers_core instead of directly using the crate itself.
|
||||||
|
let ethers_core = ethers_core_crate();
|
||||||
|
|
||||||
let implementation = quote! {
|
let implementation = quote! {
|
||||||
impl Eip712 for #primary_type {
|
impl Eip712 for #primary_type {
|
||||||
type Error = ethers_core::types::transaction::eip712::Eip712Error;
|
type Error = #ethers_core::types::transaction::eip712::Eip712Error;
|
||||||
|
|
||||||
fn type_hash() -> Result<[u8; 32], Self::Error> {
|
fn type_hash() -> Result<[u8; 32], Self::Error> {
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
@ -127,34 +130,34 @@ fn impl_eip_712_macro(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
Ok(byte_array)
|
Ok(byte_array)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn domain(&self) -> Result<ethers_core::types::transaction::eip712::EIP712Domain, Self::Error> {
|
fn domain(&self) -> Result<#ethers_core::types::transaction::eip712::EIP712Domain, Self::Error> {
|
||||||
let domain: ethers_core::types::transaction::eip712::EIP712Domain = serde_json::from_str(#domain_str)?;
|
let domain: #ethers_core::types::transaction::eip712::EIP712Domain = serde_json::from_str(#domain_str)?;
|
||||||
|
|
||||||
Ok(domain)
|
Ok(domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn struct_hash(&self) -> Result<[u8; 32], Self::Error> {
|
fn struct_hash(&self) -> Result<[u8; 32], Self::Error> {
|
||||||
use ethers_core::abi::Tokenizable;
|
use #ethers_core::abi::Tokenizable;
|
||||||
let mut items = vec![ethers_core::abi::Token::Uint(
|
let mut items = vec![#ethers_core::abi::Token::Uint(
|
||||||
ethers_core::types::U256::from(&Self::type_hash()?[..]),
|
#ethers_core::types::U256::from(&Self::type_hash()?[..]),
|
||||||
)];
|
)];
|
||||||
|
|
||||||
if let ethers_core::abi::Token::Tuple(tokens) = self.clone().into_token() {
|
if let #ethers_core::abi::Token::Tuple(tokens) = self.clone().into_token() {
|
||||||
for token in tokens {
|
for token in tokens {
|
||||||
match &token {
|
match &token {
|
||||||
ethers_core::abi::Token::Tuple(t) => {
|
#ethers_core::abi::Token::Tuple(t) => {
|
||||||
// TODO: check for nested Eip712 Type;
|
// TODO: check for nested Eip712 Type;
|
||||||
// Challenge is determining the type hash
|
// Challenge is determining the type hash
|
||||||
return Err(Self::Error::NestedEip712StructNotImplemented);
|
return Err(Self::Error::NestedEip712StructNotImplemented);
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
items.push(ethers_core::types::transaction::eip712::encode_eip712_type(token));
|
items.push(#ethers_core::types::transaction::eip712::encode_eip712_type(token));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let struct_hash = ethers_core::utils::keccak256(ethers_core::abi::encode(
|
let struct_hash = #ethers_core::utils::keccak256(#ethers_core::abi::encode(
|
||||||
&items,
|
&items,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,8 @@ pub mod abi;
|
||||||
/// Various utilities
|
/// Various utilities
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
pub mod macros;
|
||||||
|
|
||||||
// re-export rand to avoid potential confusion when there's rand version mismatches
|
// re-export rand to avoid potential confusion when there's rand version mismatches
|
||||||
pub use rand;
|
pub use rand;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
use cargo_metadata::{DependencyKind, MetadataCommand};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use syn::Path;
|
||||||
|
|
||||||
|
/// See `determine_ethers_crates`
|
||||||
|
///
|
||||||
|
/// This ensures that the `MetadataCommand` is only run once
|
||||||
|
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 {
|
||||||
|
syn::parse_str(ETHERS_CRATES.0).expect("valid path; qed")
|
||||||
|
}
|
||||||
|
/// Convenience function to turn the `ethers_contract` name in `ETHERS_CRATE` into an `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`)
|
||||||
|
///
|
||||||
|
/// We try to determine which crate ident to use based on the dependencies of
|
||||||
|
/// the project in which the macro is used. This is useful because the macros,
|
||||||
|
/// like `EthEvent` are provided by the `ethers-contract` crate which depends on
|
||||||
|
/// `ethers_core`. Most commonly `ethers` will be used as dependency which
|
||||||
|
/// reexports all the different crates, essentially `ethers::core` is
|
||||||
|
/// `ethers_core` So depending on the dependency used `ethers` ors `ethers_core
|
||||||
|
/// | 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.
|
||||||
|
///
|
||||||
|
/// This process is a bit hacky, we run `cargo metadata` internally which
|
||||||
|
/// resolves the current package but creates a new `Cargo.lock` file in the
|
||||||
|
/// process. This is not a problem for regular workspaces but becomes an issue
|
||||||
|
/// during publishing with `cargo publish` if the project does not ignore
|
||||||
|
/// `Cargo.lock` in `.gitignore`, because then cargo can't proceed with
|
||||||
|
/// publishing the crate because the created `Cargo.lock` leads to a modified
|
||||||
|
/// workspace, not the `CARGO_MANIFEST_DIR` but the workspace `cargo publish`
|
||||||
|
/// created in `./target/package/..`. Therefore we check prior to executing
|
||||||
|
/// `cargo metadata` if a `Cargo.lock` file exists and delete it afterwards if
|
||||||
|
/// it was created by `cargo metadata`.
|
||||||
|
pub fn determine_ethers_crates() -> (&'static str, &'static str, &'static str) {
|
||||||
|
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("No Manifest found");
|
||||||
|
|
||||||
|
// check if the lock file exists, if it's missing we need to clean up afterward
|
||||||
|
let lock_file = format!("{}/Cargo.lock", manifest_dir);
|
||||||
|
let needs_lock_file_cleanup = !std::path::Path::new(&lock_file).exists();
|
||||||
|
|
||||||
|
let res = MetadataCommand::new()
|
||||||
|
.manifest_path(&format!("{}/Cargo.toml", manifest_dir))
|
||||||
|
.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", "ethers::providers"))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or(("ethers_core", "ethers_contract", "ethers_providers"));
|
||||||
|
|
||||||
|
if needs_lock_file_cleanup {
|
||||||
|
// delete the `Cargo.lock` file that was created by `cargo metadata`
|
||||||
|
// if the package is not part of a workspace
|
||||||
|
let _ = std::fs::remove_file(lock_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
mod ethers_crate;
|
||||||
|
|
||||||
|
#[cfg(feature = "macros")]
|
||||||
|
pub use ethers_crate::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate};
|
Loading…
Reference in New Issue