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-10-10 08:31:34 +00:00
use ethers_core ::abi ::{ Abi , AbiParser } ;
2020-05-26 18:57:59 +00:00
use proc_macro2 ::{ Ident , Literal , TokenStream } ;
use quote ::quote ;
2021-10-02 17:16:55 +00:00
use serde ::Deserialize ;
2021-02-19 06:34:56 +00:00
use std ::collections ::BTreeMap ;
2021-10-10 08:31:34 +00:00
use syn ::Path ;
2020-05-26 18:57:59 +00:00
2021-10-11 14:18:09 +00:00
/// The result of `Context::expand`
#[ derive(Debug) ]
pub struct ExpandedContract {
/// The name of the contract module
pub module : Ident ,
/// The contract module's imports
pub imports : TokenStream ,
/// Contract, Middle related implementations
pub contract : TokenStream ,
/// All event impls of the contract
pub events : TokenStream ,
/// The contract's internal structs
pub abi_structs : TokenStream ,
}
impl ExpandedContract {
/// Merges everything into a single module
pub fn into_tokens ( self ) -> TokenStream {
let ExpandedContract {
module ,
imports ,
contract ,
events ,
abi_structs ,
} = self ;
quote! {
// export all the created data types
pub use #module ::* ;
#[ allow(clippy::too_many_arguments) ]
mod #module {
#imports
#contract
#events
#abi_structs
}
}
}
}
2020-05-26 18:57:59 +00:00
/// Internal shared context for generating smart contract bindings.
2021-10-11 14:18:09 +00:00
pub struct Context {
2020-05-26 18:57:59 +00:00
/// 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 {
2021-10-11 14:18:09 +00:00
/// Expands the whole rust contract
pub fn expand ( & self ) -> Result < ExpandedContract > {
let name = & self . contract_name ;
2020-05-26 18:57:59 +00:00
let name_mod = util ::ident ( & format! (
" {}_mod " ,
2021-10-11 14:18:09 +00:00
self . contract_name . to_string ( ) . to_lowercase ( )
2020-05-26 18:57:59 +00:00
) ) ;
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
2021-10-11 14:18:09 +00:00
let struct_decl = common ::struct_declaration ( self , & abi_name ) ;
2020-05-26 18:57:59 +00:00
// 2. Declare events structs & impl FromTokens for each event
2021-10-11 14:18:09 +00:00
let events_decl = self . events_declaration ( ) ? ;
2020-05-26 18:57:59 +00:00
// 3. impl block for the event functions
2021-10-11 14:18:09 +00:00
let contract_events = self . event_methods ( ) ? ;
2020-05-26 18:57:59 +00:00
// 4. impl block for the contract methods
2021-10-11 14:18:09 +00:00
let contract_methods = self . methods ( ) ? ;
2020-05-26 18:57:59 +00:00
2021-03-16 19:37:19 +00:00
// 5. Declare the structs parsed from the human readable abi
2021-10-11 14:18:09 +00:00
let abi_structs_decl = self . abi_structs ( ) ? ;
2021-03-16 19:37:19 +00:00
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 ( ) ;
2021-10-11 14:18:09 +00:00
let contract = quote! {
2020-05-26 18:57:59 +00:00
#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
}
2021-10-11 14:18:09 +00:00
} ;
2020-05-26 18:57:59 +00:00
2021-10-11 14:18:09 +00:00
Ok ( ExpandedContract {
module : name_mod ,
imports ,
contract ,
events : events_decl ,
abi_structs : abi_structs_decl ,
2020-05-26 18:57:59 +00:00
} )
}
/// Create a context from the code generation arguments.
2021-10-11 14:18:09 +00:00
pub 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 ( ) ;
2021-10-02 17:16:55 +00:00
let ( abi , human_readable ) : ( Abi , _ ) = if let Ok ( abi ) = abi_parser . parse_str ( & abi_str ) {
( abi , true )
2020-10-29 07:48:24 +00:00
} else {
2021-10-02 17:16:55 +00:00
// a best-effort coercion of an ABI or an artifact JSON into an artifact JSON.
let json_abi_str = if abi_str . trim ( ) . starts_with ( '[' ) {
format! ( r # "{{"abi":{}}}"# , abi_str . trim ( ) )
} else {
abi_str . clone ( )
} ;
#[ derive(Deserialize) ]
struct Contract {
abi : Abi ,
}
let contract = serde_json ::from_str ::< Contract > ( & json_abi_str ) ? ;
( contract . abi , false )
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
} )
}
2021-10-11 14:18:09 +00:00
/// The internal abi struct mapping table
pub fn internal_structs ( & self ) -> & InternalStructs {
& self . internal_structs
}
/// The internal mutable abi struct mapping table
pub fn internal_structs_mut ( & mut self ) -> & mut InternalStructs {
& mut self . internal_structs
}
2020-05-26 18:57:59 +00:00
}