2020-05-26 18:57:59 +00:00
//! Implementation of procedural macro for generating type-safe bindings to an
//! ethereum smart contract.
#![ deny(missing_docs, unsafe_code) ]
2021-08-06 12:47:17 +00:00
use ethers_contract_abigen ::{ ethers_contract_crate , ethers_core_crate , Source } ;
2021-03-15 11:59:52 +00:00
use proc_macro ::TokenStream ;
use proc_macro2 ::{ Literal , Span } ;
use quote ::{ quote , quote_spanned } ;
use syn ::spanned ::Spanned as _ ;
use syn ::{
2021-03-19 15:44:59 +00:00
parse ::Error , parse_macro_input , AttrStyle , Data , DeriveInput , Expr , Field , Fields ,
GenericArgument , Lit , Meta , NestedMeta , PathArguments , Type ,
2021-03-15 11:59:52 +00:00
} ;
2020-05-26 18:57:59 +00:00
2021-10-11 14:18:09 +00:00
use abigen ::Contracts ;
2021-03-16 08:12:32 +00:00
use ethers_core ::abi ::{ param_type ::Reader , AbiParser , Event , EventExt , EventParam , ParamType } ;
2021-03-15 11:59:52 +00:00
use hex ::FromHex ;
2020-05-26 18:57:59 +00:00
2021-03-15 11:59:52 +00:00
mod abigen ;
mod spanned ;
2020-05-26 18:57:59 +00:00
2021-10-11 14:18:09 +00:00
/// Proc macro to generate type-safe bindings to a contract(s). This macro accepts
/// one or more Ethereum contract ABI or a path. Note that this path is rooted in
2020-05-26 18:57:59 +00:00
/// the crate's root `CARGO_MANIFEST_DIR`.
///
2020-06-10 20:26:37 +00:00
/// # Examples
2020-05-26 18:57:59 +00:00
///
2020-06-11 09:33:09 +00:00
/// ```ignore
/// # use ethers_contract_derive::abigen;
2020-06-10 20:26:37 +00:00
/// // ABI Path
/// abigen!(MyContract, "MyContract.json");
2020-05-26 18:57:59 +00:00
///
/// // HTTP(S) source
2020-06-11 09:33:09 +00:00
/// abigen!(MyContract, "https://my.domain.local/path/to/contract.json");
2020-06-10 20:26:37 +00:00
///
2020-05-26 18:57:59 +00:00
/// // Etherscan.io
2020-06-03 21:05:05 +00:00
/// abigen!(MyContract, "etherscan:0x0001020304050607080910111213141516171819");
/// abigen!(MyContract, "https://etherscan.io/address/0x0001020304050607080910111213141516171819");
2020-06-10 20:26:37 +00:00
///
2020-05-26 18:57:59 +00:00
/// // npmjs
2020-06-11 09:33:09 +00:00
/// abigen!(MyContract, "npm:@org/package@1.0.0/path/to/contract.json");
2020-05-26 18:57:59 +00:00
/// ```
///
/// Note that Etherscan rate-limits requests to their API, to avoid this an
/// `ETHERSCAN_API_KEY` environment variable can be set. If it is, it will use
/// that API key when retrieving the contract ABI.
///
/// Currently the proc macro accepts additional parameters to configure some
/// aspects of the code generation. Specifically it accepts:
/// - `methods`: A list of mappings from method signatures to method names
/// allowing methods names to be explicitely set for contract methods. This
/// also provides a workaround for generating code for contracts with multiple
/// methods with the same name.
/// - `event_derives`: A list of additional derives that should be added to
/// contract event structs and enums.
///
2021-10-11 14:18:09 +00:00
/// # Example
///
2020-06-11 09:33:09 +00:00
/// ```ignore
2020-06-03 21:05:05 +00:00
/// abigen!(
2020-06-10 20:26:37 +00:00
/// MyContract,
/// "path/to/MyContract.json",
2020-05-26 18:57:59 +00:00
/// methods {
/// myMethod(uint256,bool) as my_renamed_method;
/// },
/// event_derives (serde::Deserialize, serde::Serialize),
/// );
/// ```
2021-10-11 14:18:09 +00:00
///
/// `abigen!` supports multiple abigen definitions separated by a semicolon `;`
/// This is useful if the contracts use ABIEncoderV2 structs. In which case `abigen!` bundles all type duplicates so that all rust contracts also use the same rust types.
///
/// # Example Multiple contracts
/// ```ignore
/// abigen!(
/// MyContract,
/// "path/to/MyContract.json",
/// methods {
/// myMethod(uint256,bool) as my_renamed_method;
/// },
/// event_derives (serde::Deserialize, serde::Serialize);
///
/// MyOtherContract,
/// "path/to/MyOtherContract.json",
/// event_derives (serde::Deserialize, serde::Serialize);
/// );
/// ```
2020-05-26 18:57:59 +00:00
#[ proc_macro ]
pub fn abigen ( input : TokenStream ) -> TokenStream {
2021-10-11 14:18:09 +00:00
let contracts = parse_macro_input! ( input as Contracts ) ;
2020-05-26 18:57:59 +00:00
2021-10-11 14:18:09 +00:00
contracts
. expand ( )
. unwrap_or_else ( | err | err . to_compile_error ( ) )
2020-05-26 18:57:59 +00:00
. into ( )
}
2021-03-15 11:59:52 +00:00
/// Derives the `EthEvent` and `Tokenizeable` trait for the labeled type.
///
/// Additional arguments can be specified using the `#[ethevent(...)]` attribute:
///
2021-03-19 15:44:59 +00:00
/// For the struct:
///
2021-03-16 08:12:32 +00:00
/// - `name`, `name = "..."`: Overrides the generated `EthEvent` name, default is the
/// struct's name.
/// - `signature`, `signature = "..."`: The signature as hex string to override the
/// event's signature.
2021-03-15 11:59:52 +00:00
/// - `abi`, `abi = "..."`: The ABI signature for the event this event's data corresponds to.
2021-03-16 08:12:32 +00:00
/// The `abi` should be solidity event definition or a tuple of the event's types in case the
/// event has non elementary (other `EthAbiType`) types as members
2021-03-19 15:44:59 +00:00
/// - `anonymous`: A flag to mark this as an anonymous event
///
/// For fields:
///
/// - `indexed`: flag to mark a field as an indexed event input
/// - `name`: override the name of an indexed event input, default is the rust field name
2021-03-16 08:12:32 +00:00
///
/// # Example
/// ```ignore
2021-03-19 15:44:59 +00:00
/// # use ethers_core::types::Address;
///
2021-03-16 08:12:32 +00:00
/// #[derive(Debug, EthAbiType)]
/// struct Inner {
/// inner: Address,
/// msg: String,
/// }
///
/// #[derive(Debug, EthEvent)]
/// #[ethevent(abi = "ValueChangedEvent((address,string),string)")]
/// struct ValueChangedEvent {
2021-03-19 15:44:59 +00:00
/// #[ethevent(indexed, name = "_target")]
/// target: Address,
2021-03-16 08:12:32 +00:00
/// msg: String,
2021-03-19 15:44:59 +00:00
/// inner: Inner,
2021-03-16 08:12:32 +00:00
/// }
/// ```
2021-03-15 11:59:52 +00:00
#[ proc_macro_derive(EthEvent, attributes(ethevent)) ]
pub fn derive_abi_event ( input : TokenStream ) -> TokenStream {
let input = parse_macro_input! ( input as DeriveInput ) ;
2021-08-06 12:47:17 +00:00
// the ethers crates to use
let core_crate = ethers_core_crate ( ) ;
let contract_crate = ethers_contract_crate ( ) ;
2021-03-15 11:59:52 +00:00
let name = & input . ident ;
let attributes = match parse_attributes ( & input ) {
Ok ( attributes ) = > attributes ,
Err ( errors ) = > return TokenStream ::from ( errors ) ,
} ;
let event_name = attributes
. name
2021-03-19 15:44:59 +00:00
. map ( | ( s , _ ) | s )
2021-03-15 11:59:52 +00:00
. unwrap_or_else ( | | input . ident . to_string ( ) ) ;
2021-03-19 15:44:59 +00:00
let mut event = if let Some ( ( src , span ) ) = attributes . abi {
2021-03-16 08:12:32 +00:00
// try to parse as solidity event
2021-03-19 15:44:59 +00:00
if let Ok ( event ) = parse_event ( & src ) {
event
2021-03-15 11:59:52 +00:00
} else {
2021-03-16 08:12:32 +00:00
// try as tuple
if let Some ( inputs ) = Reader ::read (
src . trim_start_matches ( " event " )
. trim_start ( )
. trim_start_matches ( & event_name ) ,
)
. ok ( )
. and_then ( | param | match param {
ParamType ::Tuple ( params ) = > Some (
params
. into_iter ( )
. map ( | kind | EventParam {
name : " " . to_string ( ) ,
indexed : false ,
kind ,
} )
. collect ( ) ,
) ,
_ = > None ,
} ) {
2021-03-19 15:44:59 +00:00
Event {
2021-03-16 08:12:32 +00:00
name : event_name . clone ( ) ,
inputs ,
anonymous : false ,
2021-03-19 15:44:59 +00:00
}
2021-03-16 08:12:32 +00:00
} else {
match src . parse ::< Source > ( ) . and_then ( | s | s . get ( ) ) {
Ok ( abi ) = > {
// try to derive the signature from the abi from the parsed abi
// TODO(mattsse): this will fail for events that contain other non elementary types in their abi
// because the parser doesn't know how to substitute the types
// this could be mitigated by getting the ABI of each non elementary type at runtime
// and computing the the signature as `static Lazy::...`
match parse_event ( & abi ) {
2021-03-19 15:44:59 +00:00
Ok ( event ) = > event ,
2021-03-16 08:12:32 +00:00
Err ( err ) = > {
return TokenStream ::from ( Error ::new ( span , err ) . to_compile_error ( ) )
}
2021-03-15 11:59:52 +00:00
}
}
2021-03-16 08:12:32 +00:00
Err ( err ) = > return TokenStream ::from ( Error ::new ( span , err ) . to_compile_error ( ) ) ,
2021-03-15 11:59:52 +00:00
}
}
}
} else {
// try to determine the abi from the fields
match derive_abi_event_from_fields ( & input ) {
2021-03-19 15:44:59 +00:00
Ok ( event ) = > event ,
2021-03-15 11:59:52 +00:00
Err ( err ) = > return TokenStream ::from ( err . to_compile_error ( ) ) ,
}
} ;
2021-03-19 15:44:59 +00:00
event . name = event_name . clone ( ) ;
if let Some ( ( anon , _ ) ) = attributes . anonymous . as_ref ( ) {
event . anonymous = * anon ;
}
let decode_log_impl = match derive_decode_from_log_impl ( & input , & event ) {
Ok ( log ) = > log ,
Err ( err ) = > return TokenStream ::from ( err . to_compile_error ( ) ) ,
} ;
let ( abi , hash ) = ( event . abi_signature ( ) , event . signature ( ) ) ;
2021-03-15 11:59:52 +00:00
let signature = if let Some ( ( hash , _ ) ) = attributes . signature_hash {
signature ( & hash )
} else {
signature ( hash . as_bytes ( ) )
} ;
2021-03-19 15:44:59 +00:00
let anon = attributes . anonymous . map ( | ( b , _ ) | b ) . unwrap_or_default ( ) ;
2021-03-15 11:59:52 +00:00
let ethevent_impl = quote! {
2021-08-06 12:47:17 +00:00
impl #contract_crate ::EthEvent for #name {
2021-03-15 11:59:52 +00:00
2021-03-16 19:37:19 +00:00
fn name ( ) -> ::std ::borrow ::Cow < 'static , str > {
2021-03-15 11:59:52 +00:00
#event_name . into ( )
}
2021-08-06 12:47:17 +00:00
fn signature ( ) -> #core_crate ::types ::H256 {
2021-03-15 11:59:52 +00:00
#signature
}
fn abi_signature ( ) -> ::std ::borrow ::Cow < 'static , str > {
#abi . into ( )
}
2021-03-19 15:44:59 +00:00
2021-08-06 12:47:17 +00:00
fn decode_log ( log : & #core_crate ::abi ::RawLog ) -> Result < Self , #core_crate ::abi ::Error > where Self : Sized {
2021-03-19 15:44:59 +00:00
#decode_log_impl
}
fn is_anonymous ( ) -> bool {
#anon
}
2021-03-15 11:59:52 +00:00
}
} ;
let tokenize_impl = derive_tokenizeable_impl ( & input ) ;
// parse attributes abi into source
TokenStream ::from ( quote! {
#tokenize_impl
#ethevent_impl
} )
}
2021-03-19 15:44:59 +00:00
struct EventField {
topic_name : Option < String > ,
index : usize ,
param : EventParam ,
}
impl EventField {
fn is_indexed ( & self ) -> bool {
self . topic_name . is_some ( )
}
}
// 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
fn topic_param_type_quote ( kind : & ParamType ) -> proc_macro2 ::TokenStream {
2021-08-06 12:47:17 +00:00
let core_crate = ethers_core_crate ( ) ;
2021-03-19 15:44:59 +00:00
match kind {
ParamType ::String
| ParamType ::Bytes
| ParamType ::Array ( _ )
| ParamType ::FixedArray ( _ , _ )
2021-08-06 12:47:17 +00:00
| ParamType ::Tuple ( _ ) = > quote! { #core_crate ::abi ::ParamType ::FixedBytes ( 32 ) } ,
2021-03-19 15:44:59 +00:00
ty = > param_type_quote ( ty ) ,
}
}
fn param_type_quote ( kind : & ParamType ) -> proc_macro2 ::TokenStream {
2021-08-06 12:47:17 +00:00
let core_crate = ethers_core_crate ( ) ;
2021-03-19 15:44:59 +00:00
match kind {
ParamType ::Address = > {
2021-08-06 12:47:17 +00:00
quote! { #core_crate ::abi ::ParamType ::Address }
2021-03-19 15:44:59 +00:00
}
ParamType ::Bytes = > {
2021-08-06 12:47:17 +00:00
quote! { #core_crate ::abi ::ParamType ::Bytes }
2021-03-19 15:44:59 +00:00
}
ParamType ::Int ( size ) = > {
let size = Literal ::usize_suffixed ( * size ) ;
2021-08-06 12:47:17 +00:00
quote! { #core_crate ::abi ::ParamType ::Int ( #size ) }
2021-03-19 15:44:59 +00:00
}
ParamType ::Uint ( size ) = > {
let size = Literal ::usize_suffixed ( * size ) ;
2021-08-06 12:47:17 +00:00
quote! { #core_crate ::abi ::ParamType ::Uint ( #size ) }
2021-03-19 15:44:59 +00:00
}
ParamType ::Bool = > {
2021-08-06 12:47:17 +00:00
quote! { #core_crate ::abi ::ParamType ::Bool }
2021-03-19 15:44:59 +00:00
}
ParamType ::String = > {
2021-08-06 12:47:17 +00:00
quote! { #core_crate ::abi ::ParamType ::String }
2021-03-19 15:44:59 +00:00
}
ParamType ::Array ( ty ) = > {
let ty = param_type_quote ( & * ty ) ;
2021-08-06 12:47:17 +00:00
quote! { #core_crate ::abi ::ParamType ::Array ( Box ::new ( #ty ) ) }
2021-03-19 15:44:59 +00:00
}
ParamType ::FixedBytes ( size ) = > {
let size = Literal ::usize_suffixed ( * size ) ;
2021-08-06 12:47:17 +00:00
quote! { #core_crate ::abi ::ParamType ::FixedBytes ( #size ) }
2021-03-19 15:44:59 +00:00
}
ParamType ::FixedArray ( ty , size ) = > {
let ty = param_type_quote ( & * ty ) ;
let size = Literal ::usize_suffixed ( * size ) ;
2021-08-06 12:47:17 +00:00
quote! { #core_crate ::abi ::ParamType ::FixedArray ( Box ::new ( #ty ) , #size ) }
2021-03-19 15:44:59 +00:00
}
ParamType ::Tuple ( tuple ) = > {
let elements = tuple . iter ( ) . map ( param_type_quote ) ;
quote! {
2021-08-06 12:47:17 +00:00
#core_crate ::abi ::ParamType ::Tuple (
2021-07-24 18:53:40 +00:00
::std ::vec! [
2021-03-19 15:44:59 +00:00
#( #elements ) , *
]
)
}
}
}
}
fn derive_decode_from_log_impl (
input : & DeriveInput ,
event : & Event ,
) -> Result < proc_macro2 ::TokenStream , Error > {
2021-08-06 12:47:17 +00:00
let core_crate = ethers_core_crate ( ) ;
2021-03-19 15:44:59 +00:00
let fields : Vec < _ > = match input . data {
Data ::Struct ( ref data ) = > match data . fields {
Fields ::Named ( ref fields ) = > {
if fields . named . len ( ) ! = event . inputs . len ( ) {
return Err ( Error ::new (
fields . span ( ) ,
format! (
" EthEvent {}'s fields length don't match with signature inputs {} " ,
event . name ,
event . abi_signature ( )
) ,
) ) ;
}
fields . named . iter ( ) . collect ( )
}
Fields ::Unnamed ( ref fields ) = > {
if fields . unnamed . len ( ) ! = event . inputs . len ( ) {
return Err ( Error ::new (
fields . span ( ) ,
format! (
" EthEvent {}'s fields length don't match with signature inputs {} " ,
event . name ,
event . abi_signature ( )
) ,
) ) ;
}
fields . unnamed . iter ( ) . collect ( )
}
Fields ::Unit = > {
return Err ( Error ::new (
input . span ( ) ,
" EthEvent cannot be derived for empty structs and unit " ,
) ) ;
}
} ,
Data ::Enum ( _ ) = > {
return Err ( Error ::new (
input . span ( ) ,
" EthEvent cannot be derived for enums " ,
) ) ;
}
Data ::Union ( _ ) = > {
return Err ( Error ::new (
input . span ( ) ,
" EthEvent cannot be derived for unions " ,
) ) ;
}
} ;
let mut event_fields = Vec ::with_capacity ( fields . len ( ) ) ;
for ( index , field ) in fields . iter ( ) . enumerate ( ) {
let mut param = event . inputs [ index ] . clone ( ) ;
let ( topic_name , indexed ) = parse_field_attributes ( field ) ? ;
if indexed {
param . indexed = true ;
}
let topic_name = if param . indexed {
if topic_name . is_none ( ) {
Some ( param . name . clone ( ) )
} else {
topic_name
}
} else {
None
} ;
event_fields . push ( EventField {
topic_name ,
index ,
param ,
} ) ;
}
// convert fields to params list
let topic_types = event_fields
. iter ( )
. filter ( | f | f . is_indexed ( ) )
. map ( | f | topic_param_type_quote ( & f . param . kind ) ) ;
2021-07-24 18:53:40 +00:00
let topic_types_init = quote! { let topic_types = ::std ::vec! [ #( #topic_types ) , * ] ; } ;
2021-03-19 15:44:59 +00:00
let data_types = event_fields
. iter ( )
. filter ( | f | ! f . is_indexed ( ) )
. map ( | f | param_type_quote ( & f . param . kind ) ) ;
2021-09-02 16:16:39 +00:00
let data_types_init = quote! { let data_types = [ #( #data_types ) , * ] ; } ;
2021-03-19 15:44:59 +00:00
// decode
let ( signature_check , flat_topics_init , topic_tokens_len_check ) = if event . anonymous {
(
quote! { } ,
quote! {
let flat_topics = topics . iter ( ) . flat_map ( | t | t . as_ref ( ) . to_vec ( ) ) . collect ::< Vec < u8 > > ( ) ;
} ,
quote! {
if topic_tokens . len ( ) ! = topics . len ( ) {
2021-08-06 12:47:17 +00:00
return Err ( #core_crate ::abi ::Error ::InvalidData ) ;
2021-03-19 15:44:59 +00:00
}
} ,
)
} else {
(
quote! {
2021-08-06 12:47:17 +00:00
let event_signature = topics . get ( 0 ) . ok_or ( #core_crate ::abi ::Error ::InvalidData ) ? ;
2021-03-19 15:44:59 +00:00
if event_signature ! = & Self ::signature ( ) {
2021-08-06 12:47:17 +00:00
return Err ( #core_crate ::abi ::Error ::InvalidData ) ;
2021-03-19 15:44:59 +00:00
}
} ,
quote! {
let flat_topics = topics . iter ( ) . skip ( 1 ) . flat_map ( | t | t . as_ref ( ) . to_vec ( ) ) . collect ::< Vec < u8 > > ( ) ;
} ,
quote! {
2021-05-24 08:52:21 +00:00
if topic_tokens . len ( ) ! = topics . len ( ) - 1 {
2021-08-06 12:47:17 +00:00
return Err ( #core_crate ::abi ::Error ::InvalidData ) ;
2021-03-19 15:44:59 +00:00
}
} ,
)
} ;
// check if indexed are sorted
let tokens_init = if event_fields
. iter ( )
. filter ( | f | f . is_indexed ( ) )
. enumerate ( )
. all ( | ( idx , f ) | f . index = = idx )
{
quote! {
2021-08-06 12:47:17 +00:00
let topic_tokens = #core_crate ::abi ::decode ( & topic_types , & flat_topics ) ? ;
2021-03-19 15:44:59 +00:00
#topic_tokens_len_check
2021-08-06 12:47:17 +00:00
let data_tokens = #core_crate ::abi ::decode ( & data_types , data ) ? ;
2021-03-19 15:44:59 +00:00
let tokens :Vec < _ > = topic_tokens . into_iter ( ) . chain ( data_tokens . into_iter ( ) ) . collect ( ) ;
}
} else {
let swap_tokens = event_fields . iter ( ) . map ( | field | {
if field . is_indexed ( ) {
quote! { topic_tokens . remove ( 0 ) }
} else {
quote! { data_tokens . remove ( 0 ) }
}
} ) ;
quote! {
2021-08-06 12:47:17 +00:00
let mut topic_tokens = #core_crate ::abi ::decode ( & topic_types , & flat_topics ) ? ;
2021-03-19 15:44:59 +00:00
#topic_tokens_len_check
2021-08-06 12:47:17 +00:00
let mut data_tokens = #core_crate ::abi ::decode ( & data_types , & data ) ? ;
2021-03-19 15:44:59 +00:00
let mut tokens = Vec ::with_capacity ( topics . len ( ) + data_tokens . len ( ) ) ;
#( tokens . push ( #swap_tokens ) ; ) *
}
} ;
Ok ( quote! {
2021-08-06 12:47:17 +00:00
let #core_crate ::abi ::RawLog { data , topics } = log ;
2021-03-19 15:44:59 +00:00
#signature_check
#topic_types_init
#data_types_init
#flat_topics_init
#tokens_init
2021-09-02 16:16:39 +00:00
#core_crate ::abi ::Tokenizable ::from_token ( #core_crate ::abi ::Token ::Tuple ( tokens ) ) . map_err ( | _ | #core_crate ::abi ::Error ::InvalidData )
2021-03-19 15:44:59 +00:00
} )
}
2021-03-15 11:59:52 +00:00
fn derive_abi_event_from_fields ( input : & DeriveInput ) -> Result < Event , Error > {
2021-03-19 15:44:59 +00:00
let fields : Vec < _ > = match input . data {
2021-03-15 11:59:52 +00:00
Data ::Struct ( ref data ) = > match data . fields {
2021-03-19 15:44:59 +00:00
Fields ::Named ( ref fields ) = > fields . named . iter ( ) . collect ( ) ,
Fields ::Unnamed ( ref fields ) = > fields . unnamed . iter ( ) . collect ( ) ,
2021-03-15 11:59:52 +00:00
Fields ::Unit = > {
return Err ( Error ::new (
input . span ( ) ,
" EthEvent cannot be derived for empty structs and unit " ,
) )
}
} ,
Data ::Enum ( _ ) = > {
return Err ( Error ::new (
input . span ( ) ,
" EthEvent cannot be derived for enums " ,
) ) ;
}
Data ::Union ( _ ) = > {
return Err ( Error ::new (
input . span ( ) ,
" EthEvent cannot be derived for unions " ,
) ) ;
}
} ;
2021-03-19 15:44:59 +00:00
let inputs = fields
2021-03-15 11:59:52 +00:00
. iter ( )
2021-03-19 15:44:59 +00:00
. map ( | f | {
let name = f
. ident
. as_ref ( )
. map ( | name | name . to_string ( ) )
. unwrap_or_else ( | | " " . to_string ( ) ) ;
find_parameter_type ( & f . ty ) . map ( | ty | ( name , ty ) )
} )
2021-03-15 11:59:52 +00:00
. collect ::< Result < Vec < _ > , _ > > ( ) ? ;
let event = Event {
name : " " . to_string ( ) ,
inputs : inputs
. into_iter ( )
2021-03-19 15:44:59 +00:00
. map ( | ( name , kind ) | EventParam {
name ,
2021-03-15 11:59:52 +00:00
kind ,
indexed : false ,
} )
. collect ( ) ,
anonymous : false ,
} ;
Ok ( event )
}
2021-03-19 15:44:59 +00:00
fn parse_field_attributes ( field : & Field ) -> Result < ( Option < String > , bool ) , Error > {
let mut indexed = false ;
let mut topic_name = None ;
for a in field . attrs . iter ( ) {
if let AttrStyle ::Outer = a . style {
if let Ok ( Meta ::List ( meta ) ) = a . parse_meta ( ) {
if meta . path . is_ident ( " ethevent " ) {
for n in meta . nested . iter ( ) {
if let NestedMeta ::Meta ( meta ) = n {
match meta {
Meta ::Path ( path ) = > {
if path . is_ident ( " indexed " ) {
indexed = true ;
} else {
return Err ( Error ::new (
path . span ( ) ,
" unrecognized ethevent parameter " ,
) ) ;
}
}
Meta ::List ( meta ) = > {
return Err ( Error ::new (
meta . path . span ( ) ,
" unrecognized ethevent parameter " ,
) ) ;
}
Meta ::NameValue ( meta ) = > {
if meta . path . is_ident ( " name " ) {
if let Lit ::Str ( ref lit_str ) = meta . lit {
topic_name = Some ( lit_str . value ( ) ) ;
} else {
return Err ( Error ::new (
meta . span ( ) ,
" name attribute must be a string " ,
) ) ;
}
}
}
}
}
}
}
}
}
}
Ok ( ( topic_name , indexed ) )
}
2021-03-15 11:59:52 +00:00
fn find_parameter_type ( ty : & Type ) -> Result < ParamType , Error > {
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 ::< usize > ( ) {
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 ( | t | find_parameter_type ( t ) )
. collect ::< Result < Vec < _ > , _ > > ( ) ? ;
Ok ( ParamType ::Tuple ( params ) )
}
2021-03-19 15:44:59 +00:00
_ = > Err ( Error ::new (
ty . span ( ) ,
" Failed to derive proper ABI from fields " ,
) ) ,
2021-03-15 11:59:52 +00:00
}
}
fn parse_int_param_type ( s : & str ) -> Option < ParamType > {
let size = s
. chars ( )
. skip ( 1 )
. collect ::< String > ( )
. parse ::< usize > ( )
. ok ( ) ? ;
if s . starts_with ( 'u' ) {
Some ( ParamType ::Uint ( size ) )
} else if s . starts_with ( 'i' ) {
Some ( ParamType ::Int ( size ) )
} else {
None
}
}
fn signature ( hash : & [ u8 ] ) -> proc_macro2 ::TokenStream {
2021-08-06 12:47:17 +00:00
let core_crate = ethers_core_crate ( ) ;
2021-03-15 11:59:52 +00:00
let bytes = hash . iter ( ) . copied ( ) . map ( Literal ::u8_unsuffixed ) ;
2021-08-06 12:47:17 +00:00
quote! { #core_crate ::types ::H256 ( [ #( #bytes ) , * ] ) }
2021-03-15 11:59:52 +00:00
}
fn parse_event ( abi : & str ) -> Result < Event , String > {
let abi = if ! abi . trim_start ( ) . starts_with ( " event " ) {
format! ( " event {} " , abi )
} else {
abi . to_string ( )
} ;
AbiParser ::default ( )
. parse_event ( & abi )
. map_err ( | err | format! ( " Failed to parse the event ABI: {:?} " , err ) )
}
/// Derives the `Tokenizable` trait for the labeled type.
///
/// This derive macro automatically adds a type bound `field: Tokenizable` for each
/// field type.
#[ proc_macro_derive(EthAbiType) ]
pub fn derive_abi_type ( input : TokenStream ) -> TokenStream {
let input = parse_macro_input! ( input as DeriveInput ) ;
TokenStream ::from ( derive_tokenizeable_impl ( & input ) )
}
fn derive_tokenizeable_impl ( input : & DeriveInput ) -> proc_macro2 ::TokenStream {
2021-08-06 12:47:17 +00:00
let core_crate = ethers_core_crate ( ) ;
2021-03-15 11:59:52 +00:00
let name = & input . ident ;
let generic_params = input . generics . params . iter ( ) . map ( | p | quote! { #p } ) ;
let generic_params = quote! { #( #generic_params , ) * } ;
let generic_args = input . generics . type_params ( ) . map ( | p | {
let name = & p . ident ;
quote_spanned! { p . ident . span ( ) = > #name }
} ) ;
let generic_args = quote! { #( #generic_args , ) * } ;
let generic_predicates = match input . generics . where_clause {
Some ( ref clause ) = > {
let predicates = clause . predicates . iter ( ) . map ( | p | quote! { #p } ) ;
quote! { #( #predicates , ) * }
}
None = > quote! { } ,
} ;
let ( tokenize_predicates , params_len , init_struct_impl , into_token_impl ) = match input . data {
Data ::Struct ( ref data ) = > match data . fields {
Fields ::Named ( ref fields ) = > {
let tokenize_predicates = fields . named . iter ( ) . map ( | f | {
let ty = & f . ty ;
2021-08-06 12:47:17 +00:00
quote_spanned! { f . span ( ) = > #ty : #core_crate ::abi ::Tokenize }
2021-03-15 11:59:52 +00:00
} ) ;
let tokenize_predicates = quote! { #( #tokenize_predicates , ) * } ;
let assignments = fields . named . iter ( ) . map ( | f | {
let name = f . ident . as_ref ( ) . expect ( " Named fields have names " ) ;
2021-08-06 12:47:17 +00:00
quote_spanned! { f . span ( ) = > #name : #core_crate ::abi ::Tokenizable ::from_token ( iter . next ( ) . expect ( " tokens size is sufficient qed " ) . into_token ( ) ) ? }
2021-03-15 11:59:52 +00:00
} ) ;
let init_struct_impl = quote! { Self { #( #assignments , ) * } } ;
let into_token = fields . named . iter ( ) . map ( | f | {
let name = f . ident . as_ref ( ) . expect ( " Named fields have names " ) ;
quote_spanned! { f . span ( ) = > self . #name . into_token ( ) }
} ) ;
let into_token_impl = quote! { #( #into_token , ) * } ;
(
tokenize_predicates ,
fields . named . len ( ) ,
init_struct_impl ,
into_token_impl ,
)
}
Fields ::Unnamed ( ref fields ) = > {
let tokenize_predicates = fields . unnamed . iter ( ) . map ( | f | {
let ty = & f . ty ;
2021-08-06 12:47:17 +00:00
quote_spanned! { f . span ( ) = > #ty : #core_crate ::abi ::Tokenize }
2021-03-15 11:59:52 +00:00
} ) ;
let tokenize_predicates = quote! { #( #tokenize_predicates , ) * } ;
let assignments = fields . unnamed . iter ( ) . map ( | f | {
2021-08-06 12:47:17 +00:00
quote_spanned! { f . span ( ) = > #core_crate ::abi ::Tokenizable ::from_token ( iter . next ( ) . expect ( " tokens size is sufficient qed " ) . into_token ( ) ) ? }
2021-03-15 11:59:52 +00:00
} ) ;
let init_struct_impl = quote! { Self ( #( #assignments , ) * ) } ;
let into_token = fields . unnamed . iter ( ) . enumerate ( ) . map ( | ( i , f ) | {
let idx = syn ::Index ::from ( i ) ;
quote_spanned! { f . span ( ) = > self . #idx . into_token ( ) }
} ) ;
let into_token_impl = quote! { #( #into_token , ) * } ;
(
tokenize_predicates ,
fields . unnamed . len ( ) ,
init_struct_impl ,
into_token_impl ,
)
}
Fields ::Unit = > {
return Error ::new (
input . span ( ) ,
" EthAbiType cannot be derived for empty structs and unit " ,
)
. to_compile_error ( ) ;
}
} ,
Data ::Enum ( _ ) = > {
return Error ::new ( input . span ( ) , " EthAbiType cannot be derived for enums " )
. to_compile_error ( ) ;
}
Data ::Union ( _ ) = > {
return Error ::new ( input . span ( ) , " EthAbiType cannot be derived for unions " )
. to_compile_error ( ) ;
}
} ;
2021-05-28 07:44:42 +00:00
// there might be the case that the event has only 1 params, which is not a tuple
let ( from_token_impl , into_token_impl ) = match params_len {
0 = > (
quote! {
Ok ( #init_struct_impl )
} ,
// can't encode an empty struct
// TODO: panic instead?
quote! {
2021-08-06 12:47:17 +00:00
#core_crate ::abi ::Token ::Tuple ( Vec ::new ( ) )
2021-05-28 07:44:42 +00:00
} ,
) ,
_ = > {
let from_token = quote! {
2021-08-06 12:47:17 +00:00
if let #core_crate ::abi ::Token ::Tuple ( tokens ) = token {
2021-03-15 11:59:52 +00:00
if tokens . len ( ) ! = #params_len {
2021-08-06 12:47:17 +00:00
return Err ( #core_crate ::abi ::InvalidOutputType ( ::std ::format! (
2021-03-15 11:59:52 +00:00
" Expected {} tokens, got {}: {:?} " ,
#params_len ,
tokens . len ( ) ,
tokens
) ) ) ;
}
let mut iter = tokens . into_iter ( ) ;
Ok ( #init_struct_impl )
} else {
2021-08-06 12:47:17 +00:00
Err ( #core_crate ::abi ::InvalidOutputType ( ::std ::format! (
2021-03-15 11:59:52 +00:00
" Expected Tuple, got {:?} " ,
token
) ) )
}
2021-05-28 07:44:42 +00:00
} ;
2021-03-15 11:59:52 +00:00
2021-05-28 07:44:42 +00:00
let into_token = quote! {
2021-08-06 12:47:17 +00:00
#core_crate ::abi ::Token ::Tuple (
2021-07-24 18:53:40 +00:00
::std ::vec! [
2021-03-15 11:59:52 +00:00
#into_token_impl
]
)
2021-05-28 07:44:42 +00:00
} ;
( from_token , into_token )
}
} ;
quote! {
2021-08-06 12:47:17 +00:00
impl < #generic_params > #core_crate ::abi ::Tokenizable for #name < #generic_args >
2021-05-28 07:44:42 +00:00
where
#generic_predicates
#tokenize_predicates
{
2021-08-06 12:47:17 +00:00
fn from_token ( token : #core_crate ::abi ::Token ) -> Result < Self , #core_crate ::abi ::InvalidOutputType > where
2021-05-28 07:44:42 +00:00
Self : Sized {
#from_token_impl
}
2021-08-06 12:47:17 +00:00
fn into_token ( self ) -> #core_crate ::abi ::Token {
2021-05-28 07:44:42 +00:00
#into_token_impl
2021-03-15 11:59:52 +00:00
}
}
2021-03-19 15:44:59 +00:00
2021-08-06 12:47:17 +00:00
impl < #generic_params > #core_crate ::abi ::TokenizableItem for #name < #generic_args >
2021-03-19 15:44:59 +00:00
where
#generic_predicates
#tokenize_predicates
{ }
2021-03-15 11:59:52 +00:00
}
}
2021-03-19 15:44:59 +00:00
#[ derive(Default) ]
2021-03-15 11:59:52 +00:00
struct Attributes {
name : Option < ( String , Span ) > ,
abi : Option < ( String , Span ) > ,
signature_hash : Option < ( Vec < u8 > , Span ) > ,
2021-03-19 15:44:59 +00:00
anonymous : Option < ( bool , Span ) > ,
2021-03-15 11:59:52 +00:00
}
fn parse_attributes ( input : & DeriveInput ) -> Result < Attributes , proc_macro2 ::TokenStream > {
let mut result = Attributes ::default ( ) ;
for a in input . attrs . iter ( ) {
if let AttrStyle ::Outer = a . style {
if let Ok ( Meta ::List ( meta ) ) = a . parse_meta ( ) {
if meta . path . is_ident ( " ethevent " ) {
for n in meta . nested . iter ( ) {
if let NestedMeta ::Meta ( meta ) = n {
match meta {
Meta ::Path ( path ) = > {
2021-03-19 15:44:59 +00:00
if let Some ( name ) = path . get_ident ( ) {
if & * name . to_string ( ) = = " anonymous " {
if result . anonymous . is_none ( ) {
result . anonymous = Some ( ( true , name . span ( ) ) ) ;
continue ;
} else {
return Err ( Error ::new (
name . span ( ) ,
" anonymous already specified " ,
)
. to_compile_error ( ) ) ;
}
}
}
2021-03-15 11:59:52 +00:00
return Err ( Error ::new (
path . span ( ) ,
" unrecognized ethevent parameter " ,
)
. to_compile_error ( ) ) ;
}
Meta ::List ( meta ) = > {
return Err ( Error ::new (
meta . path . span ( ) ,
" unrecognized ethevent parameter " ,
)
. to_compile_error ( ) ) ;
}
Meta ::NameValue ( meta ) = > {
2021-03-19 15:44:59 +00:00
if meta . path . is_ident ( " anonymous " ) {
if let Lit ::Bool ( ref bool_lit ) = meta . lit {
if result . anonymous . is_none ( ) {
result . anonymous =
Some ( ( bool_lit . value , bool_lit . span ( ) ) ) ;
} else {
return Err ( Error ::new (
meta . span ( ) ,
" anonymous already specified " ,
)
. to_compile_error ( ) ) ;
}
} else {
return Err ( Error ::new (
meta . span ( ) ,
" name must be a string " ,
)
. to_compile_error ( ) ) ;
}
} else if meta . path . is_ident ( " name " ) {
2021-03-15 11:59:52 +00:00
if let Lit ::Str ( ref lit_str ) = meta . lit {
if result . name . is_none ( ) {
result . name =
Some ( ( lit_str . value ( ) , lit_str . span ( ) ) ) ;
} else {
return Err ( Error ::new (
meta . span ( ) ,
" name already specified " ,
)
. to_compile_error ( ) ) ;
}
} else {
return Err ( Error ::new (
meta . span ( ) ,
" name must be a string " ,
)
. to_compile_error ( ) ) ;
}
} else if meta . path . is_ident ( " abi " ) {
if let Lit ::Str ( ref lit_str ) = meta . lit {
if result . abi . is_none ( ) {
result . abi =
Some ( ( lit_str . value ( ) , lit_str . span ( ) ) ) ;
} else {
return Err ( Error ::new (
meta . span ( ) ,
" abi already specified " ,
)
. to_compile_error ( ) ) ;
}
} else {
return Err ( Error ::new (
meta . span ( ) ,
" abi must be a string " ,
)
. to_compile_error ( ) ) ;
}
} else if meta . path . is_ident ( " signature " ) {
if let Lit ::Str ( ref lit_str ) = meta . lit {
if result . signature_hash . is_none ( ) {
match Vec ::from_hex ( lit_str . value ( ) ) {
Ok ( sig ) = > {
result . signature_hash =
Some ( ( sig , lit_str . span ( ) ) )
}
Err ( err ) = > {
return Err ( Error ::new (
meta . span ( ) ,
format! (
" Expected hex signature: {:?} " ,
err
) ,
)
. to_compile_error ( ) ) ;
}
}
} else {
return Err ( Error ::new (
meta . span ( ) ,
" signature already specified " ,
)
. to_compile_error ( ) ) ;
}
} else {
return Err ( Error ::new (
meta . span ( ) ,
" signature must be a hex string " ,
)
. to_compile_error ( ) ) ;
}
} else {
return Err ( Error ::new (
meta . span ( ) ,
" unrecognized ethevent parameter " ,
)
. to_compile_error ( ) ) ;
}
}
}
}
}
}
}
}
}
Ok ( result )
}