refactor(contract): derive procedural macros (#2152)
* refactor: use Result<_, Error> * fix: report both errors during parsing * refactor: abigen derive results * update Event derive * refactor: derive utils * fmt Display derive * fmt Codec derive * refactor: derives * fix artifacts * chore: clippy
This commit is contained in:
parent
37acf65dae
commit
d8597202ed
|
@ -1392,6 +1392,7 @@ version = "1.0.2"
|
|||
dependencies = [
|
||||
"ethers-contract-abigen",
|
||||
"ethers-core",
|
||||
"eyre",
|
||||
"hex",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -20,12 +20,14 @@ proc-macro = true
|
|||
ethers-core = { version = "^1.0.0", path = "../../ethers-core" }
|
||||
ethers-contract-abigen = { version = "^1.0.0", path = "../ethers-contract-abigen", default-features = false }
|
||||
|
||||
serde_json = "1.0.53"
|
||||
hex = { version = "0.4.3", default-features = false, features = ["std"] }
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = "1.0.12"
|
||||
|
||||
serde_json = "1.0.53"
|
||||
hex = { version = "0.4.3", default-features = false, features = ["std"] }
|
||||
eyre = "0.6"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
|
|
@ -4,42 +4,32 @@ use crate::utils;
|
|||
use ethers_core::macros::ethers_core_crate;
|
||||
use proc_macro2::{Ident, Literal, TokenStream};
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::{parse::Error, spanned::Spanned as _, Data, DeriveInput, Fields, Variant};
|
||||
use syn::{parse::Error, spanned::Spanned, Data, DeriveInput, Fields, Variant};
|
||||
|
||||
/// Generates the tokenize implementation
|
||||
pub fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream {
|
||||
let core_crate = ethers_core_crate();
|
||||
pub fn derive_tokenizeable_impl(input: &DeriveInput) -> Result<TokenStream, Error> {
|
||||
let ethers_core = ethers_core_crate();
|
||||
let name = &input.ident;
|
||||
let generic_params = input.generics.params.iter().map(|p| quote! { #p });
|
||||
let generic_params = quote! { #(#generic_params,)* };
|
||||
|
||||
let generic_args = input.generics.type_params().map(|p| {
|
||||
let name = &p.ident;
|
||||
quote_spanned! { p.ident.span() => #name }
|
||||
});
|
||||
|
||||
let generic_args = quote! { #(#generic_args,)* };
|
||||
|
||||
let generic_predicates = match input.generics.where_clause {
|
||||
Some(ref clause) => {
|
||||
let predicates = clause.predicates.iter().map(|p| quote! { #p });
|
||||
quote! { #(#predicates,)* }
|
||||
}
|
||||
None => quote! {},
|
||||
};
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
let generic_predicates = where_clause.map(|c| &c.predicates);
|
||||
|
||||
let (tokenize_predicates, params_len, init_struct_impl, into_token_impl) = match input.data {
|
||||
Data::Struct(ref data) => match data.fields {
|
||||
Fields::Named(ref fields) => {
|
||||
let tokenize_predicates = fields.named.iter().map(|f| {
|
||||
let ty = &f.ty;
|
||||
quote_spanned! { f.span() => #ty: #core_crate::abi::Tokenize }
|
||||
quote_spanned! { f.span() => #ty: #ethers_core::abi::Tokenize }
|
||||
});
|
||||
let tokenize_predicates = quote! { #(#tokenize_predicates,)* };
|
||||
|
||||
let assignments = fields.named.iter().map(|f| {
|
||||
let name = f.ident.as_ref().expect("Named fields have names");
|
||||
quote_spanned! { f.span() => #name: #core_crate::abi::Tokenizable::from_token(iter.next().expect("The iter is guaranteed to be something due to the size check"))? }
|
||||
quote_spanned! { f.span() =>
|
||||
#name: #ethers_core::abi::Tokenizable::from_token(
|
||||
iter.next().expect("The iter is guaranteed to be something due to the size check")
|
||||
)?
|
||||
}
|
||||
});
|
||||
let init_struct_impl = quote! { Self { #(#assignments,)* } };
|
||||
|
||||
|
@ -54,12 +44,16 @@ pub fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream
|
|||
Fields::Unnamed(ref fields) => {
|
||||
let tokenize_predicates = fields.unnamed.iter().map(|f| {
|
||||
let ty = &f.ty;
|
||||
quote_spanned! { f.span() => #ty: #core_crate::abi::Tokenize }
|
||||
quote_spanned! { f.span() => #ty: #ethers_core::abi::Tokenize }
|
||||
});
|
||||
let tokenize_predicates = quote! { #(#tokenize_predicates,)* };
|
||||
|
||||
let assignments = fields.unnamed.iter().map(|f| {
|
||||
quote_spanned! { f.span() => #core_crate::abi::Tokenizable::from_token(iter.next().expect("The iter is guaranteed to be something due to the size check"))? }
|
||||
quote_spanned! { f.span() =>
|
||||
#ethers_core::abi::Tokenizable::from_token(
|
||||
iter.next().expect("The iter is guaranteed to be something due to the size check")
|
||||
)?
|
||||
}
|
||||
});
|
||||
let init_struct_impl = quote! { Self(#(#assignments,)* ) };
|
||||
|
||||
|
@ -71,17 +65,11 @@ pub fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream
|
|||
|
||||
(tokenize_predicates, fields.unnamed.len(), init_struct_impl, into_token_impl)
|
||||
}
|
||||
Fields::Unit => return tokenize_unit_type(&input.ident),
|
||||
Fields::Unit => return Ok(tokenize_unit_type(&input.ident)),
|
||||
},
|
||||
Data::Enum(ref data) => {
|
||||
return match tokenize_enum(name, data.variants.iter()) {
|
||||
Ok(tokens) => tokens,
|
||||
Err(err) => err.to_compile_error(),
|
||||
}
|
||||
}
|
||||
Data::Enum(ref data) => return tokenize_enum(name, data.variants.iter()),
|
||||
Data::Union(_) => {
|
||||
return Error::new(input.span(), "EthAbiType cannot be derived for unions")
|
||||
.to_compile_error()
|
||||
return Err(Error::new(input.span(), "EthAbiType cannot be derived for unions"))
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -95,14 +83,14 @@ pub fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream
|
|||
// can't encode an empty struct
|
||||
// TODO: panic instead?
|
||||
quote! {
|
||||
#core_crate::abi::Token::Tuple(Vec::new())
|
||||
#ethers_core::abi::Token::Tuple(Vec::new())
|
||||
},
|
||||
),
|
||||
_ => {
|
||||
let from_token = quote! {
|
||||
if let #core_crate::abi::Token::Tuple(tokens) = token {
|
||||
if let #ethers_core::abi::Token::Tuple(tokens) = token {
|
||||
if tokens.len() != #params_len {
|
||||
return Err(#core_crate::abi::InvalidOutputType(::std::format!(
|
||||
return Err(#ethers_core::abi::InvalidOutputType(::std::format!(
|
||||
"Expected {} tokens, got {}: {:?}",
|
||||
#params_len,
|
||||
tokens.len(),
|
||||
|
@ -114,7 +102,7 @@ pub fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream
|
|||
|
||||
Ok(#init_struct_impl)
|
||||
} else {
|
||||
Err(#core_crate::abi::InvalidOutputType(::std::format!(
|
||||
Err(#ethers_core::abi::InvalidOutputType(::std::format!(
|
||||
"Expected Tuple, got {:?}",
|
||||
token
|
||||
)))
|
||||
|
@ -122,7 +110,7 @@ pub fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream
|
|||
};
|
||||
|
||||
let into_token = quote! {
|
||||
#core_crate::abi::Token::Tuple(
|
||||
#ethers_core::abi::Token::Tuple(
|
||||
::std::vec![
|
||||
#into_token_impl
|
||||
]
|
||||
|
@ -132,50 +120,51 @@ pub fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream
|
|||
}
|
||||
};
|
||||
|
||||
let params = match utils::derive_param_type_with_abi_type(input, "EthAbiType") {
|
||||
Ok(params) => params,
|
||||
Err(err) => return err.to_compile_error(),
|
||||
};
|
||||
quote! {
|
||||
let params = utils::derive_param_type_with_abi_type(input, "EthAbiType")?;
|
||||
|
||||
impl<#generic_params> #core_crate::abi::AbiType for #name<#generic_args> {
|
||||
fn param_type() -> #core_crate::abi::ParamType {
|
||||
Ok(quote! {
|
||||
impl #impl_generics #ethers_core::abi::AbiType for #name #ty_generics #where_clause {
|
||||
fn param_type() -> #ethers_core::abi::ParamType {
|
||||
#params
|
||||
}
|
||||
}
|
||||
|
||||
impl<#generic_params> #core_crate::abi::AbiArrayType for #name<#generic_args> {}
|
||||
impl #impl_generics #ethers_core::abi::AbiArrayType for #name #ty_generics #where_clause {}
|
||||
|
||||
impl<#generic_params> #core_crate::abi::Tokenizable for #name<#generic_args>
|
||||
impl #impl_generics #ethers_core::abi::Tokenizable for #name #ty_generics
|
||||
where
|
||||
#generic_predicates
|
||||
#tokenize_predicates
|
||||
{
|
||||
|
||||
fn from_token(token: #core_crate::abi::Token) -> ::std::result::Result<Self, #core_crate::abi::InvalidOutputType> where
|
||||
Self: Sized {
|
||||
fn from_token(token: #ethers_core::abi::Token) -> ::std::result::Result<Self, #ethers_core::abi::InvalidOutputType>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
#from_token_impl
|
||||
}
|
||||
|
||||
fn into_token(self) -> #core_crate::abi::Token {
|
||||
fn into_token(self) -> #ethers_core::abi::Token {
|
||||
#into_token_impl
|
||||
}
|
||||
}
|
||||
|
||||
impl<#generic_params> #core_crate::abi::TokenizableItem for #name<#generic_args>
|
||||
impl #impl_generics #ethers_core::abi::TokenizableItem for #name #ty_generics
|
||||
where
|
||||
#generic_predicates
|
||||
#tokenize_predicates
|
||||
{ }
|
||||
}
|
||||
{}
|
||||
})
|
||||
}
|
||||
|
||||
fn tokenize_unit_type(name: &Ident) -> TokenStream {
|
||||
let ethers_core = ethers_core_crate();
|
||||
|
||||
quote! {
|
||||
impl #ethers_core::abi::Tokenizable for #name {
|
||||
fn from_token(token: #ethers_core::abi::Token) -> ::std::result::Result<Self, #ethers_core::abi::InvalidOutputType> where
|
||||
Self: Sized {
|
||||
fn from_token(token: #ethers_core::abi::Token) -> ::std::result::Result<Self, #ethers_core::abi::InvalidOutputType>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
if let #ethers_core::abi::Token::Tuple(tokens) = token {
|
||||
if !tokens.is_empty() {
|
||||
Err(#ethers_core::abi::InvalidOutputType(::std::format!(
|
||||
|
@ -197,7 +186,8 @@ fn tokenize_unit_type(name: &Ident) -> TokenStream {
|
|||
#ethers_core::abi::Token::Tuple(::std::vec::Vec::new())
|
||||
}
|
||||
}
|
||||
impl #ethers_core::abi::TokenizableItem for #name { }
|
||||
|
||||
impl #ethers_core::abi::TokenizableItem for #name {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,7 +200,7 @@ fn tokenize_unit_type(name: &Ident) -> TokenStream {
|
|||
fn tokenize_enum<'a>(
|
||||
enum_name: &Ident,
|
||||
variants: impl Iterator<Item = &'a Variant> + 'a,
|
||||
) -> ::std::result::Result<TokenStream, Error> {
|
||||
) -> Result<TokenStream, Error> {
|
||||
let ethers_core = ethers_core_crate();
|
||||
|
||||
let mut into_tokens = TokenStream::new();
|
||||
|
@ -251,9 +241,10 @@ fn tokenize_enum<'a>(
|
|||
|
||||
Ok(quote! {
|
||||
impl #ethers_core::abi::Tokenizable for #enum_name {
|
||||
|
||||
fn from_token(token: #ethers_core::abi::Token) -> ::std::result::Result<Self, #ethers_core::abi::InvalidOutputType> where
|
||||
Self: Sized {
|
||||
fn from_token(token: #ethers_core::abi::Token) -> ::std::result::Result<Self, #ethers_core::abi::InvalidOutputType>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
#from_tokens
|
||||
Err(#ethers_core::abi::InvalidOutputType("Failed to decode all type variants".to_string()))
|
||||
}
|
||||
|
@ -264,6 +255,7 @@ fn tokenize_enum<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
impl #ethers_core::abi::TokenizableItem for #enum_name { }
|
||||
|
||||
impl #ethers_core::abi::TokenizableItem for #enum_name {}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -8,14 +8,15 @@ use ethers_contract_abigen::{
|
|||
Abigen,
|
||||
};
|
||||
use ethers_core::abi::{Function, FunctionExt, Param, StateMutability};
|
||||
use eyre::Result;
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::ToTokens;
|
||||
use std::{collections::HashSet, error::Error};
|
||||
use std::collections::HashSet;
|
||||
use syn::{
|
||||
braced,
|
||||
ext::IdentExt,
|
||||
parenthesized,
|
||||
parse::{Error as ParseError, Parse, ParseStream, Result as ParseResult},
|
||||
parse::{Error, Parse, ParseStream, Result as ParseResult},
|
||||
Ident, LitStr, Path, Token,
|
||||
};
|
||||
|
||||
|
@ -26,13 +27,13 @@ pub(crate) struct Contracts {
|
|||
}
|
||||
|
||||
impl Contracts {
|
||||
pub(crate) fn expand(self) -> ::std::result::Result<TokenStream2, syn::Error> {
|
||||
pub(crate) fn expand(self) -> Result<TokenStream2, Error> {
|
||||
let mut expansions = Vec::with_capacity(self.inner.len());
|
||||
|
||||
// expand all contracts
|
||||
for (span, contract) in self.inner {
|
||||
let contract = Self::expand_contract(contract)
|
||||
.map_err(|err| syn::Error::new(span, err.to_string()))?;
|
||||
let contract =
|
||||
Self::expand_contract(contract).map_err(|err| Error::new(span, err.to_string()))?;
|
||||
expansions.push(contract);
|
||||
}
|
||||
|
||||
|
@ -40,10 +41,8 @@ impl Contracts {
|
|||
Ok(MultiExpansion::new(expansions).expand_inplace())
|
||||
}
|
||||
|
||||
fn expand_contract(
|
||||
contract: ContractArgs,
|
||||
) -> Result<(ExpandedContract, Context), Box<dyn Error>> {
|
||||
Ok(contract.into_builder()?.expand()?)
|
||||
fn expand_contract(contract: ContractArgs) -> Result<(ExpandedContract, Context)> {
|
||||
contract.into_builder()?.expand()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +65,7 @@ pub(crate) struct ContractArgs {
|
|||
}
|
||||
|
||||
impl ContractArgs {
|
||||
fn into_builder(self) -> Result<Abigen, Box<dyn Error>> {
|
||||
fn into_builder(self) -> Result<Abigen> {
|
||||
let mut builder = Abigen::new(&self.name, &self.abi)?;
|
||||
|
||||
for parameter in self.parameters.into_iter() {
|
||||
|
@ -151,13 +150,13 @@ impl Parse for Parameter {
|
|||
let mut aliases = HashSet::new();
|
||||
for method in parsed {
|
||||
if !signatures.insert(method.signature.clone()) {
|
||||
return Err(ParseError::new(
|
||||
return Err(Error::new(
|
||||
method.span(),
|
||||
"duplicate method signature in `abigen!` macro invocation",
|
||||
))
|
||||
}
|
||||
if !aliases.insert(method.alias.clone()) {
|
||||
return Err(ParseError::new(
|
||||
return Err(Error::new(
|
||||
method.span(),
|
||||
"duplicate method alias in `abigen!` macro invocation",
|
||||
))
|
||||
|
@ -181,10 +180,7 @@ impl Parse for Parameter {
|
|||
Parameter::Derives(derives)
|
||||
}
|
||||
_ => {
|
||||
return Err(ParseError::new(
|
||||
name.span(),
|
||||
format!("unexpected named parameter `{name}`"),
|
||||
))
|
||||
return Err(Error::new(name.span(), format!("unexpected named parameter `{name}`")))
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -211,7 +207,7 @@ impl Parse for Method {
|
|||
.iter()
|
||||
.map(|ident| {
|
||||
let kind = serde_json::from_value(serde_json::json!(&ident.to_string()))
|
||||
.map_err(|err| ParseError::new(ident.span(), err))?;
|
||||
.map_err(|err| Error::new(ident.span(), err))?;
|
||||
Ok(Param { name: "".into(), kind, internal_type: None })
|
||||
})
|
||||
.collect::<ParseResult<Vec<_>>>()?;
|
||||
|
|
|
@ -10,50 +10,40 @@ use quote::quote;
|
|||
use syn::{parse::Error, DeriveInput};
|
||||
|
||||
/// Generates the `ethcall` trait support
|
||||
pub(crate) fn derive_eth_call_impl(input: DeriveInput) -> TokenStream {
|
||||
let attributes = match parse_calllike_attributes(&input, "ethcall") {
|
||||
Ok(attributes) => attributes,
|
||||
Err(errors) => return errors,
|
||||
};
|
||||
pub(crate) fn derive_eth_call_impl(input: DeriveInput) -> Result<TokenStream, Error> {
|
||||
let attributes = parse_calllike_attributes(&input, "ethcall")?;
|
||||
|
||||
let function_call_name =
|
||||
attributes.name.map(|(s, _)| s).unwrap_or_else(|| input.ident.to_string());
|
||||
|
||||
let mut function = if let Some((src, span)) = attributes.abi {
|
||||
let raw_function_sig = src.trim_start_matches("function ").trim_start();
|
||||
let mut function = if let Some((abi, span)) = attributes.abi {
|
||||
let sig = abi.trim_start_matches("function ").trim_start();
|
||||
// try to parse as solidity function
|
||||
if let Ok(fun) = HumanReadableParser::parse_function(&src) {
|
||||
fun
|
||||
} else {
|
||||
// try to determine the abi by using its fields at runtime
|
||||
return match derive_trait_impls_with_abi_type(
|
||||
&input,
|
||||
&function_call_name,
|
||||
Some(raw_function_sig),
|
||||
) {
|
||||
Ok(derived) => derived,
|
||||
Err(err) => {
|
||||
Error::new(span, format!("Unable to determine ABI for `{src}` : {err}"))
|
||||
.to_compile_error()
|
||||
}
|
||||
match HumanReadableParser::parse_function(&abi) {
|
||||
Ok(fun) => fun,
|
||||
Err(parse_err) => {
|
||||
return derive_trait_impls_with_abi_type(&input, &function_call_name, Some(sig))
|
||||
.map_err(|e| {
|
||||
let mut error = Error::new(span, parse_err);
|
||||
error.combine(Error::new(span, e));
|
||||
error
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// try to determine the abi by using its fields at runtime
|
||||
return match derive_trait_impls_with_abi_type(&input, &function_call_name, None) {
|
||||
Ok(derived) => derived,
|
||||
Err(err) => err.to_compile_error(),
|
||||
}
|
||||
return derive_trait_impls_with_abi_type(&input, &function_call_name, None)
|
||||
};
|
||||
function.name = function_call_name.clone();
|
||||
let abi = function.abi_signature();
|
||||
|
||||
let sig = function.abi_signature();
|
||||
let selector = utils::selector(function.selector());
|
||||
let decode_impl = derive_decode_impl_from_params(&function.inputs, ident("EthCall"));
|
||||
|
||||
derive_trait_impls(
|
||||
&input,
|
||||
&function_call_name,
|
||||
quote! {#abi.into()},
|
||||
quote!(#sig.into()),
|
||||
Some(selector),
|
||||
decode_impl,
|
||||
)
|
||||
|
@ -65,17 +55,14 @@ fn derive_trait_impls_with_abi_type(
|
|||
function_call_name: &str,
|
||||
abi_signature: Option<&str>,
|
||||
) -> Result<TokenStream, Error> {
|
||||
let abi_signature = if let Some(abi) = abi_signature {
|
||||
quote! {#abi}
|
||||
let mut abi_signature = if let Some(sig) = abi_signature {
|
||||
quote!(#sig)
|
||||
} else {
|
||||
utils::derive_abi_signature_with_abi_type(input, function_call_name, "EthCall")?
|
||||
};
|
||||
|
||||
let abi_signature = quote! {
|
||||
#abi_signature.into()
|
||||
utils::abi_signature_with_abi_type(input, function_call_name, "EthCall")?
|
||||
};
|
||||
abi_signature.extend(quote!(.into()));
|
||||
let decode_impl = derive_decode_impl_with_abi_type(input, ident("EthCall"))?;
|
||||
Ok(derive_trait_impls(input, function_call_name, abi_signature, None, decode_impl))
|
||||
derive_trait_impls(input, function_call_name, abi_signature, None, decode_impl)
|
||||
}
|
||||
|
||||
/// Generates the EthCall implementation
|
||||
|
@ -85,26 +72,25 @@ pub fn derive_trait_impls(
|
|||
abi_signature: TokenStream,
|
||||
selector: Option<TokenStream>,
|
||||
decode_impl: TokenStream,
|
||||
) -> TokenStream {
|
||||
) -> Result<TokenStream, Error> {
|
||||
// the ethers crates to use
|
||||
let core_crate = ethers_core_crate();
|
||||
let contract_crate = ethers_contract_crate();
|
||||
let ethers_core = ethers_core_crate();
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
let struct_name = &input.ident;
|
||||
|
||||
let selector = selector.unwrap_or_else(|| {
|
||||
quote! {
|
||||
#core_crate::utils::id(Self::abi_signature())
|
||||
#ethers_core::utils::id(Self::abi_signature())
|
||||
}
|
||||
});
|
||||
|
||||
let ethcall_impl = quote! {
|
||||
impl #contract_crate::EthCall for #struct_name {
|
||||
|
||||
impl #ethers_contract::EthCall for #struct_name {
|
||||
fn function_name() -> ::std::borrow::Cow<'static, str> {
|
||||
#function_call_name.into()
|
||||
}
|
||||
|
||||
fn selector() -> #core_crate::types::Selector {
|
||||
fn selector() -> #ethers_core::types::Selector {
|
||||
#selector
|
||||
}
|
||||
|
||||
|
@ -113,10 +99,10 @@ pub fn derive_trait_impls(
|
|||
}
|
||||
}
|
||||
};
|
||||
let codec_impl = derive_codec_impls(input, decode_impl, ident("EthCall"));
|
||||
let codec_impl = derive_codec_impls(input, decode_impl, ident("EthCall"))?;
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
#ethcall_impl
|
||||
#codec_impl
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use ethers_core::{
|
|||
};
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{parse::Error, spanned::Spanned as _, AttrStyle, DeriveInput, Lit, Meta, NestedMeta};
|
||||
use syn::{parse::Error, spanned::Spanned, AttrStyle, DeriveInput, Lit, Meta, NestedMeta};
|
||||
|
||||
/// All the attributes the `EthCall`/`EthError` macro supports
|
||||
#[derive(Default)]
|
||||
|
@ -20,7 +20,7 @@ pub struct EthCalllikeAttributes {
|
|||
pub fn parse_calllike_attributes(
|
||||
input: &DeriveInput,
|
||||
attr_name: &str,
|
||||
) -> Result<EthCalllikeAttributes, TokenStream> {
|
||||
) -> Result<EthCalllikeAttributes, Error> {
|
||||
let mut result = EthCalllikeAttributes::default();
|
||||
for a in input.attrs.iter() {
|
||||
if let AttrStyle::Outer = a.style {
|
||||
|
@ -33,15 +33,13 @@ pub fn parse_calllike_attributes(
|
|||
return Err(Error::new(
|
||||
path.span(),
|
||||
format!("unrecognized {attr_name} parameter"),
|
||||
)
|
||||
.to_compile_error())
|
||||
))
|
||||
}
|
||||
Meta::List(meta) => {
|
||||
return Err(Error::new(
|
||||
meta.path.span(),
|
||||
format!("unrecognized {attr_name} parameter"),
|
||||
)
|
||||
.to_compile_error())
|
||||
))
|
||||
}
|
||||
Meta::NameValue(meta) => {
|
||||
if meta.path.is_ident("name") {
|
||||
|
@ -53,15 +51,13 @@ pub fn parse_calllike_attributes(
|
|||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"name already specified",
|
||||
)
|
||||
.to_compile_error())
|
||||
))
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"name must be a string",
|
||||
)
|
||||
.to_compile_error())
|
||||
))
|
||||
}
|
||||
} else if meta.path.is_ident("abi") {
|
||||
if let Lit::Str(ref lit_str) = meta.lit {
|
||||
|
@ -72,22 +68,19 @@ pub fn parse_calllike_attributes(
|
|||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"abi already specified",
|
||||
)
|
||||
.to_compile_error())
|
||||
))
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"abi must be a string",
|
||||
)
|
||||
.to_compile_error())
|
||||
))
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
format!("unrecognized {attr_name} parameter"),
|
||||
)
|
||||
.to_compile_error())
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +98,7 @@ pub fn derive_decode_impl_with_abi_type(
|
|||
input: &DeriveInput,
|
||||
trait_ident: Ident,
|
||||
) -> Result<TokenStream, Error> {
|
||||
let datatypes_array = utils::derive_abi_parameters_array(input, &trait_ident.to_string())?;
|
||||
let datatypes_array = utils::abi_parameters_array(input, &trait_ident.to_string())?;
|
||||
Ok(derive_decode_impl(datatypes_array, trait_ident))
|
||||
}
|
||||
|
||||
|
@ -117,18 +110,18 @@ pub fn derive_decode_impl_from_params(params: &[Param], trait_ident: Ident) -> T
|
|||
}
|
||||
|
||||
pub fn derive_decode_impl(datatypes_array: TokenStream, trait_ident: Ident) -> TokenStream {
|
||||
let core_crate = ethers_core_crate();
|
||||
let contract_crate = ethers_contract_crate();
|
||||
let ethers_core = ethers_core_crate();
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
let data_types_init = quote! {let data_types = #datatypes_array;};
|
||||
|
||||
quote! {
|
||||
let bytes = bytes.as_ref();
|
||||
if bytes.len() < 4 || bytes[..4] != <Self as #contract_crate::#trait_ident>::selector() {
|
||||
return Err(#contract_crate::AbiError::WrongSelector);
|
||||
if bytes.len() < 4 || bytes[..4] != <Self as #ethers_contract::#trait_ident>::selector() {
|
||||
return Err(#ethers_contract::AbiError::WrongSelector);
|
||||
}
|
||||
#data_types_init
|
||||
let data_tokens = #core_crate::abi::decode(&data_types, &bytes[4..])?;
|
||||
Ok(<Self as #core_crate::abi::Tokenizable>::from_token( #core_crate::abi::Token::Tuple(data_tokens))?)
|
||||
let data_tokens = #ethers_core::abi::decode(&data_types, &bytes[4..])?;
|
||||
Ok(<Self as #ethers_core::abi::Tokenizable>::from_token(#ethers_core::abi::Token::Tuple(data_tokens))?)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,25 +130,24 @@ pub fn derive_codec_impls(
|
|||
input: &DeriveInput,
|
||||
decode_impl: TokenStream,
|
||||
trait_ident: Ident,
|
||||
) -> TokenStream {
|
||||
) -> Result<TokenStream, Error> {
|
||||
// the ethers crates to use
|
||||
let core_crate = ethers_core_crate();
|
||||
let contract_crate = ethers_contract_crate();
|
||||
let ethers_core = ethers_core_crate();
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
let struct_name = &input.ident;
|
||||
|
||||
let codec_impl = quote! {
|
||||
|
||||
impl #core_crate::abi::AbiDecode for #struct_name {
|
||||
fn decode(bytes: impl AsRef<[u8]>) -> ::std::result::Result<Self, #core_crate::abi::AbiError> {
|
||||
impl #ethers_core::abi::AbiDecode for #struct_name {
|
||||
fn decode(bytes: impl AsRef<[u8]>) -> ::std::result::Result<Self, #ethers_core::abi::AbiError> {
|
||||
#decode_impl
|
||||
}
|
||||
}
|
||||
|
||||
impl #core_crate::abi::AbiEncode for #struct_name {
|
||||
impl #ethers_core::abi::AbiEncode for #struct_name {
|
||||
fn encode(self) -> ::std::vec::Vec<u8> {
|
||||
let tokens = #core_crate::abi::Tokenize::into_tokens(self);
|
||||
let selector = <Self as #contract_crate::#trait_ident>::selector();
|
||||
let encoded = #core_crate::abi::encode(&tokens);
|
||||
let tokens = #ethers_core::abi::Tokenize::into_tokens(self);
|
||||
let selector = <Self as #ethers_contract::#trait_ident>::selector();
|
||||
let encoded = #ethers_core::abi::encode(&tokens);
|
||||
selector
|
||||
.iter()
|
||||
.copied()
|
||||
|
@ -165,10 +157,10 @@ pub fn derive_codec_impls(
|
|||
}
|
||||
|
||||
};
|
||||
let tokenize_impl = abi_ty::derive_tokenizeable_impl(input);
|
||||
let tokenize_impl = abi_ty::derive_tokenizeable_impl(input)?;
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
#tokenize_impl
|
||||
#codec_impl
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
//! Helper functions for deriving `EthAbiType`
|
||||
|
||||
use ethers_core::macros::ethers_core_crate;
|
||||
|
||||
use quote::quote;
|
||||
use syn::DeriveInput;
|
||||
|
||||
/// Generates the `AbiEncode` + `AbiDecode` implementation
|
||||
pub fn derive_codec_impl(input: &DeriveInput) -> proc_macro2::TokenStream {
|
||||
let name = &input.ident;
|
||||
let core_crate = ethers_core_crate();
|
||||
let ethers_core = ethers_core_crate();
|
||||
|
||||
quote! {
|
||||
impl #core_crate::abi::AbiDecode for #name {
|
||||
fn decode(bytes: impl AsRef<[u8]>) -> ::std::result::Result<Self, #core_crate::abi::AbiError> {
|
||||
if let #core_crate::abi::ParamType::Tuple(params) = <Self as #core_crate::abi::AbiType>::param_type() {
|
||||
let tokens = #core_crate::abi::decode(¶ms, bytes.as_ref())?;
|
||||
Ok(<Self as #core_crate::abi::Tokenizable>::from_token(#core_crate::abi::Token::Tuple(tokens))?)
|
||||
impl #ethers_core::abi::AbiDecode for #name {
|
||||
fn decode(bytes: impl AsRef<[u8]>) -> ::std::result::Result<Self, #ethers_core::abi::AbiError> {
|
||||
if let #ethers_core::abi::ParamType::Tuple(params) = <Self as #ethers_core::abi::AbiType>::param_type() {
|
||||
let tokens = #ethers_core::abi::decode(¶ms, bytes.as_ref())?;
|
||||
Ok(<Self as #ethers_core::abi::Tokenizable>::from_token(#ethers_core::abi::Token::Tuple(tokens))?)
|
||||
} else {
|
||||
Err(
|
||||
#core_crate::abi::InvalidOutputType("Expected tuple".to_string()).into()
|
||||
#ethers_core::abi::InvalidOutputType("Expected tuple".to_string()).into()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl #core_crate::abi::AbiEncode for #name {
|
||||
|
||||
impl #ethers_core::abi::AbiEncode for #name {
|
||||
fn encode(self) -> ::std::vec::Vec<u8> {
|
||||
let tokens = #core_crate::abi::Tokenize::into_tokens(self);
|
||||
#core_crate::abi::encode(&tokens)
|
||||
let tokens = #ethers_core::abi::Tokenize::into_tokens(self);
|
||||
#ethers_core::abi::encode(&tokens)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,18 @@
|
|||
//! Helper functions for deriving `Display`
|
||||
|
||||
use crate::utils;
|
||||
use ethers_core::{abi::ParamType, macros::ethers_core_crate};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse::Error, spanned::Spanned as _, Data, DeriveInput, Fields, Index};
|
||||
|
||||
use ethers_core::{abi::ParamType, macros::ethers_core_crate};
|
||||
|
||||
use crate::utils;
|
||||
use syn::{parse::Error, spanned::Spanned, Data, DeriveInput, Fields, Index};
|
||||
|
||||
/// Derive `fmt::Display` for the given type
|
||||
pub(crate) fn derive_eth_display_impl(input: DeriveInput) -> Result<TokenStream, Error> {
|
||||
let fields: Vec<_> = match input.data {
|
||||
let fields = match input.data {
|
||||
Data::Struct(ref data) => match data.fields {
|
||||
Fields::Named(ref fields) => fields.named.iter().collect(),
|
||||
Fields::Unnamed(ref fields) => fields.unnamed.iter().collect(),
|
||||
Fields::Unit => {
|
||||
vec![]
|
||||
}
|
||||
Fields::Unit => vec![],
|
||||
},
|
||||
Data::Enum(_) => {
|
||||
return Err(Error::new(input.span(), "Enum types are not supported by EthDisplay"))
|
||||
|
@ -25,8 +21,10 @@ pub(crate) fn derive_eth_display_impl(input: DeriveInput) -> Result<TokenStream,
|
|||
return Err(Error::new(input.span(), "Union types are not supported by EthDisplay"))
|
||||
}
|
||||
};
|
||||
let core_crate = ethers_core_crate();
|
||||
let hex_encode = quote! {#core_crate::utils::hex::encode};
|
||||
|
||||
let ethers_core = ethers_core_crate();
|
||||
let hex_encode = quote! {#ethers_core::utils::hex::encode};
|
||||
|
||||
let mut fmts = TokenStream::new();
|
||||
for (idx, field) in fields.iter().enumerate() {
|
||||
let ident = field.ident.clone().map(|id| quote! {#id}).unwrap_or_else(|| {
|
||||
|
@ -92,6 +90,7 @@ pub(crate) fn derive_eth_display_impl(input: DeriveInput) -> Result<TokenStream,
|
|||
fmts.extend(quote! { write!(f, ", ")?;});
|
||||
}
|
||||
}
|
||||
|
||||
let name = &input.ident;
|
||||
Ok(quote! {
|
||||
impl ::std::fmt::Display for #name {
|
||||
|
|
|
@ -10,46 +10,45 @@ use quote::quote;
|
|||
use syn::{parse::Error, DeriveInput};
|
||||
|
||||
/// Generates the `EthError` trait support
|
||||
pub(crate) fn derive_eth_error_impl(input: DeriveInput) -> TokenStream {
|
||||
let attributes = match parse_calllike_attributes(&input, "etherror") {
|
||||
Ok(attributes) => attributes,
|
||||
Err(errors) => return errors,
|
||||
};
|
||||
pub(crate) fn derive_eth_error_impl(input: DeriveInput) -> Result<TokenStream, Error> {
|
||||
let attributes = parse_calllike_attributes(&input, "etherror")?;
|
||||
|
||||
let error_name = attributes.name.map(|(s, _)| s).unwrap_or_else(|| input.ident.to_string());
|
||||
|
||||
let mut error = if let Some((src, span)) = attributes.abi {
|
||||
let raw_function_sig = src.trim_start_matches("error ").trim_start();
|
||||
// try to parse as solidity error
|
||||
if let Ok(fun) = HumanReadableParser::parse_error(&src) {
|
||||
fun
|
||||
} else {
|
||||
// try to determine the abi by using its fields at runtime
|
||||
match HumanReadableParser::parse_error(&src) {
|
||||
Ok(solidity_error) => solidity_error,
|
||||
Err(parse_err) => {
|
||||
return match derive_trait_impls_with_abi_type(
|
||||
&input,
|
||||
&error_name,
|
||||
Some(raw_function_sig),
|
||||
) {
|
||||
Ok(derived) => derived,
|
||||
Ok(derived) => Ok(derived),
|
||||
Err(err) => {
|
||||
Error::new(span, format!("Unable to determine ABI for `{src}` : {err}"))
|
||||
.to_compile_error()
|
||||
Err(Error::new(span, format!("Unable to determine ABI for `{src}`: {err}")))
|
||||
}
|
||||
.map_err(|e| {
|
||||
let mut error = Error::new(span, parse_err);
|
||||
error.combine(Error::new(span, e));
|
||||
error
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// try to determine the abi by using its fields at runtime
|
||||
return match derive_trait_impls_with_abi_type(&input, &error_name, None) {
|
||||
Ok(derived) => derived,
|
||||
Err(err) => err.to_compile_error(),
|
||||
}
|
||||
return derive_trait_impls_with_abi_type(&input, &error_name, None)
|
||||
};
|
||||
error.name = error_name.clone();
|
||||
let abi = error.abi_signature();
|
||||
|
||||
let sig = error.abi_signature();
|
||||
let selector = utils::selector(error.selector());
|
||||
let decode_impl = derive_decode_impl_from_params(&error.inputs, ident("EthError"));
|
||||
|
||||
derive_trait_impls(&input, &error_name, quote! {#abi.into()}, Some(selector), decode_impl)
|
||||
derive_trait_impls(&input, &error_name, quote!(#sig.into()), Some(selector), decode_impl)
|
||||
}
|
||||
|
||||
/// Use the `AbiType` trait to determine the correct `ParamType` and signature at runtime
|
||||
|
@ -58,17 +57,14 @@ fn derive_trait_impls_with_abi_type(
|
|||
function_call_name: &str,
|
||||
abi_signature: Option<&str>,
|
||||
) -> Result<TokenStream, Error> {
|
||||
let abi_signature = if let Some(abi) = abi_signature {
|
||||
quote! {#abi}
|
||||
let mut abi_signature = if let Some(sig) = abi_signature {
|
||||
quote!(#sig)
|
||||
} else {
|
||||
utils::derive_abi_signature_with_abi_type(input, function_call_name, "EthError")?
|
||||
};
|
||||
|
||||
let abi_signature = quote! {
|
||||
#abi_signature.into()
|
||||
utils::abi_signature_with_abi_type(input, function_call_name, "EthError")?
|
||||
};
|
||||
abi_signature.extend(quote!(.into()));
|
||||
let decode_impl = derive_decode_impl_with_abi_type(input, ident("EthError"))?;
|
||||
Ok(derive_trait_impls(input, function_call_name, abi_signature, None, decode_impl))
|
||||
derive_trait_impls(input, function_call_name, abi_signature, None, decode_impl)
|
||||
}
|
||||
|
||||
/// Generates the EthError implementation
|
||||
|
@ -78,26 +74,25 @@ pub fn derive_trait_impls(
|
|||
abi_signature: TokenStream,
|
||||
selector: Option<TokenStream>,
|
||||
decode_impl: TokenStream,
|
||||
) -> TokenStream {
|
||||
) -> Result<TokenStream, Error> {
|
||||
// the ethers crates to use
|
||||
let core_crate = ethers_core_crate();
|
||||
let contract_crate = ethers_contract_crate();
|
||||
let ethers_core = ethers_core_crate();
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
let struct_name = &input.ident;
|
||||
|
||||
let selector = selector.unwrap_or_else(|| {
|
||||
quote! {
|
||||
#core_crate::utils::id(Self::abi_signature())
|
||||
#ethers_core::utils::id(Self::abi_signature())
|
||||
}
|
||||
});
|
||||
|
||||
let etherror_impl = quote! {
|
||||
impl #contract_crate::EthError for #struct_name {
|
||||
|
||||
impl #ethers_contract::EthError for #struct_name {
|
||||
fn error_name() -> ::std::borrow::Cow<'static, str> {
|
||||
#function_call_name.into()
|
||||
}
|
||||
|
||||
fn selector() -> #core_crate::types::Selector {
|
||||
fn selector() -> #ethers_core::types::Selector {
|
||||
#selector
|
||||
}
|
||||
|
||||
|
@ -105,12 +100,11 @@ pub fn derive_trait_impls(
|
|||
#abi_signature
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
let codec_impl = derive_codec_impls(input, decode_impl, ident("EthError"));
|
||||
let codec_impl = derive_codec_impls(input, decode_impl, ident("EthError"))?;
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
#etherror_impl
|
||||
#codec_impl
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use ethers_contract_abigen::Source;
|
|||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
parse::Error, spanned::Spanned as _, AttrStyle, Data, DeriveInput, Field, Fields, Lit, Meta,
|
||||
parse::Error, spanned::Spanned, AttrStyle, Data, DeriveInput, Field, Fields, Lit, Meta,
|
||||
NestedMeta,
|
||||
};
|
||||
|
||||
|
@ -42,8 +42,9 @@ pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> Result<TokenStream, E
|
|||
}
|
||||
Err(source_err) => {
|
||||
// Return both error messages
|
||||
let message = format!("Failed parsing ABI: {parse_err} ({source_err})");
|
||||
Err(Error::new(span, message))
|
||||
let mut error = Error::new(span, parse_err);
|
||||
error.combine(Error::new(span, source_err));
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +103,7 @@ pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> Result<TokenStream, E
|
|||
}
|
||||
};
|
||||
|
||||
let tokenize_impl = abi_ty::derive_tokenizeable_impl(&input);
|
||||
let tokenize_impl = abi_ty::derive_tokenizeable_impl(&input)?;
|
||||
|
||||
Ok(quote! {
|
||||
#tokenize_impl
|
||||
|
|
|
@ -112,7 +112,11 @@ pub fn abigen(input: TokenStream) -> TokenStream {
|
|||
#[proc_macro_derive(EthAbiType)]
|
||||
pub fn derive_abi_type(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
TokenStream::from(abi_ty::derive_tokenizeable_impl(&input))
|
||||
match abi_ty::derive_tokenizeable_impl(&input) {
|
||||
Ok(tokens) => tokens,
|
||||
Err(err) => err.to_compile_error(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Derives the `AbiEncode`, `AbiDecode` and traits for the labeled type.
|
||||
|
@ -174,9 +178,10 @@ pub fn derive_abi_codec(input: TokenStream) -> TokenStream {
|
|||
pub fn derive_eth_display(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
match display::derive_eth_display_impl(input) {
|
||||
Ok(tokens) => TokenStream::from(tokens),
|
||||
Err(err) => err.to_compile_error().into(),
|
||||
Ok(tokens) => tokens,
|
||||
Err(err) => err.to_compile_error(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Derives the `EthEvent` and `Tokenizeable` trait for the labeled type.
|
||||
|
@ -225,9 +230,10 @@ pub fn derive_eth_display(input: TokenStream) -> TokenStream {
|
|||
pub fn derive_abi_event(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
match event::derive_eth_event_impl(input) {
|
||||
Ok(tokens) => TokenStream::from(tokens),
|
||||
Err(err) => err.to_compile_error().into(),
|
||||
Ok(tokens) => tokens,
|
||||
Err(err) => err.to_compile_error(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Derives the `EthCall` and `Tokenizeable` trait for the labeled type.
|
||||
|
@ -291,7 +297,11 @@ pub fn derive_abi_event(input: TokenStream) -> TokenStream {
|
|||
#[proc_macro_derive(EthCall, attributes(ethcall))]
|
||||
pub fn derive_abi_call(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
TokenStream::from(call::derive_eth_call_impl(input))
|
||||
match call::derive_eth_call_impl(input) {
|
||||
Ok(tokens) => tokens,
|
||||
Err(err) => err.to_compile_error(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Derives the `EthError` and `Tokenizeable` trait for the labeled type.
|
||||
|
@ -328,5 +338,9 @@ pub fn derive_abi_call(input: TokenStream) -> TokenStream {
|
|||
#[proc_macro_derive(EthError, attributes(etherror))]
|
||||
pub fn derive_abi_error(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
TokenStream::from(error::derive_eth_error_impl(input))
|
||||
match error::derive_eth_error_impl(input) {
|
||||
Ok(tokens) => tokens,
|
||||
Err(err) => err.to_compile_error(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use ethers_core::{abi::ParamType, macros::ethers_core_crate, types::Selector};
|
||||
use proc_macro2::{Ident, Literal, Span};
|
||||
use proc_macro2::{Ident, Literal, Span, TokenStream};
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::{
|
||||
parse::Error, spanned::Spanned as _, Data, DeriveInput, Expr, Fields, GenericArgument, Lit,
|
||||
parse::Error, spanned::Spanned, Data, DeriveInput, Expr, Fields, GenericArgument, Lit,
|
||||
PathArguments, Type,
|
||||
};
|
||||
|
||||
|
@ -10,26 +10,29 @@ pub fn ident(name: &str) -> Ident {
|
|||
Ident::new(name, Span::call_site())
|
||||
}
|
||||
|
||||
pub fn signature(hash: &[u8]) -> proc_macro2::TokenStream {
|
||||
let core_crate = ethers_core_crate();
|
||||
pub fn signature(hash: &[u8]) -> TokenStream {
|
||||
let ethers_core = ethers_core_crate();
|
||||
let bytes = hash.iter().copied().map(Literal::u8_unsuffixed);
|
||||
quote! {#core_crate::types::H256([#( #bytes ),*])}
|
||||
quote! {#ethers_core::types::H256([#( #bytes ),*])}
|
||||
}
|
||||
|
||||
pub fn selector(selector: Selector) -> proc_macro2::TokenStream {
|
||||
pub fn selector(selector: Selector) -> TokenStream {
|
||||
let bytes = selector.iter().copied().map(Literal::u8_unsuffixed);
|
||||
quote! {[#( #bytes ),*]}
|
||||
}
|
||||
|
||||
/// Parses an int type from its string representation
|
||||
pub fn parse_int_param_type(s: &str) -> Option<ParamType> {
|
||||
let size = s.chars().skip(1).collect::<String>().parse::<usize>().ok()?;
|
||||
if s.starts_with('u') {
|
||||
match s.chars().next() {
|
||||
Some(c @ 'u') | Some(c @ 'i') => {
|
||||
let size = s[1..].parse::<usize>().ok()?;
|
||||
if c == 'u' {
|
||||
Some(ParamType::Uint(size))
|
||||
} else if s.starts_with('i') {
|
||||
Some(ParamType::Int(size))
|
||||
} else {
|
||||
None
|
||||
Some(ParamType::Int(size))
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,64 +40,58 @@ pub fn parse_int_param_type(s: &str) -> Option<ParamType> {
|
|||
// This applies to strings, arrays, structs and bytes to follow the encoding of
|
||||
// these indexed param types according to
|
||||
// <https://solidity.readthedocs.io/en/develop/abi-spec.html#encoding-of-indexed-event-parameters>
|
||||
pub fn topic_param_type_quote(kind: &ParamType) -> proc_macro2::TokenStream {
|
||||
let core_crate = ethers_core_crate();
|
||||
pub fn topic_param_type_quote(kind: &ParamType) -> TokenStream {
|
||||
let ethers_core = ethers_core_crate();
|
||||
match kind {
|
||||
ParamType::String |
|
||||
ParamType::Bytes |
|
||||
ParamType::Array(_) |
|
||||
ParamType::FixedArray(_, _) |
|
||||
ParamType::Tuple(_) => quote! {#core_crate::abi::ParamType::FixedBytes(32)},
|
||||
ParamType::Tuple(_) => quote! {#ethers_core::abi::ParamType::FixedBytes(32)},
|
||||
ty => param_type_quote(ty),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the rust type for the given parameter
|
||||
pub fn param_type_quote(kind: &ParamType) -> proc_macro2::TokenStream {
|
||||
let core_crate = ethers_core_crate();
|
||||
pub fn param_type_quote(kind: &ParamType) -> TokenStream {
|
||||
let ethers_core = ethers_core_crate();
|
||||
match kind {
|
||||
ParamType::Address => {
|
||||
quote! {#core_crate::abi::ParamType::Address}
|
||||
quote! {#ethers_core::abi::ParamType::Address}
|
||||
}
|
||||
ParamType::Bytes => {
|
||||
quote! {#core_crate::abi::ParamType::Bytes}
|
||||
quote! {#ethers_core::abi::ParamType::Bytes}
|
||||
}
|
||||
ParamType::Int(size) => {
|
||||
let size = Literal::usize_suffixed(*size);
|
||||
quote! {#core_crate::abi::ParamType::Int(#size)}
|
||||
quote! {#ethers_core::abi::ParamType::Int(#size)}
|
||||
}
|
||||
ParamType::Uint(size) => {
|
||||
let size = Literal::usize_suffixed(*size);
|
||||
quote! {#core_crate::abi::ParamType::Uint(#size)}
|
||||
quote! {#ethers_core::abi::ParamType::Uint(#size)}
|
||||
}
|
||||
ParamType::Bool => {
|
||||
quote! {#core_crate::abi::ParamType::Bool}
|
||||
quote! {#ethers_core::abi::ParamType::Bool}
|
||||
}
|
||||
ParamType::String => {
|
||||
quote! {#core_crate::abi::ParamType::String}
|
||||
quote! {#ethers_core::abi::ParamType::String}
|
||||
}
|
||||
ParamType::Array(ty) => {
|
||||
let ty = param_type_quote(ty);
|
||||
quote! {#core_crate::abi::ParamType::Array(Box::new(#ty))}
|
||||
quote! {#ethers_core::abi::ParamType::Array(Box::new(#ty))}
|
||||
}
|
||||
ParamType::FixedBytes(size) => {
|
||||
let size = Literal::usize_suffixed(*size);
|
||||
quote! {#core_crate::abi::ParamType::FixedBytes(#size)}
|
||||
quote! {#ethers_core::abi::ParamType::FixedBytes(#size)}
|
||||
}
|
||||
ParamType::FixedArray(ty, size) => {
|
||||
let ty = param_type_quote(ty);
|
||||
let size = Literal::usize_suffixed(*size);
|
||||
quote! {#core_crate::abi::ParamType::FixedArray(Box::new(#ty),#size)}
|
||||
quote! {#ethers_core::abi::ParamType::FixedArray(Box::new(#ty), #size)}
|
||||
}
|
||||
ParamType::Tuple(tuple) => {
|
||||
let elements = tuple.iter().map(param_type_quote);
|
||||
quote! {
|
||||
#core_crate::abi::ParamType::Tuple(
|
||||
::std::vec![
|
||||
#( #elements ),*
|
||||
]
|
||||
)
|
||||
}
|
||||
quote!(#ethers_core::abi::ParamType::Tuple(::std::vec![#( #elements ),*]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,8 +117,8 @@ pub fn find_parameter_type(ty: &Type) -> Result<ParamType, Error> {
|
|||
if let PathArguments::AngleBracketed(ref args) = ty.path.segments[0].arguments {
|
||||
if args.args.len() == 1 {
|
||||
if let GenericArgument::Type(ref ty) = args.args.iter().next().unwrap() {
|
||||
let kind = find_parameter_type(ty)?;
|
||||
return Ok(ParamType::Array(Box::new(kind)))
|
||||
return find_parameter_type(ty)
|
||||
.map(|kind| ParamType::Array(Box::new(kind)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -148,10 +145,12 @@ pub fn find_parameter_type(ty: &Type) -> Result<ParamType, Error> {
|
|||
}
|
||||
Err(Error::new(ty.span(), "Failed to derive proper ABI from fields"))
|
||||
}
|
||||
Type::Tuple(ty) => {
|
||||
let params = ty.elems.iter().map(find_parameter_type).collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(ParamType::Tuple(params))
|
||||
}
|
||||
Type::Tuple(ty) => ty
|
||||
.elems
|
||||
.iter()
|
||||
.map(find_parameter_type)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map(ParamType::Tuple),
|
||||
_ => Err(Error::new(ty.span(), "Failed to derive proper ABI from fields")),
|
||||
}
|
||||
}
|
||||
|
@ -200,22 +199,22 @@ pub fn derive_abi_inputs_from_fields(
|
|||
pub fn derive_param_type_with_abi_type(
|
||||
input: &DeriveInput,
|
||||
trait_name: &str,
|
||||
) -> Result<proc_macro2::TokenStream, Error> {
|
||||
let core_crate = ethers_core_crate();
|
||||
let params = derive_abi_parameters_array(input, trait_name)?;
|
||||
) -> Result<TokenStream, Error> {
|
||||
let ethers_core = ethers_core_crate();
|
||||
let params = abi_parameters_array(input, trait_name)?;
|
||||
Ok(quote! {
|
||||
#core_crate::abi::ParamType::Tuple(::std::vec!#params)
|
||||
#ethers_core::abi::ParamType::Tuple(::std::vec!#params)
|
||||
})
|
||||
}
|
||||
|
||||
/// Use `AbiType::param_type` fo each field to construct the whole signature `<name>(<params,>*)` as
|
||||
/// `String`
|
||||
pub fn derive_abi_signature_with_abi_type(
|
||||
/// `String`.
|
||||
pub fn abi_signature_with_abi_type(
|
||||
input: &DeriveInput,
|
||||
function_name: &str,
|
||||
trait_name: &str,
|
||||
) -> Result<proc_macro2::TokenStream, Error> {
|
||||
let params = derive_abi_parameters_array(input, trait_name)?;
|
||||
) -> Result<TokenStream, Error> {
|
||||
let params = abi_parameters_array(input, trait_name)?;
|
||||
Ok(quote! {
|
||||
{
|
||||
let params: String = #params
|
||||
|
@ -231,30 +230,13 @@ pub fn derive_abi_signature_with_abi_type(
|
|||
|
||||
/// Use `AbiType::param_type` fo each field to construct the signature's parameters as runtime array
|
||||
/// `[param1, param2,...]`
|
||||
pub fn derive_abi_parameters_array(
|
||||
input: &DeriveInput,
|
||||
trait_name: &str,
|
||||
) -> Result<proc_macro2::TokenStream, Error> {
|
||||
let core_crate = ethers_core_crate();
|
||||
pub fn abi_parameters_array(input: &DeriveInput, trait_name: &str) -> Result<TokenStream, Error> {
|
||||
let ethers_core = ethers_core_crate();
|
||||
|
||||
let param_types: Vec<_> = match input.data {
|
||||
let fields = match input.data {
|
||||
Data::Struct(ref data) => match data.fields {
|
||||
Fields::Named(ref fields) => fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|f| {
|
||||
let ty = &f.ty;
|
||||
quote_spanned! { f.span() => <#ty as #core_crate::abi::AbiType>::param_type() }
|
||||
})
|
||||
.collect(),
|
||||
Fields::Unnamed(ref fields) => fields
|
||||
.unnamed
|
||||
.iter()
|
||||
.map(|f| {
|
||||
let ty = &f.ty;
|
||||
quote_spanned! { f.span() => <#ty as #core_crate::abi::AbiType>::param_type() }
|
||||
})
|
||||
.collect(),
|
||||
Fields::Named(ref fields) => &fields.named,
|
||||
Fields::Unnamed(ref fields) => &fields.unnamed,
|
||||
Fields::Unit => {
|
||||
return Err(Error::new(
|
||||
input.span(),
|
||||
|
@ -276,7 +258,12 @@ pub fn derive_abi_parameters_array(
|
|||
}
|
||||
};
|
||||
|
||||
let iter = fields.iter().map(|f| {
|
||||
let ty = &f.ty;
|
||||
quote_spanned!(f.span() => <#ty as #ethers_core::abi::AbiType>::param_type())
|
||||
});
|
||||
|
||||
Ok(quote! {
|
||||
[#( #param_types ),*]
|
||||
[#( #iter ),*]
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue