# EIP-n: Beacon chain withdrawals on EVM [toc] --- eip: title: Beacon chain withdrawals on EVM description: Enable withdrawals from the Beacon chain via a special contract on the EVM author: discussions-to: status: Draft type: Standards Track category: Core created: 2022-02-18 requires: 4788 --- ## Abstract TBD ## Motivation TBD ## Specification | Constant | Value | |--- |--- | | `FORK_BLOCK` | TBD | | `DEPOSIT_CONTRACT_ADDRESS` | `0x00000000219ab540356cBB839Cbe05303d7705Fa` | | `WITHDRAWAL_CONTRACT_ADDRESS` | TBD | | `WITHDRAWAL_CODE_HASH` | TBD | ### Block validation On `FORK_BLOCK` the code hash of the account `WITHDRAWAL_CONTRACT_ADDRESS` must equal to `WITHDRAWAL_CODE_HASH`, otherwise the block is invalid. After `FORK_BLOCK`, before processing any transactions in a block, apply the following changes to the state: 1. Set the balance of account `DEPOSIT_CONTRACT_ADDRESS` to 0. 2. Set the balance of account `WITHDRAWAL_CONTRACT_ADDRESS` to `blockHeader.totalBeaconValidatorBalance`. ### Block creation Starting `FORK_BLOCK` the block header will contain one more field, called the `totalBeaconValidatorBalancer` at the last position. This field must be set to `sum(BeaconState.balances)`. ### Contract implementation ```solidity pragma solidity 0.8.12; contract WithdrawalContract { struct WithdrawalReceipt { uint64 index; address recipient; uint64 amount; } error AlreadySpent(); error InvalidProof(); /// A bitmap of the spent receipt indexes. mapping (uint256 => uint256) public spent; function withdraw(WithdrawalReceipt calldata receipt, bytes calldata merkle_branch, uint256 merkle_root_slot) external { uint256 slot_index = receipt.index / 256; uint256 slot_bit = 1 << (receipt.index % 256); uint256 slot = spent[slot_index]; // Keeping a copy here is an SLOAD optimisation if (slot & slot_bit) revert AlreadySpent(); // Reassemble SSZ-encoded leaf bytes memory leaf = ssz_hash_tree_root(receipt); bytes32 beacon_root; assembly { // This is the beaconstateroot(slot) instruction from EIP-4788. beacon_root := verbatim_1i_1o(hex"48", merkle_root_slot) } // Calculate proof of the branch ... using leaf + merkle_branch if (calculated_root != beacon_root) revert InvalidProof(); // Mark as spent spent[slot_index] = slot | slot_bit; // TODO: use low-level transfer or the `TRANSFER` opcode (the potential future of `SELFDESTRUCT`) here? receipt.recipient.transfer(receipt.amount * 1 gwei); } } ``` This code is compiled using the specified Solidity version and settings (TBD) resulting in runtime bytecode equaling to `WITHDRAWAL_CODE_HASH`. ## Rationale ## Fork block and code hash We ensure that on `FORK_BLOCK` the correct contract is exists in place of the `WITHDRAWAL_CONTRACT_ADDRESS`. Alternatively we could choose to insert the code at the address on the `FORK_BLOCK`. ## Ether supply accounting In order to avoid special account rules, the withdrawal contract must be able to send an Ether balance. It would be possible to set once an arbitrary large balance for the account, and not touch anything else. However we chose to keep total supply accounting in balance, by modifying the supply of both the deposit and withdrawal contracts at the beginning of the block. Transactions in a block can modify the balances by properly depositing and withdrawing, and so their balances should stay valid throughout the block. There are two cases these balances could get out of sync: 1. Either of these accounts are targets of a miner reward 2. Either of these accounts are targets of a `selfdestruct` Should these happen, these balances will vanish from the chain. A side-effect is that these two accounts become the ultimate burner addresses, instead of the commonly used `0x0000000000000000000000000000000000000000` account (among others). ## Backwards Compatibility This change will affect the balance of the deposit contract. Other contracts and services depending on this value may see some disruption. If they assume the balance can only grow, their assumption will be broken. ## Security Considerations TBA ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).