1234567891011121314151617#![allow(dead_code)]181920212223use proc_macro2::TokenStream;24use quote::{quote, format_ident};25use inflector::cases;26use syn::{27 Expr, FnArg, GenericArgument, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta,28 MetaNameValue, PatType, PathArguments, ReturnType, Type,29 spanned::Spanned,30 parse::{Parse, ParseStream},31 parenthesized, Token, LitInt, LitStr,32};3334use crate::{35 parse_ident_from_pat, parse_ident_from_path, parse_path, parse_path_segment, parse_result_ok,36 pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal,37 snake_ident_to_screaming,38};3940struct Is {41 name: Ident,42 pascal_call_name: Ident,43 snake_call_name: Ident,44 via: Option<(Type, Ident)>,45 condition: Option<Expr>,46}47impl Is {48 fn expand_call_def(&self, gen_ref: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {49 let name = &self.name;50 let pascal_call_name = &self.pascal_call_name;51 quote! {52 #name(#pascal_call_name #gen_ref)53 }54 }5556 fn expand_interface_id(&self) -> proc_macro2::TokenStream {57 let pascal_call_name = &self.pascal_call_name;58 quote! {59 interface_id ^= u32::from_be_bytes(#pascal_call_name::interface_id());60 }61 }6263 fn expand_supports_interface(64 &self,65 generics: &proc_macro2::TokenStream,66 ) -> proc_macro2::TokenStream {67 let pascal_call_name = &self.pascal_call_name;68 let condition = self.condition.as_ref().map(|condition| {69 quote! {70 (#condition) &&71 }72 });73 quote! {74 #condition <#pascal_call_name #generics>::supports_interface(this, interface_id)75 }76 }7778 fn expand_variant_weight(&self) -> proc_macro2::TokenStream {79 let name = &self.name;80 quote! {81 Self::#name(call) => call.weight()82 }83 }8485 fn expand_variant_call(86 &self,87 call_name: &proc_macro2::Ident,88 generics: &proc_macro2::TokenStream,89 ) -> proc_macro2::TokenStream {90 let name = &self.name;91 let pascal_call_name = &self.pascal_call_name;92 let via_typ = self93 .via94 .as_ref()95 .map(|(t, _)| quote! {#t})96 .unwrap_or_else(|| quote! {Self});97 let via_map = self98 .via99 .as_ref()100 .map(|(_, i)| quote! {.#i()})101 .unwrap_or_default();102 let condition = self.condition.as_ref().map(|condition| {103 quote! {104 if ({let this = &self; (#condition)})105 }106 });107 quote! {108 #call_name::#name(call) #condition => return <#via_typ as ::evm_coder::Callable<#pascal_call_name #generics>>::call(self #via_map, Msg {109 call,110 caller: c.caller,111 value: c.value,112 })113 }114 }115116 fn expand_parse(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {117 let name = &self.name;118 let pascal_call_name = &self.pascal_call_name;119 quote! {120 if let Some(parsed_call) = <#pascal_call_name #generics>::parse(method_id, reader)? {121 return Ok(Some(Self::#name(parsed_call)))122 }123 }124 }125126 fn expand_generator(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {127 let pascal_call_name = &self.pascal_call_name;128 quote! {129 <#pascal_call_name #generics>::generate_solidity_interface(tc, is_impl);130 }131 }132133 fn expand_event_generator(&self) -> proc_macro2::TokenStream {134 let name = &self.name;135 quote! {136 #name::generate_solidity_interface(tc, is_impl);137 }138 }139}140141#[derive(Default)]142struct IsList(Vec<Is>);143impl Parse for IsList {144 fn parse(input: ParseStream) -> syn::Result<Self> {145 let mut out = vec![];146 loop {147 if input.is_empty() {148 break;149 }150 let name = input.parse::<Ident>()?;151 let lookahead = input.lookahead1();152153 let mut condition: Option<Expr> = None;154 let mut via: Option<(Type, Ident)> = None;155156 if lookahead.peek(syn::token::Paren) {157 let contents;158 parenthesized!(contents in input);159 let input = contents;160161 while !input.is_empty() {162 let lookahead = input.lookahead1();163 if lookahead.peek(Token![if]) {164 input.parse::<Token![if]>()?;165 let contents;166 parenthesized!(contents in input);167 let contents = contents.parse::<Expr>()?;168169 if condition.replace(contents).is_some() {170 return Err(syn::Error::new(input.span(), "condition is already set"));171 }172 } else if lookahead.peek(kw::via) {173 input.parse::<kw::via>()?;174 let contents;175 parenthesized!(contents in input);176177 let method = contents.parse::<Ident>()?;178 contents.parse::<kw::returns>()?;179 let ty = contents.parse::<Type>()?;180181 if via.replace((ty, method)).is_some() {182 return Err(syn::Error::new(input.span(), "via is already set"));183 }184 } else {185 return Err(lookahead.error());186 }187188 if input.peek(Token![,]) {189 input.parse::<Token![,]>()?;190 } else if !input.is_empty() {191 return Err(syn::Error::new(input.span(), "expected end"));192 }193 }194 } else if lookahead.peek(Token![,]) || input.is_empty() {195 196 } else {197 return Err(lookahead.error());198 };199 out.push(Is {200 pascal_call_name: pascal_ident_to_call(&name),201 snake_call_name: pascal_ident_to_snake_call(&name),202 name,203 via,204 condition,205 });206 if input.peek(Token![,]) {207 input.parse::<Token![,]>()?;208 continue;209 } else {210 break;211 }212 }213 Ok(Self(out))214 }215}216217pub struct InterfaceInfo {218 name: Ident,219 is: IsList,220 inline_is: IsList,221 events: IsList,222 expect_selector: Option<u32>,223}224impl Parse for InterfaceInfo {225 fn parse(input: ParseStream) -> syn::Result<Self> {226 let mut name = None;227 let mut is = None;228 let mut inline_is = None;229 let mut events = None;230 let mut expect_selector = None;231 232 loop {233 let lookahead = input.lookahead1();234 if lookahead.peek(kw::name) {235 let k = input.parse::<kw::name>()?;236 input.parse::<Token![=]>()?;237 if name.replace(input.parse::<Ident>()?).is_some() {238 return Err(syn::Error::new(k.span(), "name is already set"));239 }240 } else if lookahead.peek(kw::is) {241 let k = input.parse::<kw::is>()?;242 let contents;243 parenthesized!(contents in input);244 if is.replace(contents.parse::<IsList>()?).is_some() {245 return Err(syn::Error::new(k.span(), "is is already set"));246 }247 } else if lookahead.peek(kw::inline_is) {248 let k = input.parse::<kw::inline_is>()?;249 let contents;250 parenthesized!(contents in input);251 if inline_is.replace(contents.parse::<IsList>()?).is_some() {252 return Err(syn::Error::new(k.span(), "inline_is is already set"));253 }254 } else if lookahead.peek(kw::events) {255 let k = input.parse::<kw::events>()?;256 let contents;257 parenthesized!(contents in input);258 if events.replace(contents.parse::<IsList>()?).is_some() {259 return Err(syn::Error::new(k.span(), "events is already set"));260 }261 } else if lookahead.peek(kw::expect_selector) {262 let k = input.parse::<kw::expect_selector>()?;263 input.parse::<Token![=]>()?;264 let value = input.parse::<LitInt>()?;265 if expect_selector266 .replace(value.base10_parse::<u32>()?)267 .is_some()268 {269 return Err(syn::Error::new(k.span(), "expect_selector is already set"));270 }271 } else if input.is_empty() {272 break;273 } else {274 return Err(lookahead.error());275 }276 if input.peek(Token![,]) {277 input.parse::<Token![,]>()?;278 } else {279 break;280 }281 }282 Ok(Self {283 name: name.ok_or_else(|| syn::Error::new(input.span(), "missing name"))?,284 is: is.unwrap_or_default(),285 inline_is: inline_is.unwrap_or_default(),286 events: events.unwrap_or_default(),287 expect_selector,288 })289 }290}291292struct MethodInfo {293 rename_selector: Option<String>,294 hide: bool,295}296impl Parse for MethodInfo {297 fn parse(input: ParseStream) -> syn::Result<Self> {298 let mut rename_selector = None;299 let mut hide = false;300 while !input.is_empty() {301 let lookahead = input.lookahead1();302 if lookahead.peek(kw::rename_selector) {303 let k = input.parse::<kw::rename_selector>()?;304 input.parse::<Token![=]>()?;305 if rename_selector306 .replace(input.parse::<LitStr>()?.value())307 .is_some()308 {309 return Err(syn::Error::new(k.span(), "rename_selector is already set"));310 }311 } else if lookahead.peek(kw::hide) {312 input.parse::<kw::hide>()?;313 hide = true;314 } else {315 return Err(lookahead.error());316 }317318 if input.peek(Token![,]) {319 input.parse::<Token![,]>()?;320 } else if !input.is_empty() {321 return Err(syn::Error::new(input.span(), "expected end"));322 }323 }324 Ok(Self {325 rename_selector,326 hide,327 })328 }329}330331trait AbiType {332 fn plain(&self) -> syn::Result<&Ident>;333 fn is_value(&self) -> bool;334 fn is_caller(&self) -> bool;335 fn is_special(&self) -> bool;336}337338impl AbiType for Type {339 fn plain(&self) -> syn::Result<&Ident> {340 let path = parse_path(self)?;341 let segment = parse_path_segment(path)?;342 if !segment.arguments.is_empty() {343 return Err(syn::Error::new(self.span(), "Not plain type"));344 }345 Ok(&segment.ident)346 }347348 fn is_value(&self) -> bool {349 if let Ok(ident) = self.plain() {350 return ident == "value";351 }352 false353 }354355 fn is_caller(&self) -> bool {356 if let Ok(ident) = self.plain() {357 return ident == "caller";358 }359 false360 }361362 fn is_special(&self) -> bool {363 self.is_caller() || self.is_value()364 }365}366367#[derive(Debug)]368struct MethodArg {369 name: Ident,370 camel_name: String,371 ty: Type,372}373impl MethodArg {374 fn try_from(value: &PatType) -> syn::Result<Self> {375 let name = parse_ident_from_pat(&value.pat)?.clone();376 Ok(Self {377 camel_name: cases::camelcase::to_camel_case(&name.to_string()),378 name,379 ty: value.ty.as_ref().clone(),380 })381 }382 fn is_value(&self) -> bool {383 self.ty.is_value()384 }385 fn is_caller(&self) -> bool {386 self.ty.is_caller()387 }388 fn is_special(&self) -> bool {389 self.ty.is_special()390 }391392 fn expand_call_def(&self) -> proc_macro2::TokenStream {393 assert!(!self.is_special());394 let name = &self.name;395 let ty = &self.ty;396397 quote! {398 #name: #ty399 }400 }401402 fn expand_parse(&self) -> proc_macro2::TokenStream {403 assert!(!self.is_special());404 let name = &self.name;405 let ty = &self.ty;406 quote! {407 #name: <#ty>::abi_read(reader)?408 }409 }410411 fn expand_call_arg(&self) -> proc_macro2::TokenStream {412 if self.is_value() {413 quote! {414 c.value.clone()415 }416 } else if self.is_caller() {417 quote! {418 c.caller.clone()419 }420 } else {421 let name = &self.name;422 quote! {423 #name424 }425 }426 }427428 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {429 let camel_name = &self.camel_name.to_string();430 let ty = &self.ty;431 quote! {432 <NamedArgument<#ty>>::new(#camel_name)433 }434 }435}436437#[derive(PartialEq)]438enum Mutability {439 Mutable,440 View,441 Pure,442}443444445446mod kw {447 syn::custom_keyword!(weight);448449 syn::custom_keyword!(via);450 syn::custom_keyword!(returns);451 syn::custom_keyword!(name);452 syn::custom_keyword!(is);453 syn::custom_keyword!(inline_is);454 syn::custom_keyword!(events);455 syn::custom_keyword!(expect_selector);456457 syn::custom_keyword!(rename_selector);458 syn::custom_keyword!(hide);459}460461462struct Method {463 name: Ident,464 camel_name: String,465 pascal_name: Ident,466 screaming_name: Ident,467 hide: bool,468 args: Vec<MethodArg>,469 has_normal_args: bool,470 has_value_args: bool,471 mutability: Mutability,472 result: Type,473 weight: Option<Expr>,474 docs: Vec<String>,475}476impl Method {477 fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {478 let mut info = MethodInfo {479 rename_selector: None,480 hide: false,481 };482 let mut docs = Vec::new();483 let mut weight = None;484 for attr in &value.attrs {485 let ident = parse_ident_from_path(&attr.path, false)?;486 if ident == "solidity" {487 info = attr.parse_args::<MethodInfo>()?;488 } else if ident == "doc" {489 let args = attr.parse_meta().unwrap();490 let value = match args {491 Meta::NameValue(MetaNameValue {492 lit: Lit::Str(str), ..493 }) => str.value(),494 _ => unreachable!(),495 };496 docs.push(value);497 } else if ident == "weight" {498 weight = Some(attr.parse_args::<Expr>()?);499 }500 }501 let ident = &value.sig.ident;502 let ident_str = ident.to_string();503 if !cases::snakecase::is_snake_case(&ident_str) {504 return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute"));505 }506507 let mut mutability = Mutability::Pure;508509 if let Some(FnArg::Receiver(receiver)) = value510 .sig511 .inputs512 .iter()513 .find(|arg| matches!(arg, FnArg::Receiver(_)))514 {515 if receiver.reference.is_none() {516 return Err(syn::Error::new(517 receiver.span(),518 "receiver should be by ref",519 ));520 }521 if receiver.mutability.is_some() {522 mutability = Mutability::Mutable;523 } else {524 mutability = Mutability::View;525 }526 }527 let mut args = Vec::new();528 for typ in value529 .sig530 .inputs531 .iter()532 .filter(|arg| matches!(arg, FnArg::Typed(_)))533 {534 let typ = match typ {535 FnArg::Typed(typ) => typ,536 _ => unreachable!(),537 };538 args.push(MethodArg::try_from(typ)?);539 }540541 if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) {542 return Err(syn::Error::new(543 args.iter().find(|arg| arg.is_value()).unwrap().ty.span(),544 "payable function should be mutable",545 ));546 }547548 let result = match &value.sig.output {549 ReturnType::Type(_, ty) => ty,550 _ => 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)")),551 };552 let result = parse_result_ok(result)?;553554 let camel_name = info555 .rename_selector556 .unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));557 let has_normal_args = args.iter().filter(|arg| !arg.is_special()).count() != 0;558 let has_value_args = args.iter().any(|a| a.is_value());559560 Ok(Self {561 name: ident.clone(),562 camel_name,563 pascal_name: snake_ident_to_pascal(ident),564 screaming_name: snake_ident_to_screaming(ident),565 hide: info.hide,566 args,567 has_normal_args,568 has_value_args,569 mutability,570 result: result.clone(),571 weight,572 docs,573 })574 }575 fn expand_call_def(&self) -> proc_macro2::TokenStream {576 let defs = self577 .args578 .iter()579 .filter(|a| !a.is_special())580 .map(|a| a.expand_call_def());581 let pascal_name = &self.pascal_name;582 let docs = &self.docs;583584 if self.has_normal_args {585 quote! {586 #(#[doc = #docs])*587 #[allow(missing_docs)]588 #pascal_name {589 #(590 #defs,591 )*592 }593 }594 } else {595 quote! {#pascal_name}596 }597 }598599 fn expand_const(&self) -> proc_macro2::TokenStream {600 let screaming_name = &self.screaming_name;601 let screaming_name_signature = format_ident!("{}_SIGNATURE", &self.screaming_name);602 let custom_signature = self.expand_custom_signature();603 quote! {604 const #screaming_name_signature: ::evm_coder::custom_signature::FunctionSignature = #custom_signature;605 const #screaming_name: ::evm_coder::types::bytes4 = {606 let mut sum = ::evm_coder::sha3_const::Keccak256::new();607 let mut pos = 0;608 while pos < Self::#screaming_name_signature.unit.len {609 sum = sum.update(&[Self::#screaming_name_signature.unit.data[pos]; 1]);610 pos += 1;611 }612 let a = sum.finalize();613 [a[0], a[1], a[2], a[3]]614 };615 }616 }617618 fn expand_interface_id(&self) -> proc_macro2::TokenStream {619 let screaming_name = &self.screaming_name;620 quote! {621 interface_id ^= u32::from_be_bytes(Self::#screaming_name);622 }623 }624625 fn expand_parse(&self) -> proc_macro2::TokenStream {626 let pascal_name = &self.pascal_name;627 let screaming_name = &self.screaming_name;628 if self.has_normal_args {629 let parsers = self630 .args631 .iter()632 .filter(|a| !a.is_special())633 .map(|a| a.expand_parse());634 quote! {635 Self::#screaming_name => return Ok(Some(Self::#pascal_name {636 #(637 #parsers,638 )*639 }))640 }641 } else {642 quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }643 }644 }645646 fn expand_variant_call(&self, call_name: &proc_macro2::Ident) -> proc_macro2::TokenStream {647 let pascal_name = &self.pascal_name;648 let name = &self.name;649650 let matcher = if self.has_normal_args {651 let names = self652 .args653 .iter()654 .filter(|a| !a.is_special())655 .map(|a| &a.name);656657 quote! {{658 #(659 #names,660 )*661 }}662 } else {663 quote! {}664 };665666 let receiver = match self.mutability {667 Mutability::Mutable | Mutability::View => quote! {self.},668 Mutability::Pure => quote! {Self::},669 };670 let args = self.args.iter().map(|a| a.expand_call_arg());671672 quote! {673 #call_name::#pascal_name #matcher => {674 let result = #receiver #name(675 #(676 #args,677 )*678 )?;679 (&result).to_result()680 }681 }682 }683684 fn expand_variant_weight(&self) -> proc_macro2::TokenStream {685 let pascal_name = &self.pascal_name;686 if let Some(weight) = &self.weight {687 let matcher = if self.has_normal_args {688 let names = self689 .args690 .iter()691 .filter(|a| !a.is_special())692 .map(|a| &a.name);693694 quote! {{695 #(696 #names,697 )*698 }}699 } else {700 quote! {}701 };702 quote! {703 Self::#pascal_name #matcher => (#weight).into()704 }705 } else {706 let matcher = if self.has_normal_args {707 quote! {{..}}708 } else {709 quote! {}710 };711 quote! {712 Self::#pascal_name #matcher => ().into()713 }714 }715 }716717 fn expand_type(ty: &Type, token_stream: &mut proc_macro2::TokenStream, read_signature: bool) {718 match ty {719 Type::Path(tp) => {720 if let Some(qself) = &tp.qself {721 panic!("no receiver expected {:?}", qself.ty.span());722 }723 let path = &tp.path;724 if path.segments.len() != 1 {725 panic!("expected path to have only one segment {:?}", path.span());726 }727 let last_segment = path.segments.last().unwrap();728729 if last_segment.ident == "Vec" {730 let args = match &last_segment.arguments {731 PathArguments::AngleBracketed(e) => e,732 _ => {733 panic!("missing Vec generic {:?}", last_segment.arguments.span());734 }735 };736 let args = &args.args;737 if args.len() != 1 {738 panic!("expected only one generic for vec {:?}", args.span());739 }740 let arg = args.first().expect("first arg");741742 let ty = match arg {743 GenericArgument::Type(ty) => ty,744 _ => {745 panic!("expected first generic to be type {:?}", arg.span());746 }747 };748749 let mut vec_token = proc_macro2::TokenStream::new();750 Self::expand_type(ty, &mut vec_token, false);751 vec_token = if read_signature {752 quote! { (<Vec<#vec_token>>::SIGNATURE) }753 } else {754 quote! { <Vec<#vec_token>> }755 };756 token_stream.extend(vec_token);757 } else {758 if !last_segment.arguments.is_empty() {759 panic!(760 "unexpected generic arguments for non-vec type {:?}",761 last_segment.arguments.span()762 );763 }764765 let ident = &last_segment.ident;766 let plain_token = if read_signature {767 quote! {768 (<#ident>::SIGNATURE)769 }770 } else {771 quote! {772 #ident773 }774 };775776 token_stream.extend(plain_token);777 }778 }779780 Type::Tuple(tt) => {781 782 783 784785 let mut tuple_types = proc_macro2::TokenStream::new();786 let mut is_first = true;787788 for ty in tt.elems.iter() {789 if is_first {790 is_first = false791 } else {792 tuple_types.extend(quote!(,));793 }794 Self::expand_type(ty, &mut tuple_types, false);795 }796 tuple_types = if read_signature {797 quote! { (<(#tuple_types)>::SIGNATURE) }798 } else {799 quote! { (#tuple_types) }800 };801 token_stream.extend(tuple_types);802 }803804 805 806 807 808 809 810 811 812 813 814 815 816 817 _ => panic!("Unexpected type {ty:?}"),818 }819 820 821 822 823 824 825 826 827 828 829 830831 832 833834 835 836 837838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853854 855 856 857 858 859 860 861 862 863 864865 866 867 }868869 fn expand_custom_signature(&self) -> proc_macro2::TokenStream {870 let mut token_stream = TokenStream::new();871872 let mut is_first = true;873 for arg in &self.args {874 if arg.is_special() {875 continue;876 }877878 if is_first {879 is_first = false;880 } else {881 token_stream.extend(quote!(,));882 }883 Self::expand_type(&arg.ty, &mut token_stream, true);884 }885886 if !is_first {887 token_stream.extend(quote!(,));888 }889890 let func_name = self.camel_name.clone();891 let func_name = quote!(SignaturePreferences {892 open_name: Some(SignatureUnit::new(#func_name)),893 open_delimiter: Some(SignatureUnit::new("(")),894 param_delimiter: Some(SignatureUnit::new(",")),895 close_delimiter: Some(SignatureUnit::new(")")),896 close_name: None,897 });898 quote!({ ::evm_coder::make_signature!(new fn(#func_name), #token_stream) })899 }900901 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {902 let camel_name = &self.camel_name;903 let mutability = match self.mutability {904 Mutability::Mutable => quote! {SolidityMutability::Mutable},905 Mutability::View => quote! { SolidityMutability::View },906 Mutability::Pure => quote! {SolidityMutability::Pure},907 };908 let result = &self.result;909910 let args = self911 .args912 .iter()913 .filter(|a| !a.is_special())914 .map(MethodArg::expand_solidity_argument);915 let docs = &self.docs;916 let screaming_name = &self.screaming_name;917 let hide = self.hide;918 let custom_signature = self.expand_custom_signature();919 let custom_signature = quote!(920 {921 const cs: FunctionSignature = #custom_signature;922 cs923 }924 );925 let is_payable = self.has_value_args;926927 quote! {928 SolidityFunction {929 docs: &[#(#docs),*],930 hide: #hide,931 selector: u32::from_be_bytes(Self::#screaming_name),932 custom_signature: #custom_signature,933 name: #camel_name,934 mutability: #mutability,935 is_payable: #is_payable,936 args: (937 #(938 #args,939 )*940 ),941 result: <UnnamedArgument<#result>>::default(),942 }943 }944 }945}946947fn generics_list(gen: &Generics) -> proc_macro2::TokenStream {948 if gen.params.is_empty() {949 return quote! {};950 }951 let params = gen.params.iter().map(|p| match p {952 syn::GenericParam::Type(id) => {953 let v = &id.ident;954 quote! {#v}955 }956 syn::GenericParam::Lifetime(lt) => {957 let v = <.lifetime;958 quote! {#v}959 }960 syn::GenericParam::Const(c) => {961 let i = &c.ident;962 quote! {#i}963 }964 });965 quote! { #(#params),* }966}967fn generics_reference(gen: &Generics) -> proc_macro2::TokenStream {968 if gen.params.is_empty() {969 return quote! {};970 }971 let list = generics_list(gen);972 quote! { <#list> }973}974fn generics_data(gen: &Generics) -> proc_macro2::TokenStream {975 let list = generics_list(gen);976 if gen.params.len() == 1 {977 quote! {#list}978 } else {979 quote! { (#list) }980 }981}982983pub struct SolidityInterface {984 generics: Generics,985 name: Box<syn::Type>,986 info: InterfaceInfo,987 methods: Vec<Method>,988 docs: Vec<String>,989}990impl SolidityInterface {991 pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {992 let mut methods = Vec::new();993994 for item in &value.items {995 if let ImplItem::Method(method) = item {996 methods.push(Method::try_from(method)?)997 }998 }999 let mut docs = vec![];1000 for attr in &value.attrs {1001 let ident = parse_ident_from_path(&attr.path, false)?;1002 if ident == "doc" {1003 let args = attr.parse_meta().unwrap();1004 let value = match args {1005 Meta::NameValue(MetaNameValue {1006 lit: Lit::Str(str), ..1007 }) => str.value(),1008 _ => unreachable!(),1009 };1010 docs.push(value);1011 }1012 }1013 Ok(Self {1014 generics: value.generics.clone(),1015 name: value.self_ty.clone(),1016 info,1017 methods,1018 docs,1019 })1020 }1021 pub fn expand(self) -> proc_macro2::TokenStream {1022 let name = self.name;10231024 let solidity_name = self.info.name.to_string();1025 let call_name = pascal_ident_to_call(&self.info.name);1026 let generics = self.generics;1027 let gen_ref = generics_reference(&generics);1028 let gen_data = generics_data(&generics);1029 let gen_where = &generics.where_clause;10301031 let call_sub = self1032 .info1033 .inline_is1034 .01035 .iter()1036 .chain(self.info.is.0.iter())1037 .map(|c| Is::expand_call_def(c, &gen_ref));1038 let call_parse = self1039 .info1040 .inline_is1041 .01042 .iter()1043 .chain(self.info.is.0.iter())1044 .map(|is| Is::expand_parse(is, &gen_ref));1045 let call_variants = self1046 .info1047 .inline_is1048 .01049 .iter()1050 .chain(self.info.is.0.iter())1051 .map(|c| Is::expand_variant_call(c, &call_name, &gen_ref));1052 let weight_variants = self1053 .info1054 .inline_is1055 .01056 .iter()1057 .chain(self.info.is.0.iter())1058 .map(Is::expand_variant_weight);10591060 let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);1061 let supports_interface = self1062 .info1063 .is1064 .01065 .iter()1066 .map(|is| Is::expand_supports_interface(is, &gen_ref));10671068 let calls = self.methods.iter().map(Method::expand_call_def);1069 let consts = self.methods.iter().map(Method::expand_const);1070 let interface_id = self.methods.iter().map(Method::expand_interface_id);1071 let parsers = self.methods.iter().map(Method::expand_parse);1072 let call_variants_this = self1073 .methods1074 .iter()1075 .map(|m| Method::expand_variant_call(m, &call_name));1076 let weight_variants_this = self.methods.iter().map(Method::expand_variant_weight);1077 let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);10781079 1080 let solidity_is = self1081 .info1082 .is1083 .01084 .iter()1085 .chain(self.info.inline_is.0.iter())1086 .map(|is| is.name.to_string());1087 let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string());1088 let solidity_generators = self1089 .info1090 .is1091 .01092 .iter()1093 .chain(self.info.inline_is.0.iter())1094 .map(|is| Is::expand_generator(is, &gen_ref));1095 let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);10961097 let docs = &self.docs;10981099 quote! {1100 #[derive(Debug)]1101 #(#[doc = #docs])*1102 pub enum #call_name #gen_ref {1103 1104 ERC165Call(::evm_coder::ERC165Call, ::core::marker::PhantomData<#gen_data>),1105 #(1106 #calls,1107 )*1108 #(1109 #call_sub,1110 )*1111 }1112 impl #gen_ref #call_name #gen_ref {1113 #(1114 #consts1115 )*1116 1117 pub fn interface_id() -> ::evm_coder::types::bytes4 {1118 let mut interface_id = 0;1119 #(#interface_id)*1120 #(#inline_interface_id)*1121 u32::to_be_bytes(interface_id)1122 }1123 1124 pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {1125 use evm_coder::solidity::*;1126 use core::fmt::Write;1127 let interface = SolidityInterface {1128 docs: &[#(#docs),*],1129 name: #solidity_name,1130 selector: Self::interface_id(),1131 is: &["Dummy", "ERC165", #(1132 #solidity_is,1133 )* #(1134 #solidity_events_is,1135 )* ],1136 functions: (#(1137 #solidity_functions,1138 )*),1139 };11401141 let mut out = ::evm_coder::types::string::new();1142 if #solidity_name.starts_with("Inline") {1143 out.push_str("/// @dev inlined interface\n");1144 }1145 let _ = interface.format(is_impl, &mut out, tc);1146 tc.collect(out);1147 #(1148 #solidity_event_generators1149 )*1150 #(1151 #solidity_generators1152 )*1153 if is_impl {1154 tc.collect("/// @dev common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\ncontract ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool) {\n\t\trequire(false, stub_error);\n\t\tinterfaceID;\n\t\treturn true;\n\t}\n}\n".into());1155 } else {1156 tc.collect("/// @dev common stubs holder\ninterface Dummy {\n}\ninterface ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);\n}\n".into());1157 }1158 }1159 }1160 impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {1161 fn parse(method_id: ::evm_coder::types::bytes4, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {1162 use ::evm_coder::abi::AbiRead;1163 match method_id {1164 ::evm_coder::ERC165Call::INTERFACE_ID => return Ok(1165 ::evm_coder::ERC165Call::parse(method_id, reader)?1166 .map(|c| Self::ERC165Call(c, ::core::marker::PhantomData))1167 ),1168 #(1169 #parsers,1170 )*1171 _ => {},1172 }1173 #(1174 #call_parse1175 )else*1176 return Ok(None);1177 }1178 }1179 impl #generics #call_name #gen_ref1180 #gen_where1181 {1182 1183 pub fn supports_interface(this: &#name, interface_id: ::evm_coder::types::bytes4) -> bool {1184 interface_id != u32::to_be_bytes(0xffffff) && (1185 interface_id == ::evm_coder::ERC165Call::INTERFACE_ID ||1186 interface_id == Self::interface_id()1187 #(1188 || #supports_interface1189 )*1190 )1191 }1192 }1193 impl #generics ::evm_coder::Weighted for #call_name #gen_ref1194 #gen_where1195 {1196 #[allow(unused_variables)]1197 fn weight(&self) -> ::evm_coder::execution::DispatchInfo {1198 match self {1199 #(1200 #weight_variants,1201 )*1202 1203 Self::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {..}, _) => ::frame_support::weights::Weight::from_ref_time(100).into(),1204 #(1205 #weight_variants_this,1206 )*1207 }1208 }1209 }1210 impl #generics ::evm_coder::Callable<#call_name #gen_ref> for #name1211 #gen_where1212 {1213 #[allow(unreachable_code)] 1214 fn call(&mut self, c: Msg<#call_name #gen_ref>) -> ::evm_coder::execution::ResultWithPostInfo<::evm_coder::abi::AbiWriter> {1215 use ::evm_coder::abi::AbiWrite;1216 match c.call {1217 #(1218 #call_variants,1219 )*1220 #call_name::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {interface_id}, _) => {1221 let mut writer = ::evm_coder::abi::AbiWriter::default();1222 writer.bool(&<#call_name #gen_ref>::supports_interface(self, interface_id));1223 return Ok(writer.into());1224 }1225 _ => {},1226 }1227 let mut writer = ::evm_coder::abi::AbiWriter::default();1228 match c.call {1229 #(1230 #call_variants_this,1231 )*1232 _ => Err(::evm_coder::execution::Error::from("method is not available").into()),1233 }1234 }1235 }1236 }1237 }1238}