Introduces a per-block hard cap on the number of EIP-6110 deposit requests an Execution Layer block may produce. A block is invalid if the number of deposit requests derived from its receipts exceeds MAX_DEPOSIT_REQUESTS_PER_BLOCK = 8192. Execution Layer clients MUST enforce the cap during both block construction and block validation.
EIP-6110 places no Execution Layer limit on the number of deposit requests per block; the only existing bound is the Consensus Layer SSZ list cap MAX_DEPOSIT_REQUESTS_PER_PAYLOAD = 8192, expressed in execution_requests.deposits of the beacon block body.
This is a latent liveness failure as gas limits rise. Empirically, at a 193–200M gas limit a block can be filled with more than 8192 deposit requests using a batching contract (~410 deposits per 10M-gas transaction; ~8200 deposits at ~200M gas). When this happens:
Failed to convert json to execution requests: DecodeError("Failed to decode DepositRequest from EL: BytesInvalid(\"VariableList of 12300 items exceeds maximum of 8192\")"). The validator receives 400 Bad Request from the beacon node when requesting a block, and proposal fails. The same SSZ bound applies to every Consensus Layer client, so the failure is not client-specific.Relying on block builders to under-fill blocks is insufficient: an adversary willing to spend the gas can submit a flood of deposit-bearing transactions through the public mempool, and the same actor can trigger a builder circuit-breaker simultaneously, forcing fallback to local block production where no awareness of the cap exists today.
The cap therefore must be a consensus rule enforced at the Execution Layer — the only layer that observes deposits before they are serialized into the engine API response.
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.
| Name | Value | Comment |
|---|---|---|
MAX_DEPOSIT_REQUESTS_PER_BLOCK |
8192 |
Maximum number of deposit requests permitted in a single Execution Layer block |
FORK_BLOCK — the first block in a blockchain after this EIP has been activated.DepositEvent log entries emitted by DEPOSIT_CONTRACT_ADDRESS and matching DEPOSIT_EVENT_SIGNATURE_HASH across all receipts in the block, equivalently len(get_deposit_request_data(block.receipts)) // 192 using the encoding defined in EIP-6110.Beginning with FORK_BLOCK, in addition to the rules of EIP-6110, a block is invalid if its deposit-request count exceeds MAX_DEPOSIT_REQUESTS_PER_BLOCK:
assert deposit_request_count(block) <= MAX_DEPOSIT_REQUESTS_PER_BLOCK
The check MUST be applied during Execution Layer block validation. A block whose deposit-request count exceeds the cap MUST be rejected with the same severity as any other consensus-rule violation; descendants building on it are also invalid.
Execution Layer clients building payloads (whether for engine_getPayload, locally-driven mining/validation, or any other path) MUST track the running deposit-request count of the block being assembled and MUST NOT include any transaction whose execution would cause that count to exceed MAX_DEPOSIT_REQUESTS_PER_BLOCK. A transaction that would push the count past the cap MUST be omitted from the block. Subsequent transactions with no impact on the deposit-request count MAY still be included subject to ordinary block-construction rules. Excluded deposit-bearing transactions remain eligible for inclusion in subsequent blocks.
This requirement applies to all block-construction code paths, including those used as fallbacks when external block builders are unavailable. Clients MUST NOT delegate enforcement of this cap to builders.
Engine API responses (engine_getPayloadV*, engine_newPayloadV*) inherit the validity rule above: an Execution Layer client MUST NOT return a payload that violates the cap from engine_getPayload, and MUST report the payload as INVALID from engine_newPayload if it does.
No Consensus Layer changes are required. The SSZ bound MAX_DEPOSIT_REQUESTS_PER_PAYLOAD = 8192 continues to apply; the EL cap introduced here matches it exactly so that the Execution Layer never produces a deposit list the Consensus Layer cannot decode.
Raising MAX_DEPOSIT_REQUESTS_PER_PAYLOAD on the Consensus Layer was considered as an alternative. It is not preferred because:
81928192 = 2^13 matches the existing Consensus Layer SSZ list bound MAX_DEPOSIT_REQUESTS_PER_PAYLOAD exactly. Setting the EL cap equal to the CL bound:
8192 deposit requests is always SSZ-decodable, while 8193 or more is the first value that breaks Consensus Layer decoding;8192 deposits as the optimistic-sync upper bound, so no security re-analysis is required.8192 is well above any practically reachable deposit volume per block under current mainnet gas limits (a 200M-gas block fits ~8200 deposits using batching, and 30M-gas blocks fit roughly two orders of magnitude fewer). A lower cap (e.g., 8191) was considered but rejected: it would leave a one-element gap below the SSZ bound that serves no functional purpose and would introduce a second constant developers must keep in sync.
A cap denominated in deposit count is independent of future deposit-contract gas-cost changes, is simpler to reason about across forks, and matches the unit in which the Consensus Layer's processing cost scales (one signature verification per deposit).
Liveness motivates this. If only block validation enforced the cap, an attacker could submit enough deposit-bearing transactions to push the count above 8192, and any honest block builder unaware of the cap would produce an invalid payload, causing the proposer to miss its slot. The cap must be enforced at the construction step so that every honest proposer can produce a valid block under any mempool state.
This EIP introduces a new block-validity rule and is therefore a hard fork. Activation requires coordinated client upgrades.
At present-day gas limits the cap is not reachable: a 30M-gas block holds roughly 1,271 deposits per EIP-6110's analysis, and even 200M-gas blocks reach the cap only with adversarially-constructed batching transactions. The cap therefore changes no historical block and only becomes binding under attack or at significantly higher gas limits than mainnet currently uses.
Application-level consumers of deposit data are unaffected: the cap is below or equal the existing SSZ list bound they already expect.
The following describe expected validity outcomes once the EIP is active:
8192 deposit requests is valid (subject to all other rules).8193 deposit requests is invalid (the first count at which Consensus Layer SSZ decoding fails).12300 deposit requests (a value observed in practice during the failure described in Motivation) is invalid.8192 deposit requests.0 deposit requests is valid.[0, 8192] is valid (subject to all other rules).A reference adversarial scenario for cases 2–4 is the spamoor deposit-contract-batch configuration, which at ~193M gas reliably produces > 8192 deposits per block via a batching contract.
MAX_DEPOSIT_REQUESTS_PER_BLOCK = 8192
# Validation: applied alongside other EIP-6110 deposit-request checks.
def validate_deposit_requests_count(block) -> None:
deposit_requests = get_deposit_request_data(block.receipts) # from EIP-6110
count = len(deposit_requests) // 192 # each entry is 48+32+8+96+8 = 192 bytes
assert count <= MAX_DEPOSIT_REQUESTS_PER_BLOCK, "too many deposit requests"
# Construction: invoked when deciding whether to include a candidate transaction.
def can_include_transaction(running_count: int, tx_deposit_request_count: int) -> bool:
return running_count + tx_deposit_request_count <= MAX_DEPOSIT_REQUESTS_PER_BLOCK
tx_deposit_request_count is the number of DepositEvent logs the transaction would emit upon execution against the current block state; clients already track receipts during block assembly and can derive this count alongside ordinary receipt construction.
Without this EIP, an attacker can break liveness on networks whose gas limit permits more than 8192 deposits per block by spamming deposit-bearing transactions (observed empirically at ~193M gas). The Consensus Layer cannot decode such payloads (VariableList of N items exceeds maximum of 8192) and proposers using affected blocks miss their slot. Enforcing the cap at the Execution Layer — including during construction — closes this attack and makes liveness independent of future gas-limit increases.
EIP-6110 identifies optimistic sync as the most consequential deposit-related DoS surface: a syncing node may have to apply up to MAX_DEPOSIT_REQUESTS_PER_PAYLOAD deposits per block without validating their derivation. This EIP does not change that worst-case bound (the cap matches the SSZ ceiling already considered in EIP-6110), but turns it into an EL-enforced rule rather than an SSZ-derived one, so the bound is insensitive to future gas-limit changes and is enforced at the layer that produces the deposit list.
Relying on external block builders to under-fill blocks is unsafe: an attacker can simultaneously spam deposit transactions and trigger circuit-breakers that force fallback to local block production. Mandating EL-side enforcement during construction ensures every honest validator can produce a valid block regardless of builder availability.
The cap is far above any historically observed deposit volume; the largest cited daily volume in EIP-6110 was ~12,000 deposits across 24 hours (≈2 per block on average). A single-block cap of 8192 leaves multiple orders of magnitude of headroom for legitimate activity. Excess deposit-bearing transactions are simply included in subsequent blocks; deposits already accepted by the deposit contract are not lost.
Without the cap, block construction is a one-dimensional packing problem: maximize fee revenue subject to sum(gas) ≤ gas_limit. A simple greedy by fee-per-gas is near-optimal.
With the cap added, construction becomes a two-dimensional packing problem: maximize fee revenue subject to both sum(gas) ≤ gas_limit and sum(deposit_request_count) ≤ MAX_DEPOSIT_REQUESTS_PER_BLOCK. This is a 2D knapsack, which is NP-hard in general. Builders are expected to use heuristics (e.g., reserve a deposit-slot budget, drop deposit-bearing txs when the budget is exhausted), which are not optimal and are exploitable.
A concrete griefing scenario: an adversary submits transactions that look attractive on a fee-per-gas basis but each emit many deposits. A naive greedy builder fills its deposit-slot budget early on these txs and is unable to include later, more profitable deposit-emitting transactions in the same block. The cost of the attack is bounded below by the deposit contract's per-deposit cost — at the minimum deposit of 1 ETH, fully saturating the cap costs at least 8192 × 1 ETH ≈ 8192 ETH (on the order of tens of millions of USD at recent prices), which is the deterrent.
This complexity is accepted as a tradeoff for the liveness guarantee. Alternative approaches that would avoid the second constraint — e.g., raising or removing the Consensus Layer SSZ bound, or pricing deposits up so the cap is never reached — were considered and rejected as discussed in Rationale.
A block at the cap contains 8192 deposit requests of 192 bytes each, i.e. ~1.5 MB of deposit-list data. This is within the existing analysis from EIP-6110, which already considered 8192 deposits as the optimistic-sync upper bound.
The cap interacts with the weak-subjectivity-period analysis in EIP-6110 by capping maximum top-ups per epoch at MAX_DEPOSIT_REQUESTS_PER_BLOCK * SLOTS_PER_EPOCH. Because this matches the SSZ-derived bound previously analyzed, the conclusions of that analysis continue to apply unchanged.
Copyright and related rights waived via CC0.