difftreelog
feat(evm-coder) implementation generator
in: master
8 files changed
crates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth1#![allow(dead_code)]23use quote::quote;4use darling::FromMeta;5use inflector::cases;6use std::fmt::Write;7use syn::{8 FnArg, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Meta, NestedMeta, PatType, Path,9 ReturnType, Type, spanned::Spanned,10};1112use crate::{13 fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_ident_from_type,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 }76}7778#[derive(Default)]79struct IsList(Vec<Is>);80impl FromMeta for IsList {81 fn from_list(items: &[NestedMeta]) -> darling::Result<Self> {82 let mut out = Vec::new();83 for item in items {84 match item {85 NestedMeta::Meta(Meta::Path(path)) => out.push(Is::try_from(path)?),86 _ => return Err(syn::Error::new(item.span(), "expected path").into()),87 }88 }89 Ok(Self(out))90 }91}9293#[derive(FromMeta)]94pub struct InterfaceInfo {95 name: Ident,96 #[darling(default)]97 is: IsList,98 #[darling(default)]99 inline_is: IsList,100 #[darling(default)]101 events: IsList,102}103104#[derive(FromMeta)]105struct MethodInfo {106 #[darling(default)]107 rename_selector: Option<String>,108}109110struct MethodArg {111 name: Ident,112 ty: Ident,113}114impl MethodArg {115 fn try_from(value: &PatType) -> syn::Result<Self> {116 Ok(Self {117 name: parse_ident_from_pat(&value.pat)?.clone(),118 ty: parse_ident_from_type(&value.ty, false)?.clone(),119 })120 }121 fn is_value(&self) -> bool {122 self.ty == "value"123 }124 fn is_caller(&self) -> bool {125 self.ty == "caller"126 }127 fn is_special(&self) -> bool {128 self.is_value() || self.is_caller()129 }130 fn selector_ty(&self) -> &Ident {131 assert!(!self.is_special());132 &self.ty133 }134135 fn expand_call_def(&self) -> proc_macro2::TokenStream {136 assert!(!self.is_special());137 let name = &self.name;138 let ty = &self.ty;139140 quote! {141 #name: #ty142 }143 }144145 fn expand_parse(&self) -> proc_macro2::TokenStream {146 assert!(!self.is_special());147 let name = &self.name;148 quote! {149 #name: reader.abi_read()?150 }151 }152153 fn expand_call_arg(&self) -> proc_macro2::TokenStream {154 if self.is_value() {155 quote! {156 c.value.clone()157 }158 } else if self.is_caller() {159 quote! {160 c.caller.clone()161 }162 } else {163 let name = &self.name;164 quote! {165 #name166 }167 }168 }169170 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {171 let name = &self.name.to_string();172 let ty = &self.ty;173 quote! {174 <NamedArgument<#ty>>::new(#name)175 }176 }177}178179#[derive(PartialEq)]180enum Mutability {181 Mutable,182 View,183 Pure,184}185186struct Method {187 name: Ident,188 camel_name: String,189 pascal_name: Ident,190 screaming_name: Ident,191 selector_str: String,192 selector: u32,193 args: Vec<MethodArg>,194 has_normal_args: bool,195 mutability: Mutability,196 result: Type,197}198impl Method {199 fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {200 let mut info = MethodInfo {201 rename_selector: None,202 };203 for attr in &value.attrs {204 let ident = parse_ident_from_path(&attr.path, false)?;205 if ident == "solidity" {206 let args = attr.parse_meta().unwrap();207 info = MethodInfo::from_meta(&args).unwrap();208 } else if ident == "doc" {209 // TODO: Add docs to evm interfaces210 }211 }212 let ident = &value.sig.ident;213 let ident_str = ident.to_string();214 if !cases::snakecase::is_snake_case(&ident_str) {215 return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute"));216 }217218 let mut mutability = Mutability::Pure;219220 if let Some(FnArg::Receiver(receiver)) = value221 .sig222 .inputs223 .iter()224 .find(|arg| matches!(arg, FnArg::Receiver(_)))225 {226 if receiver.reference.is_none() {227 return Err(syn::Error::new(228 receiver.span(),229 "receiver should be by ref",230 ));231 }232 if receiver.mutability.is_some() {233 mutability = Mutability::Mutable;234 } else {235 mutability = Mutability::View;236 }237 }238 let mut args = Vec::new();239 for typ in value240 .sig241 .inputs242 .iter()243 .filter(|arg| matches!(arg, FnArg::Typed(_)))244 {245 let typ = match typ {246 FnArg::Typed(typ) => typ,247 _ => unreachable!(),248 };249 args.push(MethodArg::try_from(typ)?);250 }251252 if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) {253 return Err(syn::Error::new(254 args.iter().find(|arg| arg.is_value()).unwrap().ty.span(),255 "payable function should be mutable",256 ));257 }258259 let result = match &value.sig.output {260 ReturnType::Type(_, ty) => ty,261 _ => 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)")),262 };263 let result = parse_result_ok(result)?;264265 let camel_name = info266 .rename_selector267 .unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));268 let mut selector_str = camel_name.clone();269 selector_str.push('(');270 let mut has_normal_args = false;271 for (i, arg) in args.iter().filter(|arg| !arg.is_special()).enumerate() {272 if i != 0 {273 selector_str.push(',');274 }275 write!(selector_str, "{}", arg.selector_ty()).unwrap();276 has_normal_args = true;277 }278 selector_str.push(')');279 let selector = fn_selector_str(&selector_str);280281 Ok(Self {282 name: ident.clone(),283 camel_name,284 pascal_name: snake_ident_to_pascal(ident),285 screaming_name: snake_ident_to_screaming(ident),286 selector_str,287 selector,288 args,289 has_normal_args,290 mutability,291 result: result.clone(),292 })293 }294 fn expand_call_def(&self) -> proc_macro2::TokenStream {295 let defs = self296 .args297 .iter()298 .filter(|a| !a.is_special())299 .map(|a| a.expand_call_def());300 let pascal_name = &self.pascal_name;301302 if self.has_normal_args {303 quote! {304 #pascal_name {305 #(306 #defs,307 )*308 }309 }310 } else {311 quote! {#pascal_name}312 }313 }314315 fn expand_const(&self) -> proc_macro2::TokenStream {316 let screaming_name = &self.screaming_name;317 let selector = self.selector;318 let selector_str = &self.selector_str;319 quote! {320 #[doc = #selector_str]321 const #screaming_name: u32 = #selector;322 }323 }324325 fn expand_interface_id(&self) -> proc_macro2::TokenStream {326 let screaming_name = &self.screaming_name;327 quote! {328 interface_id ^= Self::#screaming_name;329 }330 }331332 fn expand_parse(&self) -> proc_macro2::TokenStream {333 let pascal_name = &self.pascal_name;334 let screaming_name = &self.screaming_name;335 if self.has_normal_args {336 let parsers = self337 .args338 .iter()339 .filter(|a| !a.is_special())340 .map(|a| a.expand_parse());341 quote! {342 Self::#screaming_name => return Ok(Some(Self::#pascal_name {343 #(344 #parsers,345 )*346 }))347 }348 } else {349 quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }350 }351 }352353 fn expand_variant_call(&self) -> proc_macro2::TokenStream {354 let pascal_name = &self.pascal_name;355 let name = &self.name;356357 let matcher = if self.has_normal_args {358 let names = self359 .args360 .iter()361 .filter(|a| !a.is_special())362 .map(|a| &a.name);363364 quote! {{365 #(366 #names,367 )*368 }}369 } else {370 quote! {}371 };372373 let receiver = match self.mutability {374 Mutability::Mutable | Mutability::View => quote! {self.},375 Mutability::Pure => quote! {Self::},376 };377 let args = self.args.iter().map(|a| a.expand_call_arg());378379 quote! {380 InternalCall::#pascal_name #matcher => {381 let result = #receiver #name(382 #(383 #args,384 )*385 )?;386 (&result).abi_write(&mut writer);387 }388 }389 }390391 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {392 let camel_name = &self.camel_name;393 let mutability = match self.mutability {394 Mutability::Mutable => quote! {SolidityMutability::Mutable},395 Mutability::View => quote! { SolidityMutability::View },396 Mutability::Pure => quote! {SolidityMutability::Pure},397 };398 let result = &self.result;399400 let args = self.args.iter().map(MethodArg::expand_solidity_argument);401402 quote! {403 SolidityFunction {404 name: #camel_name,405 mutability: #mutability,406 args: (407 #(408 #args,409 )*410 ),411 result: <UnnamedArgument<#result>>::default(),412 }413 }414 }415}416417pub struct SolidityInterface {418 generics: Generics,419 name: Box<syn::Type>,420 info: InterfaceInfo,421 methods: Vec<Method>,422}423impl SolidityInterface {424 pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {425 let mut methods = Vec::new();426427 for item in &value.items {428 if let ImplItem::Method(method) = item {429 methods.push(Method::try_from(method)?)430 }431 }432 Ok(Self {433 generics: value.generics.clone(),434 name: value.self_ty.clone(),435 info,436 methods,437 })438 }439 pub fn expand(self) -> proc_macro2::TokenStream {440 let name = self.name;441442 let solidity_name = self.info.name.to_string();443 let call_name = pascal_ident_to_call(&self.info.name);444 let generics = self.generics;445446 let call_sub = self447 .info448 .inline_is449 .0450 .iter()451 .chain(self.info.is.0.iter())452 .map(Is::expand_call_def);453 let call_parse = self454 .info455 .inline_is456 .0457 .iter()458 .chain(self.info.is.0.iter())459 .map(Is::expand_parse);460 let call_variants = self461 .info462 .inline_is463 .0464 .iter()465 .chain(self.info.is.0.iter())466 .map(Is::expand_variant_call);467468 let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);469 let supports_interface = self.info.is.0.iter().map(Is::expand_supports_interface);470471 let calls = self.methods.iter().map(Method::expand_call_def);472 let consts = self.methods.iter().map(Method::expand_const);473 let interface_id = self.methods.iter().map(Method::expand_interface_id);474 let parsers = self.methods.iter().map(Method::expand_parse);475 let call_variants_this = self.methods.iter().map(Method::expand_variant_call);476 let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);477478 // let methods = self.methods.iter().map(Method::solidity_def);479480 quote! {481 #[derive(Debug)]482 pub enum #call_name {483 #(484 #calls,485 )*486 #(487 #call_sub,488 )*489 }490 impl #call_name {491 #(492 #consts493 )*494 pub const fn interface_id() -> u32 {495 let mut interface_id = 0;496 #(#interface_id)*497 #(#inline_interface_id)*498 interface_id499 }500 pub fn supports_interface(interface_id: u32) -> bool {501 interface_id != 0xffffff && (502 interface_id == Self::interface_id()503 #(504 || #supports_interface505 )*506 )507 }508 pub fn generate_solidity_interface() -> string {509 use evm_coder::solidity::*;510 use core::fmt::Write;511 let interface = SolidityInterface {512 name: #solidity_name,513 functions: (#(514 #solidity_functions,515 )*),516 };517 let mut out = string::new();518 let _ = interface.format(&mut out);519 out520 }521 }522 impl ::evm_coder::Call for #call_name {523 fn parse(method_id: u32, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {524 use ::evm_coder::abi::AbiRead;525 match method_id {526 #(527 #parsers,528 )*529 _ => {},530 }531 #(532 #call_parse533 )else*534 return Ok(None);535 }536 }537 impl #generics ::evm_coder::Callable<#call_name> for #name {538 #[allow(unreachable_code)] // In case of no inner calls539 fn call(&mut self, c: Msg<#call_name>) -> Result<::evm_coder::abi::AbiWriter> {540 use ::evm_coder::abi::AbiWrite;541 type InternalCall = #call_name;542 match c.call {543 #(544 #call_variants,545 )*546 _ => {},547 }548 let mut writer = ::evm_coder::abi::AbiWriter::default();549 match c.call {550 #(551 #call_variants_this,552 )*553 _ => unreachable!()554 }555 Ok(writer)556 }557 }558 }559 }560}1#![allow(dead_code)]23use quote::quote;4use darling::FromMeta;5use inflector::cases;6use std::fmt::Write;7use syn::{8 FnArg, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Meta, NestedMeta, PatType, Path,9 ReturnType, Type, spanned::Spanned,10};1112use crate::{13 fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_ident_from_type,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(out_set, 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(out_set, 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}123124struct MethodArg {125 name: Ident,126 camel_name: String,127 ty: Ident,128}129impl MethodArg {130 fn try_from(value: &PatType) -> syn::Result<Self> {131 let name = parse_ident_from_pat(&value.pat)?.clone();132 Ok(Self {133 camel_name: cases::camelcase::to_camel_case(&name.to_string()),134 name,135 ty: parse_ident_from_type(&value.ty, false)?.clone(),136 })137 }138 fn is_value(&self) -> bool {139 self.ty == "value"140 }141 fn is_caller(&self) -> bool {142 self.ty == "caller"143 }144 fn is_special(&self) -> bool {145 self.is_value() || self.is_caller()146 }147 fn selector_ty(&self) -> &Ident {148 assert!(!self.is_special());149 &self.ty150 }151152 fn expand_call_def(&self) -> proc_macro2::TokenStream {153 assert!(!self.is_special());154 let name = &self.name;155 let ty = &self.ty;156157 quote! {158 #name: #ty159 }160 }161162 fn expand_parse(&self) -> proc_macro2::TokenStream {163 assert!(!self.is_special());164 let name = &self.name;165 quote! {166 #name: reader.abi_read()?167 }168 }169170 fn expand_call_arg(&self) -> proc_macro2::TokenStream {171 if self.is_value() {172 quote! {173 c.value.clone()174 }175 } else if self.is_caller() {176 quote! {177 c.caller.clone()178 }179 } else {180 let name = &self.name;181 quote! {182 #name183 }184 }185 }186187 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {188 let camel_name = &self.camel_name.to_string();189 let ty = &self.ty;190 quote! {191 <NamedArgument<#ty>>::new(#camel_name)192 }193 }194}195196#[derive(PartialEq)]197enum Mutability {198 Mutable,199 View,200 Pure,201}202203struct Method {204 name: Ident,205 camel_name: String,206 pascal_name: Ident,207 screaming_name: Ident,208 selector_str: String,209 selector: u32,210 args: Vec<MethodArg>,211 has_normal_args: bool,212 mutability: Mutability,213 result: Type,214}215impl Method {216 fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {217 let mut info = MethodInfo {218 rename_selector: None,219 };220 for attr in &value.attrs {221 let ident = parse_ident_from_path(&attr.path, false)?;222 if ident == "solidity" {223 let args = attr.parse_meta().unwrap();224 info = MethodInfo::from_meta(&args).unwrap();225 } else if ident == "doc" {226 // TODO: Add docs to evm interfaces227 }228 }229 let ident = &value.sig.ident;230 let ident_str = ident.to_string();231 if !cases::snakecase::is_snake_case(&ident_str) {232 return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute"));233 }234235 let mut mutability = Mutability::Pure;236237 if let Some(FnArg::Receiver(receiver)) = value238 .sig239 .inputs240 .iter()241 .find(|arg| matches!(arg, FnArg::Receiver(_)))242 {243 if receiver.reference.is_none() {244 return Err(syn::Error::new(245 receiver.span(),246 "receiver should be by ref",247 ));248 }249 if receiver.mutability.is_some() {250 mutability = Mutability::Mutable;251 } else {252 mutability = Mutability::View;253 }254 }255 let mut args = Vec::new();256 for typ in value257 .sig258 .inputs259 .iter()260 .filter(|arg| matches!(arg, FnArg::Typed(_)))261 {262 let typ = match typ {263 FnArg::Typed(typ) => typ,264 _ => unreachable!(),265 };266 args.push(MethodArg::try_from(typ)?);267 }268269 if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) {270 return Err(syn::Error::new(271 args.iter().find(|arg| arg.is_value()).unwrap().ty.span(),272 "payable function should be mutable",273 ));274 }275276 let result = match &value.sig.output {277 ReturnType::Type(_, ty) => ty,278 _ => 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)")),279 };280 let result = parse_result_ok(result)?;281282 let camel_name = info283 .rename_selector284 .unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));285 let mut selector_str = camel_name.clone();286 selector_str.push('(');287 let mut has_normal_args = false;288 for (i, arg) in args.iter().filter(|arg| !arg.is_special()).enumerate() {289 if i != 0 {290 selector_str.push(',');291 }292 write!(selector_str, "{}", arg.selector_ty()).unwrap();293 has_normal_args = true;294 }295 selector_str.push(')');296 let selector = fn_selector_str(&selector_str);297298 Ok(Self {299 name: ident.clone(),300 camel_name,301 pascal_name: snake_ident_to_pascal(ident),302 screaming_name: snake_ident_to_screaming(ident),303 selector_str,304 selector,305 args,306 has_normal_args,307 mutability,308 result: result.clone(),309 })310 }311 fn expand_call_def(&self) -> proc_macro2::TokenStream {312 let defs = self313 .args314 .iter()315 .filter(|a| !a.is_special())316 .map(|a| a.expand_call_def());317 let pascal_name = &self.pascal_name;318319 if self.has_normal_args {320 quote! {321 #pascal_name {322 #(323 #defs,324 )*325 }326 }327 } else {328 quote! {#pascal_name}329 }330 }331332 fn expand_const(&self) -> proc_macro2::TokenStream {333 let screaming_name = &self.screaming_name;334 let selector = self.selector;335 let selector_str = &self.selector_str;336 quote! {337 #[doc = #selector_str]338 const #screaming_name: u32 = #selector;339 }340 }341342 fn expand_interface_id(&self) -> proc_macro2::TokenStream {343 let screaming_name = &self.screaming_name;344 quote! {345 interface_id ^= Self::#screaming_name;346 }347 }348349 fn expand_parse(&self) -> proc_macro2::TokenStream {350 let pascal_name = &self.pascal_name;351 let screaming_name = &self.screaming_name;352 if self.has_normal_args {353 let parsers = self354 .args355 .iter()356 .filter(|a| !a.is_special())357 .map(|a| a.expand_parse());358 quote! {359 Self::#screaming_name => return Ok(Some(Self::#pascal_name {360 #(361 #parsers,362 )*363 }))364 }365 } else {366 quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }367 }368 }369370 fn expand_variant_call(&self) -> proc_macro2::TokenStream {371 let pascal_name = &self.pascal_name;372 let name = &self.name;373374 let matcher = if self.has_normal_args {375 let names = self376 .args377 .iter()378 .filter(|a| !a.is_special())379 .map(|a| &a.name);380381 quote! {{382 #(383 #names,384 )*385 }}386 } else {387 quote! {}388 };389390 let receiver = match self.mutability {391 Mutability::Mutable | Mutability::View => quote! {self.},392 Mutability::Pure => quote! {Self::},393 };394 let args = self.args.iter().map(|a| a.expand_call_arg());395396 quote! {397 InternalCall::#pascal_name #matcher => {398 let result = #receiver #name(399 #(400 #args,401 )*402 )?;403 (&result).abi_write(&mut writer);404 }405 }406 }407408 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {409 let camel_name = &self.camel_name;410 let mutability = match self.mutability {411 Mutability::Mutable => quote! {SolidityMutability::Mutable},412 Mutability::View => quote! { SolidityMutability::View },413 Mutability::Pure => quote! {SolidityMutability::Pure},414 };415 let result = &self.result;416417 let args = self418 .args419 .iter()420 .filter(|a| !a.is_special())421 .map(MethodArg::expand_solidity_argument);422423 quote! {424 SolidityFunction {425 name: #camel_name,426 mutability: #mutability,427 args: (428 #(429 #args,430 )*431 ),432 result: <UnnamedArgument<#result>>::default(),433 }434 }435 }436}437438pub struct SolidityInterface {439 generics: Generics,440 name: Box<syn::Type>,441 info: InterfaceInfo,442 methods: Vec<Method>,443}444impl SolidityInterface {445 pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {446 let mut methods = Vec::new();447448 for item in &value.items {449 if let ImplItem::Method(method) = item {450 methods.push(Method::try_from(method)?)451 }452 }453 Ok(Self {454 generics: value.generics.clone(),455 name: value.self_ty.clone(),456 info,457 methods,458 })459 }460 pub fn expand(self) -> proc_macro2::TokenStream {461 let name = self.name;462463 let solidity_name = self.info.name.to_string();464 let call_name = pascal_ident_to_call(&self.info.name);465 let generics = self.generics;466467 let call_sub = self468 .info469 .inline_is470 .0471 .iter()472 .chain(self.info.is.0.iter())473 .map(Is::expand_call_def);474 let call_parse = self475 .info476 .inline_is477 .0478 .iter()479 .chain(self.info.is.0.iter())480 .map(Is::expand_parse);481 let call_variants = self482 .info483 .inline_is484 .0485 .iter()486 .chain(self.info.is.0.iter())487 .map(Is::expand_variant_call);488489 let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);490 let supports_interface = self.info.is.0.iter().map(Is::expand_supports_interface);491492 let calls = self.methods.iter().map(Method::expand_call_def);493 let consts = self.methods.iter().map(Method::expand_const);494 let interface_id = self.methods.iter().map(Method::expand_interface_id);495 let parsers = self.methods.iter().map(Method::expand_parse);496 let call_variants_this = self.methods.iter().map(Method::expand_variant_call);497 let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);498499 // TODO: Inline inline_is500 let solidity_is = self501 .info502 .is503 .0504 .iter()505 .chain(self.info.inline_is.0.iter())506 .map(|is| is.name.to_string());507 let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string());508 let solidity_generators = self509 .info510 .is511 .0512 .iter()513 .chain(self.info.inline_is.0.iter())514 .map(Is::expand_generator);515 let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);516517 // let methods = self.methods.iter().map(Method::solidity_def);518519 quote! {520 #[derive(Debug)]521 pub enum #call_name {522 #(523 #calls,524 )*525 #(526 #call_sub,527 )*528 }529 impl #call_name {530 #(531 #consts532 )*533 pub const fn interface_id() -> u32 {534 let mut interface_id = 0;535 #(#interface_id)*536 #(#inline_interface_id)*537 interface_id538 }539 pub fn supports_interface(interface_id: u32) -> bool {540 interface_id != 0xffffff && (541 interface_id == Self::interface_id()542 #(543 || #supports_interface544 )*545 )546 }547 pub fn generate_solidity_interface(out_set: &mut sp_std::collections::btree_set::BTreeSet<string>, is_impl: bool) {548 use evm_coder::solidity::*;549 use core::fmt::Write;550 let interface = SolidityInterface {551 name: #solidity_name,552 is: &["Dummy", #(553 #solidity_is,554 )* #(555 #solidity_events_is,556 )* ],557 functions: (#(558 #solidity_functions,559 )*),560 };561 if is_impl {562 out_set.insert("// Common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\n".into());563 } else {564 out_set.insert("// Common stubs holder\ninterface Dummy {\n}\n".into());565 }566 #(567 #solidity_generators568 )*569 #(570 #solidity_event_generators571 )*572573 let mut out = string::new();574 // In solidity interface usage (is) should be preceeded by interface definition575 // This comment helps to sort it in a set576 if #solidity_name.starts_with("Inline") {577 out.push_str("// Inline\n");578 }579 let _ = interface.format(is_impl, &mut out);580 out_set.insert(out);581 }582 }583 impl ::evm_coder::Call for #call_name {584 fn parse(method_id: u32, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {585 use ::evm_coder::abi::AbiRead;586 match method_id {587 #(588 #parsers,589 )*590 _ => {},591 }592 #(593 #call_parse594 )else*595 return Ok(None);596 }597 }598 impl #generics ::evm_coder::Callable<#call_name> for #name {599 #[allow(unreachable_code)] // In case of no inner calls600 fn call(&mut self, c: Msg<#call_name>) -> Result<::evm_coder::abi::AbiWriter> {601 use ::evm_coder::abi::AbiWrite;602 type InternalCall = #call_name;603 match c.call {604 #(605 #call_variants,606 )*607 _ => {},608 }609 let mut writer = ::evm_coder::abi::AbiWriter::default();610 match c.call {611 #(612 #call_variants_this,613 )*614 _ => unreachable!()615 }616 Ok(writer)617 }618 }619 }620 }621}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
@@ -1,3 +1,4 @@
+use inflector::cases;
use syn::{Data, DeriveInput, Field, Fields, Ident, Variant, spanned::Spanned};
use std::fmt::Write;
use quote::quote;
@@ -6,6 +7,7 @@
struct EventField {
name: Ident,
+ camel_name: String,
ty: Ident,
indexed: bool,
}
@@ -24,10 +26,18 @@
}
Ok(Self {
name: name.to_owned(),
+ camel_name: cases::camelcase::to_camel_case(&name.to_string()),
ty: ty.to_owned(),
indexed,
})
}
+ fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {
+ let camel_name = &self.camel_name;
+ let ty = &self.ty;
+ quote! {
+ <NamedArgument<#ty>>::new(#camel_name)
+ }
+ }
}
struct Event {
@@ -116,6 +126,21 @@
)*];
}
}
+
+ fn expand_solidity_function(&self) -> proc_macro2::TokenStream {
+ let name = self.name.to_string();
+ let args = self.fields.iter().map(EventField::expand_solidity_argument);
+ quote! {
+ SolidityEvent {
+ name: #name,
+ args: (
+ #(
+ #args,
+ )*
+ ),
+ }
+ }
+ }
}
pub struct Events {
@@ -144,12 +169,30 @@
let consts = self.events.iter().map(Event::expand_consts);
let serializers = self.events.iter().map(Event::expand_serializers);
+ let solidity_name = self.name.to_string();
+ let solidity_functions = self.events.iter().map(Event::expand_solidity_function);
quote! {
impl #name {
#(
#consts
)*
+
+ pub fn generate_solidity_interface(out_set: &mut sp_std::collections::btree_set::BTreeSet<string>, is_impl: bool) {
+ use evm_coder::solidity::*;
+ use core::fmt::Write;
+ let interface = SolidityInterface {
+ name: #solidity_name,
+ is: &[],
+ functions: (#(
+ #solidity_functions,
+ )*),
+ };
+ let mut out = string::new();
+ out.push_str("// Inline\n");
+ let _ = interface.format(is_impl, &mut out);
+ out_set.insert(out);
+ }
}
#[automatically_derived]
crates/evm-coder/src/solidity.rsdiffbeforeafterboth--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -6,37 +6,44 @@
pub trait SolidityTypeName: 'static {
fn solidity_name(writer: &mut impl fmt::Write) -> fmt::Result;
+ fn solidity_default(writer: &mut impl fmt::Write) -> fmt::Result;
fn is_void() -> bool {
false
}
}
macro_rules! solidity_type_name {
- ($($ty:ident => $name:expr),* $(,)?) => {
+ ($($ty:ident => $name:literal = $default:literal),* $(,)?) => {
$(
impl SolidityTypeName for $ty {
fn solidity_name(writer: &mut impl core::fmt::Write) -> core::fmt::Result {
write!(writer, $name)
}
+ fn solidity_default(writer: &mut impl core::fmt::Write) -> core::fmt::Result {
+ write!(writer, $default)
+ }
}
)*
};
}
solidity_type_name! {
- uint8 => "uint8",
- uint32 => "uint32",
- uint128 => "uint128",
- uint256 => "uint256",
- address => "address",
- string => "memory string",
- bytes => "memory bytes",
- bool => "bool",
+ uint8 => "uint8" = "0",
+ uint32 => "uint32" = "0",
+ uint128 => "uint128" = "0",
+ uint256 => "uint256" = "0",
+ address => "address" = "0x0000000000000000000000000000000000000000",
+ string => "string memory" = "\"\"",
+ bytes => "bytes memory" = "hex\"\"",
+ bool => "bool" = "false",
}
impl SolidityTypeName for void {
fn solidity_name(_writer: &mut impl fmt::Write) -> fmt::Result {
Ok(())
}
+ fn solidity_default(_writer: &mut impl fmt::Write) -> fmt::Result {
+ Ok(())
+ }
fn is_void() -> bool {
true
}
@@ -44,6 +51,8 @@
pub trait SolidityArguments {
fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result;
+ fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;
+ fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result;
fn is_empty(&self) -> bool {
self.len() == 0
}
@@ -61,6 +70,12 @@
Ok(())
}
}
+ fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
+ Ok(())
+ }
+ fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ T::solidity_default(writer)
+ }
fn len(&self) -> usize {
if T::is_void() {
0
@@ -87,6 +102,12 @@
Ok(())
}
}
+ fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ writeln!(writer, "\t\t{};", self.0)
+ }
+ fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ T::solidity_default(writer)
+ }
fn len(&self) -> usize {
if T::is_void() {
0
@@ -100,6 +121,12 @@
fn solidity_name(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
Ok(())
}
+ fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
+ Ok(())
+ }
+ fn solidity_default(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
+ Ok(())
+ }
fn len(&self) -> usize {
0
}
@@ -122,13 +149,43 @@
)* );
Ok(())
}
+ fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ for_tuples!( #(
+ Tuple.solidity_get(writer)?;
+ )* );
+ Ok(())
+ }
+ fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ if self.is_empty() {
+ Ok(())
+ } else if self.len() == 1 {
+ for_tuples!( #(
+ Tuple.solidity_default(writer)?;
+ )* );
+ Ok(())
+ } else {
+ write!(writer, "(")?;
+ let mut first = true;
+ for_tuples!( #(
+ if !Tuple.is_empty() {
+ if !first {
+ write!(writer, ", ")?;
+ }
+ first = false;
+ Tuple.solidity_name(writer)?;
+ }
+ )* );
+ write!(writer, ")")?;
+ Ok(())
+ }
+ }
fn len(&self) -> usize {
for_tuples!( #( Tuple.len() )+* )
}
}
pub trait SolidityFunctions {
- fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result;
+ fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result;
}
pub enum SolidityMutability {
@@ -143,10 +200,15 @@
pub mutability: SolidityMutability,
}
impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {
- fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
- write!(writer, "function {}(", self.name)?;
+ fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {
+ write!(writer, "\tfunction {}(", self.name)?;
self.args.solidity_name(writer)?;
- write!(writer, ") external")?;
+ write!(writer, ")")?;
+ if is_impl {
+ write!(writer, " public")?;
+ } else {
+ write!(writer, " external")?;
+ }
match &self.mutability {
SolidityMutability::Pure => write!(writer, " pure")?,
SolidityMutability::View => write!(writer, " view")?,
@@ -157,7 +219,25 @@
self.result.solidity_name(writer)?;
write!(writer, ")")?;
}
- writeln!(writer, ";")
+ if is_impl {
+ writeln!(writer, " {{")?;
+ writeln!(writer, "\t\trequire(false, stub_error);")?;
+ self.args.solidity_get(writer)?;
+ match &self.mutability {
+ SolidityMutability::Pure => {}
+ SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,
+ SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,
+ }
+ if !self.result.is_empty() {
+ write!(writer, "\t\treturn ")?;
+ self.result.solidity_default(writer)?;
+ writeln!(writer, ";")?;
+ }
+ writeln!(writer, "\t}}")?;
+ } else {
+ writeln!(writer, ";")?;
+ }
+ Ok(())
}
}
@@ -165,10 +245,10 @@
impl SolidityFunctions for Tuple {
for_tuples!( where #( Tuple: SolidityFunctions ),* );
- fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {
let mut first = false;
for_tuples!( #(
- Tuple.solidity_name(writer)?;
+ Tuple.solidity_name(is_impl, writer)?;
)* );
Ok(())
}
@@ -176,14 +256,43 @@
pub struct SolidityInterface<F: SolidityFunctions> {
pub name: &'static str,
+ pub is: &'static [&'static str],
pub functions: F,
}
impl<F: SolidityFunctions> SolidityInterface<F> {
- pub fn format(&self, out: &mut impl fmt::Write) -> fmt::Result {
- writeln!(out, "interface {} {{", self.name)?;
- self.functions.solidity_name(out)?;
+ pub fn format(&self, is_impl: bool, out: &mut impl fmt::Write) -> fmt::Result {
+ if is_impl {
+ write!(out, "contract ")?;
+ } else {
+ write!(out, "interface ")?;
+ }
+ write!(out, "{}", self.name)?;
+ if !self.is.is_empty() {
+ write!(out, " is")?;
+ for (i, n) in self.is.iter().enumerate() {
+ if i != 0 {
+ write!(out, ",")?;
+ }
+ write!(out, " {}", n)?;
+ }
+ }
+ writeln!(out, " {{")?;
+ self.functions.solidity_name(is_impl, out)?;
writeln!(out, "}}")?;
Ok(())
}
}
+
+pub struct SolidityEvent<A> {
+ pub name: &'static str,
+ pub args: A,
+}
+
+impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {
+ fn solidity_name(&self, _is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {
+ write!(writer, "\tevent {}(", self.name)?;
+ self.args.solidity_name(writer)?;
+ writeln!(writer, ");")
+ }
+}
pallets/nft/src/eth/erc.rsdiffbeforeafterboth--- a/pallets/nft/src/eth/erc.rs
+++ b/pallets/nft/src/eth/erc.rs
@@ -395,3 +395,32 @@
#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]
impl<T: Config> CollectionHandle<T> {}
+
+macro_rules! generate_code {
+ ($name:ident, $decl:ident, $is_impl:literal) => {
+ #[test]
+ #[ignore]
+ fn $name() {
+ use sp_std::collections::btree_set::BTreeSet;
+ let mut out = BTreeSet::new();
+ $decl::generate_solidity_interface(&mut out, $is_impl);
+ println!("=== SNIP START ===");
+ println!("// SPDX-License-Identifier: OTHER");
+ println!("// This code is automatically generated with `cargo test --package pallet-nft -- eth::erc::{} --exact --nocapture --ignored`", stringify!(name));
+ println!();
+ println!("pragma solidity >=0.8.0 <0.9.0;");
+ println!();
+ for b in out {
+ println!("{}", b);
+ }
+ println!("=== SNIP END ===");
+ }
+ };
+}
+
+// Not a tests, but code generators
+generate_code!(nft_impl, UniqueNFTCall, true);
+generate_code!(nft_iface, UniqueNFTCall, false);
+
+generate_code!(fungible_impl, UniqueFungibleCall, true);
+generate_code!(fungible_iface, UniqueFungibleCall, false);
pallets/nft/src/eth/stubs/ERC20.bindiffbeforeafterbothbinary blob — no preview
pallets/nft/src/eth/stubs/ERC20.soldiffbeforeafterboth--- a/pallets/nft/src/eth/stubs/ERC20.sol
+++ b/pallets/nft/src/eth/stubs/ERC20.sol
@@ -1,69 +1,94 @@
// SPDX-License-Identifier: OTHER
+// This code is automatically generated with `cargo test --package pallet-nft -- eth::erc::name --exact --nocapture --ignored`
pragma solidity >=0.8.0 <0.9.0;
-contract ERC20 {
- uint8 _dummy = 0;
- string stub_error = "this contract does not exists, code for collections is implemented at pallet side";
+// Common stubs holder
+contract Dummy {
+ uint8 dummy;
+ string stub_error = "this contract is implemented in native";
+}
- // 0x18160ddd
- function totalSupply() external view returns (uint256) {
+// Inline
+contract ERC20Events {
+ event Transfer(address from, address to, uint256 value);
+ event Approval(address owner, address spender, uint256 value);
+}
+
+// Inline
+contract InlineNameSymbol is Dummy {
+ function name() public view returns (string memory) {
require(false, stub_error);
- _dummy;
- return 0;
+ dummy;
+ return "";
}
+ function symbol() public view returns (string memory) {
+ require(false, stub_error);
+ dummy;
+ return "";
+ }
+}
- // 0x70a08231
- function balanceOf(address account) external view returns (uint256) {
+// Inline
+contract InlineTotalSupply is Dummy {
+ function totalSupply() public view returns (uint256) {
require(false, stub_error);
- account;
- _dummy;
+ dummy;
return 0;
}
+}
- // 0xa9059cbb
- function transfer(address recipient, uint256 amount) external returns (bool) {
+contract ERC165 is Dummy {
+ function supportsInterface(uint32 interfaceId) public view returns (bool) {
require(false, stub_error);
- recipient;
- amount;
- _dummy = 0;
+ interfaceId;
+ dummy;
return false;
}
+}
- // 0xdd62ed3e
- function allowance(address owner, address spender) external view returns (uint256) {
+contract ERC20 is Dummy, InlineNameSymbol, InlineTotalSupply, ERC20Events {
+ function decimals() public view returns (uint8) {
require(false, stub_error);
+ dummy;
+ return 0;
+ }
+ function balanceOf(address owner) public view returns (uint256) {
+ require(false, stub_error);
owner;
- spender;
- return _dummy;
+ dummy;
+ return 0;
+ }
+ function transfer(address to, uint256 amount) public returns (bool) {
+ require(false, stub_error);
+ to;
+ amount;
+ dummy = 0;
+ return false;
}
-
- // 0x095ea7b3
- function approve(address spender, uint256 amount) external returns (bool) {
+ function transferFrom(address from, address to, uint256 amount) public returns (bool) {
require(false, stub_error);
- spender;
+ from;
+ to;
amount;
- _dummy = 0;
+ dummy = 0;
return false;
}
-
- // 0x23b872dd
- function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) {
+ function approve(address spender, uint256 amount) public returns (bool) {
require(false, stub_error);
- sender;
- recipient;
+ spender;
amount;
- _dummy = 0;
+ dummy = 0;
return false;
}
+ function allowance(address owner, address spender) public view returns (uint256) {
+ require(false, stub_error);
+ owner;
+ spender;
+ dummy;
+ return 0;
+ }
+}
- // While ERC165 is not required by spec of ERC20, better implement it
- // 0x01ffc9a7
- function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
- return
- // ERC20
- interfaceID == 0x36372b07 ||
- // ERC165
- interfaceID == 0x01ffc9a7;
- }
+contract UniqueFungible is Dummy, ERC165, ERC20 {
}
\ No newline at end of file
pallets/nft/src/eth/stubs/ERC721.bindiffbeforeafterbothbinary blob — no preview
pallets/nft/src/eth/stubs/ERC721.soldiffbeforeafterboth--- a/pallets/nft/src/eth/stubs/ERC721.sol
+++ b/pallets/nft/src/eth/stubs/ERC721.sol
@@ -1,163 +1,194 @@
// SPDX-License-Identifier: OTHER
+// This code is automatically generated with `cargo test --package pallet-nft -- eth::erc::name --exact --nocapture --ignored`
pragma solidity >=0.8.0 <0.9.0;
-contract ERC721 {
- uint8 _dummy = 0;
- address _dummy_addr = 0x0000000000000000000000000000000000000000;
- string _dummy_string = "";
- string stub_error =
- "this contract does not exists, code for collections is implemented at pallet side";
+// Common stubs holder
+contract Dummy {
+ uint8 dummy;
+ string stub_error = "this contract is implemented in native";
+}
- event Transfer(
- address indexed from,
- address indexed to,
- uint256 indexed tokenId
- );
+// Inline
+contract ERC721Events {
+ event Transfer(address from, address to, uint256 tokenId);
+ event Approval(address owner, address approved, uint256 tokenId);
+ event ApprovalForAll(address owner, address operator, bool approved);
+}
- event Approval(
- address indexed owner,
- address indexed approved,
- uint256 indexed tokenId
- );
+// Inline
+contract ERC721MintableEvents {
+ event MintingFinished();
+}
- event ApprovalForAll(
- address indexed owner,
- address indexed operator,
- bool approved
- );
+// Inline
+contract InlineNameSymbol is Dummy {
+ function name() public view returns (string memory) {
+ require(false, stub_error);
+ dummy;
+ return "";
+ }
+ function symbol() public view returns (string memory) {
+ require(false, stub_error);
+ dummy;
+ return "";
+ }
+}
- // 0x18160ddd
- function totalSupply() external view returns (uint256) {
- require(false, stub_error);
- return 0;
- }
+// Inline
+contract InlineTotalSupply is Dummy {
+ function totalSupply() public view returns (uint256) {
+ require(false, stub_error);
+ dummy;
+ return 0;
+ }
+}
- function name() external view returns (string memory res_name) {
- require(false, stub_error);
- res_name = _dummy_string;
- }
+contract ERC165 is Dummy {
+ function supportsInterface(uint32 interfaceId) public view returns (bool) {
+ require(false, stub_error);
+ interfaceId;
+ dummy;
+ return false;
+ }
+}
- function symbol() external view returns (string memory res_symbol) {
- require(false, stub_error);
- res_symbol = _dummy_string;
- }
+contract ERC721 is Dummy, ERC165, ERC721Events {
+ function balanceOf(address owner) public view returns (uint256) {
+ require(false, stub_error);
+ owner;
+ dummy;
+ return 0;
+ }
+ function ownerOf(uint256 tokenId) public view returns (address) {
+ require(false, stub_error);
+ tokenId;
+ dummy;
+ return 0x0000000000000000000000000000000000000000;
+ }
+ function safeTransferFromWithData(address from, address to, uint256 tokenId, bytes memory data) public {
+ require(false, stub_error);
+ from;
+ to;
+ tokenId;
+ data;
+ dummy = 0;
+ }
+ function safeTransferFrom(address from, address to, uint256 tokenId) public {
+ require(false, stub_error);
+ from;
+ to;
+ tokenId;
+ dummy = 0;
+ }
+ function transferFrom(address from, address to, uint256 tokenId) public {
+ require(false, stub_error);
+ from;
+ to;
+ tokenId;
+ dummy = 0;
+ }
+ function approve(address approved, uint256 tokenId) public {
+ require(false, stub_error);
+ approved;
+ tokenId;
+ dummy = 0;
+ }
+ function setApprovalForAll(address operator, bool approved) public {
+ require(false, stub_error);
+ operator;
+ approved;
+ dummy = 0;
+ }
+ function getApproved(uint256 tokenId) public view returns (address) {
+ require(false, stub_error);
+ tokenId;
+ dummy;
+ return 0x0000000000000000000000000000000000000000;
+ }
+ function isApprovedForAll(address owner, address operator) public view returns (address) {
+ require(false, stub_error);
+ owner;
+ operator;
+ dummy;
+ return 0x0000000000000000000000000000000000000000;
+ }
+}
- function tokenURI(uint256 tokenId) external view returns (string memory) {
- require(false, stub_error);
- tokenId;
- return _dummy_string;
- }
+contract ERC721Burnable is Dummy {
+ function burn(uint256 tokenId) public {
+ require(false, stub_error);
+ tokenId;
+ dummy = 0;
+ }
+}
- function tokenByIndex(uint256 index) external view returns (uint256) {
- require(false, stub_error);
- index;
+contract ERC721Enumerable is Dummy, InlineTotalSupply {
+ function tokenByIndex(uint256 index) public view returns (uint256) {
+ require(false, stub_error);
+ index;
+ dummy;
return 0;
- }
-
- function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256) {
- require(false, stub_error);
+ }
+ function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) {
+ require(false, stub_error);
owner;
index;
+ dummy;
return 0;
- }
+ }
+}
- // 0x70a08231
- function balanceOf(address owner) external view returns (uint256) {
- require(false, stub_error);
- owner;
- return 0;
- }
+contract ERC721Metadata is Dummy, InlineNameSymbol {
+ function tokenUri(uint256 tokenId) public view returns (string memory) {
+ require(false, stub_error);
+ tokenId;
+ dummy;
+ return "";
+ }
+}
- // 0x6352211e
- function ownerOf(uint256 tokenId) external view returns (address) {
- require(false, stub_error);
- tokenId;
- return _dummy_addr;
- }
+contract ERC721Mintable is Dummy, ERC721MintableEvents {
+ function mintingFinished() public view returns (bool) {
+ require(false, stub_error);
+ dummy;
+ return false;
+ }
+ function mint(address to, uint256 tokenId) public returns (bool) {
+ require(false, stub_error);
+ to;
+ tokenId;
+ dummy = 0;
+ return false;
+ }
+ function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) public returns (bool) {
+ require(false, stub_error);
+ to;
+ tokenId;
+ tokenUri;
+ dummy = 0;
+ return false;
+ }
+ function finishMinting() public returns (bool) {
+ require(false, stub_error);
+ dummy = 0;
+ return false;
+ }
+}
- // 0xb88d4fde
- function safeTransferFrom(
- address from,
- address to,
- uint256 tokenId,
- bytes calldata data
- ) external payable {
- require(false, stub_error);
- from;
- to;
- tokenId;
- data;
- }
-
- // 0x42842e0e
- function safeTransferFrom(
- address from,
- address to,
- uint256 tokenId
- ) external payable {
- require(false, stub_error);
- from;
- to;
- tokenId;
- }
-
- // 0x23b872dd
- function transferFrom(
- address from,
- address to,
- uint256 tokenId
- ) external payable {
- require(false, stub_error);
- from;
- to;
- tokenId;
- }
+contract ERC721UniqueExtensions is Dummy {
+ function transfer(address to, uint256 tokenId) public {
+ require(false, stub_error);
+ to;
+ tokenId;
+ dummy = 0;
+ }
+ function nextTokenId() public view returns (uint256) {
+ require(false, stub_error);
+ dummy;
+ return 0;
+ }
+}
- // 0x095ea7b3
- function approve(address approved, uint256 tokenId) external payable {
- require(false, stub_error);
- approved;
- tokenId;
- }
-
- // 0xa22cb465
- function setApprovalForAll(address operator, bool approved) external {
- require(false, stub_error);
- operator;
- approved;
- _dummy = 0;
- }
-
- // 0x081812fc
- function getApproved(uint256 tokenId) external view returns (address) {
- require(false, stub_error);
- tokenId;
- return _dummy_addr;
- }
-
- // 0xe985e9c5
- function isApprovedForAll(address owner, address operator)
- external
- view
- returns (bool)
- {
- require(false, stub_error);
- owner;
- operator;
- return false;
- }
-
- // 0x01ffc9a7
- function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
- return
- // ERC721
- interfaceID == 0x80ac58cd ||
- // ERC721Metadata
- interfaceID == 0x5b5e139f ||
- // ERC721Enumerable
- interfaceID == 0x780e9d63 ||
- // ERC165
- interfaceID == 0x01ffc9a7;
- }
-}
+contract UniqueNFT is Dummy, ERC165, ERC721, ERC721Metadata, ERC721Enumerable, ERC721UniqueExtensions, ERC721Mintable, ERC721Burnable {
+}
\ No newline at end of file