difftreelog
refactor remove AbiType
in: master
3 files changed
crates/evm-coder/procedural/src/lib.rsdiffbeforeafterboth--- a/crates/evm-coder/procedural/src/lib.rs
+++ b/crates/evm-coder/procedural/src/lib.rs
@@ -107,7 +107,7 @@
if path.segments.len() != 1 {
return Err(syn::Error::new(
path.span(),
- "expected path to have only segment",
+ "expected path to have only one segment",
));
}
let last_segment = &path.segments.last().unwrap();
crates/evm-coder/procedural/src/solidity_interface.rsdiffbeforeafterboth--- a/crates/evm-coder/procedural/src/solidity_interface.rs
+++ b/crates/evm-coder/procedural/src/solidity_interface.rs
@@ -329,149 +329,39 @@
}
}
-#[derive(Debug)]
-enum AbiType {
- // type
- Plain(Ident),
- // (type1,type2)
- Tuple(Vec<AbiType>),
- // type[]
- Vec(Box<AbiType>),
- // type[20]
- Array(Box<AbiType>, usize),
+trait AbiType {
+ fn plain(&self) -> syn::Result<&Ident>;
+ fn is_value(&self) -> bool;
+ fn is_caller(&self) -> bool;
+ fn is_special(&self) -> bool;
}
-impl AbiType {
- fn try_from(value: &Type) -> syn::Result<Self> {
- let value = Self::try_maybe_special_from(value)?;
- if value.is_special() {
- return Err(syn::Error::new(value.span(), "unexpected special type"));
+
+impl AbiType for Type {
+ fn plain(&self) -> syn::Result<&Ident> {
+ let path = parse_path(self)?;
+ let segment = parse_path_segment(path)?;
+ if !segment.arguments.is_empty() {
+ return Err(syn::Error::new(self.span(), "Not plain type"));
}
- Ok(value)
+ Ok(&segment.ident)
}
- fn try_maybe_special_from(value: &Type) -> syn::Result<Self> {
- match value {
- Type::Array(arr) => {
- let wrapped = AbiType::try_from(&arr.elem)?;
- match &arr.len {
- Expr::Lit(l) => match &l.lit {
- Lit::Int(i) => {
- let num = i.base10_parse::<usize>()?;
- Ok(AbiType::Array(Box::new(wrapped), num as usize))
- }
- _ => Err(syn::Error::new(arr.len.span(), "should be int literal")),
- },
- _ => Err(syn::Error::new(arr.len.span(), "should be literal")),
- }
- }
- Type::Path(_) => {
- let path = parse_path(value)?;
- let segment = parse_path_segment(path)?;
- if segment.ident == "Vec" {
- let args = match &segment.arguments {
- PathArguments::AngleBracketed(e) => e,
- _ => {
- return Err(syn::Error::new(
- segment.arguments.span(),
- "missing Vec generic",
- ))
- }
- };
- let args = &args.args;
- if args.len() != 1 {
- return Err(syn::Error::new(
- args.span(),
- "expected only one generic for vec",
- ));
- }
- let arg = args.first().expect("first arg");
- let ty = match arg {
- GenericArgument::Type(ty) => ty,
- _ => {
- return Err(syn::Error::new(
- arg.span(),
- "expected first generic to be type",
- ))
- }
- };
-
- let wrapped = AbiType::try_from(ty)?;
- Ok(Self::Vec(Box::new(wrapped)))
- } else {
- if !segment.arguments.is_empty() {
- return Err(syn::Error::new(
- segment.arguments.span(),
- "unexpected generic arguments for non-vec type",
- ));
- }
- Ok(Self::Plain(segment.ident.clone()))
- }
- }
- Type::Tuple(t) => {
- let mut out = Vec::with_capacity(t.elems.len());
- for el in t.elems.iter() {
- out.push(AbiType::try_from(el)?)
- }
- Ok(Self::Tuple(out))
- }
- _ => Err(syn::Error::new(
- value.span(),
- "unexpected type, only arrays, plain types and tuples are supported",
- )),
+ fn is_value(&self) -> bool {
+ if let Ok(ident) = self.plain() {
+ return ident == "value";
}
+ false
}
- fn is_value(&self) -> bool {
- matches!(self, Self::Plain(v) if v == "value")
- }
+
fn is_caller(&self) -> bool {
- matches!(self, Self::Plain(v) if v == "caller")
+ if let Ok(ident) = self.plain() {
+ return ident == "caller";
+ }
+ false
}
+
fn is_special(&self) -> bool {
self.is_caller() || self.is_value()
- }
- fn selector_ty_buf(&self, buf: &mut String) -> std::fmt::Result {
- match self {
- AbiType::Plain(t) => {
- write!(buf, "{}", t)
- }
- AbiType::Tuple(t) => {
- write!(buf, "(")?;
- for (i, t) in t.iter().enumerate() {
- if i != 0 {
- write!(buf, ",")?;
- }
- t.selector_ty_buf(buf)?;
- }
- write!(buf, ")")
- }
- AbiType::Vec(v) => {
- v.selector_ty_buf(buf)?;
- write!(buf, "[]")
- }
- AbiType::Array(v, len) => {
- v.selector_ty_buf(buf)?;
- write!(buf, "[{}]", len)
- }
- }
- }
- fn selector_ty(&self) -> String {
- let mut out = String::new();
- self.selector_ty_buf(&mut out).expect("no fmt error");
- out
- }
-}
-impl ToTokens for AbiType {
- fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
- match self {
- AbiType::Plain(t) => tokens.extend(quote! {#t}),
- AbiType::Tuple(t) => {
- tokens.extend(quote! {(
- #(#t),*
- )});
- }
- AbiType::Vec(v) => tokens.extend(quote! {Vec<#v>}),
- AbiType::Array(v, l) => tokens.extend(quote! {[#v; #l]}),
- }
}
}
@@ -479,7 +369,7 @@
struct MethodArg {
name: Ident,
camel_name: String,
- ty: AbiType,
+ ty: Type,
}
impl MethodArg {
fn try_from(value: &PatType) -> syn::Result<Self> {
@@ -487,7 +377,7 @@
Ok(Self {
camel_name: cases::camelcase::to_camel_case(&name.to_string()),
name,
- ty: AbiType::try_maybe_special_from(&value.ty)?,
+ ty: value.ty.as_ref().clone(),
})
}
fn is_value(&self) -> bool {
@@ -498,10 +388,6 @@
}
fn is_special(&self) -> bool {
self.ty.is_special()
- }
- fn selector_ty(&self) -> String {
- assert!(!self.is_special());
- self.ty.selector_ty()
}
fn expand_call_def(&self) -> proc_macro2::TokenStream {
@@ -578,8 +464,6 @@
camel_name: String,
pascal_name: Ident,
screaming_name: Ident,
- selector_str: String,
- selector: u32,
hide: bool,
args: Vec<MethodArg>,
has_normal_args: bool,
@@ -670,27 +554,14 @@
let camel_name = info
.rename_selector
.unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));
- let mut selector_str = camel_name.clone();
- selector_str.push('(');
- let mut has_normal_args = false;
- for (i, arg) in args.iter().filter(|arg| !arg.is_special()).enumerate() {
- if i != 0 {
- selector_str.push(',');
- }
- write!(selector_str, "{}", arg.selector_ty()).unwrap();
- has_normal_args = true;
- }
+ let has_normal_args = args.iter().filter(|arg| !arg.is_special()).count() != 0;
let has_value_args = args.iter().any(|a| a.is_value());
- selector_str.push(')');
- let selector = fn_selector_str(&selector_str);
Ok(Self {
name: ident.clone(),
camel_name,
pascal_name: snake_ident_to_pascal(ident),
screaming_name: snake_ident_to_screaming(ident),
- selector_str,
- selector,
hide: info.hide,
args,
has_normal_args,
@@ -728,10 +599,8 @@
fn expand_const(&self) -> proc_macro2::TokenStream {
let screaming_name = &self.screaming_name;
let screaming_name_signature = format_ident!("{}_SIGNATURE", &self.screaming_name);
- let selector_str = &self.selector_str;
let custom_signature = self.expand_custom_signature();
quote! {
- #[doc = #selector_str]
const #screaming_name_signature: ::evm_coder::custom_signature::FunctionSignature = #custom_signature;
const #screaming_name: ::evm_coder::types::bytes4 = {
let mut sum = ::evm_coder::sha3_const::Keccak256::new();
@@ -845,31 +714,78 @@
}
}
- fn expand_type(
- ty: &AbiType,
- token_stream: &mut proc_macro2::TokenStream,
- read_signature: bool,
- ) {
+ fn expand_type(ty: &Type, token_stream: &mut proc_macro2::TokenStream, read_signature: bool) {
match ty {
- AbiType::Plain(ref ident) => {
- let plain_token = if read_signature {
- quote! {
- (<#ident>::SIGNATURE)
+ Type::Path(tp) => {
+ if let Some(qself) = &tp.qself {
+ panic!("no receiver expected {:?}", qself.ty.span());
+ }
+ let path = &tp.path;
+ if path.segments.len() != 1 {
+ panic!("expected path to have only one segment {:?}", path.span());
+ }
+ let last_segment = path.segments.last().unwrap();
+
+ if last_segment.ident == "Vec" {
+ let args = match &last_segment.arguments {
+ PathArguments::AngleBracketed(e) => e,
+ _ => {
+ panic!("missing Vec generic {:?}", last_segment.arguments.span());
+ }
+ };
+ let args = &args.args;
+ if args.len() != 1 {
+ panic!("expected only one generic for vec {:?}", args.span());
}
+ let arg = args.first().expect("first arg");
+
+ let ty = match arg {
+ GenericArgument::Type(ty) => ty,
+ _ => {
+ panic!("expected first generic to be type {:?}", arg.span());
+ }
+ };
+
+ let mut vec_token = proc_macro2::TokenStream::new();
+ Self::expand_type(ty, &mut vec_token, false);
+ vec_token = if read_signature {
+ quote! { (<Vec<#vec_token>>::SIGNATURE) }
+ } else {
+ quote! { <Vec<#vec_token>> }
+ };
+ token_stream.extend(vec_token);
} else {
- quote! {
- #ident
+ if !last_segment.arguments.is_empty() {
+ panic!(
+ "unexpected generic arguments for non-vec type {:?}",
+ last_segment.arguments.span()
+ );
}
- };
- token_stream.extend(plain_token);
+ let ident = &last_segment.ident;
+ let plain_token = if read_signature {
+ quote! {
+ (<#ident>::SIGNATURE)
+ }
+ } else {
+ quote! {
+ #ident
+ }
+ };
+
+ token_stream.extend(plain_token);
+ }
}
- AbiType::Tuple(ref tuple_type) => {
+ Type::Tuple(tt) => {
+ // for ty in tt.elems.iter() {
+ // out.push(AbiType::try_from(ty)?)
+ // }
+
let mut tuple_types = proc_macro2::TokenStream::new();
let mut is_first = true;
- for ty in tuple_type {
+ for ty in tt.elems.iter() {
if is_first {
is_first = false
} else {
@@ -885,19 +801,69 @@
token_stream.extend(tuple_types);
}
- AbiType::Vec(ref vec_type) => {
- let mut vec_token = proc_macro2::TokenStream::new();
- Self::expand_type(vec_type.as_ref(), &mut vec_token, false);
- vec_token = if read_signature {
- quote! { (<Vec<#vec_token>>::SIGNATURE) }
- } else {
- quote! { <Vec<#vec_token>> }
- };
- token_stream.extend(vec_token);
- }
+ // Type::Array(arr) => {
+ // let wrapped = AbiType::try_from(&arr.elem)?;
+ // match &arr.len {
+ // Expr::Lit(l) => match &l.lit {
+ // Lit::Int(i) => {
+ // let num = i.base10_parse::<usize>()?;
+ // Ok(AbiType::Array(Box::new(wrapped), num as usize))
+ // }
+ // _ => Err(syn::Error::new(arr.len.span(), "should be int literal")),
+ // },
+ // _ => Err(syn::Error::new(arr.len.span(), "should be literal")),
+ // }
+ // }
+ _ => panic!("Unexpected type {ty:?}"),
+ }
+ // match ty {
+ // AbiType::Plain(ref ident) => {
+ // let plain_token = if read_signature {
+ // quote! {
+ // (<#ident>::SIGNATURE)
+ // }
+ // } else {
+ // quote! {
+ // #ident
+ // }
+ // };
+
+ // token_stream.extend(plain_token);
+ // }
+
+ // AbiType::Tuple(ref tuple_type) => {
+ // let mut tuple_types = proc_macro2::TokenStream::new();
+ // let mut is_first = true;
+
+ // for ty in tuple_type {
+ // if is_first {
+ // is_first = false
+ // } else {
+ // tuple_types.extend(quote!(,));
+ // }
+ // Self::expand_type(ty, &mut tuple_types, false);
+ // }
+ // tuple_types = if read_signature {
+ // quote! { (<(#tuple_types)>::SIGNATURE) }
+ // } else {
+ // quote! { (#tuple_types) }
+ // };
+ // token_stream.extend(tuple_types);
+ // }
+
+ // AbiType::Vec(ref vec_type) => {
+ // let mut vec_token = proc_macro2::TokenStream::new();
+ // Self::expand_type(vec_type.as_ref(), &mut vec_token, false);
+ // vec_token = if read_signature {
+ // quote! { (<Vec<#vec_token>>::SIGNATURE) }
+ // } else {
+ // quote! { <Vec<#vec_token>> }
+ // };
+ // token_stream.extend(vec_token);
+ // }
- AbiType::Array(_, _) => todo!("Array eth signature"),
- };
+ // AbiType::Array(_, _) => todo!("Array eth signature"),
+ // };
}
fn expand_custom_signature(&self) -> proc_macro2::TokenStream {
@@ -947,7 +913,6 @@
.filter(|a| !a.is_special())
.map(MethodArg::expand_solidity_argument);
let docs = &self.docs;
- let selector_str = &self.selector_str;
let screaming_name = &self.screaming_name;
let hide = self.hide;
let custom_signature = self.expand_custom_signature();
@@ -962,7 +927,6 @@
quote! {
SolidityFunction {
docs: &[#(#docs),*],
- selector_str: #selector_str,
hide: #hide,
selector: u32::from_be_bytes(Self::#screaming_name),
custom_signature: #custom_signature,
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::*, custom_signature::FunctionSignature};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 collect_struct<T: StructCollect>(&self) -> String {76 self.collect(<T as StructCollect>::declaration());77 <T as StructCollect>::name()78 }79 pub fn finish(self) -> Vec<string> {80 let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();81 data.sort_by_key(|(_, id)| Reverse(*id));82 data.into_iter().map(|(code, _)| code).collect()83 }84}8586pub trait StructCollect: 'static {87 /// Structure name.88 fn name() -> String;89 /// Structure declaration.90 fn declaration() -> String;91}9293pub trait SolidityTypeName: 'static {94 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;95 /// "simple" types are stored inline, no `memory` modifier should be used in solidity96 fn is_simple() -> bool;97 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;98 /// Specialization99 fn is_void() -> bool {100 false101 }102}103macro_rules! solidity_type_name {104 ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {105 $(106 impl SolidityTypeName for $ty {107 fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {108 write!(writer, $name)109 }110 fn is_simple() -> bool {111 $simple112 }113 fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {114 write!(writer, $default)115 }116 }117 )*118 };119}120121solidity_type_name! {122 uint8 => "uint8" true = "0",123 uint32 => "uint32" true = "0",124 uint64 => "uint64" true = "0",125 uint128 => "uint128" true = "0",126 uint256 => "uint256" true = "0",127 bytes4 => "bytes4" true = "bytes4(0)",128 address => "address" true = "0x0000000000000000000000000000000000000000",129 string => "string" false = "\"\"",130 bytes => "bytes" false = "hex\"\"",131 bool => "bool" true = "false",132}133impl SolidityTypeName for void {134 fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {135 Ok(())136 }137 fn is_simple() -> bool {138 true139 }140 fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {141 Ok(())142 }143 fn is_void() -> bool {144 true145 }146}147148mod sealed {149 /// Not every type should be directly placed in vec.150 /// Vec encoding is not memory efficient, as every item will be padded151 /// to 32 bytes.152 /// Instead you should use specialized types (`bytes` in case of `Vec<u8>`)153 pub trait CanBePlacedInVec {}154}155156impl sealed::CanBePlacedInVec for uint256 {}157impl sealed::CanBePlacedInVec for string {}158impl sealed::CanBePlacedInVec for address {}159impl sealed::CanBePlacedInVec for EthCrossAccount {}160161impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {162 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {163 T::solidity_name(writer, tc)?;164 write!(writer, "[]")165 }166 fn is_simple() -> bool {167 false168 }169 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {170 write!(writer, "new ")?;171 T::solidity_name(writer, tc)?;172 write!(writer, "[](0)")173 }174}175176impl SolidityTupleType for EthCrossAccount {177 fn names(tc: &TypeCollector) -> Vec<string> {178 let mut collected = Vec::with_capacity(Self::len());179 {180 let mut out = string::new();181 address::solidity_name(&mut out, tc).expect("no fmt error");182 collected.push(out);183 }184 {185 let mut out = string::new();186 uint256::solidity_name(&mut out, tc).expect("no fmt error");187 collected.push(out);188 }189 collected190 }191192 fn len() -> usize {193 2194 }195}196impl SolidityTypeName for EthCrossAccount {197 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {198 write!(writer, "{}", tc.collect_struct::<Self>())199 }200201 fn is_simple() -> bool {202 false203 }204205 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {206 write!(writer, "{}(", tc.collect_struct::<Self>())?;207 address::solidity_default(writer, tc)?;208 write!(writer, ",")?;209 uint256::solidity_default(writer, tc)?;210 write!(writer, ")")211 }212}213214impl StructCollect for EthCrossAccount {215 fn name() -> String {216 "EthCrossAccount".into()217 }218219 fn declaration() -> String {220 let mut str = String::new();221 writeln!(str, "/// @dev Cross account struct").unwrap();222 writeln!(str, "struct {} {{", Self::name()).unwrap();223 writeln!(str, "\taddress eth;").unwrap();224 writeln!(str, "\tuint256 sub;").unwrap();225 writeln!(str, "}}").unwrap();226 str227 }228}229230pub trait SolidityTupleType {231 fn names(tc: &TypeCollector) -> Vec<String>;232 fn len() -> usize;233}234235macro_rules! count {236 () => (0usize);237 ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));238}239240macro_rules! impl_tuples {241 ($($ident:ident)+) => {242 impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}243 impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {244 fn names(tc: &TypeCollector) -> Vec<string> {245 let mut collected = Vec::with_capacity(Self::len());246 $({247 let mut out = string::new();248 $ident::solidity_name(&mut out, tc).expect("no fmt error");249 collected.push(out);250 })*;251 collected252 }253254 fn len() -> usize {255 count!($($ident)*)256 }257 }258 impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {259 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {260 write!(writer, "{}", tc.collect_tuple::<Self>())261 }262 fn is_simple() -> bool {263 false264 }265 #[allow(unused_assignments)]266 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {267 write!(writer, "{}(", tc.collect_tuple::<Self>())?;268 let mut first = true;269 $(270 if !first {271 write!(writer, ",")?;272 } else {273 first = false;274 }275 <$ident>::solidity_default(writer, tc)?;276 )*277 write!(writer, ")")278 }279 }280 };281}282283impl_tuples! {A}284impl_tuples! {A B}285impl_tuples! {A B C}286impl_tuples! {A B C D}287impl_tuples! {A B C D E}288impl_tuples! {A B C D E F}289impl_tuples! {A B C D E F G}290impl_tuples! {A B C D E F G H}291impl_tuples! {A B C D E F G H I}292impl_tuples! {A B C D E F G H I J}293294pub trait SolidityArguments {295 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;296 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result;297 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;298 fn is_empty(&self) -> bool {299 self.len() == 0300 }301 fn len(&self) -> usize;302}303304#[derive(Default)]305pub struct UnnamedArgument<T>(PhantomData<*const T>);306307impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {308 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {309 if !T::is_void() {310 T::solidity_name(writer, tc)?;311 if !T::is_simple() {312 write!(writer, " memory")?;313 }314 Ok(())315 } else {316 Ok(())317 }318 }319 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {320 Ok(())321 }322 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {323 T::solidity_default(writer, tc)324 }325 fn len(&self) -> usize {326 if T::is_void() {327 0328 } else {329 1330 }331 }332}333334pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);335336impl<T> NamedArgument<T> {337 pub fn new(name: &'static str) -> Self {338 Self(name, Default::default())339 }340}341342impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {343 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {344 if !T::is_void() {345 T::solidity_name(writer, tc)?;346 if !T::is_simple() {347 write!(writer, " memory")?;348 }349 write!(writer, " {}", self.0)350 } else {351 Ok(())352 }353 }354 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {355 writeln!(writer, "\t{prefix}\t{};", self.0)356 }357 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {358 T::solidity_default(writer, tc)359 }360 fn len(&self) -> usize {361 if T::is_void() {362 0363 } else {364 1365 }366 }367}368369pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);370371impl<T> SolidityEventArgument<T> {372 pub fn new(indexed: bool, name: &'static str) -> Self {373 Self(indexed, name, Default::default())374 }375}376377impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {378 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {379 if !T::is_void() {380 T::solidity_name(writer, tc)?;381 if self.0 {382 write!(writer, " indexed")?;383 }384 write!(writer, " {}", self.1)385 } else {386 Ok(())387 }388 }389 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {390 writeln!(writer, "\t{prefix}\t{};", self.1)391 }392 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {393 T::solidity_default(writer, tc)394 }395 fn len(&self) -> usize {396 if T::is_void() {397 0398 } else {399 1400 }401 }402}403404impl SolidityArguments for () {405 fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {406 Ok(())407 }408 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {409 Ok(())410 }411 fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {412 Ok(())413 }414 fn len(&self) -> usize {415 0416 }417}418419#[impl_for_tuples(1, 12)]420impl SolidityArguments for Tuple {421 for_tuples!( where #( Tuple: SolidityArguments ),* );422423 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {424 let mut first = true;425 for_tuples!( #(426 if !Tuple.is_empty() {427 if !first {428 write!(writer, ", ")?;429 }430 first = false;431 Tuple.solidity_name(writer, tc)?;432 }433 )* );434 Ok(())435 }436 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {437 for_tuples!( #(438 Tuple.solidity_get(prefix, writer)?;439 )* );440 Ok(())441 }442 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {443 if self.is_empty() {444 Ok(())445 } else if self.len() == 1 {446 for_tuples!( #(447 Tuple.solidity_default(writer, tc)?;448 )* );449 Ok(())450 } else {451 write!(writer, "(")?;452 let mut first = true;453 for_tuples!( #(454 if !Tuple.is_empty() {455 if !first {456 write!(writer, ", ")?;457 }458 first = false;459 Tuple.solidity_default(writer, tc)?;460 }461 )* );462 write!(writer, ")")?;463 Ok(())464 }465 }466 fn len(&self) -> usize {467 for_tuples!( #( Tuple.len() )+* )468 }469}470471pub trait SolidityFunctions {472 fn solidity_name(473 &self,474 is_impl: bool,475 writer: &mut impl fmt::Write,476 tc: &TypeCollector,477 ) -> fmt::Result;478}479480pub enum SolidityMutability {481 Pure,482 View,483 Mutable,484}485pub struct SolidityFunction<A, R> {486 pub docs: &'static [&'static str],487 pub selector_str: &'static str,488 pub selector: u32,489 pub hide: bool,490 pub custom_signature: FunctionSignature,491 pub name: &'static str,492 pub args: A,493 pub result: R,494 pub mutability: SolidityMutability,495 pub is_payable: bool,496}497impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {498 fn solidity_name(499 &self,500 is_impl: bool,501 writer: &mut impl fmt::Write,502 tc: &TypeCollector,503 ) -> fmt::Result {504 let hide_comment = self.hide.then(|| "// ").unwrap_or("");505 for doc in self.docs {506 writeln!(writer, "\t{hide_comment}///{}", doc)?;507 }508 writeln!(509 writer,510 "\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",511 self.selector512 )?;513 writeln!(514 writer,515 "\t{hide_comment}/// or in textual repr: {}",516 self.selector_str517 )?;518 if self.selector_str != self.custom_signature.as_str() {519 writeln!(520 writer,521 "\t{hide_comment}/// or in the expanded repr: {}",522 self.custom_signature.as_str()523 )?;524 }525 write!(writer, "\t{hide_comment}function {}(", self.name)?;526 self.args.solidity_name(writer, tc)?;527 write!(writer, ")")?;528 if is_impl {529 write!(writer, " public")?;530 } else {531 write!(writer, " external")?;532 }533 match &self.mutability {534 SolidityMutability::Pure => write!(writer, " pure")?,535 SolidityMutability::View => write!(writer, " view")?,536 SolidityMutability::Mutable => {}537 }538 if self.is_payable {539 write!(writer, " payable")?;540 }541 if !self.result.is_empty() {542 write!(writer, " returns (")?;543 self.result.solidity_name(writer, tc)?;544 write!(writer, ")")?;545 }546 if is_impl {547 writeln!(writer, " {{")?;548 writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;549 self.args.solidity_get(hide_comment, writer)?;550 match &self.mutability {551 SolidityMutability::Pure => {}552 SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,553 SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,554 }555 if !self.result.is_empty() {556 write!(writer, "\t{hide_comment}\treturn ")?;557 self.result.solidity_default(writer, tc)?;558 writeln!(writer, ";")?;559 }560 writeln!(writer, "\t{hide_comment}}}")?;561 } else {562 writeln!(writer, ";")?;563 }564 if self.hide {565 writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;566 }567 Ok(())568 }569}570571#[impl_for_tuples(0, 48)]572impl SolidityFunctions for Tuple {573 for_tuples!( where #( Tuple: SolidityFunctions ),* );574575 fn solidity_name(576 &self,577 is_impl: bool,578 writer: &mut impl fmt::Write,579 tc: &TypeCollector,580 ) -> fmt::Result {581 let mut first = false;582 for_tuples!( #(583 Tuple.solidity_name(is_impl, writer, tc)?;584 )* );585 Ok(())586 }587}588589pub struct SolidityInterface<F: SolidityFunctions> {590 pub docs: &'static [&'static str],591 pub selector: bytes4,592 pub name: &'static str,593 pub is: &'static [&'static str],594 pub functions: F,595}596597impl<F: SolidityFunctions> SolidityInterface<F> {598 pub fn format(599 &self,600 is_impl: bool,601 out: &mut impl fmt::Write,602 tc: &TypeCollector,603 ) -> fmt::Result {604 const ZERO_BYTES: [u8; 4] = [0; 4];605 for doc in self.docs {606 writeln!(out, "///{}", doc)?;607 }608 if self.selector != ZERO_BYTES {609 writeln!(610 out,611 "/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",612 u32::from_be_bytes(self.selector)613 )?;614 }615 if is_impl {616 write!(out, "contract ")?;617 } else {618 write!(out, "interface ")?;619 }620 write!(out, "{}", self.name)?;621 if !self.is.is_empty() {622 write!(out, " is")?;623 for (i, n) in self.is.iter().enumerate() {624 if i != 0 {625 write!(out, ",")?;626 }627 write!(out, " {}", n)?;628 }629 }630 writeln!(out, " {{")?;631 self.functions.solidity_name(is_impl, out, tc)?;632 writeln!(out, "}}")?;633 Ok(())634 }635}636637pub struct SolidityEvent<A> {638 pub name: &'static str,639 pub args: A,640}641642impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {643 fn solidity_name(644 &self,645 _is_impl: bool,646 writer: &mut impl fmt::Write,647 tc: &TypeCollector,648 ) -> fmt::Result {649 write!(writer, "\tevent {}(", self.name)?;650 self.args.solidity_name(writer, tc)?;651 writeln!(writer, ");")652 }653}