1234567891011121314151617use inflector::cases;18use syn::{Data, DeriveInput, Field, Fields, Ident, Variant, spanned::Spanned};19use quote::quote;2021use crate::{parse_ident_from_path, parse_ident_from_type, snake_ident_to_screaming};2223struct EventField {24 name: Ident,25 camel_name: String,26 ty: Ident,27 indexed: bool,28}2930impl EventField {31 fn try_from(field: &Field) -> syn::Result<Self> {32 let name = field.ident.as_ref().unwrap();33 let ty = parse_ident_from_type(&field.ty, false)?;34 let mut indexed = false;35 for attr in &field.attrs {36 if let Ok(ident) = parse_ident_from_path(&attr.path, false) {37 if ident == "indexed" {38 indexed = true;39 }40 }41 }42 Ok(Self {43 name: name.to_owned(),44 camel_name: cases::camelcase::to_camel_case(&name.to_string()),45 ty: ty.to_owned(),46 indexed,47 })48 }49 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {50 let camel_name = &self.camel_name;51 let ty = &self.ty;52 let indexed = self.indexed;53 quote! {54 <SolidityEventArgument<#ty>>::new(#indexed, #camel_name)55 }56 }57}5859struct Event {60 name: Ident,61 name_screaming: Ident,62 fields: Vec<EventField>,63 selector: proc_macro2::TokenStream,64}6566impl Event {67 fn try_from(variant: &Variant) -> syn::Result<Self> {68 let name = &variant.ident;69 let name_lit = proc_macro2::Literal::string(name.to_string().as_str());70 let name_screaming = snake_ident_to_screaming(name);7172 let named = match &variant.fields {73 Fields::Named(named) => named,74 _ => {75 return Err(syn::Error::new(76 variant.fields.span(),77 "expected named fields",78 ))79 }80 };81 let mut fields = Vec::new();82 for field in &named.named {83 fields.push(EventField::try_from(field)?);84 }85 if fields.iter().filter(|f| f.indexed).count() > 3 {86 return Err(syn::Error::new(87 variant.fields.span(),88 "events can have at most 4 indexed fields (1 indexed field is reserved for event signature)"89 ));90 }9192 let mut args = proc_macro2::TokenStream::new();93 let mut has_params = false;94 for arg in fields.iter() {95 has_params = true;96 let ty = &arg.ty;97 args.extend(quote! {nameof(<#ty>::SIGNATURE)});98 args.extend(quote! {fixed(",")})99 }100101 102 if has_params {103 args.extend(quote! {shift_left(1)})104 }105106 let signature = quote! { ::evm_coder::make_signature!(new fixed(#name_lit) fixed("(") #args fixed(")")) };107108 let selector = quote! {109 {110 let signature = #signature;111 let mut sum = ::evm_coder::sha3_const::Keccak256::new();112 let mut pos = 0;113 while pos < signature.len {114 sum = sum.update(&[signature.data[pos]; 1]);115 pos += 1;116 }117 let a = sum.finalize();118 let mut selector_bytes = [0; 32];119 let mut i = 0;120 while i != 32 {121 selector_bytes[i] = a[i];122 i += 1;123 }124 selector_bytes125 }126 };127128 Ok(Self {129 name: name.to_owned(),130 name_screaming,131 fields,132 selector,133 })134 }135136 fn expand_serializers(&self) -> proc_macro2::TokenStream {137 let name = &self.name;138 let name_screaming = &self.name_screaming;139 let fields = self.fields.iter().map(|f| &f.name);140141 let indexed = self.fields.iter().filter(|f| f.indexed).map(|f| &f.name);142 let plain = self.fields.iter().filter(|f| !f.indexed).map(|f| &f.name);143144 quote! {145 Self::#name {#(146 #fields,147 )*} => {148 topics.push(topic::from(Self::#name_screaming));149 #(150 topics.push(#indexed.to_topic());151 )*152 #(153 #plain.abi_write(&mut writer);154 )*155 }156 }157 }158159 fn expand_consts(&self) -> proc_macro2::TokenStream {160 let name_screaming = &self.name_screaming;161 let selector = &self.selector;162163 quote! {164 const #name_screaming: [u8; 32] = #selector;165 }166 }167168 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {169 let name = self.name.to_string();170 let args = self.fields.iter().map(EventField::expand_solidity_argument);171 quote! {172 SolidityEvent {173 name: #name,174 args: (175 #(176 #args,177 )*178 ),179 }180 }181 }182}183184pub struct Events {185 name: Ident,186 events: Vec<Event>,187}188189impl Events {190 pub fn try_from(data: &DeriveInput) -> syn::Result<Self> {191 let name = &data.ident;192 let en = match &data.data {193 Data::Enum(en) => en,194 _ => return Err(syn::Error::new(data.span(), "expected enum")),195 };196 let mut events = Vec::new();197 for variant in &en.variants {198 events.push(Event::try_from(variant)?);199 }200 Ok(Self {201 name: name.to_owned(),202 events,203 })204 }205 pub fn expand(&self) -> proc_macro2::TokenStream {206 let name = &self.name;207208 let consts = self.events.iter().map(Event::expand_consts);209 let serializers = self.events.iter().map(Event::expand_serializers);210 let solidity_name = self.name.to_string();211 let solidity_functions = self.events.iter().map(Event::expand_solidity_function);212213 quote! {214 impl #name {215 #(216 #consts217 )*218219 220 #[cfg(feature = "stubgen")]221 pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {222 use evm_coder::solidity::*;223 use core::fmt::Write;224 let interface = SolidityInterface {225 docs: &[],226 selector: [0; 4],227 name: #solidity_name,228 is: &[],229 functions: (#(230 #solidity_functions,231 )*),232 };233 let mut out = string::new();234 out.push_str("/// @dev inlined interface\n");235 let _ = interface.format(is_impl, &mut out, tc);236 tc.collect(out);237 }238 }239240 #[automatically_derived]241 impl ::evm_coder::events::ToLog for #name {242 fn to_log(&self, contract: address) -> ::ethereum::Log {243 use ::evm_coder::events::ToTopic;244 use ::evm_coder::abi::AbiWrite;245 let mut writer = ::evm_coder::abi::AbiWriter::new();246 let mut topics = Vec::new();247 match self {248 #(249 #serializers,250 )*251 }252 ::ethereum::Log {253 address: contract,254 topics,255 data: writer.finish(),256 }257 }258 }259 }260 }261}