use ethers_contract_abigen::ethers_core_crate; use ethers_core::abi::ParamType; use proc_macro2::Literal; use quote::quote; use syn::spanned::Spanned as _; use syn::{parse::Error, Expr, GenericArgument, Lit, PathArguments, Type}; pub fn signature(hash: &[u8]) -> proc_macro2::TokenStream { let core_crate = ethers_core_crate(); let bytes = hash.iter().copied().map(Literal::u8_unsuffixed); quote! {#core_crate::types::H256([#( #bytes ),*])} } /// Parses an int type from its string representation pub fn parse_int_param_type(s: &str) -> Option { let size = s .chars() .skip(1) .collect::() .parse::() .ok()?; if s.starts_with('u') { Some(ParamType::Uint(size)) } else if s.starts_with('i') { Some(ParamType::Int(size)) } else { None } } // 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 // 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(); match kind { ParamType::String | ParamType::Bytes | ParamType::Array(_) | ParamType::FixedArray(_, _) | ParamType::Tuple(_) => quote! {#core_crate::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(); match kind { ParamType::Address => { quote! {#core_crate::abi::ParamType::Address} } ParamType::Bytes => { quote! {#core_crate::abi::ParamType::Bytes} } ParamType::Int(size) => { let size = Literal::usize_suffixed(*size); quote! {#core_crate::abi::ParamType::Int(#size)} } ParamType::Uint(size) => { let size = Literal::usize_suffixed(*size); quote! {#core_crate::abi::ParamType::Uint(#size)} } ParamType::Bool => { quote! {#core_crate::abi::ParamType::Bool} } ParamType::String => { quote! {#core_crate::abi::ParamType::String} } ParamType::Array(ty) => { let ty = param_type_quote(&*ty); quote! {#core_crate::abi::ParamType::Array(Box::new(#ty))} } ParamType::FixedBytes(size) => { let size = Literal::usize_suffixed(*size); quote! {#core_crate::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)} } ParamType::Tuple(tuple) => { let elements = tuple.iter().map(param_type_quote); quote! { #core_crate::abi::ParamType::Tuple( ::std::vec![ #( #elements ),* ] ) } } } } /// Tries to find the corresponding `ParamType` used for tokenization for the /// given type pub fn find_parameter_type(ty: &Type) -> Result { match ty { Type::Array(ty) => { let param = find_parameter_type(ty.elem.as_ref())?; if let Expr::Lit(ref expr) = ty.len { if let Lit::Int(ref len) = expr.lit { if let Ok(size) = len.base10_parse::() { return Ok(ParamType::FixedArray(Box::new(param), size)); } } } Err(Error::new( ty.span(), "Failed to derive proper ABI from array field", )) } Type::Path(ty) => { if let Some(ident) = ty.path.get_ident() { return match ident.to_string().to_lowercase().as_str() { "address" => Ok(ParamType::Address), "string" => Ok(ParamType::String), "bool" => Ok(ParamType::Bool), "int" | "uint" => Ok(ParamType::Uint(256)), "h160" => Ok(ParamType::FixedBytes(20)), "h256" | "secret" | "hash" => Ok(ParamType::FixedBytes(32)), "h512" | "public" => Ok(ParamType::FixedBytes(64)), s => parse_int_param_type(s).ok_or_else(|| { Error::new(ty.span(), "Failed to derive proper ABI from fields") }), }; } // check for `Vec` if ty.path.segments.len() == 1 && ty.path.segments[0].ident == "Vec" { 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))); } } } } 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::, _>>()?; Ok(ParamType::Tuple(params)) } _ => Err(Error::new( ty.span(), "Failed to derive proper ABI from fields", )), } }