From 88ef37f68305aed254f2580d7ff3ac500ebe0c1c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 7 Oct 2023 22:14:25 +0200 Subject: [PATCH] feat(sol-types): introduce `SolValue`, make `Encodable` an impl detail (#333) * feat(sol-types): make Encodable actually work * add more impls to both encodables * chore: clippy * fixtest * fixstable * tuple perfington * cleanup * from * officially make SolTypeEncodable an implementation detail * docs * impl Encodable for sol! SolTypes * update examples * rename to SolValue * update docs * rename value module * rename SolTypeEncodable to SolTypeValue * rename SolTypeValue methods to avoid naming collisions * update event doc * tweaks * final tweaks --- .github/ISSUE_TEMPLATE/BUG-FORM.yml | 9 +- .github/ISSUE_TEMPLATE/FEATURE-FORM.yml | 61 +- crates/dyn-abi/src/ext/abi.rs | 1 - crates/dyn-abi/src/resolve.rs | 4 +- crates/sol-macro/src/expand/enum.rs | 33 +- crates/sol-macro/src/expand/mod.rs | 13 - crates/sol-macro/src/expand/struct.rs | 51 +- crates/sol-types/src/abi/decoder.rs | 26 +- crates/sol-types/src/abi/encoder.rs | 60 +- crates/sol-types/src/abi/mod.rs | 34 +- crates/sol-types/src/abi/token.rs | 41 +- crates/sol-types/src/eip712.rs | 29 +- crates/sol-types/src/errors.rs | 7 +- crates/sol-types/src/lib.rs | 51 +- crates/sol-types/src/macros.rs | 48 +- crates/sol-types/src/types/data_type.rs | 563 +++++++++++------- crates/sol-types/src/types/enum.rs | 8 +- crates/sol-types/src/types/error.rs | 8 +- crates/sol-types/src/types/event/mod.rs | 8 +- crates/sol-types/src/types/event/topic.rs | 10 +- .../sol-types/src/types/event/topic_list.rs | 6 + crates/sol-types/src/types/function.rs | 15 +- crates/sol-types/src/types/interface.rs | 8 +- crates/sol-types/src/types/mod.rs | 5 +- crates/sol-types/src/types/struct.rs | 6 +- crates/sol-types/src/types/ty.rs | 250 ++++---- crates/sol-types/src/types/udt.rs | 26 +- crates/sol-types/src/types/value.rs | 407 +++++++++++++ crates/sol-types/tests/ui/type.stderr | 29 - crates/sol-types/type_system.md | 65 +- crates/syn-solidity/src/spanned.rs | 4 +- 31 files changed, 1226 insertions(+), 660 deletions(-) create mode 100644 crates/sol-types/src/types/value.rs diff --git a/.github/ISSUE_TEMPLATE/BUG-FORM.yml b/.github/ISSUE_TEMPLATE/BUG-FORM.yml index fd3fba5974..b4d328579a 100644 --- a/.github/ISSUE_TEMPLATE/BUG-FORM.yml +++ b/.github/ISSUE_TEMPLATE/BUG-FORM.yml @@ -15,12 +15,13 @@ body: description: What component is the bug in? multiple: true options: - - primitives - - syn-solidity - - sol-type - - sol! macro - dyn-abi - json-abi + - primitives + - sol-type-parser + - sol-types + - sol! macro + - syn-solidity - Other (please provide more details) validations: required: true diff --git a/.github/ISSUE_TEMPLATE/FEATURE-FORM.yml b/.github/ISSUE_TEMPLATE/FEATURE-FORM.yml index 022707816d..d5509fc13e 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE-FORM.yml +++ b/.github/ISSUE_TEMPLATE/FEATURE-FORM.yml @@ -3,32 +3,35 @@ description: Suggest a feature labels: ["enhancement"] title: "[Feature] " body: - - type: markdown - attributes: - value: | - Please ensure that the feature has not already been requested in the issue tracker. - - type: dropdown - attributes: - label: Component - description: What component is the feature for? - multiple: true - options: - - primitives - - syn-solidity - - sol-type - - sol! macro - - dyn-abi - - json-abi - - Other (please provide more details) - validations: - required: true - - type: textarea - attributes: - label: Describe the feature you would like - description: Please also describe your goals for the feature. What problems it solves, how it would be used, etc. - validations: - required: true - - type: textarea - attributes: - label: Additional context - description: Add any other context to the feature (like screenshots, resources) + - type: markdown + attributes: + value: | + Please ensure that the feature has not already been requested in the issue tracker. + - type: dropdown + attributes: + label: Component + description: What component is the feature for? + multiple: true + options: + - dyn-abi + - json-abi + - primitives + - sol-type-parser + - sol-types + - sol! macro + - syn-solidity + - Other (please provide more details) + validations: + required: true + - type: textarea + attributes: + label: Describe the feature you would like + description: + Please also describe your goals for the feature. What problems it solves, how it would + be used, etc. + validations: + required: true + - type: textarea + attributes: + label: Additional context + description: Add any other context to the feature (like screenshots, resources) diff --git a/crates/dyn-abi/src/ext/abi.rs b/crates/dyn-abi/src/ext/abi.rs index cfa176328a..d6ce3301a7 100644 --- a/crates/dyn-abi/src/ext/abi.rs +++ b/crates/dyn-abi/src/ext/abi.rs @@ -16,7 +16,6 @@ use sealed::Sealed; /// /// This trait is sealed and cannot be implemented for types outside of this /// crate. It is implemented only for the following types: -/// /// - [`Constructor`] /// - [`Error`] /// - [`Function`] diff --git a/crates/dyn-abi/src/resolve.rs b/crates/dyn-abi/src/resolve.rs index aecedb9bb4..fcd04ec9ff 100644 --- a/crates/dyn-abi/src/resolve.rs +++ b/crates/dyn-abi/src/resolve.rs @@ -219,7 +219,7 @@ fn tuple(slice: &[T]) -> Result> { Ok(types) } -macro_rules! deref_impl { +macro_rules! deref_impls { ($($(#[$attr:meta])* [$($gen:tt)*] $t:ty),+ $(,)?) => {$( $(#[$attr])* impl<$($gen)*> ResolveSolType for $t { @@ -231,7 +231,7 @@ macro_rules! deref_impl { )+}; } -deref_impl! { +deref_impls! { [] alloc::string::String, [T: ?Sized + ResolveSolType] &T, [T: ?Sized + ResolveSolType] &mut T, diff --git a/crates/sol-macro/src/expand/enum.rs b/crates/sol-macro/src/expand/enum.rs index 1804f6e7e5..f990d15d26 100644 --- a/crates/sol-macro/src/expand/enum.rs +++ b/crates/sol-macro/src/expand/enum.rs @@ -112,11 +112,26 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result } #[automatically_derived] - impl ::alloy_sol_types::Encodable<#name> for #name { + impl ::alloy_sol_types::SolValue for #name { + type SolType = Self; + } + + #[automatically_derived] + impl ::alloy_sol_types::private::SolTypeValue<#name> for #name { #[inline] - fn to_tokens(&self) -> #uint8_st::TokenType<'_> { + fn stv_to_tokens(&self) -> #uint8_st::TokenType<'_> { ::alloy_sol_types::Word::with_last_byte(*self as u8).into() } + + #[inline] + fn stv_eip712_data_word(&self) -> ::alloy_sol_types::Word { + #uint8_st::eip712_data_word(self.as_u8()) + } + + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut ::alloy_sol_types::private::Vec) { + out.push(*self as u8); + } } #[automatically_derived] @@ -150,16 +165,6 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result #uint8_st::detokenize(token) ).#detokenize_unwrap } - - #[inline] - fn eip712_data_word(rust: &Self::RustType) -> ::alloy_sol_types::Word { - #uint8_st::eip712_data_word(rust.as_u8()) - } - - #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut ::alloy_sol_types::private::Vec) { - out.push(*rust as u8); - } } #[automatically_derived] @@ -175,9 +180,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result } #[inline] - fn encode_topic( - rust: &Self::RustType - ) -> ::alloy_sol_types::abi::token::WordToken { + fn encode_topic(rust: &Self::RustType) -> ::alloy_sol_types::abi::token::WordToken { <#uint8 as ::alloy_sol_types::EventTopic>::encode_topic(rust.as_u8()) } } diff --git a/crates/sol-macro/src/expand/mod.rs b/crates/sol-macro/src/expand/mod.rs index 0c92d451b0..a3041d42a2 100644 --- a/crates/sol-macro/src/expand/mod.rs +++ b/crates/sol-macro/src/expand/mod.rs @@ -546,9 +546,6 @@ fn expand_from_into_tuples

(name: &Ident, fields: &Parameters

) -> TokenStre let names2 = names.clone(); let idxs = (0..fields.len()).map(syn::Index::from); - let names3 = names.clone(); - let field_tys = fields.types().map(expand_type); - let (sol_tuple, rust_tuple) = expand_tuple_types(fields.types()); quote! { @@ -574,16 +571,6 @@ fn expand_from_into_tuples

(name: &Ident, fields: &Parameters

) -> TokenStre } } } - - #[automatically_derived] - #[doc(hidden)] - impl ::alloy_sol_types::Encodable> for #name { - fn to_tokens(&self) -> as ::alloy_sol_types::SolType>::TokenType<'_> { - (#( - ::alloy_sol_types::Encodable::<#field_tys>::to_tokens(&self.#names3), - )*) - } - } } } diff --git a/crates/sol-macro/src/expand/struct.rs b/crates/sol-macro/src/expand/struct.rs index 8ddb10834a..0e3273276e 100644 --- a/crates/sol-macro/src/expand/struct.rs +++ b/crates/sol-macro/src/expand/struct.rs @@ -77,10 +77,34 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { #convert #[automatically_derived] - impl ::alloy_sol_types::Encodable for #name { - fn to_tokens(&self) -> ::TokenType<'_> { + impl ::alloy_sol_types::SolValue for #name { + type SolType = Self; + } + + #[automatically_derived] + impl ::alloy_sol_types::private::SolTypeValue for #name { + fn stv_to_tokens(&self) -> ::TokenType<'_> { #tokenize_impl } + + #[inline] + fn stv_abi_encoded_size(&self) -> usize { + // TODO: Avoid cloning + let tuple = as ::core::convert::From>::from(self.clone()); + as ::alloy_sol_types::SolType>::abi_encoded_size(&tuple) + } + + #[inline] + fn stv_eip712_data_word(&self) -> ::alloy_sol_types::Word { + ::eip712_hash_struct(self) + } + + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut ::alloy_sol_types::private::Vec) { + // TODO: Avoid cloning + let tuple = as ::core::convert::From>::from(self.clone()); + as ::alloy_sol_types::SolType>::abi_encode_packed_to(&tuple, out) + } } #[automatically_derived] @@ -95,13 +119,6 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { ) } - #[inline] - fn abi_encoded_size(rust: &Self::RustType) -> usize { - // TODO: Avoid cloning - let tuple = as ::core::convert::From>::from(rust.clone()); - as ::alloy_sol_types::SolType>::abi_encoded_size(&tuple) - } - #[inline] fn valid_token(token: &Self::TokenType<'_>) -> bool { as ::alloy_sol_types::SolType>::valid_token(token) @@ -112,18 +129,6 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { let tuple = as ::alloy_sol_types::SolType>::detokenize(token); >>::from(tuple) } - - #[inline] - fn eip712_data_word(rust: &Self::RustType) -> ::alloy_sol_types::Word { - ::eip712_hash_struct(rust) - } - - #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut ::alloy_sol_types::private::Vec) { - // TODO: Avoid cloning - let tuple = as ::core::convert::From>::from(rust.clone()); - as ::alloy_sol_types::SolType>::abi_encode_packed_to(&tuple, out) - } } #[automatically_derived] @@ -156,9 +161,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { } #[inline] - fn encode_topic( - rust: &Self::RustType - ) -> ::alloy_sol_types::abi::token::WordToken { + fn encode_topic(rust: &Self::RustType) -> ::alloy_sol_types::abi::token::WordToken { let mut out = ::alloy_sol_types::private::Vec::new(); ::encode_topic_preimage(rust, &mut out); ::alloy_sol_types::abi::token::WordToken( diff --git a/crates/sol-types/src/abi/decoder.rs b/crates/sol-types/src/abi/decoder.rs index a7de25f206..1f04f9825c 100644 --- a/crates/sol-types/src/abi/decoder.rs +++ b/crates/sol-types/src/abi/decoder.rs @@ -239,11 +239,13 @@ impl<'de> Decoder<'de> { } } -/// ABI-decodes a single token by wrapping it in a single-element tuple. +/// ABI-decodes a token by wrapping it in a single-element tuple. /// -/// You should probably be using -/// [`SolType::abi_decode`](crate::SolType::abi_decode) if you're not intending -/// to use raw tokens. +/// You are probably looking for +/// [`SolValue::abi_decode`](crate::SolValue::abi_decode) if you are not +/// intending to use raw tokens. +/// +/// See the [`abi`](super) module for more information. #[inline] pub fn decode<'de, T: TokenType<'de>>(data: &'de [u8], validate: bool) -> Result { decode_sequence::<(T,)>(data, validate).map(|(t,)| t) @@ -254,9 +256,11 @@ pub fn decode<'de, T: TokenType<'de>>(data: &'de [u8], validate: bool) -> Result /// Decodes as function parameters if [`T` is a tuple](TokenSeq::IS_TUPLE). /// Otherwise, decodes it as a single-element tuple. /// -/// You should probably be using -/// [`SolType::abi_decode_params`](crate::SolType::abi_decode_params) if you're -/// not intending to use raw tokens. +/// You are probably looking for +/// [`SolValue::abi_decode_params`](crate::SolValue::abi_decode_params) if +/// you are not intending to use raw tokens. +/// +/// See the [`abi`](super) module for more information. #[inline] pub fn decode_params<'de, T: TokenSeq<'de>>(data: &'de [u8], validate: bool) -> Result { if T::IS_TUPLE { @@ -269,9 +273,11 @@ pub fn decode_params<'de, T: TokenSeq<'de>>(data: &'de [u8], validate: bool) -> /// Decodes ABI compliant vector of bytes into vector of tokens described by /// types param. /// -/// You should probably be using -/// [`SolType::abi_decode_sequence`](crate::SolType::abi_decode_sequence) if -/// you're not intending to use raw tokens. +/// You are probably looking for +/// [`SolValue::abi_decode_sequence`](crate::SolValue::abi_decode_sequence) if +/// you are not intending to use raw tokens. +/// +/// See the [`abi`](super) module for more information. pub fn decode_sequence<'de, T: TokenSeq<'de>>(data: &'de [u8], validate: bool) -> Result { let mut decoder = Decoder::new(data, validate); let res = decoder.decode_sequence::()?; diff --git a/crates/sol-types/src/abi/encoder.rs b/crates/sol-types/src/abi/encoder.rs index de38ef2294..980cf5ce6e 100644 --- a/crates/sol-types/src/abi/encoder.rs +++ b/crates/sol-types/src/abi/encoder.rs @@ -158,23 +158,26 @@ impl Encoder { } } -/// ABI-encode a token sequence. -pub fn encode_sequence<'a, T: TokenSeq<'a>>(tokens: &T) -> Vec { - let mut enc = Encoder::with_capacity(tokens.total_words()); - enc.append_head_tail(tokens); - enc.into_bytes() -} - -/// ABI-encode a single token. +/// ABI-encodes a single token. +/// +/// You are probably looking for +/// [`SolValue::abi_encode`](crate::SolValue::abi_encode) if +/// you are not intending to use raw tokens. +/// +/// See the [`abi`](super) module for more information. #[inline] pub fn encode<'a, T: TokenType<'a>>(token: &T) -> Vec { - // Same as [`core::array::from_ref`]. - // SAFETY: Converting `&T` to `&(T,)` is sound. - encode_sequence::<(T,)>(unsafe { &*(token as *const T).cast::<(T,)>() }) + encode_sequence::<(T,)>(tuple_from_ref(token)) } -/// ABI-encode a tuple as ABI function params, suitable for passing to a +/// ABI-encodes a tuple as ABI function params, suitable for passing to a /// function. +/// +/// You are probably looking for +/// [`SolValue::abi_encode_params`](crate::SolValue::abi_encode_params) if +/// you are not intending to use raw tokens. +/// +/// See the [`abi`](super) module for more information. #[inline] pub fn encode_params<'a, T: TokenSeq<'a>>(token: &T) -> Vec { if T::IS_TUPLE { @@ -184,10 +187,33 @@ pub fn encode_params<'a, T: TokenSeq<'a>>(token: &T) -> Vec { } } +/// ABI-encodes a token sequence. +/// +/// You are probably looking for +/// [`SolValue::abi_encode_sequence`](crate::SolValue::abi_encode_sequence) if +/// you are not intending to use raw tokens. +/// +/// See the [`abi`](super) module for more information. +pub fn encode_sequence<'a, T: TokenSeq<'a>>(tokens: &T) -> Vec { + let mut enc = Encoder::with_capacity(tokens.total_words()); + enc.append_head_tail(tokens); + enc.into_bytes() +} + +/// Converts a reference to `T` into a reference to a tuple of length 1 (without +/// copying). +/// +/// Same as [`core::array::from_ref`]. +#[inline(always)] +fn tuple_from_ref(s: &T) -> &(T,) { + // SAFETY: Converting `&T` to `&(T,)` is sound. + unsafe { &*(s as *const T).cast::<(T,)>() } +} + #[cfg(test)] mod tests { use crate::{sol_data, SolType}; - use alloc::{borrow::ToOwned, string::ToString}; + use alloc::{borrow::ToOwned, string::ToString, vec::Vec}; use alloy_primitives::{hex, Address, U256}; #[test] @@ -448,7 +474,7 @@ mod tests { fn encode_empty_array() { type MyTy0 = sol_data::Array; - let data = vec![]; + let data: Vec

