diff --git a/docs/EigenPods.md b/docs/EigenPods.md index 650f2fe3f..9bcf2fcc5 100644 --- a/docs/EigenPods.md +++ b/docs/EigenPods.md @@ -3,7 +3,7 @@ ## Overview -This document explains *EigenPods*, the mechanism by which EigenLayer facilitates the restaking of native beacon chain ether. The EigenPods subprotocol allows entities that own validators that are apart of Ethereum's consensus to repoint their withdrawal credentials (explained later) to contracts in the EigenPods subprotocol that have certain mechanisms to ensure safe restaking. +This document explains *EigenPods*, the mechanism by which EigenLayer facilitates the restaking of native beacon chain ether. The EigenPods subprotocol allows entities that own validators that are a part of Ethereum's consensus to repoint their withdrawal credentials (explained later) to contracts in the EigenPods subprotocol. It is important to contrast this with the restaking of liquid staking derivatives (LSDs) on EigenLayer. EigenLayer will integrate with liquid staking protocols "above the hood", meaning that withdrawal credentials will be pointed to EigenLayer at the smart contract layer rather than the consensus layer. This is because liquid staking protocols need their contracts to be in possession of the withdrawal credentials in order to not have platform risk on EigenLayer. As always, this means that value of liquid staking derivatives carries a discount due to additional smart contract risk. @@ -34,15 +34,15 @@ The following sections are all related to managing Consensus Layer (CL) and Exec When EigenPod contracts are initially deployed, the "restaking" functionality is turned off - the withdrawal credential proof has not been initiated yet. In this "non-restaking" mode, the contract may be used by its owner freely to withdraw validator balances from the beacon chain via the `withdrawBeforeRestaking` function. This function routes the withdrawn balance directly to the `DelayedWithdrawalRouter` contract. Once the EigenPod's owner verifies that their withdrawal credentials are pointed to the EigenPod via `verifyWithdrawalCredentialsAndBalance`, the `hasRestaked` flag will be set to true and any withdrawals must now be proven for via the `verifyAndProcessWithdrawal` function. ### Merkle Proof of Correctly Pointed Withdrawal Credentials -After staking an Ethereum validator with its withdrawal credentials pointed to their EigenPod, a staker must show that the new validator exists and has its withdrawal credentials pointed to the EigenPod, by proving it against a beacon state root with a call to `verifyWithdrawalCredentialsAndBalance`. The EigenPod will verify the proof (along with checking for replays and other conditions) and, if the ETH validator's effective balance is proven to be greater than or equal to `REQUIRED_BALANCE_GWEI`, then the EigenPod will pass the validator's effective balance value through its own hysteresis calculation (see [here](#hysteresis)), which effectively underestimates the effective balance of the validator by 1 ETH. Then a call is made to the EigenPodManager to forward a call to the StrategyManager, crediting the staker with those shares of the virtual beacon chain ETH strategy. +After staking an Ethereum validator with its withdrawal credentials pointed to their EigenPod, a staker must show that the new validator exists and has its withdrawal credentials pointed to the EigenPod, by proving it against a beacon state root with a call to `verifyWithdrawalCredentialsAndBalance`. The EigenPod will verify the proof (along with checking for replays and other conditions) and, if the ETH validator's effective balance is proven to be greater than or equal to `MAX_VALIDATOR_BALANCE_GWEI`, then the EigenPod will pass the validator's effective balance value through its own hysteresis calculation (see [here](#hysteresis)), which effectively underestimates the effective balance of the validator by 1 ETH. Then a call is made to the EigenPodManager to forward a call to the StrategyManager, crediting the staker with those shares of the virtual beacon chain ETH strategy. ### Effective Restaked Balance - Hysteresis {#hysteresis} -To convey to EigenLayer that an EigenPod has validator's restaked on it, anyone can submit a proof against a beacon chain state root the proves that a validator has their withdrawal credentials pointed to the pod. The proof is verified and the EigenPod calls the EigenPodMananger that calls the StrategyManager which records the validators proven balance run through the hysteresis function worth of ETH in the "beaconChainETH" strategy. Each EigenPod keeps track of all of the validators by the hash of their public key. For each validator, their validator index and current balance in EigenLayer is kept track of. +To convey to EigenLayer that an EigenPod has validator(s) restaked on it, anyone can submit a proof against a beacon chain state root the proves that a validator has their withdrawal credentials pointed to the pod. The proof is verified and the EigenPod calls the EigenPodMananger that calls the StrategyManager which records the validators proven balance run through the hysteresis function worth of ETH in the "beaconChainETH" strategy. Each EigenPod keeps track of all of the validators by the hash of their public key. For each validator, their validator index and current balance in EigenLayer is kept track of. ### Proofs of Validator Balance Updates EigenLayer pessimistically assumes the validator has less ETH that they actually have restaked in order for the protocol to have an accurate view of the validator's restaked assets even in the case of an uncorrelated slashing event, for which the penalty is >=1 ETH. In the case that a validator's balance drops close to or below what is noted in EigenLayer, AVSs need to be notified of that ASAP, in order to get an accurate view of their security. -In the case that a validator's balance, when run through the hysteresis function, is lower or higher than what is restaked on EigenLayer, anyone is allowed to permissionlessly prove that the balance of a certain validator. If the proof is valid, the StrategyManager decrements the pod owners beacon chain ETH shares by however much is staked on EigenLayer and adds the new proven stake, i.e., the strategyManager's view of the staker's shares is an accurate representation of the consensys layer as long as timely balance update proofs are submitted. +In the case that a validator's balance, when run through the hysteresis function, is lower or higher than what is restaked on EigenLayer, anyone is allowed to permissionlessly prove the new balance of the validator, triggering an update in EigenLayer. If the proof is valid, the StrategyManager decrements the pod owners beacon chain ETH shares by however much is staked on EigenLayer and adds the new proven stake, i.e., the strategyManager's view of the staker's shares is an accurate representation of the consensus layer as long as timely balance update proofs are submitted. ### Proofs of Full/Partial Withdrawals @@ -54,13 +54,9 @@ We also must prove the `executionPayload.blockNumber > mostRecentWithdrawalBlock In this second case, in order to withdraw their balance from the EigenPod, stakers must provide a valid proof of their full withdrawal against a beacon chain state root. Full withdrawals are differentiated from partial withdrawals by checking against the validator in question's 'withdrawable epoch'; if the validator's withdrawable epoch is less than or equal to the slot's epoch, then the validator has fully withdrawn because a full withdrawal is only processable at or after the withdrawable epoch has passed. Once the full withdrawal is successfully verified, there are 2 cases, each handled slightly differently: -1. If the withdrawn amount is greater than `REQUIRED_BALANCE_GWEI`, then `REQUIRED_BALANCE_WEI` is held for processing through EigenLayer's normal withdrawal path, while the excess amount above `REQUIRED_BALANCE_GWEI` is marked as instantly withdrawable. +1. If the withdrawn amount is greater than `MAX_VALIDATOR_BALANCE_GWEI_GWEI`, then `MAX_VALIDATOR_BALANCE_GWEI` is held for processing through EigenLayer's normal withdrawal path, while the excess amount above `MAX_VALIDATOR_BALANCE_GWEI` is marked as instantly withdrawable. -2. If the withdrawn amount is greater than `REQUIRED_BALANCE_GWEI` and the validator *was* previously proven to be "overcommitted", then `REQUIRED_BALANCE_WEI` is held for processing through EigenLayer's normal withdrawal path, while the excess amount above `REQUIRED_BALANCE_GWEI` is marked as instantly withdrawable, identical to (1) above. Additionally, the podOwner's beaconChainShares in EigenLayer are increased by `REQUIRED_BALANCE_WEI` to counter-balance the decrease that occurred during the [overcommittment fraudproof process](#fraud-proofs-for-overcommitted-balances). - -3. If the amount withdrawn is less than `REQUIRED_BALANCE_GWEI` and the validator was *not* previously proven to be "overcommitted", then the full withdrawal amount is held for processing through EigenLayer's normal withdrawal path, and any excess 'beaconChainETH' shares in EigenLayer are immediately removed, somewhat similar to the process outlined in [fraud proofs for overcommitted balances]. - -4. If the amount withdrawn is less than `REQUIRED_BALANCE_GWEI` and the validator *was* previously proven to be "overcommitted", then the full withdrawal amount is held for processing through EigenLayer's normal withdrawal path, and the podOwner is credited with enough beaconChainETH shares in EigenLayer to complete the normal withdrawal process; this last step is necessary since the validator's virtual beaconChainETH shares were previously removed from EigenLayer as part of the overcommittment fraudproof process. +2. If the withdrawn amount is less than `MAX_VALIDATOR_BALANCE_GWEI`, then the amount being withdrawn is held for processing through EigenLayer's normal withdrawal path. ### The EigenPod Invariant The core complexity of the EigenPods system is to ensure that EigenLayer continuously has an accurate picture of the state of the beacon chain balances repointed to it. In other words, the invariant that governs this system is: diff --git a/src/contracts/core/StrategyManagerStorage.sol b/src/contracts/core/StrategyManagerStorage.sol index bae6c6a25..3fef50e94 100644 --- a/src/contracts/core/StrategyManagerStorage.sol +++ b/src/contracts/core/StrategyManagerStorage.sol @@ -61,6 +61,8 @@ abstract contract StrategyManagerStorage is IStrategyManager { /// @notice Mapping: strategy => whether or not stakers are allowed to deposit into it mapping(IStrategy => bool) public strategyIsWhitelistedForDeposit; + uint256 internal _deprecatedStorage; + IStrategy public constant beaconChainETHStrategy = IStrategy(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0); constructor(IDelegationManager _delegation, IEigenPodManager _eigenPodManager, ISlasher _slasher) { diff --git a/src/contracts/interfaces/IBeaconChainProofs.sol b/src/contracts/interfaces/IBeaconChainProofs.sol deleted file mode 100644 index 8303fe356..000000000 --- a/src/contracts/interfaces/IBeaconChainProofs.sol +++ /dev/null @@ -1,14 +0,0 @@ -//SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - - -interface IBeaconChainProofs { - - /** - * @notice This function verifies merkle proofs of the balance of a certain validator against a beacon chain state root - * @param validatorIndex the index of the proven validator - * @param validatorRoot is the serialized balance used to prove the balance of the validator (refer to `getBalanceFromBalanceRoot` above for detailed explanation) - */ - function verifyValidatorFields(uint40 validatorIndex, bytes32 validatorRoot) external view returns (bool); -} - diff --git a/src/contracts/interfaces/IEigenPod.sol b/src/contracts/interfaces/IEigenPod.sol index 6df3077d6..0292fdc57 100644 --- a/src/contracts/interfaces/IEigenPod.sol +++ b/src/contracts/interfaces/IEigenPod.sol @@ -90,20 +90,20 @@ interface IEigenPod { function validatorPubkeyHashToInfo(bytes32 validatorPubkeyHash) external view returns (ValidatorInfo memory); - ///@notice mapping that tracks proven partial withdrawals + ///@notice mapping that tracks proven withdrawals function provenWithdrawal(bytes32 validatorPubkeyHash, uint64 slot) external view returns (bool); - /// @notice this is a mapping of validator indices to a Validator struct containing pertinent info about the validator + /// @notice This returns the status of a given validator function validatorStatus(bytes32 pubkeyHash) external view returns(VALIDATOR_STATUS); /** - * @notice This function verifies that the withdrawal credentials of the podOwner are pointed to - * this contract. It also verifies the current (not effective) balance of the validator. It verifies the provided proof of the ETH validator against the beacon chain state + * @notice This function verifies that the withdrawal credentials of validator(s) owned by the podOwner are pointed to + * this contract. It also verifies the effective balance of the validator. It verifies the provided proof of the ETH validator against the beacon chain state * root, marks the validator as 'active' in EigenLayer, and credits the restaked ETH in Eigenlayer. * @param oracleBlockNumber is the Beacon Chain blockNumber whose state root the `proof` will be proven against. - * @param validatorIndices is the list of indices of the validator being proven, refer to consensus specs - * @param proofs is an array of proofs, where each proof proves the ETH validator's balance and withdrawal credentials against a beacon chain state root + * @param validatorIndices is the list of indices of the validators being proven, refer to consensus specs + * @param proofs is an array of proofs, where each proof proves each ETH validator's balance and withdrawal credentials against a beacon chain state root * @param validatorFields are the fields of the "Validator Container", refer to consensus specs * for details: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator */ @@ -144,6 +144,7 @@ interface IEigenPod { * @param validatorFields are the fields of the validator being proven * @param beaconChainETHStrategyIndex is the index of the beaconChainETHStrategy for the pod owner for the callback to * the EigenPodManager to the StrategyManager in case it must be removed from the podOwner's list of strategies + * @param oracleTimestamp is the timestamp of the oracle slot that the withdrawal is being proven against */ function verifyAndProcessWithdrawals( BeaconChainProofs.WithdrawalProofs[] calldata withdrawalProofs, @@ -165,6 +166,6 @@ interface IEigenPod { function decrementWithdrawableRestakedExecutionLayerGwei(uint256 amountWei) external; /// @notice called by the eigenPodManager to increment the withdrawableRestakedExecutionLayerGwei - /// in the pod, to reflect a completetion of a queued withdrawal + /// in the pod, to reflect a completion of a queued withdrawal as shares function incrementWithdrawableRestakedExecutionLayerGwei(uint256 amountWei) external; } \ No newline at end of file diff --git a/src/contracts/interfaces/IEigenPodManager.sol b/src/contracts/interfaces/IEigenPodManager.sol index 88282b3d0..4603f92fb 100644 --- a/src/contracts/interfaces/IEigenPodManager.sol +++ b/src/contracts/interfaces/IEigenPodManager.sol @@ -37,8 +37,8 @@ interface IEigenPodManager is IPausable { function restakeBeaconChainETH(address podOwner, uint256 amount) external; /** - * @notice Removes beacon chain ETH from EigenLayer on behalf of the owner of an EigenPod, when the - * @param podOwner is the pod owner to be slashed + * @notice Records an update in beacon chain strategy shares in the strategy manager + * @param podOwner is the pod owner whose shares are to be updated, * @param beaconChainETHStrategyIndex is the index of the beaconChainETHStrategy in case it must be removed, * @param sharesDelta is the change in podOwner's beaconChainETHStrategy shares * @dev Callable only by the podOwner's EigenPod contract. @@ -61,8 +61,10 @@ interface IEigenPodManager is IPausable { */ function updateBeaconChainOracle(IBeaconChainOracle newBeaconChainOracle) external; + /// @notice decrements the proven amount of withdrawable ETH to reflect decrementation of shares function decrementWithdrawableRestakedExecutionLayerGwei(address podOwner, uint256 amountWei) external; + /// @notice increments the proven amount of withdrawable ETH to reflect incrementation of shares when a podOwner completes a withdrawal as shares function incrementWithdrawableRestakedExecutionLayerGwei(address podOwner, uint256 amountWei) external; /// @notice Returns the address of the `podOwner`'s EigenPod if it has been deployed. diff --git a/src/contracts/interfaces/IStrategyManager.sol b/src/contracts/interfaces/IStrategyManager.sol index 8d7060d32..4745534e3 100644 --- a/src/contracts/interfaces/IStrategyManager.sol +++ b/src/contracts/interfaces/IStrategyManager.sol @@ -59,11 +59,11 @@ interface IStrategyManager { function depositBeaconChainETH(address staker, uint256 amount) external; /** - * @notice Records an overcommitment event on behalf of a staker. The staker's beaconChainETH shares are decremented by `amount`. - * @param podOwner is the pod owner to be slashed + * @notice Records an update in beacon chain strategy shares in the strategy manager + * @param podOwner is the pod owner whose shares are to be updated, * @param beaconChainETHStrategyIndex is the index of the beaconChainETHStrategy in case it must be removed, * @param sharesDelta is the change in podOwner's beaconChainETHStrategy shares - * @dev Only callable by EigenPodManager. + * @dev Callable only by the podOwner's EigenPod contract. */ function recordBeaconChainETHBalanceUpdate(address podOwner, uint256 beaconChainETHStrategyIndex, int256 sharesDelta) external; diff --git a/src/contracts/libraries/BeaconChainProofs.sol b/src/contracts/libraries/BeaconChainProofs.sol index b96b6be52..efdbb281a 100644 --- a/src/contracts/libraries/BeaconChainProofs.sol +++ b/src/contracts/libraries/BeaconChainProofs.sol @@ -103,7 +103,7 @@ library BeaconChainProofs { bytes8 internal constant UINT64_MASK = 0xffffffffffffffff; - + /// @notice This struct contains the merkle proofs and leaves needed to verify a partial/full withdrawal struct WithdrawalProofs { bytes32 beaconStateRoot; bytes latestBlockHeaderProof; @@ -121,6 +121,7 @@ library BeaconChainProofs { bytes32 executionPayloadRoot; } + /// @notice This struct contains the merkle proofs and leaves needed to verify a balance update struct BalanceUpdateProofs { bytes32 beaconStateRoot; bytes latestBlockHeaderProof; @@ -131,7 +132,7 @@ library BeaconChainProofs { bytes32 slotRoot; } - //struct ValidatorFieldsProof { + // @notice This struct contains the merkle proofs and leaves needed to verify a validator's withdrawal credential struct WithdrawalCredentialProofs { bytes32 beaconStateRoot; bytes latestBlockHeaderProof; @@ -162,10 +163,10 @@ library BeaconChainProofs { * @param validatorFields the claimed fields of the validator */ function verifyValidatorFields( - uint40 validatorIndex, bytes32 beaconStateRoot, - bytes calldata proof, - bytes32[] calldata validatorFields + bytes32[] calldata validatorFields, + bytes calldata proof, + uint40 validatorIndex ) internal view { require(validatorFields.length == 2**VALIDATOR_FIELD_TREE_HEIGHT, "BeaconChainProofs.verifyValidatorFields: Validator fields has incorrect length"); @@ -191,10 +192,10 @@ library BeaconChainProofs { * @param balanceRoot is the serialized balance used to prove the balance of the validator (refer to `getBalanceFromBalanceRoot` above for detailed explanation) */ function verifyValidatorBalance( - uint40 validatorIndex, bytes32 beaconStateRoot, + bytes32 balanceRoot, bytes calldata proof, - bytes32 balanceRoot + uint40 validatorIndex ) internal view { require(proof.length == 32 * ((BALANCE_TREE_HEIGHT + 1) + BEACON_STATE_FIELD_TREE_HEIGHT), "BeaconChainProofs.verifyValidatorBalance: Proof has incorrect length"); @@ -208,6 +209,13 @@ library BeaconChainProofs { require(Merkle.verifyInclusionSha256(proof, beaconStateRoot, balanceRoot, balanceIndex), "BeaconChainProofs.verifyValidatorBalance: Invalid merkle proof"); } + /** + * @notice This function verifies the slot against the state root. the slot is + * a tracked in the beacon state. + * @param beaconStateRoot is the beacon chain state root to be proven against. + * @param proof is the provided merkle proof + * @param slotRoot is hashtree root of the slot in the beacon state + */ function verifySlotRoot( bytes32 beaconStateRoot, bytes32 slotRoot, @@ -243,8 +251,8 @@ library BeaconChainProofs { */ function verifyWithdrawalProofs( bytes32 beaconStateRoot, - WithdrawalProofs calldata proofs, - bytes32[] calldata withdrawalFields + bytes32[] calldata withdrawalFields, + WithdrawalProofs calldata proofs ) internal view { require(withdrawalFields.length == 2**WITHDRAWAL_FIELD_TREE_HEIGHT, "BeaconChainProofs.verifyWithdrawalProofs: withdrawalFields has incorrect length"); diff --git a/src/contracts/pods/EigenPod.sol b/src/contracts/pods/EigenPod.sol index 898d5028c..b9acaf712 100644 --- a/src/contracts/pods/EigenPod.sol +++ b/src/contracts/pods/EigenPod.sol @@ -179,12 +179,12 @@ contract EigenPod is IEigenPod, Initializable, ReentrancyGuardUpgradeable, Eigen } /** - * @notice This function verifies that the withdrawal credentials for one or many validators of the podOwner that are pointed to - * this contract. It also verifies the current (not effective) balance of the validator. It verifies the provided proof of the ETH validator against the beacon chain state + * @notice This function verifies that the withdrawal credentials of validator(s) owned by the podOwner are pointed to + * this contract. It also verifies the effective balance of the validator. It verifies the provided proof of the ETH validator against the beacon chain state * root, marks the validator as 'active' in EigenLayer, and credits the restaked ETH in Eigenlayer. * @param oracleTimestamp is the Beacon Chain timestamp whose state root the `proof` will be proven against. - * @param validatorIndices is a list of indices of the validators being proven, refer to consensus specs - * @param proofs is the bytes that prove the ETH validator's withdrawal credentials against a beacon chain state root + * @param validatorIndices is the list of indices of the validators being proven, refer to consensus specs + * @param proofs is an array of proofs, where each proof proves each ETH validator's balance and withdrawal credentials against a beacon chain state root * @param validatorFields are the fields of the "Validator Container", refer to consensus specs * for details: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator */ @@ -236,7 +236,6 @@ contract EigenPod is IEigenPod, Initializable, ReentrancyGuardUpgradeable, Eigen require(oracleTimestamp + VERIFY_BALANCE_UPDATE_WINDOW_SECONDS >= block.timestamp, "EigenPod.verifyBalanceUpdate: specified timestamp is too far in past"); - bytes32 validatorPubkeyHash = validatorFields[BeaconChainProofs.VALIDATOR_PUBKEY_INDEX]; ValidatorInfo memory validatorInfo = _validatorPubkeyHashToInfo[validatorPubkeyHash]; @@ -249,26 +248,27 @@ contract EigenPod is IEigenPod, Initializable, ReentrancyGuardUpgradeable, Eigen // deserialize the balance field from the balanceRoot uint64 validatorNewBalanceGwei = BeaconChainProofs.getBalanceFromBalanceRoot(validatorIndex, proofs.balanceRoot); - // verify ETH validator proof - bytes32 latestBlockHeaderRoot = eigenPodManager.getBeaconChainStateRoot(oracleTimestamp); - - // verify that the provided state root is verified against the oracle-provided latest block header - BeaconChainProofs.verifyStateRootAgainstLatestBlockHeaderRoot(proofs.beaconStateRoot, latestBlockHeaderRoot, proofs.latestBlockHeaderProof); + { + // verify ETH validator proof + bytes32 latestBlockHeaderRoot = eigenPodManager.getBeaconChainStateRoot(oracleTimestamp); + // verify that the provided state root is verified against the oracle-provided latest block header + BeaconChainProofs.verifyStateRootAgainstLatestBlockHeaderRoot(proofs.beaconStateRoot, latestBlockHeaderRoot, proofs.latestBlockHeaderProof); + } // verify validator fields BeaconChainProofs.verifyValidatorFields( - validatorIndex, proofs.beaconStateRoot, + validatorFields, proofs.validatorFieldsProof, - validatorFields + validatorIndex ); // verify ETH validator's current balance, which is stored in the `balances` container of the beacon state BeaconChainProofs.verifyValidatorBalance( - validatorIndex, proofs.beaconStateRoot, + proofs.balanceRoot, proofs.validatorBalanceProof, - proofs.balanceRoot + validatorIndex ); //verify provided slot is valid against beaconStateRoot BeaconChainProofs.verifySlotRoot( @@ -303,13 +303,13 @@ contract EigenPod is IEigenPod, Initializable, ReentrancyGuardUpgradeable, Eigen /** * @notice This function records full and partial withdrawals on behalf of one of the Ethereum validators for this EigenPod - * @param withdrawalProofs is the information needed to check the veracity of the block number and withdrawal being proven - * @param validatorFieldsProofs is the information needed to check the veracity of the validator fields being proven - * @param withdrawalFields are the fields of the withdrawal being proven - * @param validatorFields are the fields of the validator being proven + * @param withdrawalProofs is the information needed to check the veracity of the block numbers and withdrawals being proven + * @param validatorFieldsProofs is the proof of the validator's fields' in the validator tree + * @param withdrawalFields are the fields of the withdrawals being proven + * @param validatorFields are the fields of the validators being proven * @param beaconChainETHStrategyIndex is the index of the beaconChainETHStrategy for the pod owner for the callback to * the EigenPodManager to the StrategyManager in case it must be removed from the podOwner's list of strategies - * @param oracleTimestamp is the Beacon Chain blockNumber whose state root the `proof` will be proven against. + * @param oracleTimestamp is the timestamp of the oracle slot that the withdrawal is being proven against */ function verifyAndProcessWithdrawals( BeaconChainProofs.WithdrawalProofs[] calldata withdrawalProofs, @@ -396,10 +396,10 @@ contract EigenPod is IEigenPod, Initializable, ReentrancyGuardUpgradeable, Eigen BeaconChainProofs.verifyValidatorFields( - validatorIndex, proofs.beaconStateRoot, + validatorFields, proofs.validatorFieldsProof, - validatorFields + validatorIndex ); // set the status to active @@ -454,9 +454,9 @@ contract EigenPod is IEigenPod, Initializable, ReentrancyGuardUpgradeable, Eigen BeaconChainProofs.verifyStateRootAgainstLatestBlockHeaderRoot(withdrawalProofs.beaconStateRoot, eigenPodManager.getBeaconChainStateRoot(oracleTimestamp), withdrawalProofs.latestBlockHeaderProof); // Verifying the withdrawal as well as the slot - BeaconChainProofs.verifyWithdrawalProofs(withdrawalProofs.beaconStateRoot, withdrawalProofs, withdrawalFields); + BeaconChainProofs.verifyWithdrawalProofs(withdrawalProofs.beaconStateRoot, withdrawalFields, withdrawalProofs); // Verifying the validator fields, specifically the withdrawable epoch - BeaconChainProofs.verifyValidatorFields(validatorIndex, withdrawalProofs.beaconStateRoot, validatorFieldsProof, validatorFields); + BeaconChainProofs.verifyValidatorFields(withdrawalProofs.beaconStateRoot, validatorFields, validatorFieldsProof, validatorIndex); { uint64 withdrawalAmountGwei = Endian.fromLittleEndianUint64(withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_AMOUNT_INDEX]); diff --git a/src/contracts/pods/EigenPodManager.sol b/src/contracts/pods/EigenPodManager.sol index cc0899642..f528f4a1d 100644 --- a/src/contracts/pods/EigenPodManager.sol +++ b/src/contracts/pods/EigenPodManager.sol @@ -176,12 +176,20 @@ contract EigenPodManager is Initializable, OwnableUpgradeable, Pausable, IEigenP _setMaxPods(newMaxPods); } + /** + * @notice This function is called to decrement withdrawableRestakedExecutionLayerGwei when a validator queues a withdrawal. + * @param amountWei is the amount of ETH in wei to decrement withdrawableRestakedExecutionLayerGwei by + */ function decrementWithdrawableRestakedExecutionLayerGwei(address podOwner, uint256 amountWei) external onlyStrategyManager onlyWhenNotPaused(PAUSED_WITHDRAW_RESTAKED_ETH) { ownerToPod[podOwner].decrementWithdrawableRestakedExecutionLayerGwei(amountWei); } + /** + * @notice This function is called to increment withdrawableRestakedExecutionLayerGwei when a validator's withdrawal is completed. + * @param amountWei is the amount of ETH in wei to increment withdrawableRestakedExecutionLayerGwei by + */ function incrementWithdrawableRestakedExecutionLayerGwei(address podOwner, uint256 amountWei) external onlyStrategyManager onlyWhenNotPaused(PAUSED_WITHDRAW_RESTAKED_ETH) { diff --git a/src/test/EigenPod.t.sol b/src/test/EigenPod.t.sol index bdecd4b9e..7d06d9ba2 100644 --- a/src/test/EigenPod.t.sol +++ b/src/test/EigenPod.t.sol @@ -369,7 +369,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { Relayer relay = new Relayer(); bytes32 beaconStateRoot = getBeaconStateRoot(); - relay.verifyWithdrawalProofs(beaconStateRoot, proofs, withdrawalFields); + relay.verifyWithdrawalProofs(beaconStateRoot, withdrawalFields, proofs); } /// @notice This test is to ensure the full withdrawal flow works @@ -1350,9 +1350,9 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { contract Relayer is Test { function verifyWithdrawalProofs( bytes32 beaconStateRoot, - BeaconChainProofs.WithdrawalProofs calldata proofs, - bytes32[] calldata withdrawalFields + bytes32[] calldata withdrawalFields, + BeaconChainProofs.WithdrawalProofs calldata proofs ) public view { - BeaconChainProofs.verifyWithdrawalProofs(beaconStateRoot, proofs, withdrawalFields); + BeaconChainProofs.verifyWithdrawalProofs(beaconStateRoot, withdrawalFields, proofs); } } \ No newline at end of file