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

difftreelog

source

crates/evm-coder/procedural/src/to_log.rs5.9 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 std::fmt::Write;20use quote::quote;2122use crate::{parse_ident_from_path, parse_ident_from_type, snake_ident_to_screaming};2324struct EventField {25	name: Ident,26	camel_name: String,27	ty: Ident,28	indexed: bool,29}3031impl EventField {32	fn try_from(field: &Field) -> syn::Result<Self> {33		let name = field.ident.as_ref().unwrap();34		let ty = parse_ident_from_type(&field.ty, false)?;35		let mut indexed = false;36		for attr in &field.attrs {37			if let Ok(ident) = parse_ident_from_path(&attr.path, false) {38				if ident == "indexed" {39					indexed = true;40				}41			}42		}43		Ok(Self {44			name: name.to_owned(),45			camel_name: cases::camelcase::to_camel_case(&name.to_string()),46			ty: ty.to_owned(),47			indexed,48		})49	}50	fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {51		let camel_name = &self.camel_name;52		let ty = &self.ty;53		let indexed = self.indexed;54		quote! {55			<SolidityEventArgument<#ty>>::new(#indexed, #camel_name)56		}57	}58}5960struct Event {61	name: Ident,62	name_screaming: Ident,63	fields: Vec<EventField>,64	selector: [u8; 32],65	selector_str: String,66}6768impl Event {69	fn try_from(variant: &Variant) -> syn::Result<Self> {70		let name = &variant.ident;71		let name_screaming = snake_ident_to_screaming(name);7273		let named = match &variant.fields {74			Fields::Named(named) => named,75			_ => {76				return Err(syn::Error::new(77					variant.fields.span(),78					"expected named fields",79				))80			}81		};82		let mut fields = Vec::new();83		for field in &named.named {84			fields.push(EventField::try_from(field)?);85		}86		if fields.iter().filter(|f| f.indexed).count() > 3 {87			return Err(syn::Error::new(88				variant.fields.span(),89				"events can have at most 4 indexed fields (1 indexed field is reserved for event signature)"90			));91		}92		let mut selector_str = format!("{}(", name);93		for (i, arg) in fields.iter().enumerate() {94			if i != 0 {95				write!(selector_str, ",").unwrap();96			}97			write!(selector_str, "{}", arg.ty).unwrap();98		}99		selector_str.push(')');100		let selector = crate::event_selector_str(&selector_str);101102		Ok(Self {103			name: name.to_owned(),104			name_screaming,105			fields,106			selector,107			selector_str,108		})109	}110111	fn expand_serializers(&self) -> proc_macro2::TokenStream {112		let name = &self.name;113		let name_screaming = &self.name_screaming;114		let fields = self.fields.iter().map(|f| &f.name);115116		let indexed = self.fields.iter().filter(|f| f.indexed).map(|f| &f.name);117		let plain = self.fields.iter().filter(|f| !f.indexed).map(|f| &f.name);118119		quote! {120			Self::#name {#(121				#fields,122			)*} => {123				topics.push(topic::from(Self::#name_screaming));124				#(125					topics.push(#indexed.to_topic());126				)*127				#(128					#plain.abi_write(&mut writer);129				)*130			}131		}132	}133134	fn expand_consts(&self) -> proc_macro2::TokenStream {135		let name_screaming = &self.name_screaming;136		let selector_str = &self.selector_str;137		let selector = &self.selector;138139		quote! {140			#[doc = #selector_str]141			const #name_screaming: [u8; 32] = [#(142				#selector,143			)*];144		}145	}146147	fn expand_solidity_function(&self) -> proc_macro2::TokenStream {148		let name = self.name.to_string();149		let args = self.fields.iter().map(EventField::expand_solidity_argument);150		quote! {151			SolidityEvent {152				name: #name,153				args: (154					#(155						#args,156					)*157				),158			}159		}160	}161}162163pub struct Events {164	name: Ident,165	events: Vec<Event>,166}167168impl Events {169	pub fn try_from(data: &DeriveInput) -> syn::Result<Self> {170		let name = &data.ident;171		let en = match &data.data {172			Data::Enum(en) => en,173			_ => return Err(syn::Error::new(data.span(), "expected enum")),174		};175		let mut events = Vec::new();176		for variant in &en.variants {177			events.push(Event::try_from(variant)?);178		}179		Ok(Self {180			name: name.to_owned(),181			events,182		})183	}184	pub fn expand(&self) -> proc_macro2::TokenStream {185		let name = &self.name;186187		let consts = self.events.iter().map(Event::expand_consts);188		let serializers = self.events.iter().map(Event::expand_serializers);189		let solidity_name = self.name.to_string();190		let solidity_functions = self.events.iter().map(Event::expand_solidity_function);191192		quote! {193			impl #name {194				#(195					#consts196				)*197198				/// Generate solidity definitions for methods described in this interface199				#[cfg(feature = "stubgen")]200				pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {201					use evm_coder::solidity::*;202					use core::fmt::Write;203					let interface = SolidityInterface {204						docs: &[],205						selector: [0; 4],206						name: #solidity_name,207						is: &[],208						functions: (#(209							#solidity_functions,210						)*),211					};212					let mut out = string::new();213					out.push_str("/// @dev inlined interface\n");214					let _ = interface.format(is_impl, &mut out, tc);215					tc.collect(out);216				}217			}218219			#[automatically_derived]220			impl ::evm_coder::events::ToLog for #name {221				fn to_log(&self, contract: address) -> ::ethereum::Log {222					use ::evm_coder::events::ToTopic;223					use ::evm_coder::abi::AbiWrite;224					let mut writer = ::evm_coder::abi::AbiWriter::new();225					let mut topics = Vec::new();226					match self {227						#(228							#serializers,229						)*230					}231					::ethereum::Log {232						address: contract,233						topics,234						data: writer.finish(),235					}236				}237			}238		}239	}240}