From 9fe97edf565c187dbd8970ddc6fe3e1b9dc32705 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 28 Jun 2025 00:44:24 +0200 Subject: [PATCH 1/2] fix: remove non-existent Decodable2718Ext import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Decodable2718Ext trait doesn't exist in alloy-eips v1.0.15. The decode_2718_exact method is directly available on the Decodable2718 trait itself, so the additional import is not needed. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- crates/rpc-types-engine/src/payload/mod.rs | 64 ++++++++++++++++++++-- crates/rpc-types-engine/src/payload/v4.rs | 20 ++++++- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/crates/rpc-types-engine/src/payload/mod.rs b/crates/rpc-types-engine/src/payload/mod.rs index 672117868..3478ed561 100644 --- a/crates/rpc-types-engine/src/payload/mod.rs +++ b/crates/rpc-types-engine/src/payload/mod.rs @@ -10,7 +10,7 @@ use alloy_eips::{Decodable2718, Encodable2718, Typed2718, eip7685::EMPTY_REQUEST use alloy_primitives::{B256, Sealable}; use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV2, - ExecutionPayloadV3, + ExecutionPayloadV3, PayloadError, }; use error::OpPayloadError; @@ -480,16 +480,41 @@ impl OpExecutionPayload { /// /// See also: [`OpExecutionPayload::try_into_block_with_sidecar`] pub fn try_into_block(self) -> Result, OpPayloadError> { + self.try_into_block_with(|tx| { + T::decode_2718_exact(tx.as_ref()) + .map_err(alloy_rlp::Error::from) + .map_err(PayloadError::from) + }) + } + + #[allow(rustdoc::broken_intra_doc_links)] + /// Converts [`OpExecutionPayload`] to [`Block`] with a custom transaction mapper. + /// + /// Checks that payload doesn't contain: + /// - blob transactions + /// - L1 withdrawals + /// + /// Caution: This does not set fields that are not part of the payload and only part of the + /// [`OpExecutionPayloadSidecar`]: + /// - parent_beacon_block_root + /// + /// See also: [`OpExecutionPayload::try_into_block_with_sidecar_with`] + pub fn try_into_block_with(self, f: F) -> Result, OpPayloadError> + where + T: Typed2718, + F: FnMut(alloy_primitives::Bytes) -> Result, + E: Into, + { if let Some(payload) = self.as_v2() { if !payload.withdrawals.is_empty() { return Err(OpPayloadError::NonEmptyL1Withdrawals); } } let block = match self { - Self::V1(payload) => return Ok(payload.try_into_block()?), - Self::V2(payload) => return Ok(payload.try_into_block()?), - Self::V3(payload) => payload.try_into_block()?, - Self::V4(payload) => payload.try_into_block()?, + Self::V1(payload) => return Ok(payload.try_into_block_with(f)?), + Self::V2(payload) => return Ok(payload.try_into_block_with(f)?), + Self::V3(payload) => payload.try_into_block_with(f)?, + Self::V4(payload) => payload.try_into_block_with(f)?, }; if block.body.has_eip4844_transactions() { return Err(OpPayloadError::BlobTransaction); @@ -511,7 +536,34 @@ impl OpExecutionPayload { self, sidecar: &OpExecutionPayloadSidecar, ) -> Result, OpPayloadError> { - let mut base_payload = self.try_into_block()?; + self.try_into_block_with_sidecar_with(sidecar, |tx| { + T::decode_2718_exact(tx.as_ref()) + .map_err(alloy_rlp::Error::from) + .map_err(PayloadError::from) + }) + } + + /// Tries to create a new unsealed block from the given payload and payload sidecar with a + /// custom transaction mapper. + /// + /// Additional to checks performed in [`OpExecutionPayload::try_into_block_with`], which is called + /// under the hood, also checks that sidecar doesn't contain: + /// - blob versioned hashes + /// - execution layer requests + /// + /// See also docs for + /// [`ExecutionPayload::try_into_block_with_sidecar_with`](alloy_rpc_types_engine::ExecutionPayload::try_into_block_with_sidecar_with). + pub fn try_into_block_with_sidecar_with( + self, + sidecar: &OpExecutionPayloadSidecar, + f: F, + ) -> Result, OpPayloadError> + where + T: Typed2718, + F: FnMut(alloy_primitives::Bytes) -> Result, + E: Into, + { + let mut base_payload = self.try_into_block_with(f)?; if let Some(blobs_hashes) = sidecar.versioned_hashes() { if !blobs_hashes.is_empty() { return Err(OpPayloadError::NonEmptyBlobVersionedHashes); diff --git a/crates/rpc-types-engine/src/payload/v4.rs b/crates/rpc-types-engine/src/payload/v4.rs index fa75db58e..3a32fb4b7 100644 --- a/crates/rpc-types-engine/src/payload/v4.rs +++ b/crates/rpc-types-engine/src/payload/v4.rs @@ -42,7 +42,25 @@ impl OpExecutionPayloadV4 { /// /// See also [`ExecutionPayloadV3::try_into_block`]. pub fn try_into_block(self) -> Result, PayloadError> { - let mut base_block = self.payload_inner.try_into_block()?; + self.try_into_block_with(|tx| { + T::decode_2718_exact(tx.as_ref()) + .map_err(alloy_rlp::Error::from) + .map_err(PayloadError::from) + }) + } + + /// Converts [`OpExecutionPayloadV4`] to [`Block`] with a custom transaction mapper. + /// + /// This performs the same conversion as the underlying V3 payload, but inserts the L2 + /// withdrawals root. + /// + /// See also [`ExecutionPayloadV3::try_into_block_with`]. + pub fn try_into_block_with(self, f: F) -> Result, PayloadError> + where + F: FnMut(Bytes) -> Result, + E: Into, + { + let mut base_block = self.payload_inner.try_into_block_with(f)?; // overwrite l1 withdrawals root with l2 withdrawals root base_block.header.withdrawals_root = Some(self.withdrawals_root); From 10611228152ec0e28345605cd74bf19b5a437237 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 28 Jun 2025 01:02:32 +0200 Subject: [PATCH 2/2] chore: fix line wrapping in documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- crates/rpc-types-engine/src/payload/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/rpc-types-engine/src/payload/mod.rs b/crates/rpc-types-engine/src/payload/mod.rs index 3478ed561..2e69888f8 100644 --- a/crates/rpc-types-engine/src/payload/mod.rs +++ b/crates/rpc-types-engine/src/payload/mod.rs @@ -546,8 +546,8 @@ impl OpExecutionPayload { /// Tries to create a new unsealed block from the given payload and payload sidecar with a /// custom transaction mapper. /// - /// Additional to checks performed in [`OpExecutionPayload::try_into_block_with`], which is called - /// under the hood, also checks that sidecar doesn't contain: + /// Additional to checks performed in [`OpExecutionPayload::try_into_block_with`], which is + /// called under the hood, also checks that sidecar doesn't contain: /// - blob versioned hashes /// - execution layer requests ///