1234567891011121314151617#![allow(dead_code)]1819use quote::{quote, ToTokens};20use inflector::cases;21use std::fmt::Write;22use syn::{23 Expr, FnArg, GenericArgument, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta,24 MetaNameValue, PatType, PathArguments, ReturnType, Type,25 spanned::Spanned,26 parse::{Parse, ParseStream},27 parenthesized, Token, LitInt, LitStr,28};2930use crate::{31 fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_path, parse_path_segment,32 parse_result_ok, pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal,33 snake_ident_to_screaming,34};3536struct Is {37 name: Ident,38 pascal_call_name: Ident,39 snake_call_name: Ident,40 via: Option<(Type, Ident)>,41}42impl Is {43 fn expand_call_def(&self, gen_ref: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {44 let name = &self.name;45 let pascal_call_name = &self.pascal_call_name;46 quote! {47 #name(#pascal_call_name #gen_ref)48 }49 }5051 fn expand_interface_id(&self) -> proc_macro2::TokenStream {52 let pascal_call_name = &self.pascal_call_name;53 quote! {54 interface_id ^= u32::from_be_bytes(#pascal_call_name::interface_id());55 }56 }5758 fn expand_supports_interface(59 &self,60 generics: &proc_macro2::TokenStream,61 ) -> proc_macro2::TokenStream {62 let pascal_call_name = &self.pascal_call_name;63 quote! {64 <#pascal_call_name #generics>::supports_interface(interface_id)65 }66 }6768 fn expand_variant_weight(&self) -> proc_macro2::TokenStream {69 let name = &self.name;70 quote! {71 Self::#name(call) => call.weight()72 }73 }7475 fn expand_variant_call(76 &self,77 call_name: &proc_macro2::Ident,78 generics: &proc_macro2::TokenStream,79 ) -> proc_macro2::TokenStream {80 let name = &self.name;81 let pascal_call_name = &self.pascal_call_name;82 let via_typ = self83 .via84 .as_ref()85 .map(|(t, _)| quote! {#t})86 .unwrap_or_else(|| quote! {Self});87 let via_map = self88 .via89 .as_ref()90 .map(|(_, i)| quote! {.#i()})91 .unwrap_or_default();92 quote! {93 #call_name::#name(call) => return <#via_typ as ::evm_coder::Callable<#pascal_call_name #generics>>::call(self #via_map, Msg {94 call,95 caller: c.caller,96 value: c.value,97 })98 }99 }100101 fn expand_parse(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {102 let name = &self.name;103 let pascal_call_name = &self.pascal_call_name;104 quote! {105 if let Some(parsed_call) = <#pascal_call_name #generics>::parse(method_id, reader)? {106 return Ok(Some(Self::#name(parsed_call)))107 }108 }109 }110111 fn expand_generator(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {112 let pascal_call_name = &self.pascal_call_name;113 quote! {114 <#pascal_call_name #generics>::generate_solidity_interface(tc, is_impl);115 }116 }117118 fn expand_event_generator(&self) -> proc_macro2::TokenStream {119 let name = &self.name;120 quote! {121 #name::generate_solidity_interface(tc, is_impl);122 }123 }124}125126#[derive(Default)]127struct IsList(Vec<Is>);128impl Parse for IsList {129 fn parse(input: ParseStream) -> syn::Result<Self> {130 let mut out = vec![];131 loop {132 if input.is_empty() {133 break;134 }135 let name = input.parse::<Ident>()?;136 let lookahead = input.lookahead1();137 let via = if lookahead.peek(syn::token::Paren) {138 let contents;139 parenthesized!(contents in input);140 let method = contents.parse::<Ident>()?;141 contents.parse::<Token![,]>()?;142 let ty = contents.parse::<Type>()?;143 Some((ty, method))144 } else if lookahead.peek(Token![,]) {145 None146 } else if input.is_empty() {147 None148 } else {149 return Err(lookahead.error());150 };151 out.push(Is {152 pascal_call_name: pascal_ident_to_call(&name),153 snake_call_name: pascal_ident_to_snake_call(&name),154 name,155 via,156 });157 if input.peek(Token![,]) {158 input.parse::<Token![,]>()?;159 continue;160 } else {161 break;162 }163 }164 Ok(Self(out))165 }166}167168pub struct InterfaceInfo {169 name: Ident,170 is: IsList,171 inline_is: IsList,172 events: IsList,173 expect_selector: Option<u32>,174}175impl Parse for InterfaceInfo {176 fn parse(input: ParseStream) -> syn::Result<Self> {177 let mut name = None;178 let mut is = None;179 let mut inline_is = None;180 let mut events = None;181 let mut expect_selector = None;182 183 loop {184 let lookahead = input.lookahead1();185 if lookahead.peek(kw::name) {186 let k = input.parse::<kw::name>()?;187 input.parse::<Token![=]>()?;188 if name.replace(input.parse::<Ident>()?).is_some() {189 return Err(syn::Error::new(k.span(), "name is already set"));190 }191 } else if lookahead.peek(kw::is) {192 let k = input.parse::<kw::is>()?;193 let contents;194 parenthesized!(contents in input);195 if is.replace(contents.parse::<IsList>()?).is_some() {196 return Err(syn::Error::new(k.span(), "is is already set"));197 }198 } else if lookahead.peek(kw::inline_is) {199 let k = input.parse::<kw::inline_is>()?;200 let contents;201 parenthesized!(contents in input);202 if inline_is.replace(contents.parse::<IsList>()?).is_some() {203 return Err(syn::Error::new(k.span(), "inline_is is already set"));204 }205 } else if lookahead.peek(kw::events) {206 let k = input.parse::<kw::events>()?;207 let contents;208 parenthesized!(contents in input);209 if events.replace(contents.parse::<IsList>()?).is_some() {210 return Err(syn::Error::new(k.span(), "events is already set"));211 }212 } else if lookahead.peek(kw::expect_selector) {213 let k = input.parse::<kw::expect_selector>()?;214 input.parse::<Token![=]>()?;215 let value = input.parse::<LitInt>()?;216 if expect_selector217 .replace(value.base10_parse::<u32>()?)218 .is_some()219 {220 return Err(syn::Error::new(k.span(), "expect_selector is already set"));221 }222 } else if input.is_empty() {223 break;224 } else {225 return Err(lookahead.error());226 }227 if input.peek(Token![,]) {228 input.parse::<Token![,]>()?;229 } else {230 break;231 }232 }233 Ok(Self {234 name: name.ok_or_else(|| syn::Error::new(input.span(), "missing name"))?,235 is: is.unwrap_or_default(),236 inline_is: inline_is.unwrap_or_default(),237 events: events.unwrap_or_default(),238 expect_selector,239 })240 }241}242243struct MethodInfo {244 rename_selector: Option<String>,245}246impl Parse for MethodInfo {247 fn parse(input: ParseStream) -> syn::Result<Self> {248 let mut rename_selector = None;249 let lookahead = input.lookahead1();250 if lookahead.peek(kw::rename_selector) {251 let k = input.parse::<kw::rename_selector>()?;252 input.parse::<Token![=]>()?;253 if rename_selector254 .replace(input.parse::<LitStr>()?.value())255 .is_some()256 {257 return Err(syn::Error::new(k.span(), "rename_selector is already set"));258 }259 }260 Ok(Self { rename_selector })261 }262}263264enum AbiType {265 266 Plain(Ident),267 268 Tuple(Vec<AbiType>),269 270 Vec(Box<AbiType>),271 272 Array(Box<AbiType>, usize),273}274impl AbiType {275 fn try_from(value: &Type) -> syn::Result<Self> {276 let value = Self::try_maybe_special_from(value)?;277 if value.is_special() {278 return Err(syn::Error::new(value.span(), "unexpected special type"));279 }280 Ok(value)281 }282 fn try_maybe_special_from(value: &Type) -> syn::Result<Self> {283 match value {284 Type::Array(arr) => {285 let wrapped = AbiType::try_from(&arr.elem)?;286 match &arr.len {287 Expr::Lit(l) => match &l.lit {288 Lit::Int(i) => {289 let num = i.base10_parse::<usize>()?;290 Ok(AbiType::Array(Box::new(wrapped), num as usize))291 }292 _ => Err(syn::Error::new(arr.len.span(), "should be int literal")),293 },294 _ => Err(syn::Error::new(arr.len.span(), "should be literal")),295 }296 }297 Type::Path(_) => {298 let path = parse_path(value)?;299 let segment = parse_path_segment(path)?;300 if segment.ident == "Vec" {301 let args = match &segment.arguments {302 PathArguments::AngleBracketed(e) => e,303 _ => {304 return Err(syn::Error::new(305 segment.arguments.span(),306 "missing Vec generic",307 ))308 }309 };310 let args = &args.args;311 if args.len() != 1 {312 return Err(syn::Error::new(313 args.span(),314 "expected only one generic for vec",315 ));316 }317 let arg = args.first().expect("first arg");318319 let ty = match arg {320 GenericArgument::Type(ty) => ty,321 _ => {322 return Err(syn::Error::new(323 arg.span(),324 "expected first generic to be type",325 ))326 }327 };328329 let wrapped = AbiType::try_from(ty)?;330 Ok(Self::Vec(Box::new(wrapped)))331 } else {332 if !segment.arguments.is_empty() {333 return Err(syn::Error::new(334 segment.arguments.span(),335 "unexpected generic arguments for non-vec type",336 ));337 }338 Ok(Self::Plain(segment.ident.clone()))339 }340 }341 Type::Tuple(t) => {342 let mut out = Vec::with_capacity(t.elems.len());343 for el in t.elems.iter() {344 out.push(AbiType::try_from(el)?)345 }346 Ok(Self::Tuple(out))347 }348 _ => Err(syn::Error::new(349 value.span(),350 "unexpected type, only arrays, plain types and tuples are supported",351 )),352 }353 }354 fn is_value(&self) -> bool {355 matches!(self, Self::Plain(v) if v == "value")356 }357 fn is_caller(&self) -> bool {358 matches!(self, Self::Plain(v) if v == "caller")359 }360 fn is_special(&self) -> bool {361 self.is_caller() || self.is_value()362 }363 fn selector_ty_buf(&self, buf: &mut String) -> std::fmt::Result {364 match self {365 AbiType::Plain(t) => {366 write!(buf, "{}", t)367 }368 AbiType::Tuple(t) => {369 write!(buf, "(")?;370 for (i, t) in t.iter().enumerate() {371 if i != 0 {372 write!(buf, ",")?;373 }374 t.selector_ty_buf(buf)?;375 }376 write!(buf, ")")377 }378 AbiType::Vec(v) => {379 v.selector_ty_buf(buf)?;380 write!(buf, "[]")381 }382 AbiType::Array(v, len) => {383 v.selector_ty_buf(buf)?;384 write!(buf, "[{}]", len)385 }386 }387 }388 fn selector_ty(&self) -> String {389 let mut out = String::new();390 self.selector_ty_buf(&mut out).expect("no fmt error");391 out392 }393}394impl ToTokens for AbiType {395 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {396 match self {397 AbiType::Plain(t) => tokens.extend(quote! {#t}),398 AbiType::Tuple(t) => {399 tokens.extend(quote! {(400 #(#t),*401 )});402 }403 AbiType::Vec(v) => tokens.extend(quote! {Vec<#v>}),404 AbiType::Array(v, l) => tokens.extend(quote! {[#v; #l]}),405 }406 }407}408409struct MethodArg {410 name: Ident,411 camel_name: String,412 ty: AbiType,413}414impl MethodArg {415 fn try_from(value: &PatType) -> syn::Result<Self> {416 let name = parse_ident_from_pat(&value.pat)?.clone();417 Ok(Self {418 camel_name: cases::camelcase::to_camel_case(&name.to_string()),419 name,420 ty: AbiType::try_maybe_special_from(&value.ty)?,421 })422 }423 fn is_value(&self) -> bool {424 self.ty.is_value()425 }426 fn is_caller(&self) -> bool {427 self.ty.is_caller()428 }429 fn is_special(&self) -> bool {430 self.ty.is_special()431 }432 fn selector_ty(&self) -> String {433 assert!(!self.is_special());434 self.ty.selector_ty()435 }436437 fn expand_call_def(&self) -> proc_macro2::TokenStream {438 assert!(!self.is_special());439 let name = &self.name;440 let ty = &self.ty;441442 quote! {443 #name: #ty444 }445 }446447 fn expand_parse(&self) -> proc_macro2::TokenStream {448 assert!(!self.is_special());449 let name = &self.name;450 quote! {451 #name: reader.abi_read()?452 }453 }454455 fn expand_call_arg(&self) -> proc_macro2::TokenStream {456 if self.is_value() {457 quote! {458 c.value.clone()459 }460 } else if self.is_caller() {461 quote! {462 c.caller.clone()463 }464 } else {465 let name = &self.name;466 quote! {467 #name468 }469 }470 }471472 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {473 let camel_name = &self.camel_name.to_string();474 let ty = &self.ty;475 quote! {476 <NamedArgument<#ty>>::new(#camel_name)477 }478 }479}480481#[derive(PartialEq)]482enum Mutability {483 Mutable,484 View,485 Pure,486}487488mod kw {489 syn::custom_keyword!(weight);490491 syn::custom_keyword!(via);492 syn::custom_keyword!(name);493 syn::custom_keyword!(is);494 syn::custom_keyword!(inline_is);495 syn::custom_keyword!(events);496 syn::custom_keyword!(expect_selector);497498 syn::custom_keyword!(rename_selector);499}500501struct Method {502 name: Ident,503 camel_name: String,504 pascal_name: Ident,505 screaming_name: Ident,506 selector_str: String,507 selector: u32,508 args: Vec<MethodArg>,509 has_normal_args: bool,510 mutability: Mutability,511 result: Type,512 weight: Option<Expr>,513 docs: Vec<String>,514}515impl Method {516 fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {517 let mut info = MethodInfo {518 rename_selector: None,519 };520 let mut docs = Vec::new();521 let mut weight = None;522 for attr in &value.attrs {523 let ident = parse_ident_from_path(&attr.path, false)?;524 if ident == "solidity" {525 info = attr.parse_args::<MethodInfo>()?;526 } else if ident == "doc" {527 let args = attr.parse_meta().unwrap();528 let value = match args {529 Meta::NameValue(MetaNameValue {530 lit: Lit::Str(str), ..531 }) => str.value(),532 _ => unreachable!(),533 };534 docs.push(value);535 } else if ident == "weight" {536 weight = Some(attr.parse_args::<Expr>()?);537 }538 }539 let ident = &value.sig.ident;540 let ident_str = ident.to_string();541 if !cases::snakecase::is_snake_case(&ident_str) {542 return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute"));543 }544545 let mut mutability = Mutability::Pure;546547 if let Some(FnArg::Receiver(receiver)) = value548 .sig549 .inputs550 .iter()551 .find(|arg| matches!(arg, FnArg::Receiver(_)))552 {553 if receiver.reference.is_none() {554 return Err(syn::Error::new(555 receiver.span(),556 "receiver should be by ref",557 ));558 }559 if receiver.mutability.is_some() {560 mutability = Mutability::Mutable;561 } else {562 mutability = Mutability::View;563 }564 }565 let mut args = Vec::new();566 for typ in value567 .sig568 .inputs569 .iter()570 .filter(|arg| matches!(arg, FnArg::Typed(_)))571 {572 let typ = match typ {573 FnArg::Typed(typ) => typ,574 _ => unreachable!(),575 };576 args.push(MethodArg::try_from(typ)?);577 }578579 if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) {580 return Err(syn::Error::new(581 args.iter().find(|arg| arg.is_value()).unwrap().ty.span(),582 "payable function should be mutable",583 ));584 }585586 let result = match &value.sig.output {587 ReturnType::Type(_, ty) => ty,588 _ => 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)")),589 };590 let result = parse_result_ok(result)?;591592 let camel_name = info593 .rename_selector594 .unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));595 let mut selector_str = camel_name.clone();596 selector_str.push('(');597 let mut has_normal_args = false;598 for (i, arg) in args.iter().filter(|arg| !arg.is_special()).enumerate() {599 if i != 0 {600 selector_str.push(',');601 }602 write!(selector_str, "{}", arg.selector_ty()).unwrap();603 has_normal_args = true;604 }605 selector_str.push(')');606 let selector = fn_selector_str(&selector_str);607608 Ok(Self {609 name: ident.clone(),610 camel_name,611 pascal_name: snake_ident_to_pascal(ident),612 screaming_name: snake_ident_to_screaming(ident),613 selector_str,614 selector,615 args,616 has_normal_args,617 mutability,618 result: result.clone(),619 weight,620 docs,621 })622 }623 fn expand_call_def(&self) -> proc_macro2::TokenStream {624 let defs = self625 .args626 .iter()627 .filter(|a| !a.is_special())628 .map(|a| a.expand_call_def());629 let pascal_name = &self.pascal_name;630 let docs = &self.docs;631632 if self.has_normal_args {633 quote! {634 #(#[doc = #docs])*635 #[allow(missing_docs)]636 #pascal_name {637 #(638 #defs,639 )*640 }641 }642 } else {643 quote! {#pascal_name}644 }645 }646647 fn expand_const(&self) -> proc_macro2::TokenStream {648 let screaming_name = &self.screaming_name;649 let selector = u32::to_be_bytes(self.selector);650 let selector_str = &self.selector_str;651 quote! {652 #[doc = #selector_str]653 const #screaming_name: ::evm_coder::types::bytes4 = [#(#selector,)*];654 }655 }656657 fn expand_interface_id(&self) -> proc_macro2::TokenStream {658 let screaming_name = &self.screaming_name;659 quote! {660 interface_id ^= u32::from_be_bytes(Self::#screaming_name);661 }662 }663664 fn expand_parse(&self) -> proc_macro2::TokenStream {665 let pascal_name = &self.pascal_name;666 let screaming_name = &self.screaming_name;667 if self.has_normal_args {668 let parsers = self669 .args670 .iter()671 .filter(|a| !a.is_special())672 .map(|a| a.expand_parse());673 quote! {674 Self::#screaming_name => return Ok(Some(Self::#pascal_name {675 #(676 #parsers,677 )*678 }))679 }680 } else {681 quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }682 }683 }684685 fn expand_variant_call(&self, call_name: &proc_macro2::Ident) -> proc_macro2::TokenStream {686 let pascal_name = &self.pascal_name;687 let name = &self.name;688689 let matcher = if self.has_normal_args {690 let names = self691 .args692 .iter()693 .filter(|a| !a.is_special())694 .map(|a| &a.name);695696 quote! {{697 #(698 #names,699 )*700 }}701 } else {702 quote! {}703 };704705 let receiver = match self.mutability {706 Mutability::Mutable | Mutability::View => quote! {self.},707 Mutability::Pure => quote! {Self::},708 };709 let args = self.args.iter().map(|a| a.expand_call_arg());710711 quote! {712 #call_name::#pascal_name #matcher => {713 let result = #receiver #name(714 #(715 #args,716 )*717 )?;718 (&result).to_result()719 }720 }721 }722723 fn expand_variant_weight(&self) -> proc_macro2::TokenStream {724 let pascal_name = &self.pascal_name;725 if let Some(weight) = &self.weight {726 let matcher = if self.has_normal_args {727 let names = self728 .args729 .iter()730 .filter(|a| !a.is_special())731 .map(|a| &a.name);732733 quote! {{734 #(735 #names,736 )*737 }}738 } else {739 quote! {}740 };741 quote! {742 Self::#pascal_name #matcher => (#weight).into()743 }744 } else {745 let matcher = if self.has_normal_args {746 quote! {{..}}747 } else {748 quote! {}749 };750 quote! {751 Self::#pascal_name #matcher => ().into()752 }753 }754 }755756 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {757 let camel_name = &self.camel_name;758 let mutability = match self.mutability {759 Mutability::Mutable => quote! {SolidityMutability::Mutable},760 Mutability::View => quote! { SolidityMutability::View },761 Mutability::Pure => quote! {SolidityMutability::Pure},762 };763 let result = &self.result;764765 let args = self766 .args767 .iter()768 .filter(|a| !a.is_special())769 .map(MethodArg::expand_solidity_argument);770 let docs = &self.docs;771 let selector_str = &self.selector_str;772 let selector = self.selector;773774 quote! {775 SolidityFunction {776 docs: &[#(#docs),*],777 selector_str: #selector_str,778 selector: #selector,779 name: #camel_name,780 mutability: #mutability,781 args: (782 #(783 #args,784 )*785 ),786 result: <UnnamedArgument<#result>>::default(),787 }788 }789 }790}791792fn generics_list(gen: &Generics) -> proc_macro2::TokenStream {793 if gen.params.is_empty() {794 return quote! {};795 }796 let params = gen.params.iter().map(|p| match p {797 syn::GenericParam::Type(id) => {798 let v = &id.ident;799 quote! {#v}800 }801 syn::GenericParam::Lifetime(lt) => {802 let v = <.lifetime;803 quote! {#v}804 }805 syn::GenericParam::Const(c) => {806 let i = &c.ident;807 quote! {#i}808 }809 });810 quote! { #(#params),* }811}812fn generics_reference(gen: &Generics) -> proc_macro2::TokenStream {813 if gen.params.is_empty() {814 return quote! {};815 }816 let list = generics_list(gen);817 quote! { <#list> }818}819fn generics_data(gen: &Generics) -> proc_macro2::TokenStream {820 let list = generics_list(gen);821 if gen.params.len() == 1 {822 quote! {#list}823 } else {824 quote! { (#list) }825 }826}827828pub struct SolidityInterface {829 generics: Generics,830 name: Box<syn::Type>,831 info: InterfaceInfo,832 methods: Vec<Method>,833 docs: Vec<String>,834}835impl SolidityInterface {836 pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {837 let mut methods = Vec::new();838839 for item in &value.items {840 if let ImplItem::Method(method) = item {841 methods.push(Method::try_from(method)?)842 }843 }844 let mut docs = vec![];845 for attr in &value.attrs {846 let ident = parse_ident_from_path(&attr.path, false)?;847 if ident == "doc" {848 let args = attr.parse_meta().unwrap();849 let value = match args {850 Meta::NameValue(MetaNameValue {851 lit: Lit::Str(str), ..852 }) => str.value(),853 _ => unreachable!(),854 };855 docs.push(value);856 }857 }858 Ok(Self {859 generics: value.generics.clone(),860 name: value.self_ty.clone(),861 info,862 methods,863 docs,864 })865 }866 pub fn expand(self) -> proc_macro2::TokenStream {867 let name = self.name;868869 let solidity_name = self.info.name.to_string();870 let call_name = pascal_ident_to_call(&self.info.name);871 let generics = self.generics;872 let gen_ref = generics_reference(&generics);873 let gen_data = generics_data(&generics);874 let gen_where = &generics.where_clause;875876 let call_sub = self877 .info878 .inline_is879 .0880 .iter()881 .chain(self.info.is.0.iter())882 .map(|c| Is::expand_call_def(c, &gen_ref));883 let call_parse = self884 .info885 .inline_is886 .0887 .iter()888 .chain(self.info.is.0.iter())889 .map(|is| Is::expand_parse(is, &gen_ref));890 let call_variants = self891 .info892 .inline_is893 .0894 .iter()895 .chain(self.info.is.0.iter())896 .map(|c| Is::expand_variant_call(c, &call_name, &gen_ref));897 let weight_variants = self898 .info899 .inline_is900 .0901 .iter()902 .chain(self.info.is.0.iter())903 .map(Is::expand_variant_weight);904905 let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);906 let supports_interface = self907 .info908 .is909 .0910 .iter()911 .map(|is| Is::expand_supports_interface(is, &gen_ref));912913 let calls = self.methods.iter().map(Method::expand_call_def);914 let consts = self.methods.iter().map(Method::expand_const);915 let interface_id = self.methods.iter().map(Method::expand_interface_id);916 let parsers = self.methods.iter().map(Method::expand_parse);917 let call_variants_this = self918 .methods919 .iter()920 .map(|m| Method::expand_variant_call(m, &call_name));921 let weight_variants_this = self.methods.iter().map(Method::expand_variant_weight);922 let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);923924 925 let solidity_is = self926 .info927 .is928 .0929 .iter()930 .chain(self.info.inline_is.0.iter())931 .map(|is| is.name.to_string());932 let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string());933 let solidity_generators = self934 .info935 .is936 .0937 .iter()938 .chain(self.info.inline_is.0.iter())939 .map(|is| Is::expand_generator(is, &gen_ref));940 let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);941942 let docs = &self.docs;943944 if let Some(expect_selector) = &self.info.expect_selector {945 if !self.info.inline_is.0.is_empty() {946 return syn::Error::new(947 name.span(),948 "expect_selector is not compatible with inline_is",949 )950 .to_compile_error();951 }952 let selector = self953 .methods954 .iter()955 .map(|m| m.selector)956 .fold(0, |a, b| a ^ b);957958 if *expect_selector != selector {959 let mut methods = String::new();960 for meth in self.methods.iter() {961 write!(methods, "\n- {}", meth.selector_str).expect("write to string");962 }963 return syn::Error::new(name.span(), format!("expected selector mismatch, expected {expect_selector:0>8x}, but implementation has {selector:0>8x}{methods}")).to_compile_error();964 }965 }966 967968 quote! {969 #[derive(Debug)]970 #(#[doc = #docs])*971 pub enum #call_name #gen_ref {972 973 ERC165Call(::evm_coder::ERC165Call, ::core::marker::PhantomData<#gen_data>),974 #(975 #calls,976 )*977 #(978 #call_sub,979 )*980 }981 impl #gen_ref #call_name #gen_ref {982 #(983 #consts984 )*985 986 pub fn interface_id() -> ::evm_coder::types::bytes4 {987 let mut interface_id = 0;988 #(#interface_id)*989 #(#inline_interface_id)*990 u32::to_be_bytes(interface_id)991 }992 993 pub fn supports_interface(interface_id: ::evm_coder::types::bytes4) -> bool {994 interface_id != u32::to_be_bytes(0xffffff) && (995 interface_id == ::evm_coder::ERC165Call::INTERFACE_ID ||996 interface_id == Self::interface_id()997 #(998 || #supports_interface999 )*1000 )1001 }1002 1003 pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {1004 use evm_coder::solidity::*;1005 use core::fmt::Write;1006 let interface = SolidityInterface {1007 docs: &[#(#docs),*],1008 name: #solidity_name,1009 selector: Self::interface_id(),1010 is: &["Dummy", "ERC165", #(1011 #solidity_is,1012 )* #(1013 #solidity_events_is,1014 )* ],1015 functions: (#(1016 #solidity_functions,1017 )*),1018 };10191020 let mut out = string::new();1021 if #solidity_name.starts_with("Inline") {1022 out.push_str("/// @dev inlined interface\n");1023 }1024 let _ = interface.format(is_impl, &mut out, tc);1025 tc.collect(out);1026 #(1027 #solidity_event_generators1028 )*1029 #(1030 #solidity_generators1031 )*1032 if is_impl {1033 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());1034 } else {1035 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());1036 }1037 }1038 }1039 impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {1040 fn parse(method_id: ::evm_coder::types::bytes4, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {1041 use ::evm_coder::abi::AbiRead;1042 match method_id {1043 ::evm_coder::ERC165Call::INTERFACE_ID => return Ok(1044 ::evm_coder::ERC165Call::parse(method_id, reader)?1045 .map(|c| Self::ERC165Call(c, ::core::marker::PhantomData))1046 ),1047 #(1048 #parsers,1049 )*1050 _ => {},1051 }1052 #(1053 #call_parse1054 )else*1055 return Ok(None);1056 }1057 }1058 impl #generics ::evm_coder::Weighted for #call_name #gen_ref1059 #gen_where1060 {1061 #[allow(unused_variables)]1062 fn weight(&self) -> ::evm_coder::execution::DispatchInfo {1063 match self {1064 #(1065 #weight_variants,1066 )*1067 1068 Self::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {..}, _) => 100u64.into(),1069 #(1070 #weight_variants_this,1071 )*1072 }1073 }1074 }1075 impl #generics ::evm_coder::Callable<#call_name #gen_ref> for #name1076 #gen_where1077 {1078 #[allow(unreachable_code)] 1079 fn call(&mut self, c: Msg<#call_name #gen_ref>) -> ::evm_coder::execution::ResultWithPostInfo<::evm_coder::abi::AbiWriter> {1080 use ::evm_coder::abi::AbiWrite;1081 match c.call {1082 #(1083 #call_variants,1084 )*1085 #call_name::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {interface_id}, _) => {1086 let mut writer = ::evm_coder::abi::AbiWriter::default();1087 writer.bool(&<#call_name #gen_ref>::supports_interface(interface_id));1088 return Ok(writer.into());1089 }1090 _ => {},1091 }1092 let mut writer = ::evm_coder::abi::AbiWriter::default();1093 match c.call {1094 #(1095 #call_variants_this,1096 )*1097 _ => unreachable!()1098 }1099 }1100 }1101 }1102 }1103}