12345678910111213141516171819#[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 40 41 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}