1#[cfg(not(feature = "std"))]2use alloc::{3 string::String,4 vec::Vec,5 collections::{BTreeSet, BTreeMap},6 format,7};8#[cfg(feature = "std")]9use std::collections::{BTreeSet, BTreeMap};10use core::{11 fmt::{self, Write},12 marker::PhantomData,13 cell::{Cell, RefCell},14};15use impl_trait_for_tuples::impl_for_tuples;16use crate::types::*;1718#[derive(Default)]19pub struct TypeCollector {20 structs: RefCell<BTreeSet<string>>,21 anonymous: RefCell<BTreeMap<Vec<string>, usize>>,22 id: Cell<usize>,23}24impl TypeCollector {25 pub fn new() -> Self {26 Self::default()27 }28 pub fn collect(&self, item: string) {29 self.structs.borrow_mut().insert(item);30 }31 pub fn next_id(&self) -> usize {32 let v = self.id.get();33 self.id.set(v + 1);34 v35 }36 pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {37 let names = T::names(self);38 if let Some(id) = self.anonymous.borrow().get(&names).cloned() {39 return format!("Tuple{}", id);40 }41 let id = self.next_id();42 let mut str = String::new();43 writeln!(str, "// Anonymous struct").unwrap();44 writeln!(str, "struct Tuple{} {{", id).unwrap();45 for (i, name) in names.iter().enumerate() {46 writeln!(str, "\t{} field_{};", name, i).unwrap();47 }48 writeln!(str, "}}").unwrap();49 self.collect(str);50 self.anonymous.borrow_mut().insert(names, id);51 format!("Tuple{}", id)52 }53 pub fn finish(self) -> BTreeSet<string> {54 self.structs.into_inner()55 }56}5758pub trait SolidityTypeName: 'static {59 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;60 fn is_simple() -> bool;61 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;62 fn is_void() -> bool {63 false64 }65}66macro_rules! solidity_type_name {67 ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {68 $(69 impl SolidityTypeName for $ty {70 fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {71 write!(writer, $name)72 }73 fn is_simple() -> bool {74 $simple75 }76 fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {77 write!(writer, $default)78 }79 }80 )*81 };82}8384solidity_type_name! {85 uint8 => "uint8" true = "0",86 uint32 => "uint32" true = "0",87 uint64 => "uint64" true = "0",88 uint128 => "uint128" true = "0",89 uint256 => "uint256" true = "0",90 address => "address" true = "0x0000000000000000000000000000000000000000",91 string => "string" false = "\"\"",92 bytes => "bytes" false = "hex\"\"",93 bool => "bool" true = "false",94}95impl SolidityTypeName for void {96 fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {97 Ok(())98 }99 fn is_simple() -> bool {100 true101 }102 fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {103 Ok(())104 }105 fn is_void() -> bool {106 true107 }108}109110mod sealed {111 pub trait CanBePlacedInVec {}112}113114impl sealed::CanBePlacedInVec for uint256 {}115impl sealed::CanBePlacedInVec for string {}116impl sealed::CanBePlacedInVec for address {}117118impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {119 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {120 T::solidity_name(writer, tc)?;121 write!(writer, "[]")122 }123 fn is_simple() -> bool {124 false125 }126 fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {127 write!(writer, "[]")128 }129}130131pub trait SolidityTupleType {132 fn names(tc: &TypeCollector) -> Vec<String>;133 fn len() -> usize;134}135136macro_rules! count {137 () => (0usize);138 ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));139}140141macro_rules! impl_tuples {142 ($($ident:ident)+) => {143 impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}144 impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {145 fn names(tc: &TypeCollector) -> Vec<string> {146 let mut collected = Vec::with_capacity(Self::len());147 $({148 let mut out = string::new();149 $ident::solidity_name(&mut out, tc).expect("no fmt error");150 collected.push(out);151 })*;152 collected153 }154155 fn len() -> usize {156 count!($($ident)*)157 }158 }159 impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {160 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {161 write!(writer, "{}", tc.collect_tuple::<Self>())162 }163 fn is_simple() -> bool {164 false165 }166 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {167 write!(writer, "{}(", tc.collect_tuple::<Self>())?;168 $(169 <$ident>::solidity_default(writer, tc)?;170 )*171 write!(writer, ")")172 }173 }174 };175}176177impl_tuples! {A}178impl_tuples! {A B}179impl_tuples! {A B C}180impl_tuples! {A B C D}181impl_tuples! {A B C D E}182impl_tuples! {A B C D E F}183impl_tuples! {A B C D E F G}184impl_tuples! {A B C D E F G H}185impl_tuples! {A B C D E F G H I}186impl_tuples! {A B C D E F G H I J}187188pub trait SolidityArguments {189 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;190 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;191 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;192 fn is_empty(&self) -> bool {193 self.len() == 0194 }195 fn len(&self) -> usize;196}197198#[derive(Default)]199pub struct UnnamedArgument<T>(PhantomData<*const T>);200201impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {202 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {203 if !T::is_void() {204 T::solidity_name(writer, tc)?;205 if !T::is_simple() {206 write!(writer, " memory")?;207 }208 Ok(())209 } else {210 Ok(())211 }212 }213 fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {214 Ok(())215 }216 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {217 T::solidity_default(writer, tc)218 }219 fn len(&self) -> usize {220 if T::is_void() {221 0222 } else {223 1224 }225 }226}227228pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);229230impl<T> NamedArgument<T> {231 pub fn new(name: &'static str) -> Self {232 Self(name, Default::default())233 }234}235236impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {237 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {238 if !T::is_void() {239 T::solidity_name(writer, tc)?;240 if !T::is_simple() {241 write!(writer, " memory")?;242 }243 write!(writer, " {}", self.0)244 } else {245 Ok(())246 }247 }248 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {249 writeln!(writer, "\t\t{};", self.0)250 }251 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {252 T::solidity_default(writer, tc)253 }254 fn len(&self) -> usize {255 if T::is_void() {256 0257 } else {258 1259 }260 }261}262263pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);264265impl<T> SolidityEventArgument<T> {266 pub fn new(indexed: bool, name: &'static str) -> Self {267 Self(indexed, name, Default::default())268 }269}270271impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {272 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {273 if !T::is_void() {274 T::solidity_name(writer, tc)?;275 if self.0 {276 write!(writer, " indexed")?;277 }278 write!(writer, " {}", self.1)279 } else {280 Ok(())281 }282 }283 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {284 writeln!(writer, "\t\t{};", self.1)285 }286 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {287 T::solidity_default(writer, tc)288 }289 fn len(&self) -> usize {290 if T::is_void() {291 0292 } else {293 1294 }295 }296}297298impl SolidityArguments for () {299 fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {300 Ok(())301 }302 fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {303 Ok(())304 }305 fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {306 Ok(())307 }308 fn len(&self) -> usize {309 0310 }311}312313#[impl_for_tuples(1, 5)]314impl SolidityArguments for Tuple {315 for_tuples!( where #( Tuple: SolidityArguments ),* );316317 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {318 let mut first = true;319 for_tuples!( #(320 if !Tuple.is_empty() {321 if !first {322 write!(writer, ", ")?;323 }324 first = false;325 Tuple.solidity_name(writer, tc)?;326 }327 )* );328 Ok(())329 }330 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {331 for_tuples!( #(332 Tuple.solidity_get(writer)?;333 )* );334 Ok(())335 }336 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {337 if self.is_empty() {338 Ok(())339 } else if self.len() == 1 {340 for_tuples!( #(341 Tuple.solidity_default(writer, tc)?;342 )* );343 Ok(())344 } else {345 write!(writer, "(")?;346 let mut first = true;347 for_tuples!( #(348 if !Tuple.is_empty() {349 if !first {350 write!(writer, ", ")?;351 }352 first = false;353 Tuple.solidity_default(writer, tc)?;354 }355 )* );356 write!(writer, ")")?;357 Ok(())358 }359 }360 fn len(&self) -> usize {361 for_tuples!( #( Tuple.len() )+* )362 }363}364365pub trait SolidityFunctions {366 fn solidity_name(367 &self,368 is_impl: bool,369 writer: &mut impl fmt::Write,370 tc: &TypeCollector,371 ) -> fmt::Result;372}373374pub enum SolidityMutability {375 Pure,376 View,377 Mutable,378}379pub struct SolidityFunction<A, R> {380 pub docs: &'static [&'static str],381 pub selector: &'static str,382 pub name: &'static str,383 pub args: A,384 pub result: R,385 pub mutability: SolidityMutability,386}387impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {388 fn solidity_name(389 &self,390 is_impl: bool,391 writer: &mut impl fmt::Write,392 tc: &TypeCollector,393 ) -> fmt::Result {394 for doc in self.docs {395 writeln!(writer, "\t//{}", doc)?;396 }397 if !self.docs.is_empty() {398 writeln!(writer, "\t//")?;399 }400 writeln!(writer, "\t// Selector: {}", self.selector)?;401 write!(writer, "\tfunction {}(", self.name)?;402 self.args.solidity_name(writer, tc)?;403 write!(writer, ")")?;404 if is_impl {405 write!(writer, " public")?;406 } else {407 write!(writer, " external")?;408 }409 match &self.mutability {410 SolidityMutability::Pure => write!(writer, " pure")?,411 SolidityMutability::View => write!(writer, " view")?,412 SolidityMutability::Mutable => {}413 }414 if !self.result.is_empty() {415 write!(writer, " returns (")?;416 self.result.solidity_name(writer, tc)?;417 write!(writer, ")")?;418 }419 if is_impl {420 writeln!(writer, " {{")?;421 writeln!(writer, "\t\trequire(false, stub_error);")?;422 self.args.solidity_get(writer)?;423 match &self.mutability {424 SolidityMutability::Pure => {}425 SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,426 SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,427 }428 if !self.result.is_empty() {429 write!(writer, "\t\treturn ")?;430 self.result.solidity_default(writer, tc)?;431 writeln!(writer, ";")?;432 }433 writeln!(writer, "\t}}")?;434 } else {435 writeln!(writer, ";")?;436 }437 Ok(())438 }439}440441#[impl_for_tuples(0, 12)]442impl SolidityFunctions for Tuple {443 for_tuples!( where #( Tuple: SolidityFunctions ),* );444445 fn solidity_name(446 &self,447 is_impl: bool,448 writer: &mut impl fmt::Write,449 tc: &TypeCollector,450 ) -> fmt::Result {451 let mut first = false;452 for_tuples!( #(453 Tuple.solidity_name(is_impl, writer, tc)?;454 )* );455 Ok(())456 }457}458459pub struct SolidityInterface<F: SolidityFunctions> {460 pub selector: u32,461 pub name: &'static str,462 pub is: &'static [&'static str],463 pub functions: F,464}465466impl<F: SolidityFunctions> SolidityInterface<F> {467 pub fn format(468 &self,469 is_impl: bool,470 out: &mut impl fmt::Write,471 tc: &TypeCollector,472 ) -> fmt::Result {473 if self.selector != 0 {474 writeln!(out, "// Selector: {:0>8x}", self.selector)?;475 }476 if is_impl {477 write!(out, "contract ")?;478 } else {479 write!(out, "interface ")?;480 }481 write!(out, "{}", self.name)?;482 if !self.is.is_empty() {483 write!(out, " is")?;484 for (i, n) in self.is.iter().enumerate() {485 if i != 0 {486 write!(out, ",")?;487 }488 write!(out, " {}", n)?;489 }490 }491 writeln!(out, " {{")?;492 self.functions.solidity_name(is_impl, out, tc)?;493 writeln!(out, "}}")?;494 Ok(())495 }496}497498pub struct SolidityEvent<A> {499 pub name: &'static str,500 pub args: A,501}502503impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {504 fn solidity_name(505 &self,506 _is_impl: bool,507 writer: &mut impl fmt::Write,508 tc: &TypeCollector,509 ) -> fmt::Result {510 write!(writer, "\tevent {}(", self.name)?;511 self.args.solidity_name(writer, tc)?;512 writeln!(writer, ");")513 }514}