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 let ty = &self.ty;407 quote! {408 #name: <#ty>::abi_read(reader)?409 }410 }411412 fn expand_call_arg(&self) -> proc_macro2::TokenStream {413 if self.is_value() {414 quote! {415 c.value.clone()416 }417 } else if self.is_caller() {418 quote! {419 c.caller.clone()420 }421 } else {422 let name = &self.name;423 quote! {424 #name425 }426 }427 }428429 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {430 let camel_name = &self.camel_name.to_string();431 let ty = &self.ty;432 quote! {433 <NamedArgument<#ty>>::new(#camel_name)434 }435 }436}437438#[derive(PartialEq)]439enum Mutability {440 Mutable,441 View,442 Pure,443}444445446447mod kw {448 syn::custom_keyword!(weight);449450 syn::custom_keyword!(via);451 syn::custom_keyword!(returns);452 syn::custom_keyword!(name);453 syn::custom_keyword!(is);454 syn::custom_keyword!(inline_is);455 syn::custom_keyword!(events);456 syn::custom_keyword!(expect_selector);457458 syn::custom_keyword!(rename_selector);459 syn::custom_keyword!(hide);460}461462463struct Method {464 name: Ident,465 camel_name: String,466 pascal_name: Ident,467 screaming_name: Ident,468 hide: bool,469 args: Vec<MethodArg>,470 has_normal_args: bool,471 has_value_args: bool,472 mutability: Mutability,473 result: Type,474 weight: Option<Expr>,475 docs: Vec<String>,476}477impl Method {478 fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {479 let mut info = MethodInfo {480 rename_selector: None,481 hide: false,482 };483 let mut docs = Vec::new();484 let mut weight = None;485 for attr in &value.attrs {486 let ident = parse_ident_from_path(&attr.path, false)?;487 if ident == "solidity" {488 info = attr.parse_args::<MethodInfo>()?;489 } else if ident == "doc" {490 let args = attr.parse_meta().unwrap();491 let value = match args {492 Meta::NameValue(MetaNameValue {493 lit: Lit::Str(str), ..494 }) => str.value(),495 _ => unreachable!(),496 };497 docs.push(value);498 } else if ident == "weight" {499 weight = Some(attr.parse_args::<Expr>()?);500 }501 }502 let ident = &value.sig.ident;503 let ident_str = ident.to_string();504 if !cases::snakecase::is_snake_case(&ident_str) {505 return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute"));506 }507508 let mut mutability = Mutability::Pure;509510 if let Some(FnArg::Receiver(receiver)) = value511 .sig512 .inputs513 .iter()514 .find(|arg| matches!(arg, FnArg::Receiver(_)))515 {516 if receiver.reference.is_none() {517 return Err(syn::Error::new(518 receiver.span(),519 "receiver should be by ref",520 ));521 }522 if receiver.mutability.is_some() {523 mutability = Mutability::Mutable;524 } else {525 mutability = Mutability::View;526 }527 }528 let mut args = Vec::new();529 for typ in value530 .sig531 .inputs532 .iter()533 .filter(|arg| matches!(arg, FnArg::Typed(_)))534 {535 let typ = match typ {536 FnArg::Typed(typ) => typ,537 _ => unreachable!(),538 };539 args.push(MethodArg::try_from(typ)?);540 }541542 if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) {543 return Err(syn::Error::new(544 args.iter().find(|arg| arg.is_value()).unwrap().ty.span(),545 "payable function should be mutable",546 ));547 }548549 let result = match &value.sig.output {550 ReturnType::Type(_, ty) => ty,551 _ => 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)")),552 };553 let result = parse_result_ok(result)?;554555 let camel_name = info556 .rename_selector557 .unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));558 let has_normal_args = args.iter().filter(|arg| !arg.is_special()).count() != 0;559 let has_value_args = args.iter().any(|a| a.is_value());560561 Ok(Self {562 name: ident.clone(),563 camel_name,564 pascal_name: snake_ident_to_pascal(ident),565 screaming_name: snake_ident_to_screaming(ident),566 hide: info.hide,567 args,568 has_normal_args,569 has_value_args,570 mutability,571 result: result.clone(),572 weight,573 docs,574 })575 }576 fn expand_call_def(&self) -> proc_macro2::TokenStream {577 let defs = self578 .args579 .iter()580 .filter(|a| !a.is_special())581 .map(|a| a.expand_call_def());582 let pascal_name = &self.pascal_name;583 let docs = &self.docs;584585 if self.has_normal_args {586 quote! {587 #(#[doc = #docs])*588 #[allow(missing_docs)]589 #pascal_name {590 #(591 #defs,592 )*593 }594 }595 } else {596 quote! {#pascal_name}597 }598 }599600 fn expand_const(&self) -> proc_macro2::TokenStream {601 let screaming_name = &self.screaming_name;602 let screaming_name_signature = format_ident!("{}_SIGNATURE", &self.screaming_name);603 let custom_signature = self.expand_custom_signature();604 quote! {605 const #screaming_name_signature: ::evm_coder::custom_signature::FunctionSignature = #custom_signature;606 const #screaming_name: ::evm_coder::types::bytes4 = {607 let mut sum = ::evm_coder::sha3_const::Keccak256::new();608 let mut pos = 0;609 while pos < Self::#screaming_name_signature.unit.len {610 sum = sum.update(&[Self::#screaming_name_signature.unit.data[pos]; 1]);611 pos += 1;612 }613 let a = sum.finalize();614 [a[0], a[1], a[2], a[3]]615 };616 }617 }618619 fn expand_interface_id(&self) -> proc_macro2::TokenStream {620 let screaming_name = &self.screaming_name;621 quote! {622 interface_id ^= u32::from_be_bytes(Self::#screaming_name);623 }624 }625626 fn expand_parse(&self) -> proc_macro2::TokenStream {627 let pascal_name = &self.pascal_name;628 let screaming_name = &self.screaming_name;629 if self.has_normal_args {630 let parsers = self631 .args632 .iter()633 .filter(|a| !a.is_special())634 .map(|a| a.expand_parse());635 quote! {636 Self::#screaming_name => return Ok(Some(Self::#pascal_name {637 #(638 #parsers,639 )*640 }))641 }642 } else {643 quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }644 }645 }646647 fn expand_variant_call(&self, call_name: &proc_macro2::Ident) -> proc_macro2::TokenStream {648 let pascal_name = &self.pascal_name;649 let name = &self.name;650651 let matcher = if self.has_normal_args {652 let names = self653 .args654 .iter()655 .filter(|a| !a.is_special())656 .map(|a| &a.name);657658 quote! {{659 #(660 #names,661 )*662 }}663 } else {664 quote! {}665 };666667 let receiver = match self.mutability {668 Mutability::Mutable | Mutability::View => quote! {self.},669 Mutability::Pure => quote! {Self::},670 };671 let args = self.args.iter().map(|a| a.expand_call_arg());672673 quote! {674 #call_name::#pascal_name #matcher => {675 let result = #receiver #name(676 #(677 #args,678 )*679 )?;680 (&result).to_result()681 }682 }683 }684685 fn expand_variant_weight(&self) -> proc_macro2::TokenStream {686 let pascal_name = &self.pascal_name;687 if let Some(weight) = &self.weight {688 let matcher = if self.has_normal_args {689 let names = self690 .args691 .iter()692 .filter(|a| !a.is_special())693 .map(|a| &a.name);694695 quote! {{696 #(697 #names,698 )*699 }}700 } else {701 quote! {}702 };703 quote! {704 Self::#pascal_name #matcher => (#weight).into()705 }706 } else {707 let matcher = if self.has_normal_args {708 quote! {{..}}709 } else {710 quote! {}711 };712 quote! {713 Self::#pascal_name #matcher => ().into()714 }715 }716 }717718 fn expand_type(ty: &Type, token_stream: &mut proc_macro2::TokenStream, read_signature: bool) {719 match ty {720 Type::Path(tp) => {721 if let Some(qself) = &tp.qself {722 panic!("no receiver expected {:?}", qself.ty.span());723 }724 let path = &tp.path;725 if path.segments.len() != 1 {726 panic!("expected path to have only one segment {:?}", path.span());727 }728 let last_segment = path.segments.last().unwrap();729730 if last_segment.ident == "Vec" {731 let args = match &last_segment.arguments {732 PathArguments::AngleBracketed(e) => e,733 _ => {734 panic!("missing Vec generic {:?}", last_segment.arguments.span());735 }736 };737 let args = &args.args;738 if args.len() != 1 {739 panic!("expected only one generic for vec {:?}", args.span());740 }741 let arg = args.first().expect("first arg");742743 let ty = match arg {744 GenericArgument::Type(ty) => ty,745 _ => {746 panic!("expected first generic to be type {:?}", arg.span());747 }748 };749750 let mut vec_token = proc_macro2::TokenStream::new();751 Self::expand_type(ty, &mut vec_token, false);752 vec_token = if read_signature {753 quote! { (<Vec<#vec_token>>::SIGNATURE) }754 } else {755 quote! { <Vec<#vec_token>> }756 };757 token_stream.extend(vec_token);758 } else {759 if !last_segment.arguments.is_empty() {760 panic!(761 "unexpected generic arguments for non-vec type {:?}",762 last_segment.arguments.span()763 );764 }765766 let ident = &last_segment.ident;767 let plain_token = if read_signature {768 quote! {769 (<#ident>::SIGNATURE)770 }771 } else {772 quote! {773 #ident774 }775 };776777 token_stream.extend(plain_token);778 }779 }780781 Type::Tuple(tt) => {782 783 784 785786 let mut tuple_types = proc_macro2::TokenStream::new();787 let mut is_first = true;788789 for ty in tt.elems.iter() {790 if is_first {791 is_first = false792 } else {793 tuple_types.extend(quote!(,));794 }795 Self::expand_type(ty, &mut tuple_types, false);796 }797 tuple_types = if read_signature {798 quote! { (<(#tuple_types)>::SIGNATURE) }799 } else {800 quote! { (#tuple_types) }801 };802 token_stream.extend(tuple_types);803 }804805 806 807 808 809 810 811 812 813 814 815 816 817 818 _ => panic!("Unexpected type {ty:?}"),819 }820 821 822 823 824 825 826 827 828 829 830 831832 833 834835 836 837 838839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854855 856 857 858 859 860 861 862 863 864 865866 867 868 }869870 fn expand_custom_signature(&self) -> proc_macro2::TokenStream {871 let mut token_stream = TokenStream::new();872873 let mut is_first = true;874 for arg in &self.args {875 if arg.is_special() {876 continue;877 }878879 if is_first {880 is_first = false;881 } else {882 token_stream.extend(quote!(,));883 }884 Self::expand_type(&arg.ty, &mut token_stream, true);885 }886887 if !is_first {888 token_stream.extend(quote!(,));889 }890891 let func_name = self.camel_name.clone();892 let func_name = quote!(SignaturePreferences {893 open_name: Some(SignatureUnit::new(#func_name)),894 open_delimiter: Some(SignatureUnit::new("(")),895 param_delimiter: Some(SignatureUnit::new(",")),896 close_delimiter: Some(SignatureUnit::new(")")),897 close_name: None,898 });899 quote!({ ::evm_coder::make_signature!(new fn(#func_name), #token_stream) })900 }901902 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {903 let camel_name = &self.camel_name;904 let mutability = match self.mutability {905 Mutability::Mutable => quote! {SolidityMutability::Mutable},906 Mutability::View => quote! { SolidityMutability::View },907 Mutability::Pure => quote! {SolidityMutability::Pure},908 };909 let result = &self.result;910911 let args = self912 .args913 .iter()914 .filter(|a| !a.is_special())915 .map(MethodArg::expand_solidity_argument);916 let docs = &self.docs;917 let screaming_name = &self.screaming_name;918 let hide = self.hide;919 let custom_signature = self.expand_custom_signature();920 let custom_signature = quote!(921 {922 const cs: FunctionSignature = #custom_signature;923 cs924 }925 );926 let is_payable = self.has_value_args;927928 quote! {929 SolidityFunction {930 docs: &[#(#docs),*],931 hide: #hide,932 selector: u32::from_be_bytes(Self::#screaming_name),933 custom_signature: #custom_signature,934 name: #camel_name,935 mutability: #mutability,936 is_payable: #is_payable,937 args: (938 #(939 #args,940 )*941 ),942 result: <UnnamedArgument<#result>>::default(),943 }944 }945 }946}947948fn generics_list(gen: &Generics) -> proc_macro2::TokenStream {949 if gen.params.is_empty() {950 return quote! {};951 }952 let params = gen.params.iter().map(|p| match p {953 syn::GenericParam::Type(id) => {954 let v = &id.ident;955 quote! {#v}956 }957 syn::GenericParam::Lifetime(lt) => {958 let v = <.lifetime;959 quote! {#v}960 }961 syn::GenericParam::Const(c) => {962 let i = &c.ident;963 quote! {#i}964 }965 });966 quote! { #(#params),* }967}968fn generics_reference(gen: &Generics) -> proc_macro2::TokenStream {969 if gen.params.is_empty() {970 return quote! {};971 }972 let list = generics_list(gen);973 quote! { <#list> }974}975fn generics_data(gen: &Generics) -> proc_macro2::TokenStream {976 let list = generics_list(gen);977 if gen.params.len() == 1 {978 quote! {#list}979 } else {980 quote! { (#list) }981 }982}983984pub struct SolidityInterface {985 generics: Generics,986 name: Box<syn::Type>,987 info: InterfaceInfo,988 methods: Vec<Method>,989 docs: Vec<String>,990}991impl SolidityInterface {992 pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {993 let mut methods = Vec::new();994995 for item in &value.items {996 if let ImplItem::Method(method) = item {997 methods.push(Method::try_from(method)?)998 }999 }1000 let mut docs = vec![];1001 for attr in &value.attrs {1002 let ident = parse_ident_from_path(&attr.path, false)?;1003 if ident == "doc" {1004 let args = attr.parse_meta().unwrap();1005 let value = match args {1006 Meta::NameValue(MetaNameValue {1007 lit: Lit::Str(str), ..1008 }) => str.value(),1009 _ => unreachable!(),1010 };1011 docs.push(value);1012 }1013 }1014 Ok(Self {1015 generics: value.generics.clone(),1016 name: value.self_ty.clone(),1017 info,1018 methods,1019 docs,1020 })1021 }1022 pub fn expand(self) -> proc_macro2::TokenStream {1023 let name = self.name;10241025 let solidity_name = self.info.name.to_string();1026 let call_name = pascal_ident_to_call(&self.info.name);1027 let generics = self.generics;1028 let gen_ref = generics_reference(&generics);1029 let gen_data = generics_data(&generics);1030 let gen_where = &generics.where_clause;10311032 let call_sub = self1033 .info1034 .inline_is1035 .01036 .iter()1037 .chain(self.info.is.0.iter())1038 .map(|c| Is::expand_call_def(c, &gen_ref));1039 let call_parse = self1040 .info1041 .inline_is1042 .01043 .iter()1044 .chain(self.info.is.0.iter())1045 .map(|is| Is::expand_parse(is, &gen_ref));1046 let call_variants = self1047 .info1048 .inline_is1049 .01050 .iter()1051 .chain(self.info.is.0.iter())1052 .map(|c| Is::expand_variant_call(c, &call_name, &gen_ref));1053 let weight_variants = self1054 .info1055 .inline_is1056 .01057 .iter()1058 .chain(self.info.is.0.iter())1059 .map(Is::expand_variant_weight);10601061 let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);1062 let supports_interface = self1063 .info1064 .is1065 .01066 .iter()1067 .map(|is| Is::expand_supports_interface(is, &gen_ref));10681069 let calls = self.methods.iter().map(Method::expand_call_def);1070 let consts = self.methods.iter().map(Method::expand_const);1071 let interface_id = self.methods.iter().map(Method::expand_interface_id);1072 let parsers = self.methods.iter().map(Method::expand_parse);1073 let call_variants_this = self1074 .methods1075 .iter()1076 .map(|m| Method::expand_variant_call(m, &call_name));1077 let weight_variants_this = self.methods.iter().map(Method::expand_variant_weight);1078 let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);10791080 1081 let solidity_is = self1082 .info1083 .is1084 .01085 .iter()1086 .chain(self.info.inline_is.0.iter())1087 .map(|is| is.name.to_string());1088 let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string());1089 let solidity_generators = self1090 .info1091 .is1092 .01093 .iter()1094 .chain(self.info.inline_is.0.iter())1095 .map(|is| Is::expand_generator(is, &gen_ref));1096 let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);10971098 let docs = &self.docs;10991100 quote! {1101 #[derive(Debug)]1102 #(#[doc = #docs])*1103 pub enum #call_name #gen_ref {1104 1105 ERC165Call(::evm_coder::ERC165Call, ::core::marker::PhantomData<#gen_data>),1106 #(1107 #calls,1108 )*1109 #(1110 #call_sub,1111 )*1112 }1113 impl #gen_ref #call_name #gen_ref {1114 #(1115 #consts1116 )*1117 1118 pub fn interface_id() -> ::evm_coder::types::bytes4 {1119 let mut interface_id = 0;1120 #(#interface_id)*1121 #(#inline_interface_id)*1122 u32::to_be_bytes(interface_id)1123 }1124 1125 pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {1126 use evm_coder::solidity::*;1127 use core::fmt::Write;1128 let interface = SolidityInterface {1129 docs: &[#(#docs),*],1130 name: #solidity_name,1131 selector: Self::interface_id(),1132 is: &["Dummy", "ERC165", #(1133 #solidity_is,1134 )* #(1135 #solidity_events_is,1136 )* ],1137 functions: (#(1138 #solidity_functions,1139 )*),1140 };11411142 let mut out = ::evm_coder::types::string::new();1143 if #solidity_name.starts_with("Inline") {1144 out.push_str("/// @dev inlined interface\n");1145 }1146 let _ = interface.format(is_impl, &mut out, tc);1147 tc.collect(out);1148 #(1149 #solidity_event_generators1150 )*1151 #(1152 #solidity_generators1153 )*1154 if is_impl {1155 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());1156 } else {1157 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());1158 }1159 }1160 }1161 impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {1162 fn parse(method_id: ::evm_coder::types::bytes4, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {1163 use ::evm_coder::abi::AbiRead;1164 match method_id {1165 ::evm_coder::ERC165Call::INTERFACE_ID => return Ok(1166 ::evm_coder::ERC165Call::parse(method_id, reader)?1167 .map(|c| Self::ERC165Call(c, ::core::marker::PhantomData))1168 ),1169 #(1170 #parsers,1171 )*1172 _ => {},1173 }1174 #(1175 #call_parse1176 )else*1177 return Ok(None);1178 }1179 }1180 impl #generics #call_name #gen_ref1181 #gen_where1182 {1183 1184 pub fn supports_interface(this: &#name, interface_id: ::evm_coder::types::bytes4) -> bool {1185 interface_id != u32::to_be_bytes(0xffffff) && (1186 interface_id == ::evm_coder::ERC165Call::INTERFACE_ID ||1187 interface_id == Self::interface_id()1188 #(1189 || #supports_interface1190 )*1191 )1192 }1193 }1194 impl #generics ::evm_coder::Weighted for #call_name #gen_ref1195 #gen_where1196 {1197 #[allow(unused_variables)]1198 fn weight(&self) -> ::evm_coder::execution::DispatchInfo {1199 match self {1200 #(1201 #weight_variants,1202 )*1203 1204 Self::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {..}, _) => ::frame_support::weights::Weight::from_ref_time(100).into(),1205 #(1206 #weight_variants_this,1207 )*1208 }1209 }1210 }1211 impl #generics ::evm_coder::Callable<#call_name #gen_ref> for #name1212 #gen_where1213 {1214 #[allow(unreachable_code)] 1215 fn call(&mut self, c: Msg<#call_name #gen_ref>) -> ::evm_coder::execution::ResultWithPostInfo<::evm_coder::abi::AbiWriter> {1216 use ::evm_coder::abi::AbiWrite;1217 match c.call {1218 #(1219 #call_variants,1220 )*1221 #call_name::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {interface_id}, _) => {1222 let mut writer = ::evm_coder::abi::AbiWriter::default();1223 writer.bool(&<#call_name #gen_ref>::supports_interface(self, interface_id));1224 return Ok(writer.into());1225 }1226 _ => {},1227 }1228 let mut writer = ::evm_coder::abi::AbiWriter::default();1229 match c.call {1230 #(1231 #call_variants_this,1232 )*1233 _ => Err(::evm_coder::execution::Error::from("method is not available").into()),1234 }1235 }1236 }1237 }1238 }1239}