From ff81902eaf9005fa49bc9b4106155868a8c95e9b Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 5 Oct 2023 02:03:32 +0200 Subject: [PATCH 01/20] feat(sol-types): make Encodable actually work --- crates/dyn-abi/src/resolve.rs | 4 +- crates/sol-macro/src/expand/enum.rs | 2 +- crates/sol-macro/src/expand/mod.rs | 4 +- crates/sol-macro/src/expand/struct.rs | 2 +- crates/sol-types/src/eip712.rs | 4 +- crates/sol-types/src/lib.rs | 6 +- crates/sol-types/src/types/data_type.rs | 104 ++++++--- crates/sol-types/src/types/event/topic.rs | 4 +- crates/sol-types/src/types/function.rs | 4 +- crates/sol-types/src/types/mod.rs | 4 +- crates/sol-types/src/types/st2.rs | 246 ++++++++++++++++++++++ crates/sol-types/src/types/ty.rs | 36 +++- crates/sol-types/src/types/udt.rs | 4 +- crates/syn-solidity/src/spanned.rs | 4 +- 14 files changed, 362 insertions(+), 66 deletions(-) create mode 100644 crates/sol-types/src/types/st2.rs 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..9ed1cd04a7 100644 --- a/crates/sol-macro/src/expand/enum.rs +++ b/crates/sol-macro/src/expand/enum.rs @@ -112,7 +112,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result } #[automatically_derived] - impl ::alloy_sol_types::Encodable<#name> for #name { + impl ::alloy_sol_types::SolTypeEncodable<#name> for #name { #[inline] fn to_tokens(&self) -> #uint8_st::TokenType<'_> { ::alloy_sol_types::Word::with_last_byte(*self as u8).into() diff --git a/crates/sol-macro/src/expand/mod.rs b/crates/sol-macro/src/expand/mod.rs index 83f2e0bb44..473cc6c4a0 100644 --- a/crates/sol-macro/src/expand/mod.rs +++ b/crates/sol-macro/src/expand/mod.rs @@ -569,10 +569,10 @@ fn expand_from_into_tuples

(name: &Ident, fields: &Parameters

) -> TokenStre #[automatically_derived] #[doc(hidden)] - impl ::alloy_sol_types::Encodable> for #name { + impl ::alloy_sol_types::SolTypeEncodable> for #name { fn to_tokens(&self) -> as ::alloy_sol_types::SolType>::TokenType<'_> { (#( - ::alloy_sol_types::Encodable::<#field_tys>::to_tokens(&self.#names3), + ::alloy_sol_types::SolTypeEncodable::<#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..9f88af7420 100644 --- a/crates/sol-macro/src/expand/struct.rs +++ b/crates/sol-macro/src/expand/struct.rs @@ -77,7 +77,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { #convert #[automatically_derived] - impl ::alloy_sol_types::Encodable for #name { + impl ::alloy_sol_types::SolTypeEncodable for #name { fn to_tokens(&self) -> ::TokenType<'_> { #tokenize_impl } diff --git a/crates/sol-types/src/eip712.rs b/crates/sol-types/src/eip712.rs index 578963a910..2f5b684ea1 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::{abi::token::WordToken, sol_data, SolType, SolTypeEncodable}; use alloc::{borrow::Cow, string::String, vec::Vec}; use alloy_primitives::{keccak256, Address, FixedBytes, B256, U256}; @@ -143,7 +143,7 @@ impl Eip712Domain { fn encode_opt(opt: Option<&T>, out: &mut Vec) where S: for<'a> SolType = WordToken>, - T: Encodable, + T: SolTypeEncodable, { if let Some(t) = opt { out.extend_from_slice(t.to_tokens().as_slice()); diff --git a/crates/sol-types/src/lib.rs b/crates/sol-types/src/lib.rs index 9e9d7dd7e9..d21b619b2e 100644 --- a/crates/sol-types/src/lib.rs +++ b/crates/sol-types/src/lib.rs @@ -176,9 +176,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, SolTypeEncodable, TopicList, }; pub mod utils; diff --git a/crates/sol-types/src/types/data_type.rs b/crates/sol-types/src/types/data_type.rs index b5a692261b..2867ba3ba7 100644 --- a/crates/sol-types/src/types/data_type.rs +++ b/crates/sol-types/src/types/data_type.rs @@ -6,7 +6,7 @@ #![allow(missing_copy_implementations, missing_debug_implementations)] -use crate::{abi::token::*, utils, Encodable, SolType, Word}; +use crate::{abi::token::*, utils, SolType, SolTypeEncodable, 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,7 +20,7 @@ use core::{borrow::Borrow, fmt::*, hash::Hash, marker::PhantomData, ops::*}; /// Bool - `bool` pub struct Bool; -impl Encodable for bool { +impl SolTypeEncodable for bool { #[inline] fn to_tokens(&self) -> WordToken { WordToken(Word::with_last_byte(*self as u8)) @@ -48,7 +48,7 @@ impl SolType for Bool { #[inline] fn eip712_data_word(rust: &Self::RustType) -> Word { - Encodable::::to_tokens(rust).0 + SolTypeEncodable::::to_tokens(rust).0 } #[inline] @@ -60,7 +60,7 @@ impl SolType for Bool { /// Int - `intX` pub struct Int; -impl Encodable> for T +impl SolTypeEncodable> for T where T: Borrow< as SupportedInt>::Int>, IntBitCount: SupportedInt, @@ -105,7 +105,7 @@ where #[inline] fn eip712_data_word(rust: &Self::RustType) -> Word { - Encodable::::to_tokens(rust).0 + SolTypeEncodable::::to_tokens(rust).0 } #[inline] @@ -117,7 +117,7 @@ where /// Uint - `uintX` pub struct Uint; -impl Encodable> for T +impl SolTypeEncodable> for T where T: Borrow< as SupportedInt>::Uint>, IntBitCount: SupportedInt, @@ -152,7 +152,7 @@ where #[inline] fn eip712_data_word(rust: &Self::RustType) -> Word { - Encodable::::to_tokens(rust).0 + SolTypeEncodable::::to_tokens(rust).0 } #[inline] @@ -164,7 +164,7 @@ where /// Address - `address` pub struct Address; -impl> Encodable

