1234567891011121314151617#[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, "// 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 address => "address" true = "0x0000000000000000000000000000000000000000",107 string => "string" false = "\"\"",108 bytes => "bytes" false = "hex\"\"",109 bool => "bool" true = "false",110}111impl SolidityTypeName for void {112 fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {113 Ok(())114 }115 fn is_simple() -> bool {116 true117 }118 fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {119 Ok(())120 }121 fn is_void() -> bool {122 true123 }124}125126mod sealed {127 pub trait CanBePlacedInVec {}128}129130impl sealed::CanBePlacedInVec for uint256 {}131impl sealed::CanBePlacedInVec for string {}132impl sealed::CanBePlacedInVec for address {}133134impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {135 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {136 T::solidity_name(writer, tc)?;137 write!(writer, "[]")138 }139 fn is_simple() -> bool {140 false141 }142 fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {143 write!(writer, "[]")144 }145}146147pub trait SolidityTupleType {148 fn names(tc: &TypeCollector) -> Vec<String>;149 fn len() -> usize;150}151152macro_rules! count {153 () => (0usize);154 ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));155}156157macro_rules! impl_tuples {158 ($($ident:ident)+) => {159 impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}160 impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {161 fn names(tc: &TypeCollector) -> Vec<string> {162 let mut collected = Vec::with_capacity(Self::len());163 $({164 let mut out = string::new();165 $ident::solidity_name(&mut out, tc).expect("no fmt error");166 collected.push(out);167 })*;168 collected169 }170171 fn len() -> usize {172 count!($($ident)*)173 }174 }175 impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {176 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {177 write!(writer, "{}", tc.collect_tuple::<Self>())178 }179 fn is_simple() -> bool {180 false181 }182 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {183 write!(writer, "{}(", tc.collect_tuple::<Self>())?;184 $(185 <$ident>::solidity_default(writer, tc)?;186 )*187 write!(writer, ")")188 }189 }190 };191}192193impl_tuples! {A}194impl_tuples! {A B}195impl_tuples! {A B C}196impl_tuples! {A B C D}197impl_tuples! {A B C D E}198impl_tuples! {A B C D E F}199impl_tuples! {A B C D E F G}200impl_tuples! {A B C D E F G H}201impl_tuples! {A B C D E F G H I}202impl_tuples! {A B C D E F G H I J}203204pub trait SolidityArguments {205 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;206 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;207 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;208 fn is_empty(&self) -> bool {209 self.len() == 0210 }211 fn len(&self) -> usize;212}213214#[derive(Default)]215pub struct UnnamedArgument<T>(PhantomData<*const T>);216217impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {218 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {219 if !T::is_void() {220 T::solidity_name(writer, tc)?;221 if !T::is_simple() {222 write!(writer, " memory")?;223 }224 Ok(())225 } else {226 Ok(())227 }228 }229 fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {230 Ok(())231 }232 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {233 T::solidity_default(writer, tc)234 }235 fn len(&self) -> usize {236 if T::is_void() {237 0238 } else {239 1240 }241 }242}243244pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);245246impl<T> NamedArgument<T> {247 pub fn new(name: &'static str) -> Self {248 Self(name, Default::default())249 }250}251252impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {253 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {254 if !T::is_void() {255 T::solidity_name(writer, tc)?;256 if !T::is_simple() {257 write!(writer, " memory")?;258 }259 write!(writer, " {}", self.0)260 } else {261 Ok(())262 }263 }264 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {265 writeln!(writer, "\t\t{};", self.0)266 }267 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {268 T::solidity_default(writer, tc)269 }270 fn len(&self) -> usize {271 if T::is_void() {272 0273 } else {274 1275 }276 }277}278279pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);280281impl<T> SolidityEventArgument<T> {282 pub fn new(indexed: bool, name: &'static str) -> Self {283 Self(indexed, name, Default::default())284 }285}286287impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {288 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {289 if !T::is_void() {290 T::solidity_name(writer, tc)?;291 if self.0 {292 write!(writer, " indexed")?;293 }294 write!(writer, " {}", self.1)295 } else {296 Ok(())297 }298 }299 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {300 writeln!(writer, "\t\t{};", self.1)301 }302 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {303 T::solidity_default(writer, tc)304 }305 fn len(&self) -> usize {306 if T::is_void() {307 0308 } else {309 1310 }311 }312}313314impl SolidityArguments for () {315 fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {316 Ok(())317 }318 fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {319 Ok(())320 }321 fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {322 Ok(())323 }324 fn len(&self) -> usize {325 0326 }327}328329#[impl_for_tuples(1, 5)]330impl SolidityArguments for Tuple {331 for_tuples!( where #( Tuple: SolidityArguments ),* );332333 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {334 let mut first = true;335 for_tuples!( #(336 if !Tuple.is_empty() {337 if !first {338 write!(writer, ", ")?;339 }340 first = false;341 Tuple.solidity_name(writer, tc)?;342 }343 )* );344 Ok(())345 }346 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {347 for_tuples!( #(348 Tuple.solidity_get(writer)?;349 )* );350 Ok(())351 }352 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {353 if self.is_empty() {354 Ok(())355 } else if self.len() == 1 {356 for_tuples!( #(357 Tuple.solidity_default(writer, tc)?;358 )* );359 Ok(())360 } else {361 write!(writer, "(")?;362 let mut first = true;363 for_tuples!( #(364 if !Tuple.is_empty() {365 if !first {366 write!(writer, ", ")?;367 }368 first = false;369 Tuple.solidity_default(writer, tc)?;370 }371 )* );372 write!(writer, ")")?;373 Ok(())374 }375 }376 fn len(&self) -> usize {377 for_tuples!( #( Tuple.len() )+* )378 }379}380381pub trait SolidityFunctions {382 fn solidity_name(383 &self,384 is_impl: bool,385 writer: &mut impl fmt::Write,386 tc: &TypeCollector,387 ) -> fmt::Result;388}389390pub enum SolidityMutability {391 Pure,392 View,393 Mutable,394}395pub struct SolidityFunction<A, R> {396 pub docs: &'static [&'static str],397 pub selector: &'static str,398 pub name: &'static str,399 pub args: A,400 pub result: R,401 pub mutability: SolidityMutability,402}403impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {404 fn solidity_name(405 &self,406 is_impl: bool,407 writer: &mut impl fmt::Write,408 tc: &TypeCollector,409 ) -> fmt::Result {410 for doc in self.docs {411 writeln!(writer, "\t//{}", doc)?;412 }413 if !self.docs.is_empty() {414 writeln!(writer, "\t//")?;415 }416 writeln!(writer, "\t// Selector: {}", self.selector)?;417 write!(writer, "\tfunction {}(", self.name)?;418 self.args.solidity_name(writer, tc)?;419 write!(writer, ")")?;420 if is_impl {421 write!(writer, " public")?;422 } else {423 write!(writer, " external")?;424 }425 match &self.mutability {426 SolidityMutability::Pure => write!(writer, " pure")?,427 SolidityMutability::View => write!(writer, " view")?,428 SolidityMutability::Mutable => {}429 }430 if !self.result.is_empty() {431 write!(writer, " returns (")?;432 self.result.solidity_name(writer, tc)?;433 write!(writer, ")")?;434 }435 if is_impl {436 writeln!(writer, " {{")?;437 writeln!(writer, "\t\trequire(false, stub_error);")?;438 self.args.solidity_get(writer)?;439 match &self.mutability {440 SolidityMutability::Pure => {}441 SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,442 SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,443 }444 if !self.result.is_empty() {445 write!(writer, "\t\treturn ")?;446 self.result.solidity_default(writer, tc)?;447 writeln!(writer, ";")?;448 }449 writeln!(writer, "\t}}")?;450 } else {451 writeln!(writer, ";")?;452 }453 Ok(())454 }455}456457#[impl_for_tuples(0, 12)]458impl SolidityFunctions for Tuple {459 for_tuples!( where #( Tuple: SolidityFunctions ),* );460461 fn solidity_name(462 &self,463 is_impl: bool,464 writer: &mut impl fmt::Write,465 tc: &TypeCollector,466 ) -> fmt::Result {467 let mut first = false;468 for_tuples!( #(469 Tuple.solidity_name(is_impl, writer, tc)?;470 )* );471 Ok(())472 }473}474475pub struct SolidityInterface<F: SolidityFunctions> {476 pub selector: u32,477 pub name: &'static str,478 pub is: &'static [&'static str],479 pub functions: F,480}481482impl<F: SolidityFunctions> SolidityInterface<F> {483 pub fn format(484 &self,485 is_impl: bool,486 out: &mut impl fmt::Write,487 tc: &TypeCollector,488 ) -> fmt::Result {489 if self.selector != 0 {490 writeln!(out, "// Selector: {:0>8x}", self.selector)?;491 }492 if is_impl {493 write!(out, "contract ")?;494 } else {495 write!(out, "interface ")?;496 }497 write!(out, "{}", self.name)?;498 if !self.is.is_empty() {499 write!(out, " is")?;500 for (i, n) in self.is.iter().enumerate() {501 if i != 0 {502 write!(out, ",")?;503 }504 write!(out, " {}", n)?;505 }506 }507 writeln!(out, " {{")?;508 self.functions.solidity_name(is_impl, out, tc)?;509 writeln!(out, "}}")?;510 Ok(())511 }512}513514pub struct SolidityEvent<A> {515 pub name: &'static str,516 pub args: A,517}518519impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {520 fn solidity_name(521 &self,522 _is_impl: bool,523 writer: &mut impl fmt::Write,524 tc: &TypeCollector,525 ) -> fmt::Result {526 write!(writer, "\tevent {}(", self.name)?;527 self.args.solidity_name(writer, tc)?;528 writeln!(writer, ");")529 }530}