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

difftreelog

source

crates/evm-coder/procedural/src/to_log.rs6.5 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use 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		// Remove trailing comma102		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				/// Generate solidity definitions for methods described in this interface220				#[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}