EIP-8282 - Builder Execution Requests

Created 2026-05-22
Status Draft
Category Core
Type Standards Track
Authors
Requires

Abstract

Predeploy two EIP-7685 request contracts for EIP-7732 builders, modelled on the request bus that EIP-7002 (withdrawals) and EIP-7251 (consolidations) use:

Each contract maintains an in-state request queue drained by an end-of-block SYSTEM_ADDRESS system call; the dequeued records become the contract's EIP-7685 request_data, committed in the block requests_hash, and each accepted request is also emitted as an anonymous log. Neither touches the validator deposit contract or the validator request predeploys; for builders created after the fork, they replace EIP-7732's onboarding through the validator deposit flow.

Motivation

EIP-7732 introduces builders as a separate, staked consensus-layer class. A builder is created by a deposit, can have stake added, and must be able to exit. Today EIP-7732 sources this lifecycle from the validator flows: a builder is registered by an ordinary validator deposit request whose withdrawal credential carries the 0x03 BUILDER_WITHDRAWAL_PREFIX, and a builder exits through a builder branch of the consensus-layer voluntary-exit operation. This EIP instead gives builders their own dedicated EIP-7685 request contracts.

Dedicated request types remove cross-actor coupling. Routing builders through the validator contracts forces the consensus layer to decide, on every request, whether it acts on the validator set or the builder set (today by inspecting the credential prefix). Dedicated builder request types make the actor explicit from the request type alone, so the validator and builder registries are keyed independently. A single public key can then be registered as both a validator and a builder. Under EIP-7732 the two cannot coexist for one key — a builder deposit is routed to the builder registry only when the key is not already a validator or pending validator, so deposit routing, not an explicit prohibition, keeps each key in at most one registry. Keying by request type removes that coupling: the registries become independent, and a key may appear in both with distinct indices and lifecycles (the only practical consequence is implementation-side; see Security Considerations).

The deposit bounds a consensus-side denial-of-service surface. A builder deposit's proof-of-possession is verified inline by the consensus layer when the deposit is processed — unlike a validator deposit, which is deferred to the churn-limited pending_deposits queue. Carried on the validator deposit request, builder deposits inherit its high per-payload ceiling, so an attacker submitting invalid-signature builder deposits at the 1-ETH builder minimum could force a full payload's worth of proof-of-possession checks. The coupled deposit request scheme also requires verifying all matching pending validator deposit signatures at builder deposit verification time. A dedicated request bus separates builder deposits from validator deposits — isolating the builder-side verification work — and caps them at MAX_DEPOSIT_REQUESTS_PER_BLOCK per block. The cap and separation are what bound the builder-side verification the consensus layer performs per block.

Exit gains a cold-key path builders lack today. EIP-7732 lets a builder exit only via a voluntary exit signed by its BLS key — the same hot key it uses to sign bids. The exit contract instead authorizes a full exit by the builder's execution_address (the address that owns its stake), exactly as EIP-7002 lets a validator's withdrawal credential trigger an exit. Routing builder exits through this request makes the consensus-layer voluntary-exit operation validator-only again.

Builders that must exist at the fork are unaffected: EIP-7732's fork-transition onboarding of builder-credentialed pending deposits is retained (see Changes to EIP-7732); only post-fork onboarding moves to the deposit contract. The deployed validator deposit contract is left untouched, and builder stake withdrawals continue to flow through EIP-7732's existing full-balance sweep.

Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.

Constants

The 0x03/0x04 request types MUST be unique across all active EIP-7685 request types, including in-flight proposals (EIP-7804, a Draft, also claims 0x03); final allocation is coordinated in consensus-specs.

