use super::{util, Context}; use crate::contract::types; use ethers_core::{ abi::{Param, ParamType}, macros::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate}, }; use proc_macro2::{Ident, TokenStream}; 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> 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 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 { let doc = util::expand_doc(&format!("{} was auto-generated with ethers-rs Abigen. More information at: https://github.com/gakonst/ethers-rs", name)); let ethers_core = ethers_core_crate(); let ethers_providers = ethers_providers_crate(); let ethers_contract = ethers_contract_crate(); quote! { #![allow(clippy::enum_variant_names)] #![allow(dead_code)] #![allow(clippy::type_complexity)] #![allow(unused_imports)] #doc use std::sync::Arc; use #ethers_core::{ abi::{Abi, Token, Detokenize, InvalidOutputType, Tokenizable}, types::*, // import all the types so that we can codegen for everything }; use #ethers_contract::{Contract, builders::{ContractCall, Event}, Lazy}; use #ethers_providers::Middleware; } } /// Generates the static `Abi` constants and the contract struct pub(crate) fn struct_declaration(cx: &Context) -> TokenStream { let name = &cx.contract_ident; let abi = &cx.abi_str; let abi_name = cx.inline_abi_ident(); let ethers_core = ethers_core_crate(); let ethers_providers = ethers_providers_crate(); let ethers_contract = ethers_contract_crate(); let abi_parse = if !cx.human_readable { quote! { pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> = #ethers_contract::Lazy::new(|| #ethers_core::utils::__serde_json::from_str(#abi) .expect("invalid abi")); } } else { quote! { pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> = #ethers_contract::Lazy::new(|| #ethers_core::abi::parse_abi_str(#abi) .expect("invalid abi")); } }; let bytecode = if let Some(ref bytecode) = cx.contract_bytecode { let bytecode_name = cx.inline_bytecode_ident(); let hex_bytecode = format!("{}", bytecode); quote! { /// Bytecode of the #name contract pub static #bytecode_name: #ethers_contract::Lazy<#ethers_core::types::Bytes> = #ethers_contract::Lazy::new(|| #hex_bytecode.parse() .expect("invalid bytecode")); } } else { quote! {} }; quote! { // Inline ABI declaration #abi_parse #bytecode // Struct declaration pub struct #name(#ethers_contract::Contract); impl Clone for #name { fn clone(&self) -> Self { #name(self.0.clone()) } } // Deref to the inner contract in order to access more specific functions functions impl std::ops::Deref for #name { type Target = #ethers_contract::Contract; fn deref(&self) -> &Self::Target { &self.0 } } impl std::fmt::Debug for #name { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_tuple(stringify!(#name)) .field(&self.address()) .finish() } } } } /// Expands to the tuple struct definition pub(crate) fn expand_data_tuple( name: &Ident, params: &[(TokenStream, TokenStream)], ) -> TokenStream { let fields = params .iter() .map(|(_, ty)| { quote! { pub #ty } }) .collect::>(); if fields.is_empty() { quote! { struct #name; } } else { quote! { struct #name( #( #fields ),* ); } } } /// Expands to a struct definition with named fields pub(crate) fn expand_data_struct( name: &Ident, params: &[(TokenStream, TokenStream)], ) -> TokenStream { let fields = params .iter() .map(|(name, ty)| { quote! { pub #name: #ty } }) .collect::>(); quote! { struct #name { #( #fields, )* } } }