refactor(abigen): solidity types expansion (#2131)
* refactor: event input expansion * refactor: common expand params * refactor: method params expansion * refactor: struct fields expansion * refactor: types array expansion * clippy
This commit is contained in:
parent
0c16eb971d
commit
f9ced12fee
|
@ -1,66 +1,8 @@
|
||||||
use super::{types, util, Context};
|
use super::Context;
|
||||||
use ethers_core::{
|
use ethers_core::macros::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate};
|
||||||
abi::{Param, ParamType},
|
|
||||||
macros::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate},
|
|
||||||
};
|
|
||||||
use proc_macro2::{Ident, Literal, TokenStream};
|
use proc_macro2::{Ident, Literal, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
||||||
/// Expands to the `name : type` pairs for the params
|
|
||||||
pub(crate) fn expand_params<'a, F>(
|
|
||||||
params: &[Param],
|
|
||||||
resolve_tuple: F,
|
|
||||||
) -> eyre::Result<Vec<(TokenStream, TokenStream)>>
|
|
||||||
where
|
|
||||||
F: Fn(&str) -> Option<&'a str>,
|
|
||||||
{
|
|
||||||
params
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(idx, param)| {
|
|
||||||
let name = util::expand_input_name(idx, ¶m.name);
|
|
||||||
let ty = expand_param_type(param, ¶m.kind, |s| resolve_tuple(s))?;
|
|
||||||
Ok((name, ty))
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// returns the Tokenstream for the corresponding rust type
|
|
||||||
pub(crate) fn expand_param_type<'a, F>(
|
|
||||||
param: &Param,
|
|
||||||
kind: &ParamType,
|
|
||||||
resolve_tuple: F,
|
|
||||||
) -> eyre::Result<TokenStream>
|
|
||||||
where
|
|
||||||
F: Fn(&str) -> Option<&'a str>,
|
|
||||||
{
|
|
||||||
match kind {
|
|
||||||
ParamType::Array(ty) => {
|
|
||||||
let ty = expand_param_type(param, ty, resolve_tuple)?;
|
|
||||||
Ok(quote! {
|
|
||||||
::std::vec::Vec<#ty>
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ParamType::FixedArray(ty, size) => {
|
|
||||||
let ty = expand_param_type(param, ty, resolve_tuple)?;
|
|
||||||
let size = *size;
|
|
||||||
Ok(quote! {[#ty; #size]})
|
|
||||||
}
|
|
||||||
ParamType::Tuple(_) => {
|
|
||||||
let ty = if let Some(rust_struct_name) =
|
|
||||||
param.internal_type.as_ref().and_then(|s| resolve_tuple(s.as_str()))
|
|
||||||
{
|
|
||||||
let ident = util::ident(rust_struct_name);
|
|
||||||
quote! {#ident}
|
|
||||||
} else {
|
|
||||||
types::expand(kind)?
|
|
||||||
};
|
|
||||||
Ok(ty)
|
|
||||||
}
|
|
||||||
_ => types::expand(kind),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn imports(name: &str) -> TokenStream {
|
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 doc_str = format!("{name} was auto-generated with ethers-rs Abigen. More information at: <https://github.com/gakonst/ethers-rs>");
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
//! derive error bindings
|
//! derive error bindings
|
||||||
|
|
||||||
use super::{util, Context};
|
use super::{
|
||||||
use crate::contract::common::{expand_data_struct, expand_data_tuple, expand_params};
|
common::{expand_data_struct, expand_data_tuple},
|
||||||
|
types, util, Context,
|
||||||
|
};
|
||||||
use ethers_core::{
|
use ethers_core::{
|
||||||
abi::{ethabi::AbiError, ErrorExt},
|
abi::{ethabi::AbiError, ErrorExt},
|
||||||
macros::{ethers_contract_crate, ethers_core_crate},
|
macros::{ethers_contract_crate, ethers_core_crate},
|
||||||
|
@ -81,7 +83,9 @@ impl Context {
|
||||||
|
|
||||||
/// Expands to the `name : type` pairs of the function's outputs
|
/// Expands to the `name : type` pairs of the function's outputs
|
||||||
fn expand_error_params(&self, error: &AbiError) -> Result<Vec<(TokenStream, TokenStream)>> {
|
fn expand_error_params(&self, error: &AbiError) -> Result<Vec<(TokenStream, TokenStream)>> {
|
||||||
expand_params(&error.inputs, |s| self.internal_structs.get_struct_type(s))
|
types::expand_params(&error.inputs, |p| {
|
||||||
|
p.internal_type.as_deref().and_then(|s| self.internal_structs.get_struct_type(s))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The name ident of the errors enum
|
/// The name ident of the errors enum
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use super::{types, util, Context};
|
use super::{types, util, Context};
|
||||||
use crate::util::can_derive_defaults;
|
use crate::util::can_derive_defaults;
|
||||||
use ethers_core::{
|
use ethers_core::{
|
||||||
abi::{Event, EventExt, EventParam, Param, ParamType},
|
abi::{Event, EventExt, Param},
|
||||||
macros::{ethers_contract_crate, ethers_core_crate},
|
macros::{ethers_contract_crate, ethers_core_crate},
|
||||||
};
|
};
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use inflector::Inflector;
|
use inflector::Inflector;
|
||||||
use proc_macro2::{Ident, Literal, TokenStream};
|
use proc_macro2::{Ident, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
@ -136,85 +136,6 @@ impl Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expands an event property type.
|
|
||||||
///
|
|
||||||
/// Note that this is slightly different from expanding a Solidity type as
|
|
||||||
/// complex types like arrays and strings get emitted as hashes when they are
|
|
||||||
/// indexed.
|
|
||||||
/// If a complex types matches with a struct previously parsed by the internal structs,
|
|
||||||
/// we can replace it
|
|
||||||
fn expand_input_type(
|
|
||||||
&self,
|
|
||||||
event: &Event,
|
|
||||||
input: &EventParam,
|
|
||||||
idx: usize,
|
|
||||||
) -> Result<TokenStream> {
|
|
||||||
let ethers_core = ethers_core_crate();
|
|
||||||
Ok(match (&input.kind, input.indexed) {
|
|
||||||
(ParamType::Array(_), true) => {
|
|
||||||
quote! { #ethers_core::types::H256 }
|
|
||||||
}
|
|
||||||
(ParamType::FixedArray(_, _), true) => {
|
|
||||||
quote! { #ethers_core::types::H256 }
|
|
||||||
}
|
|
||||||
(ParamType::Tuple(..), true) => {
|
|
||||||
quote! { #ethers_core::types::H256 }
|
|
||||||
}
|
|
||||||
(ParamType::Bytes, true) | (ParamType::String, true) => {
|
|
||||||
quote! { #ethers_core::types::H256 }
|
|
||||||
}
|
|
||||||
(ParamType::Tuple(_), false) => {
|
|
||||||
let ty = if let Some(rust_struct_name) =
|
|
||||||
self.internal_structs.get_event_input_struct_type(&event.name, idx)
|
|
||||||
{
|
|
||||||
let ident = util::ident(rust_struct_name);
|
|
||||||
quote! {#ident}
|
|
||||||
} else {
|
|
||||||
types::expand(&input.kind)?
|
|
||||||
};
|
|
||||||
ty
|
|
||||||
}
|
|
||||||
(ParamType::Array(_), _) => {
|
|
||||||
// represents an array of a struct
|
|
||||||
if let Some(rust_struct_name) =
|
|
||||||
self.internal_structs.get_event_input_struct_type(&event.name, idx)
|
|
||||||
{
|
|
||||||
let ty = util::ident(rust_struct_name);
|
|
||||||
return Ok(quote! {::std::vec::Vec<#ty>})
|
|
||||||
}
|
|
||||||
types::expand(&input.kind)?
|
|
||||||
}
|
|
||||||
(ParamType::FixedArray(_, size), _) => {
|
|
||||||
// represents a fixed array of a struct
|
|
||||||
if let Some(rust_struct_name) =
|
|
||||||
self.internal_structs.get_event_input_struct_type(&event.name, idx)
|
|
||||||
{
|
|
||||||
let ty = util::ident(rust_struct_name);
|
|
||||||
let size = Literal::usize_unsuffixed(*size);
|
|
||||||
return Ok(quote! {[#ty; #size]})
|
|
||||||
}
|
|
||||||
types::expand(&input.kind)?
|
|
||||||
}
|
|
||||||
(kind, _) => types::expand(kind)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Expands the name-type pairs for the given inputs
|
|
||||||
fn expand_event_params(&self, event: &Event) -> Result<Vec<(TokenStream, TokenStream, bool)>> {
|
|
||||||
event
|
|
||||||
.inputs
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(idx, input)| {
|
|
||||||
// NOTE: Events can contain nameless values.
|
|
||||||
let name = util::expand_input_name(idx, &input.name);
|
|
||||||
let ty = self.expand_input_type(event, input, idx)?;
|
|
||||||
|
|
||||||
Ok((name, ty, input.indexed))
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Expands into a single method for contracting an event stream.
|
/// Expands into a single method for contracting an event stream.
|
||||||
fn expand_filter(&self, event: &Event) -> TokenStream {
|
fn expand_filter(&self, event: &Event) -> TokenStream {
|
||||||
let name = &event.name;
|
let name = &event.name;
|
||||||
|
@ -251,7 +172,7 @@ impl Context {
|
||||||
|
|
||||||
let event_name = event_struct_name(&event.name, sig);
|
let event_name = event_struct_name(&event.name, sig);
|
||||||
|
|
||||||
let params = self.expand_event_params(event)?;
|
let params = types::expand_event_inputs(event, &self.internal_structs)?;
|
||||||
// expand as a tuple if all fields are anonymous
|
// expand as a tuple if all fields are anonymous
|
||||||
let all_anonymous_fields = event.inputs.iter().all(|input| input.name.is_empty());
|
let all_anonymous_fields = event.inputs.iter().all(|input| input.name.is_empty());
|
||||||
let data_type_definition = if all_anonymous_fields {
|
let data_type_definition = if all_anonymous_fields {
|
||||||
|
@ -355,6 +276,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Abigen;
|
use crate::Abigen;
|
||||||
use ethers_core::abi::{EventParam, Hash, ParamType};
|
use ethers_core::abi::{EventParam, Hash, ParamType};
|
||||||
|
use proc_macro2::Literal;
|
||||||
|
|
||||||
/// Expands a 256-bit `Hash` into a literal representation that can be used with
|
/// Expands a 256-bit `Hash` into a literal representation that can be used with
|
||||||
/// quasi-quoting for code generation. We do this to avoid allocating at runtime
|
/// quasi-quoting for code generation. We do this to avoid allocating at runtime
|
||||||
|
@ -445,7 +367,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
let cx = test_context();
|
let cx = test_context();
|
||||||
let params = cx.expand_event_params(&event).unwrap();
|
let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
|
||||||
let name = event_struct_name(&event.name, None);
|
let name = event_struct_name(&event.name, None);
|
||||||
let definition = expand_data_struct(&name, ¶ms);
|
let definition = expand_data_struct(&name, ¶ms);
|
||||||
|
|
||||||
|
@ -469,7 +391,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
let cx = test_context_with_alias("Foo(bool,address)", "FooAliased");
|
let cx = test_context_with_alias("Foo(bool,address)", "FooAliased");
|
||||||
let params = cx.expand_event_params(&event).unwrap();
|
let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
|
||||||
let alias = Some(util::ident("FooAliased"));
|
let alias = Some(util::ident("FooAliased"));
|
||||||
let name = event_struct_name(&event.name, alias);
|
let name = event_struct_name(&event.name, alias);
|
||||||
let definition = expand_data_struct(&name, ¶ms);
|
let definition = expand_data_struct(&name, ¶ms);
|
||||||
|
@ -494,7 +416,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
let cx = test_context();
|
let cx = test_context();
|
||||||
let params = cx.expand_event_params(&event).unwrap();
|
let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
|
||||||
let name = event_struct_name(&event.name, None);
|
let name = event_struct_name(&event.name, None);
|
||||||
let definition = expand_data_tuple(&name, ¶ms);
|
let definition = expand_data_tuple(&name, ¶ms);
|
||||||
|
|
||||||
|
@ -515,7 +437,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
let cx = test_context_with_alias("Foo(bool,address)", "FooAliased");
|
let cx = test_context_with_alias("Foo(bool,address)", "FooAliased");
|
||||||
let params = cx.expand_event_params(&event).unwrap();
|
let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
|
||||||
let alias = Some(util::ident("FooAliased"));
|
let alias = Some(util::ident("FooAliased"));
|
||||||
let name = event_struct_name(&event.name, alias);
|
let name = event_struct_name(&event.name, alias);
|
||||||
let definition = expand_data_tuple(&name, ¶ms);
|
let definition = expand_data_tuple(&name, ¶ms);
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
use std::collections::{btree_map::Entry, BTreeMap, HashMap, HashSet};
|
use super::{
|
||||||
|
common::{expand_data_struct, expand_data_tuple},
|
||||||
use super::{types, util, Context};
|
types, Context,
|
||||||
use crate::{
|
|
||||||
contract::common::{expand_data_struct, expand_data_tuple, expand_param_type, expand_params},
|
|
||||||
util::can_derive_defaults,
|
|
||||||
};
|
};
|
||||||
|
use crate::util::{self, can_derive_defaults};
|
||||||
use ethers_core::{
|
use ethers_core::{
|
||||||
abi::{Function, FunctionExt, Param, ParamType},
|
abi::{Function, FunctionExt, Param, ParamType},
|
||||||
macros::{ethers_contract_crate, ethers_core_crate},
|
macros::{ethers_contract_crate, ethers_core_crate},
|
||||||
|
@ -14,6 +12,7 @@ use eyre::{Context as _, Result};
|
||||||
use inflector::Inflector;
|
use inflector::Inflector;
|
||||||
use proc_macro2::{Literal, TokenStream};
|
use proc_macro2::{Literal, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
use std::collections::{btree_map::Entry, BTreeMap, HashMap, HashSet};
|
||||||
use syn::Ident;
|
use syn::Ident;
|
||||||
|
|
||||||
/// The maximum amount of overloaded functions that are attempted to auto aliased with their param
|
/// The maximum amount of overloaded functions that are attempted to auto aliased with their param
|
||||||
|
@ -304,127 +303,45 @@ impl Context {
|
||||||
|
|
||||||
/// Expands to the `name : type` pairs of the function's inputs
|
/// Expands to the `name : type` pairs of the function's inputs
|
||||||
fn expand_input_params(&self, fun: &Function) -> Result<Vec<(TokenStream, TokenStream)>> {
|
fn expand_input_params(&self, fun: &Function) -> Result<Vec<(TokenStream, TokenStream)>> {
|
||||||
fun.inputs
|
types::expand_params(&fun.inputs, |p| {
|
||||||
.iter()
|
self.internal_structs.get_function_input_struct_type(&fun.name, &p.name)
|
||||||
.enumerate()
|
|
||||||
.map(|(idx, param)| {
|
|
||||||
let name = util::expand_input_name(idx, ¶m.name);
|
|
||||||
let ty = self.expand_input_param_type(fun, ¶m.name, ¶m.kind)?;
|
|
||||||
Ok((name, ty))
|
|
||||||
})
|
})
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expands to the `name : type` pairs of the function's outputs
|
/// Expands to the `name: type` pairs of the function's outputs
|
||||||
fn expand_output_params(&self, fun: &Function) -> Result<Vec<(TokenStream, TokenStream)>> {
|
fn expand_output_params(&self, fun: &Function) -> Result<Vec<(TokenStream, TokenStream)>> {
|
||||||
expand_params(&fun.outputs, |s| {
|
types::expand_params(&fun.outputs, |p| {
|
||||||
self.internal_structs.get_function_output_struct_type(&fun.name, s)
|
p.internal_type
|
||||||
|
.as_deref()
|
||||||
|
.and_then(|s| self.internal_structs.get_function_output_struct_type(&fun.name, s))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expands to the return type of a function
|
|
||||||
fn expand_outputs(&self, fun: &Function) -> Result<TokenStream> {
|
|
||||||
let mut outputs = Vec::with_capacity(fun.outputs.len());
|
|
||||||
for param in fun.outputs.iter() {
|
|
||||||
let ty = self.expand_output_param_type(fun, param, ¶m.kind)?;
|
|
||||||
outputs.push(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
let return_ty = match outputs.len() {
|
|
||||||
0 => quote! { () },
|
|
||||||
1 => outputs[0].clone(),
|
|
||||||
_ => {
|
|
||||||
quote! { (#( #outputs ),*) }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(return_ty)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Expands the arguments for the call that eventually calls the contract
|
/// Expands the arguments for the call that eventually calls the contract
|
||||||
fn expand_contract_call_args(&self, fun: &Function) -> Result<TokenStream> {
|
fn expand_contract_call_args(&self, fun: &Function) -> TokenStream {
|
||||||
let mut call_args = Vec::with_capacity(fun.inputs.len());
|
let mut call_args = fun.inputs.iter().enumerate().map(|(idx, param)| {
|
||||||
for (idx, param) in fun.inputs.iter().enumerate() {
|
|
||||||
let name = util::expand_input_name(idx, ¶m.name);
|
let name = util::expand_input_name(idx, ¶m.name);
|
||||||
let call_arg = match param.kind {
|
match param.kind {
|
||||||
// this is awkward edge case where the function inputs are a single struct
|
// this is awkward edge case where the function inputs are a single struct
|
||||||
// we need to force this argument into a tuple so it gets expanded to `((#name,))`
|
// we need to force this argument into a tuple so it gets expanded to
|
||||||
// this is currently necessary because internally `flatten_tokens` is called which
|
// `((#name,))` this is currently necessary because
|
||||||
// removes the outermost `tuple` level and since `((#name))` is not
|
// internally `flatten_tokens` is called which removes the
|
||||||
// a rust tuple it doesn't get wrapped into another tuple that will be peeled off by
|
// outermost `tuple` level and since `((#name))` is not
|
||||||
// `flatten_tokens`
|
// a rust tuple it doesn't get wrapped into another tuple that will be peeled
|
||||||
|
// off by `flatten_tokens`
|
||||||
ParamType::Tuple(_) if fun.inputs.len() == 1 => {
|
ParamType::Tuple(_) if fun.inputs.len() == 1 => {
|
||||||
// make sure the tuple gets converted to `Token::Tuple`
|
// make sure the tuple gets converted to `Token::Tuple`
|
||||||
quote! {(#name,)}
|
quote!((#name,))
|
||||||
}
|
}
|
||||||
_ => name,
|
_ => name,
|
||||||
};
|
|
||||||
call_args.push(call_arg);
|
|
||||||
}
|
}
|
||||||
let call_args = match call_args.len() {
|
});
|
||||||
0 => quote! { () },
|
|
||||||
1 => quote! { #( #call_args )* },
|
|
||||||
_ => quote! { ( #(#call_args, )* ) },
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(call_args)
|
match fun.inputs.len() {
|
||||||
|
0 => quote!(()),
|
||||||
|
1 => call_args.next().unwrap(),
|
||||||
|
_ => quote!(( #( #call_args ),* )),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns the Tokenstream for the corresponding rust type of the param
|
|
||||||
fn expand_input_param_type(
|
|
||||||
&self,
|
|
||||||
fun: &Function,
|
|
||||||
param: &str,
|
|
||||||
kind: &ParamType,
|
|
||||||
) -> Result<TokenStream> {
|
|
||||||
let ethers_core = ethers_core_crate();
|
|
||||||
match kind {
|
|
||||||
ParamType::Array(ty) => {
|
|
||||||
let ty = self.expand_input_param_type(fun, param, ty)?;
|
|
||||||
Ok(quote! {
|
|
||||||
::std::vec::Vec<#ty>
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ParamType::FixedArray(ty, size) => {
|
|
||||||
let ty = match **ty {
|
|
||||||
ParamType::Uint(size) => {
|
|
||||||
if size / 8 == 1 {
|
|
||||||
// this prevents type ambiguity with `FixedBytes`
|
|
||||||
quote! { #ethers_core::types::Uint8}
|
|
||||||
} else {
|
|
||||||
self.expand_input_param_type(fun, param, ty)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => self.expand_input_param_type(fun, param, ty)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let size = *size;
|
|
||||||
Ok(quote! {[#ty; #size]})
|
|
||||||
}
|
|
||||||
ParamType::Tuple(_) => {
|
|
||||||
let ty = if let Some(rust_struct_name) =
|
|
||||||
self.internal_structs.get_function_input_struct_type(&fun.name, param)
|
|
||||||
{
|
|
||||||
let ident = util::ident(rust_struct_name);
|
|
||||||
quote! {#ident}
|
|
||||||
} else {
|
|
||||||
types::expand(kind)?
|
|
||||||
};
|
|
||||||
Ok(ty)
|
|
||||||
}
|
|
||||||
_ => types::expand(kind),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// returns the TokenStream for the corresponding rust type of the output param
|
|
||||||
fn expand_output_param_type(
|
|
||||||
&self,
|
|
||||||
fun: &Function,
|
|
||||||
param: &Param,
|
|
||||||
kind: &ParamType,
|
|
||||||
) -> Result<TokenStream> {
|
|
||||||
expand_param_type(param, kind, |s| {
|
|
||||||
self.internal_structs.get_function_output_struct_type(&fun.name, s)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expands a single function with the given alias
|
/// Expands a single function with the given alias
|
||||||
|
@ -439,12 +356,22 @@ impl Context {
|
||||||
|
|
||||||
let selector_tokens = expand_selector(selector);
|
let selector_tokens = expand_selector(selector);
|
||||||
|
|
||||||
let contract_args = self.expand_contract_call_args(function)?;
|
let contract_args = self.expand_contract_call_args(function);
|
||||||
let function_params =
|
let function_params =
|
||||||
self.expand_input_params(function)?.into_iter().map(|(name, ty)| quote! { #name: #ty });
|
self.expand_input_params(function)?.into_iter().map(|(name, ty)| quote! { #name: #ty });
|
||||||
let function_params = quote! { #( , #function_params )* };
|
let function_params = quote! { #( , #function_params )* };
|
||||||
|
|
||||||
let outputs = self.expand_outputs(function)?;
|
let outputs = {
|
||||||
|
let mut out = self.expand_output_params(function)?;
|
||||||
|
match out.len() {
|
||||||
|
0 => quote!(()),
|
||||||
|
1 => out.pop().unwrap().1,
|
||||||
|
_ => {
|
||||||
|
let iter = out.into_iter().map(|(_, ty)| ty);
|
||||||
|
quote!(( #( #iter ),* ))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let doc_str =
|
let doc_str =
|
||||||
format!("Calls the contract's `{name}` (0x{}) function", hex::encode(selector));
|
format!("Calls the contract's `{name}` (0x{}) function", hex::encode(selector));
|
||||||
|
|
|
@ -93,7 +93,7 @@ impl Context {
|
||||||
for field in sol_struct.fields() {
|
for field in sol_struct.fields() {
|
||||||
let ty = match field.r#type() {
|
let ty = match field.r#type() {
|
||||||
FieldType::Elementary(ty) => types::expand(ty)?,
|
FieldType::Elementary(ty) => types::expand(ty)?,
|
||||||
FieldType::Struct(struct_ty) => expand_struct_type(struct_ty),
|
FieldType::Struct(struct_ty) => types::expand_struct_type(struct_ty),
|
||||||
FieldType::Mapping(_) => {
|
FieldType::Mapping(_) => {
|
||||||
eyre::bail!("Mapping types in struct `{}` are not supported {:?}", name, field)
|
eyre::bail!("Mapping types in struct `{}` are not supported {:?}", name, field)
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ impl Context {
|
||||||
fields.push(quote! { pub #field_name: #ty });
|
fields.push(quote! { pub #field_name: #ty });
|
||||||
}
|
}
|
||||||
FieldType::Struct(struct_ty) => {
|
FieldType::Struct(struct_ty) => {
|
||||||
let ty = expand_struct_type(struct_ty);
|
let ty = types::expand_struct_type(struct_ty);
|
||||||
fields.push(quote! { pub #field_name: #ty });
|
fields.push(quote! { pub #field_name: #ty });
|
||||||
|
|
||||||
let name = struct_ty.name();
|
let name = struct_ty.name();
|
||||||
|
@ -592,24 +592,6 @@ fn struct_type_projections(name: &str) -> Vec<String> {
|
||||||
iter.rev().map(str::to_string).collect()
|
iter.rev().map(str::to_string).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expands to the rust struct type
|
|
||||||
fn expand_struct_type(struct_ty: &StructFieldType) -> TokenStream {
|
|
||||||
match struct_ty {
|
|
||||||
StructFieldType::Type(ty) => {
|
|
||||||
let ty = util::ident(&ty.name().to_pascal_case());
|
|
||||||
quote! {#ty}
|
|
||||||
}
|
|
||||||
StructFieldType::Array(ty) => {
|
|
||||||
let ty = expand_struct_type(ty);
|
|
||||||
quote! {::std::vec::Vec<#ty>}
|
|
||||||
}
|
|
||||||
StructFieldType::FixedArray(ty, size) => {
|
|
||||||
let ty = expand_struct_type(ty);
|
|
||||||
quote! { [#ty; #size]}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
use ethers_core::{abi::ParamType, macros::ethers_core_crate};
|
//! Types expansion
|
||||||
use eyre::{bail, Result};
|
|
||||||
use proc_macro2::{Literal, TokenStream};
|
|
||||||
use quote::quote;
|
|
||||||
|
|
||||||
pub(crate) fn expand(kind: &ParamType) -> Result<TokenStream> {
|
use crate::{util, InternalStructs};
|
||||||
|
use ethers_core::{
|
||||||
|
abi::{struct_def::StructFieldType, Event, EventParam, Param, ParamType},
|
||||||
|
macros::ethers_core_crate,
|
||||||
|
};
|
||||||
|
use eyre::{bail, Result};
|
||||||
|
use inflector::Inflector;
|
||||||
|
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> {
|
||||||
let ethers_core = ethers_core_crate();
|
let ethers_core = ethers_core_crate();
|
||||||
|
|
||||||
match kind {
|
match kind {
|
||||||
|
@ -16,7 +24,7 @@ pub(crate) fn expand(kind: &ParamType) -> Result<TokenStream> {
|
||||||
5..=8 => Ok(quote! { i64 }),
|
5..=8 => Ok(quote! { i64 }),
|
||||||
9..=16 => Ok(quote! { i128 }),
|
9..=16 => Ok(quote! { i128 }),
|
||||||
17..=32 => Ok(quote! { #ethers_core::types::I256 }),
|
17..=32 => Ok(quote! { #ethers_core::types::I256 }),
|
||||||
_ => bail!("unsupported solidity type int{}", n),
|
_ => bail!("unsupported solidity type int{n}"),
|
||||||
},
|
},
|
||||||
ParamType::Uint(n) => match n / 8 {
|
ParamType::Uint(n) => match n / 8 {
|
||||||
1 => Ok(quote! { u8 }),
|
1 => Ok(quote! { u8 }),
|
||||||
|
@ -25,41 +33,151 @@ pub(crate) fn expand(kind: &ParamType) -> Result<TokenStream> {
|
||||||
5..=8 => Ok(quote! { u64 }),
|
5..=8 => Ok(quote! { u64 }),
|
||||||
9..=16 => Ok(quote! { u128 }),
|
9..=16 => Ok(quote! { u128 }),
|
||||||
17..=32 => Ok(quote! { #ethers_core::types::U256 }),
|
17..=32 => Ok(quote! { #ethers_core::types::U256 }),
|
||||||
_ => bail!("unsupported solidity type uint{}", n),
|
_ => bail!("unsupported solidity type uint{n}"),
|
||||||
},
|
},
|
||||||
ParamType::Bool => Ok(quote! { bool }),
|
ParamType::Bool => Ok(quote!(bool)),
|
||||||
ParamType::String => Ok(quote! { String }),
|
ParamType::String => Ok(quote!(::std::string::String)),
|
||||||
ParamType::Array(t) => {
|
ParamType::Array(ty) => Ok(array(expand(ty)?, None)),
|
||||||
let inner = expand(t)?;
|
ParamType::FixedBytes(n) => Ok(array(quote!(u8), Some(*n))),
|
||||||
Ok(quote! { Vec<#inner> })
|
ParamType::FixedArray(ty, n) => {
|
||||||
}
|
let ty = match **ty {
|
||||||
ParamType::FixedBytes(n) => {
|
|
||||||
// TODO(nlordell): what is the performance impact of returning large
|
|
||||||
// `FixedBytes` and `FixedArray`s with `web3`?
|
|
||||||
let size = Literal::usize_unsuffixed(*n);
|
|
||||||
Ok(quote! { [u8; #size] })
|
|
||||||
}
|
|
||||||
ParamType::FixedArray(t, n) => {
|
|
||||||
// TODO(nlordell): see above
|
|
||||||
let inner = match **t {
|
|
||||||
ParamType::Uint(size) => {
|
|
||||||
if size / 8 == 1 {
|
|
||||||
// this prevents type ambiguity with `FixedBytes`
|
// this prevents type ambiguity with `FixedBytes`
|
||||||
quote! { #ethers_core::types::Uint8}
|
// see: https://github.com/gakonst/ethers-rs/issues/1636
|
||||||
} else {
|
ParamType::Uint(size) if size / 8 == 1 => quote!(#ethers_core::types::Uint8),
|
||||||
expand(t)?
|
_ => expand(ty)?,
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => expand(t)?,
|
|
||||||
};
|
};
|
||||||
let size = Literal::usize_unsuffixed(*n);
|
Ok(array(ty, Some(*n)))
|
||||||
Ok(quote! { [#inner; #size] })
|
|
||||||
}
|
}
|
||||||
ParamType::Tuple(members) => {
|
ParamType::Tuple(members) => {
|
||||||
eyre::ensure!(!members.is_empty(), "Tuple must have at least 1 member");
|
eyre::ensure!(!members.is_empty(), "Tuple must have at least 1 member");
|
||||||
|
|
||||||
let members = members.iter().map(expand).collect::<Result<Vec<_>, _>>()?;
|
let members = members.iter().map(expand).collect::<Result<Vec<_>, _>>()?;
|
||||||
Ok(quote! { (#(#members,)*) })
|
Ok(quote!(( #( #members ),* )))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Expands the event's inputs.
|
||||||
|
pub fn expand_event_inputs(
|
||||||
|
event: &Event,
|
||||||
|
internal_structs: &InternalStructs,
|
||||||
|
) -> Result<Vec<(TokenStream, TokenStream, bool)>> {
|
||||||
|
event
|
||||||
|
.inputs
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, input)| {
|
||||||
|
// NOTE: Events can contain nameless values.
|
||||||
|
expand_event_input(input, &event.name, index, internal_structs)
|
||||||
|
.map(|ty| (util::expand_input_name(index, &input.name), ty, input.indexed))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expands an event property type.
|
||||||
|
///
|
||||||
|
/// Note that this is slightly different from expanding a Solidity type as complex types like arrays
|
||||||
|
/// and strings get emitted as hashes when they are indexed.
|
||||||
|
///
|
||||||
|
/// If a complex types matches with a struct previously parsed by the internal structs, we can
|
||||||
|
/// replace it.
|
||||||
|
fn expand_event_input(
|
||||||
|
input: &EventParam,
|
||||||
|
name: &str,
|
||||||
|
index: usize,
|
||||||
|
internal_structs: &InternalStructs,
|
||||||
|
) -> Result<TokenStream> {
|
||||||
|
let kind = &input.kind;
|
||||||
|
match (kind, input.indexed) {
|
||||||
|
(ParamType::Array(_), true) |
|
||||||
|
(ParamType::FixedArray(_, _), true) |
|
||||||
|
(ParamType::Tuple(_), true) |
|
||||||
|
(ParamType::Bytes, true) |
|
||||||
|
(ParamType::String, true) => {
|
||||||
|
let ethers_core = ethers_core_crate();
|
||||||
|
Ok(quote!(#ethers_core::types::H256))
|
||||||
|
}
|
||||||
|
|
||||||
|
(ParamType::Array(_), false) |
|
||||||
|
(ParamType::FixedArray(_, _), false) |
|
||||||
|
(ParamType::Tuple(_), false) => {
|
||||||
|
match internal_structs.get_event_input_struct_type(name, index) {
|
||||||
|
Some(ty) => {
|
||||||
|
let ty = util::ident(ty);
|
||||||
|
match kind {
|
||||||
|
ParamType::Array(_) => Ok(array(ty, None)),
|
||||||
|
ParamType::FixedArray(_, size) => Ok(array(ty, Some(*size))),
|
||||||
|
ParamType::Tuple(_) => Ok(quote!(#ty)),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => expand(kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => expand(kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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>>(
|
||||||
|
params: &'a [Param],
|
||||||
|
resolve_tuple: F,
|
||||||
|
) -> Result<Vec<(TokenStream, TokenStream)>> {
|
||||||
|
params
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, param)| {
|
||||||
|
// NOTE: Params can be unnamed.
|
||||||
|
expand_resolved(¶m.kind, param, &resolve_tuple)
|
||||||
|
.map(|ty| (util::expand_input_name(idx, ¶m.name), ty))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expands a ParamType Solidity type to its Rust equivalent, while resolving tuples' types using
|
||||||
|
/// the given function.
|
||||||
|
fn expand_resolved<'a, 'b, F: Fn(&'a Param) -> Option<&'b str>>(
|
||||||
|
kind: &'a ParamType,
|
||||||
|
param: &'a Param,
|
||||||
|
resolve_tuple: &F,
|
||||||
|
) -> Result<TokenStream> {
|
||||||
|
match kind {
|
||||||
|
ParamType::Array(ty) => Ok(array(expand_resolved(ty, param, resolve_tuple)?, None)),
|
||||||
|
ParamType::FixedArray(ty, size) => {
|
||||||
|
Ok(array(expand_resolved(ty, param, resolve_tuple)?, Some(*size)))
|
||||||
|
}
|
||||||
|
ParamType::Tuple(_) => match resolve_tuple(param) {
|
||||||
|
Some(ty) => {
|
||||||
|
let ty = util::ident(ty);
|
||||||
|
Ok(quote!(#ty))
|
||||||
|
}
|
||||||
|
None => expand(kind),
|
||||||
|
},
|
||||||
|
_ => expand(kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expands to the Rust struct type.
|
||||||
|
pub fn expand_struct_type(struct_ty: &StructFieldType) -> TokenStream {
|
||||||
|
match struct_ty {
|
||||||
|
StructFieldType::Type(ty) => {
|
||||||
|
let ty = util::ident(&ty.name().to_pascal_case());
|
||||||
|
quote!(#ty)
|
||||||
|
}
|
||||||
|
StructFieldType::Array(ty) => array(expand_struct_type(ty), None),
|
||||||
|
StructFieldType::FixedArray(ty, size) => array(expand_struct_type(ty), Some(*size)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expands `ty` into a Rust array or vector.
|
||||||
|
fn array<T: ToTokens>(ty: T, size: Option<usize>) -> TokenStream {
|
||||||
|
match size {
|
||||||
|
Some(size) => {
|
||||||
|
let size = Literal::usize_unsuffixed(size);
|
||||||
|
quote!([#ty; #size])
|
||||||
|
}
|
||||||
|
None => quote!(::std::vec::Vec<#ty>),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue