git.delta.rocks / unique-network / refs/commits / e0f21c8d38f7

difftreelog

source

crates/evm-coder-macros/src/to_log.rs5.0 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		quote! {38			<NamedArgument<#ty>>::new(#camel_name)39		}40	}41}4243struct Event {44	name: Ident,45	name_screaming: Ident,46	fields: Vec<EventField>,47	selector: [u8; 32],48	selector_str: String,49}5051impl Event {52	fn try_from(variant: &Variant) -> syn::Result<Self> {53		let name = &variant.ident;54		let name_screaming = snake_ident_to_screaming(name);5556		let named = match &variant.fields {57			Fields::Named(named) => named,58			_ => {59				return Err(syn::Error::new(60					variant.fields.span(),61					"expected named fields",62				))63			}64		};65		let mut fields = Vec::new();66		for field in &named.named {67			fields.push(EventField::try_from(field)?);68		}69		if fields.iter().filter(|f| f.indexed).count() > 3 {70			return Err(syn::Error::new(71				variant.fields.span(),72				"events can have at most 4 indexed fields (1 indexed field is reserved for event signature)"73			));74		}75		let mut selector_str = format!("{}(", name);76		for (i, arg) in fields.iter().enumerate() {77			if i != 0 {78				write!(selector_str, ",").unwrap();79			}80			write!(selector_str, "{}", arg.ty).unwrap();81		}82		selector_str.push(')');83		let selector = crate::event_selector_str(&selector_str);8485		Ok(Self {86			name: name.to_owned(),87			name_screaming,88			fields,89			selector,90			selector_str,91		})92	}9394	fn expand_serializers(&self) -> proc_macro2::TokenStream {95		let name = &self.name;96		let name_screaming = &self.name_screaming;97		let fields = self.fields.iter().map(|f| &f.name);9899		let indexed = self.fields.iter().filter(|f| f.indexed).map(|f| &f.name);100		let plain = self.fields.iter().filter(|f| !f.indexed).map(|f| &f.name);101102		quote! {103			Self::#name {#(104				#fields,105			)*} => {106				topics.push(topic::from(Self::#name_screaming));107				#(108					topics.push(#indexed.to_topic());109				)*110				#(111					#plain.abi_write(&mut writer);112				)*113			}114		}115	}116117	fn expand_consts(&self) -> proc_macro2::TokenStream {118		let name_screaming = &self.name_screaming;119		let selector_str = &self.selector_str;120		let selector = &self.selector;121122		quote! {123			#[doc = #selector_str]124			const #name_screaming: [u8; 32] = [#(125				#selector,126			)*];127		}128	}129130	fn expand_solidity_function(&self) -> proc_macro2::TokenStream {131		let name = self.name.to_string();132		let args = self.fields.iter().map(EventField::expand_solidity_argument);133		quote! {134			SolidityEvent {135				name: #name,136				args: (137					#(138						#args,139					)*140				),141			}142		}143	}144}145146pub struct Events {147	name: Ident,148	events: Vec<Event>,149}150151impl Events {152	pub fn try_from(data: &DeriveInput) -> syn::Result<Self> {153		let name = &data.ident;154		let en = match &data.data {155			Data::Enum(en) => en,156			_ => return Err(syn::Error::new(data.span(), "expected enum")),157		};158		let mut events = Vec::new();159		for variant in &en.variants {160			events.push(Event::try_from(variant)?);161		}162		Ok(Self {163			name: name.to_owned(),164			events,165		})166	}167	pub fn expand(&self) -> proc_macro2::TokenStream {168		let name = &self.name;169170		let consts = self.events.iter().map(Event::expand_consts);171		let serializers = self.events.iter().map(Event::expand_serializers);172		let solidity_name = self.name.to_string();173		let solidity_functions = self.events.iter().map(Event::expand_solidity_function);174175		quote! {176			impl #name {177				#(178					#consts179				)*180181				pub fn generate_solidity_interface(out_set: &mut sp_std::collections::btree_set::BTreeSet<string>, is_impl: bool) {182					use evm_coder::solidity::*;183					use core::fmt::Write;184					let interface = SolidityInterface {185						name: #solidity_name,186						is: &[],187						functions: (#(188							#solidity_functions,189						)*),190					};191					let mut out = string::new();192					out.push_str("// Inline\n");193					let _ = interface.format(is_impl, &mut out);194					out_set.insert(out);195				}196			}197198			#[automatically_derived]199			impl ::evm_coder::events::ToLog for #name {200				fn to_log(&self, contract: address) -> ::ethereum::Log {201					use ::evm_coder::events::ToTopic;202					use ::evm_coder::abi::AbiWrite;203					let mut writer = ::evm_coder::abi::AbiWriter::new();204					let mut topics = Vec::new();205					match self {206						#(207							#serializers,208						)*209					}210					::ethereum::Log {211						address: contract,212						topics,213						data: writer.finish(),214					}215				}216			}217		}218	}219}