= vec![]; // Empty arrays let encoded = MyTy0::abi_encode_params(&data); @@ -467,7 +493,7 @@ mod tests { sol_data::Array, sol_data::Array, ); - let data = (vec![], vec![]); + let data: (Vec
, Vec
) = (vec![], vec![]); let expected = hex!( " @@ -493,7 +519,7 @@ mod tests { sol_data::Array>, ); - let data = (vec![vec![]], vec![vec![]]); + let data: (Vec>, Vec>) = (vec![vec![]], vec![vec![]]); // Nested empty arrays let expected = hex!( @@ -546,7 +572,7 @@ mod tests { assert_eq!(encoded, expected); assert_eq!( encoded.len(), - sol_data::FixedBytes::<2>::abi_encoded_size(&[0x12, 0x34].into()) + sol_data::FixedBytes::<2>::abi_encoded_size(&[0x12, 0x34]) ); } diff --git a/crates/sol-types/src/abi/mod.rs b/crates/sol-types/src/abi/mod.rs index 1007fca3d2..6c37c7cb73 100644 --- a/crates/sol-types/src/abi/mod.rs +++ b/crates/sol-types/src/abi/mod.rs @@ -1,28 +1,38 @@ -//! Ethereum ABI encoding. +//! Ethereum ABI codec implementation. +//! +//! This module provides the low-level ABI [`Encoder`] and [`Decoder`] structs, +//! along with generic functions for their operation. These utilize an +//! intermediate representation, referred to as tokens. For additional +//! information about tokens, see the [`token`] module documentation. +//! +//! You should not need this module in most cases, as the +//! [`SolType`](crate::SolType) and [`SolValue`](crate::SolValue) traits +//! provide a higher-level and easier to use interface. If you're sure you need +//! the low-level functionality of this module, there are three main interfaces: //! //! ### `{encode,decode}` //! -//! [`crate::SolType::abi_encode()`] and [`encode()`] operate on a -//! single token. They wrap this token in a tuple, and pass it to the encoder. -//! Use this interface when abi-encoding a single token. This is suitable for +//! [`encode`] operates on a single token. It wrap this token in a +//! single-element tuple, and passes it to the encoder. Similarly, [`decode`] +//! decodes a single token from a blob by decoding a single-element tuple. +//! +//! Use this interface when ABI-encoding a single token. This is suitable for //! encoding a type in isolation, or for encoding parameters for single-param //! functions. //! //! ### `{encode,decode}_params` //! -//! [`crate::SolType::abi_encode_params()`] and [`encode_params()`] operate on a -//! sequence. If the sequence is a tuple, the tuple is inferred to be a set of -//! Solidity function parameters, -//! -//! The corresponding [`crate::SolType::abi_decode_params()`] and -//! [`decode_params()`] reverse this operation, decoding a tuple from a blob. +//! [`encode_params`] operates on a sequence. If the sequence is a tuple, the +//! tuple is inferred to be a set of Solidity function parameters, +//! The corresponding [`decode_params`] reverses this operation, decoding a +//! tuple from a blob. //! //! This is used to encode the parameters for a Solidity function. //! //! ### `{encode,decode}_sequence` //! -//! [`crate::SolType::abi_encode()`] and [`encode()`] operate on a sequence of -//! tokens. This sequence is inferred not to be function parameters. +//! [`encode_sequence`] operates on a sequence of tokens. This sequence is +//! inferred not to be function parameters. //! //! This is the least useful one. Most users will not need it. diff --git a/crates/sol-types/src/abi/token.rs b/crates/sol-types/src/abi/token.rs index 38619d422f..07ddf42e41 100644 --- a/crates/sol-types/src/abi/token.rs +++ b/crates/sol-types/src/abi/token.rs @@ -7,14 +7,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Ethereum ABI Tokens. +//! Ethereum ABI tokens. //! -//! ABI encoding uses 5 types: -//! - Single EVM words (a 32-byte string) -//! - Sequences with a fixed length `T[M]` -//! - Sequences with a dynamic length `T[]` -//! - Tuples (T, U, V, ...) -//! - Dynamic-length byte arrays `u8[]` +//! See [`TokenType`] for more details. use crate::{ abi::{Decoder, Encoder}, @@ -34,18 +29,27 @@ mod sealed { } use sealed::Sealed; -/// Abi-Encoding Tokens. This is a sealed trait. It contains the type -/// information necessary to encode & decode data. Tokens are an intermediate -/// state between abi-encoded blobs, and rust types. +/// Ethereum ABI tokens. +/// +/// Tokens are an intermediate state between ABI-encoded blobs, and Rust types. +/// +/// ABI encoding uses 5 types: +/// - [`WordToken`]: Single EVM words (a 32-byte string) +/// - [`FixedSeqToken`]: Sequences with a fixed length `T[M]` +/// - [`DynSeqToken`]: Sequences with a dynamic length `T[]` +/// - [`PackedSeqToken`]: Dynamic-length byte arrays `bytes` or `string` +/// - Tuples `(T, U, V, ...)` (implemented for arity `0..=24`) /// /// A token with a lifetime borrows its data from elsewhere. During decoding, /// it borrows its data from the decoder. During encoding, it borrows its data -/// from the rust value being encoded. +/// from the Rust value being encoded. +/// +/// This trait allows us to encode and decode data with minimal copying. It may +/// also be used to enable zero-copy decoding of data, or fast transformation of +/// encoded blobs without full decoding. /// -/// This trait allows us to encode and decode data with minimal copying. It -/// may also be used to enable zero-copy decoding of data, or fast -/// transformation of encoded blobs without full decoding (for, e.g., MEV -/// Searching). +/// This trait is sealed and cannot be implemented for types outside of this +/// crate. It is implemented only for the types listed above. pub trait TokenType<'de>: Sealed + Sized { /// True if the token represents a dynamically-sized type. const DYNAMIC: bool; @@ -72,10 +76,11 @@ pub trait TokenType<'de>: Sealed + Sized { fn tail_append(&self, enc: &mut Encoder); } -/// A token composed of a sequence of other tokens +/// A token composed of a sequence of other tokens. /// -/// This functions as an extension trait for [`TokenType`], and may only be -/// implemented by [`FixedSeqToken`], [`DynSeqToken`], and [`PackedSeqToken`]. +/// This functions is an extension trait for [`TokenType`], and is only +/// implemented by [`FixedSeqToken`], [`DynSeqToken`], [`PackedSeqToken`], and +/// tuples of [`TokenType`]s (including [`WordToken`]). pub trait TokenSeq<'a>: TokenType<'a> { /// True for tuples only. const IS_TUPLE: bool = false; diff --git a/crates/sol-types/src/eip712.rs b/crates/sol-types/src/eip712.rs index 578963a910..e7229a57c5 100644 --- a/crates/sol-types/src/eip712.rs +++ b/crates/sol-types/src/eip712.rs @@ -1,4 +1,4 @@ -use crate::{abi::token::WordToken, sol_data, Encodable, SolType}; +use crate::SolValue; use alloc::{borrow::Cow, string::String, vec::Vec}; use alloy_primitives::{keccak256, Address, FixedBytes, B256, U256}; @@ -139,15 +139,12 @@ impl Eip712Domain { /// pub fn encode_data_to(&self, out: &mut Vec) { // This only works because all of the fields are encoded as words. - #[inline] - fn encode_opt(opt: Option<&T>, out: &mut Vec) - where - S: for<'a> SolType = WordToken>, - T: Encodable, - { - if let Some(t) = opt { - out.extend_from_slice(t.to_tokens().as_slice()); - } + macro_rules! encode_opt { + ($opt:expr) => { + if let Some(t) = $opt { + out.extend_from_slice(t.tokenize().as_slice()); + } + }; } #[inline] @@ -157,13 +154,11 @@ impl Eip712Domain { } out.reserve(self.abi_encoded_size()); - let name = self.name.as_ref().map(cow_keccak256); - encode_opt::, _>(name.as_ref(), out); - let version = self.version.as_ref().map(cow_keccak256); - encode_opt::, _>(version.as_ref(), out); - encode_opt::, _>(self.chain_id.as_ref(), out); - encode_opt::(self.verifying_contract.as_ref(), out); - encode_opt::, _>(self.salt.as_ref(), out); + encode_opt!(self.name.as_ref().map(cow_keccak256)); + encode_opt!(self.version.as_ref().map(cow_keccak256)); + encode_opt!(&self.chain_id); + encode_opt!(&self.verifying_contract); + encode_opt!(&self.salt); } /// EIP-712 `encodeData`: diff --git a/crates/sol-types/src/errors.rs b/crates/sol-types/src/errors.rs index 98eda1a382..0f79bc75da 100644 --- a/crates/sol-types/src/errors.rs +++ b/crates/sol-types/src/errors.rs @@ -110,11 +110,8 @@ impl Error { /// Instantiates a new [`Error::TypeCheckFail`] with the provided token. #[cold] - pub fn type_check_fail_token<'a>( - token: &impl abi::TokenType<'a>, - expected_type: impl Into>, - ) -> Self { - Self::type_check_fail(&abi::encode(token), expected_type) + pub fn type_check_fail_token(token: &T::TokenType<'_>) -> Self { + Self::type_check_fail(&abi::encode(token), T::sol_type_name()) } /// Instantiates a new [`Error::TypeCheckFail`] with the provided data. diff --git a/crates/sol-types/src/lib.rs b/crates/sol-types/src/lib.rs index 9e9d7dd7e9..7fc315d773 100644 --- a/crates/sol-types/src/lib.rs +++ b/crates/sol-types/src/lib.rs @@ -7,7 +7,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Solidity type modeling and ABI coding implementation. +//! Solidity type modeling and [ABI] and [EIP-712] codec implementation. //! //! This crate provides tools for expressing Solidity types in Rust, and for //! encoding these representations into ABI blobs suitable for smart contract @@ -19,13 +19,15 @@ //! This trait maps Solidity types to Rust types via the associated //! [`SolType::RustType`]. //! -//! Each [`SolType`] also has an associated [`SolType::TokenType`]. This is the -//! intermediate representation of the data suitable for ABI encoding. The ABI -//! `encode` and `decode` methods operate on objects implementing [`TokenType`]. +//! The ABI encoding and decoding is implemented in the [`abi`] module, see [its +//! documentation](abi) to learn how it works. +//! +//! [ABI]: https://docs.soliditylang.org/en/latest/abi-spec.html +//! [EIP-712]: https://eips.ethereum.org/EIPS/eip-712 //! //! ``` -//! use alloy_sol_types::{sol_data::*, SolType}; -//! # pub fn main() -> alloy_sol_types::Result<()> { +//! use alloy_sol_types::{sol_data::*, SolType, SolValue}; +//! //! // Represent a Solidity type in rust //! type MySolType = FixedArray; //! @@ -39,8 +41,12 @@ //! let encoded: Vec = MySolType::abi_encode(&data); //! let decoded: [bool; 2] = MySolType::abi_decode(&encoded, validate)?; //! assert_eq!(data, decoded); -//! # Ok(()) -//! # } +//! +//! // This is more easily done with the `SolValue` trait: +//! let encoded: Vec = data.abi_encode(); +//! let decoded: [bool; 2] = <[bool; 2]>::abi_decode(&encoded, validate)?; +//! assert_eq!(data, decoded); +//! # Ok::<_, alloy_sol_types::Error>(()) //! ``` //! //! ## [`sol!`] @@ -74,7 +80,6 @@ //! } //! } //! -//! # pub fn main() { //! // All structs generated with `sol!` implement `crate::SolType` & //! // `crate::SolStruct`. This means you get eip-712 signing for freeeeee //! let my_struct = MyStruct { @@ -93,7 +98,6 @@ //! // Because all the hard work is done by the `sol!` macro, EIP-712 is as easy //! // as calling `eip712_signing_hash` with your domain //! let signing_hash = my_struct.eip712_signing_hash(&my_domain); -//! # } //! ``` //! //! ### [`sol!`] User-defined Value Types @@ -176,9 +180,9 @@ mod impl_core; mod types; pub use types::{ - data_type as sol_data, decode_revert_reason, ContractError, Encodable, EventTopic, - GenericContractError, Panic, PanicKind, Revert, Selectors, SolCall, SolEnum, SolError, - SolEvent, SolInterface, SolStruct, SolType, TopicList, + data_type as sol_data, decode_revert_reason, ContractError, EventTopic, GenericContractError, + Panic, PanicKind, Revert, Selectors, SolCall, SolEnum, SolError, SolEvent, SolInterface, + SolStruct, SolType, SolValue, TopicList, }; pub mod utils; @@ -209,6 +213,27 @@ pub mod private { pub use Option::{None, Some}; pub use Result::{Err, Ok}; + /// An ABI-encodable is any type that may be encoded via a given `SolType`. + /// + /// The `SolType` trait contains encoding logic for a single associated + /// `RustType`. This trait allows us to plug in encoding logic for other + /// `RustTypes`. + /// + /// **Note:** this trait is an implementation detail. As such, it should not + /// be implemented directly unless implementing a custom + /// [`SolType`](crate::SolType), which is also discouraged. Consider + /// using [`SolValue`](crate::SolValue) instead. + pub trait SolTypeValue { + // Note: methods are prefixed with `stv_` to avoid name collisions with + // the `SolValue` trait. + fn stv_to_tokens(&self) -> T::TokenType<'_>; + fn stv_abi_encoded_size(&self) -> usize { + T::ENCODED_SIZE.unwrap() + } + fn stv_abi_encode_packed_to(&self, out: &mut Vec); + fn stv_eip712_data_word(&self) -> super::Word; + } + #[inline(always)] pub const fn u256(n: u64) -> U256 { U256::from_limbs([n, 0, 0, 0]) diff --git a/crates/sol-types/src/macros.rs b/crates/sol-types/src/macros.rs index 1a6fd7fb71..d73ceec20b 100644 --- a/crates/sol-types/src/macros.rs +++ b/crates/sol-types/src/macros.rs @@ -2,30 +2,30 @@ #[rustfmt::skip] macro_rules! all_the_tuples { (@double $mac:path) => { - $mac!((T1 U1)); - $mac!((T1 U1), (T2 U2)); - $mac!((T1 U1), (T2 U2), (T3 U3)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17), (T18 U18)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17), (T18 U18), (T19 U19)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17), (T18 U18), (T19 U19), (T20 U20)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17), (T18 U18), (T19 U19), (T20 U20), (T21 U21)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17), (T18 U18), (T19 U19), (T20 U20), (T21 U21), (T22 U22)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17), (T18 U18), (T19 U19), (T20 U20), (T21 U21), (T22 U22), (T23 U23)); - $mac!((T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17), (T18 U18), (T19 U19), (T20 U20), (T21 U21), (T22 U22), (T23 U23), (T24 U24)); + $mac!( 1 (T1 U1)); + $mac!( 2 (T1 U1), (T2 U2)); + $mac!( 3 (T1 U1), (T2 U2), (T3 U3)); + $mac!( 4 (T1 U1), (T2 U2), (T3 U3), (T4 U4)); + $mac!( 5 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5)); + $mac!( 6 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6)); + $mac!( 7 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7)); + $mac!( 8 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8)); + $mac!( 9 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9)); + $mac!(10 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10)); + $mac!(11 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11)); + $mac!(12 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12)); + $mac!(13 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13)); + $mac!(14 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14)); + $mac!(15 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15)); + $mac!(16 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16)); + $mac!(17 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17)); + $mac!(18 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17), (T18 U18)); + $mac!(19 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17), (T18 U18), (T19 U19)); + $mac!(20 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17), (T18 U18), (T19 U19), (T20 U20)); + $mac!(21 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17), (T18 U18), (T19 U19), (T20 U20), (T21 U21)); + $mac!(22 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17), (T18 U18), (T19 U19), (T20 U20), (T21 U21), (T22 U22)); + $mac!(23 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17), (T18 U18), (T19 U19), (T20 U20), (T21 U21), (T22 U22), (T23 U23)); + $mac!(24 (T1 U1), (T2 U2), (T3 U3), (T4 U4), (T5 U5), (T6 U6), (T7 U7), (T8 U8), (T9 U9), (T10 U10), (T11 U11), (T12 U12), (T13 U13), (T14 U14), (T15 U15), (T16 U16), (T17 U17), (T18 U18), (T19 U19), (T20 U20), (T21 U21), (T22 U22), (T23 U23), (T24 U24)); }; ($mac:path) => { diff --git a/crates/sol-types/src/types/data_type.rs b/crates/sol-types/src/types/data_type.rs index b5a692261b..246eded938 100644 --- a/crates/sol-types/src/types/data_type.rs +++ b/crates/sol-types/src/types/data_type.rs @@ -2,11 +2,13 @@ //! //! These are the types that are [built into Solidity][ref]. //! +//! See [`SolType`] for more details. +//! //! [ref]: https://docs.soliditylang.org/en/latest/types.html #![allow(missing_copy_implementations, missing_debug_implementations)] -use crate::{abi::token::*, utils, Encodable, SolType, Word}; +use crate::{abi::token::*, private::SolTypeValue, utils, SolType, Word}; use alloc::{borrow::Cow, string::String as RustString, vec::Vec}; use alloy_primitives::{ keccak256, Address as RustAddress, FixedBytes as RustFixedBytes, Function as RustFunction, @@ -20,11 +22,21 @@ use core::{borrow::Borrow, fmt::*, hash::Hash, marker::PhantomData, ops::*}; /// Bool - `bool` pub struct Bool; -impl Encodable for bool { +impl SolTypeValue for bool { #[inline] - fn to_tokens(&self) -> WordToken { + fn stv_to_tokens(&self) -> WordToken { WordToken(Word::with_last_byte(*self as u8)) } + + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + out.push(*self as u8); + } + + #[inline] + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::::stv_to_tokens(self).0 + } } impl SolType for Bool { @@ -45,30 +57,30 @@ impl SolType for Bool { fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { token.0 != Word::ZERO } - - #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - Encodable::::to_tokens(rust).0 - } - - #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - out.push(*rust as u8); - } } /// Int - `intX` pub struct Int; -impl Encodable> for T +impl SolTypeValue> for T where T: Borrow< as SupportedInt>::Int>, IntBitCount: SupportedInt, { #[inline] - fn to_tokens(&self) -> WordToken { + fn stv_to_tokens(&self) -> WordToken { IntBitCount::::tokenize_int(*self.borrow()) } + + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + IntBitCount::::encode_packed_to_int(*self.borrow(), out); + } + + #[inline] + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::>::stv_to_tokens(self).0 + } } impl SolType for Int @@ -102,30 +114,30 @@ where fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { IntBitCount::::detokenize_int(token) } - - #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - Encodable::::to_tokens(rust).0 - } - - #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - IntBitCount::::encode_packed_to_int(*rust, out); - } } /// Uint - `uintX` pub struct Uint; -impl Encodable> for T +impl SolTypeValue> for T where T: Borrow< as SupportedInt>::Uint>, IntBitCount: SupportedInt, { #[inline] - fn to_tokens(&self) -> WordToken { + fn stv_to_tokens(&self) -> WordToken { IntBitCount::::tokenize_uint(*self.borrow()) } + + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + IntBitCount::::encode_packed_to_uint(*self.borrow(), out); + } + + #[inline] + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::>::stv_to_tokens(self).0 + } } impl SolType for Uint @@ -149,25 +161,25 @@ where fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { IntBitCount::::detokenize_uint(token) } +} + +/// Address - `address` +pub struct Address; +impl> SolTypeValue
for T { #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - Encodable::::to_tokens(rust).0 + fn stv_to_tokens(&self) -> WordToken { + WordToken(RustAddress::new(*self.borrow()).into_word()) } #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - IntBitCount::::encode_packed_to_uint(*rust, out); + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + out.extend_from_slice(self.borrow()); } -} - -/// Address - `address` -pub struct Address; -impl> Encodable
for T { #[inline] - fn to_tokens(&self) -> WordToken { - WordToken(RustAddress::new(*self.borrow()).into_word()) + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::
::stv_to_tokens(self).0 } } @@ -189,25 +201,25 @@ impl SolType for Address { fn valid_token(token: &Self::TokenType<'_>) -> bool { utils::check_zeroes(&token.0[..12]) } +} +/// Function - `function` +pub struct Function; + +impl> SolTypeValue for T { #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - rust.into_word() + fn stv_to_tokens(&self) -> WordToken { + WordToken(RustFunction::new(*self.borrow()).into_word()) } #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - out.extend_from_slice(rust.as_slice()); + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + out.extend_from_slice(self.borrow()); } -} - -/// Function - `function` -pub struct Function; -impl> Encodable for T { #[inline] - fn to_tokens(&self) -> WordToken { - WordToken(RustFunction::new(*self.borrow()).into_word()) + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::::stv_to_tokens(self).0 } } @@ -229,25 +241,30 @@ impl SolType for Function { fn valid_token(token: &Self::TokenType<'_>) -> bool { utils::check_zeroes(&token.0[24..]) } +} +/// Bytes - `bytes` +pub struct Bytes; + +impl> SolTypeValue for T { #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - rust.into_word() + fn stv_to_tokens(&self) -> PackedSeqToken<'_> { + PackedSeqToken(self.as_ref()) } #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - out.extend_from_slice(rust.as_slice()); + fn stv_abi_encoded_size(&self) -> usize { + 32 + utils::padded_len(self.as_ref()) } -} -/// Bytes - `bytes` -pub struct Bytes; + #[inline] + fn stv_eip712_data_word(&self) -> Word { + keccak256(Bytes::abi_encode_packed(self)) + } -impl> Encodable for T { #[inline] - fn to_tokens(&self) -> PackedSeqToken<'_> { - PackedSeqToken(self.as_ref()) + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + out.extend_from_slice(self.as_ref()); } } @@ -262,11 +279,6 @@ impl SolType for Bytes { "bytes".into() } - #[inline] - fn abi_encoded_size(_data: &Self::RustType) -> usize { - 32 + utils::padded_len(_data.borrow()) - } - #[inline] fn valid_token(_token: &Self::TokenType<'_>) -> bool { true @@ -276,40 +288,119 @@ impl SolType for Bytes { fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { token.into_vec() } +} + +/// Array - `T[]` +pub struct Array(PhantomData); +impl SolTypeValue> for [T] +where + T: SolTypeValue, + U: SolType, +{ #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - keccak256(Self::abi_encode_packed(rust)) + fn stv_to_tokens(&self) -> DynSeqToken> { + DynSeqToken(self.iter().map(T::stv_to_tokens).collect()) } #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - out.extend_from_slice(rust); + fn stv_abi_encoded_size(&self) -> usize { + 32 + self.iter().map(T::stv_abi_encoded_size).sum::() + + (U::DYNAMIC as usize * 32 * self.len()) + } + + #[inline] + fn stv_eip712_data_word(&self) -> Word { + let mut encoded = Vec::new(); + for item in self { + encoded.extend_from_slice(T::stv_eip712_data_word(item).as_slice()); + } + keccak256(encoded) + } + + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + for item in self { + T::stv_abi_encode_packed_to(item, out); + } } } -/// Array - `T[]` -pub struct Array(PhantomData); +impl SolTypeValue> for &[T] +where + T: SolTypeValue, + U: SolType, +{ + #[inline] + fn stv_to_tokens(&self) -> DynSeqToken> { + (**self).stv_to_tokens() + } + + #[inline] + fn stv_abi_encoded_size(&self) -> usize { + (**self).stv_abi_encoded_size() + } + + #[inline] + fn stv_eip712_data_word(&self) -> Word { + (**self).stv_eip712_data_word() + } -impl Encodable> for [T] + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + (**self).stv_abi_encode_packed_to(out) + } +} + +impl SolTypeValue> for &mut [T] where - T: Encodable, + T: SolTypeValue, U: SolType, { #[inline] - fn to_tokens(&self) -> DynSeqToken> { - DynSeqToken(self.iter().map(|r| r.to_tokens()).collect()) + fn stv_to_tokens(&self) -> DynSeqToken> { + (**self).stv_to_tokens() + } + + #[inline] + fn stv_abi_encoded_size(&self) -> usize { + (**self).stv_abi_encoded_size() + } + + #[inline] + fn stv_eip712_data_word(&self) -> Word { + (**self).stv_eip712_data_word() + } + + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + (**self).stv_abi_encode_packed_to(out) } } -impl Encodable> for Vec +impl SolTypeValue> for Vec where - T: Encodable, + T: SolTypeValue, U: SolType, { #[inline] - fn to_tokens(&self) -> DynSeqToken> { - <[T] as Encodable>>::to_tokens(self) + fn stv_to_tokens(&self) -> DynSeqToken> { + <[T] as SolTypeValue>>::stv_to_tokens(self) + } + + #[inline] + fn stv_abi_encoded_size(&self) -> usize { + (**self).stv_abi_encoded_size() + } + + #[inline] + fn stv_eip712_data_word(&self) -> Word { + (**self).stv_eip712_data_word() + } + + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + (**self).stv_abi_encode_packed_to(out) } } @@ -324,13 +415,6 @@ impl SolType for Array { format!("{}[]", T::sol_type_name()).into() } - #[inline] - fn abi_encoded_size(rust: &Self::RustType) -> usize { - let data = rust; - 32 + data.iter().map(T::abi_encoded_size).sum::() - + (T::DYNAMIC as usize * 32 * data.len()) - } - #[inline] fn valid_token(token: &Self::TokenType<'_>) -> bool { token.0.iter().all(T::valid_token) @@ -340,31 +424,30 @@ impl SolType for Array { fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { token.0.into_iter().map(T::detokenize).collect() } +} +/// String - `string` +pub struct String; + +impl> SolTypeValue for T { #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - let mut encoded = Vec::new(); - for item in rust { - encoded.extend_from_slice(T::eip712_data_word(item).as_slice()); - } - keccak256(encoded) + fn stv_to_tokens(&self) -> PackedSeqToken<'_> { + PackedSeqToken(self.as_ref().as_bytes()) } #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - for item in rust { - T::abi_encode_packed_to(item, out); - } + fn stv_abi_encoded_size(&self) -> usize { + 32 + utils::padded_len(self.as_ref().as_ref()) } -} -/// String - `string` -pub struct String; + #[inline] + fn stv_eip712_data_word(&self) -> Word { + keccak256(String::abi_encode_packed(self)) + } -impl> Encodable for T { #[inline] - fn to_tokens(&self) -> PackedSeqToken<'_> { - PackedSeqToken(self.as_ref().as_bytes()) + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + out.extend_from_slice(self.as_ref().as_ref()); } } @@ -379,11 +462,6 @@ impl SolType for String { "string".into() } - #[inline] - fn abi_encoded_size(rust: &Self::RustType) -> usize { - 32 + utils::padded_len(rust.as_bytes()) - } - #[inline] fn valid_token(token: &Self::TokenType<'_>) -> bool { core::str::from_utf8(token.as_slice()).is_ok() @@ -397,33 +475,32 @@ impl SolType for String { // data. RustString::from_utf8_lossy(&Bytes::detokenize(token)).into_owned() } - - #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - keccak256(Self::abi_encode_packed(rust)) - } - - #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - out.extend_from_slice(rust.as_bytes()); - } } /// FixedBytes - `bytesX` #[derive(Clone, Copy, Debug)] pub struct FixedBytes; -impl Encodable> for T +impl, const N: usize> SolTypeValue> for T where ByteCount: SupportedFixedBytes, - T: Borrow<[u8; N]>, { #[inline] - fn to_tokens(&self) -> as SolType>::TokenType<'_> { + fn stv_to_tokens(&self) -> as SolType>::TokenType<'_> { let mut word = Word::ZERO; word[..N].copy_from_slice(self.borrow()); word.into() } + + #[inline] + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::>::stv_to_tokens(self).0 + } + + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + out.extend_from_slice(self.borrow().as_slice()); + } } impl SolType for FixedBytes @@ -447,32 +524,98 @@ where fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { token.0[..N].try_into().unwrap() } +} +/// FixedArray - `T[M]` +pub struct FixedArray(PhantomData); + +impl SolTypeValue> for [T; N] +where + T: SolTypeValue, + U: SolType, +{ #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - Encodable::::to_tokens(rust).0 + fn stv_to_tokens(&self) -> as SolType>::TokenType<'_> { + FixedSeqToken(core::array::from_fn(|i| self[i].stv_to_tokens())) } #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - // write only the first n bytes - out.extend_from_slice(rust.as_slice()); + fn stv_abi_encoded_size(&self) -> usize { + if let Some(size) = FixedArray::::ENCODED_SIZE { + return size + } + + self.iter().map(T::stv_abi_encoded_size).sum::() + (U::DYNAMIC as usize * N * 32) + } + + #[inline] + fn stv_eip712_data_word(&self) -> Word { + // TODO: collect into an array of [u8; 32] and flatten it to a slice like in + // tuple impl + let encoded = self + .iter() + .map(|element| T::stv_eip712_data_word(element).0) + .collect::>(); + keccak256(crate::impl_core::into_flattened(encoded)) + } + + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + for item in self { + T::stv_abi_encode_packed_to(item, out); + } } } -/// FixedArray - `T[M]` -pub struct FixedArray(PhantomData); +impl SolTypeValue> for &[T; N] +where + T: SolTypeValue, + U: SolType, +{ + #[inline] + fn stv_to_tokens(&self) -> as SolType>::TokenType<'_> { + <[T; N] as SolTypeValue>>::stv_to_tokens(&**self) + } + + #[inline] + fn stv_abi_encoded_size(&self) -> usize { + SolTypeValue::>::stv_abi_encoded_size(&**self) + } + + #[inline] + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::>::stv_eip712_data_word(&**self) + } + + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + SolTypeValue::>::stv_abi_encode_packed_to(&**self, out) + } +} -impl Encodable> for [T; N] +impl SolTypeValue> for &mut [T; N] where - T: Encodable, + T: SolTypeValue, U: SolType, { #[inline] - fn to_tokens(&self) -> as SolType>::TokenType<'_> { - FixedSeqToken::<_, N>(core::array::from_fn(|i| { - Encodable::::to_tokens(&self[i]) - })) + fn stv_to_tokens(&self) -> as SolType>::TokenType<'_> { + <[T; N] as SolTypeValue>>::stv_to_tokens(&**self) + } + + #[inline] + fn stv_abi_encoded_size(&self) -> usize { + SolTypeValue::>::stv_abi_encoded_size(&**self) + } + + #[inline] + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::>::stv_eip712_data_word(&**self) + } + + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + SolTypeValue::>::stv_abi_encode_packed_to(&**self, out) } } @@ -487,15 +630,6 @@ impl SolType for FixedArray { } }; - #[inline] - fn abi_encoded_size(rust: &Self::RustType) -> usize { - if let Some(size) = Self::ENCODED_SIZE { - return size - } - - rust.iter().map(T::abi_encoded_size).sum::() + (T::DYNAMIC as usize * N * 32) - } - #[inline] fn sol_type_name() -> Cow<'static, str> { format!("{}[{}]", T::sol_type_name(), N).into() @@ -510,56 +644,65 @@ impl SolType for FixedArray { fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { token.0.map(T::detokenize) } - - #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - // TODO: collect into an array of [u8; 32] and flatten it to a slice like in - // tuple impl - let encoded = rust - .iter() - .map(|element| T::eip712_data_word(element).0) - .collect::>(); - keccak256(crate::impl_core::into_flattened(encoded)) - } - - #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - for item in rust { - T::abi_encode_packed_to(item, out); - } - } } macro_rules! tuple_encodable_impls { - ($(($ty:ident $uty:ident)),+) => { + ($count:literal $(($ty:ident $uty:ident)),+) => { #[allow(non_snake_case)] - impl<$($ty: SolType, $uty,)+> Encodable<($($ty,)+)> for ($( $uty, )+) - where - $($uty: $crate::Encodable<$ty>,)+ - { + impl<$($ty: SolTypeValue<$uty>, $uty: SolType),+> SolTypeValue<($($uty,)+)> for ($($ty,)+) { #[inline] - fn to_tokens(&self) -> <($($ty,)+) as SolType>::TokenType<'_> { + fn stv_to_tokens(&self) -> <($($uty,)+) as SolType>::TokenType<'_> { + let ($($ty,)+) = self; + ($(SolTypeValue::<$uty>::stv_to_tokens($ty),)+) + } + + fn stv_abi_encoded_size(&self) -> usize { + if let Some(size) = <($($uty,)+) as SolType>::ENCODED_SIZE { + return size + } + + let ($($ty,)+) = self; + 0 $( + + <$uty as SolType>::abi_encoded_size($ty) + )+ + $( + + (32 * <$uty as SolType>::DYNAMIC as usize) + )+ + } + + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { let ($($ty,)+) = self; - ( - $( - Encodable::<$ty>::to_tokens($ty), - )+ - ) + // TODO: Reserve + $( + <$uty as SolType>::abi_encode_packed_to($ty, out); + )+ + } + + fn stv_eip712_data_word(&self) -> Word { + let ($($ty,)+) = self; + let encoding: [[u8; 32]; $count] = [$( + <$uty as SolType>::eip712_data_word($ty).0, + )+]; + // SAFETY: Flattening [[u8; 32]; $count] to [u8; $count * 32] is valid + let encoding: &[u8] = unsafe { core::slice::from_raw_parts(encoding.as_ptr().cast(), $count * 32) }; + keccak256(encoding).into() } } }; } macro_rules! tuple_impls { - // compile time `join(",")` format string - (@fmt $other:ident) => { ",{}" }; - (@fmt $first:ident, $($other:ident,)*) => { - concat!( - "{}", - $(tuple_impls! { @fmt $other }),* - ) + // Push 1 element, push a comma if we're not done yet, recurse + (@fmt $s:ident; ) => {}; + (@fmt $s:ident; $first:ident $(, $rest:ident)*) => { + $s.extend_from_slice(<$first as SolType>::sol_type_name().as_bytes()); + tuple_impls!(@fmt_comma $s; $($rest),*); + tuple_impls!(@fmt $s; $($rest),*); }; + (@fmt_comma $s:ident; ) => {}; + (@fmt_comma $s:ident; $($t:tt)+) => { $s.push(b',') }; + ($count:literal $($ty:ident),+) => { #[allow(non_snake_case)] impl<$($ty: SolType,)+> SolType for ($($ty,)+) { @@ -578,28 +721,12 @@ macro_rules! tuple_impls { }; fn sol_type_name() -> Cow<'static, str> { - format!( - concat!( - "(", - tuple_impls! { @fmt $($ty,)+ }, - ")", - ), - $(<$ty as SolType>::sol_type_name(),)+ - ).into() - } - - fn abi_encoded_size(rust: &Self::RustType) -> usize { - if let Some(size) = Self::ENCODED_SIZE { - return size - } - - let ($($ty,)+) = rust; - 0 $( - + <$ty as SolType>::abi_encoded_size($ty) - )+ - $( - + (32 * <$ty as SolType>::DYNAMIC as usize) - )+ + let mut s = Vec::::with_capacity(2 + $count * 8); + s.push(b'('); + tuple_impls!(@fmt s; $($ty),+); + s.push(b')'); + // SAFETY: we're pushing only other `str`s and ASCII characters + Cow::Owned(unsafe { RustString::from_utf8_unchecked(s) }) } fn valid_token(token: &Self::TokenType<'_>) -> bool { @@ -613,31 +740,21 @@ macro_rules! tuple_impls { <$ty as SolType>::detokenize($ty), )+) } - - fn eip712_data_word(rust: &Self::RustType) -> Word { - let ($($ty,)+) = rust; - let encoding: [[u8; 32]; $count] = [$( - <$ty as SolType>::eip712_data_word($ty).0, - )+]; - // SAFETY: Flattening [[u8; 32]; $count] to [u8; $count * 32] is valid - let encoding: &[u8] = unsafe { core::slice::from_raw_parts(encoding.as_ptr().cast(), $count * 32) }; - keccak256(encoding).into() - } - - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - let ($($ty,)+) = rust; - // TODO: Reserve - $( - <$ty as SolType>::abi_encode_packed_to($ty, out); - )+ - } } }; } -impl Encodable<()> for () { +impl SolTypeValue<()> for () { #[inline] - fn to_tokens(&self) {} + fn stv_to_tokens(&self) {} + + #[inline] + fn stv_eip712_data_word(&self) -> Word { + Word::ZERO + } + + #[inline] + fn stv_abi_encode_packed_to(&self, _out: &mut Vec) {} } all_the_tuples!(@double tuple_encodable_impls); @@ -660,14 +777,6 @@ impl SolType for () { #[inline] fn detokenize((): ()) -> Self::RustType {} - - #[inline] - fn eip712_data_word((): &()) -> Word { - Word::ZERO - } - - #[inline] - fn abi_encode_packed_to((): &(), _out: &mut Vec) {} } all_the_tuples!(tuple_impls); diff --git a/crates/sol-types/src/types/enum.rs b/crates/sol-types/src/types/enum.rs index 036de045b8..8299f59053 100644 --- a/crates/sol-types/src/types/enum.rs +++ b/crates/sol-types/src/types/enum.rs @@ -3,11 +3,11 @@ use alloc::vec::Vec; /// Solidity enum. This is always a wrapper around a [`u8`]. /// -/// ### Implementer's Guide +/// # Implementer's Guide /// -/// We do not recommend implementing this trait directly. Instead, we recommend -/// using the [`sol`][crate::sol] proc macro to parse a Solidity error -/// definition. +/// It should not be necessary to implement this trait manually. Instead, use +/// the [`sol!`](crate::sol!) procedural macro to parse Solidity syntax into +/// types that implement this trait. pub trait SolEnum: Sized + Copy + Into + TryFrom { /// The number of variants in the enum. /// diff --git a/crates/sol-types/src/types/error.rs b/crates/sol-types/src/types/error.rs index 39b1b22a4c..97ab322b61 100644 --- a/crates/sol-types/src/types/error.rs +++ b/crates/sol-types/src/types/error.rs @@ -11,11 +11,11 @@ use core::{borrow::Borrow, fmt}; /// Solidity Error (a tuple with a selector) /// -/// ### Implementer's Guide +/// # Implementer's Guide /// -/// We do not recommend implementing this trait directly. Instead, we recommend -/// using the [`sol`][crate::sol] proc macro to parse a Solidity error -/// definition. +/// It should not be necessary to implement this trait manually. Instead, use +/// the [`sol!`](crate::sol!) procedural macro to parse Solidity syntax into +/// types that implement this trait. pub trait SolError: Sized { /// The underlying tuple type which represents the error's members. /// diff --git a/crates/sol-types/src/types/event/mod.rs b/crates/sol-types/src/types/event/mod.rs index 2cd8331f27..bba5f91419 100644 --- a/crates/sol-types/src/types/event/mod.rs +++ b/crates/sol-types/src/types/event/mod.rs @@ -13,11 +13,11 @@ pub use topic_list::TopicList; /// Solidity event. /// -/// ### Implementer's Guide +/// # Implementer's Guide /// -/// We do not recommend implementing this trait directly. Instead, we recommend -/// using the [`sol`][crate::sol] proc macro to parse a Solidity event -/// definition. +/// It should not be necessary to implement this trait manually. Instead, use +/// the [`sol!`](crate::sol!) procedural macro to parse Solidity syntax into +/// types that implement this trait. pub trait SolEvent: Sized { /// The underlying tuple type which represents this event's non-indexed /// parameters. These parameters are ABI encoded and included in the log diff --git a/crates/sol-types/src/types/event/topic.rs b/crates/sol-types/src/types/event/topic.rs index 1b5f0abf46..3e1c547286 100644 --- a/crates/sol-types/src/types/event/topic.rs +++ b/crates/sol-types/src/types/event/topic.rs @@ -10,6 +10,12 @@ use alloy_primitives::keccak256; /// For more details, see the [Solidity reference][ref]. /// /// [ref]: https://docs.soliditylang.org/en/latest/abi-spec.html#encoding-of-indexed-event-parameters +/// +/// # Implementer's Guide +/// +/// It should not be necessary to implement this trait manually. Instead, use +/// the [`sol!`](crate::sol!) procedural macro to parse Solidity syntax into +/// types that implement this trait. pub trait EventTopic: SolType { /// The number of bytes this type occupies in another topic's preimage, /// usually a multiple of 32. @@ -49,12 +55,12 @@ macro_rules! word_impl { #[inline] fn encode_topic_preimage(rust: &Self::RustType, out: &mut Vec) { - out.extend($crate::Encodable::::to_tokens(rust).0 .0); + out.extend($crate::private::SolTypeValue::::stv_to_tokens(rust).0); } #[inline] fn encode_topic(rust: &Self::RustType) -> WordToken { - $crate::Encodable::::to_tokens(rust) + $crate::private::SolTypeValue::::stv_to_tokens(rust) } }; } diff --git a/crates/sol-types/src/types/event/topic_list.rs b/crates/sol-types/src/types/event/topic_list.rs index deba713db5..e762d3d71b 100644 --- a/crates/sol-types/src/types/event/topic_list.rs +++ b/crates/sol-types/src/types/event/topic_list.rs @@ -16,6 +16,12 @@ use sealed::Sealed; /// events' topics are encoded. /// /// [solevent]: https://docs.soliditylang.org/en/latest/abi-spec.html#events +/// +/// # Implementer's Guide +/// +/// It should not be necessary to implement this trait manually. Instead, use +/// the [`sol!`](crate::sol!) procedural macro to parse Solidity syntax into +/// types that implement this trait. pub trait TopicList: SolType + Sealed { /// The number of topics. const COUNT: usize; diff --git a/crates/sol-types/src/types/function.rs b/crates/sol-types/src/types/function.rs index d35fccd4e2..baaf7d718e 100644 --- a/crates/sol-types/src/types/function.rs +++ b/crates/sol-types/src/types/function.rs @@ -1,16 +1,17 @@ use crate::{ abi::{TokenSeq, TokenType}, - Encodable, Result, SolType, Word, + private::SolTypeValue, + Result, SolType, Word, }; use alloc::vec::Vec; /// Solidity call (a tuple with a selector). /// -/// ### Implementer's Guide +/// # Implementer's Guide /// -/// We do not recommend implementing this trait directly. Instead, we recommend -/// using the [`sol`][crate::sol] proc macro to parse a Solidity function -/// definition. +/// It should not be necessary to implement this trait manually. Instead, use +/// the [`sol!`](crate::sol!) procedural macro to parse Solidity syntax into +/// types that implement this trait. pub trait SolCall: Sized { /// The underlying tuple type which represents this type's arguments. /// @@ -93,8 +94,8 @@ pub trait SolCall: Sized { #[inline] fn abi_encode_returns<'a, E>(e: &'a E) -> Vec where - E: Encodable>, + E: SolTypeValue>, { - crate::abi::encode_sequence(&e.to_tokens()) + crate::abi::encode_sequence(&e.stv_to_tokens()) } } diff --git a/crates/sol-types/src/types/interface.rs b/crates/sol-types/src/types/interface.rs index 338f04f6bf..812f3c6135 100644 --- a/crates/sol-types/src/types/interface.rs +++ b/crates/sol-types/src/types/interface.rs @@ -17,11 +17,11 @@ use std::error::Error as StdError; /// [`SolCall`]: crate::SolCall /// [`SolError`]: crate::SolError /// -/// ### Implementer's Guide +/// # Implementer's Guide /// -/// We do not recommend implementing this trait directly. Instead, we recommend -/// using the [`sol`][crate::sol] proc macro to parse a Solidity contract -/// definition. +/// It should not be necessary to implement this trait manually. Instead, use +/// the [`sol!`](crate::sol!) procedural macro to parse Solidity syntax into +/// types that implement this trait. pub trait SolInterface: Sized { /// The name of this type. const NAME: &'static str; diff --git a/crates/sol-types/src/types/mod.rs b/crates/sol-types/src/types/mod.rs index da6f7ebe84..535f6c6aeb 100644 --- a/crates/sol-types/src/types/mod.rs +++ b/crates/sol-types/src/types/mod.rs @@ -18,8 +18,11 @@ pub use interface::{ContractError, GenericContractError, Selectors, SolInterface mod r#struct; pub use r#struct::SolStruct; +mod value; +pub use value::SolValue; + mod ty; -pub use ty::{Encodable, SolType}; +pub use ty::SolType; // Solidity user-defined value types. // No exports are needed as the only item is a macro. diff --git a/crates/sol-types/src/types/struct.rs b/crates/sol-types/src/types/struct.rs index 1b5cb48707..340affc66c 100644 --- a/crates/sol-types/src/types/struct.rs +++ b/crates/sol-types/src/types/struct.rs @@ -12,9 +12,9 @@ use alloy_primitives::{keccak256, B256}; /// /// # Implementer's Guide /// -/// We do not recommend implementing this trait directly. Instead, we recommend -/// using the [`sol`][crate::sol] proc macro to parse a Solidity struct -/// definition. +/// It should not be necessary to implement this trait manually. Instead, use +/// the [`sol!`](crate::sol!) procedural macro to parse Solidity syntax into +/// types that implement this trait. /// /// # Note /// diff --git a/crates/sol-types/src/types/ty.rs b/crates/sol-types/src/types/ty.rs index 0557b686c3..ad3b05173f 100644 --- a/crates/sol-types/src/types/ty.rs +++ b/crates/sol-types/src/types/ty.rs @@ -1,127 +1,116 @@ use crate::{ abi::{self, TokenSeq, TokenType}, + private::SolTypeValue, Result, Word, }; use alloc::{borrow::Cow, vec::Vec}; -/// An encodable is any type that may be encoded via a given [`SolType`]. +/// A Solidity type. /// -/// The [`SolType`] trait contains encoding logic for a single associated -/// `RustType`. This trait allows us to plug in encoding logic for other -/// `RustTypes`. Consumers of this library may impl `Encodable` for their -/// types. +/// This trait is implemented by types that contain ABI encoding and decoding +/// info for Solidity types. Types may be combined to express arbitrarily +/// complex Solidity types. /// -/// ### Why no `Decodable`? +/// These types are zero cost representations of Solidity types. They do not +/// exist at runtime. They **only** contain information about the type, they do +/// not carry any data. +/// +/// # Implementer's Guide /// -/// We believe in permissive encoders and restrictive decoders. To avoid type -/// ambiguity during the decoding process, we do not allow decoding into -/// arbitrary types. Users desiring this behavior should convert after decoding. +/// It should not be necessary to implement this trait manually. Instead, use +/// the [`sol!`] procedural macro to parse Solidity syntax into types that +/// implement this trait. /// -/// ### Usage Note +/// # Examples /// -/// Rust data may not have a 1:1 mapping to Solidity types. The easiest example -/// of this is [`u64`], which may correspond to any of `uint{40,48,56,64}`. -/// Similarly, [`u128`] covers `uint72-128`. Because of this, usage of this -/// trait is always ambiguous for certain types. +/// Basic usage: /// -/// ```compile_fail,E0284 -/// # use alloy_sol_types::{SolType, Encodable, sol_data::*}; -/// // Compilation fails due to ambiguity -/// // error[E0284]: type annotations needed -/// // | -/// // 6 | 100u64.to_tokens(); -/// // | ^^^^^^^^^ -/// // | -/// // = note: cannot satisfy `<_ as SolType>::TokenType<'_> == _` -/// // help: try using a fully qualified path to specify the expected types -/// // | -/// // 6 | >::to_tokens(&100u64); -/// // | ++++++++++++++++++++++++++++++++++ ~ -/// // -/// 100u64.to_tokens(); -/// # Ok::<_, alloy_sol_types::Error>(()) /// ``` +/// use alloy_sol_types::{sol_data::*, SolType}; /// -/// To resolve this, specify the related [`SolType`]. When specifying T it is -/// recommended that you invoke the [`SolType`] methods on `T`, rather than the -/// [`Encodable`] methods. +/// type Uint256DynamicArray = Array>; +/// assert_eq!(Uint256DynamicArray::sol_type_name(), "uint256[]"); /// -/// ``` -/// # use alloy_sol_types::{SolType, Encodable, sol_data::*}; -/// # fn main() -> Result<(), alloy_sol_types::Error> { -/// // Not recommended: -/// Encodable::>::to_tokens(&100u64); +/// type Erc20FunctionArgs = (Address, Uint<256>); +/// assert_eq!(Erc20FunctionArgs::sol_type_name(), "(address,uint256)"); /// -/// // Recommended: -/// Uint::<64>::tokenize(&100u64); -/// # Ok(()) -/// # } +/// type LargeComplexType = (FixedArray, 2>, (FixedBytes<13>, String)); +/// assert_eq!( +/// LargeComplexType::sol_type_name(), +/// "(bool[][2],(bytes13,string))" +/// ); /// ``` -pub trait Encodable { - /// Convert the value to tokens. - fn to_tokens(&self) -> T::TokenType<'_>; - - /// Return the Solidity type name of this value. - #[inline] - fn sol_type_name(&self) -> Cow<'static, str> { - T::sol_type_name() - } -} - -/// A Solidity Type, for ABI encoding and decoding /// -/// This trait is implemented by types that contain ABI encoding and decoding -/// info for Solidity types. Types may be combined to express arbitrarily -/// complex Solidity types. +/// The previous example can be entirely replicated with the [`sol!`] macro: /// /// ``` -/// use alloy_sol_types::{sol_data::*, SolType}; +/// use alloy_sol_types::{sol, SolType}; /// -/// type DynUint256Array = Array>; -/// assert_eq!(&DynUint256Array::sol_type_name(), "uint256[]"); +/// type Uint256DynamicArray = sol!(uint256[]); +/// assert_eq!(Uint256DynamicArray::sol_type_name(), "uint256[]"); /// -/// type Erc20FunctionArgs = (Address, Uint<256>); -/// assert_eq!(&Erc20FunctionArgs::sol_type_name(), "(address,uint256)"); +/// type Erc20FunctionArgs = sol!((address, uint256)); +/// assert_eq!(Erc20FunctionArgs::sol_type_name(), "(address,uint256)"); /// -/// type LargeComplexType = (FixedArray, 2>, (FixedBytes<13>, String)); +/// type LargeComplexType = sol!((bool[][2],(bytes13,string))); /// assert_eq!( -/// &LargeComplexType::sol_type_name(), +/// LargeComplexType::sol_type_name(), /// "(bool[][2],(bytes13,string))" /// ); /// ``` /// -/// These types are zero cost representations of Solidity types. They do not -/// exist at runtime. They ONLY contain information about the type, they do not -/// carry any data. -/// -/// ### Implementer's Guide -/// -/// We do not recommend implementing this trait directly. Instead, we recommend -/// using the [`crate::sol`] proc macro to parse a Solidity structdef into a -/// native Rust struct. +/// For more complex usage, it's recommended to use the +/// [`SolValue`](crate::SolValue) trait for primitive types, and the `Sol*` +/// traits for other types created with [`sol!`]: /// /// ``` -/// alloy_sol_types::sol! { +/// use alloy_primitives::Address; +/// use alloy_sol_types::{sol, SolCall, SolStruct, SolValue}; +/// +/// sol! { /// struct MyStruct { /// bool a; -/// bytes2 b; +/// uint64 b; +/// address c; /// } +/// +/// enum MyEnum { +/// A, +/// B, +/// C, +/// } +/// +/// function myFunction(MyStruct my_struct, MyEnum my_enum); /// } /// -/// // This is the native rust representation of a Solidity type! -/// // How cool is that! -/// const MY_STRUCT: MyStruct = MyStruct { +/// // `SolValue` +/// let my_bool = true; +/// let _ = my_bool.abi_encode(); +/// +/// let my_struct = MyStruct { /// a: true, -/// b: alloy_primitives::FixedBytes([0x01, 0x02]), +/// b: 1, +/// c: Address::ZERO, /// }; +/// let _ = my_struct.abi_encode(); +/// +/// let my_enum = MyEnum::A; +/// let _ = my_enum.abi_encode(); +/// +/// // `SolCall` +/// let my_function_call = myFunctionCall { my_struct, my_enum }; +/// let _ = my_function_call.abi_encode(); /// ``` -pub trait SolType { +/// +/// [`sol!`]: crate::sol +pub trait SolType: Sized { /// The corresponding Rust type. - type RustType: Encodable + 'static; + type RustType: SolTypeValue + 'static; - /// The corresponding ABI token type. + /// The corresponding ABI [token type](TokenType). /// - /// See implementers of [`TokenType`]. + /// This is the intermediate representation of the type that is used for + /// ABI encoding and decoding. type TokenType<'a>: TokenType<'a>; /// The encoded size of the type, if known at compile time @@ -130,15 +119,14 @@ pub trait SolType { /// Whether the encoded size is dynamic. const DYNAMIC: bool = Self::ENCODED_SIZE.is_none(); - /// The name of the type in Solidity. + /// The name of this type in Solidity. fn sol_type_name() -> Cow<'static, str>; /// Calculate the ABI-encoded size of the data, counting both head and tail /// words. For a single-word type this will always be 32. #[inline] - fn abi_encoded_size(rust: &Self::RustType) -> usize { - let _ = rust; - Self::ENCODED_SIZE.unwrap() + fn abi_encoded_size>(rust: &E) -> usize { + rust.stv_abi_encoded_size() } /// Returns `true` if the given token can be detokenized with this type. @@ -151,19 +139,20 @@ pub trait SolType { if Self::valid_token(token) { Ok(()) } else { - Err(crate::Error::type_check_fail_token( - token, - Self::sol_type_name(), - )) + Err(crate::Error::type_check_fail_token::(token)) } } - /// Detokenize a value from the given token. + /// Detokenize this type's value from the given token. + /// + /// See the [`abi::token`] module for more information. fn detokenize(token: Self::TokenType<'_>) -> Self::RustType; /// Tokenizes the given value into this type's token. - fn tokenize>(rust: &E) -> Self::TokenType<'_> { - rust.to_tokens() + /// + /// See the [`abi::token`] module for more information. + fn tokenize>(rust: &E) -> Self::TokenType<'_> { + rust.stv_to_tokens() } /// Encode this data according to EIP-712 `encodeData` rules, and hash it @@ -174,12 +163,18 @@ pub trait SolType { /// words for each element /// /// - fn eip712_data_word(rust: &Self::RustType) -> Word; + #[inline] + fn eip712_data_word>(rust: &E) -> Word { + rust.stv_eip712_data_word() + } /// Non-standard Packed Mode ABI encoding. /// /// See [`abi_encode_packed`][SolType::abi_encode_packed] for more details. - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec); + #[inline] + fn abi_encode_packed_to>(rust: &E, out: &mut Vec) { + rust.stv_abi_encode_packed_to(out) + } /// Non-standard Packed Mode ABI encoding. /// @@ -191,70 +186,87 @@ pub trait SolType { /// /// More information can be found in the [Solidity docs](https://docs.soliditylang.org/en/latest/abi-spec.html#non-standard-packed-mode). #[inline] - fn abi_encode_packed(rust: &Self::RustType) -> Vec { + fn abi_encode_packed>(rust: &E) -> Vec { let mut out = Vec::new(); Self::abi_encode_packed_to(rust, &mut out); out } - /// Encode a single ABI token by wrapping it in a 1-length sequence. + /// Tokenizes and ABI-encodes the given value by wrapping it in a + /// single-element sequence. + /// + /// See the [`abi`] module for more information. #[inline] - fn abi_encode>(rust: &E) -> Vec { - abi::encode(&rust.to_tokens()) + fn abi_encode>(rust: &E) -> Vec { + abi::encode(&rust.stv_to_tokens()) } - /// Encode an ABI sequence. + /// Tokenizes and ABI-encodes the given value as function parameters. + /// + /// See the [`abi`] module for more information. #[inline] - fn abi_encode_sequence>(rust: &E) -> Vec + fn abi_encode_params>(rust: &E) -> Vec where for<'a> Self::TokenType<'a>: TokenSeq<'a>, { - abi::encode_sequence(&rust.to_tokens()) + abi::encode_params(&rust.stv_to_tokens()) } - /// Encode an ABI sequence suitable for function parameters. + /// Tokenizes and ABI-encodes the given value as a sequence. + /// + /// See the [`abi`] module for more information. #[inline] - fn abi_encode_params>(rust: &E) -> Vec + fn abi_encode_sequence>(rust: &E) -> Vec where for<'a> Self::TokenType<'a>: TokenSeq<'a>, { - abi::encode_params(&rust.to_tokens()) + abi::encode_sequence(&rust.stv_to_tokens()) } - /// Decode a Rust type from an ABI blob. + /// Decodes this type's value from an ABI blob by interpreting it as a + /// single-element sequence. + /// + /// See the [`abi`] module for more information. #[inline] fn abi_decode(data: &[u8], validate: bool) -> Result { - abi::decode::>(data, validate) - .and_then(|t| check_decode::(t, validate)) + abi::decode::>(data, validate).and_then(check_decode::(validate)) } - /// ABI-decode the given data + /// Decodes this type's value from an ABI blob by interpreting it as + /// function parameters. + /// + /// See the [`abi`] module for more information. #[inline] fn abi_decode_params<'de>(data: &'de [u8], validate: bool) -> Result where Self::TokenType<'de>: TokenSeq<'de>, { abi::decode_params::>(data, validate) - .and_then(|t| check_decode::(t, validate)) + .and_then(check_decode::(validate)) } - /// ABI-decode a Rust type from an ABI blob. + /// Decodes this type's value from an ABI blob by interpreting it as a + /// sequence. + /// + /// See the [`abi`] module for more information. #[inline] fn abi_decode_sequence<'de>(data: &'de [u8], validate: bool) -> Result where Self::TokenType<'de>: TokenSeq<'de>, { abi::decode_sequence::>(data, validate) - .and_then(|t| check_decode::(t, validate)) + .and_then(check_decode::(validate)) } } -fn check_decode( - token: T::TokenType<'_>, +#[inline] +fn check_decode( validate: bool, -) -> Result { - if validate { - T::type_check(&token)?; +) -> impl FnOnce(T::TokenType<'_>) -> Result { + move |token| { + if validate { + T::type_check(&token)?; + } + Ok(T::detokenize(token)) } - Ok(T::detokenize(token)) } diff --git a/crates/sol-types/src/types/udt.rs b/crates/sol-types/src/types/udt.rs index 9292efe373..944bbc74d0 100644 --- a/crates/sol-types/src/types/udt.rs +++ b/crates/sol-types/src/types/udt.rs @@ -19,10 +19,20 @@ macro_rules! define_udt { <$underlying as $crate::SolType>::RustType, ); - impl $crate::Encodable<$name> for <$underlying as $crate::SolType>::RustType { + impl $crate::private::SolTypeValue<$name> for <$underlying as $crate::SolType>::RustType { #[inline] - fn to_tokens(&self) -> <$underlying as $crate::SolType>::TokenType<'_> { - $crate::Encodable::<$underlying>::to_tokens(self) + fn stv_to_tokens(&self) -> <$underlying as $crate::SolType>::TokenType<'_> { + $crate::private::SolTypeValue::<$underlying>::stv_to_tokens(self) + } + + #[inline] + fn stv_eip712_data_word(&self) -> $crate::Word { + <$underlying as $crate::SolType>::tokenize(self).0 + } + + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut $crate::private::Vec) { + <$underlying as $crate::SolType>::abi_encode_packed_to(self, out) } } @@ -83,16 +93,6 @@ macro_rules! define_udt { fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { <$underlying as $crate::SolType>::detokenize(token) } - - #[inline] - fn eip712_data_word(rust: &Self::RustType) -> $crate::Word { - ::tokenize(rust).0 - } - - #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut $crate::private::Vec) { - <$underlying as $crate::SolType>::abi_encode_packed_to(rust, out) - } } impl $crate::EventTopic for $name { diff --git a/crates/sol-types/src/types/value.rs b/crates/sol-types/src/types/value.rs new file mode 100644 index 0000000000..b3a6253238 --- /dev/null +++ b/crates/sol-types/src/types/value.rs @@ -0,0 +1,407 @@ +use super::SolType; +use crate::{ + abi::TokenSeq, + private::SolTypeValue, + sol_data::{self, ByteCount, SupportedFixedBytes}, + Result, Word, +}; +use alloc::{borrow::Cow, string::String, vec::Vec}; +use alloy_primitives::{Address, Bytes, FixedBytes, Function, I256, U256}; + +/// Solidity values. +/// +/// This is a convenience trait that re-exports the logic in [`SolType`] with +/// less generic implementations so that they can be used as methods with `self` +/// receivers. +/// +/// See [`SolType`] for more information. +/// +/// # Implementer's Guide +/// +/// It should not be necessary to implement this trait manually. Instead, use +/// the [`sol!`](crate::sol!) procedural macro to parse Solidity syntax into +/// types that implement this trait. +/// +/// # Examples +/// +/// ``` +/// use alloy_sol_types::SolValue; +/// +/// let my_values = ("hello", 0xdeadbeef_u32, true, [0x42_u8; 24]); +/// let _ = my_values.abi_encode(); +/// let _ = my_values.abi_encode_packed(); +/// assert_eq!(my_values.sol_type_name(), "(string,uint32,bool,bytes24)"); +/// ``` +pub trait SolValue: SolTypeValue { + /// The Solidity type that this type corresponds to. + type SolType: SolType; + + /// The name of the associated Solidity type. + /// + /// See [`SolType::sol_type_name`] for more information. + #[inline] + fn sol_type_name(&self) -> Cow<'static, str> { + Self::SolType::sol_type_name() + } + + /// Detokenize a value from the given token. + /// + /// See [`SolType::tokenize`] for more information. + #[inline] + fn tokenize(&self) -> ::TokenType<'_> { + >::stv_to_tokens(self) + } + + /// Tokenizes the given value into this type's token. + /// + /// See [`SolType::detokenize`] for more information. + #[inline] + fn detokenize(token: ::TokenType<'_>) -> Self + where + Self: From<::RustType>, + { + Self::from(::detokenize(token)) + } + + /// Calculate the ABI-encoded size of the data. + /// + /// See [`SolType::abi_encoded_size`] for more information. + #[inline] + fn abi_encoded_size(&self) -> usize { + >::stv_abi_encoded_size(self) + } + + /// Encode this data according to EIP-712 `encodeData` rules, and hash it + /// if necessary. + /// + /// See [`SolType::eip712_data_word`] for more information. + #[inline] + fn eip712_data_word(&self) -> Word { + >::stv_eip712_data_word(self) + } + + /// Non-standard Packed Mode ABI encoding. + /// + /// See [`SolType::abi_encode_packed_to`] for more information. + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + >::stv_abi_encode_packed_to(self, out) + } + + /// Non-standard Packed Mode ABI encoding. + /// + /// See [`SolType::abi_encode_packed`] for more information. + #[inline] + fn abi_encode_packed(&self) -> Vec { + let mut out = Vec::new(); + >::stv_abi_encode_packed_to(self, &mut out); + out + } + + /// ABI-encodes the value. + /// + /// See [`SolType::abi_encode`] for more information. + #[inline] + fn abi_encode(&self) -> Vec { + Self::SolType::abi_encode(self) + } + + /// Encodes an ABI sequence. + /// + /// See [`SolType::abi_encode_sequence`] for more information. + #[inline] + fn abi_encode_sequence(&self) -> Vec + where + for<'a> ::TokenType<'a>: TokenSeq<'a>, + { + Self::SolType::abi_encode_sequence(self) + } + + /// Encodes an ABI sequence suitable for function parameters. + /// + /// See [`SolType::abi_encode_params`] for more information. + #[inline] + fn abi_encode_params(&self) -> Vec + where + for<'a> ::TokenType<'a>: TokenSeq<'a>, + { + Self::SolType::abi_encode_params(self) + } + + /// ABI-decode this type from the given data. + /// + /// See [`SolType::abi_decode`] for more information. + fn abi_decode(data: &[u8], validate: bool) -> Result + where + Self: From<::RustType>, + { + Self::SolType::abi_decode(data, validate).map(Self::from) + } + + /// ABI-decode this type from the given data. + /// + /// See [`SolType::abi_decode_params`] for more information. + #[inline] + fn abi_decode_params<'de>(data: &'de [u8], validate: bool) -> Result + where + Self: From<::RustType>, + ::TokenType<'de>: TokenSeq<'de>, + { + Self::SolType::abi_decode_params(data, validate).map(Self::from) + } + + /// ABI-decode this type from the given data. + /// + /// See [`SolType::abi_decode_sequence`] for more information. + #[inline] + fn abi_decode_sequence<'de>(data: &'de [u8], validate: bool) -> Result + where + Self: From<::RustType>, + ::TokenType<'de>: TokenSeq<'de>, + { + Self::SolType::abi_decode_sequence(data, validate).map(Self::from) + } +} + +macro_rules! impl_sol_value { + ($($(#[$attr:meta])* [$($gen:tt)*] $rust:ty => $sol:ty [$($where:tt)*];)+) => {$( + $(#[$attr])* + impl<$($gen)*> SolValue for $rust $($where)* { + type SolType = $sol; + } + )*}; +} + +impl_sol_value! { + // Basic + [] bool => sol_data::Bool []; + + [] i8 => sol_data::Int::<8> []; + [] i16 => sol_data::Int::<16> []; + [] i32 => sol_data::Int::<32> []; + [] i64 => sol_data::Int::<64> []; + [] i128 => sol_data::Int::<128> []; + [] I256 => sol_data::Int::<256> []; + #[cfg(pointer_width = "32")] + [] isize => sol_data::Int::<32> []; + #[cfg(pointer_width = "64")] + [] isize => sol_data::Int::<64> []; + + // TODO: `u8` is specialized to encode as `bytes` or `bytesN` + // [] u8 => sol_data::Uint::<8> []; + [] u16 => sol_data::Uint::<16> []; + [] u32 => sol_data::Uint::<32> []; + [] u64 => sol_data::Uint::<64> []; + [] u128 => sol_data::Uint::<128> []; + [] U256 => sol_data::Uint::<256> []; + #[cfg(pointer_width = "32")] + [] usize => sol_data::Uint::<32> []; + #[cfg(pointer_width = "64")] + [] usize => sol_data::Uint::<64> []; + + [] Address => sol_data::Address []; + [] Function => sol_data::Function []; + [const N: usize] FixedBytes => sol_data::FixedBytes [where ByteCount: SupportedFixedBytes]; + [const N: usize] [u8; N] => sol_data::FixedBytes [where ByteCount: SupportedFixedBytes]; + [] String => sol_data::String []; + [] str => sol_data::String []; + [] Bytes => sol_data::Bytes []; + + [] Vec => sol_data::Bytes []; + [] [u8] => sol_data::Bytes []; + + // Generic + [T: SolValue] Vec => sol_data::Array []; + [T: SolValue] [T] => sol_data::Array []; + [T: SolValue, const N: usize] [T; N] => sol_data::FixedArray []; + + ['a, T: ?Sized + SolValue] &'a T => T::SolType [where &'a T: SolTypeValue]; + ['a, T: ?Sized + SolValue] &'a mut T => T::SolType [where &'a mut T: SolTypeValue]; +} + +macro_rules! tuple_impls { + ($count:literal $($ty:ident),+) => { + impl<$($ty: SolValue,)+> SolValue for ($($ty,)+) { + type SolType = ($($ty::SolType,)+); + } + }; +} + +impl SolValue for () { + type SolType = (); +} + +all_the_tuples!(tuple_impls); + +#[cfg(test)] +#[allow(clippy::type_complexity)] +mod tests { + use super::*; + + // Make sure these are in scope + #[allow(unused_imports)] + use crate::{private::SolTypeValue as _, SolType as _}; + + #[test] + fn inference() { + false.sol_type_name(); + false.abi_encoded_size(); + false.eip712_data_word(); + false.abi_encode_packed_to(&mut vec![]); + false.abi_encode_packed(); + false.abi_encode(); + (false,).abi_encode_sequence(); + (false,).abi_encode_params(); + + "".sol_type_name(); + "".abi_encoded_size(); + "".eip712_data_word(); + "".abi_encode_packed_to(&mut vec![]); + "".abi_encode_packed(); + "".abi_encode(); + ("",).abi_encode_sequence(); + ("",).abi_encode_params(); + + let _ = String::abi_decode(b"", false); + let _ = bool::abi_decode(b"", false); + } + + #[test] + fn basic() { + assert_eq!(false.abi_encode(), Word::ZERO[..]); + assert_eq!(true.abi_encode(), Word::with_last_byte(1)[..]); + + assert_eq!(0i8.abi_encode(), Word::ZERO[..]); + assert_eq!(0i16.abi_encode(), Word::ZERO[..]); + assert_eq!(0i32.abi_encode(), Word::ZERO[..]); + assert_eq!(0i64.abi_encode(), Word::ZERO[..]); + assert_eq!(0i128.abi_encode(), Word::ZERO[..]); + assert_eq!(I256::ZERO.abi_encode(), Word::ZERO[..]); + + assert_eq!(0u16.abi_encode(), Word::ZERO[..]); + assert_eq!(0u32.abi_encode(), Word::ZERO[..]); + assert_eq!(0u64.abi_encode(), Word::ZERO[..]); + assert_eq!(0u128.abi_encode(), Word::ZERO[..]); + assert_eq!(U256::ZERO.abi_encode(), Word::ZERO[..]); + + assert_eq!(Address::ZERO.abi_encode(), Word::ZERO[..]); + assert_eq!(Function::ZERO.abi_encode(), Word::ZERO[..]); + + let encode_bytes = |b: &[u8]| { + let last = Word::new({ + let mut buf = [0u8; 32]; + buf[..b.len()].copy_from_slice(b); + buf + }); + [ + &Word::with_last_byte(0x20)[..], + &Word::with_last_byte(b.len() as u8)[..], + if b.is_empty() { b } else { &last[..] }, + ] + .concat() + }; + + // empty `bytes` + assert_eq!(b"".abi_encode(), encode_bytes(b"")); + assert_eq!((b"" as &[_]).abi_encode(), encode_bytes(b"")); + // `bytes1` + assert_eq!(b"a".abi_encode()[0], b'a'); + assert_eq!(b"a".abi_encode()[1..], Word::ZERO[1..]); + // `bytes` + assert_eq!((b"a" as &[_]).abi_encode(), encode_bytes(b"a")); + + assert_eq!("".abi_encode(), encode_bytes(b"")); + assert_eq!("a".abi_encode(), encode_bytes(b"a")); + assert_eq!(String::new().abi_encode(), encode_bytes(b"")); + assert_eq!(String::from("a").abi_encode(), encode_bytes(b"a")); + assert_eq!(Vec::::new().abi_encode(), encode_bytes(b"")); + assert_eq!(Vec::::from(&b"a"[..]).abi_encode(), encode_bytes(b"a")); + } + + #[test] + fn big() { + let tuple = ( + false, + 0i8, + 0i16, + 0i32, + 0i64, + 0i128, + I256::ZERO, + // 0u8, + 0u16, + 0u32, + 0u64, + 0u128, + U256::ZERO, + Address::ZERO, + Function::ZERO, + ); + let encoded = tuple.abi_encode(); + assert_eq!(encoded.len(), 32 * 14); + assert!(encoded.iter().all(|&b| b == 0)); + } + + #[test] + fn complex() { + let tuple = ((((((false,),),),),),); + assert_eq!(tuple.abi_encode(), Word::ZERO[..]); + assert_eq!(tuple.sol_type_name(), "((((((bool))))))"); + + let tuple = ( + 42u64, + "hello world", + true, + ( + String::from("aaaa"), + Address::with_last_byte(69), + b"bbbb".to_vec(), + b"cccc", + &b"dddd"[..], + ), + ); + assert_eq!( + tuple.sol_type_name(), + "(uint64,string,bool,(string,address,bytes,bytes4,bytes))" + ); + } + + #[test] + fn derefs() { + let x: &[Address; 0] = &[]; + x.abi_encode(); + assert_eq!(x.sol_type_name(), "address[0]"); + + let x = &[Address::ZERO]; + x.abi_encode(); + assert_eq!(x.sol_type_name(), "address[1]"); + + let x = &[Address::ZERO, Address::ZERO]; + x.abi_encode(); + assert_eq!(x.sol_type_name(), "address[2]"); + + let x = &[Address::ZERO][..]; + x.abi_encode(); + assert_eq!(x.sol_type_name(), "address[]"); + + let mut x = *b"0"; + let x = (&mut x, *b"aaaa", b"00"); + x.abi_encode(); + assert_eq!(x.sol_type_name(), "(bytes1,bytes4,bytes2)"); + + let tuple = &(&0u16, &"", b"0", &mut [Address::ZERO][..]); + tuple.abi_encode(); + assert_eq!(tuple.sol_type_name(), "(uint16,string,bytes1,address[])"); + } + + #[test] + fn decode() { + let _: Result = String::abi_decode(b"", false); + + let _: Result> = Vec::::abi_decode(b"", false); + + let _: Result<(u64, String, U256)> = <(u64, String, U256)>::abi_decode(b"", false); + let _: Result<(i64, Vec<(u32, String, Vec>)>, U256)> = + <(i64, Vec<(u32, String, Vec>)>, U256)>::abi_decode(b"", false); + } +} diff --git a/crates/sol-types/tests/ui/type.stderr b/crates/sol-types/tests/ui/type.stderr index ce24e7200a..e166d280df 100644 --- a/crates/sol-types/tests/ui/type.stderr +++ b/crates/sol-types/tests/ui/type.stderr @@ -130,35 +130,6 @@ error[E0412]: cannot find type `a` in this scope 739 | mapping(mapping(a b => c d) e => mapping(f g => h i) j) map; | ^ not found in this scope -error[E0277]: the trait bound `(alloy_sol_types::sol_data::Address, alloy_sol_types::sol_data::Address, alloy_sol_types::sol_data::String, Bool, alloy_sol_types::sol_data::Bytes, alloy_sol_types::sol_data::FixedBytes<1>, alloy_sol_types::sol_data::FixedBytes<2>, alloy_sol_types::sol_data::FixedBytes<3>, alloy_sol_types::sol_data::FixedBytes<4>, alloy_sol_types::sol_data::FixedBytes<5>, alloy_sol_types::sol_data::FixedBytes<6>, alloy_sol_types::sol_data::FixedBytes<7>, alloy_sol_types::sol_data::FixedBytes<8>, alloy_sol_types::sol_data::FixedBytes<9>, alloy_sol_types::sol_data::FixedBytes<10>, alloy_sol_types::sol_data::FixedBytes<11>, alloy_sol_types::sol_data::FixedBytes<12>, alloy_sol_types::sol_data::FixedBytes<13>, alloy_sol_types::sol_data::FixedBytes<14>, alloy_sol_types::sol_data::FixedBytes<15>, alloy_sol_types::sol_data::FixedBytes<16>, alloy_sol_types::sol_data::FixedBytes<17>, alloy_sol_types::sol_data::FixedBytes<18>, alloy_sol_types::sol_data::FixedBytes<19>, alloy_sol_types::sol_data::FixedBytes<20>, alloy_sol_types::sol_data::FixedBytes<21>, alloy_sol_types::sol_data::FixedBytes<22>, alloy_sol_types::sol_data::FixedBytes<23>, alloy_sol_types::sol_data::FixedBytes<24>, alloy_sol_types::sol_data::FixedBytes<25>, alloy_sol_types::sol_data::FixedBytes<26>, alloy_sol_types::sol_data::FixedBytes<27>, alloy_sol_types::sol_data::FixedBytes<28>, alloy_sol_types::sol_data::FixedBytes<29>, alloy_sol_types::sol_data::FixedBytes<30>, alloy_sol_types::sol_data::FixedBytes<31>, alloy_sol_types::sol_data::FixedBytes<32>, Int<256>, Int<8>, Int<16>, Int<24>, Int<32>, Int<40>, Int<48>, Int<56>, Int<64>, Int<72>, Int<80>, Int<88>, Int<96>, Int<104>, Int<112>, Int<120>, Int<128>, Int<136>, Int<144>, Int<152>, Int<160>, Int<168>, Int<176>, Int<184>, Int<192>, Int<200>, Int<208>, Int<216>, Int<224>, Int<232>, Int<240>, Int<248>, Int<256>, alloy_sol_types::sol_data::Uint<256>, alloy_sol_types::sol_data::Uint<8>, alloy_sol_types::sol_data::Uint<16>, alloy_sol_types::sol_data::Uint<24>, alloy_sol_types::sol_data::Uint<32>, alloy_sol_types::sol_data::Uint<40>, alloy_sol_types::sol_data::Uint<48>, alloy_sol_types::sol_data::Uint<56>, alloy_sol_types::sol_data::Uint<64>, alloy_sol_types::sol_data::Uint<72>, alloy_sol_types::sol_data::Uint<80>, alloy_sol_types::sol_data::Uint<88>, alloy_sol_types::sol_data::Uint<96>, alloy_sol_types::sol_data::Uint<104>, alloy_sol_types::sol_data::Uint<112>, alloy_sol_types::sol_data::Uint<120>, alloy_sol_types::sol_data::Uint<128>, alloy_sol_types::sol_data::Uint<136>, alloy_sol_types::sol_data::Uint<144>, alloy_sol_types::sol_data::Uint<152>, alloy_sol_types::sol_data::Uint<160>, alloy_sol_types::sol_data::Uint<168>, alloy_sol_types::sol_data::Uint<176>, alloy_sol_types::sol_data::Uint<184>, alloy_sol_types::sol_data::Uint<192>, alloy_sol_types::sol_data::Uint<200>, alloy_sol_types::sol_data::Uint<208>, alloy_sol_types::sol_data::Uint<216>, alloy_sol_types::sol_data::Uint<224>, alloy_sol_types::sol_data::Uint<232>, alloy_sol_types::sol_data::Uint<240>, alloy_sol_types::sol_data::Uint<248>, alloy_sol_types::sol_data::Uint<256>): SolType` is not satisfied - --> tests/ui/type.rs:3:1 - | -3 | / sol! { -4 | | struct BuiltinTypes { -5 | | address a; -6 | | address payable ap; -... | -111 | | } -112 | | } - | |_^ the trait `SolType` is not implemented for `(alloy_sol_types::sol_data::Address, alloy_sol_types::sol_data::Address, alloy_sol_types::sol_data::String, Bool, alloy_sol_types::sol_data::Bytes, alloy_sol_types::sol_data::FixedBytes<1>, alloy_sol_types::sol_data::FixedBytes<2>, alloy_sol_types::sol_data::FixedBytes<3>, alloy_sol_types::sol_data::FixedBytes<4>, alloy_sol_types::sol_data::FixedBytes<5>, alloy_sol_types::sol_data::FixedBytes<6>, alloy_sol_types::sol_data::FixedBytes<7>, alloy_sol_types::sol_data::FixedBytes<8>, alloy_sol_types::sol_data::FixedBytes<9>, alloy_sol_types::sol_data::FixedBytes<10>, alloy_sol_types::sol_data::FixedBytes<11>, alloy_sol_types::sol_data::FixedBytes<12>, alloy_sol_types::sol_data::FixedBytes<13>, alloy_sol_types::sol_data::FixedBytes<14>, alloy_sol_types::sol_data::FixedBytes<15>, alloy_sol_types::sol_data::FixedBytes<16>, alloy_sol_types::sol_data::FixedBytes<17>, alloy_sol_types::sol_data::FixedBytes<18>, alloy_sol_types::sol_data::FixedBytes<19>, alloy_sol_types::sol_data::FixedBytes<20>, alloy_sol_types::sol_data::FixedBytes<21>, alloy_sol_types::sol_data::FixedBytes<22>, alloy_sol_types::sol_data::FixedBytes<23>, alloy_sol_types::sol_data::FixedBytes<24>, alloy_sol_types::sol_data::FixedBytes<25>, alloy_sol_types::sol_data::FixedBytes<26>, alloy_sol_types::sol_data::FixedBytes<27>, alloy_sol_types::sol_data::FixedBytes<28>, alloy_sol_types::sol_data::FixedBytes<29>, alloy_sol_types::sol_data::FixedBytes<30>, alloy_sol_types::sol_data::FixedBytes<31>, alloy_sol_types::sol_data::FixedBytes<32>, Int<256>, Int<8>, Int<16>, Int<24>, Int<32>, Int<40>, Int<48>, Int<56>, Int<64>, Int<72>, Int<80>, Int<88>, Int<96>, Int<104>, Int<112>, Int<120>, Int<128>, Int<136>, Int<144>, Int<152>, Int<160>, Int<168>, Int<176>, Int<184>, Int<192>, Int<200>, Int<208>, Int<216>, Int<224>, Int<232>, Int<240>, Int<248>, Int<256>, alloy_sol_types::sol_data::Uint<256>, alloy_sol_types::sol_data::Uint<8>, alloy_sol_types::sol_data::Uint<16>, alloy_sol_types::sol_data::Uint<24>, alloy_sol_types::sol_data::Uint<32>, alloy_sol_types::sol_data::Uint<40>, alloy_sol_types::sol_data::Uint<48>, alloy_sol_types::sol_data::Uint<56>, alloy_sol_types::sol_data::Uint<64>, alloy_sol_types::sol_data::Uint<72>, alloy_sol_types::sol_data::Uint<80>, alloy_sol_types::sol_data::Uint<88>, alloy_sol_types::sol_data::Uint<96>, alloy_sol_types::sol_data::Uint<104>, alloy_sol_types::sol_data::Uint<112>, alloy_sol_types::sol_data::Uint<120>, alloy_sol_types::sol_data::Uint<128>, alloy_sol_types::sol_data::Uint<136>, alloy_sol_types::sol_data::Uint<144>, alloy_sol_types::sol_data::Uint<152>, alloy_sol_types::sol_data::Uint<160>, alloy_sol_types::sol_data::Uint<168>, alloy_sol_types::sol_data::Uint<176>, alloy_sol_types::sol_data::Uint<184>, alloy_sol_types::sol_data::Uint<192>, alloy_sol_types::sol_data::Uint<200>, alloy_sol_types::sol_data::Uint<208>, alloy_sol_types::sol_data::Uint<216>, alloy_sol_types::sol_data::Uint<224>, alloy_sol_types::sol_data::Uint<232>, alloy_sol_types::sol_data::Uint<240>, alloy_sol_types::sol_data::Uint<248>, alloy_sol_types::sol_data::Uint<256>)` - | - = help: the following other types implement trait `SolType`: - () - (T1,) - (T1, T2) - (T1, T2, T3) - (T1, T2, T3, T4) - (T1, T2, T3, T4, T5) - (T1, T2, T3, T4, T5, T6) - (T1, T2, T3, T4, T5, T6, T7) - and $N others -note: required by a bound in `Encodable` - --> src/types/ty.rs - | - | pub trait Encodable { - | ^^^^^^^ required by this bound in `Encodable` - = note: this error originates in the macro `sol` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0277]: the trait bound `(alloy_sol_types::sol_data::Address, alloy_sol_types::sol_data::Address, alloy_sol_types::sol_data::String, Bool, alloy_sol_types::sol_data::Bytes, alloy_sol_types::sol_data::FixedBytes<1>, alloy_sol_types::sol_data::FixedBytes<2>, alloy_sol_types::sol_data::FixedBytes<3>, alloy_sol_types::sol_data::FixedBytes<4>, alloy_sol_types::sol_data::FixedBytes<5>, alloy_sol_types::sol_data::FixedBytes<6>, alloy_sol_types::sol_data::FixedBytes<7>, alloy_sol_types::sol_data::FixedBytes<8>, alloy_sol_types::sol_data::FixedBytes<9>, alloy_sol_types::sol_data::FixedBytes<10>, alloy_sol_types::sol_data::FixedBytes<11>, alloy_sol_types::sol_data::FixedBytes<12>, alloy_sol_types::sol_data::FixedBytes<13>, alloy_sol_types::sol_data::FixedBytes<14>, alloy_sol_types::sol_data::FixedBytes<15>, alloy_sol_types::sol_data::FixedBytes<16>, alloy_sol_types::sol_data::FixedBytes<17>, alloy_sol_types::sol_data::FixedBytes<18>, alloy_sol_types::sol_data::FixedBytes<19>, alloy_sol_types::sol_data::FixedBytes<20>, alloy_sol_types::sol_data::FixedBytes<21>, alloy_sol_types::sol_data::FixedBytes<22>, alloy_sol_types::sol_data::FixedBytes<23>, alloy_sol_types::sol_data::FixedBytes<24>, alloy_sol_types::sol_data::FixedBytes<25>, alloy_sol_types::sol_data::FixedBytes<26>, alloy_sol_types::sol_data::FixedBytes<27>, alloy_sol_types::sol_data::FixedBytes<28>, alloy_sol_types::sol_data::FixedBytes<29>, alloy_sol_types::sol_data::FixedBytes<30>, alloy_sol_types::sol_data::FixedBytes<31>, alloy_sol_types::sol_data::FixedBytes<32>, Int<256>, Int<8>, Int<16>, Int<24>, Int<32>, Int<40>, Int<48>, Int<56>, Int<64>, Int<72>, Int<80>, Int<88>, Int<96>, Int<104>, Int<112>, Int<120>, Int<128>, Int<136>, Int<144>, Int<152>, Int<160>, Int<168>, Int<176>, Int<184>, Int<192>, Int<200>, Int<208>, Int<216>, Int<224>, Int<232>, Int<240>, Int<248>, Int<256>, alloy_sol_types::sol_data::Uint<256>, alloy_sol_types::sol_data::Uint<8>, alloy_sol_types::sol_data::Uint<16>, alloy_sol_types::sol_data::Uint<24>, alloy_sol_types::sol_data::Uint<32>, alloy_sol_types::sol_data::Uint<40>, alloy_sol_types::sol_data::Uint<48>, alloy_sol_types::sol_data::Uint<56>, alloy_sol_types::sol_data::Uint<64>, alloy_sol_types::sol_data::Uint<72>, alloy_sol_types::sol_data::Uint<80>, alloy_sol_types::sol_data::Uint<88>, alloy_sol_types::sol_data::Uint<96>, alloy_sol_types::sol_data::Uint<104>, alloy_sol_types::sol_data::Uint<112>, alloy_sol_types::sol_data::Uint<120>, alloy_sol_types::sol_data::Uint<128>, alloy_sol_types::sol_data::Uint<136>, alloy_sol_types::sol_data::Uint<144>, alloy_sol_types::sol_data::Uint<152>, alloy_sol_types::sol_data::Uint<160>, alloy_sol_types::sol_data::Uint<168>, alloy_sol_types::sol_data::Uint<176>, alloy_sol_types::sol_data::Uint<184>, alloy_sol_types::sol_data::Uint<192>, alloy_sol_types::sol_data::Uint<200>, alloy_sol_types::sol_data::Uint<208>, alloy_sol_types::sol_data::Uint<216>, alloy_sol_types::sol_data::Uint<224>, alloy_sol_types::sol_data::Uint<232>, alloy_sol_types::sol_data::Uint<240>, alloy_sol_types::sol_data::Uint<248>, alloy_sol_types::sol_data::Uint<256>): SolType` is not satisfied --> tests/ui/type.rs:3:1 | diff --git a/crates/sol-types/type_system.md b/crates/sol-types/type_system.md index e13f740d87..a6be06d552 100644 --- a/crates/sol-types/type_system.md +++ b/crates/sol-types/type_system.md @@ -1,6 +1,7 @@ # Solidity Type Representation -This crate is built around a representation of the Solidity type system. This doc is a primer for how we chose to represent Solidity types in Rust. +This crate is built around a representation of the Solidity type system. +This doc is a primer for how we chose to represent Solidity types in Rust. ## Why? @@ -9,7 +10,7 @@ Its internals are complex and may not be well-understood by Solidity devs. However, Solidity devs generally do understand Solidity types. As a result, we decided the best way to represent ABI coding was as a method on Solidity types. -Rather than `Coder::encode(data, type)` we felt that `Type::encode(data)` would +Rather than `Encoder::encode(data, type)` we felt that `Type::encode(data)` would be more intuitive and idiomatic in Rust. To achieve this, we give each Solidity type a concrete Rust type that contains its data. E.g. `bytes32` is `[u8; 32]`. `uint256` is `U256`, `string` is `String`. This allows programmers to work with @@ -22,7 +23,7 @@ this to be one of the fastest implementations for regular encoding/decoding. :) ## Downside This crate works only with types known at compile-time. For types known only at -runtime (including the eip712 `eth_signTypedData` json-rpc request), see the +runtime (including the eip712 `eth_signTypedData` JSON-RPC request), see the `alloy-dyn-abi` crate. ### To what extent? @@ -40,32 +41,22 @@ defs into `SolCall` types, but may in the future. **Support overview:** - First-class Solidity types - - All elementary, fixed-size, and non-fixed size [ABI types](https://docs.soliditylang.org/en/latest/abi-spec.html#types). - EXCEPT - - [`function` types](https://docs.soliditylang.org/en/latest/types.html#function-types). - [`fixed`](https://docs.soliditylang.org/en/latest/types.html#fixed-point-numbers). - - Compound Solidity types - - Arrays `T[N]` - Dynamic arrays `T[]` - Tuples `(T, U, ..)` - - User-defined Types - - - [Structs](https://solidity-by-example.org/structs/) represented as a tuple - of the field types. - - [User-defined Value Types](https://blog.soliditylang.org/2021/09/27/user-defined-value-types/), encoded transparently. - - [Enums](https://docs.soliditylang.org/en/latest/types.html#enums) (TODO) - represented as `u8`. - + - [Structs](https://solidity-by-example.org/structs/) represented as a tuple of the field types. + - [User-defined value types](https://blog.soliditylang.org/2021/09/27/user-defined-value-types/), encoded transparently. + - [Enums](https://docs.soliditylang.org/en/latest/types.html#enums) represented as `u8`. - Externalized Types - Function arguments and returns, represented as selector-prefixed tuples. - - [Errors](https://blog.soliditylang.org/2021/04/21/custom-errors/), - represented as selector-prefixed tuples - - Events (TODO) + - [Errors](https://blog.soliditylang.org/2021/04/21/custom-errors/), represented as selector-prefixed tuples + - Events, represented as a tuples of topic and data types. ## How? @@ -85,7 +76,7 @@ errors. These are each represented by a trait (`SolCall`, `SolEvent`, and `SolError`). These types enter or exit the EVM, or pass between callstack frames, and are not part of normal Solidity computation. However, they are composed of first-class types, and their ABI coding uses the first-class type's -ABI coding; +ABI coding. ### ⚠️ Rough Edge ⚠️ @@ -98,34 +89,34 @@ system to disallow it. ``` - SolError - SolCall -- SolEvent (TODO) - +- SolEvent - SolType ├── SolStruct - ├── SolEnum (TODO) - ├── UDTs - ├── address - ├── bytes + ├── SolEnum + ├── UDVTs + ├── bool + ├── bytesX (1 - 32) ├── intX (8 - 256) ├── uintX (8 - 256) - ├── bool + ├── address + ├── function (same as bytes24) + ├── bytes + ├── string ├── T[N] (Array) ├── T[] (Dynamic Array) - ├── string - ├── bytesX (1 - 32) └── Tuples `(T, U, ..)` ``` ### Trait Quick Reference -- `SolType` - Provides type name and properties, ABI coding, packed encoding, - and EIP-712 encoding. -- `SolError` - describes custom Error types with selector, and provides - specialized coding methods. -- `SolCall` - describes function **arguments** with selector, and provides - specialized coding methods. -- `SolEvent` - describes Event types with topic 0 and internal tuple, and - provides specialized coding methods. +- `SolType` - provides type name and properties, ABI coding, packed encoding, and EIP-712 encoding. +- `SolValue` - conveniency wrapper for `SolType` that provides the same interface but as methods on the value type itself. +- `SolStruct` - describes struct types, and provides specialized coding methods. +- `SolEnum` - describes enum types as `u8` wrappers, and provides specialized coding methods. +- `SolError` - describes custom Error types with selector, and provides specialized coding methods. +- `SolCall` - describes function **arguments** with selector, and provides specialized coding methods. + An associated `Return` type describes function returns. +- `SolEvent` - describes Event types with topics and data, and provides specialized coding methods. ## Implementing these traits @@ -138,6 +129,6 @@ Solidity snippets at compile time. ## Using these traits -Users will typically want to interact with `SolType`. When using errors, +Users will typically want to interact with `SolValue`. When using errors, events, or calls, users will want to import the relevant trait, and use the specialized coding methods. diff --git a/crates/syn-solidity/src/spanned.rs b/crates/syn-solidity/src/spanned.rs index b7b0796a8d..b22028415c 100644 --- a/crates/syn-solidity/src/spanned.rs +++ b/crates/syn-solidity/src/spanned.rs @@ -140,7 +140,7 @@ impl Spanned for Pair { } } -macro_rules! deref_impl { +macro_rules! deref_impls { ($($(#[$attr:meta])* [$($gen:tt)*] $t:ty),+ $(,)?) => {$( $(#[$attr])* impl<$($gen)*> Spanned for $t { @@ -157,7 +157,7 @@ macro_rules! deref_impl { )+}; } -deref_impl! { +deref_impls! { [T: ?Sized + Spanned] &mut T, [T: ?Sized + Spanned] Box, [T: Spanned] Vec,