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

difftreelog

feat solidity code generation

Yaroslav Bolyukin2021-07-22parent: #ab49fc9.patch.diff
in: master

2 files changed

modifiedcrates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth
before · crates/evm-coder-macros/src/solidity_interface.rs
1#![allow(dead_code)]23use quote::quote;4use darling::FromMeta;5use inflector::cases;6use std::fmt::Write;7use syn::{FnArg, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Meta, NestedMeta, PatType, Path, ReturnType, Type, spanned::Spanned};89use crate::{10	fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_ident_from_type,11	parse_result_ok, pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal,12	snake_ident_to_screaming,13};1415struct Is {16	name: Ident,17	pascal_call_name: Ident,18	snake_call_name: Ident,19}20impl Is {21	fn try_from(path: &Path) -> syn::Result<Self> {22		let name = parse_ident_from_path(path, false)?.clone();23		Ok(Self {24			pascal_call_name: pascal_ident_to_call(&name),25			snake_call_name: pascal_ident_to_snake_call(&name),26			name,27		})28	}2930	fn expand_call_def(&self) -> proc_macro2::TokenStream {31		let name = &self.name;32		let pascal_call_name = &self.pascal_call_name;33		quote! {34			#name(#pascal_call_name)35		}36	}3738	fn expand_interface_id(&self) -> proc_macro2::TokenStream {39		let pascal_call_name = &self.pascal_call_name;40		quote! {41			interface_id ^= #pascal_call_name::interface_id();42		}43	}4445	fn expand_supports_interface(&self) -> proc_macro2::TokenStream {46		let pascal_call_name = &self.pascal_call_name;47		quote! {48			#pascal_call_name::supports_interface(interface_id)49		}50	}5152	fn expand_variant_call(&self) -> proc_macro2::TokenStream {53		let name = &self.name;54		let pascal_call_name = &self.pascal_call_name;55		quote! {56			InternalCall::#name(call) => return <Self as ::evm_coder::Callable<#pascal_call_name>>::call(self, Msg {57				call,58				caller: c.caller,59				value: c.value,60			})61		}62	}6364	fn expand_parse(&self) -> proc_macro2::TokenStream {65		let name = &self.name;66		let pascal_call_name = &self.pascal_call_name;67		quote! {68			if let Some(parsed_call) = #pascal_call_name::parse(method_id, reader)? {69				return Ok(Some(Self::#name(parsed_call)))70			}71		}72	}73}7475#[derive(Default)]76struct IsList(Vec<Is>);77impl FromMeta for IsList {78	fn from_list(items: &[NestedMeta]) -> darling::Result<Self> {79		let mut out = Vec::new();80		for item in items {81			match item {82				NestedMeta::Meta(Meta::Path(path)) => out.push(Is::try_from(path)?),83				_ => return Err(syn::Error::new(item.span(), "expected path").into()),84			}85		}86		Ok(Self(out))87	}88}8990#[derive(FromMeta)]91pub struct InterfaceInfo {92	name: Ident,93	#[darling(default)]94	is: IsList,95	#[darling(default)]96	inline_is: IsList,97	#[darling(default)]98	events: IsList,99}100101#[derive(FromMeta)]102struct MethodInfo {103	#[darling(default)]104	rename_selector: Option<String>,105}106107struct MethodArg {108	name: Ident,109	ty: Ident,110}111impl MethodArg {112	fn try_from(value: &PatType) -> syn::Result<Self> {113		Ok(Self {114			name: parse_ident_from_pat(&value.pat)?.clone(),115			ty: parse_ident_from_type(&value.ty, false)?.clone(),116		})117	}118	fn is_value(&self) -> bool {119		self.ty == "value"120	}121	fn is_caller(&self) -> bool {122		self.ty == "caller"123	}124	fn is_special(&self) -> bool {125		self.is_value() || self.is_caller()126	}127	fn selector_ty(&self) -> &Ident {128		assert!(!self.is_special());129		&self.ty130	}131132	fn expand_call_def(&self) -> proc_macro2::TokenStream {133		assert!(!self.is_special());134		let name = &self.name;135		let ty = &self.ty;136137		quote! {138			#name: #ty139		}140	}141142	fn expand_parse(&self) -> proc_macro2::TokenStream {143		assert!(!self.is_special());144		let name = &self.name;145		quote! {146			#name: reader.abi_read()?147		}148	}149150	fn expand_call_arg(&self) -> proc_macro2::TokenStream {151		if self.is_value() {152			quote! {153				c.value.clone()154			}155		} else if self.is_caller() {156			quote! {157				c.caller.clone()158			}159		} else {160			let name = &self.name;161			quote! {162				#name163			}164		}165	}166}167168#[derive(PartialEq)]169enum Mutability {170	Mutable,171	View,172	Pure,173}174175struct Method {176	name: Ident,177	camel_name: String,178	pascal_name: Ident,179	screaming_name: Ident,180	selector_str: String,181	selector: u32,182	args: Vec<MethodArg>,183	has_normal_args: bool,184	mutability: Mutability,185	result: Type,186}187impl Method {188	fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {189		let mut info = MethodInfo {190			rename_selector: None,191		};192		for attr in &value.attrs {193			let ident = parse_ident_from_path(&attr.path, false)?;194			if ident == "solidity" {195				let args = attr.parse_meta().unwrap();196				info = MethodInfo::from_meta(&args).unwrap();197			} else if ident == "doc" {198				// TODO: Add docs to evm interfaces199			}200		}201		let ident = &value.sig.ident;202		let ident_str = ident.to_string();203		if !cases::snakecase::is_snake_case(&ident_str) {204			return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute"));205		}206207		let mut mutability = Mutability::Pure;208209		if let Some(FnArg::Receiver(receiver)) = value210			.sig211			.inputs212			.iter()213			.find(|arg| matches!(arg, FnArg::Receiver(_)))214		{215			if receiver.reference.is_none() {216				return Err(syn::Error::new(217					receiver.span(),218					"receiver should be by ref",219				));220			}221			if receiver.mutability.is_some() {222				mutability = Mutability::Mutable;223			} else {224				mutability = Mutability::View;225			}226		}227		let mut args = Vec::new();228		for typ in value229			.sig230			.inputs231			.iter()232			.filter(|arg| matches!(arg, FnArg::Typed(_)))233		{234			let typ = match typ {235				FnArg::Typed(typ) => typ,236				_ => unreachable!(),237			};238			args.push(MethodArg::try_from(typ)?);239		}240241		if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) {242			return Err(syn::Error::new(243				args.iter().find(|arg| arg.is_value()).unwrap().ty.span(),244				"payable function should be mutable",245			));246		}247248		let result = match &value.sig.output {249			ReturnType::Type(_, ty) => ty,250			_ => return Err(syn::Error::new(value.sig.output.span(), "interface method should return Result<value>\nif there is no value to return - specify void (which is alias to unit)")),251		};252		let result = parse_result_ok(result)?;253254		let camel_name = info255			.rename_selector256			.unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));257		let mut selector_str = camel_name.clone();258		selector_str.push('(');259		let mut has_normal_args = false;260		for (i, arg) in args.iter().filter(|arg| !arg.is_special()).enumerate() {261			if i != 0 {262				selector_str.push(',');263			}264			write!(selector_str, "{}", arg.selector_ty()).unwrap();265			has_normal_args = true;266		}267		selector_str.push(')');268		let selector = fn_selector_str(&selector_str);269270		Ok(Self {271			name: ident.clone(),272			camel_name,273			pascal_name: snake_ident_to_pascal(ident),274			screaming_name: snake_ident_to_screaming(ident),275			selector_str,276			selector,277			args,278			has_normal_args,279			mutability,280			result: result.clone(),281		})282	}283	fn expand_call_def(&self) -> proc_macro2::TokenStream {284		let defs = self285			.args286			.iter()287			.filter(|a| !a.is_special())288			.map(|a| a.expand_call_def());289		let pascal_name = &self.pascal_name;290291		if self.has_normal_args {292			quote! {293				#pascal_name {294					#(295						#defs,296					)*297				}298			}299		} else {300			quote! {#pascal_name}301		}302	}303304	fn expand_const(&self) -> proc_macro2::TokenStream {305		let screaming_name = &self.screaming_name;306		let selector = self.selector;307		let selector_str = &self.selector_str;308		quote! {309			#[doc = #selector_str]310			const #screaming_name: u32 = #selector;311		}312	}313314	fn expand_interface_id(&self) -> proc_macro2::TokenStream {315		let screaming_name = &self.screaming_name;316		quote! {317			interface_id ^= Self::#screaming_name;318		}319	}320321	fn expand_parse(&self) -> proc_macro2::TokenStream {322		let pascal_name = &self.pascal_name;323		let screaming_name = &self.screaming_name;324		if self.has_normal_args {325			let parsers = self326				.args327				.iter()328				.filter(|a| !a.is_special())329				.map(|a| a.expand_parse());330			quote! {331				Self::#screaming_name => return Ok(Some(Self::#pascal_name {332					#(333						#parsers,334					)*335				}))336			}337		} else {338			quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }339		}340	}341342	fn expand_variant_call(&self) -> proc_macro2::TokenStream {343		let pascal_name = &self.pascal_name;344		let name = &self.name;345346		let matcher = if self.has_normal_args {347			let names = self348				.args349				.iter()350				.filter(|a| !a.is_special())351				.map(|a| &a.name);352353			quote! {{354				#(355					#names,356				)*357			}}358		} else {359			quote! {}360		};361362		let receiver = match self.mutability {363			Mutability::Mutable | Mutability::View => quote! {self.},364			Mutability::Pure => quote! {Self::},365		};366		let args = self.args.iter().map(|a| a.expand_call_arg());367368		quote! {369			InternalCall::#pascal_name #matcher => {370				let result = #receiver #name(371					#(372						#args,373					)*374				)?;375				(&result).abi_write(&mut writer);376			}377		}378	}379}380381pub struct SolidityInterface {382    generics: Generics,383	name: Box<syn::Type>,384	info: InterfaceInfo,385	methods: Vec<Method>,386}387impl SolidityInterface {388	pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {389		let mut methods = Vec::new();390391		for item in &value.items {392			if let ImplItem::Method(method) = item {393				methods.push(Method::try_from(method)?)394			}395		}396		Ok(Self {397            generics: value.generics.clone(),398			name: value.self_ty.clone(),399			info,400			methods,401		})402	}403	pub fn expand(self) -> proc_macro2::TokenStream {404		let name = self.name;405406		let call_name = pascal_ident_to_call(&self.info.name);407        let generics = self.generics;408409		let call_sub = self410			.info411			.inline_is412			.0413			.iter()414			.chain(self.info.is.0.iter())415			.map(Is::expand_call_def);416		let call_parse = self417			.info418			.inline_is419			.0420			.iter()421			.chain(self.info.is.0.iter())422			.map(Is::expand_parse);423		let call_variants = self424			.info425			.inline_is426			.0427			.iter()428			.chain(self.info.is.0.iter())429			.map(Is::expand_variant_call);430431		let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);432		let supports_interface = self.info.is.0.iter().map(Is::expand_supports_interface);433434		let calls = self.methods.iter().map(Method::expand_call_def);435		let consts = self.methods.iter().map(Method::expand_const);436		let interface_id = self.methods.iter().map(Method::expand_interface_id);437		let parsers = self.methods.iter().map(Method::expand_parse);438		let call_variants_this = self.methods.iter().map(Method::expand_variant_call);439440		// let methods = self.methods.iter().map(Method::solidity_def);441442		quote! {443			#[derive(Debug)]444			pub enum #call_name {445				#(446					#calls,447				)*448				#(449					#call_sub,450				)*451			}452			impl #call_name {453				#(454					#consts455				)*456				pub const fn interface_id() -> u32 {457					let mut interface_id = 0;458					#(#interface_id)*459					#(#inline_interface_id)*460					interface_id461				}462				pub fn supports_interface(interface_id: u32) -> bool {463					interface_id != 0xffffff && (464						interface_id == Self::interface_id()465						#(466							|| #supports_interface467						)*468					)469				}470			}471			impl ::evm_coder::Call for #call_name {472				fn parse(method_id: u32, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {473					use ::evm_coder::abi::AbiRead;474					match method_id {475						#(476							#parsers,477						)*478						_ => {},479					}480					#(481						#call_parse482					)else*483					return Ok(None);484				}485			}486			impl #generics ::evm_coder::Callable<#call_name> for #name {487				#[allow(unreachable_code)] // In case of no inner calls488				fn call(&mut self, c: Msg<#call_name>) -> Result<::evm_coder::abi::AbiWriter> {489					use ::evm_coder::abi::AbiWrite;490					type InternalCall = #call_name;491					match c.call {492						#(493							#call_variants,494						)*495						_ => {},496					}497					let mut writer = ::evm_coder::abi::AbiWriter::default();498					match c.call {499						#(500							#call_variants_this,501						)*502						_ => unreachable!()503					}504					Ok(writer)505				}506			}507		}508	}509}
after · crates/evm-coder-macros/src/solidity_interface.rs
1#![allow(dead_code)]23use quote::quote;4use darling::FromMeta;5use inflector::cases;6use std::fmt::Write;7use syn::{8	FnArg, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Meta, NestedMeta, PatType, Path,9	ReturnType, Type, spanned::Spanned,10};1112use crate::{13	fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_ident_from_type,14	parse_result_ok, pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal,15	snake_ident_to_screaming,16};1718struct Is {19	name: Ident,20	pascal_call_name: Ident,21	snake_call_name: Ident,22}23impl Is {24	fn try_from(path: &Path) -> syn::Result<Self> {25		let name = parse_ident_from_path(path, false)?.clone();26		Ok(Self {27			pascal_call_name: pascal_ident_to_call(&name),28			snake_call_name: pascal_ident_to_snake_call(&name),29			name,30		})31	}3233	fn expand_call_def(&self) -> proc_macro2::TokenStream {34		let name = &self.name;35		let pascal_call_name = &self.pascal_call_name;36		quote! {37			#name(#pascal_call_name)38		}39	}4041	fn expand_interface_id(&self) -> proc_macro2::TokenStream {42		let pascal_call_name = &self.pascal_call_name;43		quote! {44			interface_id ^= #pascal_call_name::interface_id();45		}46	}4748	fn expand_supports_interface(&self) -> proc_macro2::TokenStream {49		let pascal_call_name = &self.pascal_call_name;50		quote! {51			#pascal_call_name::supports_interface(interface_id)52		}53	}5455	fn expand_variant_call(&self) -> proc_macro2::TokenStream {56		let name = &self.name;57		let pascal_call_name = &self.pascal_call_name;58		quote! {59			InternalCall::#name(call) => return <Self as ::evm_coder::Callable<#pascal_call_name>>::call(self, Msg {60				call,61				caller: c.caller,62				value: c.value,63			})64		}65	}6667	fn expand_parse(&self) -> proc_macro2::TokenStream {68		let name = &self.name;69		let pascal_call_name = &self.pascal_call_name;70		quote! {71			if let Some(parsed_call) = #pascal_call_name::parse(method_id, reader)? {72				return Ok(Some(Self::#name(parsed_call)))73			}74		}75	}76}7778#[derive(Default)]79struct IsList(Vec<Is>);80impl FromMeta for IsList {81	fn from_list(items: &[NestedMeta]) -> darling::Result<Self> {82		let mut out = Vec::new();83		for item in items {84			match item {85				NestedMeta::Meta(Meta::Path(path)) => out.push(Is::try_from(path)?),86				_ => return Err(syn::Error::new(item.span(), "expected path").into()),87			}88		}89		Ok(Self(out))90	}91}9293#[derive(FromMeta)]94pub struct InterfaceInfo {95	name: Ident,96	#[darling(default)]97	is: IsList,98	#[darling(default)]99	inline_is: IsList,100	#[darling(default)]101	events: IsList,102}103104#[derive(FromMeta)]105struct MethodInfo {106	#[darling(default)]107	rename_selector: Option<String>,108}109110struct MethodArg {111	name: Ident,112	ty: Ident,113}114impl MethodArg {115	fn try_from(value: &PatType) -> syn::Result<Self> {116		Ok(Self {117			name: parse_ident_from_pat(&value.pat)?.clone(),118			ty: parse_ident_from_type(&value.ty, false)?.clone(),119		})120	}121	fn is_value(&self) -> bool {122		self.ty == "value"123	}124	fn is_caller(&self) -> bool {125		self.ty == "caller"126	}127	fn is_special(&self) -> bool {128		self.is_value() || self.is_caller()129	}130	fn selector_ty(&self) -> &Ident {131		assert!(!self.is_special());132		&self.ty133	}134135	fn expand_call_def(&self) -> proc_macro2::TokenStream {136		assert!(!self.is_special());137		let name = &self.name;138		let ty = &self.ty;139140		quote! {141			#name: #ty142		}143	}144145	fn expand_parse(&self) -> proc_macro2::TokenStream {146		assert!(!self.is_special());147		let name = &self.name;148		quote! {149			#name: reader.abi_read()?150		}151	}152153	fn expand_call_arg(&self) -> proc_macro2::TokenStream {154		if self.is_value() {155			quote! {156				c.value.clone()157			}158		} else if self.is_caller() {159			quote! {160				c.caller.clone()161			}162		} else {163			let name = &self.name;164			quote! {165				#name166			}167		}168	}169170	fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {171		let name = &self.name.to_string();172		let ty = &self.ty;173		quote! {174			<NamedArgument<#ty>>::new(#name)175		}176	}177}178179#[derive(PartialEq)]180enum Mutability {181	Mutable,182	View,183	Pure,184}185186struct Method {187	name: Ident,188	camel_name: String,189	pascal_name: Ident,190	screaming_name: Ident,191	selector_str: String,192	selector: u32,193	args: Vec<MethodArg>,194	has_normal_args: bool,195	mutability: Mutability,196	result: Type,197}198impl Method {199	fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {200		let mut info = MethodInfo {201			rename_selector: None,202		};203		for attr in &value.attrs {204			let ident = parse_ident_from_path(&attr.path, false)?;205			if ident == "solidity" {206				let args = attr.parse_meta().unwrap();207				info = MethodInfo::from_meta(&args).unwrap();208			} else if ident == "doc" {209				// TODO: Add docs to evm interfaces210			}211		}212		let ident = &value.sig.ident;213		let ident_str = ident.to_string();214		if !cases::snakecase::is_snake_case(&ident_str) {215			return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute"));216		}217218		let mut mutability = Mutability::Pure;219220		if let Some(FnArg::Receiver(receiver)) = value221			.sig222			.inputs223			.iter()224			.find(|arg| matches!(arg, FnArg::Receiver(_)))225		{226			if receiver.reference.is_none() {227				return Err(syn::Error::new(228					receiver.span(),229					"receiver should be by ref",230				));231			}232			if receiver.mutability.is_some() {233				mutability = Mutability::Mutable;234			} else {235				mutability = Mutability::View;236			}237		}238		let mut args = Vec::new();239		for typ in value240			.sig241			.inputs242			.iter()243			.filter(|arg| matches!(arg, FnArg::Typed(_)))244		{245			let typ = match typ {246				FnArg::Typed(typ) => typ,247				_ => unreachable!(),248			};249			args.push(MethodArg::try_from(typ)?);250		}251252		if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) {253			return Err(syn::Error::new(254				args.iter().find(|arg| arg.is_value()).unwrap().ty.span(),255				"payable function should be mutable",256			));257		}258259		let result = match &value.sig.output {260			ReturnType::Type(_, ty) => ty,261			_ => return Err(syn::Error::new(value.sig.output.span(), "interface method should return Result<value>\nif there is no value to return - specify void (which is alias to unit)")),262		};263		let result = parse_result_ok(result)?;264265		let camel_name = info266			.rename_selector267			.unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));268		let mut selector_str = camel_name.clone();269		selector_str.push('(');270		let mut has_normal_args = false;271		for (i, arg) in args.iter().filter(|arg| !arg.is_special()).enumerate() {272			if i != 0 {273				selector_str.push(',');274			}275			write!(selector_str, "{}", arg.selector_ty()).unwrap();276			has_normal_args = true;277		}278		selector_str.push(')');279		let selector = fn_selector_str(&selector_str);280281		Ok(Self {282			name: ident.clone(),283			camel_name,284			pascal_name: snake_ident_to_pascal(ident),285			screaming_name: snake_ident_to_screaming(ident),286			selector_str,287			selector,288			args,289			has_normal_args,290			mutability,291			result: result.clone(),292		})293	}294	fn expand_call_def(&self) -> proc_macro2::TokenStream {295		let defs = self296			.args297			.iter()298			.filter(|a| !a.is_special())299			.map(|a| a.expand_call_def());300		let pascal_name = &self.pascal_name;301302		if self.has_normal_args {303			quote! {304				#pascal_name {305					#(306						#defs,307					)*308				}309			}310		} else {311			quote! {#pascal_name}312		}313	}314315	fn expand_const(&self) -> proc_macro2::TokenStream {316		let screaming_name = &self.screaming_name;317		let selector = self.selector;318		let selector_str = &self.selector_str;319		quote! {320			#[doc = #selector_str]321			const #screaming_name: u32 = #selector;322		}323	}324325	fn expand_interface_id(&self) -> proc_macro2::TokenStream {326		let screaming_name = &self.screaming_name;327		quote! {328			interface_id ^= Self::#screaming_name;329		}330	}331332	fn expand_parse(&self) -> proc_macro2::TokenStream {333		let pascal_name = &self.pascal_name;334		let screaming_name = &self.screaming_name;335		if self.has_normal_args {336			let parsers = self337				.args338				.iter()339				.filter(|a| !a.is_special())340				.map(|a| a.expand_parse());341			quote! {342				Self::#screaming_name => return Ok(Some(Self::#pascal_name {343					#(344						#parsers,345					)*346				}))347			}348		} else {349			quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }350		}351	}352353	fn expand_variant_call(&self) -> proc_macro2::TokenStream {354		let pascal_name = &self.pascal_name;355		let name = &self.name;356357		let matcher = if self.has_normal_args {358			let names = self359				.args360				.iter()361				.filter(|a| !a.is_special())362				.map(|a| &a.name);363364			quote! {{365				#(366					#names,367				)*368			}}369		} else {370			quote! {}371		};372373		let receiver = match self.mutability {374			Mutability::Mutable | Mutability::View => quote! {self.},375			Mutability::Pure => quote! {Self::},376		};377		let args = self.args.iter().map(|a| a.expand_call_arg());378379		quote! {380			InternalCall::#pascal_name #matcher => {381				let result = #receiver #name(382					#(383						#args,384					)*385				)?;386				(&result).abi_write(&mut writer);387			}388		}389	}390391	fn expand_solidity_function(&self) -> proc_macro2::TokenStream {392		let camel_name = &self.camel_name;393		let mutability = match self.mutability {394			Mutability::Mutable => quote! {SolidityMutability::Mutable},395			Mutability::View => quote! { SolidityMutability::View },396			Mutability::Pure => quote! {SolidityMutability::Pure},397		};398		let result = &self.result;399400		let args = self.args.iter().map(MethodArg::expand_solidity_argument);401402		quote! {403			SolidityFunction {404				name: #camel_name,405				mutability: #mutability,406				args: (407					#(408						#args,409					)*410				),411				result: <UnnamedArgument<#result>>::default(),412			}413		}414	}415}416417pub struct SolidityInterface {418	generics: Generics,419	name: Box<syn::Type>,420	info: InterfaceInfo,421	methods: Vec<Method>,422}423impl SolidityInterface {424	pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {425		let mut methods = Vec::new();426427		for item in &value.items {428			if let ImplItem::Method(method) = item {429				methods.push(Method::try_from(method)?)430			}431		}432		Ok(Self {433			generics: value.generics.clone(),434			name: value.self_ty.clone(),435			info,436			methods,437		})438	}439	pub fn expand(self) -> proc_macro2::TokenStream {440		let name = self.name;441442		let solidity_name = self.info.name.to_string();443		let call_name = pascal_ident_to_call(&self.info.name);444		let generics = self.generics;445446		let call_sub = self447			.info448			.inline_is449			.0450			.iter()451			.chain(self.info.is.0.iter())452			.map(Is::expand_call_def);453		let call_parse = self454			.info455			.inline_is456			.0457			.iter()458			.chain(self.info.is.0.iter())459			.map(Is::expand_parse);460		let call_variants = self461			.info462			.inline_is463			.0464			.iter()465			.chain(self.info.is.0.iter())466			.map(Is::expand_variant_call);467468		let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);469		let supports_interface = self.info.is.0.iter().map(Is::expand_supports_interface);470471		let calls = self.methods.iter().map(Method::expand_call_def);472		let consts = self.methods.iter().map(Method::expand_const);473		let interface_id = self.methods.iter().map(Method::expand_interface_id);474		let parsers = self.methods.iter().map(Method::expand_parse);475		let call_variants_this = self.methods.iter().map(Method::expand_variant_call);476		let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);477478		// let methods = self.methods.iter().map(Method::solidity_def);479480		quote! {481			#[derive(Debug)]482			pub enum #call_name {483				#(484					#calls,485				)*486				#(487					#call_sub,488				)*489			}490			impl #call_name {491				#(492					#consts493				)*494				pub const fn interface_id() -> u32 {495					let mut interface_id = 0;496					#(#interface_id)*497					#(#inline_interface_id)*498					interface_id499				}500				pub fn supports_interface(interface_id: u32) -> bool {501					interface_id != 0xffffff && (502						interface_id == Self::interface_id()503						#(504							|| #supports_interface505						)*506					)507				}508				pub fn generate_solidity_interface() -> string {509					use evm_coder::solidity::*;510					use core::fmt::Write;511					let interface = SolidityInterface {512						name: #solidity_name,513						functions: (#(514							#solidity_functions,515						)*),516					};517					let mut out = string::new();518					let _ = interface.format(&mut out);519					out520				}521			}522			impl ::evm_coder::Call for #call_name {523				fn parse(method_id: u32, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {524					use ::evm_coder::abi::AbiRead;525					match method_id {526						#(527							#parsers,528						)*529						_ => {},530					}531					#(532						#call_parse533					)else*534					return Ok(None);535				}536			}537			impl #generics ::evm_coder::Callable<#call_name> for #name {538				#[allow(unreachable_code)] // In case of no inner calls539				fn call(&mut self, c: Msg<#call_name>) -> Result<::evm_coder::abi::AbiWriter> {540					use ::evm_coder::abi::AbiWrite;541					type InternalCall = #call_name;542					match c.call {543						#(544							#call_variants,545						)*546						_ => {},547					}548					let mut writer = ::evm_coder::abi::AbiWriter::default();549					match c.call {550						#(551							#call_variants_this,552						)*553						_ => unreachable!()554					}555					Ok(writer)556				}557			}558		}559	}560}
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -1,3 +1,5 @@
+#[cfg(not(feature = "std"))]
+use alloc::{string::String};
 use core::{fmt, marker::PhantomData};
 use impl_trait_for_tuples::impl_for_tuples;
 use crate::types::*;
