And the architecture of Ethereum has also changed from a monolithic chain to a modular blockchain with two layers: the consensus layer and the execution layer, which communicate through the engine API.
A modular blockchain with two layers
Previously, the validator could only send funds to the beacon chain via a deposit contract, and these funds would be locked in the consensus layer because there was no mechanism to withdraw the fund.
So the goal of withdrawal is to unlock validators’ funds, allow them to withdraw their funds (including 32 ETH + earned rewards) from CL to EL, and eventually enable validators’ exit function.
Withdraw staked ETH from CL to EL
This article will delve into this modest yet indispensable feature and provide an expanded explanation of its design philosophy, workflow, and potential impact. Please enjoy!
How does withdrawal work in more detail
Design Philosophy
The design goal is to enable the withdrawal function of the consensus layer under the strict premise of ensuring the security and stability of the Ethereum network, and eventually ensure that any validator can exit safely.
Besides, we also want no backward-compatible breaks, minimize complexity and engineering workload, and allow many projects can reuse withdrawal’s research and code, such as rollups and staking pools.
With this goal in mind, there have three critical questions.
When to activate withdrawals?
How to signal?
Where does the withdrawn ETH come from?
When to activate withdrawals?
Withdrawals will always be active, but we will only remove stakes when the economic layer is secure enough. Also, we want to identify the spare stake (i.e., the balance in excess of 32 ETH) and remove it from the protocol.
Based on this goal, we want to make withdrawals that can still incentives honest validators. Therefore, we have two different types of withdrawals, namely partial withdrawals and full withdrawals.
Partial withdrawals: The processing is automatic and immediate. Only the balance that exceeds 32 ETH (earned rewards) is withdrawn, so the validator will not exit the network and will still take on the corresponding responsibilities. This is to ensure that there are always enough validators in the network.
Full withdrawals: Must be done manually. Validator’s entire balance (32 ETH + rewards) will be unlocked and withdrawn, and the validator will completely exit and no longer be a part of the beacon chain. However, the rate of full withdrawals is limited by the exit queue, causing it to be lower than partial withdrawals, this is designed to limit the rate of validator exit and to ensure the security of the network (i.e., a slower exit rate makes the weak subjectivity window larger, making it take longer to get a sufficient quorum for the long-range attack).
All submitted full withdrawals must pass through the exit queue before they become withdrawable, then they can be sent to the EL and exit the network. The rate of exit queue depends on the number of active validators, which is proportional to each other, more specifically, until 327680 active validators, 4 validators can exit to be withdrawable per epoch. After that for every 65536 active validators, the validators’ exit rate goes up by one (same as the validator activation rate).
The lifecycle of full withdrawals
The rate of exit queue
If we assume that no new validators join the network and there only have full withdrawals. At this rate, the current 556983 validators need at least 119399 epochs (i.e. 530 days) to be withdrawable.
The time for all validators to go through the exit queue
How to signal?
Because of the modular design of Ethereum (i.e., CL and EL), we need to send the withdrawal information from the consensus layer to the execution layer. This introduces a new question, how to signal the execution layer?
In general, there are two solutions. One is “pull”-based solution (i.e., EIP-4788), and the other is “push”-based solution (i.e., EIP-4895).
Pull-based: Since the beacon chain uses SSZ structure, it is easy to convert the state root into a Merkle tree, where all the withdrawal is a sub-tree (i.e., withdrawal list), and we can easily construct a Merkle proof to prove that a certain validator withdrawal exists in the state root.
After that, the state root and Merkle proof will be sent to EVM via engine API, and if it passes the execution layer verification (proof is valid and withdrawal has not been consumed), the balance of the withdrawal address will be increased.
Push-based: Push-based withdrawal takes a quite simple approach, it uses a withdrawal queue to replace the withdrawal list, which means that it is no longer needed to send the state root and proof to the execution layer.
The CL only needs to dequeue and send withdrawals to the EL at a certain rate (via the engine API, in the payload), and EL processes the payload as usual, taking out the withdrawal object and adding a certain amount of ETH to the address.
These two solutions have different implementations. For the pull-based solution, we need to introduce a new “stateful” precompiled contract that handles withdrawal-related operations (i.e., can access the beacon state, but does not store the state data), and a new opcode (BEACON_STATE_ROOT) to read the state data from the “stateful” precompiled contract.
For the push-based solution, it reduces some complexity by using a withdrawal queue. However, the EL still needs to be modified accordingly (e.g., execution payload and header).
And they also have different trade-offs. The pull-based solution is simpler for protocol development but adds additional complexity (i.e., new concept of “stateful precompiled contract”, more testing, etc.) and bad UX. Users have to pay the gas for contract interactions and the withdrawals will take up the block space from other transactions.
For the push-based solution, most of the work is defined in the protocol layer rather than on the user end, so it gets better UX. And since there are no precompiled contracts, naturally no additional gas cost and block space take-up. So after all the considerations, we finally choose the “push”-based solution.
Where does the withdrawn ETH come from?
After deciding how to send the withdrawal message to the EL, we also need to design how to credit withdrawn ETH (the Ether that the EL sends to the withdrawal address). In general, two options can be chosen.
Withdrawal contract (for pull-based withdrawals): As the mirror of the deposit contract, a large amount of ETH is stored in the withdrawal contract and sent to the corresponding withdrawal address. The withdrawal transaction will look exactly the same as a normal transaction.
System-level operation: Completely different from user-level transactions, defining a new type of “operation” at the system level. Similar to the coinbase and block reward, it will create new ETH.
Withdrawal contract brings too much potential complexity and risks (i.e., if there is a bug in the contract then the entire protocol is borked). Also if large amounts of ETH are stored in the withdrawal contract (either by transferring from the deposit contract or by creating new ETH), it would look very unneutral.
Meanwhile, the system-level operation is separated from user-level transactions, which is cleaner and simpler, and does not require EVM execution (meaning no failure and no gas cost). So we finally chose the system-level operation.
How does it exactly work?
After we have clarified the goal, the design philosophy, and the critical questions of withdrawal, we can now explain how beacon chain withdrawal actually works. In short, a withdrawal is finished in the following four steps.
Validator check and submit
CL Processing
Engine API
EL Processing
We show the complete process of withdrawals by expanding these four steps one by one.
1. Validator check and submit
The most important thing for the validator is to set an Ethereum address so the funds can be withdrawn from the CL to the corresponding EL recipient address.
Validators can check if they set the Ethereum address by a withdrawal_credentials field (see it here). If there is an EL withdrawal address, the field is prefixed with 0x01, otherwise it defaults to 0x00 (as the example below).
Withdrawal credentials with 0x00 prefix
For partial withdrawals, it will only be initiated after the validator migrates withdrawal credentials from 0x00 to 0x01. Besides that, the validator does not need to do anything, and the reward over 32 ETH will automatically be sent to the withdrawal address.
And for full withdrawals, the validator can exit the network and unassign the duty at any time. But again, the fund (32 ETH + earned reward) will only be sent to the withdrawal address after migration to 0x01.
So validators should first check that if they have migrated the withdrawal credentials prefix from 0x00 to 0x01. If not, we have two ways to perform the migration.
Initial a deposit: If the validator sets the Ethereum address when sending the deposit, the withdrawal credentials prefix defaults to 0x01.
Send a BLS message: The only way to migrate to 0x01 besides initiating a new deposit is to send a BLSToExecutionChange message to the network. See more details here.
However, note that the migration from 0x00 to 0x01 is a one-time process, and the Ethereum address cannot be changed after it is set. Also, full withdrawals cannot be canceled once they are submitted. So please operate it carefully!
2. CL Processing
After the validator stage, we move to CL processing. Full withdrawals and partial withdrawals will have separate processing logic.
Full withdrawals first enter the exit queue and “exit” at a defined rate (currently 8 exit/epoch), then run slashing logic. If validators are not slashed and have a “0x01” prefix, it only takes 256 epochs (~28 h) to become withdrawable. If they are slashed, they need another 36 days of punishment delay to become withdrawable (this is to prevent attackers from escaping and colluding).
Partial withdrawals (i.e., balances exceeding 32 ETH) become withdrawable automatically and immediately after validators have the “0x01” prefix, with no waiting time.
The complete workflow is like this in the diagram below.
Lifecycle of withdrawals
3. Engine API
After that, the client will run the corresponding functions at each slot to scan all withdrawable validators (i.e., look up to 16384 at once) but only put the first 16 withdrawals (full or partial) into the withdrawal queue.
The withdrawal queue is a single channel that processes both partial withdrawals and full withdrawals. It will send the unified withdrawal objects (withdrawal and validator index, withdrawal amount, and receipt address) to the execution layer.
In the end, the withdrawal queue will be added to the execution payload (i.e., Ethereum block) when blocks are constructed.
Sending withdrawals to EL
4. EL Processing
EL processes the execution payload as usual and takes the withdrawal information out (now it has a withdrawal field), adding the corresponding amount to the withdrawal address.
Since validators’ balance increase is a “system-level operation” (similar to block reward and coinbase), it will not be executed by EVM and will not be displayed as a normal transaction.
Upgrade timeline
Before enabling withdrawal to the mainnet, a lot of testing was done, including protocol layer testing (e.g., Zhejiang testnet and Hive test sets) and application layer testing (e.g., MEV-boost). But the most important time points are Sepolia testnet, Goerli testnet, and mainnet.
So we’ll see the mainnet enable beacon chain withdrawal on April 12!
The potential impact of withdrawals
Is there going to have a large-scale exit?
After two and a half years of growth, the total number of active validators has grown from around 20,000 in December 2020 to around 555,000 in 2023.
The number of active validators
And the total number of stakes has grown to 17.77 million ETH, which is 14.8% of the total supply (12 million in the stake pools).
Percentage of staking to supply
Also, PoS Ethereum has issued about 0.356 million new ETH through the validator’s reward (PoW Ethereum is about 5.94 million), decreasing the average annual issuance from 5.62% to 0.59% (reduced by 89.5% compared to PoW Ethereum’s issuance).
Issuance
Most people are concerned about whether a large number of users will withdraw their ETH from the network to the market after enabling the withdrawals. We are worried that this will impact the network’s stability and ETH’s price.
But in fact, users may feel safer after enabling withdrawals and prefer to keep their funds in the network. The data shows that there is no large-scale withdrawal event. Only 1179 validators have submitted voluntary exits so far, and the exited ETH only represents 0.0323% of the total amount of the network, which will not have a significant impact.
Voluntary exit count
For staking pools, they already allow users to exchange staked ETH (such as sETH and rETH) for real ETH at the rate of 1:1 before they officially activate the withdrawal feature. So users of staking pools will not make large-scale withdrawals either.
Even if all validators choose to exit, it would take at least 500 days to be withdrawable (based on the rate of gradual decrease of the exit queue). So we don’t need to worry about a large-scale withdrawal event or network stability breakdown after activating the withdrawal function.
For instance, to guarantee credible neutrality and prevent malicious activity, rollups must ensure users can withdraw their funds from L2 to L1 at any time. So when rollups design a forced withdrawal mechanism, they can reuse engine API and withdrawal queue. Users can easily push their money from the rollup sequencer to the Ethereum execution layer.
Also, if Ethereum client-based rollups have a staking layer (users may need to deposit a certain amount of tokens to become a sequencer/prover), they can reuse the engine API and withdrawal queue to safely withdraw funds from the staking layer to the L2 sequencer too.
Rollups can reuse the Engine API
Conclusions
This comprehensive article delves into the concept of withdrawal, its mechanics, and its broader implications. I hope that it assists the wider community in gaining a deeper understanding of the withdrawal function.
Beacon Chain Withdrawals
Special thanks to Alex Stokes, Danny Ryan, and Vitalik Buterin for their invaluable assistance and insightful feedback during the review process.
What is beacon chain withdrawal
With “the merge”, the consensus of Ethereum is changed from Proof-of-Work to Proof-of-Stake. PoS Ethereum requires you to stake 32 ETH to become a validator, which can be randomly assigned as a proposer (responsible for producing blocks) or an attester (responsible for submitting attestations), then validators will be rewarded according to their workload.
And the architecture of Ethereum has also changed from a monolithic chain to a modular blockchain with two layers: the consensus layer and the execution layer, which communicate through the engine API.
Previously, the validator could only send funds to the beacon chain via a deposit contract, and these funds would be locked in the consensus layer because there was no mechanism to withdraw the fund.
So the goal of withdrawal is to unlock validators’ funds, allow them to withdraw their funds (including 32 ETH + earned rewards) from CL to EL, and eventually enable validators’ exit function.
This article will delve into this modest yet indispensable feature and provide an expanded explanation of its design philosophy, workflow, and potential impact. Please enjoy!
How does withdrawal work in more detail
Design Philosophy
The design goal is to enable the withdrawal function of the consensus layer under the strict premise of ensuring the security and stability of the Ethereum network, and eventually ensure that any validator can exit safely.
Besides, we also want no backward-compatible breaks, minimize complexity and engineering workload, and allow many projects can reuse withdrawal’s research and code, such as rollups and staking pools.
With this goal in mind, there have three critical questions.
When to activate withdrawals?
Withdrawals will always be active, but we will only remove stakes when the economic layer is secure enough. Also, we want to identify the spare stake (i.e., the balance in excess of 32 ETH) and remove it from the protocol.
Based on this goal, we want to make withdrawals that can still incentives honest validators. Therefore, we have two different types of withdrawals, namely partial withdrawals and full withdrawals.
All submitted full withdrawals must pass through the exit queue before they become withdrawable, then they can be sent to the EL and exit the network. The rate of exit queue depends on the number of active validators, which is proportional to each other, more specifically, until 327680 active validators, 4 validators can exit to be withdrawable per epoch. After that for every 65536 active validators, the validators’ exit rate goes up by one (same as the validator activation rate).
If we assume that no new validators join the network and there only have full withdrawals. At this rate, the current 556983 validators need at least 119399 epochs (i.e. 530 days) to be withdrawable.
How to signal?
Because of the modular design of Ethereum (i.e., CL and EL), we need to send the withdrawal information from the consensus layer to the execution layer. This introduces a new question, how to signal the execution layer?
In general, there are two solutions. One is “pull”-based solution (i.e., EIP-4788), and the other is “push”-based solution (i.e., EIP-4895).
These two solutions have different implementations. For the pull-based solution, we need to introduce a new “stateful” precompiled contract that handles withdrawal-related operations (i.e., can access the beacon state, but does not store the state data), and a new opcode (
BEACON_STATE_ROOT
) to read the state data from the “stateful” precompiled contract.For the push-based solution, it reduces some complexity by using a withdrawal queue. However, the EL still needs to be modified accordingly (e.g., execution payload and header).
And they also have different trade-offs. The pull-based solution is simpler for protocol development but adds additional complexity (i.e., new concept of “stateful precompiled contract”, more testing, etc.) and bad UX. Users have to pay the gas for contract interactions and the withdrawals will take up the block space from other transactions.
For the push-based solution, most of the work is defined in the protocol layer rather than on the user end, so it gets better UX. And since there are no precompiled contracts, naturally no additional gas cost and block space take-up. So after all the considerations, we finally choose the “push”-based solution.
Where does the withdrawn ETH come from?
After deciding how to send the withdrawal message to the EL, we also need to design how to credit withdrawn ETH (the Ether that the EL sends to the withdrawal address). In general, two options can be chosen.
Withdrawal contract brings too much potential complexity and risks (i.e., if there is a bug in the contract then the entire protocol is borked). Also if large amounts of ETH are stored in the withdrawal contract (either by transferring from the deposit contract or by creating new ETH), it would look very unneutral.
Meanwhile, the system-level operation is separated from user-level transactions, which is cleaner and simpler, and does not require EVM execution (meaning no failure and no gas cost). So we finally chose the system-level operation.
How does it exactly work?
After we have clarified the goal, the design philosophy, and the critical questions of withdrawal, we can now explain how beacon chain withdrawal actually works. In short, a withdrawal is finished in the following four steps.
We show the complete process of withdrawals by expanding these four steps one by one.
1. Validator check and submit
The most important thing for the validator is to set an Ethereum address so the funds can be withdrawn from the CL to the corresponding EL recipient address.
Validators can check if they set the Ethereum address by a
withdrawal_credentials
field (see it here). If there is an EL withdrawal address, the field is prefixed with0x01
, otherwise it defaults to0x00
(as the example below).For partial withdrawals, it will only be initiated after the validator migrates withdrawal credentials from
0x00
to0x01
. Besides that, the validator does not need to do anything, and the reward over 32 ETH will automatically be sent to the withdrawal address.And for full withdrawals, the validator can exit the network and unassign the duty at any time. But again, the fund (32 ETH + earned reward) will only be sent to the withdrawal address after migration to
0x01
.So validators should first check that if they have migrated the withdrawal credentials prefix from
0x00
to0x01
. If not, we have two ways to perform the migration.0x01
.0x01
besides initiating a new deposit is to send aBLSToExecutionChange
message to the network. See more details here.However, note that the migration from
0x00
to0x01
is a one-time process, and the Ethereum address cannot be changed after it is set. Also, full withdrawals cannot be canceled once they are submitted. So please operate it carefully!2. CL Processing After the validator stage, we move to CL processing. Full withdrawals and partial withdrawals will have separate processing logic.
The complete workflow is like this in the diagram below.
3. Engine API After that, the client will run the corresponding functions at each slot to scan all withdrawable validators (i.e., look up to 16384 at once) but only put the first 16 withdrawals (full or partial) into the withdrawal queue.
The withdrawal queue is a single channel that processes both partial withdrawals and full withdrawals. It will send the unified withdrawal objects (withdrawal and validator index, withdrawal amount, and receipt address) to the execution layer.
In the end, the withdrawal queue will be added to the execution payload (i.e., Ethereum block) when blocks are constructed.
4. EL Processing EL processes the execution payload as usual and takes the withdrawal information out (now it has a
withdrawal
field), adding the corresponding amount to the withdrawal address.Since validators’ balance increase is a “system-level operation” (similar to block reward and coinbase), it will not be executed by EVM and will not be displayed as a normal transaction.
Upgrade timeline
Before enabling withdrawal to the mainnet, a lot of testing was done, including protocol layer testing (e.g., Zhejiang testnet and Hive test sets) and application layer testing (e.g., MEV-boost). But the most important time points are Sepolia testnet, Goerli testnet, and mainnet.
So we’ll see the mainnet enable beacon chain withdrawal on April 12!
The potential impact of withdrawals
Is there going to have a large-scale exit?
After two and a half years of growth, the total number of active validators has grown from around 20,000 in December 2020 to around 555,000 in 2023.
And the total number of stakes has grown to 17.77 million ETH, which is 14.8% of the total supply (12 million in the stake pools).
Also, PoS Ethereum has issued about 0.356 million new ETH through the validator’s reward (PoW Ethereum is about 5.94 million), decreasing the average annual issuance from 5.62% to 0.59% (reduced by 89.5% compared to PoW Ethereum’s issuance).
Most people are concerned about whether a large number of users will withdraw their ETH from the network to the market after enabling the withdrawals. We are worried that this will impact the network’s stability and ETH’s price.
But in fact, users may feel safer after enabling withdrawals and prefer to keep their funds in the network. The data shows that there is no large-scale withdrawal event. Only 1179 validators have submitted voluntary exits so far, and the exited ETH only represents 0.0323% of the total amount of the network, which will not have a significant impact.
For staking pools, they already allow users to exchange staked ETH (such as sETH and rETH) for real ETH at the rate of 1:1 before they officially activate the withdrawal feature. So users of staking pools will not make large-scale withdrawals either.
Even if all validators choose to exit, it would take at least 500 days to be withdrawable (based on the rate of gradual decrease of the exit queue). So we don’t need to worry about a large-scale withdrawal event or network stability breakdown after activating the withdrawal function.
What can we learn from withdrawals?
Besides the consensus-related software upgrades (such as flashbot MEV-boost, Lido upgrade, Rocket Pool upgrade, etc.), rollups can benefit from existing withdrawal research and codebase.
For instance, to guarantee credible neutrality and prevent malicious activity, rollups must ensure users can withdraw their funds from L2 to L1 at any time. So when rollups design a forced withdrawal mechanism, they can reuse engine API and withdrawal queue. Users can easily push their money from the rollup sequencer to the Ethereum execution layer.
Also, if Ethereum client-based rollups have a staking layer (users may need to deposit a certain amount of tokens to become a sequencer/prover), they can reuse the engine API and withdrawal queue to safely withdraw funds from the staking layer to the L2 sequencer too.
Conclusions
This comprehensive article delves into the concept of withdrawal, its mechanics, and its broader implications. I hope that it assists the wider community in gaining a deeper understanding of the withdrawal function.
In addition, I eagerly anticipate the Shapella upgrade. Following this update, the consensus R&D team will forge ahead with numerous developments, such as EIP-4844, in-protocol PBS, verkle tree/statelessness, fork-choice rule enhancements, and the ultimate aim of achieving single-slot finality.
A wealth of exciting developments lies ahead, and I look forward to sharing these stories with you in upcoming articles!