Expand description

This crate defines abstractions to create and update payloads (blocks):

This crate comes with the generic PayloadBuilderService responsible for managing payload jobs.

Node integration

In a standard node the PayloadBuilderService sits downstream of the engine API, or rather the component that handles requests from the consensus layer like engine_forkchoiceUpdatedV1.

Payload building is enabled if the forkchoice update request contains payload attributes.

See also the engine API docs If the forkchoice update request is VALID and contains payload attributes the PayloadBuilderService will create a new PayloadJob via the given PayloadJobGenerator and start polling it until the payload is requested by the CL and the payload job is resolved (see PayloadJob::resolve).


A simple example of a PayloadJobGenerator that creates empty blocks:

use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use reth_payload_builder::{BuiltPayload, KeepPayloadJobAlive, PayloadBuilderAttributes, PayloadJob, PayloadJobGenerator};
use reth_payload_builder::error::PayloadBuilderError;
use reth_primitives::{Block, Header, U256};

/// The generator type that creates new jobs that builds empty blocks.
pub struct EmptyBlockPayloadJobGenerator;

impl PayloadJobGenerator for EmptyBlockPayloadJobGenerator {
    type Job = EmptyBlockPayloadJob;

/// This is invoked when the node receives payload attributes from the beacon node via `engine_forkchoiceUpdatedV1`
fn new_payload_job(&self, attr: PayloadBuilderAttributes) -> Result<Self::Job, PayloadBuilderError> {
        Ok(EmptyBlockPayloadJob{ attributes: attr,})


/// A [PayloadJob] that builds empty blocks.
pub struct EmptyBlockPayloadJob {
  attributes: PayloadBuilderAttributes,

impl PayloadJob for EmptyBlockPayloadJob {
   type ResolvePayloadFuture = futures_util::future::Ready<Result<Arc<BuiltPayload>, PayloadBuilderError>>;

fn best_payload(&self) -> Result<Arc<BuiltPayload>, PayloadBuilderError> {
    // NOTE: some fields are omitted here for brevity
    let payload = Block {
        header: Header {
            parent_hash: self.attributes.parent,
            timestamp: self.attributes.timestamp,
            beneficiary: self.attributes.suggested_fee_recipient,
    let payload = BuiltPayload::new(self.attributes.id, payload.seal_slow(), U256::ZERO);

fn payload_attributes(&self) -> Result<PayloadBuilderAttributes, PayloadBuilderError> {

fn resolve(&mut self) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive) {
       let payload = self.best_payload();
       (futures_util::future::ready(payload), KeepPayloadJobAlive::No)

/// A [PayloadJob] is a a future that's being polled by the `PayloadBuilderService`
impl Future for EmptyBlockPayloadJob {
 type Output = Result<(), PayloadBuilderError>;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {

Feature Flags

  • test-utils: Export utilities for testing


  • Database adapters for payload building.
  • Error types emitted by types or implementations of this crate.
  • A payload builder service task that does nothing.
  • test_utilstest-utils
    Utils for testing purposes.



  • Whether the payload job should be kept alive or terminated after the payload was requested by the CL.