@@ -23,8 +25,13 @@
 
 solidity_type_name! {
 	uint8 => "uint8",
+	uint32 => "uint32",
+	uint128 => "uint128",
+	uint256 => "uint256",
 	address => "address",
 	string => "memory string",
+	bytes => "memory bytes",
+	bool => "bool",
 }
 impl SolidityTypeName for void {
 	fn solidity_name(_writer: &mut impl fmt::Write) -> fmt::Result {
@@ -43,6 +50,7 @@
 	fn len(&self) -> usize;
 }
 
+#[derive(Default)]
 pub struct UnnamedArgument<T>(PhantomData<*const T>);
 
 impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {
@@ -64,6 +72,12 @@
 
 pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);
 
+impl<T> NamedArgument<T> {
+	pub fn new(name: &'static str) -> Self {
+		Self(name, Default::default())
+	}
+}
+
 impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {
 	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
 		if !T::is_void() {
@@ -96,10 +110,10 @@
 	for_tuples!( where #( Tuple: SolidityArguments ),* );
 
 	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
-		let mut first = false;
+		let mut first = true;
 		for_tuples!( #(
             if !Tuple.is_empty() {
-                if first {
+                if !first {
                     write!(writer, ", ")?;
                 }
                 first = false;
@@ -113,7 +127,7 @@
 	}
 }
 
-trait SolidityFunctions {
+pub trait SolidityFunctions {
 	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result;
 }
 
@@ -123,23 +137,23 @@
 	Mutable,
 }
 pub struct SolidityFunction<A, R> {
-	name: &'static str,
-	args: A,
-	result: R,
-	mutability: SolidityMutability,
+	pub name: &'static str,
+	pub args: A,
+	pub result: R,
+	pub mutability: SolidityMutability,
 }
 impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {
 	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
 		write!(writer, "function {}(", self.name)?;
 		self.args.solidity_name(writer)?;
-		write!(writer, ") public")?;
+		write!(writer, ") external")?;
 		match &self.mutability {
 			SolidityMutability::Pure => write!(writer, " pure")?,
 			SolidityMutability::View => write!(writer, " view")?,
 			SolidityMutability::Mutable => {}
 		}
 		if !self.result.is_empty() {
-			write!(writer, "returns (")?;
+			write!(writer, " returns (")?;
 			self.result.solidity_name(writer)?;
 			write!(writer, ")")?;
 		}
@@ -159,3 +173,17 @@
 		Ok(())
 	}
 }
+
+pub struct SolidityInterface<F: SolidityFunctions> {
+	pub name: &'static str,
+	pub functions: F,
+}
+
+impl<F: SolidityFunctions> SolidityInterface<F> {
+	pub fn format(&self, out: &mut impl fmt::Write) -> fmt::Result {
+		writeln!(out, "interface {} {{", self.name)?;
+		self.functions.solidity_name(out)?;
+		writeln!(out, "}}")?;
+		Ok(())
+	}
+}
\ No newline at end of file