From 04a1df581a56a8f0696bf580174dda2d1f75fc36 Mon Sep 17 00:00:00 2001
From: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Date: Thu, 23 Nov 2023 17:21:43 +0100
Subject: [PATCH 1/4] fix(sol-types): fixed array decode
---
crates/sol-macro/src/expand/mod.rs | 8 +++
crates/sol-macro/src/expand/struct.rs | 2 +-
crates/sol-types/src/abi/decoder.rs | 70 +++++++++++++++++++++++-
crates/sol-types/src/abi/token.rs | 47 ++++++----------
crates/sol-types/src/lib.rs | 3 +
crates/sol-types/tests/macros/sol/mod.rs | 41 +++++++++++++-
6 files changed, 138 insertions(+), 33 deletions(-)
diff --git a/crates/sol-macro/src/expand/mod.rs b/crates/sol-macro/src/expand/mod.rs
index 59d99218f..6b0247b71 100644
--- a/crates/sol-macro/src/expand/mod.rs
+++ b/crates/sol-macro/src/expand/mod.rs
@@ -558,6 +558,14 @@ fn expand_from_into_tuples
(name: &Ident, fields: &Parameters
) -> TokenStre
#[doc(hidden)]
type UnderlyingRustTuple<'a> = #rust_tuple;
+ #[cfg(test)]
+ #[allow(dead_code, unreachable_patterns)]
+ fn _type_assertion(_t: ::alloy_sol_types::private::AssertTypeEq) {
+ match _t {
+ ::alloy_sol_types::private::AssertTypeEq::<::RustType>(_) => {}
+ }
+ }
+
#[automatically_derived]
#[doc(hidden)]
impl ::core::convert::From<#name> for UnderlyingRustTuple<'_> {
diff --git a/crates/sol-macro/src/expand/struct.rs b/crates/sol-macro/src/expand/struct.rs
index 10b024dd0..5a2591d42 100644
--- a/crates/sol-macro/src/expand/struct.rs
+++ b/crates/sol-macro/src/expand/struct.rs
@@ -134,7 +134,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result {
#eip712_encode_type_fns
- fn eip712_encode_data(&self) -> Vec {
+ fn eip712_encode_data(&self) -> ::alloy_sol_types::private::Vec {
#encode_data_impl
}
}
diff --git a/crates/sol-types/src/abi/decoder.rs b/crates/sol-types/src/abi/decoder.rs
index 894c7193b..52b0db816 100644
--- a/crates/sol-types/src/abi/decoder.rs
+++ b/crates/sol-types/src/abi/decoder.rs
@@ -306,7 +306,7 @@ pub fn decode_sequence<'de, T: TokenSeq<'de>>(data: &'de [u8], validate: bool) -
#[cfg(test)]
mod tests {
- use crate::{sol_data, utils::pad_usize, SolType};
+ use crate::{sol, sol_data, utils::pad_usize, SolType, SolValue};
use alloc::string::ToString;
use alloy_primitives::{address, hex, Address, B256, U256};
@@ -686,4 +686,72 @@ mod tests {
"did not match error"
);
}
+
+ // https://github.com/alloy-rs/core/issues/433
+ #[test]
+ fn fixed_before_dynamic() {
+ sol! {
+ #[derive(Debug, PartialEq, Eq)]
+ struct Ty {
+ bytes32[3] arr;
+ bytes dyn;
+ }
+ }
+
+ let ty = Ty {
+ arr: [[0x11u8; 32].into(), [0x22u8; 32].into(), [0x33u8; 32].into()],
+ r#dyn: vec![0x44u8; 4],
+ };
+ let encoded = hex!(
+ "1111111111111111111111111111111111111111111111111111111111111111"
+ "2222222222222222222222222222222222222222222222222222222222222222"
+ "3333333333333333333333333333333333333333333333333333333333333333"
+ "0000000000000000000000000000000000000000000000000000000000000080"
+ "0000000000000000000000000000000000000000000000000000000000000004"
+ "4444444400000000000000000000000000000000000000000000000000000000"
+ );
+ assert_eq!(hex::encode(ty.abi_encode_params()), hex::encode(encoded));
+ assert_eq!(ty.abi_encoded_size(), encoded.len());
+
+ assert_eq!(::abi_decode_params(&encoded, true).unwrap(), ty);
+ }
+
+ #[test]
+ fn dynarray_before_dynamic() {
+ sol! {
+ #[derive(Debug, PartialEq, Eq)]
+ struct Ty {
+ bytes[3] arr;
+ bytes dyn;
+ }
+ }
+
+ let ty = Ty {
+ arr: [vec![0x11u8; 32], vec![0x22u8; 32], vec![0x33u8; 32]],
+ r#dyn: vec![0x44u8; 4],
+ };
+ let encoded = hex!(
+ "0000000000000000000000000000000000000000000000000000000000000040" // arr offset
+ "0000000000000000000000000000000000000000000000000000000000000160" // dyn offset
+ "0000000000000000000000000000000000000000000000000000000000000060" // arr[0] offset
+ "00000000000000000000000000000000000000000000000000000000000000a0" // arr[1] offset
+ "00000000000000000000000000000000000000000000000000000000000000e0" // arr[2] offset
+ "0000000000000000000000000000000000000000000000000000000000000020" // arr[0]
+ "1111111111111111111111111111111111111111111111111111111111111111"
+ "0000000000000000000000000000000000000000000000000000000000000020" // arr[1]
+ "2222222222222222222222222222222222222222222222222222222222222222"
+ "0000000000000000000000000000000000000000000000000000000000000020" // arr[2]
+ "3333333333333333333333333333333333333333333333333333333333333333"
+ "0000000000000000000000000000000000000000000000000000000000000004" // dyn
+ "4444444400000000000000000000000000000000000000000000000000000000"
+ );
+
+ // TODO: dyn offset is encoded as 0x100 instead of 0x160
+ if cfg!(TODO) {
+ assert_eq!(hex::encode(ty.abi_encode_params()), hex::encode(encoded));
+ }
+ assert_eq!(ty.abi_encoded_size(), encoded.len());
+
+ assert_eq!(::abi_decode_params(&encoded, false).unwrap(), ty);
+ }
}
diff --git a/crates/sol-types/src/abi/token.rs b/crates/sol-types/src/abi/token.rs
index b705ebcfa..7d09d8d93 100644
--- a/crates/sol-types/src/abi/token.rs
+++ b/crates/sol-types/src/abi/token.rs
@@ -247,9 +247,11 @@ impl<'de, T: Token<'de>, const N: usize> Token<'de> for FixedSeqToken {
#[inline]
fn decode_from(dec: &mut Decoder<'de>) -> Result {
- let mut child = if Self::DYNAMIC { dec.take_indirection()? } else { dec.raw_child() };
-
- Self::decode_sequence(&mut child)
+ if Self::DYNAMIC {
+ dec.take_indirection().and_then(|mut child| Self::decode_sequence(&mut child))
+ } else {
+ Self::decode_sequence(dec)
+ }
}
#[inline]
@@ -361,7 +363,7 @@ impl<'de, T: Token<'de>> Token<'de> for DynSeqToken {
let len = child.take_offset()?;
// This appears to be an unclarity in the Solidity spec. The spec
// specifies that offsets are relative to the first word of
- // `enc(X)`. But known-good test vectors ha vrelative to the
+ // `enc(X)`. But known-good test vectors are relative to the
// word AFTER the array size
let mut child = child.raw_child();
(0..len).map(|_| T::decode_from(&mut child)).collect::>>().map(DynSeqToken)
@@ -460,13 +462,14 @@ impl<'de: 'a, 'a> Token<'de> for PackedSeqToken<'a> {
#[inline]
fn head_words(&self) -> usize {
+ // offset
1
}
#[inline]
fn tail_words(&self) -> usize {
- // "1 +" because len is also appended
- 1 + (self.0.len() + 31) / 32
+ // length + words(data)
+ 1 + crate::utils::words_for(self.0)
}
#[inline]
@@ -508,19 +511,11 @@ macro_rules! tuple_impls {
fn decode_from(dec: &mut Decoder<'de>) -> Result {
// The first element in a dynamic tuple is an offset to the tuple's data;
// for a static tuples, the data begins right away
- let mut child = if Self::DYNAMIC {
- dec.take_indirection()?
+ if Self::DYNAMIC {
+ dec.take_indirection().and_then(|mut child| Self::decode_sequence(&mut child))
} else {
- dec.raw_child()
- };
-
- let res = Self::decode_sequence(&mut child)?;
-
- if !Self::DYNAMIC {
- dec.take_offset_from(&child);
+ Self::decode_sequence(dec)
}
-
- Ok(res)
}
#[inline]
@@ -563,18 +558,7 @@ macro_rules! tuple_impls {
#[inline]
fn tail_append(&self, enc: &mut Encoder) {
if Self::DYNAMIC {
- let ($($ty,)+) = self;
- let head_words = 0 $( + $ty.head_words() )+;
-
- enc.push_offset(head_words);
- $(
- $ty.head_append(enc);
- enc.bump_offset($ty.tail_words());
- )+
- $(
- $ty.tail_append(enc);
- )+
- enc.pop_offset();
+ self.encode_sequence(enc);
}
}
}
@@ -601,7 +585,10 @@ macro_rules! tuple_impls {
#[inline]
fn decode_sequence(dec: &mut Decoder<'de>) -> Result {
Ok(($(
- <$ty as Token>::decode_from(dec)?,
+ match <$ty as Token>::decode_from(dec) {
+ Ok(t) => t,
+ Err(e) => return Err(e),
+ },
)+))
}
}
diff --git a/crates/sol-types/src/lib.rs b/crates/sol-types/src/lib.rs
index 4c2486bfc..61bf685e9 100644
--- a/crates/sol-types/src/lib.rs
+++ b/crates/sol-types/src/lib.rs
@@ -200,6 +200,7 @@ pub use alloy_sol_macro::sol;
// Not public API.
#[doc(hidden)]
+#[allow(missing_debug_implementations)]
pub mod private {
pub use super::utils::{just_ok, next_multiple_of_32, words_for, words_for_len};
pub use alloc::{
@@ -253,4 +254,6 @@ pub mod private {
pub const fn u256(n: u64) -> U256 {
U256::from_limbs([n, 0, 0, 0])
}
+
+ pub struct AssertTypeEq(pub T);
}
diff --git a/crates/sol-types/tests/macros/sol/mod.rs b/crates/sol-types/tests/macros/sol/mod.rs
index 1f9e7aa18..37f16fc17 100644
--- a/crates/sol-types/tests/macros/sol/mod.rs
+++ b/crates/sol-types/tests/macros/sol/mod.rs
@@ -1,4 +1,4 @@
-use alloy_primitives::{hex, keccak256, Address, I256, U256};
+use alloy_primitives::{b256, hex, keccak256, Address, I256, U256};
use alloy_sol_types::{sol, SolCall, SolError, SolEvent, SolStruct, SolType};
use serde::Serialize;
use serde_json::Value;
@@ -734,3 +734,42 @@ fn duplicate_events() {
}
}
}
+
+// https://github.com/alloy-rs/core/issues/433
+#[test]
+fn decoder_fixed_array_before_dynamic() {
+ sol! {
+ #[derive(Debug, PartialEq, Eq)]
+ struct FullReport {
+ bytes32[3] report_context;
+ bytes report_blob;
+ bytes32[] raw_rs;
+ bytes32[] raw_ss;
+ bytes32 raw_vs;
+ }
+ }
+ let full_report = FullReport {
+ report_context: [
+ b256!("0006015a2de20abc8c880eb052a09c069e4edf697529d12eeae88b7b6867fc81"),
+ b256!("00000000000000000000000000000000000000000000000000000000080f7906"),
+ b256!("0000000000000000000000000000000000000000000000000000000000000000"),
+ ],
+ report_blob: hex!("0002191c50b7bdaf2cb8672453141946eea123f8baeaa8d2afa4194b6955e68300000000000000000000000000000000000000000000000000000000655ac7af00000000000000000000000000000000000000000000000000000000655ac7af000000000000000000000000000000000000000000000000000000000000138800000000000000000000000000000000000000000000000000000000000a1f6800000000000000000000000000000000000000000000000000000000655c192f000000000000000000000000000000000000000000000000d130d9ecefeaae30").into(),
+ raw_rs: vec![
+ b256!("d1e3d8b8c581a7ed9cfc41316f1bb8598d98237fc8278a01a9c6a323c4b5c331"),
+ b256!("38ef50778560ec2bb08b23960e3d74f1ffe83b9240a39555c6eb817e3f68302c"),
+ ],
+ raw_ss: vec![
+ b256!("7fb9c59cc499a4672f1481a526d01aa8c01380dcfa0ea855041254d3bcf45536"),
+ b256!("2ce612a86846a7cbb640ddcd3abdecf56618c7b24cf96242643d5c355dee5f0e"),
+ ],
+ raw_vs: b256!("0001000000000000000000000000000000000000000000000000000000000000"),
+ };
+
+ let encoded = FullReport::abi_encode(&full_report);
+ let expected = hex!("00000000000000000000000000000000000000000000000000000000000000200006015a2de20abc8c880eb052a09c069e4edf697529d12eeae88b7b6867fc8100000000000000000000000000000000000000000000000000000000080f7906000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000240000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00002191c50b7bdaf2cb8672453141946eea123f8baeaa8d2afa4194b6955e68300000000000000000000000000000000000000000000000000000000655ac7af00000000000000000000000000000000000000000000000000000000655ac7af000000000000000000000000000000000000000000000000000000000000138800000000000000000000000000000000000000000000000000000000000a1f6800000000000000000000000000000000000000000000000000000000655c192f000000000000000000000000000000000000000000000000d130d9ecefeaae300000000000000000000000000000000000000000000000000000000000000002d1e3d8b8c581a7ed9cfc41316f1bb8598d98237fc8278a01a9c6a323c4b5c33138ef50778560ec2bb08b23960e3d74f1ffe83b9240a39555c6eb817e3f68302c00000000000000000000000000000000000000000000000000000000000000027fb9c59cc499a4672f1481a526d01aa8c01380dcfa0ea855041254d3bcf455362ce612a86846a7cbb640ddcd3abdecf56618c7b24cf96242643d5c355dee5f0e");
+ assert_eq!(hex::encode(&encoded), hex::encode(expected));
+
+ let decoded = FullReport::abi_decode(&encoded, true).unwrap();
+ assert_eq!(decoded, full_report);
+}
From 85f9b5d3ee0706516ed0250500e2f37c25ffecf9 Mon Sep 17 00:00:00 2001
From: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Date: Thu, 23 Nov 2023 17:23:50 +0100
Subject: [PATCH 2/4] wip
---
crates/sol-types/src/abi/token.rs | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/crates/sol-types/src/abi/token.rs b/crates/sol-types/src/abi/token.rs
index 7d09d8d93..4557d4e96 100644
--- a/crates/sol-types/src/abi/token.rs
+++ b/crates/sol-types/src/abi/token.rs
@@ -259,14 +259,14 @@ impl<'de, T: Token<'de>, const N: usize> Token<'de> for FixedSeqToken {
if Self::DYNAMIC {
1
} else {
- self.0.iter().map(Token::head_words).sum()
+ self.0.iter().map(T::head_words).sum()
}
}
#[inline]
fn tail_words(&self) -> usize {
if Self::DYNAMIC {
- N
+ self.0.iter().map(T::tail_words).sum()
} else {
0
}
@@ -294,7 +294,7 @@ impl<'de, T: Token<'de>, const N: usize> Token<'de> for FixedSeqToken {
impl<'de, T: Token<'de>, const N: usize> TokenSeq<'de> for FixedSeqToken {
#[inline]
fn encode_sequence(&self, enc: &mut Encoder) {
- let head_words = self.0.iter().map(Token::head_words).sum::();
+ let head_words = self.0.iter().map(T::head_words).sum::();
enc.push_offset(head_words);
for inner in &self.0 {
@@ -521,6 +521,7 @@ macro_rules! tuple_impls {
#[inline]
fn head_words(&self) -> usize {
if Self::DYNAMIC {
+ // offset
1
} else {
let ($($ty,)+) = self;
@@ -531,18 +532,14 @@ macro_rules! tuple_impls {
#[inline]
fn tail_words(&self) -> usize {
if Self::DYNAMIC {
- self.total_words()
+ // elements
+ let ($($ty,)+) = self;
+ 0 $( + $ty.tail_words() )+
} else {
0
}
}
- #[inline]
- fn total_words(&self) -> usize {
- let ($($ty,)+) = self;
- 0 $( + $ty.total_words() )+
- }
-
#[inline]
fn head_append(&self, enc: &mut Encoder) {
if Self::DYNAMIC {
From 1a40ceae41b8cc73df5a8741c35d02e5244ae5f8 Mon Sep 17 00:00:00 2001
From: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Date: Thu, 23 Nov 2023 19:04:41 +0100
Subject: [PATCH 3/4] fix: word count and encoded size calculations
---
crates/sol-macro/src/expand/function.rs | 6 +-
crates/sol-types/src/abi/decoder.rs | 38 +++++----
crates/sol-types/src/abi/encoder.rs | 98 ++++++++++++++++--------
crates/sol-types/src/abi/token.rs | 14 +++-
crates/sol-types/src/types/data_type.rs | 61 ++++++++++-----
crates/sol-types/src/types/error.rs | 4 +-
crates/sol-types/src/types/function.rs | 12 +--
crates/sol-types/tests/macros/sol/mod.rs | 5 +-
8 files changed, 155 insertions(+), 83 deletions(-)
diff --git a/crates/sol-macro/src/expand/function.rs b/crates/sol-macro/src/expand/function.rs
index 4d6d1db29..d9773e0b6 100644
--- a/crates/sol-macro/src/expand/function.rs
+++ b/crates/sol-macro/src/expand/function.rs
@@ -114,8 +114,8 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, function: &ItemFunction) -> Result = #call_tuple;
- type Token<'a> = as ::alloy_sol_types::SolType>::Token<'a>;
+ type Parameters<'a> = #call_tuple;
+ type Token<'a> = as ::alloy_sol_types::SolType>::Token<'a>;
type Return = #return_name;
@@ -125,7 +125,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, function: &ItemFunction) -> Result(tuple: as ::alloy_sol_types::SolType>::RustType) -> Self {
+ fn new<'a>(tuple: as ::alloy_sol_types::SolType>::RustType) -> Self {
tuple.into()
}
diff --git a/crates/sol-types/src/abi/decoder.rs b/crates/sol-types/src/abi/decoder.rs
index 52b0db816..5d11d2000 100644
--- a/crates/sol-types/src/abi/decoder.rs
+++ b/crates/sol-types/src/abi/decoder.rs
@@ -326,15 +326,13 @@ mod tests {
"
);
- assert_eq!(
- MyTy::abi_encode_params(&vec![
- vec![Address::repeat_byte(0x11)],
- vec![Address::repeat_byte(0x22)],
- ]),
- encoded
- );
+ let ty = vec![vec![Address::repeat_byte(0x11)], vec![Address::repeat_byte(0x22)]];
+ assert_eq!(MyTy::abi_encode_params(&ty), encoded);
- MyTy::abi_decode_params(&encoded, false).unwrap();
+ let decoded = MyTy::abi_decode_params(&encoded, false).unwrap();
+ assert_eq!(decoded, ty);
+ assert_eq!(decoded.abi_encode_params(), encoded);
+ assert_eq!(decoded.abi_encoded_size(), encoded.len());
}
#[test]
@@ -354,6 +352,8 @@ mod tests {
let expected = (address1, address2, uint);
let decoded = MyTy::abi_decode_sequence(&encoded, true).unwrap();
assert_eq!(decoded, expected);
+ assert_eq!(decoded.abi_encode_params(), encoded);
+ assert_eq!(decoded.abi_encoded_size(), encoded.len());
}
#[test]
@@ -377,6 +377,8 @@ mod tests {
// this test vector contains a top-level indirect
let decoded = MyTy::abi_decode(&encoded, true).unwrap();
assert_eq!(decoded, expected);
+ assert_eq!(decoded.abi_encode(), encoded);
+ assert_eq!(decoded.abi_encoded_size(), encoded.len());
}
#[test]
@@ -427,6 +429,8 @@ mod tests {
let decoded = MyTy::abi_decode(&encoded, true).unwrap();
assert_eq!(decoded, expected);
+ assert_eq!(decoded.abi_encode(), encoded);
+ assert_eq!(decoded.abi_encoded_size(), encoded.len());
}
#[test]
@@ -452,6 +456,8 @@ mod tests {
let decoded = MyTy::abi_decode(&encoded, true).unwrap();
assert_eq!(decoded, expected);
+ assert_eq!(decoded.abi_encode(), encoded);
+ assert_eq!(decoded.abi_encoded_size(), encoded.len());
}
#[test]
@@ -492,6 +498,8 @@ mod tests {
let decoded = MyTy::abi_decode_params(&encoded, true).unwrap();
assert_eq!(decoded, expected);
+ assert_eq!(decoded.abi_encode_params(), encoded);
+ assert_eq!(decoded.abi_encoded_size(), encoded.len() + 32);
}
#[test]
@@ -703,6 +711,7 @@ mod tests {
r#dyn: vec![0x44u8; 4],
};
let encoded = hex!(
+ "0000000000000000000000000000000000000000000000000000000000000020"
"1111111111111111111111111111111111111111111111111111111111111111"
"2222222222222222222222222222222222222222222222222222222222222222"
"3333333333333333333333333333333333333333333333333333333333333333"
@@ -710,10 +719,10 @@ mod tests {
"0000000000000000000000000000000000000000000000000000000000000004"
"4444444400000000000000000000000000000000000000000000000000000000"
);
- assert_eq!(hex::encode(ty.abi_encode_params()), hex::encode(encoded));
+ assert_eq!(hex::encode(ty.abi_encode()), hex::encode(encoded));
assert_eq!(ty.abi_encoded_size(), encoded.len());
- assert_eq!(::abi_decode_params(&encoded, true).unwrap(), ty);
+ assert_eq!(::abi_decode(&encoded, true).unwrap(), ty);
}
#[test]
@@ -731,6 +740,7 @@ mod tests {
r#dyn: vec![0x44u8; 4],
};
let encoded = hex!(
+ "0000000000000000000000000000000000000000000000000000000000000020" // struct offset
"0000000000000000000000000000000000000000000000000000000000000040" // arr offset
"0000000000000000000000000000000000000000000000000000000000000160" // dyn offset
"0000000000000000000000000000000000000000000000000000000000000060" // arr[0] offset
@@ -745,13 +755,9 @@ mod tests {
"0000000000000000000000000000000000000000000000000000000000000004" // dyn
"4444444400000000000000000000000000000000000000000000000000000000"
);
-
- // TODO: dyn offset is encoded as 0x100 instead of 0x160
- if cfg!(TODO) {
- assert_eq!(hex::encode(ty.abi_encode_params()), hex::encode(encoded));
- }
+ assert_eq!(hex::encode(ty.abi_encode()), hex::encode(encoded));
assert_eq!(ty.abi_encoded_size(), encoded.len());
- assert_eq!(::abi_decode_params(&encoded, false).unwrap(), ty);
+ assert_eq!(::abi_decode(&encoded, false).unwrap(), ty);
}
}
diff --git a/crates/sol-types/src/abi/encoder.rs b/crates/sol-types/src/abi/encoder.rs
index e992a3d76..f0363d79b 100644
--- a/crates/sol-types/src/abi/encoder.rs
+++ b/crates/sol-types/src/abi/encoder.rs
@@ -134,6 +134,10 @@ impl Encoder {
/// Append a sequence of bytes, padding to the next word.
#[inline(always)]
fn append_bytes(&mut self, bytes: &[u8]) {
+ if bytes.is_empty() {
+ return;
+ }
+
let n_words = utils::words_for(bytes);
self.buf.reserve(n_words);
unsafe {
@@ -240,7 +244,7 @@ mod tests {
)
.to_vec();
assert_eq!(encoded, expected);
- assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(data,)));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -279,7 +283,7 @@ mod tests {
.to_vec();
assert_eq!(encoded, expected);
assert_eq!(encoded_params, expected);
- assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&addresses));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&addresses));
}
#[test]
@@ -309,7 +313,7 @@ mod tests {
let encoded_params = MyTy::abi_encode_params(&data);
assert_eq!(encoded_params, expected);
- assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(data,)));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -338,7 +342,7 @@ mod tests {
assert_eq!(encoded, expected);
let encoded_params = MyTy::abi_encode_params(&data);
assert_eq!(encoded_params, expected);
- assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(data,)));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -365,7 +369,7 @@ mod tests {
assert_eq!(encoded, expected);
let encoded_params = MyTy::abi_encode_params(&data);
assert_eq!(encoded_params, expected);
- assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(data,)));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -396,7 +400,7 @@ mod tests {
assert_eq!(encoded, expected);
let encoded_params = MyTy::abi_encode_params(&data);
assert_eq!(encoded_params, expected);
- assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(data,)));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -454,9 +458,11 @@ mod tests {
)
.to_vec();
+ let encoded = MyTy::abi_encode(&data);
let encoded_params = MyTy::abi_encode_params(&data);
+ assert_ne!(encoded, expected);
assert_eq!(encoded_params, expected);
- assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -476,7 +482,7 @@ mod tests {
.to_vec();
assert_eq!(encoded, expected);
- assert_eq!(encoded.len(), <(MyTy0,)>::abi_encoded_size(&(data,)));
+ assert_eq!(encoded.len(), MyTy0::abi_encoded_size(&data));
type MyTy = (sol_data::Array, sol_data::Array);
let data: (Vec, Vec) = (vec![], vec![]);
@@ -498,7 +504,7 @@ mod tests {
let encoded_params = MyTy::abi_encode_params(&data);
assert_eq!(encoded_params, expected);
assert_eq!(encoded_params.len() + 32, encoded.len());
- assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
type MyTy2 = (
sol_data::Array>,
@@ -530,25 +536,38 @@ mod tests {
assert_eq!(encoded_params, expected);
assert_eq!(encoded_params.len() + 32, encoded.len());
- assert_eq!(encoded_params.len(), MyTy2::abi_encoded_size(&data));
+ assert_eq!(encoded.len(), MyTy2::abi_encoded_size(&data));
+ }
+
+ #[test]
+ fn encode_empty_bytes() {
+ let bytes = Vec::::new();
+
+ let encoded = sol_data::Bytes::abi_encode(&bytes);
+ let expected = hex!(
+ "
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 0000000000000000000000000000000000000000000000000000000000000000
+ "
+ );
+ assert_eq!(encoded, expected);
+ assert_eq!(encoded.len(), sol_data::Bytes::abi_encoded_size(&bytes));
}
#[test]
fn encode_bytes() {
- type MyTy = sol_data::Bytes;
let bytes = vec![0x12, 0x34];
- let encoded = MyTy::abi_encode(&bytes);
+ let encoded = sol_data::Bytes::abi_encode(&bytes);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
1234000000000000000000000000000000000000000000000000000000000000
"
- )
- .to_vec();
+ );
assert_eq!(encoded, expected);
- assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(bytes,)));
+ assert_eq!(encoded.len(), sol_data::Bytes::abi_encoded_size(&bytes));
}
#[test]
@@ -559,6 +578,20 @@ mod tests {
assert_eq!(encoded.len(), sol_data::FixedBytes::<2>::abi_encoded_size(&[0x12, 0x34]));
}
+ #[test]
+ fn encode_empty_string() {
+ let s = "";
+ let encoded = sol_data::String::abi_encode(s);
+ let expected = hex!(
+ "
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 0000000000000000000000000000000000000000000000000000000000000000
+ "
+ );
+ assert_eq!(encoded, expected);
+ assert_eq!(encoded.len(), sol_data::String::abi_encoded_size(&s));
+ }
+
#[test]
fn encode_string() {
let s = "gavofyork".to_string();
@@ -569,10 +602,9 @@ mod tests {
0000000000000000000000000000000000000000000000000000000000000009
6761766f66796f726b0000000000000000000000000000000000000000000000
"
- )
- .to_vec();
+ );
assert_eq!(encoded, expected);
- assert_eq!(encoded.len(), <(sol_data::String,)>::abi_encoded_size(&(s,)));
+ assert_eq!(encoded.len(), sol_data::String::abi_encoded_size(&s));
}
#[test]
@@ -588,7 +620,7 @@ mod tests {
)
.to_vec();
assert_eq!(encoded, expected);
- assert_eq!(encoded.len(), <(sol_data::Bytes,)>::abi_encoded_size(&(bytes,)));
+ assert_eq!(encoded.len(), sol_data::Bytes::abi_encoded_size(&bytes));
}
#[test]
@@ -611,7 +643,7 @@ mod tests {
)
.to_vec();
assert_eq!(encoded, expected);
- assert_eq!(encoded.len(), <(sol_data::Bytes,)>::abi_encoded_size(&(bytes,)));
+ assert_eq!(encoded.len(), sol_data::Bytes::abi_encoded_size(&bytes));
}
#[test]
@@ -641,7 +673,7 @@ mod tests {
assert_ne!(encoded, expected);
assert_eq!(encoded_params, expected);
assert_eq!(encoded_params.len() + 32, encoded.len());
- assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&bytes));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&bytes));
}
#[test]
@@ -716,7 +748,7 @@ mod tests {
assert_ne!(encoded, expected);
assert_eq!(encoded_params, expected);
assert_eq!(encoded_params.len() + 32, encoded.len());
- assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -757,7 +789,7 @@ mod tests {
let encoded_params = MyTy::abi_encode_params(&data);
assert_eq!(encoded_params, expected);
assert_eq!(encoded_params.len() + 32, encoded.len());
- assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -784,7 +816,7 @@ mod tests {
assert_eq!(encoded, expected);
let encoded_params = MyTy::abi_encode_params(&data);
assert_eq!(encoded_params, expected);
- assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(data,)));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -818,7 +850,7 @@ mod tests {
assert_eq!(encoded, expected);
let encoded_params = MyTy::abi_encode_params(&data);
assert_eq!(encoded_params, expected);
- assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(data,)));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -838,7 +870,7 @@ mod tests {
.to_vec();
assert_eq!(encoded, expected);
assert_eq!(encoded_params, expected);
- assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -865,7 +897,7 @@ mod tests {
let encoded_params = MyTy::abi_encode_params(&data);
assert_ne!(encoded_params, expected);
assert_eq!(encoded_params.len() + 32, encoded.len());
- assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -901,7 +933,7 @@ mod tests {
assert_eq!(encoded, expected);
assert_ne!(encoded_params, expected);
assert_eq!(encoded_params.len() + 32, encoded.len());
- assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -934,7 +966,7 @@ mod tests {
let encoded_params = MyTy::abi_encode_params(&data);
assert_ne!(encoded_params, expected);
assert_eq!(encoded_params.len() + 32, encoded.len());
- assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -988,7 +1020,7 @@ mod tests {
assert_eq!(encoded, expected);
assert_ne!(encoded_params, expected);
assert_eq!(encoded_params.len() + 32, encoded.len());
- assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -1034,7 +1066,7 @@ mod tests {
assert_ne!(encoded_single, expected);
assert_eq!(encoded, expected);
assert_eq!(encoded.len() + 32, encoded_single.len());
- assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
+ assert_eq!(encoded_single.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -1071,7 +1103,7 @@ mod tests {
// a static FixedSeq should NEVER indirect
assert_eq!(encoded, expected);
assert_eq!(encoded_params, expected);
- assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
#[test]
@@ -1100,6 +1132,6 @@ mod tests {
assert_eq!(encoded, expected);
assert_ne!(encoded_params, expected);
assert_eq!(encoded_params.len() + 32, encoded.len());
- assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data));
+ assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data));
}
}
diff --git a/crates/sol-types/src/abi/token.rs b/crates/sol-types/src/abi/token.rs
index 4557d4e96..d84b9c419 100644
--- a/crates/sol-types/src/abi/token.rs
+++ b/crates/sol-types/src/abi/token.rs
@@ -257,16 +257,19 @@ impl<'de, T: Token<'de>, const N: usize> Token<'de> for FixedSeqToken {
#[inline]
fn head_words(&self) -> usize {
if Self::DYNAMIC {
+ // offset
1
} else {
- self.0.iter().map(T::head_words).sum()
+ // elements
+ self.0.iter().map(T::total_words).sum()
}
}
#[inline]
fn tail_words(&self) -> usize {
if Self::DYNAMIC {
- self.0.iter().map(T::tail_words).sum()
+ // elements
+ self.0.iter().map(T::total_words).sum()
} else {
0
}
@@ -371,11 +374,13 @@ impl<'de, T: Token<'de>> Token<'de> for DynSeqToken {
#[inline]
fn head_words(&self) -> usize {
+ // offset
1
}
#[inline]
fn tail_words(&self) -> usize {
+ // length + elements
1 + self.0.iter().map(Token::total_words).sum::()
}
@@ -524,8 +529,9 @@ macro_rules! tuple_impls {
// offset
1
} else {
+ // elements
let ($($ty,)+) = self;
- 0 $( + $ty.head_words() )+
+ 0 $( + $ty.total_words() )+
}
}
@@ -534,7 +540,7 @@ macro_rules! tuple_impls {
if Self::DYNAMIC {
// elements
let ($($ty,)+) = self;
- 0 $( + $ty.tail_words() )+
+ 0 $( + $ty.total_words() )+
} else {
0
}
diff --git a/crates/sol-types/src/types/data_type.rs b/crates/sol-types/src/types/data_type.rs
index d7a8dcd13..2b9372271 100644
--- a/crates/sol-types/src/types/data_type.rs
+++ b/crates/sol-types/src/types/data_type.rs
@@ -313,7 +313,12 @@ impl> SolTypeValue for T {
#[inline]
fn stv_abi_encoded_size(&self) -> usize {
- 32 + utils::padded_len(self.as_ref())
+ let s = self.as_ref();
+ if s.is_empty() {
+ 64
+ } else {
+ 64 + utils::padded_len(s)
+ }
}
#[inline]
@@ -360,7 +365,12 @@ impl> SolTypeValue for T {
#[inline]
fn stv_abi_encoded_size(&self) -> usize {
- 32 + utils::padded_len(self.as_ref().as_ref())
+ let s = self.as_ref();
+ if s.is_empty() {
+ 64
+ } else {
+ 64 + utils::padded_len(s.as_bytes())
+ }
}
#[inline]
@@ -415,8 +425,11 @@ where
#[inline]
fn stv_abi_encoded_size(&self) -> usize {
- 32 + self.iter().map(T::stv_abi_encoded_size).sum::()
- + (U::DYNAMIC as usize * 32 * self.len())
+ if let Some(size) = Array::::ENCODED_SIZE {
+ return size;
+ }
+
+ 64 + self.iter().map(T::stv_abi_encoded_size).sum::()
}
#[inline]
@@ -555,18 +568,24 @@ where
return size;
}
- self.iter().map(T::stv_abi_encoded_size).sum::() + (U::DYNAMIC as usize * N * 32)
+ let sum = self.iter().map(T::stv_abi_encoded_size).sum::();
+ if FixedArray::::DYNAMIC {
+ 32 + sum
+ } else {
+ sum
+ }
}
#[inline]
fn stv_eip712_data_word(&self) -> Word {
- // TODO: collect into an array of [u8; 32] and flatten it to a slice like in
- // tuple impl
- let encoded = self
- .iter()
- .map(|element| T::stv_eip712_data_word(element).0)
- .collect::>();
- keccak256(crate::impl_core::into_flattened(encoded))
+ let mut encoded = crate::impl_core::uninit_array::<[u8; 32], N>();
+ for (i, item) in self.iter().enumerate() {
+ encoded[i].write(T::stv_eip712_data_word(item).0);
+ }
+ // SAFETY: Flattening [[u8; 32]; N] to [u8; N * 32] is valid
+ let encoded: &[u8] =
+ unsafe { core::slice::from_raw_parts(encoded.as_ptr().cast(), N * 32) };
+ keccak256(encoded)
}
#[inline]
@@ -672,19 +691,19 @@ macro_rules! tuple_encodable_impls {
}
let ($($ty,)+) = self;
- 0 $(
- + <$uty as SolType>::abi_encoded_size($ty)
- )+
- $(
- + (32 * <$uty as SolType>::DYNAMIC as usize)
- )+
+ let sum = 0 $( + $ty.stv_abi_encoded_size() )+;
+ if <($($uty,)+) as SolType>::DYNAMIC {
+ 32 + sum
+ } else {
+ sum
+ }
}
fn stv_abi_encode_packed_to(&self, out: &mut Vec) {
let ($($ty,)+) = self;
// TODO: Reserve
$(
- <$uty as SolType>::abi_encode_packed_to($ty, out);
+ $ty.stv_abi_encode_packed_to(out);
)+
}
@@ -1197,7 +1216,9 @@ mod tests {
#[test]
#[cfg_attr(miri, ignore = "doesn't run in isolation and would take too long")]
fn $name(i: $t) {
- proptest::prop_assert_eq!(<$st>::detokenize(<$st>::tokenize(&i)), i);
+ let token = <$st>::tokenize(&i);
+ proptest::prop_assert_eq!(token.total_words() * 32, <$st>::abi_encoded_size(&i));
+ proptest::prop_assert_eq!(<$st>::detokenize(token), i);
}
)+}
};
diff --git a/crates/sol-types/src/types/error.rs b/crates/sol-types/src/types/error.rs
index 4436c431b..8a4721be3 100644
--- a/crates/sol-types/src/types/error.rs
+++ b/crates/sol-types/src/types/error.rs
@@ -45,7 +45,9 @@ pub trait SolError: Sized {
return size;
}
- self.tokenize().total_words() * Word::len_bytes()
+ // `total_words` includes the first dynamic offset which we ignore.
+ let offset = < as SolType>::Token<'_> as Token>::DYNAMIC as usize * 32;
+ (self.tokenize().total_words() * Word::len_bytes()).saturating_sub(offset)
}
/// ABI decode this call's arguments from the given slice, **without** its
diff --git a/crates/sol-types/src/types/function.rs b/crates/sol-types/src/types/function.rs
index 0587d57a7..0c1cdf1dc 100644
--- a/crates/sol-types/src/types/function.rs
+++ b/crates/sol-types/src/types/function.rs
@@ -16,7 +16,7 @@ pub trait SolCall: Sized {
/// The underlying tuple type which represents this type's arguments.
///
/// If this type has no arguments, this will be the unit type `()`.
- type Arguments<'a>: SolType = Self::Token<'a>>;
+ type Parameters<'a>: SolType = Self::Token<'a>>;
/// The arguments' corresponding [TokenSeq] type.
type Token<'a>: TokenSeq<'a>;
@@ -39,7 +39,7 @@ pub trait SolCall: Sized {
const SELECTOR: [u8; 4];
/// Convert from the tuple type used for ABI encoding and decoding.
- fn new(tuple: as SolType>::RustType) -> Self;
+ fn new(tuple: as SolType>::RustType) -> Self;
/// Tokenize the call's arguments.
fn tokenize(&self) -> Self::Token<'_>;
@@ -47,18 +47,20 @@ pub trait SolCall: Sized {
/// The size of the encoded data in bytes, **without** its selector.
#[inline]
fn abi_encoded_size(&self) -> usize {
- if let Some(size) = as SolType>::ENCODED_SIZE {
+ if let Some(size) = as SolType>::ENCODED_SIZE {
return size;
}
- self.tokenize().total_words() * Word::len_bytes()
+ // `total_words` includes the first dynamic offset which we ignore.
+ let offset = < as SolType>::Token<'_> as Token>::DYNAMIC as usize * 32;
+ (self.tokenize().total_words() * Word::len_bytes()).saturating_sub(offset)
}
/// ABI decode this call's arguments from the given slice, **without** its
/// selector.
#[inline]
fn abi_decode_raw(data: &[u8], validate: bool) -> Result {
- as SolType>::abi_decode_sequence(data, validate).map(Self::new)
+ as SolType>::abi_decode_sequence(data, validate).map(Self::new)
}
/// ABI decode this call's arguments from the given slice, **with** the
diff --git a/crates/sol-types/tests/macros/sol/mod.rs b/crates/sol-types/tests/macros/sol/mod.rs
index 37f16fc17..76d6e6afe 100644
--- a/crates/sol-types/tests/macros/sol/mod.rs
+++ b/crates/sol-types/tests/macros/sol/mod.rs
@@ -67,11 +67,13 @@ fn e2e() {
#[test]
fn function() {
sol! {
+ #[derive(Debug, PartialEq)]
struct CustomStruct {
address a;
uint64 b;
}
+ #[derive(Debug, PartialEq)]
function someFunction(
uint256 basic,
string memory string_,
@@ -103,12 +105,13 @@ fn function() {
],
};
let encoded = call.abi_encode();
- assert_eq!(encoded.len(), someFunctionCall::SELECTOR.len() + call.abi_encoded_size());
+ assert_eq!(someFunctionCall::abi_decode(&encoded, true).unwrap(), call);
assert_eq!(
call.abi_encoded_size(),
32 + (64 + 32) + (64 + 32 + 32) + (64 + 3 * 32) + 2 * 32 + (32 + 32) + (64 + 4 * (32 + 32))
);
+ assert_eq!(encoded.len(), 4 + call.abi_encoded_size());
}
#[test]
From edeca07e7fbc058ac82e659127e2439663b16108 Mon Sep 17 00:00:00 2001
From: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Date: Thu, 23 Nov 2023 19:36:20 +0100
Subject: [PATCH 4/4] cleanup
---
crates/sol-types/src/abi/token.rs | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/crates/sol-types/src/abi/token.rs b/crates/sol-types/src/abi/token.rs
index d84b9c419..2fc86e7af 100644
--- a/crates/sol-types/src/abi/token.rs
+++ b/crates/sol-types/src/abi/token.rs
@@ -295,10 +295,8 @@ impl<'de, T: Token<'de>, const N: usize> Token<'de> for FixedSeqToken {
}
impl<'de, T: Token<'de>, const N: usize> TokenSeq<'de> for FixedSeqToken {
- #[inline]
fn encode_sequence(&self, enc: &mut Encoder) {
- let head_words = self.0.iter().map(T::head_words).sum::();
- enc.push_offset(head_words);
+ enc.push_offset(self.0.iter().map(T::head_words).sum());
for inner in &self.0 {
inner.head_append(enc);
@@ -381,7 +379,7 @@ impl<'de, T: Token<'de>> Token<'de> for DynSeqToken {
#[inline]
fn tail_words(&self) -> usize {
// length + elements
- 1 + self.0.iter().map(Token::total_words).sum::()
+ 1 + self.0.iter().map(T::total_words).sum::()
}
#[inline]
@@ -398,8 +396,7 @@ impl<'de, T: Token<'de>> Token<'de> for DynSeqToken {
impl<'de, T: Token<'de>> TokenSeq<'de> for DynSeqToken {
fn encode_sequence(&self, enc: &mut Encoder) {
- let head_words = self.0.iter().map(Token::head_words).sum::();
- enc.push_offset(head_words);
+ enc.push_offset(self.0.iter().map(T::head_words).sum());
for inner in &self.0 {
inner.head_append(enc);
@@ -570,18 +567,19 @@ macro_rules! tuple_impls {
impl<'de, $($ty: Token<'de>,)+> TokenSeq<'de> for ($($ty,)+) {
const IS_TUPLE: bool = true;
- #[inline]
fn encode_sequence(&self, enc: &mut Encoder) {
let ($($ty,)+) = self;
- let head_words = 0 $( + $ty.head_words() )+;
- enc.push_offset(head_words);
+ enc.push_offset(0 $( + $ty.head_words() )+);
+
$(
$ty.head_append(enc);
enc.bump_offset($ty.tail_words());
)+
+
$(
$ty.tail_append(enc);
)+
+
enc.pop_offset();
}