Name Value Comment
BUILDER_DEPOSIT_CONTRACT_ADDRESS 0x0000000000000000000000000000000000007732 Predeploy address of the builder deposit contract
BUILDER_EXIT_CONTRACT_ADDRESS 0x0000000000000000000000000000000000007733 Predeploy address of the builder exit contract
BUILDER_DEPOSIT_REQUEST_TYPE 0x03 EIP-7685 request-type byte for builder deposits
BUILDER_EXIT_REQUEST_TYPE 0x04 EIP-7685 request-type byte for builder exits
SYSTEM_ADDRESS 0xfffffffffffffffffffffffffffffffffffffffe Address that invokes the end-of-block system call (as in EIP-7002)
MAX_DEPOSIT_REQUESTS_PER_BLOCK 64 Maximum records the builder deposit contract drains into one block
TARGET_DEPOSIT_REQUESTS_PER_BLOCK 8 Per-block request count above which the fee rises for the deposit contract
MAX_EXIT_REQUESTS_PER_BLOCK 16 Maximum records the builder exit contract drains into one block
TARGET_EXIT_REQUESTS_PER_BLOCK 2 Per-block request count above which the fee rises for the exit contract
MIN_REQUEST_FEE 1 Minimum request fee, in wei
REQUEST_FEE_UPDATE_FRACTION 17 Controls the fee's rate of change
EXCESS_INHIBITOR 2**256-1 Excess value that makes the fee getter revert before the first system call (as in EIP-7002/EIP-7251); set at deployment, cleared by the first system call
BUILDER_MIN_DEPOSIT 1000000000000000000 Minimum credited stake for a deposit, in wei (1 ETH — the EIP-7732 builder minimum)
BUILDER_DEPOSIT_CONTRACT_RUNTIME_CODE see Reference Implementation Runtime bytecode of the builder deposit contract
BUILDER_EXIT_CONTRACT_RUNTIME_CODE see Reference Implementation Runtime bytecode of the builder exit contract

Deployment

