1234567891011121314151617#![allow(dead_code)]181920212223use proc_macro2::TokenStream;24use quote::{quote, ToTokens, format_ident};25use inflector::cases;26use std::fmt::Write;27use syn::{28 Expr, FnArg, GenericArgument, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta,29 MetaNameValue, PatType, PathArguments, ReturnType, Type,30 spanned::Spanned,31 parse::{Parse, ParseStream},32 parenthesized, Token, LitInt, LitStr,33};3435use crate::{36 fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_path, parse_path_segment,37 parse_result_ok, pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal,38 snake_ident_to_screaming,39};4041struct Is {42 name: Ident,43 pascal_call_name: Ident,44 snake_call_name: Ident,45 via: Option<(Type, Ident)>,46 condition: Option<Expr>,47}48impl Is {49 fn expand_call_def(&self, gen_ref: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {50 let name = &self.name;51 let pascal_call_name = &self.pascal_call_name;52 quote! {53 #name(#pascal_call_name #gen_ref)54 }55 }5657 fn expand_interface_id(&self) -> proc_macro2::TokenStream {58 let pascal_call_name = &self.pascal_call_name;59 quote! {60 interface_id ^= u32::from_be_bytes(#pascal_call_name::interface_id());61 }62 }6364 fn expand_supports_interface(65 &self,66 generics: &proc_macro2::TokenStream,67 ) -> proc_macro2::TokenStream {68 let pascal_call_name = &self.pascal_call_name;69 let condition = self.condition.as_ref().map(|condition| {70 quote! {71 (#condition) &&72 }73 });74 quote! {75 #condition <#pascal_call_name #generics>::supports_interface(this, interface_id)76 }77 }7879 fn expand_variant_weight(&self) -> proc_macro2::TokenStream {80 let name = &self.name;81 quote! {82 Self::#name(call) => call.weight()83 }84 }8586 fn expand_variant_call(87 &self,88 call_name: &proc_macro2::Ident,89 generics: &proc_macro2::TokenStream,90 ) -> proc_macro2::TokenStream {91 let name = &self.name;92 let pascal_call_name = &self.pascal_call_name;93 let via_typ = self94 .via95 .as_ref()96 .map(|(t, _)| quote! {#t})97 .unwrap_or_else(|| quote! {Self});98 let via_map = self99 .via100 .as_ref()101 .map(|(_, i)| quote! {.#i()})102 .unwrap_or_default();103 let condition = self.condition.as_ref().map(|condition| {104 quote! {105 if ({let this = &self; (#condition)})106 }107 });108 quote! {109 #call_name::#name(call) #condition => return <#via_typ as ::evm_coder::Callable<#pascal_call_name #generics>>::call(self #via_map, Msg {110 call,111 caller: c.caller,112 value: c.value,113 })114 }115 }116117 fn expand_parse(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {118 let name = &self.name;119 let pascal_call_name = &self.pascal_call_name;120 quote! {121 if let Some(parsed_call) = <#pascal_call_name #generics>::parse(method_id, reader)? {122 return Ok(Some(Self::#name(parsed_call)))123 }124 }125 }126127 fn expand_generator(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {128 let pascal_call_name = &self.pascal_call_name;129 quote! {130 <#pascal_call_name #generics>::generate_solidity_interface(tc, is_impl);131 }132 }133134 fn expand_event_generator(&self) -> proc_macro2::TokenStream {135 let name = &self.name;136 quote! {137 #name::generate_solidity_interface(tc, is_impl);138 }139 }140}141142#[derive(Default)]143struct IsList(Vec<Is>);144impl Parse for IsList {145 fn parse(input: ParseStream) -> syn::Result<Self> {146 let mut out = vec![];147 loop {148 if input.is_empty() {149 break;150 }151 let name = input.parse::<Ident>()?;152 let lookahead = input.lookahead1();153154 let mut condition: Option<Expr> = None;155 let mut via: Option<(Type, Ident)> = None;156157 if lookahead.peek(syn::token::Paren) {158 let contents;159 parenthesized!(contents in input);160 let input = contents;161162 while !input.is_empty() {163 let lookahead = input.lookahead1();164 if lookahead.peek(Token![if]) {165 input.parse::<Token![if]>()?;166 let contents;167 parenthesized!(contents in input);168 let contents = contents.parse::<Expr>()?;169170 if condition.replace(contents).is_some() {171 return Err(syn::Error::new(input.span(), "condition is already set"));172 }173 } else if lookahead.peek(kw::via) {174 input.parse::<kw::via>()?;175 let contents;176 parenthesized!(contents in input);177178 let method = contents.parse::<Ident>()?;179 contents.parse::<kw::returns>()?;180 let ty = contents.parse::<Type>()?;181182 if via.replace((ty, method)).is_some() {183 return Err(syn::Error::new(input.span(), "via is already set"));184 }185 } else {186 return Err(lookahead.error());187 }188189 if input.peek(Token![,]) {190 input.parse::<Token![,]>()?;191 } else if !input.is_empty() {192 return Err(syn::Error::new(input.span(), "expected end"));193 }194 }195 } else if lookahead.peek(Token![,]) || input.is_empty() {196 197 } else {198 return Err(lookahead.error());199 };200 out.push(Is {201 pascal_call_name: pascal_ident_to_call(&name),202 snake_call_name: pascal_ident_to_snake_call(&name),203 name,204 via,205 condition,206 });207 if input.peek(Token![,]) {208 input.parse::<Token![,]>()?;209 continue;210 } else {211 break;212 }213 }214 Ok(Self(out))215 }216}217218pub struct InterfaceInfo {219 name: Ident,220 is: IsList,221 inline_is: IsList,222 events: IsList,223 expect_selector: Option<u32>,224}225impl Parse for InterfaceInfo {226 fn parse(input: ParseStream) -> syn::Result<Self> {227 let mut name = None;228 let mut is = None;229 let mut inline_is = None;230 let mut events = None;231 let mut expect_selector = None;232 233 loop {234 let lookahead = input.lookahead1();235 if lookahead.peek(kw::name) {236 let k = input.parse::<kw::name>()?;237 input.parse::<Token![=]>()?;238 if name.replace(input.parse::<Ident>()?).is_some() {239 return Err(syn::Error::new(k.span(), "name is already set"));240 }241 } else if lookahead.peek(kw::is) {242 let k = input.parse::<kw::is>()?;243 let contents;244 parenthesized!(contents in input);245 if is.replace(contents.parse::<IsList>()?).is_some() {246 return Err(syn::Error::new(k.span(), "is is already set"));247 }248 } else if lookahead.peek(kw::inline_is) {249 let k = input.parse::<kw::inline_is>()?;250 let contents;251 parenthesized!(contents in input);252 if inline_is.replace(contents.parse::<IsList>()?).is_some() {253 return Err(syn::Error::new(k.span(), "inline_is is already set"));254 }255 } else if lookahead.peek(kw::events) {256 let k = input.parse::<kw::events>()?;257 let contents;258 parenthesized!(contents in input);259 if events.replace(contents.parse::<IsList>()?).is_some() {260 return Err(syn::Error::new(k.span(), "events is already set"));261 }262 } else if lookahead.peek(kw::expect_selector) {263 let k = input.parse::<kw::expect_selector>()?;264 input.parse::<Token![=]>()?;265 let value = input.parse::<LitInt>()?;266 if expect_selector267 .replace(value.base10_parse::<u32>()?)268 .is_some()269 {270 return Err(syn::Error::new(k.span(), "expect_selector is already set"));271 }272 } else if input.is_empty() {273 break;274 } else {275 return Err(lookahead.error());276 }277 if input.peek(Token![,]) {278 input.parse::<Token![,]>()?;279 } else {280 break;281 }282 }283 Ok(Self {284 name: name.ok_or_else(|| syn::Error::new(input.span(), "missing name"))?,285 is: is.unwrap_or_default(),286 inline_is: inline_is.unwrap_or_default(),287 events: events.unwrap_or_default(),288 expect_selector,289 })290 }291}292293struct MethodInfo {294 rename_selector: Option<String>,295 hide: bool,296}297impl Parse for MethodInfo {298 fn parse(input: ParseStream) -> syn::Result<Self> {299 let mut rename_selector = None;300 let mut hide = false;301 while !input.is_empty() {302 let lookahead = input.lookahead1();303 if lookahead.peek(kw::rename_selector) {304 let k = input.parse::<kw::rename_selector>()?;305 input.parse::<Token![=]>()?;306 if rename_selector307 .replace(input.parse::<LitStr>()?.value())308 .is_some()309 {310 return Err(syn::Error::new(k.span(), "rename_selector is already set"));311 }312 } else if lookahead.peek(kw::hide) {313 input.parse::<kw::hide>()?;314 hide = true;315 } else {316 return Err(lookahead.error());317 }318319 if input.peek(Token![,]) {320 input.parse::<Token![,]>()?;321 } else if !input.is_empty() {322 return Err(syn::Error::new(input.span(), "expected end"));323 }324 }325 Ok(Self {326 rename_selector,327 hide,328 })329 }330}331332trait AbiType {333 fn plain(&self) -> syn::Result<&Ident>;334 fn is_value(&self) -> bool;335 fn is_caller(&self) -> bool;336 fn is_special(&self) -> bool;337}338339impl AbiType for Type {340 fn plain(&self) -> syn::Result<&Ident> {341 let path = parse_path(self)?;342 let segment = parse_path_segment(path)?;343 if !segment.arguments.is_empty() {344 return Err(syn::Error::new(self.span(), "Not plain type"));345 }346 Ok(&segment.ident)347 }348349 fn is_value(&self) -> bool {350 if let Ok(ident) = self.plain() {351 return ident == "value";352 }353 false354 }355356 fn is_caller(&self) -> bool {357 if let Ok(ident) = self.plain() {358 return ident == "caller";359 }360 false361 }362363 fn is_special(&self) -> bool {364 self.is_caller() || self.is_value()365 }366}367368#[derive(Debug)]369struct MethodArg {370 name: Ident,371 camel_name: String,372 ty: Type,373}374impl MethodArg {375 fn try_from(value: &PatType) -> syn::Result<Self> {376 let name = parse_ident_from_pat(&value.pat)?.clone();377 Ok(Self {378 camel_name: cases::camelcase::to_camel_case(&name.to_string()),379 name,380 ty: value.ty.as_ref().clone(),381 })382 }383 fn is_value(&self) -> bool {384 self.ty.is_value()385 }386 fn is_caller(&self) -> bool {387 self.ty.is_caller()388 }389 fn is_special(&self) -> bool {390 self.ty.is_special()391 }392393 fn expand_call_def(&self) -> proc_macro2::TokenStream {394 assert!(!self.is_special());395 let name = &self.name;396 let ty = &self.ty;397398 quote! {399 #name: #ty400 }401 }402403 fn expand_parse(&self) -> proc_macro2::TokenStream {404 assert!(!self.is_special());405 let name = &self.name;406 quote! {407 #name: reader.abi_read()?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}