use crate::{ BuildArguments, BuildOutcome, PayloadBuilder, PayloadBuilderAttributes, PayloadBuilderError, PayloadConfig, }; use alloy_eips::eip4895::Withdrawals; use alloy_primitives::{Address, B256, U256}; use reth_payload_builder::PayloadId; use reth_payload_primitives::BuiltPayload; use reth_primitives::SealedBlock; use alloy_eips::eip7685::Requests; use std::{error::Error, fmt}; /// hand rolled Either enum to handle two builder types #[derive(Debug, Clone)] pub enum Either { /// left variant Left(L), /// right variant Right(R), } impl fmt::Display for Either where L: fmt::Display, R: fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Left(l) => write!(f, "Left: {}", l), Self::Right(r) => write!(f, "Right: {}", r), } } } impl Error for Either where L: Error + 'static, R: Error + 'static, { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { Self::Left(l) => Some(l), Self::Right(r) => Some(r), } } } impl PayloadBuilderAttributes for Either where L: PayloadBuilderAttributes, R: PayloadBuilderAttributes, L::Error: Error + 'static, R::Error: Error + 'static, { type RpcPayloadAttributes = Either; type Error = Either; fn try_new( parent: B256, rpc_payload_attributes: Self::RpcPayloadAttributes, version: u8, ) -> Result { match rpc_payload_attributes { Either::Left(attr) => { L::try_new(parent, attr, version).map(Either::Left).map_err(Either::Left) } Either::Right(attr) => { R::try_new(parent, attr, version).map(Either::Right).map_err(Either::Right) } } } fn payload_id(&self) -> PayloadId { match self { Self::Left(l) => l.payload_id(), Self::Right(r) => r.payload_id(), } } fn parent(&self) -> B256 { match self { Self::Left(l) => l.parent(), Self::Right(r) => r.parent(), } } fn timestamp(&self) -> u64 { match self { Self::Left(l) => l.timestamp(), Self::Right(r) => r.timestamp(), } } fn parent_beacon_block_root(&self) -> Option { match self { Self::Left(l) => l.parent_beacon_block_root(), Self::Right(r) => r.parent_beacon_block_root(), } } fn suggested_fee_recipient(&self) -> Address { match self { Self::Left(l) => l.suggested_fee_recipient(), Self::Right(r) => r.suggested_fee_recipient(), } } fn prev_randao(&self) -> B256 { match self { Self::Left(l) => l.prev_randao(), Self::Right(r) => r.prev_randao(), } } fn withdrawals(&self) -> &Withdrawals { match self { Self::Left(l) => l.withdrawals(), Self::Right(r) => r.withdrawals(), } } } /// this structure enables the chaining of multiple `PayloadBuilder` implementations, /// creating a hierarchical fallback system. It's designed to be nestable, allowing /// for complex builder arrangements like `Stack, C>` with different #[derive(Debug)] pub struct PayloadBuilderStack { left: L, right: R, } impl PayloadBuilderStack { /// Creates a new `PayloadBuilderStack` with the given left and right builders. pub const fn new(left: L, right: R) -> Self { Self { left, right } } } impl Clone for PayloadBuilderStack where L: Clone, R: Clone, { fn clone(&self) -> Self { Self::new(self.left.clone(), self.right.clone()) } } impl BuiltPayload for Either where L: BuiltPayload, R: BuiltPayload, { fn block(&self) -> &SealedBlock { match self { Self::Left(l) => l.block(), Self::Right(r) => r.block(), } } fn fees(&self) -> U256 { match self { Self::Left(l) => l.fees(), Self::Right(r) => r.fees(), } } fn requests(&self) -> Option { match self { Self::Left(l) => l.requests(), Self::Right(r) => r.requests(), } } } impl PayloadBuilder for PayloadBuilderStack where L: PayloadBuilder + Unpin + 'static, R: PayloadBuilder + Unpin + 'static, Client: Clone, Pool: Clone, L::Attributes: Unpin + Clone, R::Attributes: Unpin + Clone, L::BuiltPayload: Unpin + Clone, R::BuiltPayload: Unpin + Clone, <>::Attributes as PayloadBuilderAttributes>::Error: 'static, <>::Attributes as PayloadBuilderAttributes>::Error: 'static, { type Attributes = Either; type BuiltPayload = Either; fn try_build( &self, args: BuildArguments, ) -> Result, PayloadBuilderError> { match args.config.attributes { Either::Left(ref left_attr) => { let left_args: BuildArguments = BuildArguments { client: args.client.clone(), pool: args.pool.clone(), cached_reads: args.cached_reads.clone(), config: PayloadConfig { parent_header: args.config.parent_header.clone(), attributes: left_attr.clone(), }, cancel: args.cancel.clone(), best_payload: args.best_payload.clone().and_then(|payload| { if let Either::Left(p) = payload { Some(p) } else { None } }), }; self.left.try_build(left_args).map(|out| out.map_payload(Either::Left)) } Either::Right(ref right_attr) => { let right_args = BuildArguments { client: args.client.clone(), pool: args.pool.clone(), cached_reads: args.cached_reads.clone(), config: PayloadConfig { parent_header: args.config.parent_header.clone(), attributes: right_attr.clone(), }, cancel: args.cancel.clone(), best_payload: args.best_payload.clone().and_then(|payload| { if let Either::Right(p) = payload { Some(p) } else { None } }), }; self.right.try_build(right_args).map(|out| out.map_payload(Either::Right)) } } } fn build_empty_payload( &self, client: &Client, config: PayloadConfig, ) -> Result { match config.attributes { Either::Left(left_attr) => { let left_config = PayloadConfig { parent_header: config.parent_header.clone(), attributes: left_attr, }; self.left.build_empty_payload(client, left_config).map(Either::Left) } Either::Right(right_attr) => { let right_config = PayloadConfig { parent_header: config.parent_header.clone(), attributes: right_attr, }; self.right.build_empty_payload(client, right_config).map(Either::Right) } } } }