123456789101112131415161718192021222324mod traits;25pub use traits::*;26mod impls;2728#[cfg(not(feature = "std"))]29use alloc::{string::String, vec::Vec, collections::BTreeMap, format};30#[cfg(feature = "std")]31use std::collections::BTreeMap;32use core::{33 fmt::{self, Write},34 marker::PhantomData,35 cell::{Cell, RefCell},36 cmp::Reverse,37};38use impl_trait_for_tuples::impl_for_tuples;39use crate::{types::*, custom_signature::SignatureUnit};4041#[derive(Default)]42pub struct TypeCollector {43 44 45 structs: RefCell<BTreeMap<string, usize>>,46 anonymous: RefCell<BTreeMap<Vec<string>, usize>>,47 48 id: Cell<usize>,49}50impl TypeCollector {51 pub fn new() -> Self {52 Self::default()53 }54 pub fn collect(&self, item: string) {55 let id = self.next_id();56 self.structs.borrow_mut().insert(item, id);57 }58 pub fn next_id(&self) -> usize {59 let v = self.id.get();60 self.id.set(v + 1);61 v62 }63 64 pub fn collect_tuple<T: SolidityTupleTy>(&self) -> String {65 let names = T::fields(self);66 if let Some(id) = self.anonymous.borrow().get(&names).cloned() {67 return format!("Tuple{}", id);68 }69 let id = self.next_id();70 let mut str = String::new();71 writeln!(str, "/// @dev anonymous struct").unwrap();72 writeln!(str, "struct Tuple{} {{", id).unwrap();73 for (i, name) in names.iter().enumerate() {74 writeln!(str, "\t{} field_{};", name, i).unwrap();75 }76 writeln!(str, "}}").unwrap();77 self.collect(str);78 self.anonymous.borrow_mut().insert(names, id);79 format!("Tuple{}", id)80 }81 pub fn collect_struct<T: SolidityStructTy>(&self) -> String {82 T::generate_solidity_interface(self)83 }84 pub fn collect_enum<T: SolidityEnumTy>(&self) -> String {85 T::generate_solidity_interface(self)86 }87 pub fn finish(self) -> Vec<string> {88 let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();89 data.sort_by_key(|(_, id)| Reverse(*id));90 data.into_iter().map(|(code, _)| code).collect()91 }92}93#[derive(Default)]94pub struct UnnamedArgument<T>(PhantomData<*const T>);9596impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {97 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {98 if !T::is_void() {99 T::solidity_name(writer, tc)?;100 if !T::is_simple() {101 write!(writer, " memory")?;102 }103 Ok(())104 } else {105 Ok(())106 }107 }108 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {109 Ok(())110 }111 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {112 T::solidity_default(writer, tc)113 }114 fn len(&self) -> usize {115 if T::is_void() {116 0117 } else {118 1119 }120 }121}122123pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);124125impl<T> NamedArgument<T> {126 pub fn new(name: &'static str) -> Self {127 Self(name, Default::default())128 }129}130131impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {132 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {133 if !T::is_void() {134 T::solidity_name(writer, tc)?;135 if !T::is_simple() {136 write!(writer, " memory")?;137 }138 write!(writer, " {}", self.0)139 } else {140 Ok(())141 }142 }143 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {144 writeln!(writer, "\t{prefix}\t{};", self.0)145 }146 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {147 T::solidity_default(writer, tc)148 }149 fn len(&self) -> usize {150 if T::is_void() {151 0152 } else {153 1154 }155 }156}157158pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);159160impl<T> SolidityEventArgument<T> {161 pub fn new(indexed: bool, name: &'static str) -> Self {162 Self(indexed, name, Default::default())163 }164}165166impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {167 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {168 if !T::is_void() {169 T::solidity_name(writer, tc)?;170 if self.0 {171 write!(writer, " indexed")?;172 }173 write!(writer, " {}", self.1)174 } else {175 Ok(())176 }177 }178 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {179 writeln!(writer, "\t{prefix}\t{};", self.1)180 }181 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {182 T::solidity_default(writer, tc)183 }184 fn len(&self) -> usize {185 if T::is_void() {186 0187 } else {188 1189 }190 }191}192193impl SolidityArguments for () {194 fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {195 Ok(())196 }197 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {198 Ok(())199 }200 fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {201 Ok(())202 }203 fn len(&self) -> usize {204 0205 }206}207208#[impl_for_tuples(1, 12)]209impl SolidityArguments for Tuple {210 for_tuples!( where #( Tuple: SolidityArguments ),* );211212 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {213 let mut first = true;214 for_tuples!( #(215 if !Tuple.is_empty() {216 if !first {217 write!(writer, ", ")?;218 }219 first = false;220 Tuple.solidity_name(writer, tc)?;221 }222 )* );223 Ok(())224 }225 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {226 for_tuples!( #(227 Tuple.solidity_get(prefix, writer)?;228 )* );229 Ok(())230 }231 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {232 if self.is_empty() {233 Ok(())234 } else if self.len() == 1 {235 for_tuples!( #(236 Tuple.solidity_default(writer, tc)?;237 )* );238 Ok(())239 } else {240 write!(writer, "(")?;241 let mut first = true;242 for_tuples!( #(243 if !Tuple.is_empty() {244 if !first {245 write!(writer, ", ")?;246 }247 first = false;248 Tuple.solidity_default(writer, tc)?;249 }250 )* );251 write!(writer, ")")?;252 Ok(())253 }254 }255 fn len(&self) -> usize {256 for_tuples!( #( Tuple.len() )+* )257 }258}259260pub enum SolidityMutability {261 Pure,262 View,263 Mutable,264}265pub struct SolidityFunction<A, R> {266 pub docs: &'static [&'static str],267 pub selector: u32,268 pub hide: bool,269 pub custom_signature: SignatureUnit,270 pub name: &'static str,271 pub args: A,272 pub result: R,273 pub mutability: SolidityMutability,274 pub is_payable: bool,275}276impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {277 fn solidity_name(278 &self,279 is_impl: bool,280 writer: &mut impl fmt::Write,281 tc: &TypeCollector,282 ) -> fmt::Result {283 let hide_comment = self.hide.then_some("// ").unwrap_or("");284 for doc in self.docs {285 writeln!(writer, "\t{hide_comment}///{}", doc)?;286 }287 writeln!(288 writer,289 "\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",290 self.selector291 )?;292 writeln!(293 writer,294 "\t{hide_comment}/// or in textual repr: {}",295 self.custom_signature.as_str().expect("bad utf-8")296 )?;297 write!(writer, "\t{hide_comment}function {}(", self.name)?;298 self.args.solidity_name(writer, tc)?;299 write!(writer, ")")?;300 if is_impl {301 write!(writer, " public")?;302 } else {303 write!(writer, " external")?;304 }305 match &self.mutability {306 SolidityMutability::Pure => write!(writer, " pure")?,307 SolidityMutability::View => write!(writer, " view")?,308 SolidityMutability::Mutable => {}309 }310 if self.is_payable {311 write!(writer, " payable")?;312 }313 if !self.result.is_empty() {314 write!(writer, " returns (")?;315 self.result.solidity_name(writer, tc)?;316 write!(writer, ")")?;317 }318 if is_impl {319 writeln!(writer, " {{")?;320 writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;321 self.args.solidity_get(hide_comment, writer)?;322 match &self.mutability {323 SolidityMutability::Pure => {}324 SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,325 SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,326 }327 if !self.result.is_empty() {328 write!(writer, "\t{hide_comment}\treturn ")?;329 self.result.solidity_default(writer, tc)?;330 writeln!(writer, ";")?;331 }332 writeln!(writer, "\t{hide_comment}}}")?;333 } else {334 writeln!(writer, ";")?;335 }336 if self.hide {337 writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;338 }339 Ok(())340 }341}342343#[impl_for_tuples(0, 48)]344impl SolidityFunctions for Tuple {345 for_tuples!( where #( Tuple: SolidityFunctions ),* );346347 fn solidity_name(348 &self,349 is_impl: bool,350 writer: &mut impl fmt::Write,351 tc: &TypeCollector,352 ) -> fmt::Result {353 let mut first = false;354 for_tuples!( #(355 Tuple.solidity_name(is_impl, writer, tc)?;356 )* );357 Ok(())358 }359}360361pub struct SolidityInterface<F: SolidityFunctions> {362 pub docs: &'static [&'static str],363 pub selector: Bytes4,364 pub name: &'static str,365 pub is: &'static [&'static str],366 pub functions: F,367}368369impl<F: SolidityFunctions> SolidityInterface<F> {370 pub fn format(371 &self,372 is_impl: bool,373 out: &mut impl fmt::Write,374 tc: &TypeCollector,375 ) -> fmt::Result {376 const ZERO_BYTES: [u8; 4] = [0; 4];377 for doc in self.docs {378 writeln!(out, "///{}", doc)?;379 }380 if self.selector != ZERO_BYTES {381 writeln!(382 out,383 "/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",384 u32::from_be_bytes(self.selector)385 )?;386 }387 if is_impl {388 write!(out, "contract ")?;389 } else {390 write!(out, "interface ")?;391 }392 write!(out, "{}", self.name)?;393 if !self.is.is_empty() {394 write!(out, " is")?;395 for (i, n) in self.is.iter().enumerate() {396 if i != 0 {397 write!(out, ",")?;398 }399 write!(out, " {}", n)?;400 }401 }402 writeln!(out, " {{")?;403 self.functions.solidity_name(is_impl, out, tc)?;404 writeln!(out, "}}")?;405 Ok(())406 }407}408409pub struct SolidityEvent<A> {410 pub name: &'static str,411 pub args: A,412}413414impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {415 fn solidity_name(416 &self,417 _is_impl: bool,418 writer: &mut impl fmt::Write,419 tc: &TypeCollector,420 ) -> fmt::Result {421 write!(writer, "\tevent {}(", self.name)?;422 self.args.solidity_name(writer, tc)?;423 writeln!(writer, ");")424 }425}426427#[impl_for_tuples(0, 48)]428impl SolidityItems for Tuple {429 for_tuples!( where #( Tuple: SolidityItems ),* );430431 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {432 for_tuples!( #(433 Tuple.solidity_name(writer, tc)?;434 )* );435 Ok(())436 }437}438439pub struct SolidityStructField<T> {440 pub docs: &'static [&'static str],441 pub name: &'static str,442 pub ty: PhantomData<*const T>,443}444445impl<T> SolidityItems for SolidityStructField<T>446where447 T: SolidityTypeName,448{449 fn solidity_name(&self, out: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {450 for doc in self.docs {451 writeln!(out, "///{}", doc)?;452 }453 write!(out, "\t")?;454 T::solidity_name(out, tc)?;455 writeln!(out, " {};", self.name)?;456 Ok(())457 }458}459pub struct SolidityStruct<'a, F> {460 pub docs: &'a [&'a str],461 462 pub name: &'a str,463 pub fields: F,464}465impl<F> SolidityStruct<'_, F>466where467 F: SolidityItems,468{469 pub fn format(&self, out: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {470 for doc in self.docs {471 writeln!(out, "///{}", doc)?;472 }473 writeln!(out, "struct {} {{", self.name)?;474 self.fields.solidity_name(out, tc)?;475 writeln!(out, "}}")?;476 Ok(())477 }478}479480pub struct SolidityEnumVariant {481 pub docs: &'static [&'static str],482 pub name: &'static str,483}484impl SolidityItems for SolidityEnumVariant {485 fn solidity_name(&self, out: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {486 for doc in self.docs {487 writeln!(out, "///{}", doc)?;488 }489 write!(out, "\t{}", self.name)?;490 Ok(())491 }492}493pub struct SolidityEnum {494 pub docs: &'static [&'static str],495 pub name: &'static str,496 pub fields: &'static [SolidityEnumVariant],497}498impl SolidityEnum {499 pub fn format(&self, out: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {500 for doc in self.docs {501 writeln!(out, "///{}", doc)?;502 }503 write!(out, "enum {} {{", self.name)?;504 for (i, field) in self.fields.iter().enumerate() {505 if i != 0 {506 write!(out, ",")?;507 }508 writeln!(out)?;509 field.solidity_name(out, tc)?;510 }511 writeln!(out)?;512 writeln!(out, "}}")?;513 Ok(())514 }515}