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;630631 if self.has_normal_args {632 quote! {633 #pascal_name {634 #(635 #defs,636 )*637 }638 }639 } else {640 quote! {#pascal_name}641 }642 }643644 fn expand_const(&self) -> proc_macro2::TokenStream {645 let screaming_name = &self.screaming_name;646 let selector = u32::to_be_bytes(self.selector);647 let selector_str = &self.selector_str;648 quote! {649 #[doc = #selector_str]650 const #screaming_name: ::evm_coder::types::bytes4 = [#(#selector,)*];651 }652 }653654 fn expand_interface_id(&self) -> proc_macro2::TokenStream {655 let screaming_name = &self.screaming_name;656 quote! {657 interface_id ^= u32::from_be_bytes(Self::#screaming_name);658 }659 }660661 fn expand_parse(&self) -> proc_macro2::TokenStream {662 let pascal_name = &self.pascal_name;663 let screaming_name = &self.screaming_name;664 if self.has_normal_args {665 let parsers = self666 .args667 .iter()668 .filter(|a| !a.is_special())669 .map(|a| a.expand_parse());670 quote! {671 Self::#screaming_name => return Ok(Some(Self::#pascal_name {672 #(673 #parsers,674 )*675 }))676 }677 } else {678 quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }679 }680 }681682 fn expand_variant_call(&self, call_name: &proc_macro2::Ident) -> proc_macro2::TokenStream {683 let pascal_name = &self.pascal_name;684 let name = &self.name;685686 let matcher = if self.has_normal_args {687 let names = self688 .args689 .iter()690 .filter(|a| !a.is_special())691 .map(|a| &a.name);692693 quote! {{694 #(695 #names,696 )*697 }}698 } else {699 quote! {}700 };701702 let receiver = match self.mutability {703 Mutability::Mutable | Mutability::View => quote! {self.},704 Mutability::Pure => quote! {Self::},705 };706 let args = self.args.iter().map(|a| a.expand_call_arg());707708 quote! {709 #call_name::#pascal_name #matcher => {710 let result = #receiver #name(711 #(712 #args,713 )*714 )?;715 (&result).to_result()716 }717 }718 }719720 fn expand_variant_weight(&self) -> proc_macro2::TokenStream {721 let pascal_name = &self.pascal_name;722 if let Some(weight) = &self.weight {723 let matcher = if self.has_normal_args {724 let names = self725 .args726 .iter()727 .filter(|a| !a.is_special())728 .map(|a| &a.name);729730 quote! {{731 #(732 #names,733 )*734 }}735 } else {736 quote! {}737 };738 quote! {739 Self::#pascal_name #matcher => (#weight).into()740 }741 } else {742 let matcher = if self.has_normal_args {743 quote! {{..}}744 } else {745 quote! {}746 };747 quote! {748 Self::#pascal_name #matcher => ().into()749 }750 }751 }752753 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {754 let camel_name = &self.camel_name;755 let mutability = match self.mutability {756 Mutability::Mutable => quote! {SolidityMutability::Mutable},757 Mutability::View => quote! { SolidityMutability::View },758 Mutability::Pure => quote! {SolidityMutability::Pure},759 };760 let result = &self.result;761762 let args = self763 .args764 .iter()765 .filter(|a| !a.is_special())766 .map(MethodArg::expand_solidity_argument);767 let docs = self.docs.iter();768 let selector = format!("{} {:0>8x}", self.selector_str, self.selector);769770 quote! {771 SolidityFunction {772 docs: &[#(#docs),*],773 selector: #selector,774 name: #camel_name,775 mutability: #mutability,776 args: (777 #(778 #args,779 )*780 ),781 result: <UnnamedArgument<#result>>::default(),782 }783 }784 }785}786787fn generics_list(gen: &Generics) -> proc_macro2::TokenStream {788 if gen.params.is_empty() {789 return quote! {};790 }791 let params = gen.params.iter().map(|p| match p {792 syn::GenericParam::Type(id) => {793 let v = &id.ident;794 quote! {#v}795 }796 syn::GenericParam::Lifetime(lt) => {797 let v = <.lifetime;798 quote! {#v}799 }800 syn::GenericParam::Const(c) => {801 let i = &c.ident;802 quote! {#i}803 }804 });805 quote! { #(#params),* }806}807fn generics_reference(gen: &Generics) -> proc_macro2::TokenStream {808 if gen.params.is_empty() {809 return quote! {};810 }811 let list = generics_list(gen);812 quote! { <#list> }813}814fn generics_data(gen: &Generics) -> proc_macro2::TokenStream {815 let list = generics_list(gen);816 if gen.params.len() == 1 {817 quote! {#list}818 } else {819 quote! { (#list) }820 }821}822823pub struct SolidityInterface {824 generics: Generics,825 name: Box<syn::Type>,826 info: InterfaceInfo,827 methods: Vec<Method>,828}829impl SolidityInterface {830 pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {831 let mut methods = Vec::new();832833 for item in &value.items {834 if let ImplItem::Method(method) = item {835 methods.push(Method::try_from(method)?)836 }837 }838 Ok(Self {839 generics: value.generics.clone(),840 name: value.self_ty.clone(),841 info,842 methods,843 })844 }845 pub fn expand(self) -> proc_macro2::TokenStream {846 let name = self.name;847848 let solidity_name = self.info.name.to_string();849 let call_name = pascal_ident_to_call(&self.info.name);850 let generics = self.generics;851 let gen_ref = generics_reference(&generics);852 let gen_data = generics_data(&generics);853 let gen_where = &generics.where_clause;854855 let call_sub = self856 .info857 .inline_is858 .0859 .iter()860 .chain(self.info.is.0.iter())861 .map(|c| Is::expand_call_def(c, &gen_ref));862 let call_parse = self863 .info864 .inline_is865 .0866 .iter()867 .chain(self.info.is.0.iter())868 .map(|is| Is::expand_parse(is, &gen_ref));869 let call_variants = self870 .info871 .inline_is872 .0873 .iter()874 .chain(self.info.is.0.iter())875 .map(|c| Is::expand_variant_call(c, &call_name, &gen_ref));876 let weight_variants = self877 .info878 .inline_is879 .0880 .iter()881 .chain(self.info.is.0.iter())882 .map(Is::expand_variant_weight);883884 let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);885 let supports_interface = self886 .info887 .is888 .0889 .iter()890 .map(|is| Is::expand_supports_interface(is, &gen_ref));891892 let calls = self.methods.iter().map(Method::expand_call_def);893 let consts = self.methods.iter().map(Method::expand_const);894 let interface_id = self.methods.iter().map(Method::expand_interface_id);895 let parsers = self.methods.iter().map(Method::expand_parse);896 let call_variants_this = self897 .methods898 .iter()899 .map(|m| Method::expand_variant_call(m, &call_name));900 let weight_variants_this = self.methods.iter().map(Method::expand_variant_weight);901 let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);902903 904 let solidity_is = self905 .info906 .is907 .0908 .iter()909 .chain(self.info.inline_is.0.iter())910 .map(|is| is.name.to_string());911 let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string());912 let solidity_generators = self913 .info914 .is915 .0916 .iter()917 .chain(self.info.inline_is.0.iter())918 .map(|is| Is::expand_generator(is, &gen_ref));919 let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);920921 if let Some(expect_selector) = &self.info.expect_selector {922 if !self.info.inline_is.0.is_empty() {923 return syn::Error::new(924 name.span(),925 "expect_selector is not compatible with inline_is",926 )927 .to_compile_error();928 }929 let selector = self930 .methods931 .iter()932 .map(|m| m.selector)933 .fold(0, |a, b| a ^ b);934935 if *expect_selector != selector {936 let mut methods = String::new();937 for meth in self.methods.iter() {938 write!(methods, "\n- {}", meth.selector_str).expect("write to string");939 }940 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();941 }942 }943 944945 quote! {946 #[derive(Debug)]947 pub enum #call_name #gen_ref {948 ERC165Call(::evm_coder::ERC165Call, ::core::marker::PhantomData<#gen_data>),949 #(950 #calls,951 )*952 #(953 #call_sub,954 )*955 }956 impl #gen_ref #call_name #gen_ref {957 #(958 #consts959 )*960 pub fn interface_id() -> ::evm_coder::types::bytes4 {961 let mut interface_id = 0;962 #(#interface_id)*963 #(#inline_interface_id)*964 u32::to_be_bytes(interface_id)965 }966 pub fn supports_interface(interface_id: ::evm_coder::types::bytes4) -> bool {967 interface_id != u32::to_be_bytes(0xffffff) && (968 interface_id == ::evm_coder::ERC165Call::INTERFACE_ID ||969 interface_id == Self::interface_id()970 #(971 || #supports_interface972 )*973 )974 }975 pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {976 use evm_coder::solidity::*;977 use core::fmt::Write;978 let interface = SolidityInterface {979 name: #solidity_name,980 selector: Self::interface_id(),981 is: &["Dummy", "ERC165", #(982 #solidity_is,983 )* #(984 #solidity_events_is,985 )* ],986 functions: (#(987 #solidity_functions,988 )*),989 };990 if is_impl {991 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());992 } else {993 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());994 }995 #(996 #solidity_generators997 )*998 #(999 #solidity_event_generators1000 )*10011002 let mut out = string::new();1003 1004 1005 if #solidity_name.starts_with("Inline") {1006 out.push_str("/// @dev inlined interface\n");1007 }1008 let _ = interface.format(is_impl, &mut out, tc);1009 tc.collect(out);1010 }1011 }1012 impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {1013 fn parse(method_id: ::evm_coder::types::bytes4, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {1014 use ::evm_coder::abi::AbiRead;1015 match method_id {1016 ::evm_coder::ERC165Call::INTERFACE_ID => return Ok(1017 ::evm_coder::ERC165Call::parse(method_id, reader)?1018 .map(|c| Self::ERC165Call(c, ::core::marker::PhantomData))1019 ),1020 #(1021 #parsers,1022 )*1023 _ => {},1024 }1025 #(1026 #call_parse1027 )else*1028 return Ok(None);1029 }1030 }1031 impl #generics ::evm_coder::Weighted for #call_name #gen_ref1032 #gen_where1033 {1034 #[allow(unused_variables)]1035 fn weight(&self) -> ::evm_coder::execution::DispatchInfo {1036 match self {1037 #(1038 #weight_variants,1039 )*1040 1041 Self::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {..}, _) => 100u64.into(),1042 #(1043 #weight_variants_this,1044 )*1045 }1046 }1047 }1048 impl #generics ::evm_coder::Callable<#call_name #gen_ref> for #name1049 #gen_where1050 {1051 #[allow(unreachable_code)] 1052 fn call(&mut self, c: Msg<#call_name #gen_ref>) -> ::evm_coder::execution::ResultWithPostInfo<::evm_coder::abi::AbiWriter> {1053 use ::evm_coder::abi::AbiWrite;1054 match c.call {1055 #(1056 #call_variants,1057 )*1058 #call_name::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {interface_id}, _) => {1059 let mut writer = ::evm_coder::abi::AbiWriter::default();1060 writer.bool(&<#call_name #gen_ref>::supports_interface(interface_id));1061 return Ok(writer.into());1062 }1063 _ => {},1064 }1065 let mut writer = ::evm_coder::abi::AbiWriter::default();1066 match c.call {1067 #(1068 #call_variants_this,1069 )*1070 _ => unreachable!()1071 }1072 }1073 }1074 }1075 }1076}