git.delta.rocks / unique-network / refs/commits / 7eec33a3cc63

difftreelog

fix AbiRead implementations

Trubnikov Sergey2022-11-16parent: #b3e1096.patch.diff
in: master

5 files changed

modifiedcrates/evm-coder/procedural/src/abi_derive.rsdiffbeforeafterboth
--- a/crates/evm-coder/procedural/src/abi_derive.rs
+++ b/crates/evm-coder/procedural/src/abi_derive.rs
@@ -1,7 +1,6 @@
 use quote::quote;
 
 pub(crate) fn impl_abi_macro(ast: &syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
-	// dbg!(ast);
 	let name = &ast.ident;
 	let (is_named_fields, field_names, field_types, params_count) = match &ast.data {
 		syn::Data::Struct(ds) => match ds.fields {
@@ -31,7 +30,7 @@
 	let abi_type = impl_abi_type(name, field_types.clone());
 	let abi_read = impl_abi_read(name, is_named_fields, field_names.clone(), field_types);
 	let abi_write = impl_abi_write(name, is_named_fields, params_count, field_names);
-	println!("{}", abi_write);
+
 	Ok(quote! {
 		#can_be_plcaed_in_vec
 		#abi_type
@@ -112,14 +111,19 @@
 	quote!(
 		impl ::evm_coder::abi::AbiRead for #name {
 			fn abi_read(reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Self> {
-				let size = if !<Self as ::evm_coder::abi::AbiType>::is_dynamic() {
+				let is_dynamic = <Self as ::evm_coder::abi::AbiType>::is_dynamic();
+				let size = if !is_dynamic {
 					Some(<Self as ::evm_coder::abi::AbiType>::size())
 				} else {
 					None
 				};
 				let mut subresult = reader.subresult(size)?;
 				#(
-					let #field_names = <#field_types as ::evm_coder::abi::AbiRead>::abi_read(&mut subresult)?;
+					let #field_names = {
+						let value = <#field_types as ::evm_coder::abi::AbiRead>::abi_read(&mut subresult)?;
+						if !is_dynamic {subresult.seek(<#field_types as ::evm_coder::abi::AbiType>::size())};
+						value
+					};
 				)*
 
 				#struct_constructor
modifiedcrates/evm-coder/src/abi/impls.rsdiffbeforeafterboth
before · crates/evm-coder/src/abi/impls.rs
1use crate::{2	execution::{Result, ResultWithPostInfo, WithPostDispatchInfo},3	types::*,4	make_signature,5	custom_signature::SignatureUnit,6};7use super::{traits::*, ABI_ALIGNMENT, AbiReader, AbiWriter};8use primitive_types::{U256, H160};910#[cfg(not(feature = "std"))]11use alloc::vec::Vec;1213macro_rules! impl_abi_readable {14	($ty:ty, $method:ident, $dynamic:literal) => {15		impl sealed::CanBePlacedInVec for $ty {}1617		impl AbiType for $ty {18			const SIGNATURE: SignatureUnit = make_signature!(new fixed(stringify!($ty)));1920			fn is_dynamic() -> bool {21				$dynamic22			}2324			fn size() -> usize {25				ABI_ALIGNMENT26			}27		}2829		impl AbiRead for $ty {30			fn abi_read(reader: &mut AbiReader) -> Result<$ty> {31				reader.$method()32			}33		}34	};35}3637impl_abi_readable!(uint32, uint32, false);38impl_abi_readable!(uint64, uint64, false);39impl_abi_readable!(uint128, uint128, false);40impl_abi_readable!(uint256, uint256, false);41impl_abi_readable!(bytes4, bytes4, false);42impl_abi_readable!(address, address, false);43impl_abi_readable!(string, string, true);4445impl sealed::CanBePlacedInVec for bool {}4647impl AbiType for bool {48	const SIGNATURE: SignatureUnit = make_signature!(new fixed("bool"));4950	fn is_dynamic() -> bool {51		false52	}53	fn size() -> usize {54		ABI_ALIGNMENT55	}56}57impl AbiRead for bool {58	fn abi_read(reader: &mut AbiReader) -> Result<bool> {59		reader.bool()60	}61}6263impl AbiType for uint8 {64	const SIGNATURE: SignatureUnit = make_signature!(new fixed("uint8"));6566	fn is_dynamic() -> bool {67		false68	}69	fn size() -> usize {70		ABI_ALIGNMENT71	}72}73impl AbiRead for uint8 {74	fn abi_read(reader: &mut AbiReader) -> Result<uint8> {75		reader.uint8()76	}77}7879impl AbiType for bytes {80	const SIGNATURE: SignatureUnit = make_signature!(new fixed("bytes"));8182	fn is_dynamic() -> bool {83		true84	}85	fn size() -> usize {86		ABI_ALIGNMENT87	}88}89impl AbiRead for bytes {90	fn abi_read(reader: &mut AbiReader) -> Result<bytes> {91		Ok(bytes(reader.bytes()?))92	}93}9495impl<R: AbiRead + sealed::CanBePlacedInVec> AbiRead for Vec<R> {96	fn abi_read(reader: &mut AbiReader) -> Result<Vec<R>> {97		let mut sub = reader.subresult(None)?;98		let size = sub.uint32()? as usize;99		sub.subresult_offset = sub.offset;100		let mut out = Vec::with_capacity(size);101		for _ in 0..size {102			out.push(<R>::abi_read(&mut sub)?);103		}104		Ok(out)105	}106}107108impl<R: AbiType> AbiType for Vec<R> {109	const SIGNATURE: SignatureUnit = make_signature!(new nameof(R::SIGNATURE) fixed("[]"));110111	fn is_dynamic() -> bool {112		true113	}114115	fn size() -> usize {116		ABI_ALIGNMENT117	}118}119120impl sealed::CanBePlacedInVec for EthCrossAccount {}121122impl AbiType for EthCrossAccount {123	const SIGNATURE: SignatureUnit = make_signature!(new fixed("(address,uint256)"));124125	fn is_dynamic() -> bool {126		address::is_dynamic() || uint256::is_dynamic()127	}128129	fn size() -> usize {130		<address as AbiType>::size() + <uint256 as AbiType>::size()131	}132}133134impl AbiRead for EthCrossAccount {135	fn abi_read(reader: &mut AbiReader) -> Result<EthCrossAccount> {136		let size = if !EthCrossAccount::is_dynamic() {137			Some(<EthCrossAccount as AbiType>::size())138		} else {139			None140		};141		let mut subresult = reader.subresult(size)?;142		let eth = <address>::abi_read(&mut subresult)?;143		let sub = <uint256>::abi_read(&mut subresult)?;144145		Ok(EthCrossAccount { eth, sub })146	}147}148149impl AbiWrite for EthCrossAccount {150	fn abi_write(&self, writer: &mut AbiWriter) {151		self.eth.abi_write(writer);152		self.sub.abi_write(writer);153	}154}155156impl sealed::CanBePlacedInVec for Property {}157158impl AbiType for Property {159	const SIGNATURE: SignatureUnit = make_signature!(new fixed("(string,bytes)"));160161	fn is_dynamic() -> bool {162		string::is_dynamic() || bytes::is_dynamic()163	}164165	fn size() -> usize {166		<string as AbiType>::size() + <bytes as AbiType>::size()167	}168}169170impl AbiRead for Property {171	fn abi_read(reader: &mut AbiReader) -> Result<Property> {172		let size = if !Property::is_dynamic() {173			Some(<Property as AbiType>::size())174		} else {175			None176		};177		let mut subresult = reader.subresult(size)?;178		let key = <string>::abi_read(&mut subresult)?;179		let value = <bytes>::abi_read(&mut subresult)?;180181		Ok(Property { key, value })182	}183}184185impl AbiWrite for Property {186	fn abi_write(&self, writer: &mut AbiWriter) {187		(&self.key, &self.value).abi_write(writer);188	}189}190191macro_rules! impl_abi_writeable {192	($ty:ty, $method:ident) => {193		impl AbiWrite for $ty {194			fn abi_write(&self, writer: &mut AbiWriter) {195				writer.$method(&self)196			}197		}198	};199}200201impl_abi_writeable!(u8, uint8);202impl_abi_writeable!(u32, uint32);203impl_abi_writeable!(u128, uint128);204impl_abi_writeable!(U256, uint256);205impl_abi_writeable!(H160, address);206impl_abi_writeable!(bool, bool);207impl_abi_writeable!(&str, string);208209impl AbiWrite for string {210	fn abi_write(&self, writer: &mut AbiWriter) {211		writer.string(self)212	}213}214215impl AbiWrite for bytes {216	fn abi_write(&self, writer: &mut AbiWriter) {217		writer.bytes(self.0.as_slice())218	}219}220221impl<T: AbiWrite + AbiType> AbiWrite for Vec<T> {222	fn abi_write(&self, writer: &mut AbiWriter) {223		let is_dynamic = T::is_dynamic();224		let mut sub = if is_dynamic {225			AbiWriter::new_dynamic(is_dynamic)226		} else {227			AbiWriter::new()228		};229230		// Write items count231		(self.len() as u32).abi_write(&mut sub);232233		for item in self {234			item.abi_write(&mut sub);235		}236		writer.write_subresult(sub);237	}238}239240impl AbiWrite for () {241	fn abi_write(&self, _writer: &mut AbiWriter) {}242}243244/// This particular AbiWrite implementation should be split to another trait,245/// which only implements `to_result`, but due to lack of specialization feature246/// in stable Rust, we can't have blanket impl of this trait `for T where T: AbiWrite`,247/// so here we abusing default trait methods for it248impl<T: AbiWrite> AbiWrite for ResultWithPostInfo<T> {249	fn abi_write(&self, _writer: &mut AbiWriter) {250		debug_assert!(false, "shouldn't be called, see comment")251	}252	fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {253		match self {254			Ok(v) => Ok(WithPostDispatchInfo {255				post_info: v.post_info.clone(),256				data: {257					let mut out = AbiWriter::new();258					v.data.abi_write(&mut out);259					out260				},261			}),262			Err(e) => Err(e.clone()),263		}264	}265}266267macro_rules! impl_tuples {268	($($ident:ident)+) => {269		impl<$($ident: AbiType,)+> AbiType for ($($ident,)+)270		where271        $(272            $ident: AbiType,273        )+274		{275            const SIGNATURE: SignatureUnit = make_signature!(276                new fixed("(")277                $(nameof(<$ident>::SIGNATURE) fixed(","))+278                shift_left(1)279                fixed(")")280            );281282			fn is_dynamic() -> bool {283				false284				$(285					|| <$ident>::is_dynamic()286				)*287			}288289			fn size() -> usize {290				0 $(+ <$ident>::size())+291			}292		}293294		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}295296		impl<$($ident),+> AbiRead for ($($ident,)+)297		where298			$($ident: AbiRead,)+299			($($ident,)+): AbiType,300		{301			fn abi_read(reader: &mut AbiReader) -> Result<($($ident,)+)> {302				let size = if !<($($ident,)+)>::is_dynamic() { Some(<($($ident,)+)>::size()) } else { None };303				let mut subresult = reader.subresult(size)?;304				Ok((305					$(<$ident>::abi_read(&mut subresult)?,)+306				))307			}308		}309310		#[allow(non_snake_case)]311		impl<$($ident),+> AbiWrite for ($($ident,)+)312		where313			$($ident: AbiWrite + AbiType,)+314		{315			fn abi_write(&self, writer: &mut AbiWriter) {316				let ($($ident,)+) = self;317				if <Self as AbiType>::is_dynamic() {318					let mut sub = AbiWriter::new();319					$($ident.abi_write(&mut sub);)+320					writer.write_subresult(sub);321				} else {322					$($ident.abi_write(writer);)+323				}324			}325		}326	};327}328329impl_tuples! {A}330impl_tuples! {A B}331impl_tuples! {A B C}332impl_tuples! {A B C D}333impl_tuples! {A B C D E}334impl_tuples! {A B C D E F}335impl_tuples! {A B C D E F G}336impl_tuples! {A B C D E F G H}337impl_tuples! {A B C D E F G H I}338impl_tuples! {A B C D E F G H I J}
after · crates/evm-coder/src/abi/impls.rs
1use crate::{2	execution::{Result, ResultWithPostInfo, WithPostDispatchInfo},3	types::*,4	make_signature,5	custom_signature::SignatureUnit,6};7use super::{traits::*, ABI_ALIGNMENT, AbiReader, AbiWriter};8use primitive_types::{U256, H160};910#[cfg(not(feature = "std"))]11use alloc::vec::Vec;1213macro_rules! impl_abi_readable {14	($ty:ty, $method:ident, $dynamic:literal) => {15		impl sealed::CanBePlacedInVec for $ty {}1617		impl AbiType for $ty {18			const SIGNATURE: SignatureUnit = make_signature!(new fixed(stringify!($ty)));1920			fn is_dynamic() -> bool {21				$dynamic22			}2324			fn size() -> usize {25				ABI_ALIGNMENT26			}27		}2829		impl AbiRead for $ty {30			fn abi_read(reader: &mut AbiReader) -> Result<$ty> {31				reader.$method()32			}33		}34	};35}3637impl_abi_readable!(uint32, uint32, false);38impl_abi_readable!(uint64, uint64, false);39impl_abi_readable!(uint128, uint128, false);40impl_abi_readable!(uint256, uint256, false);41impl_abi_readable!(bytes4, bytes4, false);42impl_abi_readable!(address, address, false);43impl_abi_readable!(string, string, true);4445impl sealed::CanBePlacedInVec for bool {}4647impl AbiType for bool {48	const SIGNATURE: SignatureUnit = make_signature!(new fixed("bool"));4950	fn is_dynamic() -> bool {51		false52	}53	fn size() -> usize {54		ABI_ALIGNMENT55	}56}57impl AbiRead for bool {58	fn abi_read(reader: &mut AbiReader) -> Result<bool> {59		reader.bool()60	}61}6263impl AbiType for uint8 {64	const SIGNATURE: SignatureUnit = make_signature!(new fixed("uint8"));6566	fn is_dynamic() -> bool {67		false68	}69	fn size() -> usize {70		ABI_ALIGNMENT71	}72}73impl AbiRead for uint8 {74	fn abi_read(reader: &mut AbiReader) -> Result<uint8> {75		reader.uint8()76	}77}7879impl AbiType for bytes {80	const SIGNATURE: SignatureUnit = make_signature!(new fixed("bytes"));8182	fn is_dynamic() -> bool {83		true84	}85	fn size() -> usize {86		ABI_ALIGNMENT87	}88}89impl AbiRead for bytes {90	fn abi_read(reader: &mut AbiReader) -> Result<bytes> {91		Ok(bytes(reader.bytes()?))92	}93}9495impl<R: AbiType + AbiRead + sealed::CanBePlacedInVec> AbiRead for Vec<R> {96	fn abi_read(reader: &mut AbiReader) -> Result<Vec<R>> {97		let mut sub = reader.subresult(None)?;98		let size = sub.uint32()? as usize;99		sub.subresult_offset = sub.offset;100		let mut out = Vec::with_capacity(size);101		for _ in 0..size {102			out.push(<R>::abi_read(&mut sub)?);103			if !<R>::is_dynamic() {104				sub.subresult_offset += <R>::size()105			};106		}107		Ok(out)108	}109}110111impl<R: AbiType> AbiType for Vec<R> {112	const SIGNATURE: SignatureUnit = make_signature!(new nameof(R::SIGNATURE) fixed("[]"));113114	fn is_dynamic() -> bool {115		true116	}117118	fn size() -> usize {119		ABI_ALIGNMENT120	}121}122123impl sealed::CanBePlacedInVec for EthCrossAccount {}124125impl AbiType for EthCrossAccount {126	const SIGNATURE: SignatureUnit = make_signature!(new fixed("(address,uint256)"));127128	fn is_dynamic() -> bool {129		address::is_dynamic() || uint256::is_dynamic()130	}131132	fn size() -> usize {133		<address as AbiType>::size() + <uint256 as AbiType>::size()134	}135}136137impl AbiRead for EthCrossAccount {138	fn abi_read(reader: &mut AbiReader) -> Result<EthCrossAccount> {139		let size = if !EthCrossAccount::is_dynamic() {140			Some(<EthCrossAccount as AbiType>::size())141		} else {142			None143		};144		let mut subresult = reader.subresult(size)?;145		let eth = <address>::abi_read(&mut subresult)?;146		let sub = <uint256>::abi_read(&mut subresult)?;147148		Ok(EthCrossAccount { eth, sub })149	}150}151152impl AbiWrite for EthCrossAccount {153	fn abi_write(&self, writer: &mut AbiWriter) {154		self.eth.abi_write(writer);155		self.sub.abi_write(writer);156	}157}158159impl sealed::CanBePlacedInVec for Property {}160161impl AbiType for Property {162	const SIGNATURE: SignatureUnit = make_signature!(new fixed("(string,bytes)"));163164	fn is_dynamic() -> bool {165		string::is_dynamic() || bytes::is_dynamic()166	}167168	fn size() -> usize {169		<string as AbiType>::size() + <bytes as AbiType>::size()170	}171}172173impl AbiRead for Property {174	fn abi_read(reader: &mut AbiReader) -> Result<Property> {175		let size = if !Property::is_dynamic() {176			Some(<Property as AbiType>::size())177		} else {178			None179		};180		let mut subresult = reader.subresult(size)?;181		let key = <string>::abi_read(&mut subresult)?;182		let value = <bytes>::abi_read(&mut subresult)?;183184		Ok(Property { key, value })185	}186}187188impl AbiWrite for Property {189	fn abi_write(&self, writer: &mut AbiWriter) {190		(&self.key, &self.value).abi_write(writer);191	}192}193194macro_rules! impl_abi_writeable {195	($ty:ty, $method:ident) => {196		impl AbiWrite for $ty {197			fn abi_write(&self, writer: &mut AbiWriter) {198				writer.$method(&self)199			}200		}201	};202}203204impl_abi_writeable!(u8, uint8);205impl_abi_writeable!(u32, uint32);206impl_abi_writeable!(u128, uint128);207impl_abi_writeable!(U256, uint256);208impl_abi_writeable!(H160, address);209impl_abi_writeable!(bool, bool);210impl_abi_writeable!(&str, string);211212impl AbiWrite for string {213	fn abi_write(&self, writer: &mut AbiWriter) {214		writer.string(self)215	}216}217218impl AbiWrite for bytes {219	fn abi_write(&self, writer: &mut AbiWriter) {220		writer.bytes(self.0.as_slice())221	}222}223224impl<T: AbiWrite + AbiType> AbiWrite for Vec<T> {225	fn abi_write(&self, writer: &mut AbiWriter) {226		let is_dynamic = T::is_dynamic();227		let mut sub = if is_dynamic {228			AbiWriter::new_dynamic(is_dynamic)229		} else {230			AbiWriter::new()231		};232233		// Write items count234		(self.len() as u32).abi_write(&mut sub);235236		for item in self {237			item.abi_write(&mut sub);238		}239		writer.write_subresult(sub);240	}241}242243impl AbiWrite for () {244	fn abi_write(&self, _writer: &mut AbiWriter) {}245}246247/// This particular AbiWrite implementation should be split to another trait,248/// which only implements `to_result`, but due to lack of specialization feature249/// in stable Rust, we can't have blanket impl of this trait `for T where T: AbiWrite`,250/// so here we abusing default trait methods for it251impl<T: AbiWrite> AbiWrite for ResultWithPostInfo<T> {252	fn abi_write(&self, _writer: &mut AbiWriter) {253		debug_assert!(false, "shouldn't be called, see comment")254	}255	fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {256		match self {257			Ok(v) => Ok(WithPostDispatchInfo {258				post_info: v.post_info.clone(),259				data: {260					let mut out = AbiWriter::new();261					v.data.abi_write(&mut out);262					out263				},264			}),265			Err(e) => Err(e.clone()),266		}267	}268}269270macro_rules! impl_tuples {271	($($ident:ident)+) => {272		impl<$($ident: AbiType,)+> AbiType for ($($ident,)+)273		where274        $(275            $ident: AbiType,276        )+277		{278            const SIGNATURE: SignatureUnit = make_signature!(279                new fixed("(")280                $(nameof(<$ident>::SIGNATURE) fixed(","))+281                shift_left(1)282                fixed(")")283            );284285			fn is_dynamic() -> bool {286				false287				$(288					|| <$ident>::is_dynamic()289				)*290			}291292			fn size() -> usize {293				0 $(+ <$ident>::size())+294			}295		}296297		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}298299		impl<$($ident),+> AbiRead for ($($ident,)+)300		where301			$($ident: AbiRead + AbiType,)+302			($($ident,)+): AbiType,303		{304			fn abi_read(reader: &mut AbiReader) -> Result<($($ident,)+)> {305				let is_dynamic = <($($ident,)+)>::is_dynamic();306				let size = if !is_dynamic { Some(<($($ident,)+)>::size()) } else { None };307				let mut subresult = reader.subresult(size)?;308				Ok((309					$({310						let value = <$ident>::abi_read(&mut subresult)?;311						if !is_dynamic {subresult.seek(<$ident as AbiType>::size())};312						value313					},)+314				))315			}316		}317318		#[allow(non_snake_case)]319		impl<$($ident),+> AbiWrite for ($($ident,)+)320		where321			$($ident: AbiWrite + AbiType,)+322		{323			fn abi_write(&self, writer: &mut AbiWriter) {324				let ($($ident,)+) = self;325				if <Self as AbiType>::is_dynamic() {326					let mut sub = AbiWriter::new();327					$($ident.abi_write(&mut sub);)+328					writer.write_subresult(sub);329				} else {330					$($ident.abi_write(writer);)+331				}332			}333		}334	};335}336337impl_tuples! {A}338impl_tuples! {A B}339impl_tuples! {A B C}340impl_tuples! {A B C D}341impl_tuples! {A B C D E}342impl_tuples! {A B C D E F}343impl_tuples! {A B C D E F G}344impl_tuples! {A B C D E F G H}345impl_tuples! {A B C D E F G H I}346impl_tuples! {A B C D E F G H I J}
modifiedcrates/evm-coder/src/abi/mod.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/abi/mod.rs
+++ b/crates/evm-coder/src/abi/mod.rs
@@ -75,19 +75,19 @@
 		buf: &[u8],
 		offset: usize,
 		pad_start: usize,
-		pad_size: usize,
+		pad_end: usize,
 		block_start: usize,
-		block_size: usize,
+		block_end: usize,
 	) -> Result<[u8; S]> {
 		if buf.len() - offset < ABI_ALIGNMENT {
 			return Err(Error::Error(ExitError::OutOfOffset));
 		}
 		let mut block = [0; S];
-		let is_pad_zeroed = buf[pad_start..pad_size].iter().all(|&v| v == 0);
+		let is_pad_zeroed = buf[pad_start..pad_end].iter().all(|&v| v == 0);
 		if !is_pad_zeroed {
 			return Err(Error::Error(ExitError::InvalidRange));
 		}
-		block.copy_from_slice(&buf[block_start..block_size]);
+		block.copy_from_slice(&buf[block_start..block_end]);
 		Ok(block)
 	}
 
@@ -190,7 +190,6 @@
 		let subresult_offset = self.subresult_offset;
 		let offset = if let Some(size) = size {
 			self.offset += size;
-			self.subresult_offset += size;
 			0
 		} else {
 			self.uint32()? as usize
@@ -208,6 +207,11 @@
 		})
 	}
 
+	/// Notify about readed data portion.
+	pub fn seek(&mut self, size: usize) {
+		self.subresult_offset += size;
+	}
+
 	/// Is this parser reached end of buffer?
 	pub fn is_finished(&self) -> bool {
 		self.buf.len() == self.offset
modifiedcrates/evm-coder/src/abi/test.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/abi/test.rs
+++ b/crates/evm-coder/src/abi/test.rs
@@ -137,6 +137,27 @@
 }
 
 #[test]
+fn encode_decode_vec_tuple_uint8_uint8() {
+	test_impl::<Vec<(u8, u8)>>(
+		0xdeadbeef,
+		vec![(0x0A, 0x0B), (0x0C, 0x0D), (0x0E, 0x0F)],
+		&hex!(
+			"
+                deadbeef
+                0000000000000000000000000000000000000000000000000000000000000020
+                0000000000000000000000000000000000000000000000000000000000000003
+                000000000000000000000000000000000000000000000000000000000000000a
+                000000000000000000000000000000000000000000000000000000000000000b
+                000000000000000000000000000000000000000000000000000000000000000c
+                000000000000000000000000000000000000000000000000000000000000000d
+                000000000000000000000000000000000000000000000000000000000000000e
+                000000000000000000000000000000000000000000000000000000000000000f
+            "
+		),
+	);
+}
+
+#[test]
 fn encode_decode_vec_tuple_uint256_string() {
 	test_impl::<Vec<(uint256, string)>>(
         0xdeadbeef,
@@ -332,3 +353,175 @@
 		),
 	);
 }
+
+#[test]
+// #[ignore = "reason"]
+fn encode_decode_tuple0_tuple1_uint8_tuple1_string_bytes_tuple1_uint8_bytes() {
+	let int = 0xff;
+	let by = bytes(vec![0x11, 0x22, 0x33]);
+	let string = "some string".to_string();
+
+	test_impl::<((u8,), (String, bytes), (u8, bytes))>(
+		0xdeadbeef,
+		((int,), (string.clone(), by.clone()), (int, by)),
+		&hex!(
+			"
+                deadbeef
+                0000000000000000000000000000000000000000000000000000000000000020
+                00000000000000000000000000000000000000000000000000000000000000ff
+                0000000000000000000000000000000000000000000000000000000000000060
+                0000000000000000000000000000000000000000000000000000000000000120
+                0000000000000000000000000000000000000000000000000000000000000040
+                0000000000000000000000000000000000000000000000000000000000000080
+                000000000000000000000000000000000000000000000000000000000000000b
+                736f6d6520737472696e67000000000000000000000000000000000000000000
+                0000000000000000000000000000000000000000000000000000000000000003
+                1122330000000000000000000000000000000000000000000000000000000000
+                00000000000000000000000000000000000000000000000000000000000000ff
+                0000000000000000000000000000000000000000000000000000000000000040
+                0000000000000000000000000000000000000000000000000000000000000003
+                1122330000000000000000000000000000000000000000000000000000000000
+            "
+		),
+	);
+}
+
+#[test]
+fn encode_decode_tuple0_tuple1_uint8_tuple1_uint8_uint8_tuple1_uint8_uint8() {
+	test_impl::<((u8,), (u8, u8), (u8, u8))>(
+		0xdeadbeef,
+		((43,), (44, 45), (46, 47)),
+		&hex!(
+			"
+                deadbeef
+                000000000000000000000000000000000000000000000000000000000000002b
+                000000000000000000000000000000000000000000000000000000000000002c
+                000000000000000000000000000000000000000000000000000000000000002d
+                000000000000000000000000000000000000000000000000000000000000002e
+                000000000000000000000000000000000000000000000000000000000000002f
+            "
+		),
+	);
+}
+
+#[test]
+fn encode_decode_tuple0_tuple1_uint8_tuple1_uint8() {
+	test_impl::<((u8,), (u8,))>(
+		0xdeadbeef,
+		((43,), (44,)),
+		&hex!(
+			"
+                deadbeef
+                000000000000000000000000000000000000000000000000000000000000002b
+                000000000000000000000000000000000000000000000000000000000000002c
+            "
+		),
+	);
+}
+
+#[test]
+fn encode_decode_tuple0_tuple1_uint8_uint8() {
+	test_impl::<((u8, u8),)>(
+		0xdeadbeef,
+		((43, 44),),
+		&hex!(
+			"
+                deadbeef
+                000000000000000000000000000000000000000000000000000000000000002b
+                000000000000000000000000000000000000000000000000000000000000002c
+            "
+		),
+	);
+}
+
+#[test]
+fn encode_decode_tuple_uint8_uint8() {
+	test_impl::<(u8, u8)>(
+		0xdeadbeef,
+		(43, 44),
+		&hex!(
+			"
+                deadbeef
+                000000000000000000000000000000000000000000000000000000000000002b
+                000000000000000000000000000000000000000000000000000000000000002c
+            "
+		),
+	);
+}
+
+#[test]
+fn encode_decode_tuple0_tuple1_string() {
+	test_impl::<((String,),)>(
+		0xdeadbeef,
+		(("some string".to_string(),),),
+		&hex!(
+			"
+                deadbeef
+                0000000000000000000000000000000000000000000000000000000000000020
+                0000000000000000000000000000000000000000000000000000000000000020
+                0000000000000000000000000000000000000000000000000000000000000020
+                000000000000000000000000000000000000000000000000000000000000000b
+                736f6d6520737472696e67000000000000000000000000000000000000000000
+            "
+		),
+	);
+}
+
+#[test]
+fn encode_decode_tuple0_tuple1_uint8_string() {
+	test_impl::<((u8, String),)>(
+		0xdeadbeef,
+		((0xff, "some string".to_string()),),
+		&hex!(
+			"
+                deadbeef
+                0000000000000000000000000000000000000000000000000000000000000020
+                0000000000000000000000000000000000000000000000000000000000000020
+                00000000000000000000000000000000000000000000000000000000000000ff
+                0000000000000000000000000000000000000000000000000000000000000040
+                000000000000000000000000000000000000000000000000000000000000000b
+                736f6d6520737472696e67000000000000000000000000000000000000000000
+            "
+		),
+	);
+}
+
+#[test]
+fn encode_decode_tuple0_tuple1_string_bytes() {
+	test_impl::<((String, bytes),)>(
+		0xdeadbeef,
+		(("some string".to_string(), bytes(vec![1, 2, 3])),),
+		&hex!(
+			"
+                deadbeef
+                0000000000000000000000000000000000000000000000000000000000000020
+                0000000000000000000000000000000000000000000000000000000000000020
+                0000000000000000000000000000000000000000000000000000000000000040
+                0000000000000000000000000000000000000000000000000000000000000080
+                000000000000000000000000000000000000000000000000000000000000000b
+                736f6d6520737472696e67000000000000000000000000000000000000000000
+                0000000000000000000000000000000000000000000000000000000000000003
+                0102030000000000000000000000000000000000000000000000000000000000
+            "
+		),
+	);
+}
+
+#[test]
+fn encode_decode_tuple0_tuple1_uint8_tuple1_string() {
+	test_impl::<((u8,), (String,))>(
+		0xdeadbeef,
+		((0xff,), ("some string".to_string(),)),
+		&hex!(
+			"
+                deadbeef
+                0000000000000000000000000000000000000000000000000000000000000020
+                00000000000000000000000000000000000000000000000000000000000000ff
+                0000000000000000000000000000000000000000000000000000000000000040
+                0000000000000000000000000000000000000000000000000000000000000020
+                000000000000000000000000000000000000000000000000000000000000000b
+                736f6d6520737472696e67000000000000000000000000000000000000000000
+            "
+		),
+	);
+}
modifiedcrates/evm-coder/tests/abi_derive_generation.rsdiffbeforeafterboth
--- a/crates/evm-coder/tests/abi_derive_generation.rs
+++ b/crates/evm-coder/tests/abi_derive_generation.rs
@@ -1,7 +1,7 @@
 use evm_coder_procedural::AbiCoder;
 use evm_coder::{
 	types::*,
-	abi::{AbiType, AbiRead, AbiWrite},
+	abi::{AbiType, AbiRead, AbiWrite, AbiReader, AbiWriter},
 };
 
 // TODO: move to build_failed tests
@@ -406,66 +406,199 @@
 	);
 }
 
-fn test_impl<TypeStruct, TupleStruct, Tuple>(
-	type_struct_data: TypeStruct,
-	tuple_struct_data: TupleStruct,
+const FUNCTION_IDENTIFIER: u32 = 0xdeadbeef;
+
+fn test_impl<Tuple, TupleStruct, TypeStruct>(
 	tuple_data: Tuple,
+	tuple_struct_data: TupleStruct,
+	type_struct_data: TypeStruct,
 ) where
 	TypeStruct: AbiWrite + AbiRead + std::cmp::PartialEq + std::fmt::Debug,
+	TupleStruct: AbiWrite + AbiRead + std::cmp::PartialEq + std::fmt::Debug,
 	Tuple: AbiWrite + AbiRead + std::cmp::PartialEq + std::fmt::Debug,
 {
-	use evm_coder::abi::{AbiReader, AbiWriter};
-	const FUNCTION_IDENTIFIER: u32 = 0xdeadbeef;
+	let encoded_type_struct = test_abi_write_impl(&type_struct_data);
+	let encoded_tuple_struct = test_abi_write_impl(&tuple_struct_data);
+	let encoded_tuple = test_abi_write_impl(&tuple_data);
 
-	let mut writer = AbiWriter::new_call(FUNCTION_IDENTIFIER);
-	tuple_data.abi_write(&mut writer);
-	let encoded_tuple = writer.finish();
+	similar_asserts::assert_eq!(encoded_tuple, encoded_type_struct);
+	similar_asserts::assert_eq!(encoded_tuple, encoded_tuple_struct);
 
-	let mut writer = AbiWriter::new_call(FUNCTION_IDENTIFIER);
-	type_struct_data.abi_write(&mut writer);
-	let encoded_struct = writer.finish();
+	// dbg!(&encoded_tuple);
+	// dbg!(&encoded_tuple_struct);
+	// dbg!(&encoded_type_struct);
 
-	similar_asserts::assert_eq!(encoded_tuple, encoded_struct);
+	{
+		let (_, mut decoder) = AbiReader::new_call(&encoded_tuple).unwrap();
+		let restored_struct_data = <TypeStruct>::abi_read(&mut decoder).unwrap();
+		assert_eq!(restored_struct_data, type_struct_data);
+	}
+	{
+		let (_, mut decoder) = AbiReader::new_call(&encoded_tuple).unwrap();
+		let restored_struct_data = <TupleStruct>::abi_read(&mut decoder).unwrap();
+		assert_eq!(restored_struct_data, tuple_struct_data);
+	}
 
-	// let (_, mut decoder) = AbiReader::new_call(&encoded_tuple).unwrap();
-	// let restored_struct_data = <TypeStruct>::abi_read(&mut decoder).unwrap();
-	// assert_eq!(restored_struct_data, type_struct_data);
+	{
+		let (_, mut decoder) = AbiReader::new_call(&encoded_type_struct).unwrap();
+		let restored_tuple_data = <Tuple>::abi_read(&mut decoder).unwrap();
+		assert_eq!(restored_tuple_data, tuple_data);
+	}
+	{
+		let (_, mut decoder) = AbiReader::new_call(&encoded_tuple_struct).unwrap();
+		let restored_tuple_data = <Tuple>::abi_read(&mut decoder).unwrap();
+		assert_eq!(restored_tuple_data, tuple_data);
+	}
+}
 
-	// let (_, mut decoder) = AbiReader::new_call(&encoded_struct).unwrap();
-	// let restored_tuple_data = <Tuple>::abi_read(&mut decoder).unwrap();
-	// assert_eq!(restored_tuple_data, tuple_data);
+fn test_abi_write_impl<A>(data: &A) -> Vec<u8>
+where
+	A: AbiWrite + AbiRead + std::cmp::PartialEq + std::fmt::Debug,
+{
+	let mut writer = AbiWriter::new_call(FUNCTION_IDENTIFIER);
+	data.abi_write(&mut writer);
+	let encoded_tuple = writer.finish();
+	encoded_tuple
 }
 
 #[test]
 fn codec_struct_1_simple() {
 	let _a = 0xff;
-	test_impl::<TypeStruct1SimpleParam, TupleStruct1SimpleParam, (uint8,)>(
-		TypeStruct1SimpleParam { _a },
+	test_impl::<(uint8,), TupleStruct1SimpleParam, TypeStruct1SimpleParam>(
+		(_a,),
 		TupleStruct1SimpleParam(_a),
-		(_a,),
+		TypeStruct1SimpleParam { _a },
 	);
 }
 
 #[test]
 fn codec_struct_1_dynamic() {
 	let _a: String = "some string".into();
-	test_impl::<TypeStruct1DynamicParam, TupleStruct1DynamicParam, (String,)>(
-		TypeStruct1DynamicParam { _a: _a.clone() },
+	test_impl::<(String,), TupleStruct1DynamicParam, TypeStruct1DynamicParam>(
+		(_a.clone(),),
 		TupleStruct1DynamicParam(_a.clone()),
-		(_a,),
+		TypeStruct1DynamicParam { _a },
+	);
+}
+
+#[test]
+fn codec_struct_1_derived_simple() {
+	let _a: u8 = 0xff;
+	test_impl::<((u8,),), TupleStruct1DerivedSimpleParam, TypeStruct1DerivedSimpleParam>(
+		((_a,),),
+		TupleStruct1DerivedSimpleParam(TupleStruct1SimpleParam(_a)),
+		TypeStruct1DerivedSimpleParam {
+			_a: TypeStruct1SimpleParam { _a },
+		},
 	);
 }
 
 #[test]
+fn codec_struct_1_derived_dynamic() {
+	let _a: String = "some string".into();
+	test_impl::<((String,),), TupleStruct1DerivedDynamicParam, TypeStruct1DerivedDynamicParam>(
+		((_a.clone(),),),
+		TupleStruct1DerivedDynamicParam(TupleStruct1DynamicParam(_a.clone())),
+		TypeStruct1DerivedDynamicParam {
+			_a: TypeStruct1DynamicParam { _a },
+		},
+	);
+}
+
+#[test]
+fn codec_struct_2_simple() {
+	let _a = 0xff;
+	let _b = 0xbeefbaba;
+	test_impl::<(u8, u32), TupleStruct2SimpleParam, TypeStruct2SimpleParam>(
+		(_a, _b),
+		TupleStruct2SimpleParam(_a, _b),
+		TypeStruct2SimpleParam { _a, _b },
+	);
+}
+
+#[test]
 fn codec_struct_2_dynamic() {
 	let _a: String = "some string".into();
 	let _b: bytes = bytes(vec![0x11, 0x22, 0x33]);
-	test_impl::<TypeStruct2DynamicParam, TupleStruct2DynamicParam, (String, bytes)>(
-		TypeStruct2DynamicParam {
-			_a: _a.clone(),
-			_b: _b.clone(),
-		},
+	test_impl::<(String, bytes), TupleStruct2DynamicParam, TypeStruct2DynamicParam>(
+		(_a.clone(), _b.clone()),
 		TupleStruct2DynamicParam(_a.clone(), _b.clone()),
-		(_a, _b),
+		TypeStruct2DynamicParam { _a, _b },
+	);
+}
+
+#[test]
+fn codec_struct_2_mixed() {
+	let _a: u8 = 0xff;
+	let _b: bytes = bytes(vec![0x11, 0x22, 0x33]);
+	test_impl::<(u8, bytes), TupleStruct2MixedParam, TypeStruct2MixedParam>(
+		(_a.clone(), _b.clone()),
+		TupleStruct2MixedParam(_a.clone(), _b.clone()),
+		TypeStruct2MixedParam { _a, _b },
+	);
+}
+
+#[test]
+fn codec_struct_2_derived_simple() {
+	let _a = 0xff;
+	let _b = 0xbeefbaba;
+	test_impl::<((u8,), (u8, u32)), TupleStruct2DerivedSimpleParam, TypeStruct2DerivedSimpleParam>(
+		((_a,), (_a, _b)),
+		TupleStruct2DerivedSimpleParam(
+			TupleStruct1SimpleParam(_a),
+			TupleStruct2SimpleParam(_a, _b),
+		),
+		TypeStruct2DerivedSimpleParam {
+			_a: TypeStruct1SimpleParam { _a },
+			_b: TypeStruct2SimpleParam { _a, _b },
+		},
+	);
+}
+
+#[test]
+fn codec_struct_2_derived_dynamic() {
+	let _a = "some string".to_string();
+	let _b = bytes(vec![0x11, 0x22, 0x33]);
+	test_impl::<
+		((String,), (String, bytes)),
+		TupleStruct2DerivedDynamicParam,
+		TypeStruct2DerivedDynamicParam,
+	>(
+		((_a.clone(),), (_a.clone(), _b.clone())),
+		TupleStruct2DerivedDynamicParam(
+			TupleStruct1DynamicParam(_a.clone()),
+			TupleStruct2DynamicParam(_a.clone(), _b.clone()),
+		),
+		TypeStruct2DerivedDynamicParam {
+			_a: TypeStruct1DynamicParam { _a: _a.clone() },
+			_b: TypeStruct2DynamicParam { _a, _b },
+		},
+	);
+}
+
+#[test]
+fn codec_struct_3_derived_mixed() {
+	let int = 0xff;
+	let by = bytes(vec![0x11, 0x22, 0x33]);
+	let string = "some string".to_string();
+	test_impl::<
+		((u8,), (String, bytes), (u8, bytes)),
+		TupleStruct3DerivedMixedParam,
+		TypeStruct3DerivedMixedParam,
+	>(
+		((int,), (string.clone(), by.clone()), (int, by.clone())),
+		TupleStruct3DerivedMixedParam(
+			TupleStruct1SimpleParam(int),
+			TupleStruct2DynamicParam(string.clone(), by.clone()),
+			TupleStruct2MixedParam(int, by.clone()),
+		),
+		TypeStruct3DerivedMixedParam {
+			_a: TypeStruct1SimpleParam { _a: int },
+			_b: TypeStruct2DynamicParam {
+				_a: string.clone(),
+				_b: by.clone(),
+			},
+			_c: TypeStruct2MixedParam { _a: int, _b: by },
+		},
 	);
 }