Each predeploy is deployed exactly as the EIP-7002 and EIP-7251 request contracts are: by a one-time presigned transaction from a single-use deployer account (the Nick's-method scheme), so that BUILDER_DEPOSIT_CONTRACT_ADDRESS and BUILDER_EXIT_CONTRACT_ADDRESS are the addresses cryptographically derived from those transactions. Each contract's init code sets its excess slot to EXCESS_INHIBITOR, so no request can be enqueued until the inhibitor is cleared (see Request fee). The concrete transactions — and therefore the final addresses — will be fixed once the runtime bytecode is audited and frozen (see Reference Implementation).

The deployment transactions MUST be included before the fork that activates this EIP. If there is no code at either predeploy address once the EIP is active, every block from activation onward MUST be invalid — the same handling EIP-7002 and EIP-7251 specify for their predeploys.

Request queue and system call

Both predeploys follow the EIP-7002 / EIP-7251 request-bus pattern, reusing those contracts' storage layout: the EIP-1559-style excess counter in slot 0, the per-block request count in slot 1, the FIFO queue's head and tail indices in slots 2 and 3, and the queued records from slot 4 onward. There is no ABI: like EIP-7002/EIP-7251, each contract dispatches on the caller and on calldatasize alone.

The execution layer prepends the contract's request-type byte and includes request_type ++ request_data in the block requests list, committed via the requests_hash (EIP-7685). The logs are informational only — the canonical flow of a request into the chain is the requests_hash.

The end-of-block system call to each predeploy follows the same rules EIP-7002 and EIP-7251 specify, restated here because EIP-7685 does not: the call is made as SYSTEM_ADDRESS with a dedicated gas limit of 30_000_000; the gas it consumes does not count against the block gas limit and no value is transferred; and if any of the predeploys' system calls fails or returns an error, the block MUST be invalid.

Request fee

Each request carries a fee, computed exactly as in EIP-7002:

fee = fake_exponential(MIN_REQUEST_FEE, excess, REQUEST_FEE_UPDATE_FRACTION)

where fake_exponential is the integer approximation of MIN_REQUEST_FEE · e^(excess / REQUEST_FEE_UPDATE_FRACTION) used by EIP-1559. Because excess grows whenever a block contains more than TARGET_REQUESTS_PER_BLOCK requests and decays otherwise, the fee rises super-linearly under sustained demand and returns to MIN_REQUEST_FEE when demand subsides. The fee is charged on top of any staked value (see the request sections below) and is left locked in the contract.

As in EIP-7002/EIP-7251, each contract's excess is initialized to EXCESS_INHIBITOR at deployment, and the fee getter reverts while excess == EXCESS_INHIBITOR. Since a request is only appended after its fee is paid, this blocks every request between deployment and the first end-of-block system call; that call clears the inhibitor (treating the prior excess as 0), and normal fee operation runs from the activation block onward.

Deposit requests

A deposit request is submitted by calling the deposit contract with calldata of exactly 184 bytes:

Bytes Field
0:48 pubkey — 48-byte BLS public key
48:80 withdrawal_credentials — 32-byte commitment (version byte + execution_address)
80:88 amount — big-endian uint64, in gwei (EIP-7002's input convention)
88:184 signature — 96-byte BLS proof-of-possession

A deposit request serves both a builder's first deposit and subsequent top-ups. The contract MUST reject the request unless both of the following hold:

  1. amount * 1 gwei >= BUILDER_MIN_DEPOSIT.
  2. msg.value >= fee, where fee is the current request fee, and msg.value - fee >= amount * 1 gwei — the value beyond the fee fully funds the stake. Any value beyond amount * 1 gwei + fee is retained by the contract and not credited to the builder.

On success it MUST append the 184 input bytes to its queue and emit them as an anonymous log (which therefore carries the amount big-endian, as submitted). The dequeued BUILDER_DEPOSIT_REQUEST_TYPE record is pubkey (48) ++ withdrawal_credentials (32) ++ amount (8, little-endian) ++ signature (96): the input verbatim, with the amount converted to its little-endian SSZ encoding, as EIP-7002 returns its amount. The signature is carried in the record and verified by the consensus layer, which checks the proof-of-possession only on the pubkey's first appearance and treats a later deposit to an existing builder as a stake top-up (see Consensus-layer processing of records).

Exit requests

An exit request is submitted by calling the exit contract with calldata of exactly 48 bytes: the pubkey of the builder to exit. The contract MUST require msg.value >= fee (the same request fee as the deposit contract); it stakes no value and moves no ETH on the execution layer. On success it MUST append a BUILDER_EXIT_REQUEST_TYPE record of source_address (20) ++ pubkey (48) to its queue, where source_address is msg.sender, and emit the record as an anonymous log.

Authorization is by source_address, as in EIP-7002: the caller proves control of the builder by transacting from the builder's execution_address. The contract records msg.sender verbatim and performs no further check; the consensus layer honours the request only when source_address equals the target builder's execution_address (see Consensus-layer processing of records).

Consensus layer request objects

The consensus layer decodes each dequeued record into one of two SSZ containers, selected by request type:

class BuilderDepositRequest(object):
    pubkey: Bytes48
    withdrawal_credentials: Bytes32
    amount: uint64  # Gwei
    signature: Bytes96

class BuilderExitRequest(object):
    source_address: Bytes20
    pubkey: Bytes48

A type's request_data is the concatenation of the fixed-size SSZ serializations of its records — 184 bytes per BuilderDepositRequest (pubkey ++ withdrawal_credentials ++ amount ++ signature) and 68 bytes per BuilderExitRequest (source_address ++ pubkey), with amount little-endian — exactly the bytes the system call returns, in the same order. BuilderDepositRequest is the validator EIP-6110 DepositRequest without the index field; the consensus layer verifies its signature (the proof-of-possession) on the builder's first registration.

Consensus-layer processing of records

Both request types are applied immediately when processed — a BuilderDepositRequest is not routed through the validator pending_deposits queue, so a builder's balance is credited without an activation-churn queue, preserving EIP-7732's existing behavior. (A newly registered builder still becomes active for bidding and exit only once its deposit epoch is finalized, per gloas is_active_builder; only the churn queue is skipped, not finality.)

Changes to EIP-7732

This EIP modifies EIP-7732's builder lifecycle on the consensus layer:

Rationale

Backwards Compatibility

This EIP is additive at the execution layer: it introduces new contracts at previously empty addresses. It does not modify the validator deposit contract at 0x00000000219ab540356cbb839cbe05303d7705fa, the validator withdrawal/consolidation predeploys, or any existing validator's lifecycle.

At the consensus layer it modifies EIP-7732 (see Changes to EIP-7732): post-fork builder onboarding moves from the validator deposit request to BUILDER_DEPOSIT_REQUEST_TYPE, and builder exits move from the voluntary-exit operation to BUILDER_EXIT_REQUEST_TYPE. The fork-transition onboarding of builder-credentialed pending deposits is unchanged, so builders present at the fork are unaffected. The new request types are additive — blocks that contain no builder requests produce empty request_data for these types, which EIP-7685 excludes from the requests_hash.

Reference Implementation

Security Considerations

Copyright

Copyright and related rights waived via CC0.