diff --git a/Cargo.toml b/Cargo.toml index 5872fc3dd17..da6e3324792 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,7 +81,7 @@ alloy-chains = { version = "0.1.18", default-features = false } # eips alloy-eip2930 = { version = "0.1.0", default-features = false } -alloy-eip7702 = { version = "0.1.0", default-features = false } +alloy-eip7702 = { version = "0.1", default-features = false } # ethereum ethereum_ssz_derive = "0.8" diff --git a/crates/consensus/Cargo.toml b/crates/consensus/Cargo.toml index 0cc3655fa2a..03d4c01f163 100644 --- a/crates/consensus/Cargo.toml +++ b/crates/consensus/Cargo.toml @@ -67,4 +67,4 @@ serde = [ "dep:alloy-serde", "alloy-eips/serde", ] -serde-bincode-compat = ["serde_with"] +serde-bincode-compat = ["alloy-eips/serde-bincode-compat", "serde_with"] diff --git a/crates/consensus/src/transaction/eip1559.rs b/crates/consensus/src/transaction/eip1559.rs index 52dc7031c51..31975f60378 100644 --- a/crates/consensus/src/transaction/eip1559.rs +++ b/crates/consensus/src/transaction/eip1559.rs @@ -450,7 +450,7 @@ mod tests { } } -/// serde-bincode-compatible [`TxEip1559`] serde implementation. +/// Bincode-compatible [`TxEip1559`] serde implementation. #[cfg(all(feature = "serde", feature = "serde-bincode-compat"))] pub(super) mod serde_bincode_compat { use alloc::borrow::Cow; diff --git a/crates/consensus/src/transaction/eip7702.rs b/crates/consensus/src/transaction/eip7702.rs index d1b4278ea41..44d071a3e3e 100644 --- a/crates/consensus/src/transaction/eip7702.rs +++ b/crates/consensus/src/transaction/eip7702.rs @@ -478,3 +478,125 @@ mod tests { assert_eq!(decoded, tx.into_signed(sig)); } } + +/// Bincode-compatible [`TxEip7702`] serde implementation. +#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))] +pub(super) mod serde_bincode_compat { + use alloc::borrow::Cow; + use alloy_eips::{eip2930::AccessList, eip7702::serde_bincode_compat::SignedAuthorization}; + use alloy_primitives::{Address, Bytes, ChainId, U256}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use serde_with::{DeserializeAs, SerializeAs}; + + /// Bincode-compatible [`super::TxEip7702`] serde implementation. + /// + /// Intended to use with the [`serde_with::serde_as`] macro in the following way: + /// ```rust + /// use alloy_consensus::{serde_bincode_compat, TxEip7702}; + /// use serde::{Deserialize, Serialize}; + /// use serde_with::serde_as; + /// + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Data { + /// #[serde_as(as = "serde_bincode_compat::transaction::TxEip7702")] + /// transaction: TxEip7702, + /// } + /// ``` + #[derive(Debug, Serialize, Deserialize)] + pub struct TxEip7702<'a> { + chain_id: ChainId, + nonce: u64, + gas_limit: u64, + max_fee_per_gas: u128, + max_priority_fee_per_gas: u128, + to: Address, + value: U256, + access_list: Cow<'a, AccessList>, + authorization_list: Vec<SignedAuthorization<'a>>, + input: Cow<'a, Bytes>, + } + + impl<'a> From<&'a super::TxEip7702> for TxEip7702<'a> { + fn from(value: &'a super::TxEip7702) -> Self { + Self { + chain_id: value.chain_id, + nonce: value.nonce, + gas_limit: value.gas_limit, + max_fee_per_gas: value.max_fee_per_gas, + max_priority_fee_per_gas: value.max_priority_fee_per_gas, + to: value.to, + value: value.value, + access_list: Cow::Borrowed(&value.access_list), + authorization_list: value.authorization_list.iter().map(Into::into).collect(), + input: Cow::Borrowed(&value.input), + } + } + } + + impl<'a> From<TxEip7702<'a>> for super::TxEip7702 { + fn from(value: TxEip7702<'a>) -> Self { + Self { + chain_id: value.chain_id, + nonce: value.nonce, + gas_limit: value.gas_limit, + max_fee_per_gas: value.max_fee_per_gas, + max_priority_fee_per_gas: value.max_priority_fee_per_gas, + to: value.to, + value: value.value, + access_list: value.access_list.into_owned(), + authorization_list: value.authorization_list.into_iter().map(Into::into).collect(), + input: value.input.into_owned(), + } + } + } + + impl<'a> SerializeAs<super::TxEip7702> for TxEip7702<'a> { + fn serialize_as<S>(source: &super::TxEip7702, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + TxEip7702::from(source).serialize(serializer) + } + } + + impl<'de> DeserializeAs<'de, super::TxEip7702> for TxEip7702<'de> { + fn deserialize_as<D>(deserializer: D) -> Result<super::TxEip7702, D::Error> + where + D: Deserializer<'de>, + { + TxEip7702::deserialize(deserializer).map(Into::into) + } + } + + #[cfg(test)] + mod tests { + use arbitrary::Arbitrary; + use rand::Rng; + use serde::{Deserialize, Serialize}; + use serde_with::serde_as; + + use super::super::{serde_bincode_compat, TxEip7702}; + + #[test] + fn test_tx_eip7702_bincode_roundtrip() { + #[serde_as] + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + struct Data { + #[serde_as(as = "serde_bincode_compat::TxEip7702")] + transaction: TxEip7702, + } + + let mut bytes = [0u8; 1024]; + rand::thread_rng().fill(bytes.as_mut_slice()); + let data = Data { + transaction: TxEip7702::arbitrary(&mut arbitrary::Unstructured::new(&bytes)) + .unwrap(), + }; + + let encoded = bincode::serialize(&data).unwrap(); + let decoded: Data = bincode::deserialize(&encoded).unwrap(); + assert_eq!(decoded, data); + } + } +} diff --git a/crates/consensus/src/transaction/mod.rs b/crates/consensus/src/transaction/mod.rs index 28048620d3d..a90a007740d 100644 --- a/crates/consensus/src/transaction/mod.rs +++ b/crates/consensus/src/transaction/mod.rs @@ -40,7 +40,7 @@ pub use typed::TypedTransaction; pub mod serde_bincode_compat { pub use super::{ eip1559::serde_bincode_compat::*, eip2930::serde_bincode_compat::*, - legacy::serde_bincode_compat::*, + eip7702::serde_bincode_compat::*, legacy::serde_bincode_compat::*, }; } diff --git a/crates/eips/Cargo.toml b/crates/eips/Cargo.toml index 7e352aeb548..121fbe82e5b 100644 --- a/crates/eips/Cargo.toml +++ b/crates/eips/Cargo.toml @@ -63,6 +63,7 @@ std = ["alloy-primitives/std", "alloy-rlp/std", "derive_more?/std", "serde?/std", "c-kzg?/std", "once_cell?/std"] serde = ["dep:alloy-serde", "dep:serde", "alloy-primitives/serde", "c-kzg?/serde", "alloy-eip2930/serde", "alloy-eip7702/serde"] +serde-bincode-compat = ["alloy-eip7702/serde-bincode-compat"] kzg = ["kzg-sidecar", "sha2", "dep:derive_more", "dep:c-kzg", "dep:once_cell"] kzg-sidecar = ["sha2"] k256 = ["alloy-eip7702/k256"]