| File | Type | Proxy |
|---|---|---|
EigenPod.sol |
Instanced, deployed per-user | Beacon proxy |
An EigenPod is deployed via the EigenPodManager by a Staker (referred to in this doc as the Pod Owner). EigenPods allow a Pod Owner to restake one or more beacon chain validators, earning shares which can be delegated to Operators to earn yield. When a Pod Owner begins running a validator on the beacon chain, they choose withdrawal credentials for that validator. Withdrawal credentials are the ETH address to which:
- A validator's principal is sent when the validator exits the beacon chain
- A validator's consensus rewards are sent as the validator proposes/attests to blocks on the beacon chain
Additionally, when running validator node software, a validator is configured with a fee recipient. The fee recipient receives:
- Execution layer rewards when the validator proposes a block
- MEV rewards if the validator is running MEV-boost/other custom block proposer software
An EigenPod may serve as EITHER/BOTH the withdrawal credentials OR the fee recipient for your validators. In prior releases, it was only possible to use an EigenPod for withdrawal credentials. However, this is no longer the case!
EigenPods support validators with BOTH 0x01 and 0x02 withdrawal credentials, as well as validators up to (and beyond) 2048 ETH.
For more background and history on EigenPod design, check out this HackMD.
The primary goal of the EigenPod system is to ensure that shares are backed 1:1 with ETH that is either already in the EigenPod, or will eventually flow through the EigenPod. To support this goal, EigenPods:
- serve as the withdrawal credentials for one or more beacon chain validators controlled by the Pod Owner
- validate beacon chain state proofs
- interpret these proofs to add or remove shares in the beacon chain ETH strategy
Because beacon chain proofs are processed asynchronously from the beacon chain itself, there is an inherent lag between an event on the beacon chain and a corresponding share update in any affected EigenPods. Therefore, the secondary goals of the EigenPod system are to minimize lag where possible and to ensure various timing windows cannot (i) create unbacked shares or (ii) prevent the withdrawal of existing shares.
- Restaking Beacon Chain ETH
- Checkpointing Validators
- Staleness Proofs
- Consolidations and Withdrawals
- Other Methods
Pod Owner: A Staker who has deployed an EigenPod is a Pod Owner. The terms are used interchangeably in this document.
- Pod Owners can only deploy a single
EigenPod, but can restake any number of beacon chain validators from the sameEigenPod. - Pod Owners can delegate their
EigenPodManagershares to Operators (viaDelegationManager). - These shares correspond to the amount of restaked beacon chain ETH held by the Pod Owner via their
EigenPod.
Proof Submitter: An address designated by the Pod Owner with permissions to call certain EigenPod methods. This role is provided to allow Pod Owners to manage their day-to-day EigenPod tasks via hot wallets, rather than the Pod Owner address which controls all funds. The Proof Submitter can call several EigenPod methods. See setProofSubmitter docs for more details.
Active validator set: This term is used frequently in this document to describe the set of validators whose withdrawal credentials have been verified to be pointed at an EigenPod. The active validator set is used to determine the number of proofs required to complete a checkpoint (see Checkpointing Validators).
- A validator enters the active validator set when their withdrawal credentials are verified (see
verifyWithdrawalCredentials) - A validator leaves the active validator set when a checkpoint proof shows they have 0 balance (see
verifyCheckpointProofs)
In the implementation, the active validator set is comprised of two state variables:
uint256 activeValidatorCount- incremented by 1 when a validator enters the active validator set
- decremented by 1 when a validator leaves the active validator set
mapping(bytes32 => ValidatorInfo) _validatorPubkeyHashToInfo(specifically, thestatusfield)VALIDATOR_STATUS.INACTIVE -> VALIDATOR_STATUS.ACTIVEwhen entering the active validator setVALIDATOR_STATUS.ACTIVE -> VALIDATOR_STATUS.WITHDRAWNwhen leaving the active validator set
Checkpoint: A snapshot of EigenPod and beacon chain state used to update the Pod Owner's shares based on a combination of beacon chain balance and native ETH balance. Checkpoints allow an EigenPod to account for validator exits, partial withdrawals of consensus rewards, or execution layer fees earned by their validators. Completing a checkpoint will account for these amounts in the EigenPod, enabling the Pod Owner to compound their restaked shares or withdraw accumulated yield.
Only one checkpoint can be active at a time in a given EigenPod. The pod's current checkpoint is represented by the following data structure:
struct Checkpoint {
bytes32 beaconBlockRoot; // proofs are verified against a beacon block root
uint24 proofsRemaining; // number of proofs remaining before the checkpoint is completed
uint64 podBalanceGwei; // native ETH that will be awarded shares when the checkpoint is completed
int64 balanceDeltasGwei; // total change in beacon chain balance tracked across submitted proofs
uint64 prevBeaconBalanceGwei; // total recorded beacon chain balance as of the last checkpoint
}Checkpoints are completed by submitting one beacon chain proof per validator in the pod's active validator set. See Checkpointing Validators for details.
If a Pod Owner has validators whose withdrawal credentials are an EigenPod, the Pod Owner can use verifyWithdrawalCredentials to begin restaking ETH while it is still on the beacon chain. Once a validator's withdrawal credentials are verified:
- the Pod Owner receives delegatable shares via
EigenPodManager.podOwnerShares - the validator enters the pod's active validator set, and must be included in future checkpoint proofs (see Checkpointing Validators)
Methods:
function verifyWithdrawalCredentials(
uint64 beaconTimestamp,
BeaconChainProofs.StateRootProof calldata stateRootProof,
uint40[] calldata validatorIndices,
bytes[] calldata validatorFieldsProofs,
bytes32[][] calldata validatorFields
)
external
onlyOwnerOrProofSubmitter
onlyWhenNotPaused(PAUSED_EIGENPODS_VERIFY_CREDENTIALS)
struct StateRootProof {
bytes32 beaconStateRoot;
bytes proof;
}This method first verifies a beacon state root against a beacon block root returned by the EIP-4788 oracle. Then, it verifies one or more withdrawal credential proofs against the beacon state root. Finally, the Pod Owner is awarded shares according to the sum of the effective balance of each verified validator (via EigenPodManager.recordBeaconChainETHBalanceUpdate).
A withdrawal credential proof uses a validator's ValidatorIndex and a merkle proof to prove the existence of a Validator container at a given block. The beacon chain Validator container holds important information used in this method:
pubkey: A BLS pubkey hash, used to uniquely identify the validator within theEigenPodwithdrawal_credentials: Used to verify that the validator will withdraw its principal to thisEigenPodif it exits the beacon chain. Can have EITHER the 0x01 or 0x02 prefix.effective_balance: The balance of the validator, updated once per epoch and capped at 32 ETH. Used to award shares to the Pod Owneractivation_epoch: Initially set totype(uint64).max, this value is updated when a validator reaches a balance of at least 32 ETH, designating the validator is ready to become active on the beacon chain. This method requires that a validator is either already active, or in the process of activating on the beacon chain.exit_epoch: Initially set totype(uint64).max, this value is updated when a validator initiates exit from the beacon chain. This method requires that a validator has not initiated an exit from the beacon chain.- If a validator has been exited prior to calling
verifyWithdrawalCredentials, their ETH can be accounted for, awarded shares, and/or withdrawn via the checkpoint system (see Checkpointing Validators).
- If a validator has been exited prior to calling
Note that it is not required to verify your validator's withdrawal credentials, unless you want to receive shares for ETH on the beacon chain. You may choose to use your EigenPod without verifying withdrawal credentials; you will still be able to withdraw yield (or receive shares for yield) via the checkpoint system. To account for ETH held on the beacon chain and to make execution layer partial withdrawal or full exits, this function MUST be called.
Effects:
- For each set of unique verified withdrawal credentials:
activeValidatorCountis increased by 1- The validator's info is recorded in state (
_validatorPubkeyHashToInfo[pubkeyHash]):validatorIndexis recorded from the passed-invalidatorIndicesrestakedBalanceGweiis set to the validator's effective balancelastCheckpointedAtis set to either thelastCheckpointTimestamporcurrentCheckpointTimestampVALIDATOR_STATUSmoves fromINACTIVEtoACTIVE
- The Pod Owner is awarded shares according to the sum of effective balances proven. See
EigenPodManager.recordBeaconChainETHBalanceUpdate
Requirements:
- Caller MUST be EITHER the Pod Owner or Proof Submitter
- Pause status MUST NOT be set:
PAUSED_EIGENPODS_VERIFY_CREDENTIALS - Input array lengths MUST be equal
beaconTimestamp:- MUST be greater than
currentCheckpointTimestamp - MUST be greater than
latestCheckpointTimestamp - MUST be queryable via the EIP-4788 oracle. Generally, this means
beaconTimestampcorresponds to a valid beacon block created within the last 8192 blocks (~27 hours).
- MUST be greater than
stateRootProofMUST verify abeaconStateRootagainst thebeaconBlockRootreturned from the EIP-4788 oracle- For each validator:
- The validator MUST NOT have been previously-verified (
VALIDATOR_STATUSshould beINACTIVE) - The validator's
activation_epochMUST NOT equaltype(uint64).max(akaFAR_FUTURE_EPOCH) - The validator's
exit_epochMUST equaltype(uint64).max(akaFAR_FUTURE_EPOCH) - The validator's
withdrawal_credentialsMUST be pointed to theEigenPod validatorFieldsProofMUST be a valid merkle proof of the correspondingvalidatorFieldsunder thebeaconStateRootat the givenvalidatorIndex
- The validator MUST NOT have been previously-verified (
- See
EigenPodManager.recordBeaconChainETHBalanceUpdate
Checkpoint proofs comprise the bulk of proofs submitted to an EigenPod. Completing a checkpoint means submitting one checkpoint proof for each validator in the pod's active validator set.
EigenPods use checkpoints to detect:
- when validators have exited from the beacon chain, leaving the pod's active validator set
- when the pod has accumulated fees / partial withdrawals from validators
- whether any validators on the beacon chain have increased/decreased in balance
- when consolidation requests have been completed
When a checkpoint is completed, shares are updated accordingly for each of these events. OwnedShares can be withdrawn via the DelegationManager withdrawal queue (see DelegationManager: Undelegating and Withdrawing), which means an EigenPod's checkpoint proofs also play an important role in allowing Pod Owners to exit funds from the system.
Important Notes:
EigenPodscan only have one active checkpoint at a given time, and once started, checkpoints cannot be cancelled (only completed)- Checkpoint proofs are based entirely off of current balance proofs. Even though partial/full withdrawals are processed via checkpoint proofs, this system does NOT use withdrawal proofs.
Methods:
function startCheckpoint(bool revertIfNoBalance)
external
onlyOwnerOrProofSubmitter()
onlyWhenNotPaused(PAUSED_START_CHECKPOINT) This method allows a Pod Owner (or Proof Submitter) to start a checkpoint, beginning the process of proving a pod's active validator set. startCheckpoint takes a snapshot of three things:
podBalanceGwei: theEigenPod'snative ETH balance, minus any balance already credited with shares through previous checkpoints- Note: if
revertIfNoBalance == true, this method will revert ifpodBalanceGwei == 0. This is to allow a Pod Owner to avoid creating a checkpoint unintentionally.
- Note: if
activeValidatorCount: the number of validators in the pod's active validator set, aka the number of validators with verified withdrawal credentials who have NOT been proven exited via a previous checkpoint- This becomes the checkpoint's
proofsRemaining, or the number of proofs that need to be submitted toverifyCheckpointProofsto complete the checkpoint
- This becomes the checkpoint's
beaconBlockRoot: the beacon block root of the previous slot, fetched by querying the EIP-4788 oracle with the currentblock.timestamp- This is used as the single source of truth for all proofs submitted for this checkpoint
startCheckpoint plays a very important role in the security of the checkpoint process: it guarantees that the pod's native ETH balance and any beacon balances proven in the checkpoint are 100% distinct. That is: if a partial/full exit is processed in the block before startCheckpoint is called, then:
- The withdrawn ETH is already in the pod when
startCheckpointis called, and is factored intopodBalanceGwei - A proof of the validator's current balance against
beaconBlockRootwill NOT include the withdrawn ETH
This guarantee means that, if we use the checkpoint to sum up the beacon chain balance of the pod's active validator set, we can award guaranteed-backed shares according to the sum of the pod's beacon chain balance and its native ETH balance.
Effects:
- Sets
currentCheckpointTimestamptoblock.timestamp - Creates a new
Checkpoint:beaconBlockRoot: set to the current block's parent beacon block root, fetched by querying the EIP-4788 oracle usingblock.timestampas input.proofsRemaining: set to the current value ofactiveValidatorCount(note that this value MAY be 0)podBalanceGwei: set to the pod's native ETH balance, minus any balance already accounted for in previous checkpointsbalanceDeltasGwei: set to 0 initially
- If
checkpoint.proofsRemaining == 0, the new checkpoint is auto-completed:withdrawableRestakedExecutionLayerGweiis increased bycheckpoint.podBalanceGweilastCheckpointTimestampis set tocurrentCheckpointTimestampcurrentCheckpointTimestampand_currentCheckpointare deleted- The Pod Owner's shares are updated (see
EigenPodManager.recordBeaconChainETHBalanceUpdate)
Requirements:
- Caller MUST be EITHER the Pod Owner or Proof Submitter
- Pause status MUST NOT be set:
PAUSED_START_CHECKPOINT - A checkpoint MUST NOT be active (
currentCheckpointTimestamp == 0) - The last checkpoint completed MUST NOT have been started in the current block (
lastCheckpointTimestamp != block.timestamp) - If
revertIfNoBalance == true, the pod's native ETH balance MUST contain some nonzero value not already accounted for in the Pod Owner's shares
function verifyCheckpointProofs(
BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof,
BeaconChainProofs.BalanceProof[] calldata proofs
)
external
onlyWhenNotPaused(PAUSED_EIGENPODS_VERIFY_CHECKPOINT_PROOFS)
struct BalanceContainerProof {
bytes32 balanceContainerRoot;
bytes proof;
}
struct BalanceProof {
bytes32 pubkeyHash;
bytes32 balanceRoot;
bytes proof;
}verifyCheckpointProofs is used to make progress on (or complete) the pod's current checkpoint. This method accepts one or more merkle proofs of validators' current balances against a balanceContainerRoot. Additionally, a balanceContainerProof verifies this balanceContainerRoot against the current checkpoint's beaconBlockRoot.
Proofs submitted to this method concern a validator's current balance, NOT their effective balance. The current balance is updated every slot, while effective balances are updated roughly once per epoch. Current balances are stored in the BeaconState.balances field.
For each validator submitted via proofs:
- The validator's
statusshould beACTIVE. That is, its withdrawal credentials are verified (seeverifyWithdrawalCredentials), and it has a nonzero balance as of the last time it was seen in a checkpoint proof. - The validator's
lastCheckpointedAtshould be less thancurrentCheckpointTimestamp. This is to prevent a validator from counting towards a checkpoint's progression more than once.
If either of these two conditions is not met, the proof will be skipped but execution will continue. Execution continues without reverting to prevent a potential griefing vector where anyone could frontrun a batch of proofs, submit one proof from the batch, and cause the batch to revert.
Each valid proof submitted decreases the current checkpoint's proofsRemaining by 1. If proofsRemaining hits 0 the checkpoint is automatically completed, updating the Pod Owner's shares accordingly.
Effects:
- Update
_currentCheckpointin storage - For each validator successfully checkpointed:
- The number of proofs remaining in the checkpoint is decreased (
checkpoint.proofsRemaining--) - A balance delta is calculated using the validator's previous
restakedBalanceGwei. This delta is added tocheckpoint.balanceDeltasGweito track the total beacon chain balance delta. - The validator's
restakedBalanceGweiandlastCheckpointedAtfields are updated. Additionally, if the proof shows that the validator has a balance of 0, the validator's status is moved toVALIDATOR_STATUS.WITHDRAWNand the pod'sactiveValidatorCountis decreased.
- The number of proofs remaining in the checkpoint is decreased (
- If the checkpoint's
proofsRemainingdrops to 0, the checkpoint is automatically completed:checkpoint.podBalanceGweiis added towithdrawableRestakedExecutionLayerGwei, rendering it accounted for in future checkpointslastCheckpointTimestampis set tocurrentCheckpointTimestamp, andcurrentCheckpointTimestampis set to 0.- The Pod Owner's total share delta is calculated as the sum of
checkpoint.podBalanceGweiandcheckpoint.balanceDeltasGwei, and forwarded to theEigenPodManager(seeEigenPodManager.recordBeaconChainETHBalanceUpdate)
Requirements:
- Pause status MUST NOT be set:
PAUSED_EIGENPODS_VERIFY_CHECKPOINT_PROOFS - A checkpoint MUST currently be active (
currentCheckpointTimestamp != 0) balanceContainerProofMUST contain a valid merkle proof of the beacon chain's balances container against_currentCheckpoint.beaconBlockRoot- Each
proofinproofsMUST contain a valid merkle proof of the validator'sbalanceRootagainstbalanceContainerProof.balanceContainerRoot
Regular checkpointing of validators plays an important role in the health of the system, as a completed checkpoint ensures that the pod's shares and backing assets are up to date.
Typically, checkpoints can only be started by the Pod Owner (see startCheckpoint). This is because completing a checkpoint with a lot of validators has the potential to be an expensive operation, so gating startCheckpoint to only be callable by the Pod Owner prevents a griefing vector where anyone can cheaply force the Pod Owner to perform a checkpoint.
In most cases, Pod Owners are incentivized to perform their own regular checkpoints, as completing checkpoints is the only way to access yield sent to the pod. However, if beacon chain validators are slashed, it's possible that a Pod Owner no longer has an incentive to start/complete a checkpoint. After all, they would be losing shares equal to the slashed amount. Unless they have enough unclaimed yield in the pod to make up for this, they only stand to lose by completing a checkpoint.
In this case, verifyStaleBalance can be used to allow a third party to start a checkpoint on the Pod Owner's behalf.
Methods:
function verifyStaleBalance(
uint64 beaconTimestamp,
BeaconChainProofs.StateRootProof calldata stateRootProof,
BeaconChainProofs.ValidatorProof calldata proof
)
external
onlyWhenNotPaused(PAUSED_START_CHECKPOINT)
onlyWhenNotPaused(PAUSED_VERIFY_STALE_BALANCE)Allows anyone to prove that a validator in the pod's active validator set was slashed on the beacon chain. A successful proof allows the caller to start a checkpoint. Note that if the pod currently has an active checkpoint, the existing checkpoint needs to be completed before verifyStaleBalance can start a checkpoint.
A valid proof has the following requirements:
- The
beaconTimestampMUST be newer than the timestamp the validator was last checkpointed at - The validator in question MUST be in the active validator set (have the status
VALIDATOR_STATUS.ACTIVE) - The proof MUST show that the validator has been slashed
If these requirements are met and the proofs are valid against a beacon block root given by beaconTimestamp, a checkpoint is started.
Effects:
- Sets
currentCheckpointTimestamptoblock.timestamp - Creates a new
Checkpoint:beaconBlockRoot: set to the current block's parent beacon block root, fetched by querying the EIP-4788 oracle usingblock.timestampas input.proofsRemaining: set to the current value ofactiveValidatorCountpodBalanceGwei: set to the pod's native ETH balance, minus any balance already accounted for in previous checkpointsbalanceDeltasGwei: set to 0 initially
Requirements:
- Pause status MUST NOT be set:
PAUSED_START_CHECKPOINT - Pause status MUST NOT be set:
PAUSED_VERIFY_STALE_BALANCE - A checkpoint MUST NOT be active (
currentCheckpointTimestamp == 0) - The last checkpoint completed MUST NOT be the current block
- For the validator given by
proof.validatorFields:beaconTimestampMUST be greater thanvalidatorInfo.lastCheckpointedAtvalidatorInfo.statusMUST beVALIDATOR_STATUS.ACTIVEproof.validatorFieldsMUST show that the validator is slashed
stateRootProofMUST verify abeaconStateRootagainst thebeaconBlockRootreturned from the EIP-4788 oracle- The
ValidatorProofMUST contain a valid merkle proof of the correspondingvalidatorFieldsunder thebeaconStateRootatvalidatorInfo.validatorIndex
Methods for interacting with predeploys introduced in Pectra. See supplemental docs here:
/// SEE FULL METHOD DOCS IN IEigenPod.sol
function requestConsolidation(
ConsolidationRequest[] calldata requests
) external payable onlyOwnerOrProofSubmitter;
/**
* @param srcPubkey the pubkey of the source validator for the consolidation
* @param targetPubkey the pubkey of the target validator for the consolidation
* @dev Note that if srcPubkey == targetPubkey, this is a "switch request," and will
* change the validator's withdrawal credential type from 0x01 to 0x02.
* For more notes on usage, see `requestConsolidation`
*/
struct ConsolidationRequest {
bytes srcPubkey;
bytes targetPubkey;
}
/// @notice Returns the fee required to add a consolidation request to the EIP-7251 predeploy this block.
/// @dev Note that the predeploy updates its fee every block according to https://eips.ethereum.org/EIPS/eip-7251#fee-calculation
/// Consider overestimating the amount sent to ensure the fee does not update before your transaction.
function getConsolidationRequestFee() external view returns (uint256);This method allows the pod owner or proof submitter to submit validator consolidation requests via the EIP-7521 predeploy. Consolidation requests come in two forms:
- "Switch requests" will switch a validator's withdrawal credentials from the 0x01 "eth1" prefix to the 0x02 "compounding" prefix. For a switch request,
srcPubkey == targetPubkey. - Standard requests will consolidate a source validator's balance into a target 0x02 validator. For a standard request,
srcPubkey != targetPubkey.
In order to initiate a consolidation request (basic how-to guide here):
- The predeploy requires a fee for each request. The current fee for the block can be queried using
getConsolidationRequestFee. This should be multiplied for each request in the passed-inrequestsarray and provided asmsg.value. The predeploy updates its fee each block depending on how many consolidation requests are queued vs how many are processed.- Note that any unused fee is transferred back to
msg.senderat the end of this method.
- Note that any unused fee is transferred back to
- The
targetvalidator MUST have verified withdrawal credentials (getValidatorStatusreturnsACTIVE) - For standard requests, the
targetvalidator MUST have 0x02 withdrawal credentials on the beacon chain.
When a standard consolidation is completed on the beacon chain, the source validator's balance will be transferred to the target validator. For all intents and purposes, the source validator will appear to have "exited" - its exit epoch and withdrawable epoch are set, and its balance drops to zero. When processed by a checkpoint, this 0 balance will cause the source validator to be marked as WITHDRAWN, exempting it from future checkpoint proofs.
Note that the beacon chain may "skip" a consolidation request for many reasons. This skip is inherently invisible to the EigenPod. See the process_consolidation_request spec for a complete list of conditions that may cause a request to be skipped.
Effects:
- Queue each
requestin the EIP 7521 predeploy, sending the current consolidation request fee each time. - If excess
msg.valuewas provided, transfer the remainder back tomsg.sender
Requirements:
- Caller MUST be EITHER the Pod Owner or Proof Submitter
- Pause status MUST NOT be set:
PAUSED_CONSOLIDATIONS msg.valueMUST be at leastgetConsolidationRequestFee() * requests.length- For each
requestinrequests:request.srcPubkeyandrequest.targetPubkeyMUST have a length of 48request.targetPubkeyMUST correspond to a validator whose withdrawal credentials are proven to point at the pod (VALIDATOR_STATUS.ACTIVE)
- If excess
msg.valuewas provided, the transfer of the excess back tomsg.senderMUST succeed.
/// SEE FULL METHOD DOCS IN IEigenPod.sol
function requestWithdrawal(
WithdrawalRequest[] calldata requests
) external payable onlyOwnerOrProofSubmitter;
/**
* @param pubkey the pubkey of the validator to withdraw from
* @param amountGwei the amount (in gwei) to withdraw from the beacon chain to the pod
* @dev Note that if amountGwei == 0, this is a "full exit request," and will fully exit
* the validator to the pod.
* For more notes on usage, see `requestWithdrawal`
*/
struct WithdrawalRequest {
bytes pubkey;
uint64 amountGwei;
}
/// @notice Returns the current fee required to add a withdrawal request to the EIP-7002 predeploy.
/// @dev Note that the predeploy updates its fee every block according to https://eips.ethereum.org/EIPS/eip-7002#fee-update-rule
/// Consider overestimating the amount sent to ensure the fee does not update before your transaction.
function getWithdrawalRequestFee() external view returns (uint256);This method allows the pod owner or proof submitter to submit validator withdrawal requests via the EIP-7002 predeploy. Withdrawal requests come in two forms:
- "Full withdrawals" will completely exit a validator from the beacon chain. For a full withdrawal,
request.amountGwei == 0. - "Partial withdrawals" will exit a portion of a validator's balance from the beacon chain, down to 32 ETH. Any amount requested that would bring a validator's balance below 32 ETH is ignored.
In order to initiate a withdrawal request:
- The
verifyWithdrawalCredentialsfunction must be called to prove the validator exists within the pod - The predeploy requires a fee for each request. The current fee for the block can be queried using
getWithdrawalRequestFee. This should be multiplied for each request in the passed-inrequestsarray and provided asmsg.value. The predeploy updates its fee each block depending on how many withdrawal requests are queued vs how many are processed.- Note that any unused fee is transferred back to
msg.senderat the end of this method.
- Note that any unused fee is transferred back to
- For partial withdrawals, note that the beacon chain will only process these if the validator has 0x02 withdrawal credentials.
When a withdrawal request is completed on the beacon chain, the requested amount will be withdrawn from the validator's beacon chain balance and exited to the pod. Note that at no point can a partial withdrawal request cause a validator to drop below 32 ETH balance. This is especially important if you're planning to use requests to track withdrawn balance: it's possible that the amount you request is not equal to the amount withdrawn. For example, my validator has 33 ETH and I request a withdrawal of 2 ETH, the withdrawal request will only result in 1 ETH being withdrawn.
Note that the beacon chain may "skip" a withdrawal request for many reasons. This skip is inherently invisible to the EigenPod. See the process_withdrawal_request spec for a complete list of conditions that may cause a request to be skipped.
Finally, note that if a validator has pending partial/full withdrawals initiated via this predeploy, it becomes ineligible to be the source validator in a standard consolidation until those requests are completed. There is no such requirement for target validators.
Effects:
- Queue each
requestin the EIP 7002 predeploy, sending the current withdrawal request fee each time. - If excess
msg.valuewas provided, transfer the remainder back tomsg.sender
Requirements:
- Caller MUST be EITHER the Pod Owner or Proof Submitter
- Pause status MUST NOT be set:
PAUSED_WITHDRAWAL_REQUESTS msg.valueMUST be at leastgetWithdrawalRequestFee() * requests.length- For each
requestinrequests:- The validator MUST have been proven to the pod
request.pubkeyMUST correspond to a validator whose withdrawal credentials are proven to point at the pod (VALIDATOR_STATUS.ACTIVE)
- If excess
msg.valuewas provided, the transfer of the excess back tomsg.senderMUST succeed.
Minor methods that do not fit well into other sections:
function setProofSubmitter(address newProofSubmitter) external onlyEigenPodOwnerAllows the Pod Owner to update the Proof Submitter address for the EigenPod. The Proof Submitter is a secondary address that can call various validator and proof-related EigenPod methods, but has no permissions outside of the EigenPod contract (e.g. this address cannot manage delegation or queue withdrawals via the DelegationManager).
This method/role is intended to allow the Pod Owner to create a hot wallet to perform validator and proof-related tasks without exposing access to funds.
If set, EITHER the Pod Owner OR Proof Submitter may call:
verifyWithdrawalCredentialsstartCheckpointrequestConsolidationrequestWithdrawal
The Pod Owner can call this with newProofSubmitter == 0 to remove the current Proof Submitter. If there is no designated Proof Submitter, ONLY the Pod Owner can call the above methods.
Effects:
- Updates
proofSubmittertonewProofSubmitter
Requirements:
- Caller MUST be the Pod Owner
function stake(
bytes calldata pubkey,
bytes calldata signature,
bytes32 depositDataRoot
)
external
payable
onlyEigenPodManagerHandles the call to the beacon chain deposit contract. Only called via EigenPodManager.stake.
Note that this method only supports deposits to 0x01 withdrawal credentials. For 0x02 credentials, pod owners should interact with the deposit contract directly.
Effects:
- Deposits 32 ETH into the beacon chain deposit contract, and provides the pod's address as the deposit's withdrawal credentials
Requirements:
- Caller MUST be the
EigenPodManager - Call value MUST be 32 ETH
- Deposit contract
depositmethod MUST succeed given the providedpubkey,signature, anddepositDataRoot
function withdrawRestakedBeaconChainETH(
address recipient,
uint256 amountWei
)
external
onlyEigenPodManagerThe EigenPodManager calls this method when withdrawing a Pod Owner's shares as tokens (native ETH). The input amountWei is converted to Gwei and subtracted from withdrawableRestakedExecutionLayerGwei, which tracks native ETH balance that has been accounted for in a checkpoint (see Checkpointing Validators).
If the EigenPod does not have amountWei available to transfer, this method will revert.
Note that if amountWei is not a whole gwei amount, the sub-gwei portion is truncated and may not be recoverable.
Effects:
- Decreases the pod's
withdrawableRestakedExecutionLayerGweibyamountWei / GWEI_TO_WEI - Converts
amountWeito gwei, then sends that amount torecipient
Requirements:
amountWei / GWEI_TO_WEIMUST NOT be greater than the provenwithdrawableRestakedExecutionLayerGwei- Pod MUST have at least
amountWeiETH balance recipientMUST NOT revert when transferredamountWei
function recoverTokens(
IERC20[] memory tokenList,
uint256[] memory amountsToWithdraw,
address recipient
)
external
onlyEigenPodOwner
onlyWhenNotPaused(PAUSED_NON_PROOF_WITHDRAWALS)Allows the Pod Owner to rescue ERC20 tokens accidentally sent to the EigenPod.
Effects:
- Calls
transferon each of the ERC20's intokenList, sending the correspondingamountsToWithdrawto therecipient
Requirements:
- Caller MUST be the Pod Owner
- Pause status MUST NOT be set:
PAUSED_NON_PROOF_WITHDRAWALS tokenListandamountsToWithdrawMUST have equal lengths