refactor(abigen): derives, struct expansion (#2160)
* refactor: abigen derives * refactor: struct expansion * refactor: structs 2 * chore: abigen derives and visibility * refactor: abigen docs * refactor: structs 3 * chore: doc * chore: structs conditional default * refactor: method expansion * refactor: final doc * refactor: expansions, add docs, extra From impl * refactor: final struct expansion * feat(types): implement bytes::Bytes static methods * feat: use static Bytes for bytecode * merge artifact * refactor: event input expansion * refactor: common expand params * refactor: method params expansion * refactor: struct fields expansion * refactor: types array expansion * mergings * cleanup * flatten * selector fmt * chore: clippy * refactor: common, imports
This commit is contained in:
parent
8d511dbd64
commit
6336e96995
|
@ -1,6 +1,5 @@
|
|||
//! Contains types to generate Rust bindings for Solidity contracts.
|
||||
|
||||
mod common;
|
||||
mod errors;
|
||||
mod events;
|
||||
mod methods;
|
||||
|
@ -52,14 +51,19 @@ impl ExpandedContract {
|
|||
abi_structs,
|
||||
errors,
|
||||
} = self;
|
||||
|
||||
quote! {
|
||||
// export all the created data types
|
||||
pub use #module::*;
|
||||
|
||||
/// This module was auto-generated with ethers-rs Abigen.
|
||||
/// More information at: <https://github.com/gakonst/ethers-rs>
|
||||
#[allow(
|
||||
clippy::enum_variant_names,
|
||||
clippy::too_many_arguments,
|
||||
clippy::upper_case_acronyms,
|
||||
clippy::type_complexity,
|
||||
dead_code,
|
||||
non_camel_case_types,
|
||||
clippy::upper_case_acronyms
|
||||
)]
|
||||
pub mod #module {
|
||||
#imports
|
||||
|
@ -103,7 +107,7 @@ pub struct Context {
|
|||
error_aliases: BTreeMap<String, Ident>,
|
||||
|
||||
/// Derives added to event structs and enums.
|
||||
event_derives: Vec<Path>,
|
||||
extra_derives: Vec<Path>,
|
||||
|
||||
/// Manually specified event aliases.
|
||||
event_aliases: BTreeMap<String, Ident>,
|
||||
|
@ -122,11 +126,8 @@ impl Context {
|
|||
let name_mod = util::ident(&util::safe_module_name(&self.contract_name));
|
||||
let abi_name = self.inline_abi_ident();
|
||||
|
||||
// 0. Imports
|
||||
let imports = common::imports(&name.to_string());
|
||||
|
||||
// 1. Declare Contract struct
|
||||
let struct_decl = common::struct_declaration(self);
|
||||
let struct_decl = self.struct_declaration();
|
||||
|
||||
// 2. Declare events structs & impl FromTokens for each event
|
||||
let events_decl = self.events_declaration()?;
|
||||
|
@ -137,7 +138,7 @@ impl Context {
|
|||
// 4. impl block for the contract methods and their corresponding types
|
||||
let (contract_methods, call_structs) = self.methods_and_call_structs()?;
|
||||
|
||||
// 5. generate deploy function if
|
||||
// 5. The deploy method, only if the contract has a bytecode object
|
||||
let deployment_methods = self.deployment_methods();
|
||||
|
||||
// 6. Declare the structs parsed from the human readable abi
|
||||
|
@ -154,9 +155,8 @@ impl Context {
|
|||
#struct_decl
|
||||
|
||||
impl<M: #ethers_providers::Middleware> #name<M> {
|
||||
/// Creates a new contract instance with the specified `ethers`
|
||||
/// client at the given `Address`. The contract derefs to a `ethers::Contract`
|
||||
/// object
|
||||
/// Creates a new contract instance with the specified `ethers` client at
|
||||
/// `address`. The contract derefs to a `ethers::Contract` object.
|
||||
pub fn new<T: Into<#ethers_core::types::Address>>(address: T, client: ::std::sync::Arc<M>) -> Self {
|
||||
Self(#ethers_contract::Contract::new(address.into(), #abi_name.clone(), client))
|
||||
}
|
||||
|
@ -166,19 +166,18 @@ impl Context {
|
|||
#contract_methods
|
||||
|
||||
#contract_events
|
||||
|
||||
}
|
||||
|
||||
impl<M : #ethers_providers::Middleware> From<#ethers_contract::Contract<M>> for #name<M> {
|
||||
impl<M: #ethers_providers::Middleware> From<#ethers_contract::Contract<M>> for #name<M> {
|
||||
fn from(contract: #ethers_contract::Contract<M>) -> Self {
|
||||
Self::new(contract.address(), contract.client())
|
||||
Self::new(contract.address(), contract.client())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ExpandedContract {
|
||||
module: name_mod,
|
||||
imports,
|
||||
imports: quote!(),
|
||||
contract,
|
||||
events: events_decl,
|
||||
errors: errors_decl,
|
||||
|
@ -282,12 +281,12 @@ impl Context {
|
|||
);
|
||||
}
|
||||
|
||||
let event_derives = args
|
||||
let extra_derives = args
|
||||
.derives
|
||||
.iter()
|
||||
.map(|derive| syn::parse_str::<Path>(derive))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.context("failed to parse event derives")?;
|
||||
.wrap_err("failed to parse event derives")?;
|
||||
|
||||
Ok(Context {
|
||||
abi,
|
||||
|
@ -301,7 +300,7 @@ impl Context {
|
|||
contract_deployed_bytecode,
|
||||
method_aliases,
|
||||
error_aliases: Default::default(),
|
||||
event_derives,
|
||||
extra_derives,
|
||||
event_aliases,
|
||||
})
|
||||
}
|
||||
|
@ -335,6 +334,116 @@ impl Context {
|
|||
pub fn internal_structs_mut(&mut self) -> &mut InternalStructs {
|
||||
&mut self.internal_structs
|
||||
}
|
||||
|
||||
/// Expands `self.extra_derives` into a comma separated list to be inserted in a
|
||||
/// `#[derive(...)]` attribute.
|
||||
pub(crate) fn expand_extra_derives(&self) -> TokenStream {
|
||||
let extra_derives = &self.extra_derives;
|
||||
quote!(#( #extra_derives, )*)
|
||||
}
|
||||
|
||||
/// Generates the token stream for the contract's ABI, bytecode and struct declarations.
|
||||
pub(crate) fn struct_declaration(&self) -> TokenStream {
|
||||
let name = &self.contract_ident;
|
||||
|
||||
let ethers_core = ethers_core_crate();
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
let abi = {
|
||||
let abi_name = self.inline_abi_ident();
|
||||
let abi = &self.abi_str;
|
||||
let (doc_str, parse) = if self.human_readable {
|
||||
// Human readable: use abi::parse_abi_str
|
||||
let doc_str = "The parsed human-readable ABI of the contract.";
|
||||
let parse = quote!(#ethers_core::abi::parse_abi_str(__ABI));
|
||||
(doc_str, parse)
|
||||
} else {
|
||||
// JSON ABI: use serde_json::from_str
|
||||
let doc_str = "The parsed JSON ABI of the contract.";
|
||||
let parse = quote!(#ethers_core::utils::__serde_json::from_str(__ABI));
|
||||
(doc_str, parse)
|
||||
};
|
||||
|
||||
quote! {
|
||||
#[rustfmt::skip]
|
||||
const __ABI: &str = #abi;
|
||||
|
||||
// This never fails as we are parsing the ABI in this macro
|
||||
#[doc = #doc_str]
|
||||
pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> =
|
||||
#ethers_contract::Lazy::new(|| #parse.expect("ABI is always valid"));
|
||||
}
|
||||
};
|
||||
|
||||
let bytecode = self.contract_bytecode.as_ref().map(|bytecode| {
|
||||
let bytecode = bytecode.iter().copied().map(Literal::u8_unsuffixed);
|
||||
let bytecode_name = self.inline_bytecode_ident();
|
||||
quote! {
|
||||
#[rustfmt::skip]
|
||||
const __BYTECODE: &[u8] = &[ #( #bytecode ),* ];
|
||||
|
||||
#[doc = "The bytecode of the contract."]
|
||||
pub static #bytecode_name: #ethers_core::types::Bytes = #ethers_core::types::Bytes::from_static(__BYTECODE);
|
||||
}
|
||||
});
|
||||
|
||||
let deployed_bytecode = self.contract_deployed_bytecode.as_ref().map(|bytecode| {
|
||||
let bytecode = bytecode.iter().copied().map(Literal::u8_unsuffixed);
|
||||
let bytecode_name = self.inline_deployed_bytecode_ident();
|
||||
quote! {
|
||||
#[rustfmt::skip]
|
||||
const __DEPLOYED_BYTECODE: &[u8] = &[ #( #bytecode ),* ];
|
||||
|
||||
#[doc = "The deployed bytecode of the contract."]
|
||||
pub static #bytecode_name: #ethers_core::types::Bytes = #ethers_core::types::Bytes::from_static(__DEPLOYED_BYTECODE);
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
// The `Lazy` ABI
|
||||
#abi
|
||||
|
||||
// The static Bytecode, if present
|
||||
#bytecode
|
||||
|
||||
// The static deployed Bytecode, if present
|
||||
#deployed_bytecode
|
||||
|
||||
// Struct declaration
|
||||
pub struct #name<M>(#ethers_contract::Contract<M>);
|
||||
|
||||
// Manual implementation since `M` is stored in `Arc<M>` and does not need to be `Clone`
|
||||
impl<M> ::core::clone::Clone for #name<M> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(::core::clone::Clone::clone(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
// Deref to the inner contract to have access to all its methods
|
||||
impl<M> ::core::ops::Deref for #name<M> {
|
||||
type Target = #ethers_contract::Contract<M>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> ::core::ops::DerefMut for #name<M> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
// `<name>(<address>)`
|
||||
impl<M> ::core::fmt::Debug for #name<M> {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
|
||||
f.debug_tuple(stringify!(#name))
|
||||
.field(&self.address())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Solidity supports overloading as long as the signature of an event, error, function is unique,
|
||||
|
|
|
@ -1,156 +0,0 @@
|
|||
use super::Context;
|
||||
use ethers_core::macros::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate};
|
||||
use proc_macro2::{Ident, Literal, TokenStream};
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) fn imports(name: &str) -> TokenStream {
|
||||
let doc_str = format!("{name} was auto-generated with ethers-rs Abigen. More information at: <https://github.com/gakonst/ethers-rs>");
|
||||
|
||||
let ethers_core = ethers_core_crate();
|
||||
let ethers_providers = ethers_providers_crate();
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
quote! {
|
||||
#![allow(clippy::enum_variant_names)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![allow(unused_imports)]
|
||||
#![doc = #doc_str]
|
||||
|
||||
use std::sync::Arc;
|
||||
use #ethers_core::{
|
||||
abi::{Abi, Token, Detokenize, InvalidOutputType, Tokenizable},
|
||||
types::*, // import all the types so that we can codegen for everything
|
||||
};
|
||||
use #ethers_contract::{Contract, builders::{ContractCall, Event}, Lazy};
|
||||
use #ethers_providers::Middleware;
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the token stream for the contract's ABI, bytecode and struct declarations.
|
||||
pub(crate) fn struct_declaration(cx: &Context) -> TokenStream {
|
||||
let name = &cx.contract_ident;
|
||||
|
||||
let ethers_core = ethers_core_crate();
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
let abi = {
|
||||
let abi_name = cx.inline_abi_ident();
|
||||
let abi = &cx.abi_str;
|
||||
let (doc_str, parse) = if cx.human_readable {
|
||||
// Human readable: use abi::parse_abi_str
|
||||
let doc_str = "The parsed human-readable ABI of the contract.";
|
||||
let parse = quote!(#ethers_core::abi::parse_abi_str(__ABI));
|
||||
(doc_str, parse)
|
||||
} else {
|
||||
// JSON ABI: use serde_json::from_str
|
||||
let doc_str = "The parsed JSON ABI of the contract.";
|
||||
let parse = quote!(#ethers_core::utils::__serde_json::from_str(__ABI));
|
||||
(doc_str, parse)
|
||||
};
|
||||
|
||||
quote! {
|
||||
#[rustfmt::skip]
|
||||
const __ABI: &str = #abi;
|
||||
|
||||
// This never fails as we are parsing the ABI in this macro
|
||||
#[doc = #doc_str]
|
||||
pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> =
|
||||
#ethers_contract::Lazy::new(|| #parse.expect("ABI is always valid"));
|
||||
}
|
||||
};
|
||||
|
||||
let bytecode = cx.contract_bytecode.as_ref().map(|bytecode| {
|
||||
let bytecode = bytecode.iter().copied().map(Literal::u8_unsuffixed);
|
||||
let bytecode_name = cx.inline_bytecode_ident();
|
||||
quote! {
|
||||
#[rustfmt::skip]
|
||||
const __BYTECODE: &[u8] = &[ #( #bytecode ),* ];
|
||||
|
||||
#[doc = "The bytecode of the contract."]
|
||||
pub static #bytecode_name: #ethers_core::types::Bytes = #ethers_core::types::Bytes::from_static(__BYTECODE);
|
||||
}
|
||||
});
|
||||
|
||||
let deployed_bytecode = cx.contract_deployed_bytecode.as_ref().map(|bytecode| {
|
||||
let bytecode = bytecode.iter().copied().map(Literal::u8_unsuffixed);
|
||||
let bytecode_name = cx.inline_deployed_bytecode_ident();
|
||||
quote! {
|
||||
#[rustfmt::skip]
|
||||
const __DEPLOYED_BYTECODE: &[u8] = &[ #( #bytecode ),* ];
|
||||
|
||||
#[doc = "The deployed bytecode of the contract."]
|
||||
pub static #bytecode_name: #ethers_core::types::Bytes = #ethers_core::types::Bytes::from_static(__DEPLOYED_BYTECODE);
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
// The `Lazy` ABI
|
||||
#abi
|
||||
|
||||
// The static Bytecode, if present
|
||||
#bytecode
|
||||
|
||||
// The static deployed Bytecode, if present
|
||||
#deployed_bytecode
|
||||
|
||||
// Struct declaration
|
||||
pub struct #name<M>(#ethers_contract::Contract<M>);
|
||||
|
||||
impl<M> Clone for #name<M> {
|
||||
fn clone(&self) -> Self {
|
||||
#name(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// Deref to the inner contract in order to access more specific functions functions
|
||||
impl<M> std::ops::Deref for #name<M> {
|
||||
type Target = #ethers_contract::Contract<M>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl<M> std::fmt::Debug for #name<M> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.debug_tuple(stringify!(#name))
|
||||
.field(&self.address())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Expands to the tuple struct definition
|
||||
pub(crate) fn expand_data_tuple(
|
||||
name: &Ident,
|
||||
params: &[(TokenStream, TokenStream)],
|
||||
) -> TokenStream {
|
||||
let fields = params
|
||||
.iter()
|
||||
.map(|(_, ty)| {
|
||||
quote! {
|
||||
pub #ty }
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if fields.is_empty() {
|
||||
quote! { struct #name; }
|
||||
} else {
|
||||
quote! { struct #name( #( #fields ),* ); }
|
||||
}
|
||||
}
|
||||
|
||||
/// Expands to a struct definition with named fields
|
||||
pub(crate) fn expand_data_struct(
|
||||
name: &Ident,
|
||||
params: &[(TokenStream, TokenStream)],
|
||||
) -> TokenStream {
|
||||
let fields = params
|
||||
.iter()
|
||||
.map(|(name, ty)| {
|
||||
quote! { pub #name: #ty }
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! { struct #name { #( #fields, )* } }
|
||||
}
|
|
@ -1,9 +1,6 @@
|
|||
//! derive error bindings
|
||||
//! Custom errors expansion
|
||||
|
||||
use super::{
|
||||
common::{expand_data_struct, expand_data_tuple},
|
||||
types, util, Context,
|
||||
};
|
||||
use super::{structs::expand_struct, types, util, Context};
|
||||
use ethers_core::{
|
||||
abi::{ethabi::AbiError, ErrorExt},
|
||||
macros::{ethers_contract_crate, ethers_core_crate},
|
||||
|
@ -25,57 +22,46 @@ impl Context {
|
|||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
// only expand an enum when multiple errors are present
|
||||
let errors_enum_decl = if self.abi.errors.values().flatten().count() > 1 {
|
||||
self.expand_errors_enum()
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
let errors_enum_decl =
|
||||
if data_types.len() > 1 { Some(self.expand_errors_enum()) } else { None };
|
||||
|
||||
Ok(quote! {
|
||||
// HERE
|
||||
#( #data_types )*
|
||||
|
||||
#errors_enum_decl
|
||||
|
||||
// HERE end
|
||||
})
|
||||
}
|
||||
|
||||
/// Expands an ABI error into a single error data type. This can expand either
|
||||
/// into a structure or a tuple in the case where all error parameters are anonymous.
|
||||
fn expand_error(&self, error: &AbiError) -> Result<TokenStream> {
|
||||
let sig = self.error_aliases.get(&error.abi_signature()).cloned();
|
||||
let error_name = &error.name;
|
||||
let abi_signature = error.abi_signature();
|
||||
|
||||
let error_name = error_struct_name(&error.name, sig);
|
||||
let alias_opt = self.error_aliases.get(&abi_signature).cloned();
|
||||
let error_struct_name = error_struct_name(&error.name, alias_opt);
|
||||
|
||||
let fields = self.expand_error_params(error)?;
|
||||
|
||||
// expand as a tuple if all fields are anonymous
|
||||
let all_anonymous_fields = error.inputs.iter().all(|input| input.name.is_empty());
|
||||
let data_type_definition = if all_anonymous_fields {
|
||||
// expand to a tuple struct
|
||||
expand_data_tuple(&error_name, &fields)
|
||||
} else {
|
||||
// expand to a struct
|
||||
expand_data_struct(&error_name, &fields)
|
||||
};
|
||||
let data_type_definition = expand_struct(&error_struct_name, &fields, all_anonymous_fields);
|
||||
|
||||
let doc_str = format!(
|
||||
"Custom Error type `{}` with signature `{}` and selector `0x{}`",
|
||||
error.name,
|
||||
abi_signature,
|
||||
hex::encode(&error.selector()[..])
|
||||
"Custom Error type `{error_name}` with signature `{abi_signature}` and selector `0x{}`",
|
||||
hex::encode(error.selector())
|
||||
);
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
// use the same derives as for events
|
||||
let derives = util::expand_derives(&self.event_derives);
|
||||
|
||||
let error_name = &error.name;
|
||||
let mut extra_derives = self.expand_extra_derives();
|
||||
if util::can_derive_defaults(&error.inputs) {
|
||||
extra_derives.extend(quote!(Default));
|
||||
}
|
||||
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
Ok(quote! {
|
||||
#[doc = #doc_str]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, #ethers_contract::EthError, #ethers_contract::EthDisplay, #derives)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, #ethers_contract::EthError, #ethers_contract::EthDisplay, #extra_derives)]
|
||||
#[etherror(name = #error_name, abi = #abi_signature)]
|
||||
pub #data_type_definition
|
||||
})
|
||||
|
@ -95,6 +81,7 @@ impl Context {
|
|||
|
||||
/// Generate an enum with a variant for each event
|
||||
fn expand_errors_enum(&self) -> TokenStream {
|
||||
let enum_name = self.expand_error_enum_name();
|
||||
let variants = self
|
||||
.abi
|
||||
.errors
|
||||
|
@ -105,58 +92,57 @@ impl Context {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let extra_derives = self.expand_extra_derives();
|
||||
|
||||
let ethers_core = ethers_core_crate();
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
// use the same derives as for events
|
||||
let derives = util::expand_derives(&self.event_derives);
|
||||
let enum_name = self.expand_error_enum_name();
|
||||
|
||||
quote! {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, #ethers_contract::EthAbiType, #derives)]
|
||||
#[doc = "Container type for all of the contract's custom errors"]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, #ethers_contract::EthAbiType, #extra_derives)]
|
||||
pub enum #enum_name {
|
||||
#(#variants(#variants)),*
|
||||
#( #variants(#variants), )*
|
||||
}
|
||||
|
||||
impl #ethers_core::abi::AbiDecode for #enum_name {
|
||||
fn decode(data: impl AsRef<[u8]>) -> ::std::result::Result<Self, #ethers_core::abi::AbiError> {
|
||||
#(
|
||||
if let Ok(decoded) = <#variants as #ethers_core::abi::AbiDecode>::decode(data.as_ref()) {
|
||||
return Ok(#enum_name::#variants(decoded))
|
||||
impl #ethers_core::abi::AbiDecode for #enum_name {
|
||||
fn decode(data: impl AsRef<[u8]>) -> ::core::result::Result<Self, #ethers_core::abi::AbiError> {
|
||||
let data = data.as_ref();
|
||||
#(
|
||||
if let Ok(decoded) = <#variants as #ethers_core::abi::AbiDecode>::decode(data) {
|
||||
return Ok(Self::#variants(decoded))
|
||||
}
|
||||
)*
|
||||
Err(#ethers_core::abi::Error::InvalidData.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl #ethers_core::abi::AbiEncode for #enum_name {
|
||||
fn encode(self) -> ::std::vec::Vec<u8> {
|
||||
match self {
|
||||
#(
|
||||
Self::#variants(element) => #ethers_core::abi::AbiEncode::encode(element),
|
||||
)*
|
||||
}
|
||||
)*
|
||||
Err(#ethers_core::abi::Error::InvalidData.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl #ethers_core::abi::AbiEncode for #enum_name {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
match self {
|
||||
#(
|
||||
#enum_name::#variants(element) => element.encode()
|
||||
),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for #enum_name {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
match self {
|
||||
#(
|
||||
#enum_name::#variants(element) => element.fmt(f)
|
||||
),*
|
||||
impl ::core::fmt::Display for #enum_name {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
|
||||
match self {
|
||||
#(
|
||||
Self::#variants(element) => ::core::fmt::Display::fmt(element, f)
|
||||
),*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#(
|
||||
impl ::std::convert::From<#variants> for #enum_name {
|
||||
fn from(var: #variants) -> Self {
|
||||
#enum_name::#variants(var)
|
||||
#(
|
||||
impl ::core::convert::From<#variants> for #enum_name {
|
||||
fn from(value: #variants) -> Self {
|
||||
Self::#variants(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use super::{types, util, Context};
|
||||
use crate::util::can_derive_defaults;
|
||||
//! Events expansion
|
||||
|
||||
use super::{structs::expand_event_struct, types, Context};
|
||||
use crate::util;
|
||||
use ethers_core::{
|
||||
abi::{Event, EventExt, Param},
|
||||
abi::{Event, EventExt},
|
||||
macros::{ethers_contract_crate, ethers_core_crate},
|
||||
};
|
||||
use eyre::Result;
|
||||
|
@ -21,11 +23,8 @@ impl Context {
|
|||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
// only expand enums when multiple events are present
|
||||
let events_enum_decl = if sorted_events.values().flatten().count() > 1 {
|
||||
self.expand_events_enum()
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
let events_enum_decl =
|
||||
if data_types.len() > 1 { Some(self.expand_events_enum()) } else { None };
|
||||
|
||||
Ok(quote! {
|
||||
#( #data_types )*
|
||||
|
@ -40,8 +39,7 @@ impl Context {
|
|||
let filter_methods = sorted_events
|
||||
.values()
|
||||
.flat_map(std::ops::Deref::deref)
|
||||
.map(|event| self.expand_filter(event))
|
||||
.collect::<Vec<_>>();
|
||||
.map(|event| self.expand_filter(event));
|
||||
|
||||
let events_method = self.expand_events_method();
|
||||
|
||||
|
@ -67,22 +65,19 @@ impl Context {
|
|||
let ethers_core = ethers_core_crate();
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
// use the same derives as for events
|
||||
let derives = util::expand_derives(&self.event_derives);
|
||||
let extra_derives = self.expand_extra_derives();
|
||||
let enum_name = self.expand_event_enum_name();
|
||||
|
||||
quote! {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, #ethers_contract::EthAbiType, #derives)]
|
||||
#[doc = "Container type for all of the contract's events"]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, #ethers_contract::EthAbiType, #extra_derives)]
|
||||
pub enum #enum_name {
|
||||
#(#variants(#variants)),*
|
||||
#( #variants(#variants), )*
|
||||
}
|
||||
|
||||
impl #ethers_contract::EthLogDecode for #enum_name {
|
||||
fn decode_log(log: &#ethers_core::abi::RawLog) -> ::std::result::Result<Self, #ethers_core::abi::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
#(
|
||||
impl #ethers_contract::EthLogDecode for #enum_name {
|
||||
fn decode_log(log: &#ethers_core::abi::RawLog) -> ::core::result::Result<Self, #ethers_core::abi::Error> {
|
||||
#(
|
||||
if let Ok(decoded) = #variants::decode_log(log) {
|
||||
return Ok(#enum_name::#variants(decoded))
|
||||
}
|
||||
|
@ -91,15 +86,23 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for #enum_name {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
impl ::core::fmt::Display for #enum_name {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
|
||||
match self {
|
||||
#(
|
||||
#enum_name::#variants(element) => element.fmt(f)
|
||||
),*
|
||||
Self::#variants(element) => ::core::fmt::Display::fmt(element, f),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#(
|
||||
impl ::core::convert::From<#variants> for #enum_name {
|
||||
fn from(value: #variants) -> Self {
|
||||
Self::#variants(value)
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +112,7 @@ impl Context {
|
|||
}
|
||||
|
||||
/// Expands the `events` function that bundles all declared events of this contract
|
||||
fn expand_events_method(&self) -> TokenStream {
|
||||
fn expand_events_method(&self) -> Option<TokenStream> {
|
||||
let sorted_events: BTreeMap<_, _> = self.abi.events.clone().into_iter().collect();
|
||||
|
||||
let mut iter = sorted_events.values().flatten();
|
||||
|
@ -125,28 +128,35 @@ impl Context {
|
|||
)
|
||||
};
|
||||
|
||||
quote! {
|
||||
/// Returns an [`Event`](#ethers_contract::builders::Event) builder for all events of this contract
|
||||
pub fn events(&self) -> #ethers_contract::builders::Event<Arc<M>, M, #ty> {
|
||||
self.0.event_with_filter(Default::default())
|
||||
Some(quote! {
|
||||
/// Returns an `Event` builder for all the events of this contract.
|
||||
pub fn events(&self) -> #ethers_contract::builders::Event<
|
||||
::std::sync::Arc<M>,
|
||||
M,
|
||||
#ty,
|
||||
> {
|
||||
self.0.event_with_filter(::core::default::Default::default())
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
quote! {}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Expands into a single method for contracting an event stream.
|
||||
fn expand_filter(&self, event: &Event) -> TokenStream {
|
||||
let name = &event.name;
|
||||
let alias = self.event_aliases.get(&event.abi_signature()).cloned();
|
||||
let sig = event.abi_signature();
|
||||
let alias = self.event_aliases.get(&sig).cloned();
|
||||
|
||||
// append `filter` to disambiguate with potentially conflicting
|
||||
// function names
|
||||
let function_name = if let Some(id) = alias.clone() {
|
||||
util::safe_ident(&format!("{}_filter", id.to_string().to_snake_case()))
|
||||
} else {
|
||||
util::safe_ident(&format!("{}_filter", event.name.to_snake_case()))
|
||||
// append `filter` to disambiguate with potentially conflicting function names
|
||||
let function_name = {
|
||||
let name = if let Some(ref id) = alias {
|
||||
id.to_string().to_snake_case()
|
||||
} else {
|
||||
name.to_snake_case()
|
||||
};
|
||||
util::safe_ident(&format!("{name}_filter"))
|
||||
};
|
||||
let struct_name = event_struct_name(name, alias);
|
||||
|
||||
|
@ -156,7 +166,11 @@ impl Context {
|
|||
|
||||
quote! {
|
||||
#[doc = #doc_str]
|
||||
pub fn #function_name(&self) -> #ethers_contract::builders::Event<Arc<M>, M, #struct_name> {
|
||||
pub fn #function_name(&self) -> #ethers_contract::builders::Event<
|
||||
::std::sync::Arc<M>,
|
||||
M,
|
||||
#struct_name
|
||||
> {
|
||||
self.0.event()
|
||||
}
|
||||
}
|
||||
|
@ -166,49 +180,27 @@ impl Context {
|
|||
/// into a structure or a tuple in the case where all event parameters (topics
|
||||
/// and data) are anonymous.
|
||||
fn expand_event(&self, event: &Event) -> Result<TokenStream> {
|
||||
let sig = self.event_aliases.get(&event.abi_signature()).cloned();
|
||||
let name = &event.name;
|
||||
let abi_signature = event.abi_signature();
|
||||
let event_abi_name = event.name.clone();
|
||||
let alias = self.event_aliases.get(&abi_signature).cloned();
|
||||
|
||||
let event_name = event_struct_name(&event.name, sig);
|
||||
let struct_name = event_struct_name(name, alias);
|
||||
|
||||
let params = types::expand_event_inputs(event, &self.internal_structs)?;
|
||||
let fields = types::expand_event_inputs(event, &self.internal_structs)?;
|
||||
// expand as a tuple if all fields are anonymous
|
||||
let all_anonymous_fields = event.inputs.iter().all(|input| input.name.is_empty());
|
||||
let data_type_definition = if all_anonymous_fields {
|
||||
expand_data_tuple(&event_name, ¶ms)
|
||||
} else {
|
||||
expand_data_struct(&event_name, ¶ms)
|
||||
};
|
||||
let data_type_definition = expand_event_struct(&struct_name, &fields, all_anonymous_fields);
|
||||
|
||||
let derives = util::expand_derives(&self.event_derives);
|
||||
|
||||
// rust-std only derives default automatically for arrays len <= 32
|
||||
// for large array types we skip derive(Default) <https://github.com/gakonst/ethers-rs/issues/1640>
|
||||
let derive_default = if can_derive_defaults(
|
||||
&event
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|param| Param {
|
||||
name: param.name.clone(),
|
||||
kind: param.kind.clone(),
|
||||
internal_type: None,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
) {
|
||||
quote! {
|
||||
#[derive(Default)]
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
let mut extra_derives = self.expand_extra_derives();
|
||||
if event.inputs.iter().map(|param| ¶m.kind).all(util::can_derive_default) {
|
||||
extra_derives.extend(quote!(Default));
|
||||
}
|
||||
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
Ok(quote! {
|
||||
#[derive(Clone, Debug, Eq, PartialEq, #ethers_contract::EthEvent, #ethers_contract::EthDisplay, #derives)]
|
||||
#derive_default
|
||||
#[ethevent( name = #event_abi_name, abi = #abi_signature )]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, #ethers_contract::EthEvent, #ethers_contract::EthDisplay, #extra_derives)]
|
||||
#[ethevent(name = #name, abi = #abi_signature)]
|
||||
pub #data_type_definition
|
||||
})
|
||||
}
|
||||
|
@ -231,46 +223,6 @@ pub(crate) fn event_struct_alias(event_name: &str) -> Ident {
|
|||
util::ident(&event_name.to_pascal_case())
|
||||
}
|
||||
|
||||
/// Expands an event data structure from its name-type parameter pairs. Returns
|
||||
/// a tuple with the type definition (i.e. the struct declaration) and
|
||||
/// construction (i.e. code for creating an instance of the event data).
|
||||
fn expand_data_struct(name: &Ident, params: &[(TokenStream, TokenStream, bool)]) -> TokenStream {
|
||||
let fields = params
|
||||
.iter()
|
||||
.map(|(name, ty, indexed)| {
|
||||
if *indexed {
|
||||
quote! {
|
||||
#[ethevent(indexed)]
|
||||
pub #name: #ty
|
||||
}
|
||||
} else {
|
||||
quote! { pub #name: #ty }
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! { struct #name { #( #fields, )* } }
|
||||
}
|
||||
|
||||
/// Expands an event data named tuple from its name-type parameter pairs.
|
||||
/// Returns a tuple with the type definition and construction.
|
||||
fn expand_data_tuple(name: &Ident, params: &[(TokenStream, TokenStream, bool)]) -> TokenStream {
|
||||
let fields = params
|
||||
.iter()
|
||||
.map(|(_, ty, indexed)| {
|
||||
if *indexed {
|
||||
quote! {
|
||||
#[ethevent(indexed)] pub #ty }
|
||||
} else {
|
||||
quote! {
|
||||
pub #ty }
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! { struct #name( #( #fields ),* ); }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -328,7 +280,11 @@ mod tests {
|
|||
#[doc = "Gets the contract's `Transfer` event"]
|
||||
pub fn transfer_event_filter(
|
||||
&self
|
||||
) -> ::ethers_contract::builders::Event<Arc<M>, M, TransferEventFilter> {
|
||||
) -> ::ethers_contract::builders::Event<
|
||||
::std::sync::Arc<M>,
|
||||
M,
|
||||
TransferEventFilter,
|
||||
> {
|
||||
self.0.event()
|
||||
}
|
||||
});
|
||||
|
@ -349,7 +305,8 @@ mod tests {
|
|||
#[doc = "Gets the contract's `Transfer` event"]
|
||||
pub fn transfer_filter(
|
||||
&self,
|
||||
) -> ::ethers_contract::builders::Event<Arc<M>, M, TransferFilter> {
|
||||
) -> ::ethers_contract::builders::Event<::std::sync::Arc<M>, M, TransferFilter>
|
||||
{
|
||||
self.0.event()
|
||||
}
|
||||
});
|
||||
|
@ -369,7 +326,7 @@ mod tests {
|
|||
let cx = test_context();
|
||||
let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
|
||||
let name = event_struct_name(&event.name, None);
|
||||
let definition = expand_data_struct(&name, ¶ms);
|
||||
let definition = expand_event_struct(&name, ¶ms, false);
|
||||
|
||||
assert_quote!(definition, {
|
||||
struct FooFilter {
|
||||
|
@ -394,7 +351,7 @@ mod tests {
|
|||
let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
|
||||
let alias = Some(util::ident("FooAliased"));
|
||||
let name = event_struct_name(&event.name, alias);
|
||||
let definition = expand_data_struct(&name, ¶ms);
|
||||
let definition = expand_event_struct(&name, ¶ms, false);
|
||||
|
||||
assert_quote!(definition, {
|
||||
struct FooAliasedFilter {
|
||||
|
@ -418,7 +375,7 @@ mod tests {
|
|||
let cx = test_context();
|
||||
let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
|
||||
let name = event_struct_name(&event.name, None);
|
||||
let definition = expand_data_tuple(&name, ¶ms);
|
||||
let definition = expand_event_struct(&name, ¶ms, true);
|
||||
|
||||
assert_quote!(definition, {
|
||||
struct FooFilter(pub bool, pub ::ethers_core::types::Address);
|
||||
|
@ -440,7 +397,7 @@ mod tests {
|
|||
let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
|
||||
let alias = Some(util::ident("FooAliased"));
|
||||
let name = event_struct_name(&event.name, alias);
|
||||
let definition = expand_data_tuple(&name, ¶ms);
|
||||
let definition = expand_event_struct(&name, ¶ms, true);
|
||||
|
||||
assert_quote!(definition, {
|
||||
struct FooAliasedFilter(pub bool, pub ::ethers_core::types::Address);
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use super::{
|
||||
common::{expand_data_struct, expand_data_tuple},
|
||||
types, Context,
|
||||
};
|
||||
use crate::util::{self, can_derive_defaults};
|
||||
//! Methods expansion
|
||||
|
||||
use super::{structs::expand_struct, types, Context};
|
||||
use crate::util;
|
||||
use ethers_core::{
|
||||
abi::{Function, FunctionExt, Param, ParamType},
|
||||
macros::{ethers_contract_crate, ethers_core_crate},
|
||||
|
@ -33,7 +32,7 @@ impl Context {
|
|||
.map(|function| {
|
||||
let signature = function.abi_signature();
|
||||
self.expand_function(function, aliases.get(&signature).cloned())
|
||||
.with_context(|| format!("error expanding function '{signature}'"))
|
||||
.wrap_err_with(|| eyre::eyre!("error expanding function '{signature}'"))
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
|
@ -50,11 +49,10 @@ impl Context {
|
|||
}
|
||||
|
||||
/// Returns all deploy (constructor) implementations
|
||||
pub(crate) fn deployment_methods(&self) -> TokenStream {
|
||||
if self.contract_bytecode.is_none() {
|
||||
// don't generate deploy if no bytecode
|
||||
return quote! {}
|
||||
}
|
||||
pub(crate) fn deployment_methods(&self) -> Option<TokenStream> {
|
||||
// don't generate deploy if no bytecode
|
||||
self.contract_bytecode.as_ref()?;
|
||||
|
||||
let ethers_core = ethers_core_crate();
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
|
@ -68,14 +66,14 @@ impl Context {
|
|||
#bytecode_name.clone().into()
|
||||
};
|
||||
|
||||
let deploy = quote! {
|
||||
Some(quote! {
|
||||
/// Constructs the general purpose `Deployer` instance based on the provided constructor arguments and sends it.
|
||||
/// Returns a new instance of a deployer that returns an instance of this contract after sending the transaction
|
||||
///
|
||||
/// Notes:
|
||||
/// 1. If there are no constructor arguments, you should pass `()` as the argument.
|
||||
/// 1. The default poll duration is 7 seconds.
|
||||
/// 1. The default number of confirmations is 1 block.
|
||||
/// - If there are no constructor arguments, you should pass `()` as the argument.
|
||||
/// - The default poll duration is 7 seconds.
|
||||
/// - The default number of confirmations is 1 block.
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
|
@ -86,22 +84,22 @@ impl Context {
|
|||
///
|
||||
/// ```ignore
|
||||
/// # async fn deploy<M: ethers::providers::Middleware>(client: ::std::sync::Arc<M>) {
|
||||
/// abigen!(Greeter,"../greeter.json");
|
||||
/// abigen!(Greeter, "../greeter.json");
|
||||
///
|
||||
/// let greeter_contract = Greeter::deploy(client, "Hello world!".to_string()).unwrap().send().await.unwrap();
|
||||
/// let msg = greeter_contract.greet().call().await.unwrap();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn deploy<T: #ethers_core::abi::Tokenize >(client: ::std::sync::Arc<M>, constructor_args: T) -> ::std::result::Result<#ethers_contract::builders::ContractDeployer<M, Self>, #ethers_contract::ContractError<M>> {
|
||||
let factory = #ethers_contract::ContractFactory::new(#get_abi, #get_bytecode, client);
|
||||
let deployer = factory.deploy(constructor_args)?;
|
||||
let deployer = #ethers_contract::ContractDeployer::new(deployer);
|
||||
Ok(deployer)
|
||||
pub fn deploy<T: #ethers_core::abi::Tokenize>(
|
||||
client: ::std::sync::Arc<M>,
|
||||
constructor_args: T,
|
||||
) -> ::core::result::Result<#ethers_contract::builders::ContractDeployer<M, Self>, #ethers_contract::ContractError<M>> {
|
||||
let factory = #ethers_contract::ContractFactory::new(#get_abi, #get_bytecode, client);
|
||||
let deployer = factory.deploy(constructor_args)?;
|
||||
let deployer = #ethers_contract::ContractDeployer::new(deployer);
|
||||
Ok(deployer)
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
deploy
|
||||
})
|
||||
}
|
||||
|
||||
/// Expands to the corresponding struct type based on the inputs of the given function
|
||||
|
@ -110,42 +108,30 @@ impl Context {
|
|||
function: &Function,
|
||||
alias: Option<&MethodAlias>,
|
||||
) -> Result<TokenStream> {
|
||||
let call_name = expand_call_struct_name(function, alias);
|
||||
let struct_name = expand_call_struct_name(function, alias);
|
||||
|
||||
let fields = self.expand_input_params(function)?;
|
||||
// expand as a tuple if all fields are anonymous
|
||||
let all_anonymous_fields = function.inputs.iter().all(|input| input.name.is_empty());
|
||||
let call_type_definition = if all_anonymous_fields {
|
||||
// expand to a tuple struct
|
||||
expand_data_tuple(&call_name, &fields)
|
||||
} else {
|
||||
// expand to a struct
|
||||
expand_data_struct(&call_name, &fields)
|
||||
};
|
||||
let call_type_definition = expand_struct(&struct_name, &fields, all_anonymous_fields);
|
||||
|
||||
let function_name = &function.name;
|
||||
let abi_signature = function.abi_signature();
|
||||
let doc_str = format!(
|
||||
"Container type for all input parameters for the `{function_name}` function with signature `{abi_signature}` and selector `0x{}`",
|
||||
hex::encode(&function.selector()[..])
|
||||
hex::encode(function.selector())
|
||||
);
|
||||
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
// use the same derives as for events
|
||||
let derives = util::expand_derives(&self.event_derives);
|
||||
let mut extra_derives = self.expand_extra_derives();
|
||||
if util::can_derive_defaults(&function.inputs) {
|
||||
extra_derives.extend(quote!(Default));
|
||||
}
|
||||
|
||||
// rust-std only derives default automatically for arrays len <= 32
|
||||
// for large array types we skip derive(Default) <https://github.com/gakonst/ethers-rs/issues/1640>
|
||||
let derive_default = if can_derive_defaults(&function.inputs) {
|
||||
quote! {
|
||||
#[derive(Default)]
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
Ok(quote! {
|
||||
#[doc = #doc_str]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, #ethers_contract::EthCall, #ethers_contract::EthDisplay, #derives)]
|
||||
#derive_default
|
||||
#[derive(Clone, Debug, Eq, PartialEq, #ethers_contract::EthCall, #ethers_contract::EthDisplay, #extra_derives)]
|
||||
#[ethcall( name = #function_name, abi = #abi_signature )]
|
||||
pub #call_type_definition
|
||||
})
|
||||
|
@ -156,56 +142,46 @@ impl Context {
|
|||
&self,
|
||||
function: &Function,
|
||||
alias: Option<&MethodAlias>,
|
||||
) -> Result<TokenStream> {
|
||||
let name = &function.name;
|
||||
let struct_name = expand_return_struct_name(function, alias);
|
||||
let fields = self.expand_output_params(function)?;
|
||||
) -> Result<Option<TokenStream>> {
|
||||
// no point in having structs when there is no data returned
|
||||
if function.outputs.is_empty() {
|
||||
return Ok(TokenStream::new())
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
let name = &function.name;
|
||||
|
||||
let struct_name = expand_return_struct_name(function, alias);
|
||||
let fields = self.expand_output_params(function)?;
|
||||
// expand as a tuple if all fields are anonymous
|
||||
let all_anonymous_fields = function.outputs.iter().all(|output| output.name.is_empty());
|
||||
let return_type_definition = if all_anonymous_fields {
|
||||
// expand to a tuple struct
|
||||
expand_data_tuple(&struct_name, &fields)
|
||||
} else {
|
||||
// expand to a struct
|
||||
expand_data_struct(&struct_name, &fields)
|
||||
};
|
||||
let return_type_definition = expand_struct(&struct_name, &fields, all_anonymous_fields);
|
||||
|
||||
let abi_signature = function.abi_signature();
|
||||
let doc_str = format!(
|
||||
"Container type for all return fields from the `{name}` function with signature `{abi_signature}` and selector `0x{}`",
|
||||
hex::encode(&function.selector()[..])
|
||||
hex::encode(function.selector())
|
||||
);
|
||||
|
||||
let mut extra_derives = self.expand_extra_derives();
|
||||
if util::can_derive_defaults(&function.inputs) {
|
||||
extra_derives.extend(quote!(Default));
|
||||
}
|
||||
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
// use the same derives as for events
|
||||
let derives = util::expand_derives(&self.event_derives);
|
||||
|
||||
// rust-std only derives default automatically for arrays len <= 32
|
||||
// for large array types we skip derive(Default) <https://github.com/gakonst/ethers-rs/issues/1640>
|
||||
let derive_default = if can_derive_defaults(&function.outputs) {
|
||||
quote! {
|
||||
#[derive(Default)]
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
Ok(Some(quote! {
|
||||
#[doc = #doc_str]
|
||||
#[derive(Clone, Debug,Eq, PartialEq, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #derives)]
|
||||
#derive_default
|
||||
#[derive(Clone, Debug, Eq, PartialEq, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #extra_derives)]
|
||||
pub #return_type_definition
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
/// Expands all call structs
|
||||
fn expand_call_structs(&self, aliases: BTreeMap<String, MethodAlias>) -> Result<TokenStream> {
|
||||
let mut struct_defs = Vec::new();
|
||||
let mut struct_names = Vec::new();
|
||||
let mut variant_names = Vec::new();
|
||||
let len = self.abi.functions.len();
|
||||
let mut struct_defs = Vec::with_capacity(len);
|
||||
let mut struct_names = Vec::with_capacity(len);
|
||||
let mut variant_names = Vec::with_capacity(len);
|
||||
for function in self.abi.functions.values().flatten() {
|
||||
let signature = function.abi_signature();
|
||||
let alias = aliases.get(&signature);
|
||||
|
@ -214,86 +190,86 @@ impl Context {
|
|||
variant_names.push(expand_call_struct_variant_name(function, alias));
|
||||
}
|
||||
|
||||
let struct_def_tokens = quote! {
|
||||
#(#struct_defs)*
|
||||
};
|
||||
let struct_def_tokens = quote!(#(#struct_defs)*);
|
||||
|
||||
if struct_defs.len() <= 1 {
|
||||
// no need for an enum
|
||||
return Ok(struct_def_tokens)
|
||||
}
|
||||
|
||||
let extra_derives = self.expand_extra_derives();
|
||||
|
||||
let enum_name = self.expand_calls_enum_name();
|
||||
|
||||
let ethers_core = ethers_core_crate();
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
// use the same derives as for events
|
||||
let derives = util::expand_derives(&self.event_derives);
|
||||
let enum_name = self.expand_calls_enum_name();
|
||||
|
||||
Ok(quote! {
|
||||
let tokens = quote! {
|
||||
#struct_def_tokens
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, #ethers_contract::EthAbiType, #derives)]
|
||||
#[doc = "Container type for all of the contract's call "]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, #ethers_contract::EthAbiType, #extra_derives)]
|
||||
pub enum #enum_name {
|
||||
#(#variant_names(#struct_names)),*
|
||||
#( #variant_names(#struct_names), )*
|
||||
}
|
||||
|
||||
impl #ethers_core::abi::AbiDecode for #enum_name {
|
||||
fn decode(data: impl AsRef<[u8]>) -> ::std::result::Result<Self, #ethers_core::abi::AbiError> {
|
||||
#(
|
||||
if let Ok(decoded) = <#struct_names as #ethers_core::abi::AbiDecode>::decode(data.as_ref()) {
|
||||
return Ok(#enum_name::#variant_names(decoded))
|
||||
impl #ethers_core::abi::AbiDecode for #enum_name {
|
||||
fn decode(data: impl AsRef<[u8]>) -> ::core::result::Result<Self, #ethers_core::abi::AbiError> {
|
||||
let data = data.as_ref();
|
||||
#(
|
||||
if let Ok(decoded) = <#struct_names as #ethers_core::abi::AbiDecode>::decode(data) {
|
||||
return Ok(Self::#variant_names(decoded))
|
||||
}
|
||||
)*
|
||||
Err(#ethers_core::abi::Error::InvalidData.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl #ethers_core::abi::AbiEncode for #enum_name {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
match self {
|
||||
#(
|
||||
Self::#variant_names(element) => #ethers_core::abi::AbiEncode::encode(element),
|
||||
)*
|
||||
}
|
||||
)*
|
||||
Err(#ethers_core::abi::Error::InvalidData.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl #ethers_core::abi::AbiEncode for #enum_name {
|
||||
fn encode(self) -> Vec<u8> {
|
||||
match self {
|
||||
#(
|
||||
#enum_name::#variant_names(element) => element.encode()
|
||||
),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for #enum_name {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
match self {
|
||||
#(
|
||||
#enum_name::#variant_names(element) => element.fmt(f)
|
||||
),*
|
||||
impl ::core::fmt::Display for #enum_name {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
|
||||
match self {
|
||||
#(
|
||||
Self::#variant_names(element) => ::core::fmt::Display::fmt(element, f),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#(
|
||||
impl ::std::convert::From<#struct_names> for #enum_name {
|
||||
fn from(var: #struct_names) -> Self {
|
||||
#enum_name::#variant_names(var)
|
||||
#(
|
||||
impl ::core::convert::From<#struct_names> for #enum_name {
|
||||
fn from(value: #struct_names) -> Self {
|
||||
Self::#variant_names(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
)*
|
||||
};
|
||||
|
||||
})
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
/// Expands all return structs
|
||||
fn expand_return_structs(&self, aliases: BTreeMap<String, MethodAlias>) -> Result<TokenStream> {
|
||||
let mut struct_defs = Vec::new();
|
||||
let mut tokens = TokenStream::new();
|
||||
for function in self.abi.functions.values().flatten() {
|
||||
let signature = function.abi_signature();
|
||||
let alias = aliases.get(&signature);
|
||||
struct_defs.push(self.expand_return_struct(function, alias)?);
|
||||
match self.expand_return_struct(function, alias) {
|
||||
Ok(Some(def)) => tokens.extend(def),
|
||||
Ok(None) => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
let struct_def_tokens = quote! {
|
||||
#(#struct_defs)*
|
||||
};
|
||||
|
||||
Ok(struct_def_tokens)
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
/// The name ident of the calls enum
|
||||
|
@ -597,7 +573,7 @@ impl Context {
|
|||
|
||||
fn expand_selector(selector: Selector) -> TokenStream {
|
||||
let bytes = selector.iter().copied().map(Literal::u8_unsuffixed);
|
||||
quote! { [#( #bytes ),*] }
|
||||
quote!([ #( #bytes ),* ])
|
||||
}
|
||||
|
||||
/// Represents the aliases to use when generating method related elements
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
//! Methods for expanding structs
|
||||
use crate::{
|
||||
contract::{types, Context},
|
||||
util,
|
||||
};
|
||||
//! Structs expansion
|
||||
|
||||
use super::{types, Context};
|
||||
use crate::util;
|
||||
use ethers_core::{
|
||||
abi::{
|
||||
struct_def::{FieldDeclaration, FieldType, StructFieldType, StructType},
|
||||
|
@ -15,6 +14,7 @@ use inflector::Inflector;
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use syn::Ident;
|
||||
|
||||
impl Context {
|
||||
/// Generate corresponding types for structs parsed from a human readable ABI
|
||||
|
@ -51,17 +51,17 @@ impl Context {
|
|||
/// Generates the type definition for the name that matches the given identifier
|
||||
fn generate_internal_struct(&self, id: &str) -> Result<TokenStream> {
|
||||
let sol_struct =
|
||||
self.internal_structs.structs.get(id).ok_or_else(|| eyre!("struct not found"))?;
|
||||
self.internal_structs.structs.get(id).ok_or_else(|| eyre!("Struct not found"))?;
|
||||
let struct_name = self
|
||||
.internal_structs
|
||||
.rust_type_names
|
||||
.get(id)
|
||||
.ok_or_else(|| eyre!("No types found for {}", id))?;
|
||||
.ok_or_else(|| eyre!("No types found for {id}"))?;
|
||||
let tuple = self
|
||||
.internal_structs
|
||||
.struct_tuples
|
||||
.get(id)
|
||||
.ok_or_else(|| eyre!("No types found for {}", id))?
|
||||
.ok_or_else(|| eyre!("No types found for {id}"))?
|
||||
.clone();
|
||||
self.expand_internal_struct(struct_name, sol_struct, tuple)
|
||||
}
|
||||
|
@ -95,33 +95,22 @@ impl Context {
|
|||
FieldType::Elementary(ty) => types::expand(ty)?,
|
||||
FieldType::Struct(struct_ty) => types::expand_struct_type(struct_ty),
|
||||
FieldType::Mapping(_) => {
|
||||
eyre::bail!("Mapping types in struct `{}` are not supported {:?}", name, field)
|
||||
eyre::bail!("Mapping types in struct `{name}` are not supported")
|
||||
}
|
||||
};
|
||||
|
||||
if is_tuple {
|
||||
fields.push(quote!(pub #ty));
|
||||
let field_name = if is_tuple {
|
||||
TokenStream::new()
|
||||
} else {
|
||||
let field_name = util::safe_ident(&field.name().to_snake_case());
|
||||
fields.push(quote! { pub #field_name: #ty });
|
||||
}
|
||||
quote!(#field_name)
|
||||
};
|
||||
fields.push((field_name, ty));
|
||||
}
|
||||
|
||||
let name = util::ident(name);
|
||||
|
||||
let struct_def = if is_tuple {
|
||||
quote! {
|
||||
pub struct #name(
|
||||
#( #fields ),*
|
||||
);
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
pub struct #name {
|
||||
#( #fields ),*
|
||||
}
|
||||
}
|
||||
};
|
||||
let struct_def = expand_struct(&name, &fields, is_tuple);
|
||||
|
||||
let sig = match tuple {
|
||||
ParamType::Tuple(ref types) if !types.is_empty() => util::abi_signature_types(types),
|
||||
|
@ -129,20 +118,20 @@ impl Context {
|
|||
};
|
||||
let doc_str = format!("`{name}({sig})`");
|
||||
|
||||
// use the same derives as for events
|
||||
let derives = util::expand_derives(&self.event_derives);
|
||||
let extra_derives = self.expand_extra_derives();
|
||||
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
Ok(quote! {
|
||||
#[doc = #doc_str]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #derives)]
|
||||
#struct_def
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #extra_derives)]
|
||||
pub #struct_def
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_human_readable_struct(&self, name: &str) -> Result<TokenStream> {
|
||||
let sol_struct =
|
||||
self.abi_parser.structs.get(name).ok_or_else(|| eyre!("struct not found"))?;
|
||||
self.abi_parser.structs.get(name).ok_or_else(|| eyre!("Struct `{name}` not found"))?;
|
||||
let mut fields = Vec::with_capacity(sol_struct.fields().len());
|
||||
let mut param_types = Vec::with_capacity(sol_struct.fields().len());
|
||||
for field in sol_struct.fields() {
|
||||
|
@ -162,14 +151,14 @@ impl Context {
|
|||
.abi_parser
|
||||
.struct_tuples
|
||||
.get(name)
|
||||
.ok_or_else(|| eyre!("No types found for {}", name))?
|
||||
.ok_or_else(|| eyre!("No types found for {name}"))?
|
||||
.clone();
|
||||
let tuple = ParamType::Tuple(tuple);
|
||||
|
||||
param_types.push(struct_ty.as_param(tuple));
|
||||
}
|
||||
FieldType::Mapping(_) => {
|
||||
eyre::bail!("Mapping types in struct `{}` are not supported {:?}", name, field)
|
||||
eyre::bail!("Mapping types in struct `{name}` are not supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,15 +167,16 @@ impl Context {
|
|||
|
||||
let name = util::ident(name);
|
||||
|
||||
// use the same derives as for events
|
||||
let derives = &self.event_derives;
|
||||
let derives = quote! {#(#derives),*};
|
||||
let mut extra_derives = self.expand_extra_derives();
|
||||
if param_types.iter().all(util::can_derive_default) {
|
||||
extra_derives.extend(quote!(Default))
|
||||
}
|
||||
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
Ok(quote! {
|
||||
#[doc = #abi_signature]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #derives)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #extra_derives)]
|
||||
pub struct #name {
|
||||
#( #fields ),*
|
||||
}
|
||||
|
@ -580,7 +570,7 @@ fn struct_type_name(name: &str) -> &str {
|
|||
}
|
||||
|
||||
/// `Pairing.G2Point` -> `Pairing.G2Point`
|
||||
pub fn struct_type_identifier(name: &str) -> &str {
|
||||
fn struct_type_identifier(name: &str) -> &str {
|
||||
name.trim_start_matches("struct ").split('[').next().unwrap()
|
||||
}
|
||||
|
||||
|
@ -592,16 +582,56 @@ fn struct_type_projections(name: &str) -> Vec<String> {
|
|||
iter.rev().map(str::to_string).collect()
|
||||
}
|
||||
|
||||
pub(crate) fn expand_struct(
|
||||
name: &Ident,
|
||||
fields: &[(TokenStream, TokenStream)],
|
||||
is_tuple: bool,
|
||||
) -> TokenStream {
|
||||
_expand_struct(name, fields.iter().map(|(a, b)| (a, b, false)), is_tuple)
|
||||
}
|
||||
|
||||
pub(crate) fn expand_event_struct(
|
||||
name: &Ident,
|
||||
fields: &[(TokenStream, TokenStream, bool)],
|
||||
is_tuple: bool,
|
||||
) -> TokenStream {
|
||||
_expand_struct(name, fields.iter().map(|(a, b, c)| (a, b, *c)), is_tuple)
|
||||
}
|
||||
|
||||
fn _expand_struct<'a>(
|
||||
name: &Ident,
|
||||
fields: impl Iterator<Item = (&'a TokenStream, &'a TokenStream, bool)>,
|
||||
is_tuple: bool,
|
||||
) -> TokenStream {
|
||||
let fields = fields.map(|(field, ty, indexed)| {
|
||||
(field, ty, if indexed { Some(quote!(#[ethevent(indexed)])) } else { None })
|
||||
});
|
||||
let fields = if let Some(0) = fields.size_hint().1 {
|
||||
// unit struct
|
||||
quote!(;)
|
||||
} else if is_tuple {
|
||||
// tuple struct
|
||||
let fields = fields.map(|(_, ty, indexed)| quote!(#indexed pub #ty));
|
||||
quote!(( #( #fields ),* );)
|
||||
} else {
|
||||
// struct
|
||||
let fields = fields.map(|(field, ty, indexed)| quote!(#indexed pub #field: #ty));
|
||||
quote!({ #( #fields, )* })
|
||||
};
|
||||
|
||||
quote!(struct #name #fields)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn can_determine_structs() {
|
||||
const VERIFIER_ABI: &str =
|
||||
include_str!("../../../tests/solidity-contracts/verifier_abi.json");
|
||||
let abi = serde_json::from_str::<RawAbi>(VERIFIER_ABI).unwrap();
|
||||
|
||||
let internal = InternalStructs::new(abi);
|
||||
dbg!(internal.rust_type_names);
|
||||
let _internal = InternalStructs::new(abi);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use proc_macro2::{Literal, TokenStream};
|
|||
use quote::{quote, ToTokens};
|
||||
|
||||
/// Expands a ParamType Solidity type to its Rust equivalent.
|
||||
pub fn expand(kind: &ParamType) -> Result<TokenStream> {
|
||||
pub(crate) fn expand(kind: &ParamType) -> Result<TokenStream> {
|
||||
let ethers_core = ethers_core_crate();
|
||||
|
||||
match kind {
|
||||
|
@ -58,7 +58,7 @@ pub fn expand(kind: &ParamType) -> Result<TokenStream> {
|
|||
}
|
||||
|
||||
/// Expands the event's inputs.
|
||||
pub fn expand_event_inputs(
|
||||
pub(crate) fn expand_event_inputs(
|
||||
event: &Event,
|
||||
internal_structs: &InternalStructs,
|
||||
) -> Result<Vec<(TokenStream, TokenStream, bool)>> {
|
||||
|
@ -121,7 +121,7 @@ fn expand_event_input(
|
|||
|
||||
/// Expands `params` to `(name, type)` tokens pairs, while resolving tuples' types using the given
|
||||
/// function.
|
||||
pub fn expand_params<'a, 'b, F: Fn(&'a Param) -> Option<&'b str>>(
|
||||
pub(crate) fn expand_params<'a, 'b, F: Fn(&'a Param) -> Option<&'b str>>(
|
||||
params: &'a [Param],
|
||||
resolve_tuple: F,
|
||||
) -> Result<Vec<(TokenStream, TokenStream)>> {
|
||||
|
@ -160,7 +160,7 @@ fn expand_resolved<'a, 'b, F: Fn(&'a Param) -> Option<&'b str>>(
|
|||
}
|
||||
|
||||
/// Expands to the Rust struct type.
|
||||
pub fn expand_struct_type(struct_ty: &StructFieldType) -> TokenStream {
|
||||
pub(crate) fn expand_struct_type(struct_ty: &StructFieldType) -> TokenStream {
|
||||
match struct_ty {
|
||||
StructFieldType::Type(ty) => {
|
||||
let ty = util::ident(&ty.name().to_pascal_case());
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
//! [abigen]: https://docs.rs/ethers/latest/ethers/contract/macro.abigen.html
|
||||
|
||||
#![deny(rustdoc::broken_intra_doc_links, missing_docs, unsafe_code)]
|
||||
#![warn(unreachable_pub)]
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(missing_docs)]
|
||||
|
@ -25,6 +26,8 @@ pub mod multi;
|
|||
pub use multi::MultiAbigen;
|
||||
|
||||
mod source;
|
||||
#[cfg(all(feature = "online", not(target_arch = "wasm32")))]
|
||||
pub use source::Explorer;
|
||||
pub use source::Source;
|
||||
|
||||
mod util;
|
||||
|
|
|
@ -9,10 +9,14 @@ use url::Url;
|
|||
/// An [etherscan](https://etherscan.io)-like blockchain explorer.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum Explorer {
|
||||
/// <https://etherscan.io>
|
||||
#[default]
|
||||
Etherscan,
|
||||
/// <https://bscscan.com>
|
||||
Bscscan,
|
||||
/// <https://polygonscan.com>
|
||||
Polygonscan,
|
||||
/// <https://snowtrace.io>
|
||||
Snowtrace,
|
||||
}
|
||||
|
||||
|
|
|
@ -3,35 +3,38 @@ use eyre::Result;
|
|||
use inflector::Inflector;
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::quote;
|
||||
use std::path::PathBuf;
|
||||
use syn::{Ident as SynIdent, Path};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Expands a identifier string into a token.
|
||||
pub fn ident(name: &str) -> Ident {
|
||||
/// Creates a new Ident with the given string at [`Span::call_site`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the input string is neither a keyword nor a legal variable name.
|
||||
pub(crate) fn ident(name: &str) -> Ident {
|
||||
Ident::new(name, Span::call_site())
|
||||
}
|
||||
|
||||
/// Expands an identifier string into a token and appending `_` if the
|
||||
/// identifier is for a reserved keyword.
|
||||
/// Expands an identifier string into a token and appending `_` if the identifier is for a reserved
|
||||
/// keyword.
|
||||
///
|
||||
/// Parsing keywords like `self` can fail, in this case we add an underscore.
|
||||
pub fn safe_ident(name: &str) -> Ident {
|
||||
syn::parse_str::<SynIdent>(name).unwrap_or_else(|_| ident(&format!("{name}_")))
|
||||
pub(crate) fn safe_ident(name: &str) -> Ident {
|
||||
syn::parse_str::<Ident>(name).unwrap_or_else(|_| ident(&format!("{name}_")))
|
||||
}
|
||||
|
||||
/// Converts a `&str` to `snake_case` `String` while respecting identifier rules
|
||||
pub fn safe_snake_case(ident: &str) -> String {
|
||||
pub(crate) fn safe_snake_case(ident: &str) -> String {
|
||||
safe_identifier_name(ident.to_snake_case())
|
||||
}
|
||||
|
||||
/// Converts a `&str` to `PascalCase` `String` while respecting identifier rules
|
||||
pub fn safe_pascal_case(ident: &str) -> String {
|
||||
pub(crate) fn safe_pascal_case(ident: &str) -> String {
|
||||
safe_identifier_name(ident.to_pascal_case())
|
||||
}
|
||||
|
||||
/// respects identifier rules, such as, an identifier must not start with a numeric char
|
||||
fn safe_identifier_name(name: String) -> String {
|
||||
if name.starts_with(|c: char| c.is_numeric()) {
|
||||
pub(crate) fn safe_identifier_name(name: String) -> String {
|
||||
if name.starts_with(char::is_numeric) {
|
||||
format!("_{name}")
|
||||
} else {
|
||||
name
|
||||
|
@ -39,39 +42,46 @@ fn safe_identifier_name(name: String) -> String {
|
|||
}
|
||||
|
||||
/// converts invalid rust module names to valid ones
|
||||
pub fn safe_module_name(name: &str) -> String {
|
||||
pub(crate) fn safe_module_name(name: &str) -> String {
|
||||
// handle reserve words used in contracts (eg Enum is a gnosis contract)
|
||||
safe_ident(&safe_snake_case(name)).to_string()
|
||||
}
|
||||
|
||||
/// Expands an identifier as snakecase and preserve any leading or trailing underscores
|
||||
pub fn safe_snake_case_ident(name: &str) -> Ident {
|
||||
pub(crate) fn safe_snake_case_ident(name: &str) -> Ident {
|
||||
let i = name.to_snake_case();
|
||||
ident(&preserve_underscore_delim(&i, name))
|
||||
}
|
||||
|
||||
/// Expands an identifier as pascal case and preserve any leading or trailing underscores
|
||||
pub fn safe_pascal_case_ident(name: &str) -> Ident {
|
||||
pub(crate) fn safe_pascal_case_ident(name: &str) -> Ident {
|
||||
let i = name.to_pascal_case();
|
||||
ident(&preserve_underscore_delim(&i, name))
|
||||
}
|
||||
|
||||
/// Reapplies leading and trailing underscore chars to the ident
|
||||
/// Example `ident = "pascalCase"; alias = __pascalcase__` -> `__pascalCase__`
|
||||
pub fn preserve_underscore_delim(ident: &str, alias: &str) -> String {
|
||||
alias
|
||||
.chars()
|
||||
.take_while(|c| *c == '_')
|
||||
.chain(ident.chars())
|
||||
.chain(alias.chars().rev().take_while(|c| *c == '_'))
|
||||
.collect()
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// # use ethers_contract_abigen::util::preserve_underscore_delim;
|
||||
/// assert_eq!(
|
||||
/// preserve_underscore_delim("pascalCase", "__pascalcase__"),
|
||||
/// "__pascalCase__"
|
||||
/// );
|
||||
/// ```
|
||||
pub(crate) fn preserve_underscore_delim(ident: &str, original: &str) -> String {
|
||||
let is_underscore = |c: &char| *c == '_';
|
||||
let pre = original.chars().take_while(is_underscore);
|
||||
let post = original.chars().rev().take_while(is_underscore);
|
||||
pre.chain(ident.chars()).chain(post).collect()
|
||||
}
|
||||
|
||||
/// Expands a positional identifier string that may be empty.
|
||||
///
|
||||
/// Note that this expands the parameter name with `safe_ident`, meaning that
|
||||
/// identifiers that are reserved keywords get `_` appended to them.
|
||||
pub fn expand_input_name(index: usize, name: &str) -> TokenStream {
|
||||
pub(crate) fn expand_input_name(index: usize, name: &str) -> TokenStream {
|
||||
let name_str = match name {
|
||||
"" => format!("p{index}"),
|
||||
n => n.to_snake_case(),
|
||||
|
@ -81,18 +91,14 @@ pub fn expand_input_name(index: usize, name: &str) -> TokenStream {
|
|||
quote! { #name }
|
||||
}
|
||||
|
||||
pub fn expand_derives(derives: &[Path]) -> TokenStream {
|
||||
quote! {#(#derives),*}
|
||||
}
|
||||
|
||||
/// Perform a blocking HTTP GET request and return the contents of the response as a String.
|
||||
#[cfg(all(feature = "online", not(target_arch = "wasm32")))]
|
||||
pub fn http_get(url: impl reqwest::IntoUrl) -> Result<String> {
|
||||
pub(crate) fn http_get(url: impl reqwest::IntoUrl) -> Result<String> {
|
||||
Ok(reqwest::blocking::get(url)?.text()?)
|
||||
}
|
||||
|
||||
/// Replaces any occurrences of env vars in the `raw` str with their value
|
||||
pub fn resolve_path(raw: &str) -> Result<PathBuf> {
|
||||
pub(crate) fn resolve_path(raw: &str) -> Result<PathBuf> {
|
||||
let mut unprocessed = raw;
|
||||
let mut resolved = String::new();
|
||||
|
||||
|
@ -107,7 +113,7 @@ pub fn resolve_path(raw: &str) -> Result<PathBuf> {
|
|||
unprocessed = rest;
|
||||
}
|
||||
None => {
|
||||
eyre::bail!("Unable to parse a variable from \"{}\"", tail)
|
||||
eyre::bail!("Unable to parse a variable from \"{tail}\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +155,7 @@ fn take_while(s: &str, mut predicate: impl FnMut(char) -> bool) -> (&str, &str)
|
|||
}
|
||||
|
||||
/// Returns a list of absolute paths to all the json files under the root
|
||||
pub fn json_files(root: impl AsRef<std::path::Path>) -> Vec<PathBuf> {
|
||||
pub(crate) fn json_files(root: impl AsRef<Path>) -> Vec<PathBuf> {
|
||||
walkdir::WalkDir::new(root)
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
|
@ -162,14 +168,14 @@ pub fn json_files(root: impl AsRef<std::path::Path>) -> Vec<PathBuf> {
|
|||
/// Returns whether all the given parameters can derive [`Default`].
|
||||
///
|
||||
/// rust-std derives `Default` automatically only for arrays len <= 32
|
||||
pub fn can_derive_defaults<'a>(params: impl IntoIterator<Item = &'a Param>) -> bool {
|
||||
pub(crate) fn can_derive_defaults<'a>(params: impl IntoIterator<Item = &'a Param>) -> bool {
|
||||
params.into_iter().map(|param| ¶m.kind).all(can_derive_default)
|
||||
}
|
||||
|
||||
/// Returns whether the given type can derive [`Default`].
|
||||
///
|
||||
/// rust-std derives `Default` automatically only for arrays len <= 32
|
||||
pub fn can_derive_default(param: &ParamType) -> bool {
|
||||
pub(crate) fn can_derive_default(param: &ParamType) -> bool {
|
||||
const MAX_SUPPORTED_LEN: usize = 32;
|
||||
match param {
|
||||
ParamType::FixedBytes(len) => *len <= MAX_SUPPORTED_LEN,
|
||||
|
@ -186,7 +192,7 @@ pub fn can_derive_default(param: &ParamType) -> bool {
|
|||
}
|
||||
|
||||
/// Returns the formatted Solidity ABI signature.
|
||||
pub fn abi_signature<'a, N, T>(name: N, types: T) -> String
|
||||
pub(crate) fn abi_signature<'a, N, T>(name: N, types: T) -> String
|
||||
where
|
||||
N: std::fmt::Display,
|
||||
T: IntoIterator<Item = &'a ParamType>,
|
||||
|
@ -196,7 +202,7 @@ where
|
|||
}
|
||||
|
||||
/// Returns the Solidity stringified ABI types joined by a single comma.
|
||||
pub fn abi_signature_types<'a, T: IntoIterator<Item = &'a ParamType>>(types: T) -> String {
|
||||
pub(crate) fn abi_signature_types<'a, T: IntoIterator<Item = &'a ParamType>>(types: T) -> String {
|
||||
types.into_iter().map(ToString::to_string).collect::<Vec<_>>().join(",")
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ use syn::{
|
|||
};
|
||||
|
||||
/// A series of `ContractArgs` separated by `;`
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Contracts {
|
||||
inner: Vec<(Span, ContractArgs)>,
|
||||
pub(crate) inner: Vec<(Span, ContractArgs)>,
|
||||
}
|
||||
|
||||
impl Contracts {
|
||||
|
@ -57,7 +57,7 @@ impl Parse for Contracts {
|
|||
}
|
||||
|
||||
/// Contract procedural macro arguments.
|
||||
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) struct ContractArgs {
|
||||
name: String,
|
||||
abi: String,
|
||||
|
@ -128,7 +128,7 @@ impl ParseInner for ContractArgs {
|
|||
}
|
||||
|
||||
/// A single procedural macro parameter.
|
||||
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
enum Parameter {
|
||||
Methods(Vec<Method>),
|
||||
Derives(Vec<String>),
|
||||
|
@ -189,7 +189,7 @@ impl Parse for Parameter {
|
|||
}
|
||||
|
||||
/// An explicitely named contract method.
|
||||
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct Method {
|
||||
signature: String,
|
||||
alias: String,
|
||||
|
|
|
@ -205,9 +205,9 @@ fn derive_decode_from_log_impl(
|
|||
// decode
|
||||
let (signature_check, flat_topics_init, topic_tokens_len_check) = if event.anonymous {
|
||||
(
|
||||
quote! {},
|
||||
None,
|
||||
quote! {
|
||||
let flat_topics = topics.iter().flat_map(|t| t.as_ref().to_vec()).collect::<Vec<u8>>();
|
||||
let flat_topics = topics.iter().flat_map(|t| t.as_ref().to_vec()).collect::<Vec<u8>>();
|
||||
},
|
||||
quote! {
|
||||
if topic_tokens.len() != topics.len() {
|
||||
|
@ -217,12 +217,12 @@ fn derive_decode_from_log_impl(
|
|||
)
|
||||
} else {
|
||||
(
|
||||
quote! {
|
||||
Some(quote! {
|
||||
let event_signature = topics.get(0).ok_or(#ethers_core::abi::Error::InvalidData)?;
|
||||
if event_signature != &Self::signature() {
|
||||
return Err(#ethers_core::abi::Error::InvalidData);
|
||||
}
|
||||
},
|
||||
}),
|
||||
quote! {
|
||||
let flat_topics = topics.iter().skip(1).flat_map(|t| t.as_ref().to_vec()).collect::<Vec<u8>>();
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue