EIP-7002 Execution Layer Triggerable Withdrawals
Final · Consensus & staking · EIP status Final · created 2023-05-09
Lets the owner of a validator's withdrawal credentials (a 0x01 execution layer address) request validator exits and partial withdrawals directly from the execution layer. Requests are queued in a dedicated contract, then included in blocks and passed to the consensus layer.
Why it is needed
A validator uses two keys. One is the active key, which signs every day and performs validation duties. The other is the withdrawal credential, which holds ownership of the deposited ETH. Under the previous rules, however, only the active key could initiate a validator exit. In delegated operation, where the active key is entrusted to another provider, the withdrawal credential owner, who is the actual owner of the funds, cannot decide on an exit by themselves, which creates a trust problem: the active key operator can effectively hold the ETH hostage. Workarounds such as pre-signed exit messages are incomplete, and if the active key is lost there is no way to recover the funds either. This EIP lets a 0x01 credential trigger exits and withdrawals directly, so that the withdrawal credential, whether an EOA or a smart contract, can decide the fate of the deposit without trust.
What changes
Today
- A validator exit can only be initiated by a signature from the active key
- The withdrawal credential owner must trust the active key operator or rely on workarounds such as pre-signed exit messages
- If the active key is lost, there is no practical way to recover the deposit
After EIP-7002
- The 0x01 withdrawal credential address calls a dedicated contract to request exits and partial withdrawals directly
- Staking pools run by smart contracts can use the same feature
- Up to 16 requests per block are included in blocks, and the fee adjusts automatically with demand to prevent abuse
Who is affected
- Stakers: They can exit the validator and recover funds with just the withdrawal address, regardless of the active key operator. There is also a recovery path if a key is lost.
- Staking pool and LST developers: A smart contract holding the validator withdrawal credentials can trigger exits and withdrawals with on-chain logic, making designs with fewer trust assumptions possible.
- Client teams: Execution clients must implement the end-of-block system call and request validation, and consensus clients must implement processing of the requests included in blocks.
- Custodians and delegated-operation providers: If any product relied on the assumption that a withdrawal credential cannot trigger exits, its structure should be re-examined.
In depth
EIP-7685 request type 0x01 and its fields
The withdrawal request is defined as type 0x01 of the EIP-7685 generic request framework. It has three fields: source_address (Bytes20), validator_pubkey (Bytes48) and amount (uint64). The contract returns amount in little-endian, and the encoding also uses little-endian as is. In contrast, the input to the contract call that receives a request takes 56 bytes: the 48-byte public key followed by a big-endian uint64 amount.
Three code paths of the predeploy contract
The contract deployed at WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS (0x00000961Ef480Eb55e80D19ad83579A64c007002) behaves in three ways depending on the input. If the input is exactly 56 bytes, it checks that the fee is sufficient (check_fee), increments the number of requests in the block by 1 (increment_count), and puts msg.sender, the public key and the amount into the queue (insert_withdrawal_request_into_queue). If the input length is 0, it returns the current fee (the result of get_fee). If SYSTEM_ADDRESS calls it, it dequeues this block's share of requests from the queue. The queue consists of head and tail pointers in storage slots 2 (head) and 3 (tail), and an array that starts at slot 4 and uses 3 slots per request.
Rate limiting with an EIP-1559 style fee
With gas alone, an attacker could pay the market fee, fill the exit limit cheaply and obstruct honest validators. To prevent this, a dynamic fee is attached to the request itself. The fee is computed with fake_exponential, which approximates fee = MIN_WITHDRAWAL_REQUEST_FEE * e**(excess / WITHDRAWAL_REQUEST_FEE_UPDATE_FRACTION), where MIN_WITHDRAWAL_REQUEST_FEE is 1 and WITHDRAWAL_REQUEST_FEE_UPDATE_FRACTION is 17. excess grows by the amount over TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK (2) in each block, so the fee rises exponentially, and it falls again when usage drops. The maximum change rate per block is about 1.125x. Before the first system call, the excess slot holds EXCESS_INHIBITOR (2**256-1), which makes get_fee fail. The alternative using beacon chain proofs (method a) was not adopted because of its dependence on EIP-4788 and the complexity across layers.
Message queue and the limit of 16 per block
The number of requests one block can pass to the consensus layer is limited by MAX_WITHDRAWAL_REQUESTS_PER_BLOCK (16). 16 matches the limits of similar beacon chain operations such as VoluntaryExit and Deposit. Because the execution layer gas limit would allow far more calls in a single block, calls over the limit are not failed but queued in the state and carried over to later blocks. The authors judged the cost of maintaining the queue to be lower than that of alternative designs where call failures degrade UX. TARGET is set to 2 so that, even when exit throughput is extremely congested, the beacon state's partial withdrawal queue grows only by a negligible amount.
End-of-block system call rules
Starting at FORK_BLOCK, after processing all transactions, every block calls the predeploy contract from SYSTEM_ADDRESS (0xfffffffffffffffffffffffffffffffffffffffe) with no calldata. This call dequeues requests from the queue (dequeue_withdrawal_requests), updates the excess (update_excess_withdrawal_requests), and resets the per-block count to 0 (reset_withdrawal_requests_count). The call is given a dedicated gas limit of 30_000_000, the gas it consumes is not counted in the block's gas usage, and it is excluded from the block gas limit check. EIP-1559 fee burning does not apply either. If the address has no code or the call fails, the block is invalid. The dequeued requests must be placed in the EIP-7685 request list in exactly the order dequeue returned them.
Why validator_pubkey is used instead of an index
Several validators can share the same execution layer withdrawal credential, so the public key identifies which validator to exit. validator_index could also distinguish them, but an index is only assigned after the validator is created on the beacon chain, while the public key is known in advance, which avoids the problem of some staking pool contracts not knowing the index. Designing the request as an execution layer submission rather than a consensus layer signature check follows the same reasoning. The alternative of verifying a secp256k1 signature through the Engine API is easier to implement, but it would leave smart contracts unable to use this feature. The requests are carried directly in the beacon block body because the consensus layer and execution layer must be able to run independently of each other.
Consensus layer processing
The consensus layer gains a new operation, ExecutionLayerWithdrawalRequest. It appears in ExecutionPayload as an SSZ list whose length is bounded by MAX_WITHDRAWAL_REQUESTS_PER_BLOCK, and process_operations calls a function similar to process_voluntary_exit for each request. However, a validation failure such as a validator that has already exited does not make the block itself fail. This is the same way deposits coming from the execution layer are handled. The full specification is in the electra beacon-chain document in consensus-specs.
Security considerations: overpayment, caller match, failure handling
Fees that are overpaid are not refunded. When requesting from a contract, the recommended pattern is to read the current fee with an empty-input staticcall, send exactly that amount, and revert if it exceeds a fee limit (requestFeeLimit). The fee can change significantly between transaction creation and block inclusion. A request from an EOA cannot avoid overpayment, so the recommendation is to estimate a suitable amount through simulation. The system contract uses CALLER (msg.sender) as the withdrawal target address, so the calling address must match the 0x01 withdrawal credential recorded in the beacon state. If the system call fails the block becomes invalid, and if the cause transaction stays in the mempool and is included again, consecutive slots can end up empty, so block producers are advised to shuffle the transaction composition. If the contract has no code at the fork time, every block becomes invalid, so the recovery strategies presented are deploying the code through a transaction or postponing the fork time.