2020-05-26 18:57:59 +00:00
#![ deny(missing_docs) ]
mod common ;
mod events ;
mod methods ;
2021-03-16 19:37:19 +00:00
mod structs ;
2020-05-26 18:57:59 +00:00
mod types ;
use super ::util ;
2020-06-03 20:09:46 +00:00
use super ::Abigen ;
2021-08-16 07:29:44 +00:00
use crate ::contract ::structs ::InternalStructs ;
use crate ::rawabi ::RawAbi ;
2020-05-26 18:57:59 +00:00
use anyhow ::{ anyhow , Context as _ , Result } ;
2021-03-16 19:37:19 +00:00
use ethers_core ::abi ::AbiParser ;
2020-10-29 07:48:24 +00:00
use ethers_core ::{
abi ::{ parse_abi , Abi } ,
types ::Address ,
} ;
2020-05-26 18:57:59 +00:00
use inflector ::Inflector ;
use proc_macro2 ::{ Ident , Literal , TokenStream } ;
use quote ::quote ;
2021-02-19 06:34:56 +00:00
use std ::collections ::BTreeMap ;
2020-05-26 18:57:59 +00:00
use syn ::{ Path , Visibility } ;
/// Internal shared context for generating smart contract bindings.
pub ( crate ) struct Context {
/// The ABI string pre-parsing.
abi_str : Literal ,
/// The parsed ABI.
abi : Abi ,
2021-03-16 19:37:19 +00:00
/// The parser used for human readable format
abi_parser : AbiParser ,
2021-08-16 07:29:44 +00:00
/// Contains all the solidity structs extracted from the JSON ABI.
internal_structs : InternalStructs ,
2020-10-29 07:48:24 +00:00
/// Was the ABI in human readable format?
human_readable : bool ,
2020-05-26 18:57:59 +00:00
/// The contract name as an identifier.
contract_name : Ident ,
/// Manually specified method aliases.
2021-02-19 06:34:56 +00:00
method_aliases : BTreeMap < String , Ident > ,
2020-05-26 18:57:59 +00:00
/// Derives added to event structs and enums.
event_derives : Vec < Path > ,
2021-09-03 15:57:40 +00:00
/// Manually specified event aliases.
event_aliases : BTreeMap < String , Ident > ,
2020-05-26 18:57:59 +00:00
}
impl Context {
2020-06-03 20:09:46 +00:00
pub ( crate ) fn expand ( args : Abigen ) -> Result < TokenStream > {
let cx = Self ::from_abigen ( args ) ? ;
2020-05-26 18:57:59 +00:00
let name = & cx . contract_name ;
let name_mod = util ::ident ( & format! (
" {}_mod " ,
cx . contract_name . to_string ( ) . to_lowercase ( )
) ) ;
2020-06-02 21:10:46 +00:00
let abi_name = super ::util ::safe_ident ( & format! ( " {} _ABI " , name . to_string ( ) . to_uppercase ( ) ) ) ;
2020-05-26 18:57:59 +00:00
// 0. Imports
2020-06-22 08:44:08 +00:00
let imports = common ::imports ( & name . to_string ( ) ) ;
2020-05-26 18:57:59 +00:00
// 1. Declare Contract struct
2020-06-02 21:10:46 +00:00
let struct_decl = common ::struct_declaration ( & cx , & abi_name ) ;
2020-05-26 18:57:59 +00:00
// 2. Declare events structs & impl FromTokens for each event
let events_decl = cx . events_declaration ( ) ? ;
// 3. impl block for the event functions
2021-03-16 19:37:19 +00:00
let contract_events = cx . event_methods ( ) ? ;
2020-05-26 18:57:59 +00:00
// 4. impl block for the contract methods
let contract_methods = cx . methods ( ) ? ;
2021-03-16 19:37:19 +00:00
// 5. Declare the structs parsed from the human readable abi
let abi_structs_decl = cx . abi_structs ( ) ? ;
2021-08-28 18:53:01 +00:00
let ethers_core = util ::ethers_core_crate ( ) ;
let ethers_contract = util ::ethers_contract_crate ( ) ;
let ethers_providers = util ::ethers_providers_crate ( ) ;
2020-05-26 18:57:59 +00:00
Ok ( quote! {
// export all the created data types
pub use #name_mod ::* ;
2021-01-05 12:15:18 +00:00
#[ allow(clippy::too_many_arguments) ]
2020-05-26 18:57:59 +00:00
mod #name_mod {
#imports
#struct_decl
2021-08-28 18:53:01 +00:00
impl < ' a , M : #ethers_providers ::Middleware > #name < M > {
2020-05-26 18:57:59 +00:00
/// Creates a new contract instance with the specified `ethers`
/// client at the given `Address`. The contract derefs to a `ethers::Contract`
/// object
2021-08-28 18:53:01 +00:00
pub fn new < T : Into < #ethers_core ::types ::Address > > ( address : T , client : ::std ::sync ::Arc < M > ) -> Self {
let contract = #ethers_contract ::Contract ::new ( address . into ( ) , #abi_name . clone ( ) , client ) ;
2020-05-26 18:57:59 +00:00
Self ( contract )
}
// TODO: Implement deployment.
#contract_methods
#contract_events
}
#events_decl
2021-03-16 19:37:19 +00:00
#abi_structs_decl
2020-05-26 18:57:59 +00:00
}
} )
}
/// Create a context from the code generation arguments.
2020-06-03 20:09:46 +00:00
fn from_abigen ( args : Abigen ) -> Result < Self > {
2020-05-26 18:57:59 +00:00
// get the actual ABI string
let abi_str = args . abi_source . get ( ) . context ( " failed to get ABI JSON " ) ? ;
2021-03-16 19:37:19 +00:00
let mut abi_parser = AbiParser ::default ( ) ;
2020-05-26 18:57:59 +00:00
// parse it
2020-10-29 07:48:24 +00:00
let ( abi , human_readable ) : ( Abi , _ ) = if let Ok ( abi ) = serde_json ::from_str ( & abi_str ) {
// normal abi format
( abi , false )
} else {
// heuristic for parsing the human readable format
2021-03-16 19:37:19 +00:00
( abi_parser . parse_str ( & abi_str ) ? , true )
2020-10-29 07:48:24 +00:00
} ;
2020-05-26 18:57:59 +00:00
2021-08-16 07:29:44 +00:00
// try to extract all the solidity structs from the normal JSON ABI
2021-10-02 14:34:01 +00:00
// we need to parse the json abi again because we need the internalType fields which are omitted by ethabi. If the ABI was defined as human readable we use the `internal_structs` from the Abi Parser
let internal_structs = if human_readable {
let mut internal_structs = InternalStructs ::default ( ) ;
// the types in the abi_parser are already valid rust types so simply clone them to make it consistent with the `RawAbi` variant
internal_structs . rust_type_names . extend (
abi_parser
. function_params
. values ( )
. map ( | ty | ( ty . clone ( ) , ty . clone ( ) ) ) ,
) ;
internal_structs . function_params = abi_parser . function_params . clone ( ) ;
internal_structs . outputs = abi_parser . outputs . clone ( ) ;
internal_structs
} else {
serde_json ::from_str ::< RawAbi > ( & abi_str )
. ok ( )
. map ( InternalStructs ::new )
. unwrap_or_default ( )
} ;
2021-08-16 07:29:44 +00:00
2020-06-03 20:09:46 +00:00
let contract_name = util ::ident ( & args . contract_name ) ;
2020-05-26 18:57:59 +00:00
// NOTE: We only check for duplicate signatures here, since if there are
// duplicate aliases, the compiler will produce a warning because a
// method will be re-defined.
2021-02-19 06:34:56 +00:00
let mut method_aliases = BTreeMap ::new ( ) ;
2020-05-26 18:57:59 +00:00
for ( signature , alias ) in args . method_aliases . into_iter ( ) {
let alias = syn ::parse_str ( & alias ) ? ;
if method_aliases . insert ( signature . clone ( ) , alias ) . is_some ( ) {
return Err ( anyhow! (
" duplicate method signature '{}' in method aliases " ,
signature ,
) ) ;
}
}
2021-09-03 15:57:40 +00:00
let mut event_aliases = BTreeMap ::new ( ) ;
for ( signature , alias ) in args . event_aliases . into_iter ( ) {
let alias = syn ::parse_str ( & alias ) ? ;
event_aliases . insert ( signature , alias ) ;
}
2020-05-26 18:57:59 +00:00
let event_derives = args
. event_derives
. iter ( )
. map ( | derive | syn ::parse_str ::< Path > ( derive ) )
. collect ::< Result < Vec < _ > , _ > > ( )
. context ( " failed to parse event derives " ) ? ;
Ok ( Context {
abi ,
2020-10-29 07:48:24 +00:00
human_readable ,
2020-05-26 18:57:59 +00:00
abi_str : Literal ::string ( & abi_str ) ,
2021-03-16 19:37:19 +00:00
abi_parser ,
2021-08-16 07:29:44 +00:00
internal_structs ,
2020-05-26 18:57:59 +00:00
contract_name ,
method_aliases ,
event_derives ,
2021-09-03 15:57:40 +00:00
event_aliases ,
2020-05-26 18:57:59 +00:00
} )
}
}