git.delta.rocks / unique-network / refs/commits / 8057bddf39cb

difftreelog

source

crates/evm-coder-macros/src/to_log.rs5.1 KiBsourcehistory
1use inflector::cases;2use syn::{Data, DeriveInput, Field, Fields, Ident, Variant, spanned::Spanned};3use std::fmt::Write;4use quote::quote;56use crate::{parse_ident_from_path, parse_ident_from_type, snake_ident_to_screaming};78struct EventField {9	name: Ident,10	camel_name: String,11	ty: Ident,12	indexed: bool,13}1415impl EventField {16	fn try_from(field: &Field) -> syn::Result<Self> {17		let name = field.ident.as_ref().unwrap();18		let ty = parse_ident_from_type(&field.ty, false)?;19		let mut indexed = false;20		for attr in &field.attrs {21			if let Ok(ident) = parse_ident_from_path(&attr.path, false) {22				if ident == "indexed" {23					indexed = true;24				}25			}26		}27		Ok(Self {28			name: name.to_owned(),29			camel_name: cases::camelcase::to_camel_case(&name.to_string()),30			ty: ty.to_owned(),31			indexed,32		})33	}34	fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {35		let camel_name = &self.camel_name;36		let ty = &self.ty;37		let indexed = self.indexed;38		quote! {39			<SolidityEventArgument<#ty>>::new(#indexed, #camel_name)40		}41	}42}4344struct Event {45	name: Ident,46	name_screaming: Ident,47	fields: Vec<EventField>,48	selector: [u8; 32],49	selector_str: String,50}5152impl Event {53	fn try_from(variant: &Variant) -> syn::Result<Self> {54		let name = &variant.ident;55		let name_screaming = snake_ident_to_screaming(name);5657		let named = match &variant.fields {58			Fields::Named(named) => named,59			_ => {60				return Err(syn::Error::new(61					variant.fields.span(),62					"expected named fields",63				))64			}65		};66		let mut fields = Vec::new();67		for field in &named.named {68			fields.push(EventField::try_from(field)?);69		}70		if fields.iter().filter(|f| f.indexed).count() > 3 {71			return Err(syn::Error::new(72				variant.fields.span(),73				"events can have at most 4 indexed fields (1 indexed field is reserved for event signature)"74			));75		}76		let mut selector_str = format!("{}(", name);77		for (i, arg) in fields.iter().enumerate() {78			if i != 0 {79				write!(selector_str, ",").unwrap();80			}81			write!(selector_str, "{}", arg.ty).unwrap();82		}83		selector_str.push(')');84		let selector = crate::event_selector_str(&selector_str);8586		Ok(Self {87			name: name.to_owned(),88			name_screaming,89			fields,90			selector,91			selector_str,92		})93	}9495	fn expand_serializers(&self) -> proc_macro2::TokenStream {96		let name = &self.name;97		let name_screaming = &self.name_screaming;98		let fields = self.fields.iter().map(|f| &f.name);99100		let indexed = self.fields.iter().filter(|f| f.indexed).map(|f| &f.name);101		let plain = self.fields.iter().filter(|f| !f.indexed).map(|f| &f.name);102103		quote! {104			Self::#name {#(105				#fields,106			)*} => {107				topics.push(topic::from(Self::#name_screaming));108				#(109					topics.push(#indexed.to_topic());110				)*111				#(112					#plain.abi_write(&mut writer);113				)*114			}115		}116	}117118	fn expand_consts(&self) -> proc_macro2::TokenStream {119		let name_screaming = &self.name_screaming;120		let selector_str = &self.selector_str;121		let selector = &self.selector;122123		quote! {124			#[doc = #selector_str]125			const #name_screaming: [u8; 32] = [#(126				#selector,127			)*];128		}129	}130131	fn expand_solidity_function(&self) -> proc_macro2::TokenStream {132		let name = self.name.to_string();133		let args = self.fields.iter().map(EventField::expand_solidity_argument);134		quote! {135			SolidityEvent {136				name: #name,137				args: (138					#(139						#args,140					)*141				),142			}143		}144	}145}146147pub struct Events {148	name: Ident,149	events: Vec<Event>,150}151152impl Events {153	pub fn try_from(data: &DeriveInput) -> syn::Result<Self> {154		let name = &data.ident;155		let en = match &data.data {156			Data::Enum(en) => en,157			_ => return Err(syn::Error::new(data.span(), "expected enum")),158		};159		let mut events = Vec::new();160		for variant in &en.variants {161			events.push(Event::try_from(variant)?);162		}163		Ok(Self {164			name: name.to_owned(),165			events,166		})167	}168	pub fn expand(&self) -> proc_macro2::TokenStream {169		let name = &self.name;170171		let consts = self.events.iter().map(Event::expand_consts);172		let serializers = self.events.iter().map(Event::expand_serializers);173		let solidity_name = self.name.to_string();174		let solidity_functions = self.events.iter().map(Event::expand_solidity_function);175176		quote! {177			impl #name {178				#(179					#consts180				)*181182				pub fn generate_solidity_interface(out_set: &mut sp_std::collections::btree_set::BTreeSet<string>, is_impl: bool) {183					use evm_coder::solidity::*;184					use core::fmt::Write;185					let interface = SolidityInterface {186						name: #solidity_name,187						is: &[],188						functions: (#(189							#solidity_functions,190						)*),191					};192					let mut out = string::new();193					out.push_str("// Inline\n");194					let _ = interface.format(is_impl, &mut out);195					out_set.insert(out);196				}197			}198199			#[automatically_derived]200			impl ::evm_coder::events::ToLog for #name {201				fn to_log(&self, contract: address) -> ::ethereum::Log {202					use ::evm_coder::events::ToTopic;203					use ::evm_coder::abi::AbiWrite;204					let mut writer = ::evm_coder::abi::AbiWriter::new();205					let mut topics = Vec::new();206					match self {207						#(208							#serializers,209						)*210					}211					::ethereum::Log {212						address: contract,213						topics,214						data: writer.finish(),215					}216				}217			}218		}219	}220}