difftreelog
feat solidity code generation
in: master
2 files changed
crates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth1#![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}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}crates/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