for T { +impl> SolTypeEncodable
for T { #[inline] fn to_tokens(&self) -> WordToken { WordToken(RustAddress::new(*self.borrow()).into_word()) @@ -204,7 +204,7 @@ impl SolType for Address { /// Function - `function` pub struct Function; -impl> Encodable for T { +impl> SolTypeEncodable for T { #[inline] fn to_tokens(&self) -> WordToken { WordToken(RustFunction::new(*self.borrow()).into_word()) @@ -244,7 +244,7 @@ impl SolType for Function { /// Bytes - `bytes` pub struct Bytes; -impl> Encodable for T { +impl> SolTypeEncodable for T { #[inline] fn to_tokens(&self) -> PackedSeqToken<'_> { PackedSeqToken(self.as_ref()) @@ -291,9 +291,9 @@ impl SolType for Bytes { /// Array - `T[]` pub struct Array(PhantomData); -impl Encodable> for [T] +impl SolTypeEncodable> for [T] where - T: Encodable, + T: SolTypeEncodable, U: SolType, { #[inline] @@ -302,14 +302,36 @@ where } } -impl Encodable> for Vec +impl SolTypeEncodable> for &[T] where - T: Encodable, + T: SolTypeEncodable, U: SolType, { #[inline] fn to_tokens(&self) -> DynSeqToken> { - <[T] as Encodable>>::to_tokens(self) + (**self).to_tokens() + } +} + +impl SolTypeEncodable> for &mut [T] +where + T: SolTypeEncodable, + U: SolType, +{ + #[inline] + fn to_tokens(&self) -> DynSeqToken> { + (**self).to_tokens() + } +} + +impl SolTypeEncodable> for Vec +where + T: SolTypeEncodable, + U: SolType, +{ + #[inline] + fn to_tokens(&self) -> DynSeqToken> { + <[T] as SolTypeEncodable>>::to_tokens(self) } } @@ -361,7 +383,7 @@ impl SolType for Array { /// String - `string` pub struct String; -impl> Encodable for T { +impl> SolTypeEncodable for T { #[inline] fn to_tokens(&self) -> PackedSeqToken<'_> { PackedSeqToken(self.as_ref().as_bytes()) @@ -413,10 +435,9 @@ impl SolType for String { #[derive(Clone, Copy, Debug)] pub struct FixedBytes; -impl Encodable> for T +impl, const N: usize> SolTypeEncodable> for T where ByteCount: SupportedFixedBytes, - T: Borrow<[u8; N]>, { #[inline] fn to_tokens(&self) -> as SolType>::TokenType<'_> { @@ -450,7 +471,7 @@ where #[inline] fn eip712_data_word(rust: &Self::RustType) -> Word { - Encodable::::to_tokens(rust).0 + SolTypeEncodable::::to_tokens(rust).0 } #[inline] @@ -463,16 +484,36 @@ where /// FixedArray - `T[M]` pub struct FixedArray(PhantomData); -impl Encodable> for [T; N] +impl SolTypeEncodable> for [T; N] +where + T: SolTypeEncodable, + U: SolType, +{ + #[inline] + fn to_tokens(&self) -> as SolType>::TokenType<'_> { + FixedSeqToken(core::array::from_fn(|i| self[i].to_tokens())) + } +} + +impl SolTypeEncodable> for &[T; N] +where + T: SolTypeEncodable, + U: SolType, +{ + #[inline] + fn to_tokens(&self) -> as SolType>::TokenType<'_> { + <[T; N] as SolTypeEncodable>>::to_tokens(&**self) + } +} + +impl SolTypeEncodable> for &mut [T; N] where - T: Encodable, + T: SolTypeEncodable, U: SolType, { #[inline] fn to_tokens(&self) -> as SolType>::TokenType<'_> { - FixedSeqToken::<_, N>(core::array::from_fn(|i| { - Encodable::::to_tokens(&self[i]) - })) + <[T; N] as SolTypeEncodable>>::to_tokens(&**self) } } @@ -533,18 +574,11 @@ impl SolType for FixedArray { macro_rules! tuple_encodable_impls { ($(($ty:ident $uty:ident)),+) => { #[allow(non_snake_case)] - impl<$($ty: SolType, $uty,)+> Encodable<($($ty,)+)> for ($( $uty, )+) - where - $($uty: $crate::Encodable<$ty>,)+ - { + impl<$($ty: SolTypeEncodable<$uty>, $uty: SolType),+> SolTypeEncodable<($($uty,)+)> for ($($ty,)+) { #[inline] - fn to_tokens(&self) -> <($($ty,)+) as SolType>::TokenType<'_> { + fn to_tokens(&self) -> <($($uty,)+) as SolType>::TokenType<'_> { let ($($ty,)+) = self; - ( - $( - Encodable::<$ty>::to_tokens($ty), - )+ - ) + ($(SolTypeEncodable::<$uty>::to_tokens($ty),)+) } } }; @@ -635,7 +669,7 @@ macro_rules! tuple_impls { }; } -impl Encodable<()> for () { +impl SolTypeEncodable<()> for () { #[inline] fn to_tokens(&self) {} } diff --git a/crates/sol-types/src/types/event/topic.rs b/crates/sol-types/src/types/event/topic.rs index 1b5f0abf46..150e252a38 100644 --- a/crates/sol-types/src/types/event/topic.rs +++ b/crates/sol-types/src/types/event/topic.rs @@ -49,12 +49,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::SolTypeEncodable::::to_tokens(rust).0 .0); } #[inline] fn encode_topic(rust: &Self::RustType) -> WordToken { - $crate::Encodable::::to_tokens(rust) + $crate::SolTypeEncodable::::to_tokens(rust) } }; } diff --git a/crates/sol-types/src/types/function.rs b/crates/sol-types/src/types/function.rs index d35fccd4e2..c477ed40e7 100644 --- a/crates/sol-types/src/types/function.rs +++ b/crates/sol-types/src/types/function.rs @@ -1,6 +1,6 @@ use crate::{ abi::{TokenSeq, TokenType}, - Encodable, Result, SolType, Word, + Result, SolType, SolTypeEncodable, Word, }; use alloc::vec::Vec; @@ -93,7 +93,7 @@ pub trait SolCall: Sized { #[inline] fn abi_encode_returns<'a, E>(e: &'a E) -> Vec where - E: Encodable>, + E: SolTypeEncodable>, { crate::abi::encode_sequence(&e.to_tokens()) } diff --git a/crates/sol-types/src/types/mod.rs b/crates/sol-types/src/types/mod.rs index da6f7ebe84..c25a399862 100644 --- a/crates/sol-types/src/types/mod.rs +++ b/crates/sol-types/src/types/mod.rs @@ -19,8 +19,10 @@ mod r#struct; pub use r#struct::SolStruct; mod ty; -pub use ty::{Encodable, SolType}; +pub use ty::{SolType, SolTypeEncodable}; // Solidity user-defined value types. // No exports are needed as the only item is a macro. mod udt; + +mod st2; diff --git a/crates/sol-types/src/types/st2.rs b/crates/sol-types/src/types/st2.rs new file mode 100644 index 0000000000..fba686fc55 --- /dev/null +++ b/crates/sol-types/src/types/st2.rs @@ -0,0 +1,246 @@ +use super::{SolType, SolTypeEncodable}; +use crate::sol_data::{self, ByteCount, SupportedFixedBytes}; +use alloc::{borrow::Cow, string::String, vec::Vec}; +use alloy_primitives::{Address, Bytes, FixedBytes, Function, I256, U256}; + +trait Encodable { + type SolType: SolType; + + #[inline] + fn abi_encode(&self) -> Vec + where + Self: SolTypeEncodable, + { + SolType::abi_encode(self) + } + + #[inline] + fn sol_type_name(&self) -> Cow<'static, str> { + Self::SolType::sol_type_name() + } +} + +macro_rules! impl_encodable { + ($($(#[$attr:meta])* [$($gen:tt)*] $rust:ty => $sol:ty [$($where:tt)*];)+) => {$( + $(#[$attr])* + impl<$($gen)*> Encodable for $rust $($where)* { + type SolType = $sol; + } + )*}; +} + +impl_encodable! { + // 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: Array is specialized to encode as `bytes` + // [] 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]; + [] String => sol_data::String []; + [] str => sol_data::String []; + [] Bytes => sol_data::Bytes []; + + // Specialize u8 to bytes + [] Vec => sol_data::Bytes []; + [] [u8] => sol_data::Bytes []; + [const N: usize] [u8; N] => sol_data::Bytes []; + + // Generic + [T: Encodable] Vec => sol_data::Array []; + [T: Encodable] [T] => sol_data::Array []; + [T: Encodable, const N: usize] [T; N] => sol_data::FixedArray []; +} + +// Have to override the `Self: SolTypeEncodable` bound for these +// because `SolTypeEncodable` is not implemented for references +macro_rules! deref_impls { + ($($(#[$attr:meta])* [$($gen:tt)*] $rust:ty => $sol:ty [$($where:tt)*];)+) => {$( + $(#[$attr])* + impl<$($gen)*> Encodable for $rust $($where)* { + type SolType = $sol; + + #[inline] + fn abi_encode(&self) -> Vec { + (**self).abi_encode() + } + } + )*}; +} + +deref_impls! { + [T: ?Sized + Encodable + SolTypeEncodable] &T => T::SolType []; + [T: ?Sized + Encodable + SolTypeEncodable] &mut T => T::SolType []; + [T: ?Sized + Encodable + SolTypeEncodable] alloc::boxed::Box => T::SolType []; + [T: ?Sized + alloc::borrow::ToOwned + Encodable + SolTypeEncodable] alloc::borrow::Cow<'_, T> => T::SolType []; + [T: ?Sized + Encodable + SolTypeEncodable] alloc::rc::Rc => T::SolType []; + [T: ?Sized + Encodable + SolTypeEncodable] alloc::sync::Arc => T::SolType []; +} + +macro_rules! tuple_impls { + ($count:literal $($ty:ident),+) => { + impl<$($ty: Encodable,)+> Encodable for ($($ty,)+) { + type SolType = ($($ty::SolType,)+); + } + }; +} + +impl Encodable for () { + type SolType = (); +} + +all_the_tuples!(tuple_impls); + +#[cfg(test)] +mod tests { + use super::*; + use crate::Word; + + // Make sure these are in scope + #[allow(unused_imports)] + use crate::{SolType as _, SolTypeEncodable as _}; + + #[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() + }; + 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!(b"".abi_encode(), encode_bytes(b"")); + assert_eq!(b"a".abi_encode(), encode_bytes(b"a")); + assert_eq!((b"" as &[_]).abi_encode(), encode_bytes(b"")); + assert_eq!((b"a" as &[_]).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), + Vec::from(b"bbbb"), + b"cccc", + &b"dddd"[..], + ), + ); + assert_eq!( + tuple.sol_type_name(), + "(uint64,string,bool,(string,address,bytes,bytes,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""; + let x = (&mut x, *b"aaaa", &mut &mut b""); + x.abi_encode(); + assert_eq!(x.sol_type_name(), "(bytes,bytes,bytes)"); + + let tuple = &(&0u16, &"", &mut b"", &mut [Address::ZERO][..]); + tuple.abi_encode(); + assert_eq!(tuple.sol_type_name(), "(uint16,string,bytes,address[])"); + } +} diff --git a/crates/sol-types/src/types/ty.rs b/crates/sol-types/src/types/ty.rs index 0557b686c3..2e81a10b5d 100644 --- a/crates/sol-types/src/types/ty.rs +++ b/crates/sol-types/src/types/ty.rs @@ -57,15 +57,29 @@ use alloc::{borrow::Cow, vec::Vec}; /// # Ok(()) /// # } /// ``` -pub trait Encodable { +pub trait SolTypeEncodable { /// 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() - } +macro_rules! deref_impls { + ($($(#[$attr:meta])* [$($gen:tt)*] $t:ty),* $(,)?) => {$( + $(#[$attr])* + impl<$($gen)*, U: SolType> SolTypeEncodable for $t { + #[inline] + fn to_tokens(&self) -> <$t as SolType>::TokenType<'_> { + (**self).to_tokens() + } + } + )*}; +} + +deref_impls! { + // [T: ?Sized + SolTypeEncodable] &T, + // [T: ?Sized + SolTypeEncodable] &mut T, + // [T: ?Sized + alloc::borrow::ToOwned + SolTypeEncodable] alloc::borrow::Cow<'_, T>, + // [T: ?Sized + SolTypeEncodable] alloc::rc::Rc, + // [T: ?Sized + SolTypeEncodable] alloc::sync::Arc, } /// A Solidity Type, for ABI encoding and decoding @@ -117,7 +131,7 @@ pub trait Encodable { /// ``` pub trait SolType { /// The corresponding Rust type. - type RustType: Encodable + 'static; + type RustType: SolTypeEncodable + 'static; /// The corresponding ABI token type. /// @@ -162,7 +176,7 @@ pub trait SolType { fn detokenize(token: Self::TokenType<'_>) -> Self::RustType; /// Tokenizes the given value into this type's token. - fn tokenize>(rust: &E) -> Self::TokenType<'_> { + fn tokenize>(rust: &E) -> Self::TokenType<'_> { rust.to_tokens() } @@ -199,13 +213,13 @@ pub trait SolType { /// Encode a single ABI token by wrapping it in a 1-length sequence. #[inline] - fn abi_encode>(rust: &E) -> Vec { + fn abi_encode>(rust: &E) -> Vec { abi::encode(&rust.to_tokens()) } /// Encode an ABI sequence. #[inline] - fn abi_encode_sequence>(rust: &E) -> Vec + fn abi_encode_sequence>(rust: &E) -> Vec where for<'a> Self::TokenType<'a>: TokenSeq<'a>, { @@ -214,7 +228,7 @@ pub trait SolType { /// Encode an ABI sequence suitable for function parameters. #[inline] - fn abi_encode_params>(rust: &E) -> Vec + fn abi_encode_params>(rust: &E) -> Vec where for<'a> Self::TokenType<'a>: TokenSeq<'a>, { diff --git a/crates/sol-types/src/types/udt.rs b/crates/sol-types/src/types/udt.rs index 9292efe373..ce2b64e9ca 100644 --- a/crates/sol-types/src/types/udt.rs +++ b/crates/sol-types/src/types/udt.rs @@ -19,10 +19,10 @@ macro_rules! define_udt { <$underlying as $crate::SolType>::RustType, ); - impl $crate::Encodable<$name> for <$underlying as $crate::SolType>::RustType { + impl $crate::SolTypeEncodable<$name> for <$underlying as $crate::SolType>::RustType { #[inline] fn to_tokens(&self) -> <$underlying as $crate::SolType>::TokenType<'_> { - $crate::Encodable::<$underlying>::to_tokens(self) + $crate::SolTypeEncodable::<$underlying>::to_tokens(self) } } diff --git a/crates/syn-solidity/src/spanned.rs b/crates/syn-solidity/src/spanned.rs index 10128dc9c7..f7defce377 100644 --- a/crates/syn-solidity/src/spanned.rs +++ b/crates/syn-solidity/src/spanned.rs @@ -139,7 +139,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 { @@ -156,7 +156,7 @@ macro_rules! deref_impl { )+}; } -deref_impl! { +deref_impls! { [T: ?Sized + Spanned] &mut T, [T: ?Sized + Spanned] Box, [T: Spanned] Vec, From 02462e20b3d228a3c5eb3116a948bc298ec661e5 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:18:43 +0200 Subject: [PATCH 02/20] add more impls to both encodables --- crates/sol-macro/src/expand/enum.rs | 24 +- crates/sol-macro/src/expand/mod.rs | 13 - crates/sol-macro/src/expand/struct.rs | 42 +- crates/sol-types/src/abi/encoder.rs | 8 +- crates/sol-types/src/lib.rs | 6 +- crates/sol-types/src/macros.rs | 48 +- crates/sol-types/src/types/data_type.rs | 439 ++++++++++-------- .../src/types/{st2.rs => encodable.rs} | 211 ++++++++- crates/sol-types/src/types/mod.rs | 5 +- crates/sol-types/src/types/ty.rs | 87 ++-- crates/sol-types/src/types/udt.rs | 20 +- crates/sol-types/tests/ui/type.stderr | 29 -- 12 files changed, 567 insertions(+), 365 deletions(-) rename crates/sol-types/src/types/{st2.rs => encodable.rs} (54%) diff --git a/crates/sol-macro/src/expand/enum.rs b/crates/sol-macro/src/expand/enum.rs index 9ed1cd04a7..099818d3f7 100644 --- a/crates/sol-macro/src/expand/enum.rs +++ b/crates/sol-macro/src/expand/enum.rs @@ -117,6 +117,16 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result fn to_tokens(&self) -> #uint8_st::TokenType<'_> { ::alloy_sol_types::Word::with_last_byte(*self as u8).into() } + + #[inline] + fn eip712_data_word(&self) -> ::alloy_sol_types::Word { + #uint8_st::eip712_data_word(self.as_u8()) + } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut ::alloy_sol_types::private::Vec) { + out.push(*self as u8); + } } #[automatically_derived] @@ -150,16 +160,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 +175,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 473cc6c4a0..763ff4bba0 100644 --- a/crates/sol-macro/src/expand/mod.rs +++ b/crates/sol-macro/src/expand/mod.rs @@ -538,9 +538,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! { @@ -566,16 +563,6 @@ fn expand_from_into_tuples

(name: &Ident, fields: &Parameters

) -> TokenStre } } } - - #[automatically_derived] - #[doc(hidden)] - impl ::alloy_sol_types::SolTypeEncodable> for #name { - fn to_tokens(&self) -> as ::alloy_sol_types::SolType>::TokenType<'_> { - (#( - ::alloy_sol_types::SolTypeEncodable::<#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 9f88af7420..a84f56eb3a 100644 --- a/crates/sol-macro/src/expand/struct.rs +++ b/crates/sol-macro/src/expand/struct.rs @@ -81,6 +81,25 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { fn to_tokens(&self) -> ::TokenType<'_> { #tokenize_impl } + + #[inline] + fn 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 eip712_data_word(&self) -> ::alloy_sol_types::Word { + ::eip712_hash_struct(self) + } + + #[inline] + fn 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 +114,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 +124,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 +156,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/encoder.rs b/crates/sol-types/src/abi/encoder.rs index de38ef2294..e62fdb8901 100644 --- a/crates/sol-types/src/abi/encoder.rs +++ b/crates/sol-types/src/abi/encoder.rs @@ -448,7 +448,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 +467,7 @@ mod tests { sol_data::Array, sol_data::Array, ); - let data = (vec![], vec![]); + let data: (Vec
, Vec
) = (vec![], vec![]); let expected = hex!( " @@ -493,7 +493,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 +546,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/lib.rs b/crates/sol-types/src/lib.rs index d21b619b2e..18ed47bd65 100644 --- a/crates/sol-types/src/lib.rs +++ b/crates/sol-types/src/lib.rs @@ -176,9 +176,9 @@ mod impl_core; mod types; pub use types::{ - data_type as sol_data, decode_revert_reason, ContractError, EventTopic, GenericContractError, - Panic, PanicKind, Revert, Selectors, SolCall, SolEnum, SolError, SolEvent, SolInterface, - SolStruct, SolType, SolTypeEncodable, TopicList, + data_type as sol_data, decode_revert_reason, ContractError, Encodable, EventTopic, + GenericContractError, Panic, PanicKind, Revert, Selectors, SolCall, SolEnum, SolError, + SolEvent, SolInterface, SolStruct, SolType, SolTypeEncodable, TopicList, }; pub mod utils; 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 2867ba3ba7..f1c7f43870 100644 --- a/crates/sol-types/src/types/data_type.rs +++ b/crates/sol-types/src/types/data_type.rs @@ -25,6 +25,16 @@ impl SolTypeEncodable for bool { fn to_tokens(&self) -> WordToken { WordToken(Word::with_last_byte(*self as u8)) } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + out.push(*self as u8); + } + + #[inline] + fn eip712_data_word(&self) -> Word { + SolTypeEncodable::::to_tokens(self).0 + } } impl SolType for Bool { @@ -45,16 +55,6 @@ impl SolType for Bool { fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { token.0 != Word::ZERO } - - #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - SolTypeEncodable::::to_tokens(rust).0 - } - - #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - out.push(*rust as u8); - } } /// Int - `intX` @@ -69,6 +69,16 @@ where fn to_tokens(&self) -> WordToken { IntBitCount::::tokenize_int(*self.borrow()) } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + IntBitCount::::encode_packed_to_int(*self.borrow(), out); + } + + #[inline] + fn eip712_data_word(&self) -> Word { + SolTypeEncodable::>::to_tokens(self).0 + } } impl SolType for Int @@ -102,16 +112,6 @@ where fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { IntBitCount::::detokenize_int(token) } - - #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - SolTypeEncodable::::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` @@ -126,6 +126,16 @@ where fn to_tokens(&self) -> WordToken { IntBitCount::::tokenize_uint(*self.borrow()) } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + IntBitCount::::encode_packed_to_uint(*self.borrow(), out); + } + + #[inline] + fn eip712_data_word(&self) -> Word { + SolTypeEncodable::>::to_tokens(self).0 + } } impl SolType for Uint @@ -149,16 +159,6 @@ where fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { IntBitCount::::detokenize_uint(token) } - - #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - SolTypeEncodable::::to_tokens(rust).0 - } - - #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - IntBitCount::::encode_packed_to_uint(*rust, out); - } } /// Address - `address` @@ -169,6 +169,16 @@ impl> SolTypeEncodable
for T { fn to_tokens(&self) -> WordToken { WordToken(RustAddress::new(*self.borrow()).into_word()) } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + out.extend_from_slice(self.borrow()); + } + + #[inline] + fn eip712_data_word(&self) -> Word { + SolTypeEncodable::
::to_tokens(self).0 + } } impl SolType for Address { @@ -189,16 +199,6 @@ impl SolType for Address { fn valid_token(token: &Self::TokenType<'_>) -> bool { utils::check_zeroes(&token.0[..12]) } - - #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - rust.into_word() - } - - #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - out.extend_from_slice(rust.as_slice()); - } } /// Function - `function` @@ -209,6 +209,16 @@ impl> SolTypeEncodable for T { fn to_tokens(&self) -> WordToken { WordToken(RustFunction::new(*self.borrow()).into_word()) } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + out.extend_from_slice(self.borrow()); + } + + #[inline] + fn eip712_data_word(&self) -> Word { + SolTypeEncodable::::to_tokens(self).0 + } } impl SolType for Function { @@ -229,16 +239,6 @@ impl SolType for Function { fn valid_token(token: &Self::TokenType<'_>) -> bool { utils::check_zeroes(&token.0[24..]) } - - #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - rust.into_word() - } - - #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - out.extend_from_slice(rust.as_slice()); - } } /// Bytes - `bytes` @@ -249,6 +249,21 @@ impl> SolTypeEncodable for T { fn to_tokens(&self) -> PackedSeqToken<'_> { PackedSeqToken(self.as_ref()) } + + #[inline] + fn abi_encoded_size(&self) -> usize { + 32 + utils::padded_len(self.as_ref()) + } + + #[inline] + fn eip712_data_word(&self) -> Word { + keccak256(Bytes::abi_encode_packed(self)) + } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + out.extend_from_slice(self.as_ref()); + } } impl SolType for Bytes { @@ -262,11 +277,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,16 +286,6 @@ impl SolType for Bytes { fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { token.into_vec() } - - #[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); - } } /// Array - `T[]` @@ -298,7 +298,29 @@ where { #[inline] fn to_tokens(&self) -> DynSeqToken> { - DynSeqToken(self.iter().map(|r| r.to_tokens()).collect()) + DynSeqToken(self.iter().map(T::to_tokens).collect()) + } + + #[inline] + fn abi_encoded_size(&self) -> usize { + 32 + self.iter().map(T::abi_encoded_size).sum::() + + (U::DYNAMIC as usize * 32 * self.len()) + } + + #[inline] + fn eip712_data_word(&self) -> Word { + let mut encoded = Vec::new(); + for item in self { + encoded.extend_from_slice(T::eip712_data_word(item).as_slice()); + } + keccak256(encoded) + } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + for item in self { + T::abi_encode_packed_to(item, out); + } } } @@ -311,6 +333,21 @@ where fn to_tokens(&self) -> DynSeqToken> { (**self).to_tokens() } + + #[inline] + fn abi_encoded_size(&self) -> usize { + (**self).abi_encoded_size() + } + + #[inline] + fn eip712_data_word(&self) -> Word { + (**self).eip712_data_word() + } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + (**self).abi_encode_packed_to(out) + } } impl SolTypeEncodable> for &mut [T] @@ -322,6 +359,21 @@ where fn to_tokens(&self) -> DynSeqToken> { (**self).to_tokens() } + + #[inline] + fn abi_encoded_size(&self) -> usize { + (**self).abi_encoded_size() + } + + #[inline] + fn eip712_data_word(&self) -> Word { + (**self).eip712_data_word() + } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + (**self).abi_encode_packed_to(out) + } } impl SolTypeEncodable> for Vec @@ -333,6 +385,21 @@ where fn to_tokens(&self) -> DynSeqToken> { <[T] as SolTypeEncodable>>::to_tokens(self) } + + #[inline] + fn abi_encoded_size(&self) -> usize { + (**self).abi_encoded_size() + } + + #[inline] + fn eip712_data_word(&self) -> Word { + (**self).eip712_data_word() + } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + (**self).abi_encode_packed_to(out) + } } impl SolType for Array { @@ -346,13 +413,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) @@ -362,22 +422,6 @@ impl SolType for Array { fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { token.0.into_iter().map(T::detokenize).collect() } - - #[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) - } - - #[inline] - fn abi_encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - for item in rust { - T::abi_encode_packed_to(item, out); - } - } } /// String - `string` @@ -388,6 +432,21 @@ impl> SolTypeEncodable for T { fn to_tokens(&self) -> PackedSeqToken<'_> { PackedSeqToken(self.as_ref().as_bytes()) } + + #[inline] + fn abi_encoded_size(&self) -> usize { + 32 + utils::padded_len(self.as_ref().as_ref()) + } + + #[inline] + fn eip712_data_word(&self) -> Word { + keccak256(String::abi_encode_packed(self)) + } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + out.extend_from_slice(self.as_ref().as_ref()); + } } impl SolType for String { @@ -401,11 +460,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() @@ -419,16 +473,6 @@ 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` @@ -445,6 +489,16 @@ where word[..N].copy_from_slice(self.borrow()); word.into() } + + #[inline] + fn eip712_data_word(&self) -> Word { + SolTypeEncodable::>::to_tokens(self).0 + } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + out.extend_from_slice(self.borrow().as_slice()); + } } impl SolType for FixedBytes @@ -468,17 +522,6 @@ where fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { token.0[..N].try_into().unwrap() } - - #[inline] - fn eip712_data_word(rust: &Self::RustType) -> Word { - SolTypeEncodable::::to_tokens(rust).0 - } - - #[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()); - } } /// FixedArray - `T[M]` @@ -493,6 +536,33 @@ where fn to_tokens(&self) -> as SolType>::TokenType<'_> { FixedSeqToken(core::array::from_fn(|i| self[i].to_tokens())) } + + #[inline] + fn abi_encoded_size(&self) -> usize { + if let Some(size) = FixedArray::::ENCODED_SIZE { + return size + } + + self.iter().map(T::abi_encoded_size).sum::() + (U::DYNAMIC as usize * N * 32) + } + + #[inline] + fn 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::eip712_data_word(element).0) + .collect::>(); + keccak256(crate::impl_core::into_flattened(encoded)) + } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + for item in self { + T::abi_encode_packed_to(item, out); + } + } } impl SolTypeEncodable> for &[T; N] @@ -504,6 +574,21 @@ where fn to_tokens(&self) -> as SolType>::TokenType<'_> { <[T; N] as SolTypeEncodable>>::to_tokens(&**self) } + + #[inline] + fn abi_encoded_size(&self) -> usize { + SolTypeEncodable::>::abi_encoded_size(&**self) + } + + #[inline] + fn eip712_data_word(&self) -> Word { + SolTypeEncodable::>::eip712_data_word(&**self) + } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + SolTypeEncodable::>::abi_encode_packed_to(&**self, out) + } } impl SolTypeEncodable> for &mut [T; N] @@ -515,6 +600,21 @@ where fn to_tokens(&self) -> as SolType>::TokenType<'_> { <[T; N] as SolTypeEncodable>>::to_tokens(&**self) } + + #[inline] + fn abi_encoded_size(&self) -> usize { + SolTypeEncodable::>::abi_encoded_size(&**self) + } + + #[inline] + fn eip712_data_word(&self) -> Word { + SolTypeEncodable::>::eip712_data_word(&**self) + } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut Vec) { + SolTypeEncodable::>::abi_encode_packed_to(&**self, out) + } } impl SolType for FixedArray { @@ -528,15 +628,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() @@ -551,28 +642,10 @@ 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: SolTypeEncodable<$uty>, $uty: SolType),+> SolTypeEncodable<($($uty,)+)> for ($($ty,)+) { #[inline] @@ -580,6 +653,38 @@ macro_rules! tuple_encodable_impls { let ($($ty,)+) = self; ($(SolTypeEncodable::<$uty>::to_tokens($ty),)+) } + + fn 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 abi_encode_packed_to(&self, out: &mut Vec) { + let ($($ty,)+) = self; + // TODO: Reserve + $( + <$uty as SolType>::abi_encode_packed_to($ty, out); + )+ + } + + fn 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() + } } }; } @@ -622,20 +727,6 @@ macro_rules! tuple_impls { ).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) - )+ - } - fn valid_token(token: &Self::TokenType<'_>) -> bool { let ($($ty,)+) = token; $(<$ty as SolType>::valid_token($ty))&&+ @@ -647,24 +738,6 @@ 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); - )+ - } } }; } @@ -672,6 +745,14 @@ macro_rules! tuple_impls { impl SolTypeEncodable<()> for () { #[inline] fn to_tokens(&self) {} + + #[inline] + fn eip712_data_word(&self) -> Word { + Word::ZERO + } + + #[inline] + fn abi_encode_packed_to(&self, _out: &mut Vec) {} } all_the_tuples!(@double tuple_encodable_impls); @@ -694,14 +775,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/st2.rs b/crates/sol-types/src/types/encodable.rs similarity index 54% rename from crates/sol-types/src/types/st2.rs rename to crates/sol-types/src/types/encodable.rs index fba686fc55..3183df64dc 100644 --- a/crates/sol-types/src/types/st2.rs +++ b/crates/sol-types/src/types/encodable.rs @@ -1,22 +1,152 @@ use super::{SolType, SolTypeEncodable}; -use crate::sol_data::{self, ByteCount, SupportedFixedBytes}; +use crate::{ + abi::TokenSeq, + sol_data::{self, ByteCount, SupportedFixedBytes}, + Result, Word, +}; use alloc::{borrow::Cow, string::String, vec::Vec}; use alloy_primitives::{Address, Bytes, FixedBytes, Function, I256, U256}; -trait Encodable { +/// ABI-encodable types. +/// +/// This is a convenience trait that combines the logic in [`SolTypeEncodable`] +/// and [`SolType`] to provide a single trait for encoding and decoding Solidity +/// types. +/// +/// # Examples +/// +/// ``` +/// ``` +pub trait Encodable { + /// 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() + } + + /// Calculate the ABI-encoded size of the data. + /// + /// See [`SolType::abi_encoded_size`] for more information. + #[inline] + fn abi_encoded_size(&self) -> usize + where + Self: SolTypeEncodable, + { + >::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 + where + Self: SolTypeEncodable, + { + >::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) + where + Self: SolTypeEncodable, + { + >::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 + where + Self: SolTypeEncodable, + { + let mut out = Vec::new(); + >::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 where Self: SolTypeEncodable, { - SolType::abi_encode(self) + Self::SolType::abi_encode(self) } + /// Encodes an ABI sequence. + /// + /// See [`SolType::abi_encode_sequence`] for more information. #[inline] - fn sol_type_name(&self) -> Cow<'static, str> { - Self::SolType::sol_type_name() + fn abi_encode_sequence(&self) -> Vec + where + Self: SolTypeEncodable, + 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 + Self: SolTypeEncodable, + 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<'de>( + data: &'de [u8], + validate: bool, + ) -> Result<::RustType> { + Self::SolType::abi_decode(data, validate) + } + + /// 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<::RustType> + where + ::TokenType<'de>: TokenSeq<'de>, + { + Self::SolType::abi_decode_params(data, validate) + } + + /// 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<::RustType> + where + ::TokenType<'de>: TokenSeq<'de>, + { + Self::SolType::abi_decode_sequence(data, validate) } } @@ -44,7 +174,7 @@ impl_encodable! { #[cfg(pointer_width = "64")] [] isize => sol_data::Int::<64> []; - // TODO: Array is specialized to encode as `bytes` + // TODO: Array is specialized to encode as `bytes[N]` // [] u8 => sol_data::Uint::<8> []; [] u16 => sol_data::Uint::<16> []; [] u32 => sol_data::Uint::<32> []; @@ -59,14 +189,13 @@ impl_encodable! { [] 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 []; - // Specialize u8 to bytes [] Vec => sol_data::Bytes []; [] [u8] => sol_data::Bytes []; - [const N: usize] [u8; N] => sol_data::Bytes []; // Generic [T: Encodable] Vec => sol_data::Array []; @@ -116,12 +245,32 @@ all_the_tuples!(tuple_impls); #[cfg(test)] mod tests { use super::*; - use crate::Word; // Make sure these are in scope #[allow(unused_imports)] use crate::{SolType as _, SolTypeEncodable 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(); + } + #[test] fn basic() { assert_eq!(false.abi_encode(), Word::ZERO[..]); @@ -156,14 +305,20 @@ mod tests { ] .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!(b"".abi_encode(), encode_bytes(b"")); - assert_eq!(b"a".abi_encode(), encode_bytes(b"a")); - assert_eq!((b"" as &[_]).abi_encode(), encode_bytes(b"")); - assert_eq!((b"a" as &[_]).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")); } @@ -212,7 +367,7 @@ mod tests { ); assert_eq!( tuple.sol_type_name(), - "(uint64,string,bool,(string,address,bytes,bytes,bytes))" + "(uint64,string,bool,(string,address,bytes,bytes4,bytes))" ); } @@ -234,13 +389,31 @@ mod tests { x.abi_encode(); assert_eq!(x.sol_type_name(), "address[]"); - let mut x = *b""; - let x = (&mut x, *b"aaaa", &mut &mut b""); + let mut x = *b"0"; + let x = (&mut x, *b"aaaa", b"00"); x.abi_encode(); - assert_eq!(x.sol_type_name(), "(bytes,bytes,bytes)"); + assert_eq!(x.sol_type_name(), "(bytes1,bytes4,bytes2)"); - let tuple = &(&0u16, &"", &mut b"", &mut [Address::ZERO][..]); + let tuple = &(&0u16, &"", b"0", &mut [Address::ZERO][..]); tuple.abi_encode(); - assert_eq!(tuple.sol_type_name(), "(uint16,string,bytes,address[])"); + assert_eq!(tuple.sol_type_name(), "(uint16,string,bytes1,address[])"); + } + + #[test] + fn decode() { + let _: Result = String::abi_decode(b"", false); + let _: Result = <&str>::abi_decode(b"", false); + + let _: Result> = Vec::::abi_decode(b"", false); + let _: Result> = Vec::<&str>::abi_decode(b"", false); + let _: Result> = <&[String]>::abi_decode(b"", false); + let _: Result<[String; 4]> = <&[String; 4]>::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, &[(u32, &str, Vec<[u8; 4]>)], U256)>::abi_decode(b"", false); } } diff --git a/crates/sol-types/src/types/mod.rs b/crates/sol-types/src/types/mod.rs index c25a399862..e52c7a3bf2 100644 --- a/crates/sol-types/src/types/mod.rs +++ b/crates/sol-types/src/types/mod.rs @@ -18,11 +18,12 @@ pub use interface::{ContractError, GenericContractError, Selectors, SolInterface mod r#struct; pub use r#struct::SolStruct; +mod encodable; +pub use encodable::Encodable; + mod ty; pub use ty::{SolType, SolTypeEncodable}; // Solidity user-defined value types. // No exports are needed as the only item is a macro. mod udt; - -mod st2; diff --git a/crates/sol-types/src/types/ty.rs b/crates/sol-types/src/types/ty.rs index 2e81a10b5d..53014281e6 100644 --- a/crates/sol-types/src/types/ty.rs +++ b/crates/sol-types/src/types/ty.rs @@ -4,19 +4,17 @@ use crate::{ }; use alloc::{borrow::Cow, vec::Vec}; -/// An encodable is any type that may be encoded via a given [`SolType`]. +/// An ABI-encodable is any type that may be encoded via a given [`SolType`]. +/// +/// **Note:** this trait should not be implemented directly unless implementing +/// a custom [`SolType`], which is also discouraged. Consider using +/// [`Encodable`](crate::Encodable). /// /// 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. /// -/// ### Why no `Decodable`? -/// -/// 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. -/// /// ### Usage Note /// /// Rust data may not have a 1:1 mapping to Solidity types. The easiest example @@ -25,7 +23,7 @@ use alloc::{borrow::Cow, vec::Vec}; /// trait is always ambiguous for certain types. /// /// ```compile_fail,E0284 -/// # use alloy_sol_types::{SolType, Encodable, sol_data::*}; +/// # use alloy_sol_types::{SolType, SolTypeEncodable, sol_data::*}; /// // Compilation fails due to ambiguity /// // error[E0284]: type annotations needed /// // | @@ -44,45 +42,43 @@ use alloc::{borrow::Cow, vec::Vec}; /// /// 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. +/// [`SolTypeEncodable`] methods. /// /// ``` -/// # use alloy_sol_types::{SolType, Encodable, sol_data::*}; +/// # use alloy_sol_types::{SolType, SolTypeEncodable, sol_data::*}; /// # fn main() -> Result<(), alloy_sol_types::Error> { /// // Not recommended: -/// Encodable::>::to_tokens(&100u64); +/// SolTypeEncodable::>::to_tokens(&100u64); /// /// // Recommended: /// Uint::<64>::tokenize(&100u64); /// # Ok(()) /// # } /// ``` -pub trait SolTypeEncodable { +pub trait SolTypeEncodable { /// Convert the value to tokens. fn to_tokens(&self) -> T::TokenType<'_>; -} -macro_rules! deref_impls { - ($($(#[$attr:meta])* [$($gen:tt)*] $t:ty),* $(,)?) => {$( - $(#[$attr])* - impl<$($gen)*, U: SolType> SolTypeEncodable for $t { - #[inline] - fn to_tokens(&self) -> <$t as SolType>::TokenType<'_> { - (**self).to_tokens() - } - } - )*}; -} + /// Calculate the ABI-encoded size of the data. + /// + /// See [`SolType::abi_encoded_size`] for more information. + fn abi_encoded_size(&self) -> usize { + T::ENCODED_SIZE.unwrap() + } + + /// Non-standard Packed Mode ABI encoding. + /// + /// See [`SolType::abi_encode_packed_to`] for more information. + fn abi_encode_packed_to(&self, out: &mut Vec); -deref_impls! { - // [T: ?Sized + SolTypeEncodable] &T, - // [T: ?Sized + SolTypeEncodable] &mut T, - // [T: ?Sized + alloc::borrow::ToOwned + SolTypeEncodable] alloc::borrow::Cow<'_, T>, - // [T: ?Sized + SolTypeEncodable] alloc::rc::Rc, - // [T: ?Sized + SolTypeEncodable] alloc::sync::Arc, + /// Encode this data according to EIP-712 `encodeData` rules, and hash it + /// if necessary. + /// + /// See [`SolType::eip712_data_word`] for more information. + fn eip712_data_word(&self) -> Word; } -/// A Solidity Type, for ABI encoding and decoding +/// 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 @@ -129,7 +125,7 @@ deref_impls! { /// b: alloy_primitives::FixedBytes([0x01, 0x02]), /// }; /// ``` -pub trait SolType { +pub trait SolType: Sized { /// The corresponding Rust type. type RustType: SolTypeEncodable + 'static; @@ -150,9 +146,8 @@ pub trait SolType { /// 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.abi_encoded_size() } /// Returns `true` if the given token can be detokenized with this type. @@ -188,12 +183,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.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.abi_encode_packed_to(out) + } /// Non-standard Packed Mode ABI encoding. /// @@ -205,30 +206,30 @@ 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. + /// Encodes a single ABI value by wrapping it in a 1-length sequence. #[inline] fn abi_encode>(rust: &E) -> Vec { abi::encode(&rust.to_tokens()) } - /// Encode an ABI sequence. + /// Encodes an ABI sequence. #[inline] - fn abi_encode_sequence>(rust: &E) -> Vec + fn abi_encode_sequence>(rust: &E) -> Vec where for<'a> Self::TokenType<'a>: TokenSeq<'a>, { abi::encode_sequence(&rust.to_tokens()) } - /// Encode an ABI sequence suitable for function parameters. + /// Encodes an ABI sequence suitable for function parameters. #[inline] - fn abi_encode_params>(rust: &E) -> Vec + fn abi_encode_params>(rust: &E) -> Vec where for<'a> Self::TokenType<'a>: TokenSeq<'a>, { diff --git a/crates/sol-types/src/types/udt.rs b/crates/sol-types/src/types/udt.rs index ce2b64e9ca..c250d63046 100644 --- a/crates/sol-types/src/types/udt.rs +++ b/crates/sol-types/src/types/udt.rs @@ -24,6 +24,16 @@ macro_rules! define_udt { fn to_tokens(&self) -> <$underlying as $crate::SolType>::TokenType<'_> { $crate::SolTypeEncodable::<$underlying>::to_tokens(self) } + + #[inline] + fn eip712_data_word(&self) -> $crate::Word { + <$underlying as $crate::SolType>::tokenize(self).0 + } + + #[inline] + fn abi_encode_packed_to(&self, out: &mut $crate::private::Vec) { + <$underlying as $crate::SolType>::abi_encode_packed_to(self, out) + } } impl $name { @@ -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/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 | From 713fe63437b78e62b1362a36483dcd0223e9f5bd Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:25:52 +0200 Subject: [PATCH 03/20] chore: clippy --- crates/sol-types/src/types/encodable.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/sol-types/src/types/encodable.rs b/crates/sol-types/src/types/encodable.rs index 3183df64dc..bca704579d 100644 --- a/crates/sol-types/src/types/encodable.rs +++ b/crates/sol-types/src/types/encodable.rs @@ -16,6 +16,12 @@ use alloy_primitives::{Address, Bytes, FixedBytes, Function, I256, U256}; /// # Examples /// /// ``` +/// use alloy_sol_types::Encodable; +/// +/// 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 Encodable { /// The Solidity type that this type corresponds to. @@ -114,10 +120,7 @@ pub trait Encodable { /// ABI-decode this type from the given data. /// /// See [`SolType::abi_decode`] for more information. - fn abi_decode<'de>( - data: &'de [u8], - validate: bool, - ) -> Result<::RustType> { + fn abi_decode(data: &[u8], validate: bool) -> Result<::RustType> { Self::SolType::abi_decode(data, validate) } @@ -243,6 +246,7 @@ impl Encodable for () { all_the_tuples!(tuple_impls); #[cfg(test)] +#[allow(clippy::type_complexity)] mod tests { use super::*; From 3e6903b32facf2fa76d65f74a8e30b0502110610 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:26:57 +0200 Subject: [PATCH 04/20] fixtest --- crates/sol-types/src/abi/encoder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sol-types/src/abi/encoder.rs b/crates/sol-types/src/abi/encoder.rs index e62fdb8901..565d448c93 100644 --- a/crates/sol-types/src/abi/encoder.rs +++ b/crates/sol-types/src/abi/encoder.rs @@ -187,7 +187,7 @@ pub fn encode_params<'a, T: TokenSeq<'a>>(token: &T) -> Vec { #[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] From 2abad059e8bba904bf9f301256f221b881b33b5e Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:29:18 +0200 Subject: [PATCH 05/20] fixstable --- crates/sol-types/src/types/encodable.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/sol-types/src/types/encodable.rs b/crates/sol-types/src/types/encodable.rs index bca704579d..3175921167 100644 --- a/crates/sol-types/src/types/encodable.rs +++ b/crates/sol-types/src/types/encodable.rs @@ -324,7 +324,7 @@ mod tests { 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")); + assert_eq!(Vec::::from(&b"a"[..]).abi_encode(), encode_bytes(b"a")); } #[test] @@ -364,7 +364,7 @@ mod tests { ( String::from("aaaa"), Address::with_last_byte(69), - Vec::from(b"bbbb"), + b"bbbb".to_vec(), b"cccc", &b"dddd"[..], ), From 25c4f8a567cf60706f10306e2f412c542a4b5f77 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:59:05 +0200 Subject: [PATCH 06/20] tuple perfington --- crates/sol-types/src/types/data_type.rs | 30 ++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/crates/sol-types/src/types/data_type.rs b/crates/sol-types/src/types/data_type.rs index f1c7f43870..1ff7144b9d 100644 --- a/crates/sol-types/src/types/data_type.rs +++ b/crates/sol-types/src/types/data_type.rs @@ -690,15 +690,17 @@ macro_rules! tuple_encodable_impls { } 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,)+) { @@ -717,14 +719,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() + 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 { From dd98881ec4d3b86398ffdf290fd238927e620760 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 5 Oct 2023 10:07:52 +0200 Subject: [PATCH 07/20] cleanup --- crates/sol-types/src/types/encodable.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/crates/sol-types/src/types/encodable.rs b/crates/sol-types/src/types/encodable.rs index 3175921167..8fc1db72e8 100644 --- a/crates/sol-types/src/types/encodable.rs +++ b/crates/sol-types/src/types/encodable.rs @@ -204,25 +204,7 @@ impl_encodable! { [T: Encodable] Vec => sol_data::Array []; [T: Encodable] [T] => sol_data::Array []; [T: Encodable, const N: usize] [T; N] => sol_data::FixedArray []; -} - -// Have to override the `Self: SolTypeEncodable` bound for these -// because `SolTypeEncodable` is not implemented for references -macro_rules! deref_impls { - ($($(#[$attr:meta])* [$($gen:tt)*] $rust:ty => $sol:ty [$($where:tt)*];)+) => {$( - $(#[$attr])* - impl<$($gen)*> Encodable for $rust $($where)* { - type SolType = $sol; - - #[inline] - fn abi_encode(&self) -> Vec { - (**self).abi_encode() - } - } - )*}; -} -deref_impls! { [T: ?Sized + Encodable + SolTypeEncodable] &T => T::SolType []; [T: ?Sized + Encodable + SolTypeEncodable] &mut T => T::SolType []; [T: ?Sized + Encodable + SolTypeEncodable] alloc::boxed::Box => T::SolType []; From 5c758016070bed3dd356d8bf2ff99e914078bd53 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:12:34 +0200 Subject: [PATCH 08/20] from --- crates/sol-types/src/types/encodable.rs | 34 ++++++++++--------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/crates/sol-types/src/types/encodable.rs b/crates/sol-types/src/types/encodable.rs index 8fc1db72e8..933510b28e 100644 --- a/crates/sol-types/src/types/encodable.rs +++ b/crates/sol-types/src/types/encodable.rs @@ -120,36 +120,35 @@ pub trait Encodable { /// ABI-decode this type from the given data. /// /// See [`SolType::abi_decode`] for more information. - fn abi_decode(data: &[u8], validate: bool) -> Result<::RustType> { - Self::SolType::abi_decode(data, validate) + 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<::RustType> + 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) + 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<::RustType> + 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) + Self::SolType::abi_decode_sequence(data, validate).map(Self::from) } } @@ -388,18 +387,11 @@ mod tests { #[test] fn decode() { let _: Result = String::abi_decode(b"", false); - let _: Result = <&str>::abi_decode(b"", false); let _: Result> = Vec::::abi_decode(b"", false); - let _: Result> = Vec::<&str>::abi_decode(b"", false); - let _: Result> = <&[String]>::abi_decode(b"", false); - let _: Result<[String; 4]> = <&[String; 4]>::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, &[(u32, &str, Vec<[u8; 4]>)], U256)>::abi_decode(b"", false); + let _: Result<(i64, Vec<(u32, String, Vec>)>, U256)> = + <(i64, Vec<(u32, String, Vec>)>, U256)>::abi_decode(b"", false); } } From 155c4d498962597bacc6cdf410ecbcdcc76c3867 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 7 Oct 2023 09:37:01 +0200 Subject: [PATCH 09/20] officially make SolTypeEncodable an implementation detail --- crates/dyn-abi/src/ext/abi.rs | 1 - crates/sol-macro/src/expand/enum.rs | 2 +- crates/sol-macro/src/expand/struct.rs | 2 +- crates/sol-types/src/abi/token.rs | 41 +++--- crates/sol-types/src/eip712.rs | 29 ++--- crates/sol-types/src/lib.rs | 21 +++- crates/sol-types/src/types/data_type.rs | 2 +- crates/sol-types/src/types/encodable.rs | 65 +++++----- crates/sol-types/src/types/event/topic.rs | 4 +- crates/sol-types/src/types/function.rs | 3 +- crates/sol-types/src/types/mod.rs | 2 +- crates/sol-types/src/types/ty.rs | 144 ++++++++-------------- crates/sol-types/src/types/udt.rs | 4 +- 13 files changed, 148 insertions(+), 172 deletions(-) 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/sol-macro/src/expand/enum.rs b/crates/sol-macro/src/expand/enum.rs index 099818d3f7..08ad017c29 100644 --- a/crates/sol-macro/src/expand/enum.rs +++ b/crates/sol-macro/src/expand/enum.rs @@ -112,7 +112,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result } #[automatically_derived] - impl ::alloy_sol_types::SolTypeEncodable<#name> for #name { + impl ::alloy_sol_types::private::SolTypeEncodable<#name> for #name { #[inline] fn to_tokens(&self) -> #uint8_st::TokenType<'_> { ::alloy_sol_types::Word::with_last_byte(*self as u8).into() diff --git a/crates/sol-macro/src/expand/struct.rs b/crates/sol-macro/src/expand/struct.rs index a84f56eb3a..256107dc11 100644 --- a/crates/sol-macro/src/expand/struct.rs +++ b/crates/sol-macro/src/expand/struct.rs @@ -77,7 +77,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { #convert #[automatically_derived] - impl ::alloy_sol_types::SolTypeEncodable for #name { + impl ::alloy_sol_types::private::SolTypeEncodable for #name { fn to_tokens(&self) -> ::TokenType<'_> { #tokenize_impl } diff --git a/crates/sol-types/src/abi/token.rs b/crates/sol-types/src/abi/token.rs index 38619d422f..bc01a979f4 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 of arity `0..=24` `(T, U, V, ...)` /// /// 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 2f5b684ea1..5a0505381d 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, SolType, SolTypeEncodable}; +use crate::Encodable; 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: SolTypeEncodable, - { - 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/lib.rs b/crates/sol-types/src/lib.rs index 18ed47bd65..515673ef86 100644 --- a/crates/sol-types/src/lib.rs +++ b/crates/sol-types/src/lib.rs @@ -178,7 +178,7 @@ 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, SolTypeEncodable, TopicList, + SolEvent, SolInterface, SolStruct, SolType, TopicList, }; pub mod utils; @@ -209,6 +209,25 @@ 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`], + /// which is also discouraged. Consider using + /// [`Encodable`](crate::Encodable) instead. + pub trait SolTypeEncodable { + fn to_tokens(&self) -> T::TokenType<'_>; + fn abi_encoded_size(&self) -> usize { + T::ENCODED_SIZE.unwrap() + } + fn abi_encode_packed_to(&self, out: &mut Vec); + fn 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/types/data_type.rs b/crates/sol-types/src/types/data_type.rs index 1ff7144b9d..a5b8c20928 100644 --- a/crates/sol-types/src/types/data_type.rs +++ b/crates/sol-types/src/types/data_type.rs @@ -6,7 +6,7 @@ #![allow(missing_copy_implementations, missing_debug_implementations)] -use crate::{abi::token::*, utils, SolType, SolTypeEncodable, Word}; +use crate::{abi::token::*, private::SolTypeEncodable, 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, diff --git a/crates/sol-types/src/types/encodable.rs b/crates/sol-types/src/types/encodable.rs index 933510b28e..b613c97543 100644 --- a/crates/sol-types/src/types/encodable.rs +++ b/crates/sol-types/src/types/encodable.rs @@ -1,6 +1,7 @@ -use super::{SolType, SolTypeEncodable}; +use super::SolType; use crate::{ abi::TokenSeq, + private::SolTypeEncodable, sol_data::{self, ByteCount, SupportedFixedBytes}, Result, Word, }; @@ -9,9 +10,9 @@ use alloy_primitives::{Address, Bytes, FixedBytes, Function, I256, U256}; /// ABI-encodable types. /// -/// This is a convenience trait that combines the logic in [`SolTypeEncodable`] -/// and [`SolType`] to provide a single trait for encoding and decoding Solidity -/// types. +/// 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. /// /// # Examples /// @@ -23,7 +24,7 @@ use alloy_primitives::{Address, Bytes, FixedBytes, Function, I256, U256}; /// let _ = my_values.abi_encode_packed(); /// assert_eq!(my_values.sol_type_name(), "(string,uint32,bool,bytes24)"); /// ``` -pub trait Encodable { +pub trait Encodable: SolTypeEncodable { /// The Solidity type that this type corresponds to. type SolType: SolType; @@ -35,14 +36,28 @@ pub trait Encodable { Self::SolType::sol_type_name() } - /// Calculate the ABI-encoded size of the data. + /// Tokenizes this value into this type's token. /// - /// See [`SolType::abi_encoded_size`] for more information. + /// See [`SolType::tokenize`] for more information. + #[inline] + fn tokenize(&self) -> ::TokenType<'_> { + >::to_tokens(self) + } + + /// Detokenize the given token into this type. #[inline] - fn abi_encoded_size(&self) -> usize + fn detokenize(token: ::TokenType<'_>) -> Self where - Self: SolTypeEncodable, + 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 { >::abi_encoded_size(self) } @@ -51,10 +66,7 @@ pub trait Encodable { /// /// See [`SolType::eip712_data_word`] for more information. #[inline] - fn eip712_data_word(&self) -> Word - where - Self: SolTypeEncodable, - { + fn eip712_data_word(&self) -> Word { >::eip712_data_word(self) } @@ -62,10 +74,7 @@ pub trait Encodable { /// /// See [`SolType::abi_encode_packed_to`] for more information. #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) - where - Self: SolTypeEncodable, - { + fn abi_encode_packed_to(&self, out: &mut Vec) { >::abi_encode_packed_to(self, out) } @@ -73,10 +82,7 @@ pub trait Encodable { /// /// See [`SolType::abi_encode_packed`] for more information. #[inline] - fn abi_encode_packed(&self) -> Vec - where - Self: SolTypeEncodable, - { + fn abi_encode_packed(&self) -> Vec { let mut out = Vec::new(); >::abi_encode_packed_to(self, &mut out); out @@ -86,10 +92,7 @@ pub trait Encodable { /// /// See [`SolType::abi_encode`] for more information. #[inline] - fn abi_encode(&self) -> Vec - where - Self: SolTypeEncodable, - { + fn abi_encode(&self) -> Vec { Self::SolType::abi_encode(self) } @@ -99,7 +102,6 @@ pub trait Encodable { #[inline] fn abi_encode_sequence(&self) -> Vec where - Self: SolTypeEncodable, for<'a> ::TokenType<'a>: TokenSeq<'a>, { Self::SolType::abi_encode_sequence(self) @@ -111,7 +113,6 @@ pub trait Encodable { #[inline] fn abi_encode_params(&self) -> Vec where - Self: SolTypeEncodable, for<'a> ::TokenType<'a>: TokenSeq<'a>, { Self::SolType::abi_encode_params(self) @@ -204,12 +205,8 @@ impl_encodable! { [T: Encodable] [T] => sol_data::Array []; [T: Encodable, const N: usize] [T; N] => sol_data::FixedArray []; - [T: ?Sized + Encodable + SolTypeEncodable] &T => T::SolType []; - [T: ?Sized + Encodable + SolTypeEncodable] &mut T => T::SolType []; - [T: ?Sized + Encodable + SolTypeEncodable] alloc::boxed::Box => T::SolType []; - [T: ?Sized + alloc::borrow::ToOwned + Encodable + SolTypeEncodable] alloc::borrow::Cow<'_, T> => T::SolType []; - [T: ?Sized + Encodable + SolTypeEncodable] alloc::rc::Rc => T::SolType []; - [T: ?Sized + Encodable + SolTypeEncodable] alloc::sync::Arc => T::SolType []; + ['a, T: ?Sized + Encodable] &'a T => T::SolType [where &'a T: SolTypeEncodable]; + ['a, T: ?Sized + Encodable] &'a mut T => T::SolType [where &'a mut T: SolTypeEncodable]; } macro_rules! tuple_impls { @@ -233,7 +230,7 @@ mod tests { // Make sure these are in scope #[allow(unused_imports)] - use crate::{SolType as _, SolTypeEncodable as _}; + use crate::{private::SolTypeEncodable as _, SolType as _}; #[test] fn inference() { diff --git a/crates/sol-types/src/types/event/topic.rs b/crates/sol-types/src/types/event/topic.rs index 150e252a38..5b2b993a25 100644 --- a/crates/sol-types/src/types/event/topic.rs +++ b/crates/sol-types/src/types/event/topic.rs @@ -49,12 +49,12 @@ macro_rules! word_impl { #[inline] fn encode_topic_preimage(rust: &Self::RustType, out: &mut Vec) { - out.extend($crate::SolTypeEncodable::::to_tokens(rust).0 .0); + out.extend($crate::private::SolTypeEncodable::::to_tokens(rust).0); } #[inline] fn encode_topic(rust: &Self::RustType) -> WordToken { - $crate::SolTypeEncodable::::to_tokens(rust) + $crate::private::SolTypeEncodable::::to_tokens(rust) } }; } diff --git a/crates/sol-types/src/types/function.rs b/crates/sol-types/src/types/function.rs index c477ed40e7..90757a3545 100644 --- a/crates/sol-types/src/types/function.rs +++ b/crates/sol-types/src/types/function.rs @@ -1,6 +1,7 @@ use crate::{ abi::{TokenSeq, TokenType}, - Result, SolType, SolTypeEncodable, Word, + private::SolTypeEncodable, + Result, SolType, Word, }; use alloc::vec::Vec; diff --git a/crates/sol-types/src/types/mod.rs b/crates/sol-types/src/types/mod.rs index e52c7a3bf2..4b325daabe 100644 --- a/crates/sol-types/src/types/mod.rs +++ b/crates/sol-types/src/types/mod.rs @@ -22,7 +22,7 @@ mod encodable; pub use encodable::Encodable; mod ty; -pub use ty::{SolType, SolTypeEncodable}; +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/ty.rs b/crates/sol-types/src/types/ty.rs index 53014281e6..7a662fff23 100644 --- a/crates/sol-types/src/types/ty.rs +++ b/crates/sol-types/src/types/ty.rs @@ -1,94 +1,51 @@ use crate::{ abi::{self, TokenSeq, TokenType}, + private::SolTypeEncodable, Result, Word, }; use alloc::{borrow::Cow, vec::Vec}; -/// An ABI-encodable is any type that may be encoded via a given [`SolType`]. +/// A Solidity type. /// -/// **Note:** this trait should not be implemented directly unless implementing -/// a custom [`SolType`], which is also discouraged. Consider using -/// [`Encodable`](crate::Encodable). +/// 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 [`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. +/// 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. /// -/// ### Usage Note +/// ### Implementer's Guide /// -/// 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. +/// Implementing this trait directly is **heavily** discouraged. Instead, use +/// the [`sol!`] procedural macro to parse Solidity syntax into types that +/// implement this trait. /// -/// ```compile_fail,E0284 -/// # use alloy_sol_types::{SolType, SolTypeEncodable, 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>(()) /// ``` +/// alloy_sol_types::sol! { +/// struct MyStruct { +/// bool a; +/// bytes2 b; +/// } +/// } /// -/// To resolve this, specify the related [`SolType`]. When specifying T it is -/// recommended that you invoke the [`SolType`] methods on `T`, rather than the -/// [`SolTypeEncodable`] methods. -/// +/// // This is the native rust representation of a Solidity type! +/// // How cool is that! +/// const MY_STRUCT: MyStruct = MyStruct { +/// a: true, +/// b: alloy_primitives::FixedBytes([0x01, 0x02]), +/// }; /// ``` -/// # use alloy_sol_types::{SolType, SolTypeEncodable, sol_data::*}; -/// # fn main() -> Result<(), alloy_sol_types::Error> { -/// // Not recommended: -/// SolTypeEncodable::>::to_tokens(&100u64); /// -/// // Recommended: -/// Uint::<64>::tokenize(&100u64); -/// # Ok(()) -/// # } -/// ``` -pub trait SolTypeEncodable { - /// Convert the value to tokens. - fn to_tokens(&self) -> T::TokenType<'_>; - - /// Calculate the ABI-encoded size of the data. - /// - /// See [`SolType::abi_encoded_size`] for more information. - fn abi_encoded_size(&self) -> usize { - T::ENCODED_SIZE.unwrap() - } - - /// Non-standard Packed Mode ABI encoding. - /// - /// See [`SolType::abi_encode_packed_to`] for more information. - fn abi_encode_packed_to(&self, out: &mut Vec); - - /// Encode this data according to EIP-712 `encodeData` rules, and hash it - /// if necessary. - /// - /// See [`SolType::eip712_data_word`] for more information. - fn eip712_data_word(&self) -> Word; -} - -/// A Solidity Type, for ABI encoding and decoding. +/// # Examples /// -/// 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. +/// Basic usage: /// /// ``` /// use alloy_sol_types::{sol_data::*, SolType}; /// -/// type DynUint256Array = Array>; -/// assert_eq!(&DynUint256Array::sol_type_name(), "uint256[]"); +/// type Uint256DynamicArray = Array>; +/// assert_eq!(&Uint256DynamicArray::sol_type_name(), "uint256[]"); /// /// type Erc20FunctionArgs = (Address, Uint<256>); /// assert_eq!(&Erc20FunctionArgs::sol_type_name(), "(address,uint256)"); @@ -100,38 +57,41 @@ pub trait SolTypeEncodable { /// ); /// ``` /// -/// 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. +/// The previous example can be entirely replicated with the [`sol!`] macro: /// -/// ### Implementer's Guide +/// ``` +/// use alloy_sol_types::{sol, SolType}; /// -/// 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. +/// type Uint256DynamicArray = sol!(uint256[]); +/// assert_eq!(&Uint256DynamicArray::sol_type_name(), "uint256[]"); /// +/// type Erc20FunctionArgs = sol!((address, uint256)); +/// assert_eq!(&Erc20FunctionArgs::sol_type_name(), "(address,uint256)"); +/// +/// type LargeComplexType = sol!((bool[][2],(bytes13,string))); +/// assert_eq!( +/// &LargeComplexType::sol_type_name(), +/// "(bool[][2],(bytes13,string))" +/// ); /// ``` -/// alloy_sol_types::sol! { -/// struct MyStruct { -/// bool a; -/// bytes2 b; -/// } -/// } /// -/// // This is the native rust representation of a Solidity type! -/// // How cool is that! -/// const MY_STRUCT: MyStruct = MyStruct { -/// a: true, -/// b: alloy_primitives::FixedBytes([0x01, 0x02]), -/// }; +/// For more complex usage, it's recommended to use the +/// [`Encodable`](crate::Encodable) trait for primitive types, and the other +/// `Sol*` traits for other types created with [`sol!`]: +/// /// ``` +/// use alloy_sol_types::{sol_data::*, SolType}; +/// ``` +/// +/// [`sol!`]: crate::sol pub trait SolType: Sized { /// The corresponding Rust type. type RustType: SolTypeEncodable + '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 diff --git a/crates/sol-types/src/types/udt.rs b/crates/sol-types/src/types/udt.rs index c250d63046..d8868a14d5 100644 --- a/crates/sol-types/src/types/udt.rs +++ b/crates/sol-types/src/types/udt.rs @@ -19,10 +19,10 @@ macro_rules! define_udt { <$underlying as $crate::SolType>::RustType, ); - impl $crate::SolTypeEncodable<$name> for <$underlying as $crate::SolType>::RustType { + impl $crate::private::SolTypeEncodable<$name> for <$underlying as $crate::SolType>::RustType { #[inline] fn to_tokens(&self) -> <$underlying as $crate::SolType>::TokenType<'_> { - $crate::SolTypeEncodable::<$underlying>::to_tokens(self) + $crate::private::SolTypeEncodable::<$underlying>::to_tokens(self) } #[inline] From 8e7121ca9ca34114335979b2594d4fd4919fadda Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 7 Oct 2023 09:38:36 +0200 Subject: [PATCH 10/20] docs --- crates/sol-types/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/sol-types/src/lib.rs b/crates/sol-types/src/lib.rs index 515673ef86..c6a41337fa 100644 --- a/crates/sol-types/src/lib.rs +++ b/crates/sol-types/src/lib.rs @@ -216,9 +216,9 @@ pub mod private { /// `RustTypes`. /// /// **Note:** this trait is an implementation detail. As such, it should not - /// be implemented directly unless implementing a custom [`SolType`], - /// which is also discouraged. Consider using - /// [`Encodable`](crate::Encodable) instead. + /// be implemented directly unless implementing a custom + /// [`SolType`](crate::SolType), which is also discouraged. Consider + /// using [`Encodable`](crate::Encodable) instead. pub trait SolTypeEncodable { fn to_tokens(&self) -> T::TokenType<'_>; fn abi_encoded_size(&self) -> usize { From d16d31d081f160987d9e49480713d8a6abbd0814 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 7 Oct 2023 09:53:08 +0200 Subject: [PATCH 11/20] impl Encodable for sol! SolTypes --- crates/sol-macro/src/expand/enum.rs | 5 +++++ crates/sol-macro/src/expand/struct.rs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/crates/sol-macro/src/expand/enum.rs b/crates/sol-macro/src/expand/enum.rs index 08ad017c29..786cb9121f 100644 --- a/crates/sol-macro/src/expand/enum.rs +++ b/crates/sol-macro/src/expand/enum.rs @@ -111,6 +111,11 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result } } + #[automatically_derived] + impl ::alloy_sol_types::Encodable for #name { + type SolType = Self; + } + #[automatically_derived] impl ::alloy_sol_types::private::SolTypeEncodable<#name> for #name { #[inline] diff --git a/crates/sol-macro/src/expand/struct.rs b/crates/sol-macro/src/expand/struct.rs index 256107dc11..6baad552d7 100644 --- a/crates/sol-macro/src/expand/struct.rs +++ b/crates/sol-macro/src/expand/struct.rs @@ -76,6 +76,11 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { const _: () = { #convert + #[automatically_derived] + impl ::alloy_sol_types::Encodable for #name { + type SolType = Self; + } + #[automatically_derived] impl ::alloy_sol_types::private::SolTypeEncodable for #name { fn to_tokens(&self) -> ::TokenType<'_> { From ec06a0ab31e4e69b1915ef24d11ead26cae7e185 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 7 Oct 2023 09:53:15 +0200 Subject: [PATCH 12/20] update examples --- crates/sol-types/src/types/ty.rs | 53 ++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/crates/sol-types/src/types/ty.rs b/crates/sol-types/src/types/ty.rs index 7a662fff23..19ca30790e 100644 --- a/crates/sol-types/src/types/ty.rs +++ b/crates/sol-types/src/types/ty.rs @@ -45,14 +45,14 @@ use alloc::{borrow::Cow, vec::Vec}; /// use alloy_sol_types::{sol_data::*, SolType}; /// /// type Uint256DynamicArray = Array>; -/// assert_eq!(&Uint256DynamicArray::sol_type_name(), "uint256[]"); +/// assert_eq!(Uint256DynamicArray::sol_type_name(), "uint256[]"); /// /// type Erc20FunctionArgs = (Address, Uint<256>); -/// assert_eq!(&Erc20FunctionArgs::sol_type_name(), "(address,uint256)"); +/// assert_eq!(Erc20FunctionArgs::sol_type_name(), "(address,uint256)"); /// /// type LargeComplexType = (FixedArray, 2>, (FixedBytes<13>, String)); /// assert_eq!( -/// &LargeComplexType::sol_type_name(), +/// LargeComplexType::sol_type_name(), /// "(bool[][2],(bytes13,string))" /// ); /// ``` @@ -63,24 +63,59 @@ use alloc::{borrow::Cow, vec::Vec}; /// use alloy_sol_types::{sol, SolType}; /// /// type Uint256DynamicArray = sol!(uint256[]); -/// assert_eq!(&Uint256DynamicArray::sol_type_name(), "uint256[]"); +/// assert_eq!(Uint256DynamicArray::sol_type_name(), "uint256[]"); /// /// type Erc20FunctionArgs = sol!((address, uint256)); -/// assert_eq!(&Erc20FunctionArgs::sol_type_name(), "(address,uint256)"); +/// assert_eq!(Erc20FunctionArgs::sol_type_name(), "(address,uint256)"); /// /// type LargeComplexType = sol!((bool[][2],(bytes13,string))); /// assert_eq!( -/// &LargeComplexType::sol_type_name(), +/// LargeComplexType::sol_type_name(), /// "(bool[][2],(bytes13,string))" /// ); /// ``` /// /// For more complex usage, it's recommended to use the -/// [`Encodable`](crate::Encodable) trait for primitive types, and the other -/// `Sol*` traits for other types created with [`sol!`]: +/// [`Encodable`](crate::Encodable) trait for primitive types, and the `Sol*` +/// traits for other types created with [`sol!`]: /// /// ``` -/// use alloy_sol_types::{sol_data::*, SolType}; +/// use alloy_primitives::Address; +/// use alloy_sol_types::{sol, Encodable, SolCall, SolStruct}; +/// +/// sol! { +/// struct MyStruct { +/// bool a; +/// uint64 b; +/// address c; +/// } +/// +/// enum MyEnum { +/// A, +/// B, +/// C, +/// } +/// +/// function myFunction(MyStruct my_struct, MyEnum my_enum) {} +/// } +/// +/// // `Encodable` +/// let my_bool = true; +/// let _ = my_bool.abi_encode(); +/// +/// let my_struct = MyStruct { +/// a: true, +/// 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(); /// ``` /// /// [`sol!`]: crate::sol From 73b0bb1bf2452995375678e58c013c5aa3d050b9 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 7 Oct 2023 09:55:53 +0200 Subject: [PATCH 13/20] rename to SolValue --- crates/sol-macro/src/expand/enum.rs | 2 +- crates/sol-macro/src/expand/struct.rs | 2 +- crates/sol-types/src/eip712.rs | 2 +- crates/sol-types/src/lib.rs | 8 ++++---- crates/sol-types/src/types/encodable.rs | 26 ++++++++++++++----------- crates/sol-types/src/types/mod.rs | 2 +- crates/sol-types/src/types/ty.rs | 6 +++--- 7 files changed, 26 insertions(+), 22 deletions(-) diff --git a/crates/sol-macro/src/expand/enum.rs b/crates/sol-macro/src/expand/enum.rs index 786cb9121f..708eed9f38 100644 --- a/crates/sol-macro/src/expand/enum.rs +++ b/crates/sol-macro/src/expand/enum.rs @@ -112,7 +112,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result } #[automatically_derived] - impl ::alloy_sol_types::Encodable for #name { + impl ::alloy_sol_types::SolValue for #name { type SolType = Self; } diff --git a/crates/sol-macro/src/expand/struct.rs b/crates/sol-macro/src/expand/struct.rs index 6baad552d7..0b0f726738 100644 --- a/crates/sol-macro/src/expand/struct.rs +++ b/crates/sol-macro/src/expand/struct.rs @@ -77,7 +77,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { #convert #[automatically_derived] - impl ::alloy_sol_types::Encodable for #name { + impl ::alloy_sol_types::SolValue for #name { type SolType = Self; } diff --git a/crates/sol-types/src/eip712.rs b/crates/sol-types/src/eip712.rs index 5a0505381d..e7229a57c5 100644 --- a/crates/sol-types/src/eip712.rs +++ b/crates/sol-types/src/eip712.rs @@ -1,4 +1,4 @@ -use crate::Encodable; +use crate::SolValue; use alloc::{borrow::Cow, string::String, vec::Vec}; use alloy_primitives::{keccak256, Address, FixedBytes, B256, U256}; diff --git a/crates/sol-types/src/lib.rs b/crates/sol-types/src/lib.rs index c6a41337fa..2bbbbc4463 100644 --- a/crates/sol-types/src/lib.rs +++ b/crates/sol-types/src/lib.rs @@ -176,9 +176,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; @@ -218,7 +218,7 @@ pub mod private { /// **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 [`Encodable`](crate::Encodable) instead. + /// using [`SolValue`](crate::SolValue) instead. pub trait SolTypeEncodable { fn to_tokens(&self) -> T::TokenType<'_>; fn abi_encoded_size(&self) -> usize { diff --git a/crates/sol-types/src/types/encodable.rs b/crates/sol-types/src/types/encodable.rs index b613c97543..57a104e093 100644 --- a/crates/sol-types/src/types/encodable.rs +++ b/crates/sol-types/src/types/encodable.rs @@ -8,23 +8,25 @@ use crate::{ use alloc::{borrow::Cow, string::String, vec::Vec}; use alloy_primitives::{Address, Bytes, FixedBytes, Function, I256, U256}; -/// ABI-encodable types. +/// 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. +/// /// # Examples /// /// ``` -/// use alloy_sol_types::Encodable; +/// 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 Encodable: SolTypeEncodable { +pub trait SolValue: SolTypeEncodable { /// The Solidity type that this type corresponds to. type SolType: SolType; @@ -45,6 +47,8 @@ pub trait Encodable: SolTypeEncodable { } /// Detokenize the given token into this type. + /// + /// See [`SolType::detokenize`] for more information. #[inline] fn detokenize(token: ::TokenType<'_>) -> Self where @@ -156,7 +160,7 @@ pub trait Encodable: SolTypeEncodable { macro_rules! impl_encodable { ($($(#[$attr:meta])* [$($gen:tt)*] $rust:ty => $sol:ty [$($where:tt)*];)+) => {$( $(#[$attr])* - impl<$($gen)*> Encodable for $rust $($where)* { + impl<$($gen)*> SolValue for $rust $($where)* { type SolType = $sol; } )*}; @@ -201,23 +205,23 @@ impl_encodable! { [] [u8] => sol_data::Bytes []; // Generic - [T: Encodable] Vec => sol_data::Array []; - [T: Encodable] [T] => sol_data::Array []; - [T: Encodable, const N: usize] [T; N] => sol_data::FixedArray []; + [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 + Encodable] &'a T => T::SolType [where &'a T: SolTypeEncodable]; - ['a, T: ?Sized + Encodable] &'a mut T => T::SolType [where &'a mut T: SolTypeEncodable]; + ['a, T: ?Sized + SolValue] &'a T => T::SolType [where &'a T: SolTypeEncodable]; + ['a, T: ?Sized + SolValue] &'a mut T => T::SolType [where &'a mut T: SolTypeEncodable]; } macro_rules! tuple_impls { ($count:literal $($ty:ident),+) => { - impl<$($ty: Encodable,)+> Encodable for ($($ty,)+) { + impl<$($ty: SolValue,)+> SolValue for ($($ty,)+) { type SolType = ($($ty::SolType,)+); } }; } -impl Encodable for () { +impl SolValue for () { type SolType = (); } diff --git a/crates/sol-types/src/types/mod.rs b/crates/sol-types/src/types/mod.rs index 4b325daabe..26260715d8 100644 --- a/crates/sol-types/src/types/mod.rs +++ b/crates/sol-types/src/types/mod.rs @@ -19,7 +19,7 @@ mod r#struct; pub use r#struct::SolStruct; mod encodable; -pub use encodable::Encodable; +pub use encodable::SolValue; mod ty; pub use ty::SolType; diff --git a/crates/sol-types/src/types/ty.rs b/crates/sol-types/src/types/ty.rs index 19ca30790e..d8a964ff34 100644 --- a/crates/sol-types/src/types/ty.rs +++ b/crates/sol-types/src/types/ty.rs @@ -76,12 +76,12 @@ use alloc::{borrow::Cow, vec::Vec}; /// ``` /// /// For more complex usage, it's recommended to use the -/// [`Encodable`](crate::Encodable) trait for primitive types, and the `Sol*` +/// [`SolValue`](crate::SolValue) trait for primitive types, and the `Sol*` /// traits for other types created with [`sol!`]: /// /// ``` /// use alloy_primitives::Address; -/// use alloy_sol_types::{sol, Encodable, SolCall, SolStruct}; +/// use alloy_sol_types::{sol, SolCall, SolStruct, SolValue}; /// /// sol! { /// struct MyStruct { @@ -99,7 +99,7 @@ use alloc::{borrow::Cow, vec::Vec}; /// function myFunction(MyStruct my_struct, MyEnum my_enum) {} /// } /// -/// // `Encodable` +/// // `SolValue` /// let my_bool = true; /// let _ = my_bool.abi_encode(); /// From 92b37fc44c20b67c00f9f03557a6f2a30ba30196 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 7 Oct 2023 10:51:33 +0200 Subject: [PATCH 14/20] update docs --- crates/sol-types/src/abi/decoder.rs | 24 ++++--- crates/sol-types/src/abi/encoder.rs | 50 ++++++++++---- crates/sol-types/src/abi/mod.rs | 34 ++++++---- crates/sol-types/src/abi/token.rs | 2 +- crates/sol-types/src/lib.rs | 24 ++++--- crates/sol-types/src/types/data_type.rs | 2 + crates/sol-types/src/types/encodable.rs | 6 ++ crates/sol-types/src/types/enum.rs | 8 +-- crates/sol-types/src/types/error.rs | 8 +-- crates/sol-types/src/types/event/mod.rs | 2 +- crates/sol-types/src/types/event/topic.rs | 6 ++ .../sol-types/src/types/event/topic_list.rs | 6 ++ crates/sol-types/src/types/function.rs | 8 +-- crates/sol-types/src/types/interface.rs | 8 +-- crates/sol-types/src/types/struct.rs | 6 +- crates/sol-types/src/types/ty.rs | 20 +----- crates/sol-types/type_system.md | 65 ++++++++----------- 17 files changed, 160 insertions(+), 119 deletions(-) diff --git a/crates/sol-types/src/abi/decoder.rs b/crates/sol-types/src/abi/decoder.rs index a7de25f206..27ffc5614a 100644 --- a/crates/sol-types/src/abi/decoder.rs +++ b/crates/sol-types/src/abi/decoder.rs @@ -241,9 +241,11 @@ impl<'de> Decoder<'de> { /// ABI-decodes a single 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 565d448c93..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,6 +187,29 @@ 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}; 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 bc01a979f4..07ddf42e41 100644 --- a/crates/sol-types/src/abi/token.rs +++ b/crates/sol-types/src/abi/token.rs @@ -38,7 +38,7 @@ use sealed::Sealed; /// - [`FixedSeqToken`]: Sequences with a fixed length `T[M]` /// - [`DynSeqToken`]: Sequences with a dynamic length `T[]` /// - [`PackedSeqToken`]: Dynamic-length byte arrays `bytes` or `string` -/// - Tuples of arity `0..=24` `(T, U, V, ...)` +/// - 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 diff --git a/crates/sol-types/src/lib.rs b/crates/sol-types/src/lib.rs index 2bbbbc4463..7cd82fc954 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 diff --git a/crates/sol-types/src/types/data_type.rs b/crates/sol-types/src/types/data_type.rs index a5b8c20928..cae211c69e 100644 --- a/crates/sol-types/src/types/data_type.rs +++ b/crates/sol-types/src/types/data_type.rs @@ -2,6 +2,8 @@ //! //! 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)] diff --git a/crates/sol-types/src/types/encodable.rs b/crates/sol-types/src/types/encodable.rs index 57a104e093..f040f4302f 100644 --- a/crates/sol-types/src/types/encodable.rs +++ b/crates/sol-types/src/types/encodable.rs @@ -16,6 +16,12 @@ use alloy_primitives::{Address, Bytes, FixedBytes, Function, I256, U256}; /// /// 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 /// /// ``` 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..e3723e83c3 100644 --- a/crates/sol-types/src/types/event/mod.rs +++ b/crates/sol-types/src/types/event/mod.rs @@ -13,7 +13,7 @@ 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 diff --git a/crates/sol-types/src/types/event/topic.rs b/crates/sol-types/src/types/event/topic.rs index 5b2b993a25..6483ecc3b4 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. 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 90757a3545..59937f3b49 100644 --- a/crates/sol-types/src/types/function.rs +++ b/crates/sol-types/src/types/function.rs @@ -7,11 +7,11 @@ 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. /// 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/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 d8a964ff34..eae5200823 100644 --- a/crates/sol-types/src/types/ty.rs +++ b/crates/sol-types/src/types/ty.rs @@ -15,28 +15,12 @@ use alloc::{borrow::Cow, vec::Vec}; /// exist at runtime. They **only** contain information about the type, they do /// not carry any data. /// -/// ### Implementer's Guide +/// # Implementer's Guide /// -/// Implementing this trait directly is **heavily** discouraged. Instead, use +/// 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. /// -/// ``` -/// alloy_sol_types::sol! { -/// struct MyStruct { -/// bool a; -/// bytes2 b; -/// } -/// } -/// -/// // This is the native rust representation of a Solidity type! -/// // How cool is that! -/// const MY_STRUCT: MyStruct = MyStruct { -/// a: true, -/// b: alloy_primitives::FixedBytes([0x01, 0x02]), -/// }; -/// ``` -/// /// # Examples /// /// Basic usage: 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. From b55f1a656fa2a810fb8394c73c13a189b688b7ea Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 7 Oct 2023 10:52:41 +0200 Subject: [PATCH 15/20] rename value module --- crates/sol-types/src/types/mod.rs | 4 ++-- crates/sol-types/src/types/{encodable.rs => value.rs} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename crates/sol-types/src/types/{encodable.rs => value.rs} (100%) diff --git a/crates/sol-types/src/types/mod.rs b/crates/sol-types/src/types/mod.rs index 26260715d8..535f6c6aeb 100644 --- a/crates/sol-types/src/types/mod.rs +++ b/crates/sol-types/src/types/mod.rs @@ -18,8 +18,8 @@ pub use interface::{ContractError, GenericContractError, Selectors, SolInterface mod r#struct; pub use r#struct::SolStruct; -mod encodable; -pub use encodable::SolValue; +mod value; +pub use value::SolValue; mod ty; pub use ty::SolType; diff --git a/crates/sol-types/src/types/encodable.rs b/crates/sol-types/src/types/value.rs similarity index 100% rename from crates/sol-types/src/types/encodable.rs rename to crates/sol-types/src/types/value.rs From fad5042f0ecf8e16dc59d984fe240a7cc359fa72 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 7 Oct 2023 11:13:10 +0200 Subject: [PATCH 16/20] rename SolTypeEncodable to SolTypeValue --- crates/sol-macro/src/expand/enum.rs | 2 +- crates/sol-macro/src/expand/struct.rs | 2 +- crates/sol-types/src/errors.rs | 7 +- crates/sol-types/src/lib.rs | 2 +- crates/sol-types/src/types/data_type.rs | 82 +++++++++++------------ crates/sol-types/src/types/event/topic.rs | 4 +- crates/sol-types/src/types/function.rs | 4 +- crates/sol-types/src/types/ty.rs | 56 ++++++++++------ crates/sol-types/src/types/udt.rs | 4 +- crates/sol-types/src/types/value.rs | 30 ++++----- 10 files changed, 102 insertions(+), 91 deletions(-) diff --git a/crates/sol-macro/src/expand/enum.rs b/crates/sol-macro/src/expand/enum.rs index 708eed9f38..ebd0cdf78a 100644 --- a/crates/sol-macro/src/expand/enum.rs +++ b/crates/sol-macro/src/expand/enum.rs @@ -117,7 +117,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result } #[automatically_derived] - impl ::alloy_sol_types::private::SolTypeEncodable<#name> for #name { + impl ::alloy_sol_types::private::SolTypeValue<#name> for #name { #[inline] fn to_tokens(&self) -> #uint8_st::TokenType<'_> { ::alloy_sol_types::Word::with_last_byte(*self as u8).into() diff --git a/crates/sol-macro/src/expand/struct.rs b/crates/sol-macro/src/expand/struct.rs index 0b0f726738..61d5d9e6ca 100644 --- a/crates/sol-macro/src/expand/struct.rs +++ b/crates/sol-macro/src/expand/struct.rs @@ -82,7 +82,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { } #[automatically_derived] - impl ::alloy_sol_types::private::SolTypeEncodable for #name { + impl ::alloy_sol_types::private::SolTypeValue for #name { fn to_tokens(&self) -> ::TokenType<'_> { #tokenize_impl } 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 7cd82fc954..4c0a3dc766 100644 --- a/crates/sol-types/src/lib.rs +++ b/crates/sol-types/src/lib.rs @@ -223,7 +223,7 @@ pub mod private { /// be implemented directly unless implementing a custom /// [`SolType`](crate::SolType), which is also discouraged. Consider /// using [`SolValue`](crate::SolValue) instead. - pub trait SolTypeEncodable { + pub trait SolTypeValue { fn to_tokens(&self) -> T::TokenType<'_>; fn abi_encoded_size(&self) -> usize { T::ENCODED_SIZE.unwrap() diff --git a/crates/sol-types/src/types/data_type.rs b/crates/sol-types/src/types/data_type.rs index cae211c69e..112b151179 100644 --- a/crates/sol-types/src/types/data_type.rs +++ b/crates/sol-types/src/types/data_type.rs @@ -8,7 +8,7 @@ #![allow(missing_copy_implementations, missing_debug_implementations)] -use crate::{abi::token::*, private::SolTypeEncodable, utils, 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, @@ -22,7 +22,7 @@ use core::{borrow::Borrow, fmt::*, hash::Hash, marker::PhantomData, ops::*}; /// Bool - `bool` pub struct Bool; -impl SolTypeEncodable for bool { +impl SolTypeValue for bool { #[inline] fn to_tokens(&self) -> WordToken { WordToken(Word::with_last_byte(*self as u8)) @@ -35,7 +35,7 @@ impl SolTypeEncodable for bool { #[inline] fn eip712_data_word(&self) -> Word { - SolTypeEncodable::::to_tokens(self).0 + SolTypeValue::::to_tokens(self).0 } } @@ -62,7 +62,7 @@ impl SolType for Bool { /// Int - `intX` pub struct Int; -impl SolTypeEncodable> for T +impl SolTypeValue> for T where T: Borrow< as SupportedInt>::Int>, IntBitCount: SupportedInt, @@ -79,7 +79,7 @@ where #[inline] fn eip712_data_word(&self) -> Word { - SolTypeEncodable::>::to_tokens(self).0 + SolTypeValue::>::to_tokens(self).0 } } @@ -119,7 +119,7 @@ where /// Uint - `uintX` pub struct Uint; -impl SolTypeEncodable> for T +impl SolTypeValue> for T where T: Borrow< as SupportedInt>::Uint>, IntBitCount: SupportedInt, @@ -136,7 +136,7 @@ where #[inline] fn eip712_data_word(&self) -> Word { - SolTypeEncodable::>::to_tokens(self).0 + SolTypeValue::>::to_tokens(self).0 } } @@ -166,7 +166,7 @@ where /// Address - `address` pub struct Address; -impl> SolTypeEncodable
for T { +impl> SolTypeValue
for T { #[inline] fn to_tokens(&self) -> WordToken { WordToken(RustAddress::new(*self.borrow()).into_word()) @@ -179,7 +179,7 @@ impl> SolTypeEncodable
for T { #[inline] fn eip712_data_word(&self) -> Word { - SolTypeEncodable::
::to_tokens(self).0 + SolTypeValue::
::to_tokens(self).0 } } @@ -206,7 +206,7 @@ impl SolType for Address { /// Function - `function` pub struct Function; -impl> SolTypeEncodable for T { +impl> SolTypeValue for T { #[inline] fn to_tokens(&self) -> WordToken { WordToken(RustFunction::new(*self.borrow()).into_word()) @@ -219,7 +219,7 @@ impl> SolTypeEncodable for T { #[inline] fn eip712_data_word(&self) -> Word { - SolTypeEncodable::::to_tokens(self).0 + SolTypeValue::::to_tokens(self).0 } } @@ -246,7 +246,7 @@ impl SolType for Function { /// Bytes - `bytes` pub struct Bytes; -impl> SolTypeEncodable for T { +impl> SolTypeValue for T { #[inline] fn to_tokens(&self) -> PackedSeqToken<'_> { PackedSeqToken(self.as_ref()) @@ -293,9 +293,9 @@ impl SolType for Bytes { /// Array - `T[]` pub struct Array(PhantomData); -impl SolTypeEncodable> for [T] +impl SolTypeValue> for [T] where - T: SolTypeEncodable, + T: SolTypeValue, U: SolType, { #[inline] @@ -326,9 +326,9 @@ where } } -impl SolTypeEncodable> for &[T] +impl SolTypeValue> for &[T] where - T: SolTypeEncodable, + T: SolTypeValue, U: SolType, { #[inline] @@ -352,9 +352,9 @@ where } } -impl SolTypeEncodable> for &mut [T] +impl SolTypeValue> for &mut [T] where - T: SolTypeEncodable, + T: SolTypeValue, U: SolType, { #[inline] @@ -378,14 +378,14 @@ where } } -impl SolTypeEncodable> for Vec +impl SolTypeValue> for Vec where - T: SolTypeEncodable, + T: SolTypeValue, U: SolType, { #[inline] fn to_tokens(&self) -> DynSeqToken> { - <[T] as SolTypeEncodable>>::to_tokens(self) + <[T] as SolTypeValue>>::to_tokens(self) } #[inline] @@ -429,7 +429,7 @@ impl SolType for Array { /// String - `string` pub struct String; -impl> SolTypeEncodable for T { +impl> SolTypeValue for T { #[inline] fn to_tokens(&self) -> PackedSeqToken<'_> { PackedSeqToken(self.as_ref().as_bytes()) @@ -481,7 +481,7 @@ impl SolType for String { #[derive(Clone, Copy, Debug)] pub struct FixedBytes; -impl, const N: usize> SolTypeEncodable> for T +impl, const N: usize> SolTypeValue> for T where ByteCount: SupportedFixedBytes, { @@ -494,7 +494,7 @@ where #[inline] fn eip712_data_word(&self) -> Word { - SolTypeEncodable::>::to_tokens(self).0 + SolTypeValue::>::to_tokens(self).0 } #[inline] @@ -529,9 +529,9 @@ where /// FixedArray - `T[M]` pub struct FixedArray(PhantomData); -impl SolTypeEncodable> for [T; N] +impl SolTypeValue> for [T; N] where - T: SolTypeEncodable, + T: SolTypeValue, U: SolType, { #[inline] @@ -567,55 +567,55 @@ where } } -impl SolTypeEncodable> for &[T; N] +impl SolTypeValue> for &[T; N] where - T: SolTypeEncodable, + T: SolTypeValue, U: SolType, { #[inline] fn to_tokens(&self) -> as SolType>::TokenType<'_> { - <[T; N] as SolTypeEncodable>>::to_tokens(&**self) + <[T; N] as SolTypeValue>>::to_tokens(&**self) } #[inline] fn abi_encoded_size(&self) -> usize { - SolTypeEncodable::>::abi_encoded_size(&**self) + SolTypeValue::>::abi_encoded_size(&**self) } #[inline] fn eip712_data_word(&self) -> Word { - SolTypeEncodable::>::eip712_data_word(&**self) + SolTypeValue::>::eip712_data_word(&**self) } #[inline] fn abi_encode_packed_to(&self, out: &mut Vec) { - SolTypeEncodable::>::abi_encode_packed_to(&**self, out) + SolTypeValue::>::abi_encode_packed_to(&**self, out) } } -impl SolTypeEncodable> for &mut [T; N] +impl SolTypeValue> for &mut [T; N] where - T: SolTypeEncodable, + T: SolTypeValue, U: SolType, { #[inline] fn to_tokens(&self) -> as SolType>::TokenType<'_> { - <[T; N] as SolTypeEncodable>>::to_tokens(&**self) + <[T; N] as SolTypeValue>>::to_tokens(&**self) } #[inline] fn abi_encoded_size(&self) -> usize { - SolTypeEncodable::>::abi_encoded_size(&**self) + SolTypeValue::>::abi_encoded_size(&**self) } #[inline] fn eip712_data_word(&self) -> Word { - SolTypeEncodable::>::eip712_data_word(&**self) + SolTypeValue::>::eip712_data_word(&**self) } #[inline] fn abi_encode_packed_to(&self, out: &mut Vec) { - SolTypeEncodable::>::abi_encode_packed_to(&**self, out) + SolTypeValue::>::abi_encode_packed_to(&**self, out) } } @@ -649,11 +649,11 @@ impl SolType for FixedArray { macro_rules! tuple_encodable_impls { ($count:literal $(($ty:ident $uty:ident)),+) => { #[allow(non_snake_case)] - impl<$($ty: SolTypeEncodable<$uty>, $uty: SolType),+> SolTypeEncodable<($($uty,)+)> for ($($ty,)+) { + impl<$($ty: SolTypeValue<$uty>, $uty: SolType),+> SolTypeValue<($($uty,)+)> for ($($ty,)+) { #[inline] fn to_tokens(&self) -> <($($uty,)+) as SolType>::TokenType<'_> { let ($($ty,)+) = self; - ($(SolTypeEncodable::<$uty>::to_tokens($ty),)+) + ($(SolTypeValue::<$uty>::to_tokens($ty),)+) } fn abi_encoded_size(&self) -> usize { @@ -744,7 +744,7 @@ macro_rules! tuple_impls { }; } -impl SolTypeEncodable<()> for () { +impl SolTypeValue<()> for () { #[inline] fn to_tokens(&self) {} diff --git a/crates/sol-types/src/types/event/topic.rs b/crates/sol-types/src/types/event/topic.rs index 6483ecc3b4..dff551de4f 100644 --- a/crates/sol-types/src/types/event/topic.rs +++ b/crates/sol-types/src/types/event/topic.rs @@ -55,12 +55,12 @@ macro_rules! word_impl { #[inline] fn encode_topic_preimage(rust: &Self::RustType, out: &mut Vec) { - out.extend($crate::private::SolTypeEncodable::::to_tokens(rust).0); + out.extend($crate::private::SolTypeValue::::to_tokens(rust).0); } #[inline] fn encode_topic(rust: &Self::RustType) -> WordToken { - $crate::private::SolTypeEncodable::::to_tokens(rust) + $crate::private::SolTypeValue::::to_tokens(rust) } }; } diff --git a/crates/sol-types/src/types/function.rs b/crates/sol-types/src/types/function.rs index 59937f3b49..f641361606 100644 --- a/crates/sol-types/src/types/function.rs +++ b/crates/sol-types/src/types/function.rs @@ -1,6 +1,6 @@ use crate::{ abi::{TokenSeq, TokenType}, - private::SolTypeEncodable, + private::SolTypeValue, Result, SolType, Word, }; use alloc::vec::Vec; @@ -94,7 +94,7 @@ pub trait SolCall: Sized { #[inline] fn abi_encode_returns<'a, E>(e: &'a E) -> Vec where - E: SolTypeEncodable>, + E: SolTypeValue>, { crate::abi::encode_sequence(&e.to_tokens()) } diff --git a/crates/sol-types/src/types/ty.rs b/crates/sol-types/src/types/ty.rs index eae5200823..59c607dca2 100644 --- a/crates/sol-types/src/types/ty.rs +++ b/crates/sol-types/src/types/ty.rs @@ -1,6 +1,6 @@ use crate::{ abi::{self, TokenSeq, TokenType}, - private::SolTypeEncodable, + private::SolTypeValue, Result, Word, }; use alloc::{borrow::Cow, vec::Vec}; @@ -80,7 +80,7 @@ use alloc::{borrow::Cow, vec::Vec}; /// C, /// } /// -/// function myFunction(MyStruct my_struct, MyEnum my_enum) {} +/// function myFunction(MyStruct my_struct, MyEnum my_enum); /// } /// /// // `SolValue` @@ -105,7 +105,7 @@ use alloc::{borrow::Cow, vec::Vec}; /// [`sol!`]: crate::sol pub trait SolType: Sized { /// The corresponding Rust type. - type RustType: SolTypeEncodable + 'static; + type RustType: SolTypeValue + 'static; /// The corresponding ABI [token type](TokenType). /// @@ -119,13 +119,13 @@ pub trait SolType: Sized { /// 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: &E) -> usize { + fn abi_encoded_size>(rust: &E) -> usize { rust.abi_encoded_size() } @@ -139,18 +139,19 @@ pub trait SolType: Sized { 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. + /// + /// 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<'_> { + /// + /// See the [`abi::token`] module for more information. + fn tokenize>(rust: &E) -> Self::TokenType<'_> { rust.to_tokens() } @@ -163,7 +164,7 @@ pub trait SolType: Sized { /// /// #[inline] - fn eip712_data_word>(rust: &E) -> Word { + fn eip712_data_word>(rust: &E) -> Word { rust.eip712_data_word() } @@ -171,7 +172,7 @@ pub trait SolType: Sized { /// /// See [`abi_encode_packed`][SolType::abi_encode_packed] for more details. #[inline] - fn abi_encode_packed_to>(rust: &E, out: &mut Vec) { + fn abi_encode_packed_to>(rust: &E, out: &mut Vec) { rust.abi_encode_packed_to(out) } @@ -185,37 +186,46 @@ pub trait SolType: Sized { /// /// 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: &E) -> Vec { + fn abi_encode_packed>(rust: &E) -> Vec { let mut out = Vec::new(); Self::abi_encode_packed_to(rust, &mut out); out } - /// Encodes a single ABI value 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 { + fn abi_encode>(rust: &E) -> Vec { abi::encode(&rust.to_tokens()) } - /// Encodes 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.to_tokens()) } - /// Encodes 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.to_tokens()) } /// Decode a Rust type from an ABI blob. + /// + /// See the [`abi`] module for more information. #[inline] fn abi_decode(data: &[u8], validate: bool) -> Result { abi::decode::>(data, validate) @@ -223,6 +233,8 @@ pub trait SolType: Sized { } /// ABI-decode the given data + /// + /// See the [`abi`] module for more information. #[inline] fn abi_decode_params<'de>(data: &'de [u8], validate: bool) -> Result where @@ -233,6 +245,8 @@ pub trait SolType: Sized { } /// ABI-decode a Rust type from an ABI blob. + /// + /// See the [`abi`] module for more information. #[inline] fn abi_decode_sequence<'de>(data: &'de [u8], validate: bool) -> Result where diff --git a/crates/sol-types/src/types/udt.rs b/crates/sol-types/src/types/udt.rs index d8868a14d5..18872fd32b 100644 --- a/crates/sol-types/src/types/udt.rs +++ b/crates/sol-types/src/types/udt.rs @@ -19,10 +19,10 @@ macro_rules! define_udt { <$underlying as $crate::SolType>::RustType, ); - impl $crate::private::SolTypeEncodable<$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::private::SolTypeEncodable::<$underlying>::to_tokens(self) + $crate::private::SolTypeValue::<$underlying>::to_tokens(self) } #[inline] diff --git a/crates/sol-types/src/types/value.rs b/crates/sol-types/src/types/value.rs index f040f4302f..e2ac9fb258 100644 --- a/crates/sol-types/src/types/value.rs +++ b/crates/sol-types/src/types/value.rs @@ -1,7 +1,7 @@ use super::SolType; use crate::{ abi::TokenSeq, - private::SolTypeEncodable, + private::SolTypeValue, sol_data::{self, ByteCount, SupportedFixedBytes}, Result, Word, }; @@ -32,7 +32,7 @@ use alloy_primitives::{Address, Bytes, FixedBytes, Function, I256, U256}; /// let _ = my_values.abi_encode_packed(); /// assert_eq!(my_values.sol_type_name(), "(string,uint32,bool,bytes24)"); /// ``` -pub trait SolValue: SolTypeEncodable { +pub trait SolValue: SolTypeValue { /// The Solidity type that this type corresponds to. type SolType: SolType; @@ -44,15 +44,15 @@ pub trait SolValue: SolTypeEncodable { Self::SolType::sol_type_name() } - /// Tokenizes this value into this type's token. + /// Detokenize a value from the given token. /// /// See [`SolType::tokenize`] for more information. #[inline] fn tokenize(&self) -> ::TokenType<'_> { - >::to_tokens(self) + >::to_tokens(self) } - /// Detokenize the given token into this type. + /// Tokenizes the given value into this type's token. /// /// See [`SolType::detokenize`] for more information. #[inline] @@ -68,7 +68,7 @@ pub trait SolValue: SolTypeEncodable { /// See [`SolType::abi_encoded_size`] for more information. #[inline] fn abi_encoded_size(&self) -> usize { - >::abi_encoded_size(self) + >::abi_encoded_size(self) } /// Encode this data according to EIP-712 `encodeData` rules, and hash it @@ -77,7 +77,7 @@ pub trait SolValue: SolTypeEncodable { /// See [`SolType::eip712_data_word`] for more information. #[inline] fn eip712_data_word(&self) -> Word { - >::eip712_data_word(self) + >::eip712_data_word(self) } /// Non-standard Packed Mode ABI encoding. @@ -85,7 +85,7 @@ pub trait SolValue: SolTypeEncodable { /// See [`SolType::abi_encode_packed_to`] for more information. #[inline] fn abi_encode_packed_to(&self, out: &mut Vec) { - >::abi_encode_packed_to(self, out) + >::abi_encode_packed_to(self, out) } /// Non-standard Packed Mode ABI encoding. @@ -94,7 +94,7 @@ pub trait SolValue: SolTypeEncodable { #[inline] fn abi_encode_packed(&self) -> Vec { let mut out = Vec::new(); - >::abi_encode_packed_to(self, &mut out); + >::abi_encode_packed_to(self, &mut out); out } @@ -163,7 +163,7 @@ pub trait SolValue: SolTypeEncodable { } } -macro_rules! impl_encodable { +macro_rules! impl_sol_value { ($($(#[$attr:meta])* [$($gen:tt)*] $rust:ty => $sol:ty [$($where:tt)*];)+) => {$( $(#[$attr])* impl<$($gen)*> SolValue for $rust $($where)* { @@ -172,7 +172,7 @@ macro_rules! impl_encodable { )*}; } -impl_encodable! { +impl_sol_value! { // Basic [] bool => sol_data::Bool []; @@ -187,7 +187,7 @@ impl_encodable! { #[cfg(pointer_width = "64")] [] isize => sol_data::Int::<64> []; - // TODO: Array is specialized to encode as `bytes[N]` + // 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> []; @@ -215,8 +215,8 @@ impl_encodable! { [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: SolTypeEncodable]; - ['a, T: ?Sized + SolValue] &'a mut T => T::SolType [where &'a mut T: SolTypeEncodable]; + ['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 { @@ -240,7 +240,7 @@ mod tests { // Make sure these are in scope #[allow(unused_imports)] - use crate::{private::SolTypeEncodable as _, SolType as _}; + use crate::{private::SolTypeValue as _, SolType as _}; #[test] fn inference() { From 46a50dfd1448d3a5656b86a59c824664c0f2646b Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 7 Oct 2023 11:16:06 +0200 Subject: [PATCH 17/20] rename SolTypeValue methods to avoid naming collisions --- crates/sol-macro/src/expand/enum.rs | 6 +- crates/sol-macro/src/expand/struct.rs | 8 +- crates/sol-types/src/lib.rs | 10 +- crates/sol-types/src/types/data_type.rs | 192 +++++++++++----------- crates/sol-types/src/types/event/topic.rs | 4 +- crates/sol-types/src/types/function.rs | 2 +- crates/sol-types/src/types/ty.rs | 14 +- crates/sol-types/src/types/udt.rs | 8 +- crates/sol-types/src/types/value.rs | 10 +- 9 files changed, 128 insertions(+), 126 deletions(-) diff --git a/crates/sol-macro/src/expand/enum.rs b/crates/sol-macro/src/expand/enum.rs index ebd0cdf78a..f990d15d26 100644 --- a/crates/sol-macro/src/expand/enum.rs +++ b/crates/sol-macro/src/expand/enum.rs @@ -119,17 +119,17 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result #[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 eip712_data_word(&self) -> ::alloy_sol_types::Word { + fn stv_eip712_data_word(&self) -> ::alloy_sol_types::Word { #uint8_st::eip712_data_word(self.as_u8()) } #[inline] - fn abi_encode_packed_to(&self, out: &mut ::alloy_sol_types::private::Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut ::alloy_sol_types::private::Vec) { out.push(*self as u8); } } diff --git a/crates/sol-macro/src/expand/struct.rs b/crates/sol-macro/src/expand/struct.rs index 61d5d9e6ca..0e3273276e 100644 --- a/crates/sol-macro/src/expand/struct.rs +++ b/crates/sol-macro/src/expand/struct.rs @@ -83,24 +83,24 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { #[automatically_derived] impl ::alloy_sol_types::private::SolTypeValue for #name { - fn to_tokens(&self) -> ::TokenType<'_> { + fn stv_to_tokens(&self) -> ::TokenType<'_> { #tokenize_impl } #[inline] - fn abi_encoded_size(&self) -> usize { + 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 eip712_data_word(&self) -> ::alloy_sol_types::Word { + fn stv_eip712_data_word(&self) -> ::alloy_sol_types::Word { ::eip712_hash_struct(self) } #[inline] - fn abi_encode_packed_to(&self, out: &mut ::alloy_sol_types::private::Vec) { + 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) diff --git a/crates/sol-types/src/lib.rs b/crates/sol-types/src/lib.rs index 4c0a3dc766..7fc315d773 100644 --- a/crates/sol-types/src/lib.rs +++ b/crates/sol-types/src/lib.rs @@ -224,12 +224,14 @@ pub mod private { /// [`SolType`](crate::SolType), which is also discouraged. Consider /// using [`SolValue`](crate::SolValue) instead. pub trait SolTypeValue { - fn to_tokens(&self) -> T::TokenType<'_>; - fn abi_encoded_size(&self) -> usize { + // 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 abi_encode_packed_to(&self, out: &mut Vec); - fn eip712_data_word(&self) -> super::Word; + fn stv_abi_encode_packed_to(&self, out: &mut Vec); + fn stv_eip712_data_word(&self) -> super::Word; } #[inline(always)] diff --git a/crates/sol-types/src/types/data_type.rs b/crates/sol-types/src/types/data_type.rs index 112b151179..246eded938 100644 --- a/crates/sol-types/src/types/data_type.rs +++ b/crates/sol-types/src/types/data_type.rs @@ -24,18 +24,18 @@ pub struct 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 abi_encode_packed_to(&self, out: &mut Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { out.push(*self as u8); } #[inline] - fn eip712_data_word(&self) -> Word { - SolTypeValue::::to_tokens(self).0 + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::::stv_to_tokens(self).0 } } @@ -68,18 +68,18 @@ where IntBitCount: SupportedInt, { #[inline] - fn to_tokens(&self) -> WordToken { + fn stv_to_tokens(&self) -> WordToken { IntBitCount::::tokenize_int(*self.borrow()) } #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { IntBitCount::::encode_packed_to_int(*self.borrow(), out); } #[inline] - fn eip712_data_word(&self) -> Word { - SolTypeValue::>::to_tokens(self).0 + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::>::stv_to_tokens(self).0 } } @@ -125,18 +125,18 @@ where IntBitCount: SupportedInt, { #[inline] - fn to_tokens(&self) -> WordToken { + fn stv_to_tokens(&self) -> WordToken { IntBitCount::::tokenize_uint(*self.borrow()) } #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { IntBitCount::::encode_packed_to_uint(*self.borrow(), out); } #[inline] - fn eip712_data_word(&self) -> Word { - SolTypeValue::>::to_tokens(self).0 + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::>::stv_to_tokens(self).0 } } @@ -168,18 +168,18 @@ pub struct Address; impl> SolTypeValue
for T { #[inline] - fn to_tokens(&self) -> WordToken { + fn stv_to_tokens(&self) -> WordToken { WordToken(RustAddress::new(*self.borrow()).into_word()) } #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { out.extend_from_slice(self.borrow()); } #[inline] - fn eip712_data_word(&self) -> Word { - SolTypeValue::
::to_tokens(self).0 + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::
::stv_to_tokens(self).0 } } @@ -208,18 +208,18 @@ pub struct Function; impl> SolTypeValue for T { #[inline] - fn to_tokens(&self) -> WordToken { + fn stv_to_tokens(&self) -> WordToken { WordToken(RustFunction::new(*self.borrow()).into_word()) } #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { out.extend_from_slice(self.borrow()); } #[inline] - fn eip712_data_word(&self) -> Word { - SolTypeValue::::to_tokens(self).0 + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::::stv_to_tokens(self).0 } } @@ -248,22 +248,22 @@ pub struct Bytes; impl> SolTypeValue for T { #[inline] - fn to_tokens(&self) -> PackedSeqToken<'_> { + fn stv_to_tokens(&self) -> PackedSeqToken<'_> { PackedSeqToken(self.as_ref()) } #[inline] - fn abi_encoded_size(&self) -> usize { + fn stv_abi_encoded_size(&self) -> usize { 32 + utils::padded_len(self.as_ref()) } #[inline] - fn eip712_data_word(&self) -> Word { + fn stv_eip712_data_word(&self) -> Word { keccak256(Bytes::abi_encode_packed(self)) } #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { out.extend_from_slice(self.as_ref()); } } @@ -299,29 +299,29 @@ where U: SolType, { #[inline] - fn to_tokens(&self) -> DynSeqToken> { - DynSeqToken(self.iter().map(T::to_tokens).collect()) + fn stv_to_tokens(&self) -> DynSeqToken> { + DynSeqToken(self.iter().map(T::stv_to_tokens).collect()) } #[inline] - fn abi_encoded_size(&self) -> usize { - 32 + self.iter().map(T::abi_encoded_size).sum::() + 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 eip712_data_word(&self) -> Word { + fn stv_eip712_data_word(&self) -> Word { let mut encoded = Vec::new(); for item in self { - encoded.extend_from_slice(T::eip712_data_word(item).as_slice()); + encoded.extend_from_slice(T::stv_eip712_data_word(item).as_slice()); } keccak256(encoded) } #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { for item in self { - T::abi_encode_packed_to(item, out); + T::stv_abi_encode_packed_to(item, out); } } } @@ -332,23 +332,23 @@ where U: SolType, { #[inline] - fn to_tokens(&self) -> DynSeqToken> { - (**self).to_tokens() + fn stv_to_tokens(&self) -> DynSeqToken> { + (**self).stv_to_tokens() } #[inline] - fn abi_encoded_size(&self) -> usize { - (**self).abi_encoded_size() + fn stv_abi_encoded_size(&self) -> usize { + (**self).stv_abi_encoded_size() } #[inline] - fn eip712_data_word(&self) -> Word { - (**self).eip712_data_word() + fn stv_eip712_data_word(&self) -> Word { + (**self).stv_eip712_data_word() } #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) { - (**self).abi_encode_packed_to(out) + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + (**self).stv_abi_encode_packed_to(out) } } @@ -358,23 +358,23 @@ where U: SolType, { #[inline] - fn to_tokens(&self) -> DynSeqToken> { - (**self).to_tokens() + fn stv_to_tokens(&self) -> DynSeqToken> { + (**self).stv_to_tokens() } #[inline] - fn abi_encoded_size(&self) -> usize { - (**self).abi_encoded_size() + fn stv_abi_encoded_size(&self) -> usize { + (**self).stv_abi_encoded_size() } #[inline] - fn eip712_data_word(&self) -> Word { - (**self).eip712_data_word() + fn stv_eip712_data_word(&self) -> Word { + (**self).stv_eip712_data_word() } #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) { - (**self).abi_encode_packed_to(out) + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + (**self).stv_abi_encode_packed_to(out) } } @@ -384,23 +384,23 @@ where U: SolType, { #[inline] - fn to_tokens(&self) -> DynSeqToken> { - <[T] as SolTypeValue>>::to_tokens(self) + fn stv_to_tokens(&self) -> DynSeqToken> { + <[T] as SolTypeValue>>::stv_to_tokens(self) } #[inline] - fn abi_encoded_size(&self) -> usize { - (**self).abi_encoded_size() + fn stv_abi_encoded_size(&self) -> usize { + (**self).stv_abi_encoded_size() } #[inline] - fn eip712_data_word(&self) -> Word { - (**self).eip712_data_word() + fn stv_eip712_data_word(&self) -> Word { + (**self).stv_eip712_data_word() } #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) { - (**self).abi_encode_packed_to(out) + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + (**self).stv_abi_encode_packed_to(out) } } @@ -431,22 +431,22 @@ pub struct String; impl> SolTypeValue for T { #[inline] - fn to_tokens(&self) -> PackedSeqToken<'_> { + fn stv_to_tokens(&self) -> PackedSeqToken<'_> { PackedSeqToken(self.as_ref().as_bytes()) } #[inline] - fn abi_encoded_size(&self) -> usize { + fn stv_abi_encoded_size(&self) -> usize { 32 + utils::padded_len(self.as_ref().as_ref()) } #[inline] - fn eip712_data_word(&self) -> Word { + fn stv_eip712_data_word(&self) -> Word { keccak256(String::abi_encode_packed(self)) } #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { out.extend_from_slice(self.as_ref().as_ref()); } } @@ -486,19 +486,19 @@ where ByteCount: SupportedFixedBytes, { #[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 eip712_data_word(&self) -> Word { - SolTypeValue::>::to_tokens(self).0 + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::>::stv_to_tokens(self).0 } #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { out.extend_from_slice(self.borrow().as_slice()); } } @@ -535,34 +535,34 @@ where U: SolType, { #[inline] - fn to_tokens(&self) -> as SolType>::TokenType<'_> { - FixedSeqToken(core::array::from_fn(|i| self[i].to_tokens())) + fn stv_to_tokens(&self) -> as SolType>::TokenType<'_> { + FixedSeqToken(core::array::from_fn(|i| self[i].stv_to_tokens())) } #[inline] - fn abi_encoded_size(&self) -> usize { + fn stv_abi_encoded_size(&self) -> usize { if let Some(size) = FixedArray::::ENCODED_SIZE { return size } - self.iter().map(T::abi_encoded_size).sum::() + (U::DYNAMIC as usize * N * 32) + self.iter().map(T::stv_abi_encoded_size).sum::() + (U::DYNAMIC as usize * N * 32) } #[inline] - fn eip712_data_word(&self) -> Word { + 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::eip712_data_word(element).0) + .map(|element| T::stv_eip712_data_word(element).0) .collect::>(); keccak256(crate::impl_core::into_flattened(encoded)) } #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { for item in self { - T::abi_encode_packed_to(item, out); + T::stv_abi_encode_packed_to(item, out); } } } @@ -573,23 +573,23 @@ where U: SolType, { #[inline] - fn to_tokens(&self) -> as SolType>::TokenType<'_> { - <[T; N] as SolTypeValue>>::to_tokens(&**self) + fn stv_to_tokens(&self) -> as SolType>::TokenType<'_> { + <[T; N] as SolTypeValue>>::stv_to_tokens(&**self) } #[inline] - fn abi_encoded_size(&self) -> usize { - SolTypeValue::>::abi_encoded_size(&**self) + fn stv_abi_encoded_size(&self) -> usize { + SolTypeValue::>::stv_abi_encoded_size(&**self) } #[inline] - fn eip712_data_word(&self) -> Word { - SolTypeValue::>::eip712_data_word(&**self) + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::>::stv_eip712_data_word(&**self) } #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) { - SolTypeValue::>::abi_encode_packed_to(&**self, out) + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + SolTypeValue::>::stv_abi_encode_packed_to(&**self, out) } } @@ -599,23 +599,23 @@ where U: SolType, { #[inline] - fn to_tokens(&self) -> as SolType>::TokenType<'_> { - <[T; N] as SolTypeValue>>::to_tokens(&**self) + fn stv_to_tokens(&self) -> as SolType>::TokenType<'_> { + <[T; N] as SolTypeValue>>::stv_to_tokens(&**self) } #[inline] - fn abi_encoded_size(&self) -> usize { - SolTypeValue::>::abi_encoded_size(&**self) + fn stv_abi_encoded_size(&self) -> usize { + SolTypeValue::>::stv_abi_encoded_size(&**self) } #[inline] - fn eip712_data_word(&self) -> Word { - SolTypeValue::>::eip712_data_word(&**self) + fn stv_eip712_data_word(&self) -> Word { + SolTypeValue::>::stv_eip712_data_word(&**self) } #[inline] - fn abi_encode_packed_to(&self, out: &mut Vec) { - SolTypeValue::>::abi_encode_packed_to(&**self, out) + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { + SolTypeValue::>::stv_abi_encode_packed_to(&**self, out) } } @@ -651,12 +651,12 @@ macro_rules! tuple_encodable_impls { #[allow(non_snake_case)] impl<$($ty: SolTypeValue<$uty>, $uty: SolType),+> SolTypeValue<($($uty,)+)> for ($($ty,)+) { #[inline] - fn to_tokens(&self) -> <($($uty,)+) as SolType>::TokenType<'_> { + fn stv_to_tokens(&self) -> <($($uty,)+) as SolType>::TokenType<'_> { let ($($ty,)+) = self; - ($(SolTypeValue::<$uty>::to_tokens($ty),)+) + ($(SolTypeValue::<$uty>::stv_to_tokens($ty),)+) } - fn abi_encoded_size(&self) -> usize { + fn stv_abi_encoded_size(&self) -> usize { if let Some(size) = <($($uty,)+) as SolType>::ENCODED_SIZE { return size } @@ -670,7 +670,7 @@ macro_rules! tuple_encodable_impls { )+ } - fn abi_encode_packed_to(&self, out: &mut Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut Vec) { let ($($ty,)+) = self; // TODO: Reserve $( @@ -678,7 +678,7 @@ macro_rules! tuple_encodable_impls { )+ } - fn eip712_data_word(&self) -> Word { + fn stv_eip712_data_word(&self) -> Word { let ($($ty,)+) = self; let encoding: [[u8; 32]; $count] = [$( <$uty as SolType>::eip712_data_word($ty).0, @@ -746,15 +746,15 @@ macro_rules! tuple_impls { impl SolTypeValue<()> for () { #[inline] - fn to_tokens(&self) {} + fn stv_to_tokens(&self) {} #[inline] - fn eip712_data_word(&self) -> Word { + fn stv_eip712_data_word(&self) -> Word { Word::ZERO } #[inline] - fn abi_encode_packed_to(&self, _out: &mut Vec) {} + fn stv_abi_encode_packed_to(&self, _out: &mut Vec) {} } all_the_tuples!(@double tuple_encodable_impls); diff --git a/crates/sol-types/src/types/event/topic.rs b/crates/sol-types/src/types/event/topic.rs index dff551de4f..3e1c547286 100644 --- a/crates/sol-types/src/types/event/topic.rs +++ b/crates/sol-types/src/types/event/topic.rs @@ -55,12 +55,12 @@ macro_rules! word_impl { #[inline] fn encode_topic_preimage(rust: &Self::RustType, out: &mut Vec) { - out.extend($crate::private::SolTypeValue::::to_tokens(rust).0); + out.extend($crate::private::SolTypeValue::::stv_to_tokens(rust).0); } #[inline] fn encode_topic(rust: &Self::RustType) -> WordToken { - $crate::private::SolTypeValue::::to_tokens(rust) + $crate::private::SolTypeValue::::stv_to_tokens(rust) } }; } diff --git a/crates/sol-types/src/types/function.rs b/crates/sol-types/src/types/function.rs index f641361606..baaf7d718e 100644 --- a/crates/sol-types/src/types/function.rs +++ b/crates/sol-types/src/types/function.rs @@ -96,6 +96,6 @@ pub trait SolCall: Sized { where 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/ty.rs b/crates/sol-types/src/types/ty.rs index 59c607dca2..1b4cd73cff 100644 --- a/crates/sol-types/src/types/ty.rs +++ b/crates/sol-types/src/types/ty.rs @@ -126,7 +126,7 @@ pub trait SolType: Sized { /// words. For a single-word type this will always be 32. #[inline] fn abi_encoded_size>(rust: &E) -> usize { - rust.abi_encoded_size() + rust.stv_abi_encoded_size() } /// Returns `true` if the given token can be detokenized with this type. @@ -152,7 +152,7 @@ pub trait SolType: Sized { /// /// See the [`abi::token`] module for more information. fn tokenize>(rust: &E) -> Self::TokenType<'_> { - rust.to_tokens() + rust.stv_to_tokens() } /// Encode this data according to EIP-712 `encodeData` rules, and hash it @@ -165,7 +165,7 @@ pub trait SolType: Sized { /// #[inline] fn eip712_data_word>(rust: &E) -> Word { - rust.eip712_data_word() + rust.stv_eip712_data_word() } /// Non-standard Packed Mode ABI encoding. @@ -173,7 +173,7 @@ pub trait SolType: Sized { /// See [`abi_encode_packed`][SolType::abi_encode_packed] for more details. #[inline] fn abi_encode_packed_to>(rust: &E, out: &mut Vec) { - rust.abi_encode_packed_to(out) + rust.stv_abi_encode_packed_to(out) } /// Non-standard Packed Mode ABI encoding. @@ -198,7 +198,7 @@ pub trait SolType: Sized { /// See the [`abi`] module for more information. #[inline] fn abi_encode>(rust: &E) -> Vec { - abi::encode(&rust.to_tokens()) + abi::encode(&rust.stv_to_tokens()) } /// Tokenizes and ABI-encodes the given value as function parameters. @@ -209,7 +209,7 @@ pub trait SolType: Sized { where for<'a> Self::TokenType<'a>: TokenSeq<'a>, { - abi::encode_params(&rust.to_tokens()) + abi::encode_params(&rust.stv_to_tokens()) } /// Tokenizes and ABI-encodes the given value as a sequence. @@ -220,7 +220,7 @@ pub trait SolType: Sized { where for<'a> Self::TokenType<'a>: TokenSeq<'a>, { - abi::encode_sequence(&rust.to_tokens()) + abi::encode_sequence(&rust.stv_to_tokens()) } /// Decode a Rust type from an ABI blob. diff --git a/crates/sol-types/src/types/udt.rs b/crates/sol-types/src/types/udt.rs index 18872fd32b..944bbc74d0 100644 --- a/crates/sol-types/src/types/udt.rs +++ b/crates/sol-types/src/types/udt.rs @@ -21,17 +21,17 @@ macro_rules! define_udt { impl $crate::private::SolTypeValue<$name> for <$underlying as $crate::SolType>::RustType { #[inline] - fn to_tokens(&self) -> <$underlying as $crate::SolType>::TokenType<'_> { - $crate::private::SolTypeValue::<$underlying>::to_tokens(self) + fn stv_to_tokens(&self) -> <$underlying as $crate::SolType>::TokenType<'_> { + $crate::private::SolTypeValue::<$underlying>::stv_to_tokens(self) } #[inline] - fn eip712_data_word(&self) -> $crate::Word { + fn stv_eip712_data_word(&self) -> $crate::Word { <$underlying as $crate::SolType>::tokenize(self).0 } #[inline] - fn abi_encode_packed_to(&self, out: &mut $crate::private::Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut $crate::private::Vec) { <$underlying as $crate::SolType>::abi_encode_packed_to(self, out) } } diff --git a/crates/sol-types/src/types/value.rs b/crates/sol-types/src/types/value.rs index e2ac9fb258..4b8adfa75a 100644 --- a/crates/sol-types/src/types/value.rs +++ b/crates/sol-types/src/types/value.rs @@ -49,7 +49,7 @@ pub trait SolValue: SolTypeValue { /// See [`SolType::tokenize`] for more information. #[inline] fn tokenize(&self) -> ::TokenType<'_> { - >::to_tokens(self) + >::stv_to_tokens(self) } /// Tokenizes the given value into this type's token. @@ -68,7 +68,7 @@ pub trait SolValue: SolTypeValue { /// See [`SolType::abi_encoded_size`] for more information. #[inline] fn abi_encoded_size(&self) -> usize { - >::abi_encoded_size(self) + >::stv_abi_encoded_size(self) } /// Encode this data according to EIP-712 `encodeData` rules, and hash it @@ -77,7 +77,7 @@ pub trait SolValue: SolTypeValue { /// See [`SolType::eip712_data_word`] for more information. #[inline] fn eip712_data_word(&self) -> Word { - >::eip712_data_word(self) + >::stv_eip712_data_word(self) } /// Non-standard Packed Mode ABI encoding. @@ -85,7 +85,7 @@ pub trait SolValue: SolTypeValue { /// See [`SolType::abi_encode_packed_to`] for more information. #[inline] fn abi_encode_packed_to(&self, out: &mut Vec) { - >::abi_encode_packed_to(self, out) + >::stv_abi_encode_packed_to(self, out) } /// Non-standard Packed Mode ABI encoding. @@ -94,7 +94,7 @@ pub trait SolValue: SolTypeValue { #[inline] fn abi_encode_packed(&self) -> Vec { let mut out = Vec::new(); - >::abi_encode_packed_to(self, &mut out); + >::stv_abi_encode_packed_to(self, &mut out); out } From fa86685cea891ce967e2cfe510fd2afffdf9b61d Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 7 Oct 2023 11:20:31 +0200 Subject: [PATCH 18/20] update event doc --- crates/sol-types/src/types/event/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/sol-types/src/types/event/mod.rs b/crates/sol-types/src/types/event/mod.rs index e3723e83c3..bba5f91419 100644 --- a/crates/sol-types/src/types/event/mod.rs +++ b/crates/sol-types/src/types/event/mod.rs @@ -15,9 +15,9 @@ pub use topic_list::TopicList; /// /// # 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 From 310e0640417a02fed02d5d47171153fa77f746cd Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 7 Oct 2023 11:32:01 +0200 Subject: [PATCH 19/20] tweaks --- crates/sol-types/src/abi/decoder.rs | 2 +- crates/sol-types/src/types/ty.rs | 32 ++++++++++++++++------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/crates/sol-types/src/abi/decoder.rs b/crates/sol-types/src/abi/decoder.rs index 27ffc5614a..1f04f9825c 100644 --- a/crates/sol-types/src/abi/decoder.rs +++ b/crates/sol-types/src/abi/decoder.rs @@ -239,7 +239,7 @@ 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 are probably looking for /// [`SolValue::abi_decode`](crate::SolValue::abi_decode) if you are not diff --git a/crates/sol-types/src/types/ty.rs b/crates/sol-types/src/types/ty.rs index 1b4cd73cff..ad3b05173f 100644 --- a/crates/sol-types/src/types/ty.rs +++ b/crates/sol-types/src/types/ty.rs @@ -143,7 +143,7 @@ pub trait SolType: Sized { } } - /// 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; @@ -223,16 +223,17 @@ pub trait SolType: Sized { 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] @@ -241,10 +242,11 @@ pub trait SolType: Sized { 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] @@ -253,16 +255,18 @@ pub trait SolType: Sized { 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)) } From ba8cdf4c2ec4a504371c450bdf4ec770b63f61d0 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 7 Oct 2023 11:43:13 +0200 Subject: [PATCH 20/20] final tweaks --- .github/ISSUE_TEMPLATE/BUG-FORM.yml | 9 ++-- .github/ISSUE_TEMPLATE/FEATURE-FORM.yml | 61 +++++++++++++------------ crates/sol-types/src/types/value.rs | 15 +++--- 3 files changed, 46 insertions(+), 39 deletions(-) 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/sol-types/src/types/value.rs b/crates/sol-types/src/types/value.rs index 4b8adfa75a..b3a6253238 100644 --- a/crates/sol-types/src/types/value.rs +++ b/crates/sol-types/src/types/value.rs @@ -245,22 +245,25 @@ mod tests { #[test] fn inference() { false.sol_type_name(); - // false.abi_encoded_size(); - // false.eip712_data_word(); - // false.abi_encode_packed_to(&mut vec![]); + 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_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]