difftreelog
refactor(evm-coder) toposort generated code
in: master
Previously, all items from evm-coder codegen was sorted lexically, which caused some problems, where lexically sorted names of contract were not in the same order as contract inherit chain Now they are sorted by insertion order, and macro-generated code now inserts them in order of definition, thus providing topological sort of output contract items
3 files changed
crates/evm-coder-macros/src/lib.rsdiffbeforeafterboth--- a/crates/evm-coder-macros/src/lib.rs
+++ b/crates/evm-coder-macros/src/lib.rs
@@ -22,7 +22,7 @@
use sha3::{Digest, Keccak256};
use syn::{
DeriveInput, GenericArgument, Ident, ItemImpl, Pat, Path, PathArguments,
- PathSegment, Type, parse_macro_input, spanned::Spanned, Attribute, parse::Parse,
+ PathSegment, Type, parse_macro_input, spanned::Spanned,
};
mod solidity_interface;
@@ -241,7 +241,7 @@
/// Event(#[indexed] uint32),
/// }
///
-/// #[solidity_interface(name = "MyContract", is(SuperContract), inline_is(InlineContract))]
+/// #[solidity_interface(name = MyContract, is(SuperContract), inline_is(InlineContract))]
/// impl Contract {
/// /// Multiply two numbers
/// #[weight(200 + a + b)]
crates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth--- a/crates/evm-coder-macros/src/solidity_interface.rs
+++ b/crates/evm-coder-macros/src/solidity_interface.rs
@@ -989,26 +989,24 @@
#solidity_functions,
)*),
};
- if is_impl {
- tc.collect("/// @dev common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\ncontract ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool) {\n\t\trequire(false, stub_error);\n\t\tinterfaceID;\n\t\treturn true;\n\t}\n}\n".into());
- } else {
- tc.collect("/// @dev common stubs holder\ninterface Dummy {\n}\ninterface ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);\n}\n".into());
- }
- #(
- #solidity_generators
- )*
- #(
- #solidity_event_generators
- )*
let mut out = string::new();
- // In solidity interface usage (is) should be preceeded by interface definition
- // HACK: this comment helps to sort it in a set
if #solidity_name.starts_with("Inline") {
out.push_str("/// @dev inlined interface\n");
}
let _ = interface.format(is_impl, &mut out, tc);
tc.collect(out);
+ #(
+ #solidity_event_generators
+ )*
+ #(
+ #solidity_generators
+ )*
+ if is_impl {
+ tc.collect("/// @dev common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\ncontract ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool) {\n\t\trequire(false, stub_error);\n\t\tinterfaceID;\n\t\treturn true;\n\t}\n}\n".into());
+ } else {
+ tc.collect("/// @dev common stubs holder\ninterface Dummy {\n}\ninterface ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);\n}\n".into());
+ }
}
}
impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {
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#[cfg(not(feature = "std"))]18use alloc::{19 string::String,20 vec::Vec,21 collections::{BTreeSet, BTreeMap},22 format,23};24#[cfg(feature = "std")]25use std::collections::{BTreeSet, BTreeMap};26use core::{27 fmt::{self, Write},28 marker::PhantomData,29 cell::{Cell, RefCell},30};31use impl_trait_for_tuples::impl_for_tuples;32use crate::types::*;3334#[derive(Default)]35pub struct TypeCollector {36 structs: RefCell<BTreeSet<string>>,37 anonymous: RefCell<BTreeMap<Vec<string>, usize>>,38 id: Cell<usize>,39}40impl TypeCollector {41 pub fn new() -> Self {42 Self::default()43 }44 pub fn collect(&self, item: string) {45 self.structs.borrow_mut().insert(item);46 }47 pub fn next_id(&self) -> usize {48 let v = self.id.get();49 self.id.set(v + 1);50 v51 }52 pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {53 let names = T::names(self);54 if let Some(id) = self.anonymous.borrow().get(&names).cloned() {55 return format!("Tuple{}", id);56 }57 let id = self.next_id();58 let mut str = String::new();59 writeln!(str, "/// @dev anonymous struct").unwrap();60 writeln!(str, "struct Tuple{} {{", id).unwrap();61 for (i, name) in names.iter().enumerate() {62 writeln!(str, "\t{} field_{};", name, i).unwrap();63 }64 writeln!(str, "}}").unwrap();65 self.collect(str);66 self.anonymous.borrow_mut().insert(names, id);67 format!("Tuple{}", id)68 }69 pub fn finish(self) -> BTreeSet<string> {70 self.structs.into_inner()71 }72}7374pub trait SolidityTypeName: 'static {75 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;76 fn is_simple() -> bool;77 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;78 fn is_void() -> bool {79 false80 }81}82macro_rules! solidity_type_name {83 ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {84 $(85 impl SolidityTypeName for $ty {86 fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {87 write!(writer, $name)88 }89 fn is_simple() -> bool {90 $simple91 }92 fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {93 write!(writer, $default)94 }95 }96 )*97 };98}99100solidity_type_name! {101 uint8 => "uint8" true = "0",102 uint32 => "uint32" true = "0",103 uint64 => "uint64" true = "0",104 uint128 => "uint128" true = "0",105 uint256 => "uint256" true = "0",106 bytes4 => "bytes4" true = "bytes4(0)",107 address => "address" true = "0x0000000000000000000000000000000000000000",108 string => "string" false = "\"\"",109 bytes => "bytes" false = "hex\"\"",110 bool => "bool" true = "false",111}112impl SolidityTypeName for void {113 fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {114 Ok(())115 }116 fn is_simple() -> bool {117 true118 }119 fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {120 Ok(())121 }122 fn is_void() -> bool {123 true124 }125}126127mod sealed {128 pub trait CanBePlacedInVec {}129}130131impl sealed::CanBePlacedInVec for uint256 {}132impl sealed::CanBePlacedInVec for string {}133impl sealed::CanBePlacedInVec for address {}134135impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {136 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {137 T::solidity_name(writer, tc)?;138 write!(writer, "[]")139 }140 fn is_simple() -> bool {141 false142 }143 fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {144 write!(writer, "[]")145 }146}147148pub trait SolidityTupleType {149 fn names(tc: &TypeCollector) -> Vec<String>;150 fn len() -> usize;151}152153macro_rules! count {154 () => (0usize);155 ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));156}157158macro_rules! impl_tuples {159 ($($ident:ident)+) => {160 impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}161 impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {162 fn names(tc: &TypeCollector) -> Vec<string> {163 let mut collected = Vec::with_capacity(Self::len());164 $({165 let mut out = string::new();166 $ident::solidity_name(&mut out, tc).expect("no fmt error");167 collected.push(out);168 })*;169 collected170 }171172 fn len() -> usize {173 count!($($ident)*)174 }175 }176 impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {177 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {178 write!(writer, "{}", tc.collect_tuple::<Self>())179 }180 fn is_simple() -> bool {181 false182 }183 #[allow(unused_assignments)]184 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {185 write!(writer, "{}(", tc.collect_tuple::<Self>())?;186 let mut first = true;187 $(188 if !first {189 write!(writer, ",")?;190 } else {191 first = false;192 }193 <$ident>::solidity_default(writer, tc)?;194 )*195 write!(writer, ")")196 }197 }198 };199}200201impl_tuples! {A}202impl_tuples! {A B}203impl_tuples! {A B C}204impl_tuples! {A B C D}205impl_tuples! {A B C D E}206impl_tuples! {A B C D E F}207impl_tuples! {A B C D E F G}208impl_tuples! {A B C D E F G H}209impl_tuples! {A B C D E F G H I}210impl_tuples! {A B C D E F G H I J}211212pub trait SolidityArguments {213 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;214 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;215 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;216 fn is_empty(&self) -> bool {217 self.len() == 0218 }219 fn len(&self) -> usize;220}221222#[derive(Default)]223pub struct UnnamedArgument<T>(PhantomData<*const T>);224225impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {226 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {227 if !T::is_void() {228 T::solidity_name(writer, tc)?;229 if !T::is_simple() {230 write!(writer, " memory")?;231 }232 Ok(())233 } else {234 Ok(())235 }236 }237 fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {238 Ok(())239 }240 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {241 T::solidity_default(writer, tc)242 }243 fn len(&self) -> usize {244 if T::is_void() {245 0246 } else {247 1248 }249 }250}251252pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);253254impl<T> NamedArgument<T> {255 pub fn new(name: &'static str) -> Self {256 Self(name, Default::default())257 }258}259260impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {261 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {262 if !T::is_void() {263 T::solidity_name(writer, tc)?;264 if !T::is_simple() {265 write!(writer, " memory")?;266 }267 write!(writer, " {}", self.0)268 } else {269 Ok(())270 }271 }272 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {273 writeln!(writer, "\t\t{};", self.0)274 }275 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {276 T::solidity_default(writer, tc)277 }278 fn len(&self) -> usize {279 if T::is_void() {280 0281 } else {282 1283 }284 }285}286287pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);288289impl<T> SolidityEventArgument<T> {290 pub fn new(indexed: bool, name: &'static str) -> Self {291 Self(indexed, name, Default::default())292 }293}294295impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {296 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {297 if !T::is_void() {298 T::solidity_name(writer, tc)?;299 if self.0 {300 write!(writer, " indexed")?;301 }302 write!(writer, " {}", self.1)303 } else {304 Ok(())305 }306 }307 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {308 writeln!(writer, "\t\t{};", self.1)309 }310 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {311 T::solidity_default(writer, tc)312 }313 fn len(&self) -> usize {314 if T::is_void() {315 0316 } else {317 1318 }319 }320}321322impl SolidityArguments for () {323 fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {324 Ok(())325 }326 fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {327 Ok(())328 }329 fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {330 Ok(())331 }332 fn len(&self) -> usize {333 0334 }335}336337#[impl_for_tuples(1, 12)]338impl SolidityArguments for Tuple {339 for_tuples!( where #( Tuple: SolidityArguments ),* );340341 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {342 let mut first = true;343 for_tuples!( #(344 if !Tuple.is_empty() {345 if !first {346 write!(writer, ", ")?;347 }348 first = false;349 Tuple.solidity_name(writer, tc)?;350 }351 )* );352 Ok(())353 }354 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {355 for_tuples!( #(356 Tuple.solidity_get(writer)?;357 )* );358 Ok(())359 }360 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {361 if self.is_empty() {362 Ok(())363 } else if self.len() == 1 {364 for_tuples!( #(365 Tuple.solidity_default(writer, tc)?;366 )* );367 Ok(())368 } else {369 write!(writer, "(")?;370 let mut first = true;371 for_tuples!( #(372 if !Tuple.is_empty() {373 if !first {374 write!(writer, ", ")?;375 }376 first = false;377 Tuple.solidity_default(writer, tc)?;378 }379 )* );380 write!(writer, ")")?;381 Ok(())382 }383 }384 fn len(&self) -> usize {385 for_tuples!( #( Tuple.len() )+* )386 }387}388389pub trait SolidityFunctions {390 fn solidity_name(391 &self,392 is_impl: bool,393 writer: &mut impl fmt::Write,394 tc: &TypeCollector,395 ) -> fmt::Result;396}397398pub enum SolidityMutability {399 Pure,400 View,401 Mutable,402}403pub struct SolidityFunction<A, R> {404 pub docs: &'static [&'static str],405 pub selector_str: &'static str,406 pub selector: u32,407 pub name: &'static str,408 pub args: A,409 pub result: R,410 pub mutability: SolidityMutability,411}412impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {413 fn solidity_name(414 &self,415 is_impl: bool,416 writer: &mut impl fmt::Write,417 tc: &TypeCollector,418 ) -> fmt::Result {419 for doc in self.docs {420 writeln!(writer, "\t///{}", doc)?;421 }422 if !self.docs.is_empty() {423 writeln!(writer, "\t///")?;424 }425 writeln!(writer, "\t/// @dev EVM selector for this function is: 0x{:0>8x},", self.selector)?;426 writeln!(writer, "\t/// or in textual repr: {}", self.selector_str)?;427 write!(writer, "\tfunction {}(", self.name)?;428 self.args.solidity_name(writer, tc)?;429 write!(writer, ")")?;430 if is_impl {431 write!(writer, " public")?;432 } else {433 write!(writer, " external")?;434 }435 match &self.mutability {436 SolidityMutability::Pure => write!(writer, " pure")?,437 SolidityMutability::View => write!(writer, " view")?,438 SolidityMutability::Mutable => {}439 }440 if !self.result.is_empty() {441 write!(writer, " returns (")?;442 self.result.solidity_name(writer, tc)?;443 write!(writer, ")")?;444 }445 if is_impl {446 writeln!(writer, " {{")?;447 writeln!(writer, "\t\trequire(false, stub_error);")?;448 self.args.solidity_get(writer)?;449 match &self.mutability {450 SolidityMutability::Pure => {}451 SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,452 SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,453 }454 if !self.result.is_empty() {455 write!(writer, "\t\treturn ")?;456 self.result.solidity_default(writer, tc)?;457 writeln!(writer, ";")?;458 }459 writeln!(writer, "\t}}")?;460 } else {461 writeln!(writer, ";")?;462 }463 Ok(())464 }465}466467#[impl_for_tuples(0, 24)]468impl SolidityFunctions for Tuple {469 for_tuples!( where #( Tuple: SolidityFunctions ),* );470471 fn solidity_name(472 &self,473 is_impl: bool,474 writer: &mut impl fmt::Write,475 tc: &TypeCollector,476 ) -> fmt::Result {477 let mut first = false;478 for_tuples!( #(479 Tuple.solidity_name(is_impl, writer, tc)?;480 )* );481 Ok(())482 }483}484485pub struct SolidityInterface<F: SolidityFunctions> {486 pub selector: bytes4,487 pub name: &'static str,488 pub is: &'static [&'static str],489 pub functions: F,490}491492impl<F: SolidityFunctions> SolidityInterface<F> {493 pub fn format(494 &self,495 is_impl: bool,496 out: &mut impl fmt::Write,497 tc: &TypeCollector,498 ) -> fmt::Result {499 const ZERO_BYTES: [u8; 4] = [0; 4];500 if self.selector != ZERO_BYTES {501 writeln!(502 out,503 "/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",504 u32::from_be_bytes(self.selector)505 )?;506 }507 if is_impl {508 write!(out, "contract ")?;509 } else {510 write!(out, "interface ")?;511 }512 write!(out, "{}", self.name)?;513 if !self.is.is_empty() {514 write!(out, " is")?;515 for (i, n) in self.is.iter().enumerate() {516 if i != 0 {517 write!(out, ",")?;518 }519 write!(out, " {}", n)?;520 }521 }522 writeln!(out, " {{")?;523 self.functions.solidity_name(is_impl, out, tc)?;524 writeln!(out, "}}")?;525 Ok(())526 }527}528529pub struct SolidityEvent<A> {530 pub name: &'static str,531 pub args: A,532}533534impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {535 fn solidity_name(536 &self,537 _is_impl: bool,538 writer: &mut impl fmt::Write,539 tc: &TypeCollector,540 ) -> fmt::Result {541 write!(writer, "\tevent {}(", self.name)?;542 self.args.solidity_name(writer, tc)?;543 writeln!(writer, ");")544 }545}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 [`evm_coder::solidity_interface`] macro code-generation1819#[cfg(not(feature = "std"))]20use alloc::{21 string::String,22 vec::Vec,23 collections::BTreeMap,24 format,25};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 fn is_simple() -> bool;85 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;86 fn is_void() -> bool {87 false88 }89}90macro_rules! solidity_type_name {91 ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {92 $(93 impl SolidityTypeName for $ty {94 fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {95 write!(writer, $name)96 }97 fn is_simple() -> bool {98 $simple99 }100 fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {101 write!(writer, $default)102 }103 }104 )*105 };106}107108solidity_type_name! {109 uint8 => "uint8" true = "0",110 uint32 => "uint32" true = "0",111 uint64 => "uint64" true = "0",112 uint128 => "uint128" true = "0",113 uint256 => "uint256" true = "0",114 bytes4 => "bytes4" true = "bytes4(0)",115 address => "address" true = "0x0000000000000000000000000000000000000000",116 string => "string" false = "\"\"",117 bytes => "bytes" false = "hex\"\"",118 bool => "bool" true = "false",119}120impl SolidityTypeName for void {121 fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {122 Ok(())123 }124 fn is_simple() -> bool {125 true126 }127 fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {128 Ok(())129 }130 fn is_void() -> bool {131 true132 }133}134135mod sealed {136 pub trait CanBePlacedInVec {}137}138139impl sealed::CanBePlacedInVec for uint256 {}140impl sealed::CanBePlacedInVec for string {}141impl sealed::CanBePlacedInVec for address {}142143impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {144 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {145 T::solidity_name(writer, tc)?;146 write!(writer, "[]")147 }148 fn is_simple() -> bool {149 false150 }151 fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {152 write!(writer, "[]")153 }154}155156pub trait SolidityTupleType {157 fn names(tc: &TypeCollector) -> Vec<String>;158 fn len() -> usize;159}160161macro_rules! count {162 () => (0usize);163 ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));164}165166macro_rules! impl_tuples {167 ($($ident:ident)+) => {168 impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}169 impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {170 fn names(tc: &TypeCollector) -> Vec<string> {171 let mut collected = Vec::with_capacity(Self::len());172 $({173 let mut out = string::new();174 $ident::solidity_name(&mut out, tc).expect("no fmt error");175 collected.push(out);176 })*;177 collected178 }179180 fn len() -> usize {181 count!($($ident)*)182 }183 }184 impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {185 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {186 write!(writer, "{}", tc.collect_tuple::<Self>())187 }188 fn is_simple() -> bool {189 false190 }191 #[allow(unused_assignments)]192 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {193 write!(writer, "{}(", tc.collect_tuple::<Self>())?;194 let mut first = true;195 $(196 if !first {197 write!(writer, ",")?;198 } else {199 first = false;200 }201 <$ident>::solidity_default(writer, tc)?;202 )*203 write!(writer, ")")204 }205 }206 };207}208209impl_tuples! {A}210impl_tuples! {A B}211impl_tuples! {A B C}212impl_tuples! {A B C D}213impl_tuples! {A B C D E}214impl_tuples! {A B C D E F}215impl_tuples! {A B C D E F G}216impl_tuples! {A B C D E F G H}217impl_tuples! {A B C D E F G H I}218impl_tuples! {A B C D E F G H I J}219220pub trait SolidityArguments {221 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;222 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;223 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;224 fn is_empty(&self) -> bool {225 self.len() == 0226 }227 fn len(&self) -> usize;228}229230#[derive(Default)]231pub struct UnnamedArgument<T>(PhantomData<*const T>);232233impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {234 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {235 if !T::is_void() {236 T::solidity_name(writer, tc)?;237 if !T::is_simple() {238 write!(writer, " memory")?;239 }240 Ok(())241 } else {242 Ok(())243 }244 }245 fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {246 Ok(())247 }248 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {249 T::solidity_default(writer, tc)250 }251 fn len(&self) -> usize {252 if T::is_void() {253 0254 } else {255 1256 }257 }258}259260pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);261262impl<T> NamedArgument<T> {263 pub fn new(name: &'static str) -> Self {264 Self(name, Default::default())265 }266}267268impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {269 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {270 if !T::is_void() {271 T::solidity_name(writer, tc)?;272 if !T::is_simple() {273 write!(writer, " memory")?;274 }275 write!(writer, " {}", self.0)276 } else {277 Ok(())278 }279 }280 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {281 writeln!(writer, "\t\t{};", self.0)282 }283 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {284 T::solidity_default(writer, tc)285 }286 fn len(&self) -> usize {287 if T::is_void() {288 0289 } else {290 1291 }292 }293}294295pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);296297impl<T> SolidityEventArgument<T> {298 pub fn new(indexed: bool, name: &'static str) -> Self {299 Self(indexed, name, Default::default())300 }301}302303impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {304 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {305 if !T::is_void() {306 T::solidity_name(writer, tc)?;307 if self.0 {308 write!(writer, " indexed")?;309 }310 write!(writer, " {}", self.1)311 } else {312 Ok(())313 }314 }315 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {316 writeln!(writer, "\t\t{};", self.1)317 }318 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {319 T::solidity_default(writer, tc)320 }321 fn len(&self) -> usize {322 if T::is_void() {323 0324 } else {325 1326 }327 }328}329330impl SolidityArguments for () {331 fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {332 Ok(())333 }334 fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {335 Ok(())336 }337 fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {338 Ok(())339 }340 fn len(&self) -> usize {341 0342 }343}344345#[impl_for_tuples(1, 12)]346impl SolidityArguments for Tuple {347 for_tuples!( where #( Tuple: SolidityArguments ),* );348349 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {350 let mut first = true;351 for_tuples!( #(352 if !Tuple.is_empty() {353 if !first {354 write!(writer, ", ")?;355 }356 first = false;357 Tuple.solidity_name(writer, tc)?;358 }359 )* );360 Ok(())361 }362 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {363 for_tuples!( #(364 Tuple.solidity_get(writer)?;365 )* );366 Ok(())367 }368 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {369 if self.is_empty() {370 Ok(())371 } else if self.len() == 1 {372 for_tuples!( #(373 Tuple.solidity_default(writer, tc)?;374 )* );375 Ok(())376 } else {377 write!(writer, "(")?;378 let mut first = true;379 for_tuples!( #(380 if !Tuple.is_empty() {381 if !first {382 write!(writer, ", ")?;383 }384 first = false;385 Tuple.solidity_default(writer, tc)?;386 }387 )* );388 write!(writer, ")")?;389 Ok(())390 }391 }392 fn len(&self) -> usize {393 for_tuples!( #( Tuple.len() )+* )394 }395}396397pub trait SolidityFunctions {398 fn solidity_name(399 &self,400 is_impl: bool,401 writer: &mut impl fmt::Write,402 tc: &TypeCollector,403 ) -> fmt::Result;404}405406pub enum SolidityMutability {407 Pure,408 View,409 Mutable,410}411pub struct SolidityFunction<A, R> {412 pub docs: &'static [&'static str],413 pub selector_str: &'static str,414 pub selector: u32,415 pub name: &'static str,416 pub args: A,417 pub result: R,418 pub mutability: SolidityMutability,419}420impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {421 fn solidity_name(422 &self,423 is_impl: bool,424 writer: &mut impl fmt::Write,425 tc: &TypeCollector,426 ) -> fmt::Result {427 for doc in self.docs {428 writeln!(writer, "\t///{}", doc)?;429 }430 if !self.docs.is_empty() {431 writeln!(writer, "\t///")?;432 }433 writeln!(writer, "\t/// @dev EVM selector for this function is: 0x{:0>8x},", self.selector)?;434 writeln!(writer, "\t/// or in textual repr: {}", self.selector_str)?;435 write!(writer, "\tfunction {}(", self.name)?;436 self.args.solidity_name(writer, tc)?;437 write!(writer, ")")?;438 if is_impl {439 write!(writer, " public")?;440 } else {441 write!(writer, " external")?;442 }443 match &self.mutability {444 SolidityMutability::Pure => write!(writer, " pure")?,445 SolidityMutability::View => write!(writer, " view")?,446 SolidityMutability::Mutable => {}447 }448 if !self.result.is_empty() {449 write!(writer, " returns (")?;450 self.result.solidity_name(writer, tc)?;451 write!(writer, ")")?;452 }453 if is_impl {454 writeln!(writer, " {{")?;455 writeln!(writer, "\t\trequire(false, stub_error);")?;456 self.args.solidity_get(writer)?;457 match &self.mutability {458 SolidityMutability::Pure => {}459 SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,460 SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,461 }462 if !self.result.is_empty() {463 write!(writer, "\t\treturn ")?;464 self.result.solidity_default(writer, tc)?;465 writeln!(writer, ";")?;466 }467 writeln!(writer, "\t}}")?;468 } else {469 writeln!(writer, ";")?;470 }471 Ok(())472 }473}474475#[impl_for_tuples(0, 24)]476impl SolidityFunctions for Tuple {477 for_tuples!( where #( Tuple: SolidityFunctions ),* );478479 fn solidity_name(480 &self,481 is_impl: bool,482 writer: &mut impl fmt::Write,483 tc: &TypeCollector,484 ) -> fmt::Result {485 let mut first = false;486 for_tuples!( #(487 Tuple.solidity_name(is_impl, writer, tc)?;488 )* );489 Ok(())490 }491}492493pub struct SolidityInterface<F: SolidityFunctions> {494 pub selector: bytes4,495 pub name: &'static str,496 pub is: &'static [&'static str],497 pub functions: F,498}499500impl<F: SolidityFunctions> SolidityInterface<F> {501 pub fn format(502 &self,503 is_impl: bool,504 out: &mut impl fmt::Write,505 tc: &TypeCollector,506 ) -> fmt::Result {507 const ZERO_BYTES: [u8; 4] = [0; 4];508 if self.selector != ZERO_BYTES {509 writeln!(510 out,511 "/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",512 u32::from_be_bytes(self.selector)513 )?;514 }515 if is_impl {516 write!(out, "contract ")?;517 } else {518 write!(out, "interface ")?;519 }520 write!(out, "{}", self.name)?;521 if !self.is.is_empty() {522 write!(out, " is")?;523 for (i, n) in self.is.iter().enumerate() {524 if i != 0 {525 write!(out, ",")?;526 }527 write!(out, " {}", n)?;528 }529 }530 writeln!(out, " {{")?;531 self.functions.solidity_name(is_impl, out, tc)?;532 writeln!(out, "}}")?;533 Ok(())534 }535}536537pub struct SolidityEvent<A> {538 pub name: &'static str,539 pub args: A,540}541542impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {543 fn solidity_name(544 &self,545 _is_impl: bool,546 writer: &mut impl fmt::Write,547 tc: &TypeCollector,548 ) -> fmt::Result {549 write!(writer, "\tevent {}(", self.name)?;550 self.args.solidity_name(writer, tc)?;551 writeln!(writer, ");")552 }553}