From 9b548b5c715961fcc59907c8b51fcbf29855e35d Mon Sep 17 00:00:00 2001 From: steven Date: Fri, 31 Oct 2025 16:25:48 -0600 Subject: [PATCH 1/2] feat: add support for 7594 blobs to anvil --- crates/anvil/core/src/eth/transaction/mod.rs | 140 +++++++++++++++++-- crates/anvil/src/eth/backend/mem/mod.rs | 20 ++- crates/anvil/src/eth/sign.rs | 21 ++- 3 files changed, 163 insertions(+), 18 deletions(-) diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 004da5a025a3e..36f6b9974dd1d 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -12,7 +12,10 @@ use alloy_consensus::{ }, }; -use alloy_eips::eip2718::{Decodable2718, Eip2718Error, Encodable2718}; +use alloy_eips::{ + eip2718::{Decodable2718, Eip2718Error, Encodable2718}, + eip7594::BlobTransactionSidecarVariant, +}; use alloy_network::{AnyReceiptEnvelope, AnyRpcTransaction, AnyTransactionReceipt, AnyTxEnvelope}; use alloy_primitives::{Address, B256, Bloom, Bytes, Signature, TxHash, TxKind, U64, U256}; use alloy_rlp::{Decodable, Encodable, Header}; @@ -574,7 +577,7 @@ pub enum TypedTransaction { /// EIP-1559 transaction EIP1559(Signed), /// EIP-4844 transaction - EIP4844(Signed), + EIP4844(Signed>), /// EIP-7702 transaction EIP7702(Signed), /// op-stack deposit transaction @@ -592,7 +595,23 @@ impl TryFrom for TypedTransaction { TxEnvelope::Legacy(tx) => Ok(Self::Legacy(tx)), TxEnvelope::Eip2930(tx) => Ok(Self::EIP2930(tx)), TxEnvelope::Eip1559(tx) => Ok(Self::EIP1559(tx)), - TxEnvelope::Eip4844(tx) => Ok(Self::EIP4844(tx)), + TxEnvelope::Eip4844(tx) => { + // Convert from TxEip4844Variant to + // TxEip4844Variant + let (variant, sig, hash) = tx.into_parts(); + let blob_variant = match variant { + TxEip4844Variant::TxEip4844(tx) => TxEip4844Variant::TxEip4844(tx), + TxEip4844Variant::TxEip4844WithSidecar(tx_with_sidecar) => { + TxEip4844Variant::TxEip4844WithSidecar( + TxEip4844WithSidecar::from_tx_and_sidecar( + tx_with_sidecar.tx, + BlobTransactionSidecarVariant::Eip4844(tx_with_sidecar.sidecar), + ), + ) + } + }; + Ok(Self::EIP4844(Signed::new_unchecked(blob_variant, sig, hash))) + } TxEnvelope::Eip7702(tx) => Ok(Self::EIP7702(tx)), }, AnyTxEnvelope::Unknown(mut tx) => { @@ -625,7 +644,30 @@ impl TypedTransaction { Self::Legacy(tx) => Ok(TxEnvelope::Legacy(tx)), Self::EIP2930(tx) => Ok(TxEnvelope::Eip2930(tx)), Self::EIP1559(tx) => Ok(TxEnvelope::Eip1559(tx)), - Self::EIP4844(tx) => Ok(TxEnvelope::Eip4844(tx)), + Self::EIP4844(tx) => { + // Convert BlobTransactionSidecarVariant back to standard TxEip4844Variant + let (variant, sig, hash) = tx.into_parts(); + let standard_variant = match variant { + TxEip4844Variant::TxEip4844(tx) => TxEip4844Variant::TxEip4844(tx), + TxEip4844Variant::TxEip4844WithSidecar(tx_with_sidecar) => { + match tx_with_sidecar.sidecar() { + BlobTransactionSidecarVariant::Eip4844(sidecar) => { + TxEip4844Variant::TxEip4844WithSidecar( + TxEip4844WithSidecar::from_tx_and_sidecar( + tx_with_sidecar.tx().clone(), + sidecar.clone(), + ), + ) + } + BlobTransactionSidecarVariant::Eip7594(_) => { + // For now, convert to Eip4844 variant without sidecar + TxEip4844Variant::TxEip4844(tx_with_sidecar.tx().clone()) + } + } + } + }; + Ok(TxEnvelope::Eip4844(Signed::new_unchecked(standard_variant, sig, hash))) + } Self::EIP7702(tx) => Ok(TxEnvelope::Eip7702(tx)), Self::Deposit(_) => Err(self), } @@ -718,7 +760,7 @@ impl TypedTransaction { } } - pub fn sidecar(&self) -> Option<&TxEip4844WithSidecar> { + pub fn sidecar(&self) -> Option<&TxEip4844WithSidecar> { match self { Self::EIP4844(signed_variant) => match signed_variant.tx() { TxEip4844Variant::TxEip4844WithSidecar(with_sidecar) => Some(with_sidecar), @@ -993,7 +1035,30 @@ impl Encodable2718 for TypedTransaction { Self::Legacy(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), Self::EIP2930(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), Self::EIP1559(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), - Self::EIP4844(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), + Self::EIP4844(tx) => { + // Convert BlobTransactionSidecarVariant back to standard TxEip4844Variant + let (variant, sig, hash) = tx.clone().into_parts(); + let standard_variant = match variant { + TxEip4844Variant::TxEip4844(tx) => TxEip4844Variant::TxEip4844(tx), + TxEip4844Variant::TxEip4844WithSidecar(tx_with_sidecar) => { + match tx_with_sidecar.sidecar() { + BlobTransactionSidecarVariant::Eip4844(sidecar) => { + TxEip4844Variant::TxEip4844WithSidecar( + TxEip4844WithSidecar::from_tx_and_sidecar( + tx_with_sidecar.tx().clone(), + sidecar.clone(), + ), + ) + } + BlobTransactionSidecarVariant::Eip7594(_) => { + TxEip4844Variant::TxEip4844(tx_with_sidecar.tx().clone()) + } + } + } + }; + TxEnvelope::from(Signed::new_unchecked(standard_variant, sig, hash)) + .encode_2718_len() + } Self::EIP7702(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), Self::Deposit(tx) => 1 + tx.length(), } @@ -1004,7 +1069,30 @@ impl Encodable2718 for TypedTransaction { Self::Legacy(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), Self::EIP2930(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), Self::EIP1559(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), - Self::EIP4844(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), + Self::EIP4844(tx) => { + // Convert BlobTransactionSidecarVariant back to standard TxEip4844Variant + let (variant, sig, hash) = tx.clone().into_parts(); + let standard_variant = match variant { + TxEip4844Variant::TxEip4844(tx) => TxEip4844Variant::TxEip4844(tx), + TxEip4844Variant::TxEip4844WithSidecar(tx_with_sidecar) => { + match tx_with_sidecar.sidecar() { + BlobTransactionSidecarVariant::Eip4844(sidecar) => { + TxEip4844Variant::TxEip4844WithSidecar( + TxEip4844WithSidecar::from_tx_and_sidecar( + tx_with_sidecar.tx().clone(), + sidecar.clone(), + ), + ) + } + BlobTransactionSidecarVariant::Eip7594(_) => { + TxEip4844Variant::TxEip4844(tx_with_sidecar.tx().clone()) + } + } + } + }; + TxEnvelope::from(Signed::new_unchecked(standard_variant, sig, hash)) + .encode_2718(out) + } Self::EIP7702(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), Self::Deposit(tx) => { tx.encode_2718(out); @@ -1021,7 +1109,24 @@ impl Decodable2718 for TypedTransaction { match TxEnvelope::typed_decode(ty, buf)? { TxEnvelope::Eip2930(tx) => Ok(Self::EIP2930(tx)), TxEnvelope::Eip1559(tx) => Ok(Self::EIP1559(tx)), - TxEnvelope::Eip4844(tx) => Ok(Self::EIP4844(tx)), + TxEnvelope::Eip4844(tx) => { + // Convert from standard TxEip4844Variant to BlobTransactionSidecarVariant + let (variant, sig, hash) = tx.into_parts(); + let blob_variant = match variant { + TxEip4844Variant::TxEip4844(tx) => TxEip4844Variant::TxEip4844(tx), + TxEip4844Variant::TxEip4844WithSidecar(tx_with_sidecar) => { + TxEip4844Variant::TxEip4844WithSidecar( + TxEip4844WithSidecar::from_tx_and_sidecar( + tx_with_sidecar.tx().clone(), + BlobTransactionSidecarVariant::Eip4844( + tx_with_sidecar.sidecar().clone(), + ), + ), + ) + } + }; + Ok(Self::EIP4844(Signed::new_unchecked(blob_variant, sig, hash))) + } TxEnvelope::Eip7702(tx) => Ok(Self::EIP7702(tx)), _ => Err(Eip2718Error::RlpError(alloy_rlp::Error::Custom("unexpected tx type"))), } @@ -1041,7 +1146,24 @@ impl From for TypedTransaction { TxEnvelope::Legacy(tx) => Self::Legacy(tx), TxEnvelope::Eip2930(tx) => Self::EIP2930(tx), TxEnvelope::Eip1559(tx) => Self::EIP1559(tx), - TxEnvelope::Eip4844(tx) => Self::EIP4844(tx), + TxEnvelope::Eip4844(tx) => { + // Convert from standard TxEip4844Variant to BlobTransactionSidecarVariant + let (variant, sig, hash) = tx.into_parts(); + let blob_variant = match variant { + TxEip4844Variant::TxEip4844(tx) => TxEip4844Variant::TxEip4844(tx), + TxEip4844Variant::TxEip4844WithSidecar(tx_with_sidecar) => { + TxEip4844Variant::TxEip4844WithSidecar( + TxEip4844WithSidecar::from_tx_and_sidecar( + tx_with_sidecar.tx().clone(), + BlobTransactionSidecarVariant::Eip4844( + tx_with_sidecar.sidecar().clone(), + ), + ), + ) + } + }; + Self::EIP4844(Signed::new_unchecked(blob_variant, sig, hash)) + } TxEnvelope::Eip7702(tx) => Self::EIP7702(tx), } } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 2da7c68333a6a..050bbde4b2ed0 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -45,6 +45,7 @@ use alloy_eips::{ Encodable2718, eip1559::BaseFeeParams, eip4844::{BlobTransactionSidecar, kzg_to_versioned_hash}, + eip7594::BlobTransactionSidecarVariant, eip7840::BlobParams, eip7910::SystemContract, }; @@ -3340,7 +3341,7 @@ impl Backend { && let Ok(typed_tx) = TypedTransaction::try_from(tx) && let Some(sidecar) = typed_tx.sidecar() { - return Ok(Some(sidecar.sidecar.blobs.clone())); + return Ok(Some(sidecar.sidecar().blobs().to_vec())); } Ok(None) @@ -3357,7 +3358,7 @@ impl Backend { .iter() .filter_map(|tx| tx.as_ref().sidecar()) .flat_map(|sidecar| { - sidecar.sidecar.blobs.iter().zip(sidecar.sidecar.commitments.iter()) + sidecar.sidecar().blobs().iter().zip(sidecar.sidecar().commitments().iter()) }) .filter(|(_, commitment)| { // Filter blobs by versioned_hashes if provided @@ -3381,9 +3382,14 @@ impl Backend { typed_tx_result.ok()?.sidecar().map(|sidecar| sidecar.sidecar().clone()) }) .fold(BlobTransactionSidecar::default(), |mut acc, sidecar| { - acc.blobs.extend(sidecar.blobs); - acc.commitments.extend(sidecar.commitments); - acc.proofs.extend(sidecar.proofs); + acc.blobs.extend(sidecar.blobs()); + acc.commitments.extend(sidecar.commitments()); + match &sidecar { + BlobTransactionSidecarVariant::Eip4844(eip4844_sidecar) => { + acc.proofs.extend(eip4844_sidecar.proofs.clone()); + } + BlobTransactionSidecarVariant::Eip7594(_) => {} + } acc }); Ok(Some(sidecar)) @@ -3401,10 +3407,10 @@ impl Backend { for versioned_hash in sidecar.sidecar.versioned_hashes() { if versioned_hash == hash && let Some(index) = - sidecar.sidecar.commitments.iter().position(|commitment| { + sidecar.sidecar().commitments().iter().position(|commitment| { kzg_to_versioned_hash(commitment.as_slice()) == *hash }) - && let Some(blob) = sidecar.sidecar.blobs.get(index) + && let Some(blob) = sidecar.sidecar().blobs().get(index) { return Ok(Some(*blob)); } diff --git a/crates/anvil/src/eth/sign.rs b/crates/anvil/src/eth/sign.rs index 57f273953c620..9f0f462c51499 100644 --- a/crates/anvil/src/eth/sign.rs +++ b/crates/anvil/src/eth/sign.rs @@ -1,6 +1,7 @@ use crate::eth::error::BlockchainError; -use alloy_consensus::SignableTransaction; +use alloy_consensus::{SignableTransaction, Signed, TxEip4844Variant, TxEip4844WithSidecar}; use alloy_dyn_abi::TypedData; +use alloy_eips::eip7594::BlobTransactionSidecarVariant; use alloy_network::TxSignerSync; use alloy_primitives::{Address, B256, Signature, map::AddressHashMap}; use alloy_signer::Signer as AlloySigner; @@ -131,7 +132,23 @@ pub fn build_typed_transaction( TypedTransaction::EIP7702(tx.into_signed(signature)) } TypedTransactionRequest::EIP4844(tx) => { - TypedTransaction::EIP4844(tx.into_signed(signature)) + let signed = tx.into_signed(signature); + // Convert from standard TxEip4844Variant to BlobTransactionSidecarVariant + let (variant, sig, hash) = signed.into_parts(); + let blob_variant = match variant { + TxEip4844Variant::TxEip4844(tx) => TxEip4844Variant::TxEip4844(tx), + TxEip4844Variant::TxEip4844WithSidecar(tx_with_sidecar) => { + TxEip4844Variant::TxEip4844WithSidecar( + TxEip4844WithSidecar::from_tx_and_sidecar( + tx_with_sidecar.tx().clone(), + BlobTransactionSidecarVariant::Eip4844( + tx_with_sidecar.sidecar().clone(), + ), + ), + ) + } + }; + TypedTransaction::EIP4844(Signed::new_unchecked(blob_variant, sig, hash)) } TypedTransactionRequest::Deposit(tx) => TypedTransaction::Deposit(tx), }; From 4e3b16fc5061c6112733b53e77c04acc2b591cb1 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 5 Nov 2025 14:05:15 -0600 Subject: [PATCH 2/2] use alloy conversions --- crates/anvil/core/src/eth/transaction/mod.rs | 44 ++------------------ crates/anvil/src/eth/sign.rs | 17 +------- 2 files changed, 5 insertions(+), 56 deletions(-) diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 900f988fc25bd..e1059d7155203 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -612,20 +612,8 @@ impl TryFrom for TypedTransaction { TxEnvelope::Eip2930(tx) => Ok(Self::EIP2930(tx)), TxEnvelope::Eip1559(tx) => Ok(Self::EIP1559(tx)), TxEnvelope::Eip4844(tx) => { - // Convert from TxEip4844Variant to - // TxEip4844Variant let (variant, sig, hash) = tx.into_parts(); - let blob_variant = match variant { - TxEip4844Variant::TxEip4844(tx) => TxEip4844Variant::TxEip4844(tx), - TxEip4844Variant::TxEip4844WithSidecar(tx_with_sidecar) => { - TxEip4844Variant::TxEip4844WithSidecar( - TxEip4844WithSidecar::from_tx_and_sidecar( - tx_with_sidecar.tx, - BlobTransactionSidecarVariant::Eip4844(tx_with_sidecar.sidecar), - ), - ) - } - }; + let blob_variant = variant.into(); Ok(Self::EIP4844(Signed::new_unchecked(blob_variant, sig, hash))) } TxEnvelope::Eip7702(tx) => Ok(Self::EIP7702(tx)), @@ -1126,21 +1114,8 @@ impl Decodable2718 for TypedTransaction { TxEnvelope::Eip2930(tx) => Ok(Self::EIP2930(tx)), TxEnvelope::Eip1559(tx) => Ok(Self::EIP1559(tx)), TxEnvelope::Eip4844(tx) => { - // Convert from standard TxEip4844Variant to BlobTransactionSidecarVariant let (variant, sig, hash) = tx.into_parts(); - let blob_variant = match variant { - TxEip4844Variant::TxEip4844(tx) => TxEip4844Variant::TxEip4844(tx), - TxEip4844Variant::TxEip4844WithSidecar(tx_with_sidecar) => { - TxEip4844Variant::TxEip4844WithSidecar( - TxEip4844WithSidecar::from_tx_and_sidecar( - tx_with_sidecar.tx().clone(), - BlobTransactionSidecarVariant::Eip4844( - tx_with_sidecar.sidecar().clone(), - ), - ), - ) - } - }; + let blob_variant = variant.into(); Ok(Self::EIP4844(Signed::new_unchecked(blob_variant, sig, hash))) } TxEnvelope::Eip7702(tx) => Ok(Self::EIP7702(tx)), @@ -1163,21 +1138,8 @@ impl From for TypedTransaction { TxEnvelope::Eip2930(tx) => Self::EIP2930(tx), TxEnvelope::Eip1559(tx) => Self::EIP1559(tx), TxEnvelope::Eip4844(tx) => { - // Convert from standard TxEip4844Variant to BlobTransactionSidecarVariant let (variant, sig, hash) = tx.into_parts(); - let blob_variant = match variant { - TxEip4844Variant::TxEip4844(tx) => TxEip4844Variant::TxEip4844(tx), - TxEip4844Variant::TxEip4844WithSidecar(tx_with_sidecar) => { - TxEip4844Variant::TxEip4844WithSidecar( - TxEip4844WithSidecar::from_tx_and_sidecar( - tx_with_sidecar.tx().clone(), - BlobTransactionSidecarVariant::Eip4844( - tx_with_sidecar.sidecar().clone(), - ), - ), - ) - } - }; + let blob_variant = variant.into(); Self::EIP4844(Signed::new_unchecked(blob_variant, sig, hash)) } TxEnvelope::Eip7702(tx) => Self::EIP7702(tx), diff --git a/crates/anvil/src/eth/sign.rs b/crates/anvil/src/eth/sign.rs index 9f0f462c51499..760d810067c70 100644 --- a/crates/anvil/src/eth/sign.rs +++ b/crates/anvil/src/eth/sign.rs @@ -1,7 +1,6 @@ use crate::eth::error::BlockchainError; -use alloy_consensus::{SignableTransaction, Signed, TxEip4844Variant, TxEip4844WithSidecar}; +use alloy_consensus::{SignableTransaction, Signed}; use alloy_dyn_abi::TypedData; -use alloy_eips::eip7594::BlobTransactionSidecarVariant; use alloy_network::TxSignerSync; use alloy_primitives::{Address, B256, Signature, map::AddressHashMap}; use alloy_signer::Signer as AlloySigner; @@ -135,19 +134,7 @@ pub fn build_typed_transaction( let signed = tx.into_signed(signature); // Convert from standard TxEip4844Variant to BlobTransactionSidecarVariant let (variant, sig, hash) = signed.into_parts(); - let blob_variant = match variant { - TxEip4844Variant::TxEip4844(tx) => TxEip4844Variant::TxEip4844(tx), - TxEip4844Variant::TxEip4844WithSidecar(tx_with_sidecar) => { - TxEip4844Variant::TxEip4844WithSidecar( - TxEip4844WithSidecar::from_tx_and_sidecar( - tx_with_sidecar.tx().clone(), - BlobTransactionSidecarVariant::Eip4844( - tx_with_sidecar.sidecar().clone(), - ), - ), - ) - } - }; + let blob_variant = variant.into(); TypedTransaction::EIP4844(Signed::new_unchecked(blob_variant, sig, hash)) } TypedTransactionRequest::Deposit(tx) => TypedTransaction::Deposit(tx),