difftreelog
feat generate solidity documentation
in: master
9 files changed
crates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth1#![allow(dead_code)]23use quote::quote;4use darling::{FromMeta, ToTokens};5use inflector::cases;6use std::fmt::Write;7use syn::{8 Expr, FnArg, GenericArgument, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta,9 NestedMeta, PatType, Path, PathArguments, ReturnType, Type, spanned::Spanned,10};1112use crate::{13 fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_path, parse_path_segment,14 parse_result_ok, pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal,15 snake_ident_to_screaming,16};1718struct Is {19 name: Ident,20 pascal_call_name: Ident,21 snake_call_name: Ident,22}23impl Is {24 fn try_from(path: &Path) -> syn::Result<Self> {25 let name = parse_ident_from_path(path, false)?.clone();26 Ok(Self {27 pascal_call_name: pascal_ident_to_call(&name),28 snake_call_name: pascal_ident_to_snake_call(&name),29 name,30 })31 }3233 fn expand_call_def(&self) -> proc_macro2::TokenStream {34 let name = &self.name;35 let pascal_call_name = &self.pascal_call_name;36 quote! {37 #name(#pascal_call_name)38 }39 }4041 fn expand_interface_id(&self) -> proc_macro2::TokenStream {42 let pascal_call_name = &self.pascal_call_name;43 quote! {44 interface_id ^= #pascal_call_name::interface_id();45 }46 }4748 fn expand_supports_interface(&self) -> proc_macro2::TokenStream {49 let pascal_call_name = &self.pascal_call_name;50 quote! {51 #pascal_call_name::supports_interface(interface_id)52 }53 }5455 fn expand_variant_call(&self) -> proc_macro2::TokenStream {56 let name = &self.name;57 let pascal_call_name = &self.pascal_call_name;58 quote! {59 InternalCall::#name(call) => return <Self as ::evm_coder::Callable<#pascal_call_name>>::call(self, Msg {60 call,61 caller: c.caller,62 value: c.value,63 })64 }65 }6667 fn expand_parse(&self) -> proc_macro2::TokenStream {68 let name = &self.name;69 let pascal_call_name = &self.pascal_call_name;70 quote! {71 if let Some(parsed_call) = #pascal_call_name::parse(method_id, reader)? {72 return Ok(Some(Self::#name(parsed_call)))73 }74 }75 }7677 fn expand_generator(&self) -> proc_macro2::TokenStream {78 let pascal_call_name = &self.pascal_call_name;79 quote! {80 #pascal_call_name::generate_solidity_interface(tc, is_impl);81 }82 }8384 fn expand_event_generator(&self) -> proc_macro2::TokenStream {85 let name = &self.name;86 quote! {87 #name::generate_solidity_interface(tc, is_impl);88 }89 }90}9192#[derive(Default)]93struct IsList(Vec<Is>);94impl FromMeta for IsList {95 fn from_list(items: &[NestedMeta]) -> darling::Result<Self> {96 let mut out = Vec::new();97 for item in items {98 match item {99 NestedMeta::Meta(Meta::Path(path)) => out.push(Is::try_from(path)?),100 _ => return Err(syn::Error::new(item.span(), "expected path").into()),101 }102 }103 Ok(Self(out))104 }105}106107#[derive(FromMeta)]108pub struct InterfaceInfo {109 name: Ident,110 #[darling(default)]111 is: IsList,112 #[darling(default)]113 inline_is: IsList,114 #[darling(default)]115 events: IsList,116}117118#[derive(FromMeta)]119struct MethodInfo {120 #[darling(default)]121 rename_selector: Option<String>,122}123124enum AbiType {125 // type126 Plain(Ident),127 // (type1,type2)128 Tuple(Vec<AbiType>),129 // type[]130 Vec(Box<AbiType>),131 // type[20]132 Array(Box<AbiType>, usize),133}134impl AbiType {135 fn try_from(value: &Type) -> syn::Result<Self> {136 let value = Self::try_maybe_special_from(value)?;137 if value.is_special() {138 return Err(syn::Error::new(value.span(), "unexpected special type"));139 }140 Ok(value)141 }142 fn try_maybe_special_from(value: &Type) -> syn::Result<Self> {143 match value {144 Type::Array(arr) => {145 let wrapped = AbiType::try_from(&arr.elem)?;146 match &arr.len {147 Expr::Lit(l) => match &l.lit {148 Lit::Int(i) => {149 let num = i.base10_parse::<usize>()?;150 Ok(AbiType::Array(Box::new(wrapped), num as usize))151 }152 _ => Err(syn::Error::new(arr.len.span(), "should be int literal")),153 },154 _ => Err(syn::Error::new(arr.len.span(), "should be literal")),155 }156 }157 Type::Path(_) => {158 let path = parse_path(value)?;159 let segment = parse_path_segment(path)?;160 if segment.ident == "Vec" {161 let args = match &segment.arguments {162 PathArguments::AngleBracketed(e) => e,163 _ => {164 return Err(syn::Error::new(165 segment.arguments.span(),166 "missing Vec generic",167 ))168 }169 };170 let args = &args.args;171 if args.len() != 1 {172 return Err(syn::Error::new(173 args.span(),174 "expected only one generic for vec",175 ));176 }177 let arg = args.first().unwrap();178179 let ty = match arg {180 GenericArgument::Type(ty) => ty,181 _ => {182 return Err(syn::Error::new(183 arg.span(),184 "expected first generic to be type",185 ))186 }187 };188189 let wrapped = AbiType::try_from(ty)?;190 Ok(Self::Vec(Box::new(wrapped)))191 } else {192 if !segment.arguments.is_empty() {193 return Err(syn::Error::new(194 segment.arguments.span(),195 "unexpected generic arguments for non-vec type",196 ));197 }198 Ok(Self::Plain(segment.ident.clone()))199 }200 }201 Type::Tuple(t) => {202 let mut out = Vec::with_capacity(t.elems.len());203 for el in t.elems.iter() {204 out.push(AbiType::try_from(el)?)205 }206 Ok(Self::Tuple(out))207 }208 _ => Err(syn::Error::new(209 value.span(),210 "unexpected type, only arrays, plain types and tuples are supported",211 )),212 }213 }214 fn is_value(&self) -> bool {215 match self {216 Self::Plain(v) if v == "value" => true,217 _ => false,218 }219 }220 fn is_caller(&self) -> bool {221 match self {222 Self::Plain(v) if v == "caller" => true,223 _ => false,224 }225 }226 fn is_special(&self) -> bool {227 self.is_caller() || self.is_value()228 }229 fn selector_ty_buf(&self, buf: &mut String) -> std::fmt::Result {230 match self {231 AbiType::Plain(t) => {232 write!(buf, "{}", t)233 }234 AbiType::Tuple(t) => {235 write!(buf, "(")?;236 for (i, t) in t.iter().enumerate() {237 if i != 0 {238 write!(buf, ",")?;239 }240 t.selector_ty_buf(buf)?;241 }242 write!(buf, ")")243 }244 AbiType::Vec(v) => {245 v.selector_ty_buf(buf)?;246 write!(buf, "[]")247 }248 AbiType::Array(v, len) => {249 v.selector_ty_buf(buf)?;250 write!(buf, "[{}]", len)251 }252 }253 }254 fn selector_ty(&self) -> String {255 let mut out = String::new();256 self.selector_ty_buf(&mut out).expect("no fmt error");257 out258 }259}260impl ToTokens for AbiType {261 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {262 match self {263 AbiType::Plain(t) => tokens.extend(quote! {#t}),264 AbiType::Tuple(t) => {265 tokens.extend(quote! {(266 #(#t),*267 )});268 }269 AbiType::Vec(v) => tokens.extend(quote! {Vec<#v>}),270 AbiType::Array(v, l) => tokens.extend(quote! {[#v; #l]}),271 }272 }273}274275struct MethodArg {276 name: Ident,277 camel_name: String,278 ty: AbiType,279}280impl MethodArg {281 fn try_from(value: &PatType) -> syn::Result<Self> {282 let name = parse_ident_from_pat(&value.pat)?.clone();283 Ok(Self {284 camel_name: cases::camelcase::to_camel_case(&name.to_string()),285 name,286 ty: AbiType::try_maybe_special_from(&value.ty)?,287 })288 }289 fn is_value(&self) -> bool {290 self.ty.is_value()291 }292 fn is_caller(&self) -> bool {293 self.ty.is_caller()294 }295 fn is_special(&self) -> bool {296 self.ty.is_special()297 }298 fn selector_ty(&self) -> String {299 assert!(!self.is_special());300 self.ty.selector_ty()301 }302303 fn expand_call_def(&self) -> proc_macro2::TokenStream {304 assert!(!self.is_special());305 let name = &self.name;306 let ty = &self.ty;307308 quote! {309 #name: #ty310 }311 }312313 fn expand_parse(&self) -> proc_macro2::TokenStream {314 assert!(!self.is_special());315 let name = &self.name;316 quote! {317 #name: reader.abi_read()?318 }319 }320321 fn expand_call_arg(&self) -> proc_macro2::TokenStream {322 if self.is_value() {323 quote! {324 c.value.clone()325 }326 } else if self.is_caller() {327 quote! {328 c.caller.clone()329 }330 } else {331 let name = &self.name;332 quote! {333 #name334 }335 }336 }337338 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {339 let camel_name = &self.camel_name.to_string();340 let ty = &self.ty;341 quote! {342 <NamedArgument<#ty>>::new(#camel_name)343 }344 }345}346347#[derive(PartialEq)]348enum Mutability {349 Mutable,350 View,351 Pure,352}353354struct Method {355 name: Ident,356 camel_name: String,357 pascal_name: Ident,358 screaming_name: Ident,359 selector_str: String,360 selector: u32,361 args: Vec<MethodArg>,362 has_normal_args: bool,363 mutability: Mutability,364 result: Type,365}366impl Method {367 fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {368 let mut info = MethodInfo {369 rename_selector: None,370 };371 for attr in &value.attrs {372 let ident = parse_ident_from_path(&attr.path, false)?;373 if ident == "solidity" {374 let args = attr.parse_meta().unwrap();375 info = MethodInfo::from_meta(&args).unwrap();376 } else if ident == "doc" {377 // TODO: Add docs to evm interfaces378 }379 }380 let ident = &value.sig.ident;381 let ident_str = ident.to_string();382 if !cases::snakecase::is_snake_case(&ident_str) {383 return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute"));384 }385386 let mut mutability = Mutability::Pure;387388 if let Some(FnArg::Receiver(receiver)) = value389 .sig390 .inputs391 .iter()392 .find(|arg| matches!(arg, FnArg::Receiver(_)))393 {394 if receiver.reference.is_none() {395 return Err(syn::Error::new(396 receiver.span(),397 "receiver should be by ref",398 ));399 }400 if receiver.mutability.is_some() {401 mutability = Mutability::Mutable;402 } else {403 mutability = Mutability::View;404 }405 }406 let mut args = Vec::new();407 for typ in value408 .sig409 .inputs410 .iter()411 .filter(|arg| matches!(arg, FnArg::Typed(_)))412 {413 let typ = match typ {414 FnArg::Typed(typ) => typ,415 _ => unreachable!(),416 };417 args.push(MethodArg::try_from(typ)?);418 }419420 if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) {421 return Err(syn::Error::new(422 args.iter().find(|arg| arg.is_value()).unwrap().ty.span(),423 "payable function should be mutable",424 ));425 }426427 let result = match &value.sig.output {428 ReturnType::Type(_, ty) => ty,429 _ => 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)")),430 };431 let result = parse_result_ok(result)?;432433 let camel_name = info434 .rename_selector435 .unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));436 let mut selector_str = camel_name.clone();437 selector_str.push('(');438 let mut has_normal_args = false;439 for (i, arg) in args.iter().filter(|arg| !arg.is_special()).enumerate() {440 if i != 0 {441 selector_str.push(',');442 }443 write!(selector_str, "{}", arg.selector_ty()).unwrap();444 has_normal_args = true;445 }446 selector_str.push(')');447 let selector = fn_selector_str(&selector_str);448449 Ok(Self {450 name: ident.clone(),451 camel_name,452 pascal_name: snake_ident_to_pascal(ident),453 screaming_name: snake_ident_to_screaming(ident),454 selector_str,455 selector,456 args,457 has_normal_args,458 mutability,459 result: result.clone(),460 })461 }462 fn expand_call_def(&self) -> proc_macro2::TokenStream {463 let defs = self464 .args465 .iter()466 .filter(|a| !a.is_special())467 .map(|a| a.expand_call_def());468 let pascal_name = &self.pascal_name;469470 if self.has_normal_args {471 quote! {472 #pascal_name {473 #(474 #defs,475 )*476 }477 }478 } else {479 quote! {#pascal_name}480 }481 }482483 fn expand_const(&self) -> proc_macro2::TokenStream {484 let screaming_name = &self.screaming_name;485 let selector = self.selector;486 let selector_str = &self.selector_str;487 quote! {488 #[doc = #selector_str]489 const #screaming_name: u32 = #selector;490 }491 }492493 fn expand_interface_id(&self) -> proc_macro2::TokenStream {494 let screaming_name = &self.screaming_name;495 quote! {496 interface_id ^= Self::#screaming_name;497 }498 }499500 fn expand_parse(&self) -> proc_macro2::TokenStream {501 let pascal_name = &self.pascal_name;502 let screaming_name = &self.screaming_name;503 if self.has_normal_args {504 let parsers = self505 .args506 .iter()507 .filter(|a| !a.is_special())508 .map(|a| a.expand_parse());509 quote! {510 Self::#screaming_name => return Ok(Some(Self::#pascal_name {511 #(512 #parsers,513 )*514 }))515 }516 } else {517 quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }518 }519 }520521 fn expand_variant_call(&self) -> proc_macro2::TokenStream {522 let pascal_name = &self.pascal_name;523 let name = &self.name;524525 let matcher = if self.has_normal_args {526 let names = self527 .args528 .iter()529 .filter(|a| !a.is_special())530 .map(|a| &a.name);531532 quote! {{533 #(534 #names,535 )*536 }}537 } else {538 quote! {}539 };540541 let receiver = match self.mutability {542 Mutability::Mutable | Mutability::View => quote! {self.},543 Mutability::Pure => quote! {Self::},544 };545 let args = self.args.iter().map(|a| a.expand_call_arg());546547 quote! {548 InternalCall::#pascal_name #matcher => {549 let result = #receiver #name(550 #(551 #args,552 )*553 )?;554 (&result).abi_write(&mut writer);555 }556 }557 }558559 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {560 let camel_name = &self.camel_name;561 let mutability = match self.mutability {562 Mutability::Mutable => quote! {SolidityMutability::Mutable},563 Mutability::View => quote! { SolidityMutability::View },564 Mutability::Pure => quote! {SolidityMutability::Pure},565 };566 let result = &self.result;567568 let args = self569 .args570 .iter()571 .filter(|a| !a.is_special())572 .map(MethodArg::expand_solidity_argument);573 let selector = format!("{} {:0>8x}", self.selector_str, self.selector);574575 quote! {576 SolidityFunction {577 selector: #selector,578 name: #camel_name,579 mutability: #mutability,580 args: (581 #(582 #args,583 )*584 ),585 result: <UnnamedArgument<#result>>::default(),586 }587 }588 }589}590591pub struct SolidityInterface {592 generics: Generics,593 name: Box<syn::Type>,594 info: InterfaceInfo,595 methods: Vec<Method>,596}597impl SolidityInterface {598 pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {599 let mut methods = Vec::new();600601 for item in &value.items {602 if let ImplItem::Method(method) = item {603 methods.push(Method::try_from(method)?)604 }605 }606 Ok(Self {607 generics: value.generics.clone(),608 name: value.self_ty.clone(),609 info,610 methods,611 })612 }613 pub fn expand(self) -> proc_macro2::TokenStream {614 let name = self.name;615616 let solidity_name = self.info.name.to_string();617 let call_name = pascal_ident_to_call(&self.info.name);618 let generics = self.generics;619620 let call_sub = self621 .info622 .inline_is623 .0624 .iter()625 .chain(self.info.is.0.iter())626 .map(Is::expand_call_def);627 let call_parse = self628 .info629 .inline_is630 .0631 .iter()632 .chain(self.info.is.0.iter())633 .map(Is::expand_parse);634 let call_variants = self635 .info636 .inline_is637 .0638 .iter()639 .chain(self.info.is.0.iter())640 .map(Is::expand_variant_call);641642 let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);643 let supports_interface = self.info.is.0.iter().map(Is::expand_supports_interface);644645 let calls = self.methods.iter().map(Method::expand_call_def);646 let consts = self.methods.iter().map(Method::expand_const);647 let interface_id = self.methods.iter().map(Method::expand_interface_id);648 let parsers = self.methods.iter().map(Method::expand_parse);649 let call_variants_this = self.methods.iter().map(Method::expand_variant_call);650 let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);651652 // TODO: Inline inline_is653 let solidity_is = self654 .info655 .is656 .0657 .iter()658 .chain(self.info.inline_is.0.iter())659 .map(|is| is.name.to_string());660 let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string());661 let solidity_generators = self662 .info663 .is664 .0665 .iter()666 .chain(self.info.inline_is.0.iter())667 .map(Is::expand_generator);668 let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);669670 // let methods = self.methods.iter().map(Method::solidity_def);671672 quote! {673 #[derive(Debug)]674 pub enum #call_name {675 ERC165Call(::evm_coder::ERC165Call),676 #(677 #calls,678 )*679 #(680 #call_sub,681 )*682 }683 impl #call_name {684 #(685 #consts686 )*687 pub const fn interface_id() -> u32 {688 let mut interface_id = 0;689 #(#interface_id)*690 #(#inline_interface_id)*691 interface_id692 }693 pub fn supports_interface(interface_id: u32) -> bool {694 interface_id != 0xffffff && (695 interface_id == ::evm_coder::ERC165Call::INTERFACE_ID ||696 interface_id == Self::interface_id()697 #(698 || #supports_interface699 )*700 )701 }702 pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {703 use evm_coder::solidity::*;704 use core::fmt::Write;705 let interface = SolidityInterface {706 name: #solidity_name,707 is: &["Dummy", "ERC165", #(708 #solidity_is,709 )* #(710 #solidity_events_is,711 )* ],712 functions: (#(713 #solidity_functions,714 )*),715 };716 if is_impl {717 tc.collect("// 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());718 } else {719 tc.collect("// Common stubs holder\ninterface Dummy {\n}\ninterface ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);\n}\n".into());720 }721 #(722 #solidity_generators723 )*724 #(725 #solidity_event_generators726 )*727728 let mut out = string::new();729 // In solidity interface usage (is) should be preceeded by interface definition730 // This comment helps to sort it in a set731 if #solidity_name.starts_with("Inline") {732 out.push_str("// Inline\n");733 }734 let _ = interface.format(is_impl, &mut out, tc);735 tc.collect(out);736 }737 }738 impl ::evm_coder::Call for #call_name {739 fn parse(method_id: u32, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {740 use ::evm_coder::abi::AbiRead;741 match method_id {742 ::evm_coder::ERC165Call::INTERFACE_ID => return Ok(::evm_coder::ERC165Call::parse(method_id, reader)?.map(Self::ERC165Call)),743 #(744 #parsers,745 )*746 _ => {},747 }748 #(749 #call_parse750 )else*751 return Ok(None);752 }753 }754 impl #generics ::evm_coder::Callable<#call_name> for #name {755 #[allow(unreachable_code)] // In case of no inner calls756 fn call(&mut self, c: Msg<#call_name>) -> Result<::evm_coder::abi::AbiWriter> {757 use ::evm_coder::abi::AbiWrite;758 type InternalCall = #call_name;759 match c.call {760 #(761 #call_variants,762 )*763 InternalCall::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {interface_id}) => {764 let mut writer = ::evm_coder::abi::AbiWriter::default();765 writer.bool(&InternalCall::supports_interface(interface_id));766 return Ok(writer);767 }768 _ => {},769 }770 let mut writer = ::evm_coder::abi::AbiWriter::default();771 match c.call {772 #(773 #call_variants_this,774 )*775 _ => unreachable!()776 }777 Ok(writer)778 }779 }780 }781 }782}1#![allow(dead_code)]23use quote::quote;4use darling::{FromMeta, ToTokens};5use inflector::cases;6use std::fmt::Write;7use syn::{8 Expr, FnArg, GenericArgument, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta,9 MetaNameValue, NestedMeta, PatType, Path, PathArguments, ReturnType, Type, spanned::Spanned,10};1112use crate::{13 fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_path, parse_path_segment,14 parse_result_ok, pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal,15 snake_ident_to_screaming,16};1718struct Is {19 name: Ident,20 pascal_call_name: Ident,21 snake_call_name: Ident,22}23impl Is {24 fn try_from(path: &Path) -> syn::Result<Self> {25 let name = parse_ident_from_path(path, false)?.clone();26 Ok(Self {27 pascal_call_name: pascal_ident_to_call(&name),28 snake_call_name: pascal_ident_to_snake_call(&name),29 name,30 })31 }3233 fn expand_call_def(&self) -> proc_macro2::TokenStream {34 let name = &self.name;35 let pascal_call_name = &self.pascal_call_name;36 quote! {37 #name(#pascal_call_name)38 }39 }4041 fn expand_interface_id(&self) -> proc_macro2::TokenStream {42 let pascal_call_name = &self.pascal_call_name;43 quote! {44 interface_id ^= #pascal_call_name::interface_id();45 }46 }4748 fn expand_supports_interface(&self) -> proc_macro2::TokenStream {49 let pascal_call_name = &self.pascal_call_name;50 quote! {51 #pascal_call_name::supports_interface(interface_id)52 }53 }5455 fn expand_variant_call(&self) -> proc_macro2::TokenStream {56 let name = &self.name;57 let pascal_call_name = &self.pascal_call_name;58 quote! {59 InternalCall::#name(call) => return <Self as ::evm_coder::Callable<#pascal_call_name>>::call(self, Msg {60 call,61 caller: c.caller,62 value: c.value,63 })64 }65 }6667 fn expand_parse(&self) -> proc_macro2::TokenStream {68 let name = &self.name;69 let pascal_call_name = &self.pascal_call_name;70 quote! {71 if let Some(parsed_call) = #pascal_call_name::parse(method_id, reader)? {72 return Ok(Some(Self::#name(parsed_call)))73 }74 }75 }7677 fn expand_generator(&self) -> proc_macro2::TokenStream {78 let pascal_call_name = &self.pascal_call_name;79 quote! {80 #pascal_call_name::generate_solidity_interface(tc, is_impl);81 }82 }8384 fn expand_event_generator(&self) -> proc_macro2::TokenStream {85 let name = &self.name;86 quote! {87 #name::generate_solidity_interface(tc, is_impl);88 }89 }90}9192#[derive(Default)]93struct IsList(Vec<Is>);94impl FromMeta for IsList {95 fn from_list(items: &[NestedMeta]) -> darling::Result<Self> {96 let mut out = Vec::new();97 for item in items {98 match item {99 NestedMeta::Meta(Meta::Path(path)) => out.push(Is::try_from(path)?),100 _ => return Err(syn::Error::new(item.span(), "expected path").into()),101 }102 }103 Ok(Self(out))104 }105}106107#[derive(FromMeta)]108pub struct InterfaceInfo {109 name: Ident,110 #[darling(default)]111 is: IsList,112 #[darling(default)]113 inline_is: IsList,114 #[darling(default)]115 events: IsList,116}117118#[derive(FromMeta)]119struct MethodInfo {120 #[darling(default)]121 rename_selector: Option<String>,122}123124enum AbiType {125 // type126 Plain(Ident),127 // (type1,type2)128 Tuple(Vec<AbiType>),129 // type[]130 Vec(Box<AbiType>),131 // type[20]132 Array(Box<AbiType>, usize),133}134impl AbiType {135 fn try_from(value: &Type) -> syn::Result<Self> {136 let value = Self::try_maybe_special_from(value)?;137 if value.is_special() {138 return Err(syn::Error::new(value.span(), "unexpected special type"));139 }140 Ok(value)141 }142 fn try_maybe_special_from(value: &Type) -> syn::Result<Self> {143 match value {144 Type::Array(arr) => {145 let wrapped = AbiType::try_from(&arr.elem)?;146 match &arr.len {147 Expr::Lit(l) => match &l.lit {148 Lit::Int(i) => {149 let num = i.base10_parse::<usize>()?;150 Ok(AbiType::Array(Box::new(wrapped), num as usize))151 }152 _ => Err(syn::Error::new(arr.len.span(), "should be int literal")),153 },154 _ => Err(syn::Error::new(arr.len.span(), "should be literal")),155 }156 }157 Type::Path(_) => {158 let path = parse_path(value)?;159 let segment = parse_path_segment(path)?;160 if segment.ident == "Vec" {161 let args = match &segment.arguments {162 PathArguments::AngleBracketed(e) => e,163 _ => {164 return Err(syn::Error::new(165 segment.arguments.span(),166 "missing Vec generic",167 ))168 }169 };170 let args = &args.args;171 if args.len() != 1 {172 return Err(syn::Error::new(173 args.span(),174 "expected only one generic for vec",175 ));176 }177 let arg = args.first().unwrap();178179 let ty = match arg {180 GenericArgument::Type(ty) => ty,181 _ => {182 return Err(syn::Error::new(183 arg.span(),184 "expected first generic to be type",185 ))186 }187 };188189 let wrapped = AbiType::try_from(ty)?;190 Ok(Self::Vec(Box::new(wrapped)))191 } else {192 if !segment.arguments.is_empty() {193 return Err(syn::Error::new(194 segment.arguments.span(),195 "unexpected generic arguments for non-vec type",196 ));197 }198 Ok(Self::Plain(segment.ident.clone()))199 }200 }201 Type::Tuple(t) => {202 let mut out = Vec::with_capacity(t.elems.len());203 for el in t.elems.iter() {204 out.push(AbiType::try_from(el)?)205 }206 Ok(Self::Tuple(out))207 }208 _ => Err(syn::Error::new(209 value.span(),210 "unexpected type, only arrays, plain types and tuples are supported",211 )),212 }213 }214 fn is_value(&self) -> bool {215 match self {216 Self::Plain(v) if v == "value" => true,217 _ => false,218 }219 }220 fn is_caller(&self) -> bool {221 match self {222 Self::Plain(v) if v == "caller" => true,223 _ => false,224 }225 }226 fn is_special(&self) -> bool {227 self.is_caller() || self.is_value()228 }229 fn selector_ty_buf(&self, buf: &mut String) -> std::fmt::Result {230 match self {231 AbiType::Plain(t) => {232 write!(buf, "{}", t)233 }234 AbiType::Tuple(t) => {235 write!(buf, "(")?;236 for (i, t) in t.iter().enumerate() {237 if i != 0 {238 write!(buf, ",")?;239 }240 t.selector_ty_buf(buf)?;241 }242 write!(buf, ")")243 }244 AbiType::Vec(v) => {245 v.selector_ty_buf(buf)?;246 write!(buf, "[]")247 }248 AbiType::Array(v, len) => {249 v.selector_ty_buf(buf)?;250 write!(buf, "[{}]", len)251 }252 }253 }254 fn selector_ty(&self) -> String {255 let mut out = String::new();256 self.selector_ty_buf(&mut out).expect("no fmt error");257 out258 }259}260impl ToTokens for AbiType {261 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {262 match self {263 AbiType::Plain(t) => tokens.extend(quote! {#t}),264 AbiType::Tuple(t) => {265 tokens.extend(quote! {(266 #(#t),*267 )});268 }269 AbiType::Vec(v) => tokens.extend(quote! {Vec<#v>}),270 AbiType::Array(v, l) => tokens.extend(quote! {[#v; #l]}),271 }272 }273}274275struct MethodArg {276 name: Ident,277 camel_name: String,278 ty: AbiType,279}280impl MethodArg {281 fn try_from(value: &PatType) -> syn::Result<Self> {282 let name = parse_ident_from_pat(&value.pat)?.clone();283 Ok(Self {284 camel_name: cases::camelcase::to_camel_case(&name.to_string()),285 name,286 ty: AbiType::try_maybe_special_from(&value.ty)?,287 })288 }289 fn is_value(&self) -> bool {290 self.ty.is_value()291 }292 fn is_caller(&self) -> bool {293 self.ty.is_caller()294 }295 fn is_special(&self) -> bool {296 self.ty.is_special()297 }298 fn selector_ty(&self) -> String {299 assert!(!self.is_special());300 self.ty.selector_ty()301 }302303 fn expand_call_def(&self) -> proc_macro2::TokenStream {304 assert!(!self.is_special());305 let name = &self.name;306 let ty = &self.ty;307308 quote! {309 #name: #ty310 }311 }312313 fn expand_parse(&self) -> proc_macro2::TokenStream {314 assert!(!self.is_special());315 let name = &self.name;316 quote! {317 #name: reader.abi_read()?318 }319 }320321 fn expand_call_arg(&self) -> proc_macro2::TokenStream {322 if self.is_value() {323 quote! {324 c.value.clone()325 }326 } else if self.is_caller() {327 quote! {328 c.caller.clone()329 }330 } else {331 let name = &self.name;332 quote! {333 #name334 }335 }336 }337338 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {339 let camel_name = &self.camel_name.to_string();340 let ty = &self.ty;341 quote! {342 <NamedArgument<#ty>>::new(#camel_name)343 }344 }345}346347#[derive(PartialEq)]348enum Mutability {349 Mutable,350 View,351 Pure,352}353354struct Method {355 name: Ident,356 camel_name: String,357 pascal_name: Ident,358 screaming_name: Ident,359 selector_str: String,360 selector: u32,361 args: Vec<MethodArg>,362 has_normal_args: bool,363 mutability: Mutability,364 result: Type,365 docs: Vec<String>,366}367impl Method {368 fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {369 let mut info = MethodInfo {370 rename_selector: None,371 };372 let mut docs = Vec::new();373 for attr in &value.attrs {374 let ident = parse_ident_from_path(&attr.path, false)?;375 if ident == "solidity" {376 let args = attr.parse_meta().unwrap();377 info = MethodInfo::from_meta(&args).unwrap();378 } else if ident == "doc" {379 let args = attr.parse_meta().unwrap();380 let value = match args {381 Meta::NameValue(MetaNameValue {382 lit: Lit::Str(str), ..383 }) => str.value(),384 _ => unreachable!(),385 };386 docs.push(value);387 }388 }389 let ident = &value.sig.ident;390 let ident_str = ident.to_string();391 if !cases::snakecase::is_snake_case(&ident_str) {392 return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute"));393 }394395 let mut mutability = Mutability::Pure;396397 if let Some(FnArg::Receiver(receiver)) = value398 .sig399 .inputs400 .iter()401 .find(|arg| matches!(arg, FnArg::Receiver(_)))402 {403 if receiver.reference.is_none() {404 return Err(syn::Error::new(405 receiver.span(),406 "receiver should be by ref",407 ));408 }409 if receiver.mutability.is_some() {410 mutability = Mutability::Mutable;411 } else {412 mutability = Mutability::View;413 }414 }415 let mut args = Vec::new();416 for typ in value417 .sig418 .inputs419 .iter()420 .filter(|arg| matches!(arg, FnArg::Typed(_)))421 {422 let typ = match typ {423 FnArg::Typed(typ) => typ,424 _ => unreachable!(),425 };426 args.push(MethodArg::try_from(typ)?);427 }428429 if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) {430 return Err(syn::Error::new(431 args.iter().find(|arg| arg.is_value()).unwrap().ty.span(),432 "payable function should be mutable",433 ));434 }435436 let result = match &value.sig.output {437 ReturnType::Type(_, ty) => ty,438 _ => 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)")),439 };440 let result = parse_result_ok(result)?;441442 let camel_name = info443 .rename_selector444 .unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));445 let mut selector_str = camel_name.clone();446 selector_str.push('(');447 let mut has_normal_args = false;448 for (i, arg) in args.iter().filter(|arg| !arg.is_special()).enumerate() {449 if i != 0 {450 selector_str.push(',');451 }452 write!(selector_str, "{}", arg.selector_ty()).unwrap();453 has_normal_args = true;454 }455 selector_str.push(')');456 let selector = fn_selector_str(&selector_str);457458 Ok(Self {459 name: ident.clone(),460 camel_name,461 pascal_name: snake_ident_to_pascal(ident),462 screaming_name: snake_ident_to_screaming(ident),463 selector_str,464 selector,465 args,466 has_normal_args,467 mutability,468 result: result.clone(),469 docs,470 })471 }472 fn expand_call_def(&self) -> proc_macro2::TokenStream {473 let defs = self474 .args475 .iter()476 .filter(|a| !a.is_special())477 .map(|a| a.expand_call_def());478 let pascal_name = &self.pascal_name;479480 if self.has_normal_args {481 quote! {482 #pascal_name {483 #(484 #defs,485 )*486 }487 }488 } else {489 quote! {#pascal_name}490 }491 }492493 fn expand_const(&self) -> proc_macro2::TokenStream {494 let screaming_name = &self.screaming_name;495 let selector = self.selector;496 let selector_str = &self.selector_str;497 quote! {498 #[doc = #selector_str]499 const #screaming_name: u32 = #selector;500 }501 }502503 fn expand_interface_id(&self) -> proc_macro2::TokenStream {504 let screaming_name = &self.screaming_name;505 quote! {506 interface_id ^= Self::#screaming_name;507 }508 }509510 fn expand_parse(&self) -> proc_macro2::TokenStream {511 let pascal_name = &self.pascal_name;512 let screaming_name = &self.screaming_name;513 if self.has_normal_args {514 let parsers = self515 .args516 .iter()517 .filter(|a| !a.is_special())518 .map(|a| a.expand_parse());519 quote! {520 Self::#screaming_name => return Ok(Some(Self::#pascal_name {521 #(522 #parsers,523 )*524 }))525 }526 } else {527 quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }528 }529 }530531 fn expand_variant_call(&self) -> proc_macro2::TokenStream {532 let pascal_name = &self.pascal_name;533 let name = &self.name;534535 let matcher = if self.has_normal_args {536 let names = self537 .args538 .iter()539 .filter(|a| !a.is_special())540 .map(|a| &a.name);541542 quote! {{543 #(544 #names,545 )*546 }}547 } else {548 quote! {}549 };550551 let receiver = match self.mutability {552 Mutability::Mutable | Mutability::View => quote! {self.},553 Mutability::Pure => quote! {Self::},554 };555 let args = self.args.iter().map(|a| a.expand_call_arg());556557 quote! {558 InternalCall::#pascal_name #matcher => {559 let result = #receiver #name(560 #(561 #args,562 )*563 )?;564 (&result).abi_write(&mut writer);565 }566 }567 }568569 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {570 let camel_name = &self.camel_name;571 let mutability = match self.mutability {572 Mutability::Mutable => quote! {SolidityMutability::Mutable},573 Mutability::View => quote! { SolidityMutability::View },574 Mutability::Pure => quote! {SolidityMutability::Pure},575 };576 let result = &self.result;577578 let args = self579 .args580 .iter()581 .filter(|a| !a.is_special())582 .map(MethodArg::expand_solidity_argument);583 let docs = self.docs.iter();584 let selector = format!("{} {:0>8x}", self.selector_str, self.selector);585586 quote! {587 SolidityFunction {588 docs: &[#(#docs),*],589 selector: #selector,590 name: #camel_name,591 mutability: #mutability,592 args: (593 #(594 #args,595 )*596 ),597 result: <UnnamedArgument<#result>>::default(),598 }599 }600 }601}602603pub struct SolidityInterface {604 generics: Generics,605 name: Box<syn::Type>,606 info: InterfaceInfo,607 methods: Vec<Method>,608}609impl SolidityInterface {610 pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {611 let mut methods = Vec::new();612613 for item in &value.items {614 if let ImplItem::Method(method) = item {615 methods.push(Method::try_from(method)?)616 }617 }618 Ok(Self {619 generics: value.generics.clone(),620 name: value.self_ty.clone(),621 info,622 methods,623 })624 }625 pub fn expand(self) -> proc_macro2::TokenStream {626 let name = self.name;627628 let solidity_name = self.info.name.to_string();629 let call_name = pascal_ident_to_call(&self.info.name);630 let generics = self.generics;631632 let call_sub = self633 .info634 .inline_is635 .0636 .iter()637 .chain(self.info.is.0.iter())638 .map(Is::expand_call_def);639 let call_parse = self640 .info641 .inline_is642 .0643 .iter()644 .chain(self.info.is.0.iter())645 .map(Is::expand_parse);646 let call_variants = self647 .info648 .inline_is649 .0650 .iter()651 .chain(self.info.is.0.iter())652 .map(Is::expand_variant_call);653654 let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);655 let supports_interface = self.info.is.0.iter().map(Is::expand_supports_interface);656657 let calls = self.methods.iter().map(Method::expand_call_def);658 let consts = self.methods.iter().map(Method::expand_const);659 let interface_id = self.methods.iter().map(Method::expand_interface_id);660 let parsers = self.methods.iter().map(Method::expand_parse);661 let call_variants_this = self.methods.iter().map(Method::expand_variant_call);662 let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);663664 // TODO: Inline inline_is665 let solidity_is = self666 .info667 .is668 .0669 .iter()670 .chain(self.info.inline_is.0.iter())671 .map(|is| is.name.to_string());672 let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string());673 let solidity_generators = self674 .info675 .is676 .0677 .iter()678 .chain(self.info.inline_is.0.iter())679 .map(Is::expand_generator);680 let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);681682 // let methods = self.methods.iter().map(Method::solidity_def);683684 quote! {685 #[derive(Debug)]686 pub enum #call_name {687 ERC165Call(::evm_coder::ERC165Call),688 #(689 #calls,690 )*691 #(692 #call_sub,693 )*694 }695 impl #call_name {696 #(697 #consts698 )*699 pub const fn interface_id() -> u32 {700 let mut interface_id = 0;701 #(#interface_id)*702 #(#inline_interface_id)*703 interface_id704 }705 pub fn supports_interface(interface_id: u32) -> bool {706 interface_id != 0xffffff && (707 interface_id == ::evm_coder::ERC165Call::INTERFACE_ID ||708 interface_id == Self::interface_id()709 #(710 || #supports_interface711 )*712 )713 }714 pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {715 use evm_coder::solidity::*;716 use core::fmt::Write;717 let interface = SolidityInterface {718 name: #solidity_name,719 selector: Self::interface_id(),720 is: &["Dummy", "ERC165", #(721 #solidity_is,722 )* #(723 #solidity_events_is,724 )* ],725 functions: (#(726 #solidity_functions,727 )*),728 };729 if is_impl {730 tc.collect("// 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());731 } else {732 tc.collect("// Common stubs holder\ninterface Dummy {\n}\ninterface ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);\n}\n".into());733 }734 #(735 #solidity_generators736 )*737 #(738 #solidity_event_generators739 )*740741 let mut out = string::new();742 // In solidity interface usage (is) should be preceeded by interface definition743 // This comment helps to sort it in a set744 if #solidity_name.starts_with("Inline") {745 out.push_str("// Inline\n");746 }747 let _ = interface.format(is_impl, &mut out, tc);748 tc.collect(out);749 }750 }751 impl ::evm_coder::Call for #call_name {752 fn parse(method_id: u32, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {753 use ::evm_coder::abi::AbiRead;754 match method_id {755 ::evm_coder::ERC165Call::INTERFACE_ID => return Ok(::evm_coder::ERC165Call::parse(method_id, reader)?.map(Self::ERC165Call)),756 #(757 #parsers,758 )*759 _ => {},760 }761 #(762 #call_parse763 )else*764 return Ok(None);765 }766 }767 impl #generics ::evm_coder::Callable<#call_name> for #name {768 #[allow(unreachable_code)] // In case of no inner calls769 fn call(&mut self, c: Msg<#call_name>) -> Result<::evm_coder::abi::AbiWriter> {770 use ::evm_coder::abi::AbiWrite;771 type InternalCall = #call_name;772 match c.call {773 #(774 #call_variants,775 )*776 InternalCall::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {interface_id}) => {777 let mut writer = ::evm_coder::abi::AbiWriter::default();778 writer.bool(&InternalCall::supports_interface(interface_id));779 return Ok(writer);780 }781 _ => {},782 }783 let mut writer = ::evm_coder::abi::AbiWriter::default();784 match c.call {785 #(786 #call_variants_this,787 )*788 _ => unreachable!()789 }790 Ok(writer)791 }792 }793 }794 }795}crates/evm-coder-macros/src/to_log.rsdiffbeforeafterboth--- a/crates/evm-coder-macros/src/to_log.rs
+++ b/crates/evm-coder-macros/src/to_log.rs
@@ -183,6 +183,7 @@
use evm_coder::solidity::*;
use core::fmt::Write;
let interface = SolidityInterface {
+ selector: 0,
name: #solidity_name,
is: &[],
functions: (#(
crates/evm-coder/src/solidity.rsdiffbeforeafterboth--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -84,6 +84,7 @@
solidity_type_name! {
uint8 => "uint8" true = "0",
uint32 => "uint32" true = "0",
+ uint64 => "uint64" true = "0",
uint128 => "uint128" true = "0",
uint256 => "uint256" true = "0",
address => "address" true = "0x0000000000000000000000000000000000000000",
@@ -376,6 +377,7 @@
Mutable,
}
pub struct SolidityFunction<A, R> {
+ pub docs: &'static [&'static str],
pub selector: &'static str,
pub name: &'static str,
pub args: A,
@@ -389,6 +391,12 @@
writer: &mut impl fmt::Write,
tc: &TypeCollector,
) -> fmt::Result {
+ for doc in self.docs {
+ writeln!(writer, "\t//{}", doc)?;
+ }
+ if !self.docs.is_empty() {
+ writeln!(writer, "\t//")?;
+ }
writeln!(writer, "\t// Selector: {}", self.selector)?;
write!(writer, "\tfunction {}(", self.name)?;
self.args.solidity_name(writer, tc)?;
@@ -449,6 +457,7 @@
}
pub struct SolidityInterface<F: SolidityFunctions> {
+ pub selector: u32,
pub name: &'static str,
pub is: &'static [&'static str],
pub functions: F,
@@ -461,6 +470,9 @@
out: &mut impl fmt::Write,
tc: &TypeCollector,
) -> fmt::Result {
+ if self.selector != 0 {
+ writeln!(out, "// Selector: {:0>8x}", self.selector)?;
+ }
if is_impl {
write!(out, "contract ")?;
} else {
pallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -31,6 +31,7 @@
);
}
+// Selector: 942e8b22
contract ERC20 is Dummy, ERC165, ERC20Events {
// Selector: name() 06fdde03
function name() public view returns (string memory) {
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -61,6 +61,7 @@
Ok(string::from_utf8_lossy(&self.token_prefix).into())
}
+ /// Returns token's const_metadata
#[solidity(rename_selector = "tokenURI")]
fn token_uri(&self, token_id: uint256) -> Result<string> {
let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
@@ -79,6 +80,7 @@
Ok(index)
}
+ /// Not implemented
fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {
// TODO: Not implemetable
Err("not implemented".into())
@@ -103,6 +105,7 @@
.owner
.as_eth())
}
+ /// Not implemented
fn safe_transfer_from_with_data(
&mut self,
_from: address,
@@ -114,6 +117,7 @@
// TODO: Not implemetable
Err("not implemented".into())
}
+ /// Not implemented
fn safe_transfer_from(
&mut self,
_from: address,
@@ -159,6 +163,7 @@
Ok(())
}
+ /// Not implemented
fn set_approval_for_all(
&mut self,
_caller: caller,
@@ -169,11 +174,13 @@
Err("not implemented".into())
}
+ /// Not implemented
fn get_approved(&self, _token_id: uint256) -> Result<address> {
// TODO: Not implemetable
Err("not implemented".into())
}
+ /// Not implemented
fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {
// TODO: Not implemetable
Err("not implemented".into())
@@ -197,6 +204,8 @@
Ok(false)
}
+ /// `token_id` should be obtained with `next_token_id` method,
+ /// unlike standard, you can't specify it manually
fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let to = T::CrossAccountId::from_eth(to);
@@ -223,6 +232,8 @@
Ok(true)
}
+ /// `token_id` should be obtained with `next_token_id` method,
+ /// unlike standard, you can't specify it manually
#[solidity(rename_selector = "mintWithTokenURI")]
fn mint_with_token_uri(
&mut self,
@@ -257,6 +268,7 @@
Ok(true)
}
+ /// Not implemented
fn finish_minting(&mut self, _caller: caller) -> Result<bool> {
Err("not implementable".into())
}
pallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -51,6 +51,17 @@
event MintingFinished();
}
+// Selector: 42966c68
+contract ERC721Burnable is Dummy, ERC165 {
+ // Selector: burn(uint256) 42966c68
+ function burn(uint256 tokenId) public {
+ require(false, stub_error);
+ tokenId;
+ dummy = 0;
+ }
+}
+
+// Selector: 58800161
contract ERC721 is Dummy, ERC165, ERC721Events {
// Selector: balanceOf(address) 70a08231
function balanceOf(address owner) public view returns (uint256) {
@@ -68,6 +79,8 @@
return 0x0000000000000000000000000000000000000000;
}
+ // Not implemented
+ //
// Selector: safeTransferFromWithData(address,address,uint256,bytes) 60a11672
function safeTransferFromWithData(
address from,
@@ -83,6 +96,8 @@
dummy = 0;
}
+ // Not implemented
+ //
// Selector: safeTransferFrom(address,address,uint256) 42842e0e
function safeTransferFrom(
address from,
@@ -117,6 +132,8 @@
dummy = 0;
}
+ // Not implemented
+ //
// Selector: setApprovalForAll(address,bool) a22cb465
function setApprovalForAll(address operator, bool approved) public {
require(false, stub_error);
@@ -125,6 +142,8 @@
dummy = 0;
}
+ // Not implemented
+ //
// Selector: getApproved(uint256) 081812fc
function getApproved(uint256 tokenId) public view returns (address) {
require(false, stub_error);
@@ -133,6 +152,8 @@
return 0x0000000000000000000000000000000000000000;
}
+ // Not implemented
+ //
// Selector: isApprovedForAll(address,address) e985e9c5
function isApprovedForAll(address owner, address operator)
public
@@ -147,45 +168,7 @@
}
}
-contract ERC721Burnable is Dummy, ERC165 {
- // Selector: burn(uint256) 42966c68
- function burn(uint256 tokenId) public {
- require(false, stub_error);
- tokenId;
- dummy = 0;
- }
-}
-
-contract ERC721Enumerable is Dummy, ERC165 {
- // Selector: tokenByIndex(uint256) 4f6ccce7
- function tokenByIndex(uint256 index) public view returns (uint256) {
- require(false, stub_error);
- index;
- dummy;
- return 0;
- }
-
- // Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
- function tokenOfOwnerByIndex(address owner, uint256 index)
- public
- view
- returns (uint256)
- {
- require(false, stub_error);
- owner;
- index;
- dummy;
- return 0;
- }
-
- // Selector: totalSupply() 18160ddd
- function totalSupply() public view returns (uint256) {
- require(false, stub_error);
- dummy;
- return 0;
- }
-}
-
+// Selector: 5b5e139f
contract ERC721Metadata is Dummy, ERC165 {
// Selector: name() 06fdde03
function name() public view returns (string memory) {
@@ -201,6 +184,8 @@
return "";
}
+ // Returns token's const_metadata
+ //
// Selector: tokenURI(uint256) c87b56dd
function tokenURI(uint256 tokenId) public view returns (string memory) {
require(false, stub_error);
@@ -210,6 +195,7 @@
}
}
+// Selector: 68ccfe89
contract ERC721Mintable is Dummy, ERC165, ERC721MintableEvents {
// Selector: mintingFinished() 05d2035b
function mintingFinished() public view returns (bool) {
@@ -218,6 +204,9 @@
return false;
}
+ // `token_id` should be obtained with `next_token_id` method,
+ // unlike standard, you can't specify it manually
+ //
// Selector: mint(address,uint256) 40c10f19
function mint(address to, uint256 tokenId) public returns (bool) {
require(false, stub_error);
@@ -227,6 +216,9 @@
return false;
}
+ // `token_id` should be obtained with `next_token_id` method,
+ // unlike standard, you can't specify it manually
+ //
// Selector: mintWithTokenURI(address,uint256,string) 50bb4e7f
function mintWithTokenURI(
address to,
@@ -241,6 +233,8 @@
return false;
}
+ // Not implemented
+ //
// Selector: finishMinting() 7d64bcb4
function finishMinting() public returns (bool) {
require(false, stub_error);
@@ -249,6 +243,40 @@
}
}
+// Selector: 780e9d63
+contract ERC721Enumerable is Dummy, ERC165 {
+ // Selector: tokenByIndex(uint256) 4f6ccce7
+ function tokenByIndex(uint256 index) public view returns (uint256) {
+ require(false, stub_error);
+ index;
+ dummy;
+ return 0;
+ }
+
+ // Not implemented
+ //
+ // Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
+ function tokenOfOwnerByIndex(address owner, uint256 index)
+ public
+ view
+ returns (uint256)
+ {
+ require(false, stub_error);
+ owner;
+ index;
+ dummy;
+ return 0;
+ }
+
+ // Selector: totalSupply() 18160ddd
+ function totalSupply() public view returns (uint256) {
+ require(false, stub_error);
+ dummy;
+ return 0;
+ }
+}
+
+// Selector: e562194d
contract ERC721UniqueExtensions is Dummy, ERC165 {
// Selector: transfer(address,uint256) a9059cbb
function transfer(address to, uint256 tokenId) public {
tests/src/eth/api/ContractHelpers.soldiffbeforeafterboth--- a/tests/src/eth/api/ContractHelpers.sol
+++ b/tests/src/eth/api/ContractHelpers.sol
@@ -12,6 +12,7 @@
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
+// Selector: 31acb1fe
interface ContractHelpers is Dummy, ERC165 {
// Selector: contractOwner(address) 5152b14c
function contractOwner(address contractAddress)
tests/src/eth/api/UniqueFungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -22,6 +22,7 @@
);
}
+// Selector: 942e8b22
interface ERC20 is Dummy, ERC165, ERC20Events {
// Selector: name() 06fdde03
function name() external view returns (string memory);
tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -42,6 +42,13 @@
event MintingFinished();
}
+// Selector: 42966c68
+interface ERC721Burnable is Dummy, ERC165 {
+ // Selector: burn(uint256) 42966c68
+ function burn(uint256 tokenId) external;
+}
+
+// Selector: 58800161
interface ERC721 is Dummy, ERC165, ERC721Events {
// Selector: balanceOf(address) 70a08231
function balanceOf(address owner) external view returns (uint256);
@@ -49,6 +56,8 @@
// Selector: ownerOf(uint256) 6352211e
function ownerOf(uint256 tokenId) external view returns (address);
+ // Not implemented
+ //
// Selector: safeTransferFromWithData(address,address,uint256,bytes) 60a11672
function safeTransferFromWithData(
address from,
@@ -57,6 +66,8 @@
bytes memory data
) external;
+ // Not implemented
+ //
// Selector: safeTransferFrom(address,address,uint256) 42842e0e
function safeTransferFrom(
address from,
@@ -74,12 +85,18 @@
// Selector: approve(address,uint256) 095ea7b3
function approve(address approved, uint256 tokenId) external;
+ // Not implemented
+ //
// Selector: setApprovalForAll(address,bool) a22cb465
function setApprovalForAll(address operator, bool approved) external;
+ // Not implemented
+ //
// Selector: getApproved(uint256) 081812fc
function getApproved(uint256 tokenId) external view returns (address);
+ // Not implemented
+ //
// Selector: isApprovedForAll(address,address) e985e9c5
function isApprovedForAll(address owner, address operator)
external
@@ -87,25 +104,7 @@
returns (address);
}
-interface ERC721Burnable is Dummy, ERC165 {
- // Selector: burn(uint256) 42966c68
- function burn(uint256 tokenId) external;
-}
-
-interface ERC721Enumerable is Dummy, ERC165 {
- // Selector: tokenByIndex(uint256) 4f6ccce7
- function tokenByIndex(uint256 index) external view returns (uint256);
-
- // Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
- function tokenOfOwnerByIndex(address owner, uint256 index)
- external
- view
- returns (uint256);
-
- // Selector: totalSupply() 18160ddd
- function totalSupply() external view returns (uint256);
-}
-
+// Selector: 5b5e139f
interface ERC721Metadata is Dummy, ERC165 {
// Selector: name() 06fdde03
function name() external view returns (string memory);
@@ -113,17 +112,26 @@
// Selector: symbol() 95d89b41
function symbol() external view returns (string memory);
+ // Returns token's const_metadata
+ //
// Selector: tokenURI(uint256) c87b56dd
function tokenURI(uint256 tokenId) external view returns (string memory);
}
+// Selector: 68ccfe89
interface ERC721Mintable is Dummy, ERC165, ERC721MintableEvents {
// Selector: mintingFinished() 05d2035b
function mintingFinished() external view returns (bool);
+ // `token_id` should be obtained with `next_token_id` method,
+ // unlike standard, you can't specify it manually
+ //
// Selector: mint(address,uint256) 40c10f19
function mint(address to, uint256 tokenId) external returns (bool);
+ // `token_id` should be obtained with `next_token_id` method,
+ // unlike standard, you can't specify it manually
+ //
// Selector: mintWithTokenURI(address,uint256,string) 50bb4e7f
function mintWithTokenURI(
address to,
@@ -131,10 +139,30 @@
string memory tokenUri
) external returns (bool);
+ // Not implemented
+ //
// Selector: finishMinting() 7d64bcb4
function finishMinting() external returns (bool);
}
+// Selector: 780e9d63
+interface ERC721Enumerable is Dummy, ERC165 {
+ // Selector: tokenByIndex(uint256) 4f6ccce7
+ function tokenByIndex(uint256 index) external view returns (uint256);
+
+ // Not implemented
+ //
+ // Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
+ function tokenOfOwnerByIndex(address owner, uint256 index)
+ external
+ view
+ returns (uint256);
+
+ // Selector: totalSupply() 18160ddd
+ function totalSupply() external view returns (uint256);
+}
+
+// Selector: e562194d
interface ERC721UniqueExtensions is Dummy, ERC165 {
// Selector: transfer(address,uint256) a9059cbb
function transfer(address to, uint256 tokenId) external;