From c0989353baf4b1120643206f027feda3def84839 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Tue, 12 Jul 2022 15:23:24 +0000 Subject: [PATCH] doc(evm-coder): document public api --- --- a/crates/evm-coder/Cargo.toml +++ b/crates/evm-coder/Cargo.toml @@ -5,15 +5,21 @@ edition = "2021" [dependencies] -evm-coder-macros = { path = "../evm-coder-macros" } +# evm-coder reexports those proc-macro +evm-coder-procedural = { path = "./procedural" } +# Evm uses primitive-types for H160, H256 and others primitive-types = { version = "0.11.1", default-features = false } -hex-literal = "0.3.3" +# Evm doesn't have reexports for log and others ethereum = { version = "0.12.0", default-features = false } +# Error types for execution evm-core = { default-features = false, git = "https://github.com/uniquenetwork/evm", branch = "unique-polkadot-v0.9.27" } -impl-trait-for-tuples = "0.2.1" +# We have tuple-heavy code in solidity.rs +impl-trait-for-tuples = "0.2.2" [dev-dependencies] +# We want to assert some large binary blobs equality in tests hex = "0.4.3" +hex-literal = "0.3.4" [features] default = ["std"] --- /dev/null +++ b/crates/evm-coder/README.md @@ -0,0 +1,15 @@ +# evm-coder + +Library for seamless call translation between Rust and Solidity code + +By encoding solidity definitions in Rust, this library also provides generation of +solidity interfaces for ethereum developers + +## Overview + +Most of this library functionality shouldn't be used directly, but via macros + +- [`solidity_interface`] +- [`ToLog`] + + \ No newline at end of file --- a/crates/evm-coder/procedural/Cargo.toml +++ b/crates/evm-coder/procedural/Cargo.toml @@ -8,9 +8,12 @@ proc-macro = true [dependencies] +# Ethereum uses keccak (=sha3) for selectors sha3 = "0.10.1" +# Value formatting +hex = "0.4.3" +Inflector = "0.11.4" +# General proc-macro utilities quote = "1.0" proc-macro2 = "1.0" syn = { version = "1.0", features = ["full"] } -hex = "0.4.3" -Inflector = "0.11.4" --- a/crates/evm-coder/procedural/src/lib.rs +++ b/crates/evm-coder/procedural/src/lib.rs @@ -21,8 +21,8 @@ use quote::quote; use sha3::{Digest, Keccak256}; use syn::{ - DeriveInput, GenericArgument, Ident, ItemImpl, Pat, Path, PathArguments, - PathSegment, Type, parse_macro_input, spanned::Spanned, + DeriveInput, GenericArgument, Ident, ItemImpl, Pat, Path, PathArguments, PathSegment, Type, + parse_macro_input, spanned::Spanned, }; mod solidity_interface; @@ -199,58 +199,7 @@ Ident::new(&name, ident.span()) } -/// Derives call enum implementing [`evm_coder::Callable`], [`evm_coder::Weighted`] -/// and [`evm_coder::Call`] from impl block -/// -/// ## Macro syntax -/// -/// `#[solidity_interface(name, is, inline_is, events)]` -/// - *name*: used in generated code, and for Call enum name -/// - *is*: used to provide call inheritance, not found methods will be delegated to all contracts -/// specified in is/inline_is -/// - *inline_is*: same as is, but selectors for passed contracts will be used by derived ERC165 -/// implementation -/// -/// `#[weight(value)]` -/// Can be added to every method of impl block, used for deriving [`evm_coder::Weighted`], which -/// is used by substrate bridge -/// - *value*: expression, which evaluates to weight required to call this method. -/// This expression can use call arguments to calculate non-constant execution time. -/// This expression should evaluate faster than actual execution does, and may provide worser case -/// than one is called -/// -/// `#[solidity_interface(rename_selector)]` -/// - *rename_selector*: by default, selector name will be generated by transforming method name -/// from snake_case to camelCase. Use this option, if other naming convention is required. -/// I.e: method `token_uri` will be automatically renamed to `tokenUri` in selector, but name -/// required by ERC721 standard is `tokenURI`, thus we need to specify `rename_selector = "tokenURI"` -/// explicitly -/// -/// Also, any contract method may have doc comments, which will be automatically added to generated -/// solidity interface definitions -/// -/// ## Example -/// -/// ```ignore -/// struct SuperContract; -/// struct InlineContract; -/// struct Contract; -/// -/// #[derive(ToLog)] -/// enum ContractEvents { -/// Event(#[indexed] uint32), -/// } -/// -/// #[solidity_interface(name = MyContract, is(SuperContract), inline_is(InlineContract))] -/// impl Contract { -/// /// Multiply two numbers -/// #[weight(200 + a + b)] -/// #[solidity_interface(rename_selector = "mul")] -/// fn mul(&mut self, a: uint32, b: uint32) -> Result { -/// Ok(a.checked_mul(b).ok_or("overflow")?) -/// } -/// } -/// ``` +/// See documentation for this proc-macro reexported in `evm-coder` crate #[proc_macro_attribute] pub fn solidity_interface(args: TokenStream, stream: TokenStream) -> TokenStream { let args = parse_macro_input!(args as solidity_interface::InterfaceInfo); @@ -282,10 +231,7 @@ stream } -/// ## Syntax -/// -/// `#[indexed]` -/// Marks this field as indexed, so it will appear in [`ethereum::Log`] topics instead of data +/// See documentation for this proc-macro reexported in `evm-coder` crate #[proc_macro_derive(ToLog, attributes(indexed))] pub fn to_log(value: TokenStream) -> TokenStream { let input = parse_macro_input!(value as DeriveInput); --- a/crates/evm-coder/src/abi.rs +++ b/crates/evm-coder/src/abi.rs @@ -14,8 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -//! TODO: I misunterstood therminology, abi IS rlp encoded, so -//! this module should be replaced with rlp crate +//! Implementation of EVM RLP reader/writer #![allow(dead_code)] @@ -32,6 +31,7 @@ const ABI_ALIGNMENT: usize = 32; +/// View into RLP data, which provides method to read typed items from it #[derive(Clone)] pub struct AbiReader<'i> { buf: &'i [u8], @@ -39,6 +39,7 @@ offset: usize, } impl<'i> AbiReader<'i> { + /// Start reading RLP buffer, assuming there is no padding bytes pub fn new(buf: &'i [u8]) -> Self { Self { buf, @@ -46,6 +47,7 @@ offset: 0, } } + /// Start reading RLP buffer, parsing first 4 bytes as selector pub fn new_call(buf: &'i [u8]) -> Result<(types::bytes4, Self)> { if buf.len() < 4 { return Err(Error::Error(ExitError::OutOfOffset)); @@ -109,10 +111,12 @@ ) } + /// Read [`H160`] at current position, then advance pub fn address(&mut self) -> Result { Ok(H160(self.read_padleft()?)) } + /// Read [`bool`] at current position, then advance pub fn bool(&mut self) -> Result { let data: [u8; 1] = self.read_padleft()?; match data[0] { @@ -122,49 +126,61 @@ } } + /// Read [`[u8; 4]`] at current position, then advance pub fn bytes4(&mut self) -> Result<[u8; 4]> { self.read_padright() } + /// Read [`Vec`] at current position, then advance pub fn bytes(&mut self) -> Result> { let mut subresult = self.subresult()?; - let length = subresult.read_usize()?; + let length = subresult.uint32()? as usize; if subresult.buf.len() < subresult.offset + length { return Err(Error::Error(ExitError::OutOfOffset)); } Ok(subresult.buf[subresult.offset..subresult.offset + length].into()) } + + /// Read [`string`] at current position, then advance pub fn string(&mut self) -> Result { string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange)) } + /// Read [`u8`] at current position, then advance pub fn uint8(&mut self) -> Result { Ok(self.read_padleft::<1>()?[0]) } + /// Read [`u32`] at current position, then advance pub fn uint32(&mut self) -> Result { Ok(u32::from_be_bytes(self.read_padleft()?)) } + /// Read [`u128`] at current position, then advance pub fn uint128(&mut self) -> Result { Ok(u128::from_be_bytes(self.read_padleft()?)) } + /// Read [`U256`] at current position, then advance pub fn uint256(&mut self) -> Result { let buf: [u8; 32] = self.read_padleft()?; Ok(U256::from_big_endian(&buf)) } + /// Read [`u64`] at current position, then advance pub fn uint64(&mut self) -> Result { Ok(u64::from_be_bytes(self.read_padleft()?)) } + /// Read [`usize`] at current position, then advance + #[deprecated = "dangerous, as usize may have different width in wasm and native execution"] pub fn read_usize(&mut self) -> Result { Ok(usize::from_be_bytes(self.read_padleft()?)) } + /// Slice recursive buffer, advance one word for buffer offset fn subresult(&mut self) -> Result> { - let offset = self.read_usize()?; + let offset = self.uint32()? as usize; if offset + self.subresult_offset > self.buf.len() { return Err(Error::Error(ExitError::InvalidRange)); } @@ -175,11 +191,13 @@ }) } + /// Is this parser reached end of buffer? pub fn is_finished(&self) -> bool { self.buf.len() == self.offset } } +/// Writer for RLP encoded data #[derive(Default)] pub struct AbiWriter { static_part: Vec, @@ -187,9 +205,11 @@ had_call: bool, } impl AbiWriter { + /// Initialize internal buffers for output data, assuming no padding required pub fn new() -> Self { Self::default() } + /// Initialize internal buffers, inserting method selector at beginning pub fn new_call(method_id: u32) -> Self { let mut val = Self::new(); val.static_part.extend(&method_id.to_be_bytes()); @@ -211,59 +231,71 @@ .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - bytes.len()]); } + /// Write [`H160`] to end of buffer pub fn address(&mut self, address: &H160) { self.write_padleft(&address.0) } + /// Write [`bool`] to end of buffer pub fn bool(&mut self, value: &bool) { self.write_padleft(&[if *value { 1 } else { 0 }]) } + /// Write [`u8`] to end of buffer pub fn uint8(&mut self, value: &u8) { self.write_padleft(&[*value]) } + /// Write [`u32`] to end of buffer pub fn uint32(&mut self, value: &u32) { self.write_padleft(&u32::to_be_bytes(*value)) } + /// Write [`u128`] to end of buffer pub fn uint128(&mut self, value: &u128) { self.write_padleft(&u128::to_be_bytes(*value)) } + /// Write [`U256`] to end of buffer pub fn uint256(&mut self, value: &U256) { let mut out = [0; 32]; value.to_big_endian(&mut out); self.write_padleft(&out) } + /// Write [`usize`] to end of buffer + #[deprecated = "dangerous, as usize may have different width in wasm and native execution"] pub fn write_usize(&mut self, value: &usize) { self.write_padleft(&usize::to_be_bytes(*value)) } + /// Append recursive data, writing pending offset at end of buffer pub fn write_subresult(&mut self, result: Self) { self.dynamic_part.push((self.static_part.len(), result)); // Empty block, to be filled later self.write_padleft(&[]); } - pub fn memory(&mut self, value: &[u8]) { + fn memory(&mut self, value: &[u8]) { let mut sub = Self::new(); - sub.write_usize(&value.len()); + sub.uint32(&(value.len() as u32)); for chunk in value.chunks(ABI_ALIGNMENT) { sub.write_padright(chunk); } self.write_subresult(sub); } + /// Append recursive [`str`] at end of buffer pub fn string(&mut self, value: &str) { self.memory(value.as_bytes()) } + /// Append recursive [`[u8]`] at end of buffer pub fn bytes(&mut self, value: &[u8]) { self.memory(value) } + /// Finish writer, concatenating all internal buffers pub fn finish(mut self) -> Vec { for (static_offset, part) in self.dynamic_part { let part_offset = self.static_part.len() - self.had_call.then(|| 4).unwrap_or(0); @@ -278,7 +310,13 @@ } } +/// [`AbiReader`] implements reading of many types, but it should +/// be limited to types defined in spec +/// +/// As this trait can't be made sealed, +/// instead of having `impl AbiRead for T`, we have `impl AbiRead for AbiReader` pub trait AbiRead { + /// Read item from current position, advanding decoder fn abi_read(&mut self) -> Result; } @@ -318,7 +356,7 @@ { fn abi_read(&mut self) -> Result> { let mut sub = self.subresult()?; - let size = sub.read_usize()?; + let size = sub.uint32()? as usize; sub.subresult_offset = sub.offset; let mut out = Vec::with_capacity(size); for _ in 0..size { @@ -366,8 +404,13 @@ impl_tuples! {A B C D E F G H I} impl_tuples! {A B C D E F G H I J} +/// For questions about inability to provide custom implementations, +/// see [`AbiRead`] pub trait AbiWrite { + /// Write value to end of specified encoder fn abi_write(&self, writer: &mut AbiWriter); + /// Specialization for [`crate::solidity_interface`] implementation, + /// see comment in `impl AbiWrite for ResultWithPostInfo` fn to_result(&self) -> ResultWithPostInfo { let mut writer = AbiWriter::new(); self.abi_write(&mut writer); @@ -375,13 +418,11 @@ } } +/// This particular AbiWrite implementation should be split to another trait, +/// which only implements `to_result`, but due to lack of specialization feature +/// in stable Rust, we can't have blanket impl of this trait `for T where T: AbiWrite`, +/// so here we abusing default trait methods for it impl AbiWrite for ResultWithPostInfo { - // this particular AbiWrite implementation should be split to another trait, - // which only implements [`to_result`] - // - // But due to lack of specialization feature in stable Rust, we can't have - // blanket impl of this trait `for T where T: AbiWrite`, so here we abusing - // default trait methods for it fn abi_write(&self, _writer: &mut AbiWriter) { debug_assert!(false, "shouldn't be called, see comment") } @@ -432,6 +473,8 @@ fn abi_write(&self, _writer: &mut AbiWriter) {} } +/// Helper macros to parse reader into variables +#[deprecated] #[macro_export] macro_rules! abi_decode { ($reader:expr, $($name:ident: $typ:ident),+ $(,)?) => { @@ -440,6 +483,9 @@ )+ } } + +/// Helper macros to construct RLP-encoded buffer +#[deprecated] #[macro_export] macro_rules! abi_encode { ($($typ:ident($value:expr)),* $(,)?) => {{ --- a/crates/evm-coder/src/events.rs +++ b/crates/evm-coder/src/events.rs @@ -19,11 +19,23 @@ use crate::types::*; +/// Implementation of this trait should not be written manually, +/// instead use [`crate::ToLog`] proc macros. +/// +/// See also [`evm_coder_procedural::ToLog`], [solidity docs on events](https://docs.soliditylang.org/en/develop/contracts.html#events) pub trait ToLog { + /// Convert event to [`ethereum::Log`]. + /// Because event by itself doesn't contains current contract + /// address, it should be specified manually. fn to_log(&self, contract: H160) -> Log; } +/// Only items implementing `ToTopic` may be used as `#[indexed]` field +/// in [`crate::ToLog`] macro usage. +/// +/// See also (solidity docs on events)[] pub trait ToTopic { + /// Convert value to topic to be used in [`ethereum::Log`] fn to_topic(&self) -> H256; } --- a/crates/evm-coder/src/execution.rs +++ b/crates/evm-coder/src/execution.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . +//! Contract execution related types + #[cfg(not(feature = "std"))] use alloc::string::{String, ToString}; use evm_core::{ExitError, ExitFatal}; @@ -22,10 +24,14 @@ use crate::Weight; +/// Execution error, should be convertible between EVM and Substrate. #[derive(Debug, Clone)] pub enum Error { + /// Non-fatal contract error occured Revert(String), + /// EVM fatal error Fatal(ExitFatal), + /// EVM normal error Error(ExitError), } @@ -38,9 +44,12 @@ } } +/// To be used in [`crate::solidity_interface`] implementation. pub type Result = core::result::Result; +/// Static information collected from [`crate::weight`]. pub struct DispatchInfo { + /// Statically predicted call weight pub weight: Weight, } @@ -55,16 +64,22 @@ } } +/// Weight information that is only available post dispatch. +/// Note: This can only be used to reduce the weight or fee, not increase it. #[derive(Default, Clone)] pub struct PostDispatchInfo { + /// Actual weight consumed by call actual_weight: Option, } impl PostDispatchInfo { + /// Calculate amount to be returned back to user pub fn calc_unspent(&self, info: &DispatchInfo) -> Weight { info.weight - self.calc_actual_weight(info) } + /// Calculate actual cansumed weight, saturating to weight reported + /// pre-dispatch pub fn calc_actual_weight(&self, info: &DispatchInfo) -> Weight { if let Some(actual_weight) = self.actual_weight { actual_weight.min(info.weight) @@ -74,9 +89,12 @@ } } +/// Wrapper for PostDispatchInfo and any user-provided data #[derive(Clone)] pub struct WithPostDispatchInfo { + /// User provided data pub data: T, + /// Info known after dispatch pub post_info: PostDispatchInfo, } @@ -89,5 +107,6 @@ } } +/// Return type of items in [`crate::solidity_interface`] definition pub type ResultWithPostInfo = core::result::Result, WithPostDispatchInfo>; --- a/crates/evm-coder/src/lib.rs +++ b/crates/evm-coder/src/lib.rs @@ -14,22 +14,102 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] extern crate alloc; use abi::{AbiRead, AbiReader, AbiWriter}; -pub use evm_coder_macros::{event_topic, fn_selector, solidity_interface, solidity, weight, ToLog}; +pub use evm_coder_procedural::{event_topic, fn_selector}; pub mod abi; -pub mod events; -pub use events::ToLog; +pub use events::{ToLog, ToTopic}; use execution::DispatchInfo; pub mod execution; + +/// Derives call enum implementing [`crate::Callable`], [`crate::Weighted`] +/// and [`crate::Call`] from impl block. +/// +/// ## Macro syntax +/// +/// `#[solidity_interface(name, is, inline_is, events)]` +/// - *name* - used in generated code, and for Call enum name +/// - *is* - used to provide call inheritance, not found methods will be delegated to all contracts +/// specified in is/inline_is +/// - *inline_is* - same as is, but selectors for passed contracts will be used by derived ERC165 +/// implementation +/// +/// `#[weight(value)]` +/// Can be added to every method of impl block, used for deriving [`crate::Weighted`], which +/// is used by substrate bridge. +/// - *value*: expression, which evaluates to weight required to call this method. +/// This expression can use call arguments to calculate non-constant execution time. +/// This expression should evaluate faster than actual execution does, and may provide worser case +/// than one is called. +/// +/// `#[solidity_interface(rename_selector)]` +/// - *rename_selector* - by default, selector name will be generated by transforming method name +/// from snake_case to camelCase. Use this option, if other naming convention is required. +/// I.e: method `token_uri` will be automatically renamed to `tokenUri` in selector, but name +/// required by ERC721 standard is `tokenURI`, thus we need to specify `rename_selector = "tokenURI"` +/// explicitly. +/// +/// Both contract and contract methods may have doccomments, which will end up in a generated +/// solidity interface file, thus you should use [solidity syntax](https://docs.soliditylang.org/en/latest/natspec-format.html) for writing documentation in this macro +/// +/// ## Example +/// +/// ```ignore +/// struct SuperContract; +/// struct InlineContract; +/// struct Contract; +/// +/// #[derive(ToLog)] +/// enum ContractEvents { +/// Event(#[indexed] uint32), +/// } +/// +/// /// @dev This contract provides function to multiply two numbers +/// #[solidity_interface(name = MyContract, is(SuperContract), inline_is(InlineContract))] +/// impl Contract { +/// /// Multiply two numbers +/// /// @param a First number +/// /// @param b Second number +/// /// @return uint32 Product of two passed numbers +/// /// @dev This function returns error in case of overflow +/// #[weight(200 + a + b)] +/// #[solidity_interface(rename_selector = "mul")] +/// fn mul(&mut self, a: uint32, b: uint32) -> Result { +/// Ok(a.checked_mul(b).ok_or("overflow")?) +/// } +/// } +/// ``` +pub use evm_coder_procedural::solidity_interface; +/// See [`solidity_interface`] +pub use evm_coder_procedural::solidity; +/// See [`solidity_interface`] +pub use evm_coder_procedural::weight; + +/// Derives [`ToLog`] for enum +/// +/// Selectors will be derived from variant names, there is currently no way to have custom naming +/// for them +/// +/// `#[indexed]` +/// Marks this field as indexed, so it will appear in [`ethereum::Log`] topics instead of data +pub use evm_coder_procedural::ToLog; + +// Api of those modules shouldn't be consumed directly, it is only exported for usage in proc macros +#[doc(hidden)] +pub mod events; +#[doc(hidden)] pub mod solidity; -/// Solidity type definitions +/// Solidity type definitions (aliases from solidity name to rust type) +/// To be used in [`solidity_interface`] definitions, to make sure there is no +/// type conflict between Rust code and generated definitions pub mod types { - #![allow(non_camel_case_types)] + #![allow(non_camel_case_types, missing_docs)] #[cfg(not(feature = "std"))] use alloc::{vec::Vec}; @@ -54,6 +134,8 @@ pub type string = ::std::string::String; pub type bytes = Vec; + /// Solidity doesn't have `void` type, however we have special implementation + /// for empty tuple return type pub type void = (); //#region Special types @@ -63,36 +145,64 @@ pub type caller = address; //#endregion + /// Ethereum typed call message, similar to solidity + /// `msg` object. pub struct Msg { pub call: C, + /// Address of user, which called this contract. pub caller: H160, + /// Payment amount to contract. + /// Contract should reject payment, if target call is not payable, + /// and there is no `receiver()` function defined. pub value: U256, } } +/// Parseable EVM call, this trait should be implemented with [`solidity_interface`] macro pub trait Call: Sized { + /// Parse call buffer into typed call enum fn parse(selector: types::bytes4, input: &mut AbiReader) -> execution::Result>; } +/// Intended to be used as `#[weight]` output type +/// Should be same between evm-coder and substrate to avoid confusion +/// +/// Isn't same thing as gas, some mapping is required between those types pub type Weight = u64; +/// In substrate, we have benchmarking, which allows +/// us to not rely on gas metering, but instead predict amount of gas to execute call pub trait Weighted: Call { + /// Predict weight of this call fn weight(&self) -> DispatchInfo; } +/// Type callable with ethereum message, may be implemented by [`solidity_interface`] macro +/// on interface implementation, or for externally-owned real EVM contract pub trait Callable { + /// Call contract using specified call data fn call(&mut self, call: types::Msg) -> execution::ResultWithPostInfo; } -/// Implementation is implicitly provided for all interfaces +/// Implementation of ERC165 is implicitly generated for all interfaces in [`solidity_interface`], +/// this structure holds parsed data for ERC165Call subvariant +/// +/// Note: no [`Callable`] implementation is provided, call implementation is inlined into every +/// implementing contract /// -/// Note: no Callable implementation is provided +/// See #[derive(Debug)] pub enum ERC165Call { - SupportsInterface { interface_id: types::bytes4 }, + /// ERC165 provides single method, which returns true, if contract + /// implements specified interface + SupportsInterface { + /// Requested interface + interface_id: types::bytes4, + }, } impl ERC165Call { + /// ERC165 selector is provided by standard pub const INTERFACE_ID: types::bytes4 = u32::to_be_bytes(0x01ffc9a7); } --- a/crates/evm-coder/src/solidity.rs +++ b/crates/evm-coder/src/solidity.rs @@ -14,15 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -//! Implementation detail of [`evm_coder::solidity_interface`] macro code-generation +//! Implementation detail of [`crate::solidity_interface`] macro code-generation. +//! You should not rely on any public item from this module, as it is only intended to be used +//! by procedural macro, API and output format may be changed at any time. +//! +//! Purpose of this module is to receive solidity contract definition in module-specified +//! format, and then output string, representing interface of this contract in solidity language #[cfg(not(feature = "std"))] -use alloc::{ - string::String, - vec::Vec, - collections::BTreeMap, - format, -}; +use alloc::{string::String, vec::Vec, collections::BTreeMap, format}; #[cfg(feature = "std")] use std::collections::BTreeMap; use core::{ @@ -81,8 +81,10 @@ pub trait SolidityTypeName: 'static { fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result; + /// "simple" types are stored inline, no `memory` modifier should be used in solidity fn is_simple() -> bool; fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result; + /// Specialization fn is_void() -> bool { false } @@ -133,6 +135,10 @@ } mod sealed { + /// Not every type should be directly placed in vec. + /// Vec encoding is not memory efficient, as every item will be padded + /// to 32 bytes. + /// Instead you should use specialized types (`bytes` in case of `Vec`) pub trait CanBePlacedInVec {} } @@ -427,7 +433,11 @@ for doc in self.docs { writeln!(writer, "\t///{}", doc)?; } - writeln!(writer, "\t/// @dev EVM selector for this function is: 0x{:0>8x},", self.selector)?; + writeln!( + writer, + "\t/// @dev EVM selector for this function is: 0x{:0>8x},", + self.selector + )?; writeln!(writer, "\t/// or in textual repr: {}", self.selector_str)?; write!(writer, "\tfunction {}(", self.name)?; self.args.solidity_name(writer, tc)?; -- gitstuff