difftreelog
feat support complex types in solidity defs
in: master
5 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 }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
@@ -179,7 +179,7 @@
#consts
)*
- pub fn generate_solidity_interface(out_set: &mut sp_std::collections::btree_set::BTreeSet<string>, is_impl: bool) {
+ pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {
use evm_coder::solidity::*;
use core::fmt::Write;
let interface = SolidityInterface {
@@ -191,8 +191,8 @@
};
let mut out = string::new();
out.push_str("// Inline\n");
- let _ = interface.format(is_impl, &mut out);
- out_set.insert(out);
+ let _ = interface.format(is_impl, &mut out, tc);
+ tc.collect(out);
}
}
crates/evm-coder/src/abi.rsdiffbeforeafterboth--- a/crates/evm-coder/src/abi.rs
+++ b/crates/evm-coder/src/abi.rs
@@ -247,6 +247,51 @@
impl_abi_readable!(bool, bool);
impl_abi_readable!(string, string);
+mod sealed {
+ /// Not all types can be placed in vec, i.e `Vec<u8>` is restricted, `bytes` should be used instead
+ pub trait CanBePlacedInVec {}
+}
+
+impl sealed::CanBePlacedInVec for U256 {}
+impl sealed::CanBePlacedInVec for string {}
+impl sealed::CanBePlacedInVec for H160 {}
+
+impl<R: sealed::CanBePlacedInVec> AbiRead<Vec<R>> for AbiReader<'_>
+where
+ Self: AbiRead<R>,
+{
+ fn abi_read(&mut self) -> Result<Vec<R>> {
+ todo!()
+ }
+}
+
+macro_rules! impl_tuples {
+ ($($ident:ident)+) => {
+ impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}
+ impl<$($ident),+> AbiRead<($($ident,)+)> for AbiReader<'_>
+ where
+ $(Self: AbiRead<$ident>),+
+ {
+ fn abi_read(&mut self) -> Result<($($ident,)+)> {
+ Ok((
+ $(<Self as AbiRead<$ident>>::abi_read(self)?,)+
+ ))
+ }
+ }
+ };
+}
+
+impl_tuples! {A}
+impl_tuples! {A B}
+impl_tuples! {A B C}
+impl_tuples! {A B C D}
+impl_tuples! {A B C D E}
+impl_tuples! {A B C D E F}
+impl_tuples! {A B C D E F G}
+impl_tuples! {A B C D E F G H}
+impl_tuples! {A B C D E F G H I}
+impl_tuples! {A B C D E F G H I J}
+
pub trait AbiWrite {
fn abi_write(&self, writer: &mut AbiWriter);
}
crates/evm-coder/src/lib.rsdiffbeforeafterboth--- a/crates/evm-coder/src/lib.rs
+++ b/crates/evm-coder/src/lib.rs
@@ -67,8 +67,8 @@
#[test]
#[ignore]
fn $name() {
- use sp_std::collections::btree_set::BTreeSet;
- let mut out = BTreeSet::new();
+ use evm_coder::solidity::TypeCollector;
+ let mut out = TypeCollector::new();
$decl::generate_solidity_interface(&mut out, $is_impl);
println!("=== SNIP START ===");
println!("// SPDX-License-Identifier: OTHER");
@@ -76,7 +76,7 @@
println!();
println!("pragma solidity >=0.8.0 <0.9.0;");
println!();
- for b in out {
+ for b in out.finish() {
println!("{}", b);
}
println!("=== SNIP END ===");
crates/evm-coder/src/solidity.rsdiffbeforeafterboth--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -1,25 +1,79 @@
#[cfg(not(feature = "std"))]
-use alloc::{string::String};
-use core::{fmt, marker::PhantomData};
+use alloc::{
+ string::String,
+ vec::Vec,
+ collections::{BTreeSet, BTreeMap},
+ format,
+};
+#[cfg(feature = "std")]
+use std::collections::{BTreeSet, BTreeMap};
+use core::{
+ fmt::{self, Write},
+ marker::PhantomData,
+ cell::{Cell, RefCell},
+};
use impl_trait_for_tuples::impl_for_tuples;
use crate::types::*;
+#[derive(Default)]
+pub struct TypeCollector {
+ structs: RefCell<BTreeSet<string>>,
+ anonymous: RefCell<BTreeMap<Vec<string>, usize>>,
+ id: Cell<usize>,
+}
+impl TypeCollector {
+ pub fn new() -> Self {
+ Self::default()
+ }
+ pub fn collect(&self, item: string) {
+ self.structs.borrow_mut().insert(item);
+ }
+ pub fn next_id(&self) -> usize {
+ let v = self.id.get();
+ self.id.set(v + 1);
+ v
+ }
+ pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {
+ let names = T::names(self);
+ if let Some(id) = self.anonymous.borrow().get(&names).cloned() {
+ return format!("Tuple{}", id);
+ }
+ let id = self.next_id();
+ let mut str = String::new();
+ writeln!(str, "// Anonymous struct").unwrap();
+ writeln!(str, "struct Tuple{} {{", id).unwrap();
+ for (i, name) in names.iter().enumerate() {
+ writeln!(str, "\t{} field_{};", name, i).unwrap();
+ }
+ writeln!(str, "}}").unwrap();
+ self.collect(str);
+ self.anonymous.borrow_mut().insert(names, id);
+ format!("Tuple{}", id)
+ }
+ pub fn finish(self) -> BTreeSet<string> {
+ self.structs.into_inner()
+ }
+}
+
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 solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
+ fn is_simple() -> bool;
+ fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
fn is_void() -> bool {
false
}
}
-
macro_rules! solidity_type_name {
- ($($ty:ident => $name:literal = $default:literal),* $(,)?) => {
+ ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {
$(
impl SolidityTypeName for $ty {
- fn solidity_name(writer: &mut impl core::fmt::Write) -> core::fmt::Result {
+ fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {
write!(writer, $name)
}
- fn solidity_default(writer: &mut impl core::fmt::Write) -> core::fmt::Result {
+ fn is_simple() -> bool {
+ $simple
+ }
+ fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {
write!(writer, $default)
}
}
@@ -28,20 +82,23 @@
}
solidity_type_name! {
- 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",
+ uint8 => "uint8" true = "0",
+ uint32 => "uint32" true = "0",
+ uint128 => "uint128" true = "0",
+ uint256 => "uint256" true = "0",
+ address => "address" true = "0x0000000000000000000000000000000000000000",
+ string => "string" false = "\"\"",
+ bytes => "bytes" false = "hex\"\"",
+ bool => "bool" true = "false",
}
impl SolidityTypeName for void {
- fn solidity_name(_writer: &mut impl fmt::Write) -> fmt::Result {
+ fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
Ok(())
}
- fn solidity_default(_writer: &mut impl fmt::Write) -> fmt::Result {
+ fn is_simple() -> bool {
+ true
+ }
+ fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
Ok(())
}
fn is_void() -> bool {
@@ -49,10 +106,88 @@
}
}
+mod sealed {
+ pub trait CanBePlacedInVec {}
+}
+
+impl sealed::CanBePlacedInVec for uint256 {}
+impl sealed::CanBePlacedInVec for string {}
+impl sealed::CanBePlacedInVec for address {}
+
+impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {
+ fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ T::solidity_name(writer, tc)?;
+ write!(writer, "[]")
+ }
+ fn is_simple() -> bool {
+ false
+ }
+ fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
+ write!(writer, "[]")
+ }
+}
+
+pub trait SolidityTupleType {
+ fn names(tc: &TypeCollector) -> Vec<String>;
+ fn len() -> usize;
+}
+
+macro_rules! count {
+ () => (0usize);
+ ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
+}
+
+macro_rules! impl_tuples {
+ ($($ident:ident)+) => {
+ impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}
+ impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {
+ fn names(tc: &TypeCollector) -> Vec<string> {
+ let mut collected = Vec::with_capacity(Self::len());
+ $({
+ let mut out = string::new();
+ $ident::solidity_name(&mut out, tc).expect("no fmt error");
+ collected.push(out);
+ })*;
+ collected
+ }
+
+ fn len() -> usize {
+ count!($($ident)*)
+ }
+ }
+ impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {
+ fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ write!(writer, "{}", tc.collect_tuple::<Self>())
+ }
+ fn is_simple() -> bool {
+ false
+ }
+ fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ write!(writer, "{}(", tc.collect_tuple::<Self>())?;
+ $(
+ <$ident>::solidity_default(writer, tc)?;
+ )*
+ write!(writer, ")")
+ }
+ }
+ };
+}
+
+impl_tuples! {A}
+impl_tuples! {A B}
+impl_tuples! {A B C}
+impl_tuples! {A B C D}
+impl_tuples! {A B C D E}
+impl_tuples! {A B C D E F}
+impl_tuples! {A B C D E F G}
+impl_tuples! {A B C D E F G H}
+impl_tuples! {A B C D E F G H I}
+impl_tuples! {A B C D E F G H I J}
+
pub trait SolidityArguments {
- fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result;
+ fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> 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 solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
fn is_empty(&self) -> bool {
self.len() == 0
}
@@ -63,9 +198,13 @@
pub struct UnnamedArgument<T>(PhantomData<*const T>);
impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {
- fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
if !T::is_void() {
- T::solidity_name(writer)
+ T::solidity_name(writer, tc)?;
+ if !T::is_simple() {
+ write!(writer, " memory")?;
+ }
+ Ok(())
} else {
Ok(())
}
@@ -73,8 +212,8 @@
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 solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ T::solidity_default(writer, tc)
}
fn len(&self) -> usize {
if T::is_void() {
@@ -94,9 +233,12 @@
}
impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {
- fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
if !T::is_void() {
- T::solidity_name(writer)?;
+ T::solidity_name(writer, tc)?;
+ if !T::is_simple() {
+ write!(writer, " memory")?;
+ }
write!(writer, " {}", self.0)
} else {
Ok(())
@@ -105,8 +247,8 @@
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 solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ T::solidity_default(writer, tc)
}
fn len(&self) -> usize {
if T::is_void() {
@@ -126,9 +268,9 @@
}
impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {
- fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
if !T::is_void() {
- T::solidity_name(writer)?;
+ T::solidity_name(writer, tc)?;
if self.0 {
write!(writer, " indexed")?;
}
@@ -140,8 +282,8 @@
fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {
writeln!(writer, "\t\t{};", self.1)
}
- fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {
- T::solidity_default(writer)
+ fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ T::solidity_default(writer, tc)
}
fn len(&self) -> usize {
if T::is_void() {
@@ -153,13 +295,13 @@
}
impl SolidityArguments for () {
- fn solidity_name(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
+ fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> 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 {
+ fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
Ok(())
}
fn len(&self) -> usize {
@@ -171,7 +313,7 @@
impl SolidityArguments for Tuple {
for_tuples!( where #( Tuple: SolidityArguments ),* );
- fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
let mut first = true;
for_tuples!( #(
if !Tuple.is_empty() {
@@ -179,7 +321,7 @@
write!(writer, ", ")?;
}
first = false;
- Tuple.solidity_name(writer)?;
+ Tuple.solidity_name(writer, tc)?;
}
)* );
Ok(())
@@ -190,12 +332,12 @@
)* );
Ok(())
}
- fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
if self.is_empty() {
Ok(())
} else if self.len() == 1 {
for_tuples!( #(
- Tuple.solidity_default(writer)?;
+ Tuple.solidity_default(writer, tc)?;
)* );
Ok(())
} else {
@@ -207,7 +349,7 @@
write!(writer, ", ")?;
}
first = false;
- Tuple.solidity_name(writer)?;
+ Tuple.solidity_default(writer, tc)?;
}
)* );
write!(writer, ")")?;
@@ -220,7 +362,12 @@
}
pub trait SolidityFunctions {
- fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result;
+ fn solidity_name(
+ &self,
+ is_impl: bool,
+ writer: &mut impl fmt::Write,
+ tc: &TypeCollector,
+ ) -> fmt::Result;
}
pub enum SolidityMutability {
@@ -235,9 +382,14 @@
pub mutability: SolidityMutability,
}
impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {
- fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {
+ fn solidity_name(
+ &self,
+ is_impl: bool,
+ writer: &mut impl fmt::Write,
+ tc: &TypeCollector,
+ ) -> fmt::Result {
write!(writer, "\tfunction {}(", self.name)?;
- self.args.solidity_name(writer)?;
+ self.args.solidity_name(writer, tc)?;
write!(writer, ")")?;
if is_impl {
write!(writer, " public")?;
@@ -251,7 +403,7 @@
}
if !self.result.is_empty() {
write!(writer, " returns (")?;
- self.result.solidity_name(writer)?;
+ self.result.solidity_name(writer, tc)?;
write!(writer, ")")?;
}
if is_impl {
@@ -265,7 +417,7 @@
}
if !self.result.is_empty() {
write!(writer, "\t\treturn ")?;
- self.result.solidity_default(writer)?;
+ self.result.solidity_default(writer, tc)?;
writeln!(writer, ";")?;
}
writeln!(writer, "\t}}")?;
@@ -280,10 +432,15 @@
impl SolidityFunctions for Tuple {
for_tuples!( where #( Tuple: SolidityFunctions ),* );
- fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {
+ fn solidity_name(
+ &self,
+ is_impl: bool,
+ writer: &mut impl fmt::Write,
+ tc: &TypeCollector,
+ ) -> fmt::Result {
let mut first = false;
for_tuples!( #(
- Tuple.solidity_name(is_impl, writer)?;
+ Tuple.solidity_name(is_impl, writer, tc)?;
)* );
Ok(())
}
@@ -296,7 +453,12 @@
}
impl<F: SolidityFunctions> SolidityInterface<F> {
- pub fn format(&self, is_impl: bool, out: &mut impl fmt::Write) -> fmt::Result {
+ pub fn format(
+ &self,
+ is_impl: bool,
+ out: &mut impl fmt::Write,
+ tc: &TypeCollector,
+ ) -> fmt::Result {
if is_impl {
write!(out, "contract ")?;
} else {
@@ -313,7 +475,7 @@
}
}
writeln!(out, " {{")?;
- self.functions.solidity_name(is_impl, out)?;
+ self.functions.solidity_name(is_impl, out, tc)?;
writeln!(out, "}}")?;
Ok(())
}
@@ -325,9 +487,14 @@
}
impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {
- fn solidity_name(&self, _is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {
+ fn solidity_name(
+ &self,
+ _is_impl: bool,
+ writer: &mut impl fmt::Write,
+ tc: &TypeCollector,
+ ) -> fmt::Result {
write!(writer, "\tevent {}(", self.name)?;
- self.args.solidity_name(writer)?;
+ self.args.solidity_name(writer, tc)?;
writeln!(writer, ");")
}
}