difftreelog
refactor solidity module
in: master
4 files changed
crates/evm-coder/src/solidity.rsdiffbeforeafterboth--- a/crates/evm-coder/src/solidity.rs
+++ /dev/null
@@ -1,646 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-//! Implementation detail of [`crate::solidity_interface`] macro code-generation.
-//! You should not rely on any public item from this module, as it is only intended to be used
-//! by procedural macro, API and output format may be changed at any time.
-//!
-//! Purpose of this module is to receive solidity contract definition in module-specified
-//! format, and then output string, representing interface of this contract in solidity language
-
-#[cfg(not(feature = "std"))]
-use alloc::{string::String, vec::Vec, collections::BTreeMap, format};
-#[cfg(feature = "std")]
-use std::collections::BTreeMap;
-use core::{
- fmt::{self, Write},
- marker::PhantomData,
- cell::{Cell, RefCell},
- cmp::Reverse,
-};
-use impl_trait_for_tuples::impl_for_tuples;
-use crate::{types::*, custom_signature::SignatureUnit};
-
-#[derive(Default)]
-pub struct TypeCollector {
- /// Code => id
- /// id ordering is required to perform topo-sort on the resulting data
- structs: RefCell<BTreeMap<string, usize>>,
- anonymous: RefCell<BTreeMap<Vec<string>, usize>>,
- id: Cell<usize>,
-}
-impl TypeCollector {
- pub fn new() -> Self {
- Self::default()
- }
- pub fn collect(&self, item: string) {
- let id = self.next_id();
- self.structs.borrow_mut().insert(item, id);
- }
- 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, "/// @dev 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 collect_struct<T: StructCollect>(&self) -> String {
- self.collect(<T as StructCollect>::declaration());
- <T as StructCollect>::name()
- }
- pub fn finish(self) -> Vec<string> {
- let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();
- data.sort_by_key(|(_, id)| Reverse(*id));
- data.into_iter().map(|(code, _)| code).collect()
- }
-}
-
-pub trait StructCollect: 'static {
- /// Structure name.
- fn name() -> String;
- /// Structure declaration.
- fn declaration() -> String;
-}
-
-pub trait SolidityTypeName: 'static {
- fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
- /// "simple" types are stored inline, no `memory` modifier should be used in solidity
- fn is_simple() -> bool;
- fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
- /// Specialization
- fn is_void() -> bool {
- false
- }
-}
-macro_rules! solidity_type_name {
- ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {
- $(
- impl SolidityTypeName for $ty {
- fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {
- write!(writer, $name)
- }
- fn is_simple() -> bool {
- $simple
- }
- fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {
- write!(writer, $default)
- }
- }
- )*
- };
-}
-
-solidity_type_name! {
- uint8 => "uint8" true = "0",
- uint32 => "uint32" true = "0",
- uint64 => "uint64" true = "0",
- uint128 => "uint128" true = "0",
- uint256 => "uint256" true = "0",
- bytes4 => "bytes4" true = "bytes4(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, _tc: &TypeCollector) -> fmt::Result {
- Ok(())
- }
- fn is_simple() -> bool {
- true
- }
- fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
- Ok(())
- }
- fn is_void() -> bool {
- true
- }
-}
-
-pub mod sealed {
- /// Not every type should be directly placed in vec.
- /// Vec encoding is not memory efficient, as every item will be padded
- /// to 32 bytes.
- /// Instead you should use specialized types (`bytes` in case of `Vec<u8>`)
- pub trait CanBePlacedInVec {}
-}
-
-impl sealed::CanBePlacedInVec for uint256 {}
-impl sealed::CanBePlacedInVec for string {}
-impl sealed::CanBePlacedInVec for address {}
-impl sealed::CanBePlacedInVec for Property {}
-
-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, "new ")?;
- T::solidity_name(writer, tc)?;
- write!(writer, "[](0)")
- }
-}
-
-impl StructCollect for Property {
- fn name() -> String {
- "Property".into()
- }
-
- fn declaration() -> String {
- let mut str = String::new();
- writeln!(str, "/// @dev Property struct").unwrap();
- writeln!(str, "struct {} {{", Self::name()).unwrap();
- writeln!(str, "\tstring key;").unwrap();
- writeln!(str, "\tbytes value;").unwrap();
- writeln!(str, "}}").unwrap();
- str
- }
-}
-
-impl SolidityTypeName for Property {
- fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- write!(writer, "{}", tc.collect_struct::<Self>())
- }
-
- fn is_simple() -> bool {
- false
- }
-
- fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- write!(writer, "{}(", tc.collect_struct::<Self>())?;
- address::solidity_default(writer, tc)?;
- write!(writer, ",")?;
- uint256::solidity_default(writer, tc)?;
- write!(writer, ")")
- }
-}
-
-impl SolidityTupleType for Property {
- fn names(tc: &TypeCollector) -> Vec<string> {
- let mut collected = Vec::with_capacity(Self::len());
- {
- let mut out = string::new();
- string::solidity_name(&mut out, tc).expect("no fmt error");
- collected.push(out);
- }
- {
- let mut out = string::new();
- bytes::solidity_name(&mut out, tc).expect("no fmt error");
- collected.push(out);
- }
- collected
- }
-
- fn len() -> usize {
- 2
- }
-}
-
-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
- }
- #[allow(unused_assignments)]
- fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- write!(writer, "{}(", tc.collect_tuple::<Self>())?;
- let mut first = true;
- $(
- if !first {
- write!(writer, ",")?;
- } else {
- first = false;
- }
- <$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, tc: &TypeCollector) -> fmt::Result;
- fn solidity_get(&self, prefix: &str, 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
- }
- fn len(&self) -> usize;
-}
-
-#[derive(Default)]
-pub struct UnnamedArgument<T>(PhantomData<*const T>);
-
-impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {
- fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- if !T::is_void() {
- T::solidity_name(writer, tc)?;
- if !T::is_simple() {
- write!(writer, " memory")?;
- }
- Ok(())
- } else {
- Ok(())
- }
- }
- fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {
- Ok(())
- }
- 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() {
- 0
- } else {
- 1
- }
- }
-}
-
-pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);
-
-impl<T> NamedArgument<T> {
- pub fn new(name: &'static str) -> Self {
- Self(name, Default::default())
- }
-}
-
-impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {
- fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- if !T::is_void() {
- T::solidity_name(writer, tc)?;
- if !T::is_simple() {
- write!(writer, " memory")?;
- }
- write!(writer, " {}", self.0)
- } else {
- Ok(())
- }
- }
- fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {
- writeln!(writer, "\t{prefix}\t{};", self.0)
- }
- 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() {
- 0
- } else {
- 1
- }
- }
-}
-
-pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);
-
-impl<T> SolidityEventArgument<T> {
- pub fn new(indexed: bool, name: &'static str) -> Self {
- Self(indexed, name, Default::default())
- }
-}
-
-impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {
- fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- if !T::is_void() {
- T::solidity_name(writer, tc)?;
- if self.0 {
- write!(writer, " indexed")?;
- }
- write!(writer, " {}", self.1)
- } else {
- Ok(())
- }
- }
- fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {
- writeln!(writer, "\t{prefix}\t{};", self.1)
- }
- 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() {
- 0
- } else {
- 1
- }
- }
-}
-
-impl SolidityArguments for () {
- fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
- Ok(())
- }
- fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {
- Ok(())
- }
- fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
- Ok(())
- }
- fn len(&self) -> usize {
- 0
- }
-}
-
-#[impl_for_tuples(1, 12)]
-impl SolidityArguments for Tuple {
- for_tuples!( where #( Tuple: SolidityArguments ),* );
-
- fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- let mut first = true;
- for_tuples!( #(
- if !Tuple.is_empty() {
- if !first {
- write!(writer, ", ")?;
- }
- first = false;
- Tuple.solidity_name(writer, tc)?;
- }
- )* );
- Ok(())
- }
- fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {
- for_tuples!( #(
- Tuple.solidity_get(prefix, writer)?;
- )* );
- Ok(())
- }
- 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, tc)?;
- )* );
- Ok(())
- } else {
- write!(writer, "(")?;
- let mut first = true;
- for_tuples!( #(
- if !Tuple.is_empty() {
- if !first {
- write!(writer, ", ")?;
- }
- first = false;
- Tuple.solidity_default(writer, tc)?;
- }
- )* );
- write!(writer, ")")?;
- Ok(())
- }
- }
- fn len(&self) -> usize {
- for_tuples!( #( Tuple.len() )+* )
- }
-}
-
-pub trait SolidityFunctions {
- fn solidity_name(
- &self,
- is_impl: bool,
- writer: &mut impl fmt::Write,
- tc: &TypeCollector,
- ) -> fmt::Result;
-}
-
-pub enum SolidityMutability {
- Pure,
- View,
- Mutable,
-}
-pub struct SolidityFunction<A, R> {
- pub docs: &'static [&'static str],
- pub selector: u32,
- pub hide: bool,
- pub custom_signature: SignatureUnit,
- pub name: &'static str,
- pub args: A,
- pub result: R,
- pub mutability: SolidityMutability,
- pub is_payable: bool,
-}
-impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {
- fn solidity_name(
- &self,
- is_impl: bool,
- writer: &mut impl fmt::Write,
- tc: &TypeCollector,
- ) -> fmt::Result {
- let hide_comment = self.hide.then(|| "// ").unwrap_or("");
- for doc in self.docs {
- writeln!(writer, "\t{hide_comment}///{}", doc)?;
- }
- writeln!(
- writer,
- "\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",
- self.selector
- )?;
- writeln!(
- writer,
- "\t{hide_comment}/// or in textual repr: {}",
- self.custom_signature.as_str().expect("bad utf-8")
- )?;
- write!(writer, "\t{hide_comment}function {}(", self.name)?;
- self.args.solidity_name(writer, tc)?;
- write!(writer, ")")?;
- if is_impl {
- write!(writer, " public")?;
- } else {
- write!(writer, " external")?;
- }
- match &self.mutability {
- SolidityMutability::Pure => write!(writer, " pure")?,
- SolidityMutability::View => write!(writer, " view")?,
- SolidityMutability::Mutable => {}
- }
- if self.is_payable {
- write!(writer, " payable")?;
- }
- if !self.result.is_empty() {
- write!(writer, " returns (")?;
- self.result.solidity_name(writer, tc)?;
- write!(writer, ")")?;
- }
- if is_impl {
- writeln!(writer, " {{")?;
- writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;
- self.args.solidity_get(hide_comment, writer)?;
- match &self.mutability {
- SolidityMutability::Pure => {}
- SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,
- SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,
- }
- if !self.result.is_empty() {
- write!(writer, "\t{hide_comment}\treturn ")?;
- self.result.solidity_default(writer, tc)?;
- writeln!(writer, ";")?;
- }
- writeln!(writer, "\t{hide_comment}}}")?;
- } else {
- writeln!(writer, ";")?;
- }
- if self.hide {
- writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;
- }
- Ok(())
- }
-}
-
-#[impl_for_tuples(0, 48)]
-impl SolidityFunctions for Tuple {
- for_tuples!( where #( Tuple: SolidityFunctions ),* );
-
- 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, tc)?;
- )* );
- Ok(())
- }
-}
-
-pub struct SolidityInterface<F: SolidityFunctions> {
- pub docs: &'static [&'static str],
- pub selector: bytes4,
- pub name: &'static str,
- pub is: &'static [&'static str],
- pub functions: F,
-}
-
-impl<F: SolidityFunctions> SolidityInterface<F> {
- pub fn format(
- &self,
- is_impl: bool,
- out: &mut impl fmt::Write,
- tc: &TypeCollector,
- ) -> fmt::Result {
- const ZERO_BYTES: [u8; 4] = [0; 4];
- for doc in self.docs {
- writeln!(out, "///{}", doc)?;
- }
- if self.selector != ZERO_BYTES {
- writeln!(
- out,
- "/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",
- u32::from_be_bytes(self.selector)
- )?;
- }
- if is_impl {
- write!(out, "contract ")?;
- } else {
- write!(out, "interface ")?;
- }
- write!(out, "{}", self.name)?;
- if !self.is.is_empty() {
- write!(out, " is")?;
- for (i, n) in self.is.iter().enumerate() {
- if i != 0 {
- write!(out, ",")?;
- }
- write!(out, " {}", n)?;
- }
- }
- writeln!(out, " {{")?;
- self.functions.solidity_name(is_impl, out, tc)?;
- writeln!(out, "}}")?;
- Ok(())
- }
-}
-
-pub struct SolidityEvent<A> {
- pub name: &'static str,
- pub args: A,
-}
-
-impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {
- fn solidity_name(
- &self,
- _is_impl: bool,
- writer: &mut impl fmt::Write,
- tc: &TypeCollector,
- ) -> fmt::Result {
- write!(writer, "\tevent {}(", self.name)?;
- self.args.solidity_name(writer, tc)?;
- writeln!(writer, ");")
- }
-}
crates/evm-coder/src/solidity/impls.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/src/solidity/impls.rs
@@ -0,0 +1,183 @@
+use super::{TypeCollector, SolidityTypeName, SolidityTupleType, sealed};
+use crate::types::*;
+use core::fmt;
+
+impl sealed::CanBePlacedInVec for uint256 {}
+impl sealed::CanBePlacedInVec for string {}
+impl sealed::CanBePlacedInVec for address {}
+
+macro_rules! solidity_type_name {
+ ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {
+ $(
+ impl SolidityTypeName for $ty {
+ fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {
+ write!(writer, $name)
+ }
+ fn is_simple() -> bool {
+ $simple
+ }
+ fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {
+ write!(writer, $default)
+ }
+ }
+ )*
+ };
+}
+
+solidity_type_name! {
+ uint8 => "uint8" true = "0",
+ uint32 => "uint32" true = "0",
+ uint64 => "uint64" true = "0",
+ uint128 => "uint128" true = "0",
+ uint256 => "uint256" true = "0",
+ bytes4 => "bytes4" true = "bytes4(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, _tc: &TypeCollector) -> fmt::Result {
+ Ok(())
+ }
+ fn is_simple() -> bool {
+ true
+ }
+ fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
+ Ok(())
+ }
+ fn is_void() -> bool {
+ true
+ }
+}
+
+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, "new ")?;
+ T::solidity_name(writer, tc)?;
+ write!(writer, "[](0)")
+ }
+}
+
+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
+ }
+ #[allow(unused_assignments)]
+ fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ write!(writer, "{}(", tc.collect_tuple::<Self>())?;
+ let mut first = true;
+ $(
+ if !first {
+ write!(writer, ",")?;
+ } else {
+ first = false;
+ }
+ <$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}
+
+impl sealed::CanBePlacedInVec for Property {}
+impl StructCollect for Property {
+ fn name() -> String {
+ "Property".into()
+ }
+
+ fn declaration() -> String {
+ let mut str = String::new();
+ writeln!(str, "/// @dev Property struct").unwrap();
+ writeln!(str, "struct {} {{", Self::name()).unwrap();
+ writeln!(str, "\tstring key;").unwrap();
+ writeln!(str, "\tbytes value;").unwrap();
+ writeln!(str, "}}").unwrap();
+ str
+ }
+}
+
+impl SolidityTypeName for Property {
+ fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ write!(writer, "{}", tc.collect_struct::<Self>())
+ }
+
+ fn is_simple() -> bool {
+ false
+ }
+
+ fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ write!(writer, "{}(", tc.collect_struct::<Self>())?;
+ address::solidity_default(writer, tc)?;
+ write!(writer, ",")?;
+ uint256::solidity_default(writer, tc)?;
+ write!(writer, ")")
+ }
+}
+
+impl SolidityTupleType for Property {
+ fn names(tc: &TypeCollector) -> Vec<string> {
+ let mut collected = Vec::with_capacity(Self::len());
+ {
+ let mut out = string::new();
+ string::solidity_name(&mut out, tc).expect("no fmt error");
+ collected.push(out);
+ }
+ {
+ let mut out = string::new();
+ bytes::solidity_name(&mut out, tc).expect("no fmt error");
+ collected.push(out);
+ }
+ collected
+ }
+
+ fn len() -> usize {
+ 2
+ }
+}
crates/evm-coder/src/solidity/mod.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 language2324mod traits;25pub use traits::*;26mod impls;2728#[cfg(not(feature = "std"))]29use alloc::{string::String, vec::Vec, collections::BTreeMap, format};30#[cfg(feature = "std")]31use std::collections::BTreeMap;32use core::{33 fmt::{self, Write},34 marker::PhantomData,35 cell::{Cell, RefCell},36 cmp::Reverse,37};38use impl_trait_for_tuples::impl_for_tuples;39use crate::{types::*, custom_signature::SignatureUnit};4041#[derive(Default)]42pub struct TypeCollector {43 /// Code => id44 /// id ordering is required to perform topo-sort on the resulting data45 structs: RefCell<BTreeMap<string, usize>>,46 anonymous: RefCell<BTreeMap<Vec<string>, usize>>,47 id: Cell<usize>,48}49impl TypeCollector {50 pub fn new() -> Self {51 Self::default()52 }53 pub fn collect(&self, item: string) {54 let id = self.next_id();55 self.structs.borrow_mut().insert(item, id);56 }57 pub fn next_id(&self) -> usize {58 let v = self.id.get();59 self.id.set(v + 1);60 v61 }62 pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {63 let names = T::names(self);64 if let Some(id) = self.anonymous.borrow().get(&names).cloned() {65 return format!("Tuple{}", id);66 }67 let id = self.next_id();68 let mut str = String::new();69 writeln!(str, "/// @dev anonymous struct").unwrap();70 writeln!(str, "struct Tuple{} {{", id).unwrap();71 for (i, name) in names.iter().enumerate() {72 writeln!(str, "\t{} field_{};", name, i).unwrap();73 }74 writeln!(str, "}}").unwrap();75 self.collect(str);76 self.anonymous.borrow_mut().insert(names, id);77 format!("Tuple{}", id)78 }79 pub fn collect_struct<T: StructCollect>(&self) -> String {80 self.collect(<T as StructCollect>::declaration());81 <T as StructCollect>::name()82 }83 pub fn finish(self) -> Vec<string> {84 let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();85 data.sort_by_key(|(_, id)| Reverse(*id));86 data.into_iter().map(|(code, _)| code).collect()87 }88}8990#[derive(Default)]91pub struct UnnamedArgument<T>(PhantomData<*const T>);9293impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {94 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {95 if !T::is_void() {96 T::solidity_name(writer, tc)?;97 if !T::is_simple() {98 write!(writer, " memory")?;99 }100 Ok(())101 } else {102 Ok(())103 }104 }105 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {106 Ok(())107 }108 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {109 T::solidity_default(writer, tc)110 }111 fn len(&self) -> usize {112 if T::is_void() {113 0114 } else {115 1116 }117 }118}119120pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);121122impl<T> NamedArgument<T> {123 pub fn new(name: &'static str) -> Self {124 Self(name, Default::default())125 }126}127128impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {129 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {130 if !T::is_void() {131 T::solidity_name(writer, tc)?;132 if !T::is_simple() {133 write!(writer, " memory")?;134 }135 write!(writer, " {}", self.0)136 } else {137 Ok(())138 }139 }140 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {141 writeln!(writer, "\t{prefix}\t{};", self.0)142 }143 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {144 T::solidity_default(writer, tc)145 }146 fn len(&self) -> usize {147 if T::is_void() {148 0149 } else {150 1151 }152 }153}154155pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);156157impl<T> SolidityEventArgument<T> {158 pub fn new(indexed: bool, name: &'static str) -> Self {159 Self(indexed, name, Default::default())160 }161}162163impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {164 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {165 if !T::is_void() {166 T::solidity_name(writer, tc)?;167 if self.0 {168 write!(writer, " indexed")?;169 }170 write!(writer, " {}", self.1)171 } else {172 Ok(())173 }174 }175 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {176 writeln!(writer, "\t{prefix}\t{};", self.1)177 }178 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {179 T::solidity_default(writer, tc)180 }181 fn len(&self) -> usize {182 if T::is_void() {183 0184 } else {185 1186 }187 }188}189190impl SolidityArguments for () {191 fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {192 Ok(())193 }194 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {195 Ok(())196 }197 fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {198 Ok(())199 }200 fn len(&self) -> usize {201 0202 }203}204205#[impl_for_tuples(1, 12)]206impl SolidityArguments for Tuple {207 for_tuples!( where #( Tuple: SolidityArguments ),* );208209 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {210 let mut first = true;211 for_tuples!( #(212 if !Tuple.is_empty() {213 if !first {214 write!(writer, ", ")?;215 }216 first = false;217 Tuple.solidity_name(writer, tc)?;218 }219 )* );220 Ok(())221 }222 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {223 for_tuples!( #(224 Tuple.solidity_get(prefix, writer)?;225 )* );226 Ok(())227 }228 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {229 if self.is_empty() {230 Ok(())231 } else if self.len() == 1 {232 for_tuples!( #(233 Tuple.solidity_default(writer, tc)?;234 )* );235 Ok(())236 } else {237 write!(writer, "(")?;238 let mut first = true;239 for_tuples!( #(240 if !Tuple.is_empty() {241 if !first {242 write!(writer, ", ")?;243 }244 first = false;245 Tuple.solidity_default(writer, tc)?;246 }247 )* );248 write!(writer, ")")?;249 Ok(())250 }251 }252 fn len(&self) -> usize {253 for_tuples!( #( Tuple.len() )+* )254 }255}256257pub enum SolidityMutability {258 Pure,259 View,260 Mutable,261}262pub struct SolidityFunction<A, R> {263 pub docs: &'static [&'static str],264 pub selector: u32,265 pub hide: bool,266 pub custom_signature: SignatureUnit,267 pub name: &'static str,268 pub args: A,269 pub result: R,270 pub mutability: SolidityMutability,271 pub is_payable: bool,272}273impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {274 fn solidity_name(275 &self,276 is_impl: bool,277 writer: &mut impl fmt::Write,278 tc: &TypeCollector,279 ) -> fmt::Result {280 let hide_comment = self.hide.then(|| "// ").unwrap_or("");281 for doc in self.docs {282 writeln!(writer, "\t{hide_comment}///{}", doc)?;283 }284 writeln!(285 writer,286 "\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",287 self.selector288 )?;289 writeln!(290 writer,291 "\t{hide_comment}/// or in textual repr: {}",292 self.custom_signature.as_str().expect("bad utf-8")293 )?;294 write!(writer, "\t{hide_comment}function {}(", self.name)?;295 self.args.solidity_name(writer, tc)?;296 write!(writer, ")")?;297 if is_impl {298 write!(writer, " public")?;299 } else {300 write!(writer, " external")?;301 }302 match &self.mutability {303 SolidityMutability::Pure => write!(writer, " pure")?,304 SolidityMutability::View => write!(writer, " view")?,305 SolidityMutability::Mutable => {}306 }307 if self.is_payable {308 write!(writer, " payable")?;309 }310 if !self.result.is_empty() {311 write!(writer, " returns (")?;312 self.result.solidity_name(writer, tc)?;313 write!(writer, ")")?;314 }315 if is_impl {316 writeln!(writer, " {{")?;317 writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;318 self.args.solidity_get(hide_comment, writer)?;319 match &self.mutability {320 SolidityMutability::Pure => {}321 SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,322 SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,323 }324 if !self.result.is_empty() {325 write!(writer, "\t{hide_comment}\treturn ")?;326 self.result.solidity_default(writer, tc)?;327 writeln!(writer, ";")?;328 }329 writeln!(writer, "\t{hide_comment}}}")?;330 } else {331 writeln!(writer, ";")?;332 }333 if self.hide {334 writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;335 }336 Ok(())337 }338}339340#[impl_for_tuples(0, 48)]341impl SolidityFunctions for Tuple {342 for_tuples!( where #( Tuple: SolidityFunctions ),* );343344 fn solidity_name(345 &self,346 is_impl: bool,347 writer: &mut impl fmt::Write,348 tc: &TypeCollector,349 ) -> fmt::Result {350 let mut first = false;351 for_tuples!( #(352 Tuple.solidity_name(is_impl, writer, tc)?;353 )* );354 Ok(())355 }356}357358pub struct SolidityInterface<F: SolidityFunctions> {359 pub docs: &'static [&'static str],360 pub selector: bytes4,361 pub name: &'static str,362 pub is: &'static [&'static str],363 pub functions: F,364}365366impl<F: SolidityFunctions> SolidityInterface<F> {367 pub fn format(368 &self,369 is_impl: bool,370 out: &mut impl fmt::Write,371 tc: &TypeCollector,372 ) -> fmt::Result {373 const ZERO_BYTES: [u8; 4] = [0; 4];374 for doc in self.docs {375 writeln!(out, "///{}", doc)?;376 }377 if self.selector != ZERO_BYTES {378 writeln!(379 out,380 "/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",381 u32::from_be_bytes(self.selector)382 )?;383 }384 if is_impl {385 write!(out, "contract ")?;386 } else {387 write!(out, "interface ")?;388 }389 write!(out, "{}", self.name)?;390 if !self.is.is_empty() {391 write!(out, " is")?;392 for (i, n) in self.is.iter().enumerate() {393 if i != 0 {394 write!(out, ",")?;395 }396 write!(out, " {}", n)?;397 }398 }399 writeln!(out, " {{")?;400 self.functions.solidity_name(is_impl, out, tc)?;401 writeln!(out, "}}")?;402 Ok(())403 }404}405406pub struct SolidityEvent<A> {407 pub name: &'static str,408 pub args: A,409}410411impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {412 fn solidity_name(413 &self,414 _is_impl: bool,415 writer: &mut impl fmt::Write,416 tc: &TypeCollector,417 ) -> fmt::Result {418 write!(writer, "\tevent {}(", self.name)?;419 self.args.solidity_name(writer, tc)?;420 writeln!(writer, ");")421 }422}crates/evm-coder/src/solidity/traits.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/src/solidity/traits.rs
@@ -0,0 +1,52 @@
+use super::TypeCollector;
+use core::fmt;
+
+pub mod sealed {
+ /// Not every type should be directly placed in vec.
+ /// Vec encoding is not memory efficient, as every item will be padded
+ /// to 32 bytes.
+ /// Instead you should use specialized types (`bytes` in case of `Vec<u8>`)
+ pub trait CanBePlacedInVec {}
+}
+
+pub trait StructCollect: 'static {
+ /// Structure name.
+ fn name() -> String;
+ /// Structure declaration.
+ fn declaration() -> String;
+}
+
+pub trait SolidityTypeName: 'static {
+ fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
+ /// "simple" types are stored inline, no `memory` modifier should be used in solidity
+ fn is_simple() -> bool;
+ fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
+ /// Specialization
+ fn is_void() -> bool {
+ false
+ }
+}
+
+pub trait SolidityTupleType {
+ fn names(tc: &TypeCollector) -> Vec<String>;
+ fn len() -> usize;
+}
+
+pub trait SolidityArguments {
+ fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
+ fn solidity_get(&self, prefix: &str, 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
+ }
+ fn len(&self) -> usize;
+}
+
+pub trait SolidityFunctions {
+ fn solidity_name(
+ &self,
+ is_impl: bool,
+ writer: &mut impl fmt::Write,
+ tc: &TypeCollector,
+ ) -> fmt::Result;
+}