2021-11-05 13:00:01 +00:00
|
|
|
use ethers_core::{abi::ParamType, macros::ethers_core_crate, types::Selector};
|
2023-02-14 03:54:00 +00:00
|
|
|
use proc_macro2::{Ident, Literal, Span, TokenStream};
|
2021-12-04 04:19:00 +00:00
|
|
|
use quote::{quote, quote_spanned};
|
2021-10-18 10:28:38 +00:00
|
|
|
use syn::{
|
2023-02-14 03:54:00 +00:00
|
|
|
parse::Error, spanned::Spanned, Data, DeriveInput, Expr, Fields, GenericArgument, Lit,
|
2021-10-29 12:29:35 +00:00
|
|
|
PathArguments, Type,
|
2021-10-18 10:28:38 +00:00
|
|
|
};
|
2021-10-16 08:19:42 +00:00
|
|
|
|
2022-08-02 18:03:52 +00:00
|
|
|
pub fn ident(name: &str) -> Ident {
|
|
|
|
Ident::new(name, Span::call_site())
|
|
|
|
}
|
|
|
|
|
2023-02-14 03:54:00 +00:00
|
|
|
pub fn signature(hash: &[u8]) -> TokenStream {
|
|
|
|
let ethers_core = ethers_core_crate();
|
2021-10-16 08:19:42 +00:00
|
|
|
let bytes = hash.iter().copied().map(Literal::u8_unsuffixed);
|
2023-02-14 03:54:00 +00:00
|
|
|
quote! {#ethers_core::types::H256([#( #bytes ),*])}
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
|
|
|
|
2023-02-14 03:54:00 +00:00
|
|
|
pub fn selector(selector: Selector) -> TokenStream {
|
2021-10-18 10:28:38 +00:00
|
|
|
let bytes = selector.iter().copied().map(Literal::u8_unsuffixed);
|
|
|
|
quote! {[#( #bytes ),*]}
|
|
|
|
}
|
|
|
|
|
2023-02-21 00:27:43 +00:00
|
|
|
/// Parses an int / hash type from its string representation
|
|
|
|
pub fn parse_param_type(s: &str) -> Option<ParamType> {
|
2023-02-14 03:54:00 +00:00
|
|
|
match s.chars().next() {
|
2023-02-21 00:27:43 +00:00
|
|
|
Some('H' | 'h') => {
|
|
|
|
let size = s[1..].parse::<usize>().ok()? / 8;
|
|
|
|
Some(ParamType::FixedBytes(size))
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(c @ 'U' | c @ 'I' | c @ 'u' | c @ 'i') => {
|
2023-02-14 03:54:00 +00:00
|
|
|
let size = s[1..].parse::<usize>().ok()?;
|
2023-02-21 00:27:43 +00:00
|
|
|
if matches!(c, 'U' | 'u') {
|
2023-02-14 03:54:00 +00:00
|
|
|
Some(ParamType::Uint(size))
|
|
|
|
} else {
|
|
|
|
Some(ParamType::Int(size))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => None,
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Converts param types for indexed parameters to bytes32 where appropriate
|
|
|
|
// This applies to strings, arrays, structs and bytes to follow the encoding of
|
|
|
|
// these indexed param types according to
|
2022-03-19 17:05:39 +00:00
|
|
|
// <https://solidity.readthedocs.io/en/develop/abi-spec.html#encoding-of-indexed-event-parameters>
|
2023-02-14 03:54:00 +00:00
|
|
|
pub fn topic_param_type_quote(kind: &ParamType) -> TokenStream {
|
|
|
|
let ethers_core = ethers_core_crate();
|
2021-10-16 08:19:42 +00:00
|
|
|
match kind {
|
2021-10-29 12:29:35 +00:00
|
|
|
ParamType::String |
|
|
|
|
ParamType::Bytes |
|
|
|
|
ParamType::Array(_) |
|
|
|
|
ParamType::FixedArray(_, _) |
|
2023-02-14 03:54:00 +00:00
|
|
|
ParamType::Tuple(_) => quote! {#ethers_core::abi::ParamType::FixedBytes(32)},
|
2021-10-16 08:19:42 +00:00
|
|
|
ty => param_type_quote(ty),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the rust type for the given parameter
|
2023-02-14 03:54:00 +00:00
|
|
|
pub fn param_type_quote(kind: &ParamType) -> TokenStream {
|
|
|
|
let ethers_core = ethers_core_crate();
|
2021-10-16 08:19:42 +00:00
|
|
|
match kind {
|
|
|
|
ParamType::Address => {
|
2023-02-14 03:54:00 +00:00
|
|
|
quote! {#ethers_core::abi::ParamType::Address}
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
|
|
|
ParamType::Bytes => {
|
2023-02-14 03:54:00 +00:00
|
|
|
quote! {#ethers_core::abi::ParamType::Bytes}
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
|
|
|
ParamType::Int(size) => {
|
|
|
|
let size = Literal::usize_suffixed(*size);
|
2023-02-14 03:54:00 +00:00
|
|
|
quote! {#ethers_core::abi::ParamType::Int(#size)}
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
|
|
|
ParamType::Uint(size) => {
|
|
|
|
let size = Literal::usize_suffixed(*size);
|
2023-02-14 03:54:00 +00:00
|
|
|
quote! {#ethers_core::abi::ParamType::Uint(#size)}
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
|
|
|
ParamType::Bool => {
|
2023-02-14 03:54:00 +00:00
|
|
|
quote! {#ethers_core::abi::ParamType::Bool}
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
|
|
|
ParamType::String => {
|
2023-02-14 03:54:00 +00:00
|
|
|
quote! {#ethers_core::abi::ParamType::String}
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
|
|
|
ParamType::Array(ty) => {
|
2022-08-01 02:00:31 +00:00
|
|
|
let ty = param_type_quote(ty);
|
2023-02-14 03:54:00 +00:00
|
|
|
quote! {#ethers_core::abi::ParamType::Array(Box::new(#ty))}
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
|
|
|
ParamType::FixedBytes(size) => {
|
|
|
|
let size = Literal::usize_suffixed(*size);
|
2023-02-14 03:54:00 +00:00
|
|
|
quote! {#ethers_core::abi::ParamType::FixedBytes(#size)}
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
|
|
|
ParamType::FixedArray(ty, size) => {
|
2022-08-01 02:00:31 +00:00
|
|
|
let ty = param_type_quote(ty);
|
2021-10-16 08:19:42 +00:00
|
|
|
let size = Literal::usize_suffixed(*size);
|
2023-02-14 03:54:00 +00:00
|
|
|
quote! {#ethers_core::abi::ParamType::FixedArray(Box::new(#ty), #size)}
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
|
|
|
ParamType::Tuple(tuple) => {
|
|
|
|
let elements = tuple.iter().map(param_type_quote);
|
2023-02-14 03:54:00 +00:00
|
|
|
quote!(#ethers_core::abi::ParamType::Tuple(::std::vec![#( #elements ),*]))
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-16 13:42:17 +00:00
|
|
|
/// Tries to find the corresponding `ParamType` used for tokenization for the
|
|
|
|
/// given type
|
2021-10-16 08:19:42 +00:00
|
|
|
pub fn find_parameter_type(ty: &Type) -> Result<ParamType, Error> {
|
|
|
|
match ty {
|
2023-02-21 00:27:43 +00:00
|
|
|
Type::Array(arr) => {
|
|
|
|
let ty = find_parameter_type(&arr.elem)?;
|
|
|
|
if let Expr::Lit(ref expr) = arr.len {
|
2021-10-16 08:19:42 +00:00
|
|
|
if let Lit::Int(ref len) = expr.lit {
|
|
|
|
if let Ok(size) = len.base10_parse::<usize>() {
|
2023-02-21 00:27:43 +00:00
|
|
|
return Ok(ParamType::FixedArray(Box::new(ty), size))
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-21 00:27:43 +00:00
|
|
|
Err(Error::new(arr.span(), "Failed to derive proper ABI from array field"))
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
2023-02-21 00:27:43 +00:00
|
|
|
|
2021-10-16 08:19:42 +00:00
|
|
|
Type::Path(ty) => {
|
2022-04-16 20:08:31 +00:00
|
|
|
// check for `Vec`
|
2023-02-21 00:27:43 +00:00
|
|
|
if let Some(segment) = ty.path.segments.iter().find(|s| s.ident == "Vec") {
|
|
|
|
if let PathArguments::AngleBracketed(ref args) = segment.arguments {
|
|
|
|
// Vec<T, A?>
|
|
|
|
debug_assert!(matches!(args.args.len(), 1 | 2));
|
|
|
|
let ty = args.args.iter().next().unwrap();
|
|
|
|
if let GenericArgument::Type(ref ty) = ty {
|
|
|
|
return find_parameter_type(ty).map(|kind| ParamType::Array(Box::new(kind)))
|
2022-04-16 20:08:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-21 00:27:43 +00:00
|
|
|
|
|
|
|
// match on the last segment of the path
|
|
|
|
ty.path
|
|
|
|
.get_ident()
|
|
|
|
.or_else(|| ty.path.segments.last().map(|s| &s.ident))
|
|
|
|
.and_then(|ident| {
|
|
|
|
match ident.to_string().as_str() {
|
|
|
|
// eth types
|
|
|
|
"Address" => Some(ParamType::Address),
|
|
|
|
"Bytes" => Some(ParamType::Bytes),
|
|
|
|
"Uint8" => Some(ParamType::Uint(8)),
|
|
|
|
|
|
|
|
// core types
|
|
|
|
"String" => Some(ParamType::String),
|
|
|
|
"bool" => Some(ParamType::Bool),
|
|
|
|
// usize / isize, shouldn't happen but use max width
|
|
|
|
"usize" => Some(ParamType::Uint(64)),
|
|
|
|
"isize" => Some(ParamType::Int(64)),
|
|
|
|
|
|
|
|
s => parse_param_type(s),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.ok_or_else(|| Error::new(ty.span(), "Failed to derive proper ABI from fields"))
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
2023-02-21 00:27:43 +00:00
|
|
|
|
2023-02-14 03:54:00 +00:00
|
|
|
Type::Tuple(ty) => ty
|
|
|
|
.elems
|
|
|
|
.iter()
|
|
|
|
.map(find_parameter_type)
|
|
|
|
.collect::<Result<Vec<_>, _>>()
|
|
|
|
.map(ParamType::Tuple),
|
2023-02-21 00:27:43 +00:00
|
|
|
|
2021-10-29 12:29:35 +00:00
|
|
|
_ => Err(Error::new(ty.span(), "Failed to derive proper ABI from fields")),
|
2021-10-16 08:19:42 +00:00
|
|
|
}
|
|
|
|
}
|
2021-10-18 10:28:38 +00:00
|
|
|
|
|
|
|
/// Attempts to determine the ABI Paramtypes from the type's AST
|
|
|
|
pub fn derive_abi_inputs_from_fields(
|
|
|
|
input: &DeriveInput,
|
|
|
|
trait_name: &str,
|
|
|
|
) -> Result<Vec<(String, ParamType)>, Error> {
|
|
|
|
let fields: Vec<_> = 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 => {
|
|
|
|
return Err(Error::new(
|
|
|
|
input.span(),
|
2022-11-07 23:43:11 +00:00
|
|
|
format!("{trait_name} cannot be derived for empty structs and unit"),
|
2021-10-18 10:28:38 +00:00
|
|
|
))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Data::Enum(_) => {
|
|
|
|
return Err(Error::new(
|
|
|
|
input.span(),
|
2022-11-07 23:43:11 +00:00
|
|
|
format!("{trait_name} cannot be derived for enums"),
|
2021-10-29 12:29:35 +00:00
|
|
|
))
|
2021-10-18 10:28:38 +00:00
|
|
|
}
|
|
|
|
Data::Union(_) => {
|
|
|
|
return Err(Error::new(
|
|
|
|
input.span(),
|
2022-11-07 23:43:11 +00:00
|
|
|
format!("{trait_name} cannot be derived for unions"),
|
2021-10-29 12:29:35 +00:00
|
|
|
))
|
2021-10-18 10:28:38 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
fields
|
|
|
|
.iter()
|
|
|
|
.map(|f| {
|
2021-10-29 12:29:35 +00:00
|
|
|
let name =
|
|
|
|
f.ident.as_ref().map(|name| name.to_string()).unwrap_or_else(|| "".to_string());
|
2021-10-18 10:28:38 +00:00
|
|
|
find_parameter_type(&f.ty).map(|ty| (name, ty))
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
2021-12-04 04:19:00 +00:00
|
|
|
|
|
|
|
/// Use `AbiType::param_type` fo each field to construct the input types own param type
|
|
|
|
pub fn derive_param_type_with_abi_type(
|
|
|
|
input: &DeriveInput,
|
|
|
|
trait_name: &str,
|
2023-02-14 03:54:00 +00:00
|
|
|
) -> Result<TokenStream, Error> {
|
|
|
|
let ethers_core = ethers_core_crate();
|
|
|
|
let params = abi_parameters_array(input, trait_name)?;
|
2021-12-04 04:19:00 +00:00
|
|
|
Ok(quote! {
|
2023-02-14 03:54:00 +00:00
|
|
|
#ethers_core::abi::ParamType::Tuple(::std::vec!#params)
|
2021-12-04 04:19:00 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Use `AbiType::param_type` fo each field to construct the whole signature `<name>(<params,>*)` as
|
2023-02-14 03:54:00 +00:00
|
|
|
/// `String`.
|
|
|
|
pub fn abi_signature_with_abi_type(
|
2021-12-04 04:19:00 +00:00
|
|
|
input: &DeriveInput,
|
|
|
|
function_name: &str,
|
|
|
|
trait_name: &str,
|
2023-02-14 03:54:00 +00:00
|
|
|
) -> Result<TokenStream, Error> {
|
|
|
|
let params = abi_parameters_array(input, trait_name)?;
|
2021-12-04 04:19:00 +00:00
|
|
|
Ok(quote! {
|
|
|
|
{
|
|
|
|
let params: String = #params
|
2023-02-14 03:54:00 +00:00
|
|
|
.iter()
|
|
|
|
.map(|p| p.to_string())
|
|
|
|
.collect::<::std::vec::Vec<_>>()
|
|
|
|
.join(",");
|
2021-12-04 04:19:00 +00:00
|
|
|
let function_name = #function_name;
|
|
|
|
format!("{}({})", function_name, params)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Use `AbiType::param_type` fo each field to construct the signature's parameters as runtime array
|
|
|
|
/// `[param1, param2,...]`
|
2023-02-14 03:54:00 +00:00
|
|
|
pub fn abi_parameters_array(input: &DeriveInput, trait_name: &str) -> Result<TokenStream, Error> {
|
|
|
|
let ethers_core = ethers_core_crate();
|
2021-12-04 04:19:00 +00:00
|
|
|
|
2023-02-14 03:54:00 +00:00
|
|
|
let fields = match input.data {
|
2021-12-04 04:19:00 +00:00
|
|
|
Data::Struct(ref data) => match data.fields {
|
2023-02-14 03:54:00 +00:00
|
|
|
Fields::Named(ref fields) => &fields.named,
|
|
|
|
Fields::Unnamed(ref fields) => &fields.unnamed,
|
2021-12-04 04:19:00 +00:00
|
|
|
Fields::Unit => {
|
|
|
|
return Err(Error::new(
|
|
|
|
input.span(),
|
2022-11-07 23:43:11 +00:00
|
|
|
format!("{trait_name} cannot be derived for empty structs and unit"),
|
2021-12-04 04:19:00 +00:00
|
|
|
))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Data::Enum(_) => {
|
|
|
|
return Err(Error::new(
|
|
|
|
input.span(),
|
2022-11-07 23:43:11 +00:00
|
|
|
format!("{trait_name} cannot be derived for enums"),
|
2021-12-04 04:19:00 +00:00
|
|
|
))
|
|
|
|
}
|
|
|
|
Data::Union(_) => {
|
|
|
|
return Err(Error::new(
|
|
|
|
input.span(),
|
2022-11-07 23:43:11 +00:00
|
|
|
format!("{trait_name} cannot be derived for unions"),
|
2021-12-04 04:19:00 +00:00
|
|
|
))
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-02-14 03:54:00 +00:00
|
|
|
let iter = fields.iter().map(|f| {
|
|
|
|
let ty = &f.ty;
|
|
|
|
quote_spanned!(f.span() => <#ty as #ethers_core::abi::AbiType>::param_type())
|
|
|
|
});
|
|
|
|
|
2021-12-04 04:19:00 +00:00
|
|
|
Ok(quote! {
|
2023-02-14 03:54:00 +00:00
|
|
|
[#( #iter ),*]
|
2021-12-04 04:19:00 +00:00
|
|
|
})
|
|
|
|
}
|
2023-02-21 00:27:43 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use syn::parse_quote;
|
|
|
|
|
|
|
|
macro_rules! type_test_cases {
|
|
|
|
($($t:ty => $e:expr),+ $(,)?) => {{
|
|
|
|
&[
|
|
|
|
$(
|
|
|
|
(parse_quote!($t), $e),
|
|
|
|
)+
|
|
|
|
]
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn arr(ty: ParamType) -> ParamType {
|
|
|
|
ParamType::Array(Box::new(ty))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn farr(ty: ParamType, len: usize) -> ParamType {
|
|
|
|
ParamType::FixedArray(Box::new(ty), len)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_find_params() {
|
|
|
|
use ParamType as PT;
|
|
|
|
let test_cases: &[(Type, ParamType)] = type_test_cases! {
|
|
|
|
u8 => PT::Uint(8),
|
|
|
|
u16 => PT::Uint(16),
|
|
|
|
u32 => PT::Uint(32),
|
|
|
|
u64 => PT::Uint(64),
|
|
|
|
usize => PT::Uint(64),
|
|
|
|
u128 => PT::Uint(128),
|
|
|
|
::ethers::types::U256 => PT::Uint(256),
|
|
|
|
ethers::types::U256 => PT::Uint(256),
|
|
|
|
::ethers_core::types::U256 => PT::Uint(256),
|
|
|
|
ethers_core::types::U256 => PT::Uint(256),
|
|
|
|
U256 => PT::Uint(256),
|
|
|
|
|
|
|
|
i8 => PT::Int(8),
|
|
|
|
i16 => PT::Int(16),
|
|
|
|
i32 => PT::Int(32),
|
|
|
|
i64 => PT::Int(64),
|
|
|
|
isize => PT::Int(64),
|
|
|
|
i128 => PT::Int(128),
|
|
|
|
::ethers::types::I256 => PT::Int(256),
|
|
|
|
ethers::types::I256 => PT::Int(256),
|
|
|
|
::ethers_core::types::I256 => PT::Int(256),
|
|
|
|
ethers_core::types::I256 => PT::Int(256),
|
|
|
|
I256 => PT::Int(256),
|
|
|
|
|
|
|
|
|
|
|
|
::ethers::types::H160 => PT::FixedBytes(20),
|
|
|
|
H160 => PT::FixedBytes(20),
|
|
|
|
::ethers::types::H256 => PT::FixedBytes(32),
|
|
|
|
H256 => PT::FixedBytes(32),
|
|
|
|
::ethers::types::H512 => PT::FixedBytes(64),
|
|
|
|
H512 => PT::FixedBytes(64),
|
|
|
|
|
|
|
|
::std::vec::Vec<::ethers_core::types::U256, ::std::alloc::Global> => arr(PT::Uint(256)),
|
|
|
|
::std::vec::Vec<::ethers_core::types::U256, Global> => arr(PT::Uint(256)),
|
|
|
|
::std::vec::Vec<::ethers_core::types::U256> => arr(PT::Uint(256)),
|
|
|
|
::std::vec::Vec<ethers::types::U256> => arr(PT::Uint(256)),
|
|
|
|
::std::vec::Vec<U256> => arr(PT::Uint(256)),
|
|
|
|
std::vec::Vec<U256> => arr(PT::Uint(256)),
|
|
|
|
vec::Vec<U256> => arr(PT::Uint(256)),
|
|
|
|
Vec<U256> => arr(PT::Uint(256)),
|
|
|
|
|
|
|
|
[u64; 8] => farr(PT::Uint(64), 8),
|
|
|
|
[u64; 16] => farr(PT::Uint(64), 16),
|
|
|
|
[::ethers_core::types::U256; 2] => farr(PT::Uint(256), 2),
|
|
|
|
[String; 4] => farr(PT::String, 4),
|
|
|
|
[Address; 2] => farr(PT::Address, 2),
|
|
|
|
|
|
|
|
(String, String, Address) => PT::Tuple(vec![PT::String, PT::String, PT::Address]),
|
|
|
|
(::ethers_core::types::U256, u8, ::ethers_core::types::Address)
|
|
|
|
=> PT::Tuple(vec![PT::Uint(256), PT::Uint(8), PT::Address]),
|
|
|
|
(::ethers::types::Bytes, ::ethers::types::H256, (::ethers::types::Address, ::std::string::String))
|
|
|
|
=> PT::Tuple(vec![
|
|
|
|
PT::Bytes,
|
|
|
|
PT::FixedBytes(32),
|
|
|
|
PT::Tuple(vec![PT::Address, PT::String])
|
|
|
|
]),
|
|
|
|
};
|
|
|
|
|
|
|
|
for (ty, expected) in test_cases {
|
|
|
|
match find_parameter_type(ty) {
|
|
|
|
Ok(ty) => assert_eq!(ty, *expected),
|
|
|
|
Err(e) => panic!("{e}: {ty:#?}\n{expected}"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|