difftreelog
fix Generate default vaue for vec with tuple
in: master
1 file changed
crates/evm-coder/src/solidity.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! Implementation detail of [`crate::solidity_interface`] macro code-generation.18//! You should not rely on any public item from this module, as it is only intended to be used19//! by procedural macro, API and output format may be changed at any time.20//!21//! Purpose of this module is to receive solidity contract definition in module-specified22//! format, and then output string, representing interface of this contract in solidity language2324#[cfg(not(feature = "std"))]25use alloc::{string::String, vec::Vec, collections::BTreeMap, format};26#[cfg(feature = "std")]27use std::collections::BTreeMap;28use core::{29 fmt::{self, Write},30 marker::PhantomData,31 cell::{Cell, RefCell},32 cmp::Reverse,33};34use impl_trait_for_tuples::impl_for_tuples;35use crate::types::*;3637#[derive(Default)]38pub struct TypeCollector {39 /// Code => id40 /// id ordering is required to perform topo-sort on the resulting data41 structs: RefCell<BTreeMap<string, usize>>,42 anonymous: RefCell<BTreeMap<Vec<string>, usize>>,43 id: Cell<usize>,44}45impl TypeCollector {46 pub fn new() -> Self {47 Self::default()48 }49 pub fn collect(&self, item: string) {50 let id = self.next_id();51 self.structs.borrow_mut().insert(item, id);52 }53 pub fn next_id(&self) -> usize {54 let v = self.id.get();55 self.id.set(v + 1);56 v57 }58 pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {59 let names = T::names(self);60 if let Some(id) = self.anonymous.borrow().get(&names).cloned() {61 return format!("Tuple{}", id);62 }63 let id = self.next_id();64 let mut str = String::new();65 writeln!(str, "/// @dev anonymous struct").unwrap();66 writeln!(str, "struct Tuple{} {{", id).unwrap();67 for (i, name) in names.iter().enumerate() {68 writeln!(str, "\t{} field_{};", name, i).unwrap();69 }70 writeln!(str, "}}").unwrap();71 self.collect(str);72 self.anonymous.borrow_mut().insert(names, id);73 format!("Tuple{}", id)74 }75 pub fn finish(self) -> Vec<string> {76 let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();77 data.sort_by_key(|(_, id)| Reverse(*id));78 data.into_iter().map(|(code, _)| code).collect()79 }80}8182pub trait SolidityTypeName: 'static {83 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;84 /// "simple" types are stored inline, no `memory` modifier should be used in solidity85 fn is_simple() -> bool;86 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;87 /// Specialization88 fn is_void() -> bool {89 false90 }91}92macro_rules! solidity_type_name {93 ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {94 $(95 impl SolidityTypeName for $ty {96 fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {97 write!(writer, $name)98 }99 fn is_simple() -> bool {100 $simple101 }102 fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {103 write!(writer, $default)104 }105 }106 )*107 };108}109110solidity_type_name! {111 uint8 => "uint8" true = "0",112 uint32 => "uint32" true = "0",113 uint64 => "uint64" true = "0",114 uint128 => "uint128" true = "0",115 uint256 => "uint256" true = "0",116 bytes4 => "bytes4" true = "bytes4(0)",117 address => "address" true = "0x0000000000000000000000000000000000000000",118 string => "string" false = "\"\"",119 bytes => "bytes" false = "hex\"\"",120 bool => "bool" true = "false",121}122impl SolidityTypeName for void {123 fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {124 Ok(())125 }126 fn is_simple() -> bool {127 true128 }129 fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {130 Ok(())131 }132 fn is_void() -> bool {133 true134 }135}136137mod sealed {138 /// Not every type should be directly placed in vec.139 /// Vec encoding is not memory efficient, as every item will be padded140 /// to 32 bytes.141 /// Instead you should use specialized types (`bytes` in case of `Vec<u8>`)142 pub trait CanBePlacedInVec {}143}144145impl sealed::CanBePlacedInVec for uint256 {}146impl sealed::CanBePlacedInVec for string {}147impl sealed::CanBePlacedInVec for address {}148149impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {150 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {151 T::solidity_name(writer, tc)?;152 write!(writer, "[]")153 }154 fn is_simple() -> bool {155 false156 }157 fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {158 write!(writer, "[]")159 }160}161162pub trait SolidityTupleType {163 fn names(tc: &TypeCollector) -> Vec<String>;164 fn len() -> usize;165}166167macro_rules! count {168 () => (0usize);169 ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));170}171172macro_rules! impl_tuples {173 ($($ident:ident)+) => {174 impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}175 impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {176 fn names(tc: &TypeCollector) -> Vec<string> {177 let mut collected = Vec::with_capacity(Self::len());178 $({179 let mut out = string::new();180 $ident::solidity_name(&mut out, tc).expect("no fmt error");181 collected.push(out);182 })*;183 collected184 }185186 fn len() -> usize {187 count!($($ident)*)188 }189 }190 impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {191 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {192 write!(writer, "{}", tc.collect_tuple::<Self>())193 }194 fn is_simple() -> bool {195 false196 }197 #[allow(unused_assignments)]198 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {199 write!(writer, "{}(", tc.collect_tuple::<Self>())?;200 let mut first = true;201 $(202 if !first {203 write!(writer, ",")?;204 } else {205 first = false;206 }207 <$ident>::solidity_default(writer, tc)?;208 )*209 write!(writer, ")")210 }211 }212 };213}214215impl_tuples! {A}216impl_tuples! {A B}217impl_tuples! {A B C}218impl_tuples! {A B C D}219impl_tuples! {A B C D E}220impl_tuples! {A B C D E F}221impl_tuples! {A B C D E F G}222impl_tuples! {A B C D E F G H}223impl_tuples! {A B C D E F G H I}224impl_tuples! {A B C D E F G H I J}225226pub trait SolidityArguments {227 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;228 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result;229 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;230 fn is_empty(&self) -> bool {231 self.len() == 0232 }233 fn len(&self) -> usize;234}235236#[derive(Default)]237pub struct UnnamedArgument<T>(PhantomData<*const T>);238239impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {240 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {241 if !T::is_void() {242 T::solidity_name(writer, tc)?;243 if !T::is_simple() {244 write!(writer, " memory")?;245 }246 Ok(())247 } else {248 Ok(())249 }250 }251 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {252 Ok(())253 }254 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {255 T::solidity_default(writer, tc)256 }257 fn len(&self) -> usize {258 if T::is_void() {259 0260 } else {261 1262 }263 }264}265266pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);267268impl<T> NamedArgument<T> {269 pub fn new(name: &'static str) -> Self {270 Self(name, Default::default())271 }272}273274impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {275 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {276 if !T::is_void() {277 T::solidity_name(writer, tc)?;278 if !T::is_simple() {279 write!(writer, " memory")?;280 }281 write!(writer, " {}", self.0)282 } else {283 Ok(())284 }285 }286 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {287 writeln!(writer, "\t{prefix}\t{};", self.0)288 }289 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {290 T::solidity_default(writer, tc)291 }292 fn len(&self) -> usize {293 if T::is_void() {294 0295 } else {296 1297 }298 }299}300301pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);302303impl<T> SolidityEventArgument<T> {304 pub fn new(indexed: bool, name: &'static str) -> Self {305 Self(indexed, name, Default::default())306 }307}308309impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {310 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {311 if !T::is_void() {312 T::solidity_name(writer, tc)?;313 if self.0 {314 write!(writer, " indexed")?;315 }316 write!(writer, " {}", self.1)317 } else {318 Ok(())319 }320 }321 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {322 writeln!(writer, "\t{prefix}\t{};", self.1)323 }324 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {325 T::solidity_default(writer, tc)326 }327 fn len(&self) -> usize {328 if T::is_void() {329 0330 } else {331 1332 }333 }334}335336impl SolidityArguments for () {337 fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {338 Ok(())339 }340 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {341 Ok(())342 }343 fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {344 Ok(())345 }346 fn len(&self) -> usize {347 0348 }349}350351#[impl_for_tuples(1, 12)]352impl SolidityArguments for Tuple {353 for_tuples!( where #( Tuple: SolidityArguments ),* );354355 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {356 let mut first = true;357 for_tuples!( #(358 if !Tuple.is_empty() {359 if !first {360 write!(writer, ", ")?;361 }362 first = false;363 Tuple.solidity_name(writer, tc)?;364 }365 )* );366 Ok(())367 }368 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {369 for_tuples!( #(370 Tuple.solidity_get(prefix, writer)?;371 )* );372 Ok(())373 }374 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {375 if self.is_empty() {376 Ok(())377 } else if self.len() == 1 {378 for_tuples!( #(379 Tuple.solidity_default(writer, tc)?;380 )* );381 Ok(())382 } else {383 write!(writer, "(")?;384 let mut first = true;385 for_tuples!( #(386 if !Tuple.is_empty() {387 if !first {388 write!(writer, ", ")?;389 }390 first = false;391 Tuple.solidity_default(writer, tc)?;392 }393 )* );394 write!(writer, ")")?;395 Ok(())396 }397 }398 fn len(&self) -> usize {399 for_tuples!( #( Tuple.len() )+* )400 }401}402403pub trait SolidityFunctions {404 fn solidity_name(405 &self,406 is_impl: bool,407 writer: &mut impl fmt::Write,408 tc: &TypeCollector,409 ) -> fmt::Result;410}411412pub enum SolidityMutability {413 Pure,414 View,415 Mutable,416}417pub struct SolidityFunction<A, R> {418 pub docs: &'static [&'static str],419 pub selector_str: &'static str,420 pub selector: u32,421 pub hide: bool,422 pub name: &'static str,423 pub args: A,424 pub result: R,425 pub mutability: SolidityMutability,426 pub is_payable: bool,427}428impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {429 fn solidity_name(430 &self,431 is_impl: bool,432 writer: &mut impl fmt::Write,433 tc: &TypeCollector,434 ) -> fmt::Result {435 let hide_comment = self.hide.then(|| "// ").unwrap_or("");436 for doc in self.docs {437 writeln!(writer, "\t{hide_comment}///{}", doc)?;438 }439 writeln!(440 writer,441 "\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",442 self.selector443 )?;444 writeln!(445 writer,446 "\t{hide_comment}/// or in textual repr: {}",447 self.selector_str448 )?;449 write!(writer, "\t{hide_comment}function {}(", self.name)?;450 self.args.solidity_name(writer, tc)?;451 write!(writer, ")")?;452 if is_impl {453 write!(writer, " public")?;454 } else {455 write!(writer, " external")?;456 }457 match &self.mutability {458 SolidityMutability::Pure => write!(writer, " pure")?,459 SolidityMutability::View => write!(writer, " view")?,460 SolidityMutability::Mutable => {}461 }462 if self.is_payable {463 write!(writer, " payable")?;464 }465 if !self.result.is_empty() {466 write!(writer, " returns (")?;467 self.result.solidity_name(writer, tc)?;468 write!(writer, ")")?;469 }470 if is_impl {471 writeln!(writer, " {{")?;472 writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;473 self.args.solidity_get(hide_comment, writer)?;474 match &self.mutability {475 SolidityMutability::Pure => {}476 SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,477 SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,478 }479 if !self.result.is_empty() {480 write!(writer, "\t{hide_comment}\treturn ")?;481 self.result.solidity_default(writer, tc)?;482 writeln!(writer, ";")?;483 }484 writeln!(writer, "\t{hide_comment}}}")?;485 } else {486 writeln!(writer, ";")?;487 }488 if self.hide {489 writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;490 }491 Ok(())492 }493}494495#[impl_for_tuples(0, 48)]496impl SolidityFunctions for Tuple {497 for_tuples!( where #( Tuple: SolidityFunctions ),* );498499 fn solidity_name(500 &self,501 is_impl: bool,502 writer: &mut impl fmt::Write,503 tc: &TypeCollector,504 ) -> fmt::Result {505 let mut first = false;506 for_tuples!( #(507 Tuple.solidity_name(is_impl, writer, tc)?;508 )* );509 Ok(())510 }511}512513pub struct SolidityInterface<F: SolidityFunctions> {514 pub docs: &'static [&'static str],515 pub selector: bytes4,516 pub name: &'static str,517 pub is: &'static [&'static str],518 pub functions: F,519}520521impl<F: SolidityFunctions> SolidityInterface<F> {522 pub fn format(523 &self,524 is_impl: bool,525 out: &mut impl fmt::Write,526 tc: &TypeCollector,527 ) -> fmt::Result {528 const ZERO_BYTES: [u8; 4] = [0; 4];529 for doc in self.docs {530 writeln!(out, "///{}", doc)?;531 }532 if self.selector != ZERO_BYTES {533 writeln!(534 out,535 "/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",536 u32::from_be_bytes(self.selector)537 )?;538 }539 if is_impl {540 write!(out, "contract ")?;541 } else {542 write!(out, "interface ")?;543 }544 write!(out, "{}", self.name)?;545 if !self.is.is_empty() {546 write!(out, " is")?;547 for (i, n) in self.is.iter().enumerate() {548 if i != 0 {549 write!(out, ",")?;550 }551 write!(out, " {}", n)?;552 }553 }554 writeln!(out, " {{")?;555 self.functions.solidity_name(is_impl, out, tc)?;556 writeln!(out, "}}")?;557 Ok(())558 }559}560561pub struct SolidityEvent<A> {562 pub name: &'static str,563 pub args: A,564}565566impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {567 fn solidity_name(568 &self,569 _is_impl: bool,570 writer: &mut impl fmt::Write,571 tc: &TypeCollector,572 ) -> fmt::Result {573 write!(writer, "\tevent {}(", self.name)?;574 self.args.solidity_name(writer, tc)?;575 writeln!(writer, ");")576 }577}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! Implementation detail of [`crate::solidity_interface`] macro code-generation.18//! You should not rely on any public item from this module, as it is only intended to be used19//! by procedural macro, API and output format may be changed at any time.20//!21//! Purpose of this module is to receive solidity contract definition in module-specified22//! format, and then output string, representing interface of this contract in solidity language2324#[cfg(not(feature = "std"))]25use alloc::{string::String, vec::Vec, collections::BTreeMap, format};26#[cfg(feature = "std")]27use std::collections::BTreeMap;28use core::{29 fmt::{self, Write},30 marker::PhantomData,31 cell::{Cell, RefCell},32 cmp::Reverse,33};34use impl_trait_for_tuples::impl_for_tuples;35use crate::types::*;3637#[derive(Default)]38pub struct TypeCollector {39 /// Code => id40 /// id ordering is required to perform topo-sort on the resulting data41 structs: RefCell<BTreeMap<string, usize>>,42 anonymous: RefCell<BTreeMap<Vec<string>, usize>>,43 id: Cell<usize>,44}45impl TypeCollector {46 pub fn new() -> Self {47 Self::default()48 }49 pub fn collect(&self, item: string) {50 let id = self.next_id();51 self.structs.borrow_mut().insert(item, id);52 }53 pub fn next_id(&self) -> usize {54 let v = self.id.get();55 self.id.set(v + 1);56 v57 }58 pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {59 let names = T::names(self);60 if let Some(id) = self.anonymous.borrow().get(&names).cloned() {61 return format!("Tuple{}", id);62 }63 let id = self.next_id();64 let mut str = String::new();65 writeln!(str, "/// @dev anonymous struct").unwrap();66 writeln!(str, "struct Tuple{} {{", id).unwrap();67 for (i, name) in names.iter().enumerate() {68 writeln!(str, "\t{} field_{};", name, i).unwrap();69 }70 writeln!(str, "}}").unwrap();71 self.collect(str);72 self.anonymous.borrow_mut().insert(names, id);73 format!("Tuple{}", id)74 }75 pub fn finish(self) -> Vec<string> {76 let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();77 data.sort_by_key(|(_, id)| Reverse(*id));78 data.into_iter().map(|(code, _)| code).collect()79 }80}8182pub trait SolidityTypeName: 'static {83 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;84 /// "simple" types are stored inline, no `memory` modifier should be used in solidity85 fn is_simple() -> bool;86 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;87 /// Specialization88 fn is_void() -> bool {89 false90 }91}92macro_rules! solidity_type_name {93 ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {94 $(95 impl SolidityTypeName for $ty {96 fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {97 write!(writer, $name)98 }99 fn is_simple() -> bool {100 $simple101 }102 fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {103 write!(writer, $default)104 }105 }106 )*107 };108}109110solidity_type_name! {111 uint8 => "uint8" true = "0",112 uint32 => "uint32" true = "0",113 uint64 => "uint64" true = "0",114 uint128 => "uint128" true = "0",115 uint256 => "uint256" true = "0",116 bytes4 => "bytes4" true = "bytes4(0)",117 address => "address" true = "0x0000000000000000000000000000000000000000",118 string => "string" false = "\"\"",119 bytes => "bytes" false = "hex\"\"",120 bool => "bool" true = "false",121}122impl SolidityTypeName for void {123 fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {124 Ok(())125 }126 fn is_simple() -> bool {127 true128 }129 fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {130 Ok(())131 }132 fn is_void() -> bool {133 true134 }135}136137mod sealed {138 /// Not every type should be directly placed in vec.139 /// Vec encoding is not memory efficient, as every item will be padded140 /// to 32 bytes.141 /// Instead you should use specialized types (`bytes` in case of `Vec<u8>`)142 pub trait CanBePlacedInVec {}143}144145impl sealed::CanBePlacedInVec for uint256 {}146impl sealed::CanBePlacedInVec for string {}147impl sealed::CanBePlacedInVec for address {}148149impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {150 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {151 T::solidity_name(writer, tc)?;152 write!(writer, "[]")153 }154 fn is_simple() -> bool {155 false156 }157 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {158 write!(writer, "new ")?;159 T::solidity_name(writer, tc)?;160 write!(writer, "[](0)")161 }162}163164pub trait SolidityTupleType {165 fn names(tc: &TypeCollector) -> Vec<String>;166 fn len() -> usize;167}168169macro_rules! count {170 () => (0usize);171 ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));172}173174macro_rules! impl_tuples {175 ($($ident:ident)+) => {176 impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}177 impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {178 fn names(tc: &TypeCollector) -> Vec<string> {179 let mut collected = Vec::with_capacity(Self::len());180 $({181 let mut out = string::new();182 $ident::solidity_name(&mut out, tc).expect("no fmt error");183 collected.push(out);184 })*;185 collected186 }187188 fn len() -> usize {189 count!($($ident)*)190 }191 }192 impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {193 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {194 write!(writer, "{}", tc.collect_tuple::<Self>())195 }196 fn is_simple() -> bool {197 false198 }199 #[allow(unused_assignments)]200 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {201 write!(writer, "{}(", tc.collect_tuple::<Self>())?;202 let mut first = true;203 $(204 if !first {205 write!(writer, ",")?;206 } else {207 first = false;208 }209 <$ident>::solidity_default(writer, tc)?;210 )*211 write!(writer, ")")212 }213 }214 };215}216217impl_tuples! {A}218impl_tuples! {A B}219impl_tuples! {A B C}220impl_tuples! {A B C D}221impl_tuples! {A B C D E}222impl_tuples! {A B C D E F}223impl_tuples! {A B C D E F G}224impl_tuples! {A B C D E F G H}225impl_tuples! {A B C D E F G H I}226impl_tuples! {A B C D E F G H I J}227228pub trait SolidityArguments {229 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;230 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result;231 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;232 fn is_empty(&self) -> bool {233 self.len() == 0234 }235 fn len(&self) -> usize;236}237238#[derive(Default)]239pub struct UnnamedArgument<T>(PhantomData<*const T>);240241impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {242 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {243 if !T::is_void() {244 T::solidity_name(writer, tc)?;245 if !T::is_simple() {246 write!(writer, " memory")?;247 }248 Ok(())249 } else {250 Ok(())251 }252 }253 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {254 Ok(())255 }256 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {257 T::solidity_default(writer, tc)258 }259 fn len(&self) -> usize {260 if T::is_void() {261 0262 } else {263 1264 }265 }266}267268pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);269270impl<T> NamedArgument<T> {271 pub fn new(name: &'static str) -> Self {272 Self(name, Default::default())273 }274}275276impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {277 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {278 if !T::is_void() {279 T::solidity_name(writer, tc)?;280 if !T::is_simple() {281 write!(writer, " memory")?;282 }283 write!(writer, " {}", self.0)284 } else {285 Ok(())286 }287 }288 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {289 writeln!(writer, "\t{prefix}\t{};", self.0)290 }291 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {292 T::solidity_default(writer, tc)293 }294 fn len(&self) -> usize {295 if T::is_void() {296 0297 } else {298 1299 }300 }301}302303pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);304305impl<T> SolidityEventArgument<T> {306 pub fn new(indexed: bool, name: &'static str) -> Self {307 Self(indexed, name, Default::default())308 }309}310311impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {312 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {313 if !T::is_void() {314 T::solidity_name(writer, tc)?;315 if self.0 {316 write!(writer, " indexed")?;317 }318 write!(writer, " {}", self.1)319 } else {320 Ok(())321 }322 }323 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {324 writeln!(writer, "\t{prefix}\t{};", self.1)325 }326 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {327 T::solidity_default(writer, tc)328 }329 fn len(&self) -> usize {330 if T::is_void() {331 0332 } else {333 1334 }335 }336}337338impl SolidityArguments for () {339 fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {340 Ok(())341 }342 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {343 Ok(())344 }345 fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {346 Ok(())347 }348 fn len(&self) -> usize {349 0350 }351}352353#[impl_for_tuples(1, 12)]354impl SolidityArguments for Tuple {355 for_tuples!( where #( Tuple: SolidityArguments ),* );356357 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {358 let mut first = true;359 for_tuples!( #(360 if !Tuple.is_empty() {361 if !first {362 write!(writer, ", ")?;363 }364 first = false;365 Tuple.solidity_name(writer, tc)?;366 }367 )* );368 Ok(())369 }370 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {371 for_tuples!( #(372 Tuple.solidity_get(prefix, writer)?;373 )* );374 Ok(())375 }376 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {377 if self.is_empty() {378 Ok(())379 } else if self.len() == 1 {380 for_tuples!( #(381 Tuple.solidity_default(writer, tc)?;382 )* );383 Ok(())384 } else {385 write!(writer, "(")?;386 let mut first = true;387 for_tuples!( #(388 if !Tuple.is_empty() {389 if !first {390 write!(writer, ", ")?;391 }392 first = false;393 Tuple.solidity_default(writer, tc)?;394 }395 )* );396 write!(writer, ")")?;397 Ok(())398 }399 }400 fn len(&self) -> usize {401 for_tuples!( #( Tuple.len() )+* )402 }403}404405pub trait SolidityFunctions {406 fn solidity_name(407 &self,408 is_impl: bool,409 writer: &mut impl fmt::Write,410 tc: &TypeCollector,411 ) -> fmt::Result;412}413414pub enum SolidityMutability {415 Pure,416 View,417 Mutable,418}419pub struct SolidityFunction<A, R> {420 pub docs: &'static [&'static str],421 pub selector_str: &'static str,422 pub selector: u32,423 pub hide: bool,424 pub name: &'static str,425 pub args: A,426 pub result: R,427 pub mutability: SolidityMutability,428 pub is_payable: bool,429}430impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {431 fn solidity_name(432 &self,433 is_impl: bool,434 writer: &mut impl fmt::Write,435 tc: &TypeCollector,436 ) -> fmt::Result {437 let hide_comment = self.hide.then(|| "// ").unwrap_or("");438 for doc in self.docs {439 writeln!(writer, "\t{hide_comment}///{}", doc)?;440 }441 writeln!(442 writer,443 "\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",444 self.selector445 )?;446 writeln!(447 writer,448 "\t{hide_comment}/// or in textual repr: {}",449 self.selector_str450 )?;451 write!(writer, "\t{hide_comment}function {}(", self.name)?;452 self.args.solidity_name(writer, tc)?;453 write!(writer, ")")?;454 if is_impl {455 write!(writer, " public")?;456 } else {457 write!(writer, " external")?;458 }459 match &self.mutability {460 SolidityMutability::Pure => write!(writer, " pure")?,461 SolidityMutability::View => write!(writer, " view")?,462 SolidityMutability::Mutable => {}463 }464 if self.is_payable {465 write!(writer, " payable")?;466 }467 if !self.result.is_empty() {468 write!(writer, " returns (")?;469 self.result.solidity_name(writer, tc)?;470 write!(writer, ")")?;471 }472 if is_impl {473 writeln!(writer, " {{")?;474 writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;475 self.args.solidity_get(hide_comment, writer)?;476 match &self.mutability {477 SolidityMutability::Pure => {}478 SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,479 SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,480 }481 if !self.result.is_empty() {482 write!(writer, "\t{hide_comment}\treturn ")?;483 self.result.solidity_default(writer, tc)?;484 writeln!(writer, ";")?;485 }486 writeln!(writer, "\t{hide_comment}}}")?;487 } else {488 writeln!(writer, ";")?;489 }490 if self.hide {491 writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;492 }493 Ok(())494 }495}496497#[impl_for_tuples(0, 48)]498impl SolidityFunctions for Tuple {499 for_tuples!( where #( Tuple: SolidityFunctions ),* );500501 fn solidity_name(502 &self,503 is_impl: bool,504 writer: &mut impl fmt::Write,505 tc: &TypeCollector,506 ) -> fmt::Result {507 let mut first = false;508 for_tuples!( #(509 Tuple.solidity_name(is_impl, writer, tc)?;510 )* );511 Ok(())512 }513}514515pub struct SolidityInterface<F: SolidityFunctions> {516 pub docs: &'static [&'static str],517 pub selector: bytes4,518 pub name: &'static str,519 pub is: &'static [&'static str],520 pub functions: F,521}522523impl<F: SolidityFunctions> SolidityInterface<F> {524 pub fn format(525 &self,526 is_impl: bool,527 out: &mut impl fmt::Write,528 tc: &TypeCollector,529 ) -> fmt::Result {530 const ZERO_BYTES: [u8; 4] = [0; 4];531 for doc in self.docs {532 writeln!(out, "///{}", doc)?;533 }534 if self.selector != ZERO_BYTES {535 writeln!(536 out,537 "/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",538 u32::from_be_bytes(self.selector)539 )?;540 }541 if is_impl {542 write!(out, "contract ")?;543 } else {544 write!(out, "interface ")?;545 }546 write!(out, "{}", self.name)?;547 if !self.is.is_empty() {548 write!(out, " is")?;549 for (i, n) in self.is.iter().enumerate() {550 if i != 0 {551 write!(out, ",")?;552 }553 write!(out, " {}", n)?;554 }555 }556 writeln!(out, " {{")?;557 self.functions.solidity_name(is_impl, out, tc)?;558 writeln!(out, "}}")?;559 Ok(())560 }561}562563pub struct SolidityEvent<A> {564 pub name: &'static str,565 pub args: A,566}567568impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {569 fn solidity_name(570 &self,571 _is_impl: bool,572 writer: &mut impl fmt::Write,573 tc: &TypeCollector,574 ) -> fmt::Result {575 write!(writer, "\tevent {}(", self.name)?;576 self.args.solidity_name(writer, tc)?;577 writeln!(writer, ");")578 }579}