123456789101112131415161718192021222324#[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 str::from_utf8,34};35use impl_trait_for_tuples::impl_for_tuples;36use crate::{types::*, custom_signature::FunctionSignature};3738#[derive(Default)]39pub struct TypeCollector {40 41 42 structs: RefCell<BTreeMap<string, usize>>,43 anonymous: RefCell<BTreeMap<Vec<string>, usize>>,44 id: Cell<usize>,45}46impl TypeCollector {47 pub fn new() -> Self {48 Self::default()49 }50 pub fn collect(&self, item: string) {51 let id = self.next_id();52 self.structs.borrow_mut().insert(item, id);53 }54 pub fn next_id(&self) -> usize {55 let v = self.id.get();56 self.id.set(v + 1);57 v58 }59 pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {60 let names = T::names(self);61 if let Some(id) = self.anonymous.borrow().get(&names).cloned() {62 return format!("Tuple{}", id);63 }64 let id = self.next_id();65 let mut str = String::new();66 writeln!(str, "/// @dev anonymous struct").unwrap();67 writeln!(str, "struct Tuple{} {{", id).unwrap();68 for (i, name) in names.iter().enumerate() {69 writeln!(str, "\t{} field_{};", name, i).unwrap();70 }71 writeln!(str, "}}").unwrap();72 self.collect(str);73 self.anonymous.borrow_mut().insert(names, id);74 format!("Tuple{}", id)75 }76 pub fn collect_struct<T: StructCollect>(&self) -> String {77 self.collect(<T as StructCollect>::declaration());78 <T as StructCollect>::name()79 }80 pub fn finish(self) -> Vec<string> {81 let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();82 data.sort_by_key(|(_, id)| Reverse(*id));83 data.into_iter().map(|(code, _)| code).collect()84 }85}8687pub trait StructCollect: 'static {88 89 fn name() -> String;90 91 fn declaration() -> String;92}9394pub trait SolidityTypeName: 'static {95 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;96 97 fn is_simple() -> bool;98 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;99 100 fn is_void() -> bool {101 false102 }103}104macro_rules! solidity_type_name {105 ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {106 $(107 impl SolidityTypeName for $ty {108 fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {109 write!(writer, $name)110 }111 fn is_simple() -> bool {112 $simple113 }114 fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {115 write!(writer, $default)116 }117 }118 )*119 };120}121122solidity_type_name! {123 uint8 => "uint8" true = "0",124 uint32 => "uint32" true = "0",125 uint64 => "uint64" true = "0",126 uint128 => "uint128" true = "0",127 uint256 => "uint256" true = "0",128 bytes4 => "bytes4" true = "bytes4(0)",129 address => "address" true = "0x0000000000000000000000000000000000000000",130 string => "string" false = "\"\"",131 bytes => "bytes" false = "hex\"\"",132 bool => "bool" true = "false",133}134impl SolidityTypeName for void {135 fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {136 Ok(())137 }138 fn is_simple() -> bool {139 true140 }141 fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {142 Ok(())143 }144 fn is_void() -> bool {145 true146 }147}148149mod sealed {150 151 152 153 154 pub trait CanBePlacedInVec {}155}156157impl sealed::CanBePlacedInVec for uint256 {}158impl sealed::CanBePlacedInVec for string {}159impl sealed::CanBePlacedInVec for address {}160impl sealed::CanBePlacedInVec for EthCrossAccount {}161162impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {163 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {164 T::solidity_name(writer, tc)?;165 write!(writer, "[]")166 }167 fn is_simple() -> bool {168 false169 }170 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {171 write!(writer, "new ")?;172 T::solidity_name(writer, tc)?;173 write!(writer, "[](0)")174 }175}176177impl SolidityTupleType for EthCrossAccount {178 fn names(tc: &TypeCollector) -> Vec<string> {179 let mut collected = Vec::with_capacity(Self::len());180 {181 let mut out = string::new();182 address::solidity_name(&mut out, tc).expect("no fmt error");183 collected.push(out);184 }185 {186 let mut out = string::new();187 uint256::solidity_name(&mut out, tc).expect("no fmt error");188 collected.push(out);189 }190 collected191 }192193 fn len() -> usize {194 2195 }196}197impl SolidityTypeName for EthCrossAccount {198 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {199 write!(writer, "{}", tc.collect_struct::<Self>())200 }201202 fn is_simple() -> bool {203 false204 }205206 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {207 write!(writer, "{}(", tc.collect_tuple::<Self>())?;208 address::solidity_default(writer, tc)?;209 write!(writer, ",")?;210 uint256::solidity_default(writer, tc)?;211 write!(writer, ")")212 }213}214215impl StructCollect for EthCrossAccount {216 fn name() -> String {217 "EthCrossAccount".into()218 }219220 fn declaration() -> String {221 let mut str = String::new();222 writeln!(str, "/// @dev Cross account struct").unwrap();223 writeln!(str, "struct {} {{", Self::name()).unwrap();224 writeln!(str, "\taddress eth;").unwrap();225 writeln!(str, "\tuint256 sub;").unwrap();226 writeln!(str, "}}").unwrap();227 str228 }229}230231pub trait SolidityTupleType {232 fn names(tc: &TypeCollector) -> Vec<String>;233 fn len() -> usize;234}235236macro_rules! count {237 () => (0usize);238 ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));239}240241macro_rules! impl_tuples {242 ($($ident:ident)+) => {243 impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}244 impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {245 fn names(tc: &TypeCollector) -> Vec<string> {246 let mut collected = Vec::with_capacity(Self::len());247 $({248 let mut out = string::new();249 $ident::solidity_name(&mut out, tc).expect("no fmt error");250 collected.push(out);251 })*;252 collected253 }254255 fn len() -> usize {256 count!($($ident)*)257 }258 }259 impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {260 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {261 write!(writer, "{}", tc.collect_tuple::<Self>())262 }263 fn is_simple() -> bool {264 false265 }266 #[allow(unused_assignments)]267 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {268 write!(writer, "{}(", tc.collect_tuple::<Self>())?;269 let mut first = true;270 $(271 if !first {272 write!(writer, ",")?;273 } else {274 first = false;275 }276 <$ident>::solidity_default(writer, tc)?;277 )*278 write!(writer, ")")279 }280 }281 };282}283284impl_tuples! {A}285impl_tuples! {A B}286impl_tuples! {A B C}287impl_tuples! {A B C D}288impl_tuples! {A B C D E}289impl_tuples! {A B C D E F}290impl_tuples! {A B C D E F G}291impl_tuples! {A B C D E F G H}292impl_tuples! {A B C D E F G H I}293impl_tuples! {A B C D E F G H I J}294295pub trait SolidityArguments {296 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;297 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result;298 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;299 fn is_empty(&self) -> bool {300 self.len() == 0301 }302 fn len(&self) -> usize;303}304305#[derive(Default)]306pub struct UnnamedArgument<T>(PhantomData<*const T>);307308impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {309 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {310 if !T::is_void() {311 T::solidity_name(writer, tc)?;312 if !T::is_simple() {313 write!(writer, " memory")?;314 }315 Ok(())316 } else {317 Ok(())318 }319 }320 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {321 Ok(())322 }323 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {324 T::solidity_default(writer, tc)325 }326 fn len(&self) -> usize {327 if T::is_void() {328 0329 } else {330 1331 }332 }333}334335pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);336337impl<T> NamedArgument<T> {338 pub fn new(name: &'static str) -> Self {339 Self(name, Default::default())340 }341}342343impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {344 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {345 if !T::is_void() {346 T::solidity_name(writer, tc)?;347 if !T::is_simple() {348 write!(writer, " memory")?;349 }350 write!(writer, " {}", self.0)351 } else {352 Ok(())353 }354 }355 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {356 writeln!(writer, "\t{prefix}\t{};", self.0)357 }358 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {359 T::solidity_default(writer, tc)360 }361 fn len(&self) -> usize {362 if T::is_void() {363 0364 } else {365 1366 }367 }368}369370pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);371372impl<T> SolidityEventArgument<T> {373 pub fn new(indexed: bool, name: &'static str) -> Self {374 Self(indexed, name, Default::default())375 }376}377378impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {379 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {380 if !T::is_void() {381 T::solidity_name(writer, tc)?;382 if self.0 {383 write!(writer, " indexed")?;384 }385 write!(writer, " {}", self.1)386 } else {387 Ok(())388 }389 }390 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {391 writeln!(writer, "\t{prefix}\t{};", self.1)392 }393 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {394 T::solidity_default(writer, tc)395 }396 fn len(&self) -> usize {397 if T::is_void() {398 0399 } else {400 1401 }402 }403}404405impl SolidityArguments for () {406 fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {407 Ok(())408 }409 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {410 Ok(())411 }412 fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {413 Ok(())414 }415 fn len(&self) -> usize {416 0417 }418}419420#[impl_for_tuples(1, 12)]421impl SolidityArguments for Tuple {422 for_tuples!( where #( Tuple: SolidityArguments ),* );423424 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {425 let mut first = true;426 for_tuples!( #(427 if !Tuple.is_empty() {428 if !first {429 write!(writer, ", ")?;430 }431 first = false;432 Tuple.solidity_name(writer, tc)?;433 }434 )* );435 Ok(())436 }437 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {438 for_tuples!( #(439 Tuple.solidity_get(prefix, writer)?;440 )* );441 Ok(())442 }443 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {444 if self.is_empty() {445 Ok(())446 } else if self.len() == 1 {447 for_tuples!( #(448 Tuple.solidity_default(writer, tc)?;449 )* );450 Ok(())451 } else {452 write!(writer, "(")?;453 let mut first = true;454 for_tuples!( #(455 if !Tuple.is_empty() {456 if !first {457 write!(writer, ", ")?;458 }459 first = false;460 Tuple.solidity_default(writer, tc)?;461 }462 )* );463 write!(writer, ")")?;464 Ok(())465 }466 }467 fn len(&self) -> usize {468 for_tuples!( #( Tuple.len() )+* )469 }470}471472pub trait SolidityFunctions {473 fn solidity_name(474 &self,475 is_impl: bool,476 writer: &mut impl fmt::Write,477 tc: &TypeCollector,478 ) -> fmt::Result;479}480481pub enum SolidityMutability {482 Pure,483 View,484 Mutable,485}486pub struct SolidityFunction<A, R> {487 pub docs: &'static [&'static str],488 pub selector_str: &'static str,489 pub selector: u32,490 pub hide: bool,491 pub custom_signature: FunctionSignature,492 pub name: &'static str,493 pub args: A,494 pub result: R,495 pub mutability: SolidityMutability,496 pub is_payable: bool,497}498impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {499 fn solidity_name(500 &self,501 is_impl: bool,502 writer: &mut impl fmt::Write,503 tc: &TypeCollector,504 ) -> fmt::Result {505 let hide_comment = self.hide.then(|| "// ").unwrap_or("");506 for doc in self.docs {507 writeln!(writer, "\t{hide_comment}///{}", doc)?;508 }509 writeln!(510 writer,511 "\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",512 self.selector513 )?;514 writeln!(515 writer,516 "\t{hide_comment}/// or in textual repr: {}",517 518 self.selector_str519 )?;520 write!(writer, "\t{hide_comment}function {}(", self.name)?;521 self.args.solidity_name(writer, tc)?;522 write!(writer, ")")?;523 if is_impl {524 write!(writer, " public")?;525 } else {526 write!(writer, " external")?;527 }528 match &self.mutability {529 SolidityMutability::Pure => write!(writer, " pure")?,530 SolidityMutability::View => write!(writer, " view")?,531 SolidityMutability::Mutable => {}532 }533 if self.is_payable {534 write!(writer, " payable")?;535 }536 if !self.result.is_empty() {537 write!(writer, " returns (")?;538 self.result.solidity_name(writer, tc)?;539 write!(writer, ")")?;540 }541 if is_impl {542 writeln!(writer, " {{")?;543 writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;544 self.args.solidity_get(hide_comment, writer)?;545 match &self.mutability {546 SolidityMutability::Pure => {}547 SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,548 SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,549 }550 if !self.result.is_empty() {551 write!(writer, "\t{hide_comment}\treturn ")?;552 self.result.solidity_default(writer, tc)?;553 writeln!(writer, ";")?;554 }555 writeln!(writer, "\t{hide_comment}}}")?;556 } else {557 writeln!(writer, ";")?;558 }559 if self.hide {560 writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;561 }562 Ok(())563 }564}565566#[impl_for_tuples(0, 48)]567impl SolidityFunctions for Tuple {568 for_tuples!( where #( Tuple: SolidityFunctions ),* );569570 fn solidity_name(571 &self,572 is_impl: bool,573 writer: &mut impl fmt::Write,574 tc: &TypeCollector,575 ) -> fmt::Result {576 let mut first = false;577 for_tuples!( #(578 Tuple.solidity_name(is_impl, writer, tc)?;579 )* );580 Ok(())581 }582}583584pub struct SolidityInterface<F: SolidityFunctions> {585 pub docs: &'static [&'static str],586 pub selector: bytes4,587 pub name: &'static str,588 pub is: &'static [&'static str],589 pub functions: F,590}591592impl<F: SolidityFunctions> SolidityInterface<F> {593 pub fn format(594 &self,595 is_impl: bool,596 out: &mut impl fmt::Write,597 tc: &TypeCollector,598 ) -> fmt::Result {599 const ZERO_BYTES: [u8; 4] = [0; 4];600 for doc in self.docs {601 writeln!(out, "///{}", doc)?;602 }603 if self.selector != ZERO_BYTES {604 writeln!(605 out,606 "/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",607 u32::from_be_bytes(self.selector)608 )?;609 }610 if is_impl {611 write!(out, "contract ")?;612 } else {613 write!(out, "interface ")?;614 }615 write!(out, "{}", self.name)?;616 if !self.is.is_empty() {617 write!(out, " is")?;618 for (i, n) in self.is.iter().enumerate() {619 if i != 0 {620 write!(out, ",")?;621 }622 write!(out, " {}", n)?;623 }624 }625 writeln!(out, " {{")?;626 self.functions.solidity_name(is_impl, out, tc)?;627 writeln!(out, "}}")?;628 Ok(())629 }630}631632pub struct SolidityEvent<A> {633 pub name: &'static str,634 pub args: A,635}636637impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {638 fn solidity_name(639 &self,640 _is_impl: bool,641 writer: &mut impl fmt::Write,642 tc: &TypeCollector,643 ) -> fmt::Result {644 write!(writer, "\tevent {}(", self.name)?;645 self.args.solidity_name(writer, tc)?;646 writeln!(writer, ");")647 }648}