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 id: Cell<usize>,48}49impl TypeCollector {50 pub fn new() -> Self {51 Self::default()52 }53 pub fn collect(&self, item: string) {54 let id = self.next_id();55 self.structs.borrow_mut().insert(item, id);56 }57 pub fn next_id(&self) -> usize {58 let v = self.id.get();59 self.id.set(v + 1);60 v61 }62 pub fn collect_tuple<T: SolidityType>(&self) -> String {63 let names = T::names(self);64 if let Some(id) = self.anonymous.borrow().get(&names).cloned() {65 return format!("Tuple{}", id);66 }67 let id = self.next_id();68 let mut str = String::new();69 writeln!(str, "/// @dev anonymous struct").unwrap();70 writeln!(str, "struct Tuple{} {{", id).unwrap();71 for (i, name) in names.iter().enumerate() {72 writeln!(str, "\t{} field_{};", name, i).unwrap();73 }74 writeln!(str, "}}").unwrap();75 self.collect(str);76 self.anonymous.borrow_mut().insert(names, id);77 format!("Tuple{}", id)78 }79 pub fn collect_struct<T: StructCollect>(&self) -> String {80 self.collect(<T as StructCollect>::declaration());81 <T as StructCollect>::name()82 }83 pub fn finish(self) -> Vec<string> {84 let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();85 data.sort_by_key(|(_, id)| Reverse(*id));86 data.into_iter().map(|(code, _)| code).collect()87 }88}89#[derive(Default)]90pub struct UnnamedArgument<T>(PhantomData<*const T>);9192impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {93 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {94 if !T::is_void() {95 T::solidity_name(writer, tc)?;96 if !T::is_simple() {97 write!(writer, " memory")?;98 }99 Ok(())100 } else {101 Ok(())102 }103 }104 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {105 Ok(())106 }107 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {108 T::solidity_default(writer, tc)109 }110 fn len(&self) -> usize {111 if T::is_void() {112 0113 } else {114 1115 }116 }117}118119pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);120121impl<T> NamedArgument<T> {122 pub fn new(name: &'static str) -> Self {123 Self(name, Default::default())124 }125}126127impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {128 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {129 if !T::is_void() {130 T::solidity_name(writer, tc)?;131 if !T::is_simple() {132 write!(writer, " memory")?;133 }134 write!(writer, " {}", self.0)135 } else {136 Ok(())137 }138 }139 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {140 writeln!(writer, "\t{prefix}\t{};", self.0)141 }142 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {143 T::solidity_default(writer, tc)144 }145 fn len(&self) -> usize {146 if T::is_void() {147 0148 } else {149 1150 }151 }152}153154pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);155156impl<T> SolidityEventArgument<T> {157 pub fn new(indexed: bool, name: &'static str) -> Self {158 Self(indexed, name, Default::default())159 }160}161162impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {163 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {164 if !T::is_void() {165 T::solidity_name(writer, tc)?;166 if self.0 {167 write!(writer, " indexed")?;168 }169 write!(writer, " {}", self.1)170 } else {171 Ok(())172 }173 }174 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {175 writeln!(writer, "\t{prefix}\t{};", self.1)176 }177 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {178 T::solidity_default(writer, tc)179 }180 fn len(&self) -> usize {181 if T::is_void() {182 0183 } else {184 1185 }186 }187}188189impl SolidityArguments for () {190 fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {191 Ok(())192 }193 fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {194 Ok(())195 }196 fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {197 Ok(())198 }199 fn len(&self) -> usize {200 0201 }202}203204#[impl_for_tuples(1, 12)]205impl SolidityArguments for Tuple {206 for_tuples!( where #( Tuple: SolidityArguments ),* );207208 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {209 let mut first = true;210 for_tuples!( #(211 if !Tuple.is_empty() {212 if !first {213 write!(writer, ", ")?;214 }215 first = false;216 Tuple.solidity_name(writer, tc)?;217 }218 )* );219 Ok(())220 }221 fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {222 for_tuples!( #(223 Tuple.solidity_get(prefix, writer)?;224 )* );225 Ok(())226 }227 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {228 if self.is_empty() {229 Ok(())230 } else if self.len() == 1 {231 for_tuples!( #(232 Tuple.solidity_default(writer, tc)?;233 )* );234 Ok(())235 } else {236 write!(writer, "(")?;237 let mut first = true;238 for_tuples!( #(239 if !Tuple.is_empty() {240 if !first {241 write!(writer, ", ")?;242 }243 first = false;244 Tuple.solidity_default(writer, tc)?;245 }246 )* );247 write!(writer, ")")?;248 Ok(())249 }250 }251 fn len(&self) -> usize {252 for_tuples!( #( Tuple.len() )+* )253 }254}255256pub enum SolidityMutability {257 Pure,258 View,259 Mutable,260}261pub struct SolidityFunction<A, R> {262 pub docs: &'static [&'static str],263 pub selector: u32,264 pub hide: bool,265 pub custom_signature: SignatureUnit,266 pub name: &'static str,267 pub args: A,268 pub result: R,269 pub mutability: SolidityMutability,270 pub is_payable: bool,271}272impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {273 fn solidity_name(274 &self,275 is_impl: bool,276 writer: &mut impl fmt::Write,277 tc: &TypeCollector,278 ) -> fmt::Result {279 let hide_comment = self.hide.then(|| "// ").unwrap_or("");280 for doc in self.docs {281 writeln!(writer, "\t{hide_comment}///{}", doc)?;282 }283 writeln!(284 writer,285 "\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",286 self.selector287 )?;288 writeln!(289 writer,290 "\t{hide_comment}/// or in textual repr: {}",291 self.custom_signature.as_str().expect("bad utf-8")292 )?;293 write!(writer, "\t{hide_comment}function {}(", self.name)?;294 self.args.solidity_name(writer, tc)?;295 write!(writer, ")")?;296 if is_impl {297 write!(writer, " public")?;298 } else {299 write!(writer, " external")?;300 }301 match &self.mutability {302 SolidityMutability::Pure => write!(writer, " pure")?,303 SolidityMutability::View => write!(writer, " view")?,304 SolidityMutability::Mutable => {}305 }306 if self.is_payable {307 write!(writer, " payable")?;308 }309 if !self.result.is_empty() {310 write!(writer, " returns (")?;311 self.result.solidity_name(writer, tc)?;312 write!(writer, ")")?;313 }314 if is_impl {315 writeln!(writer, " {{")?;316 writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;317 self.args.solidity_get(hide_comment, writer)?;318 match &self.mutability {319 SolidityMutability::Pure => {}320 SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,321 SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,322 }323 if !self.result.is_empty() {324 write!(writer, "\t{hide_comment}\treturn ")?;325 self.result.solidity_default(writer, tc)?;326 writeln!(writer, ";")?;327 }328 writeln!(writer, "\t{hide_comment}}}")?;329 } else {330 writeln!(writer, ";")?;331 }332 if self.hide {333 writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;334 }335 Ok(())336 }337}338339#[impl_for_tuples(0, 48)]340impl SolidityFunctions for Tuple {341 for_tuples!( where #( Tuple: SolidityFunctions ),* );342343 fn solidity_name(344 &self,345 is_impl: bool,346 writer: &mut impl fmt::Write,347 tc: &TypeCollector,348 ) -> fmt::Result {349 let mut first = false;350 for_tuples!( #(351 Tuple.solidity_name(is_impl, writer, tc)?;352 )* );353 Ok(())354 }355}356357pub struct SolidityInterface<F: SolidityFunctions> {358 pub docs: &'static [&'static str],359 pub selector: bytes4,360 pub name: &'static str,361 pub is: &'static [&'static str],362 pub functions: F,363}364365impl<F: SolidityFunctions> SolidityInterface<F> {366 pub fn format(367 &self,368 is_impl: bool,369 out: &mut impl fmt::Write,370 tc: &TypeCollector,371 ) -> fmt::Result {372 const ZERO_BYTES: [u8; 4] = [0; 4];373 for doc in self.docs {374 writeln!(out, "///{}", doc)?;375 }376 if self.selector != ZERO_BYTES {377 writeln!(378 out,379 "/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",380 u32::from_be_bytes(self.selector)381 )?;382 }383 if is_impl {384 write!(out, "contract ")?;385 } else {386 write!(out, "interface ")?;387 }388 write!(out, "{}", self.name)?;389 if !self.is.is_empty() {390 write!(out, " is")?;391 for (i, n) in self.is.iter().enumerate() {392 if i != 0 {393 write!(out, ",")?;394 }395 write!(out, " {}", n)?;396 }397 }398 writeln!(out, " {{")?;399 self.functions.solidity_name(is_impl, out, tc)?;400 writeln!(out, "}}")?;401 Ok(())402 }403}404405pub struct SolidityEvent<A> {406 pub name: &'static str,407 pub args: A,408}409410impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {411 fn solidity_name(412 &self,413 _is_impl: bool,414 writer: &mut impl fmt::Write,415 tc: &TypeCollector,416 ) -> fmt::Result {417 write!(writer, "\tevent {}(", self.name)?;418 self.args.solidity_name(writer, tc)?;419 writeln!(writer, ");")420 }421}