LOW severity: [EIG-7] Direct loss of user principal funds when processing a full withdrawal of penalised validator. **Severity:** Critical

**Path:** EigenPod.sol:_processFullWithdrawal#L652-L706

**Description:**  

The EigenPod allows users to proof withdrawals from the beacon chain for connected validators, so the ETH can then be withdrawn from the EigenPod. The function `_processFullWithdrawal` is called...

Vulnerable code:
function _processFullWithdrawal(
    uint40 validatorIndex,
    bytes32 validatorPubkeyHash,
    uint64 withdrawalHappenedTimestamp,
    address recipient,
    uint64 withdrawalAmountGwei,
    ValidatorInfo memory validatorInfo
) internal returns (VerifiedWithdrawal memory) {
    VerifiedWithdrawal memory verifiedWithdrawal;
    uint256 withdrawalAmountWei;

    uint256 currentValidatorRestakedBalanceWei = validatorInfo.restakedBalanceGwei * GWEI_TO_WEI;

    /**
      * If the validator is already withdrawn and additional deposits are made, they will be automatically withdrawn
      * in the beacon chain as a full withdrawal.  Thus such a validator can prove another full withdrawal, and
      * withdraw that ETH via the queuedWithdrawal flow in the strategy manager.
      */
        // if the withdrawal amount is greater than the MAX_VALIDATOR_BALANCE_GWEI (i.e. the max amount restaked on EigenLayer, per ETH validator)
    uint64 maxRestakedBalanceGwei = _calculateRestakedBalanceGwei(MAX_VALIDATOR_BALANCE_GWEI);
    if (withdrawalAmountGwei > maxRestakedBalanceGwei) {
        // then the excess is immediately withdrawable
        verifiedWithdrawal.amountToSend =
            uint256(withdrawalAmountGwei - maxRestakedBalanceGwei) *
            uint256(GWEI_TO_WEI);
        // and the extra execution layer ETH in the contract is MAX_VALIDATOR_BALANCE_GWEI, which must be withdrawn through EigenLayer's normal withdrawal process
        withdrawableRestakedExecutionLayerGwei += maxRestakedBalanceGwei;
        withdrawalAmountWei = maxRestakedBalanceGwei * GWEI_TO_WEI;
    } else {
        // otherwise, just use the full withdrawal amount to continue to "back" the podOwner's remaining shares in EigenLayer
        // (i.e. none is instantly withdrawable)
        withdrawalAmountGwei = _calculateRestakedBalanceGwei(withdrawalAmountGwei);
        withdrawableRestakedExecutionLayerGwei += withdrawalAmountGwei;
        withdrawalAmountWei = withdrawalAmountGwei * GWEI_TO_WEI;
    }
    // if the amount being withdrawn is not equal to the current accounted for validator balance, an update must be made
    if (currentValidatorRestakedBalanceWei != withdrawalAmountWei) {
        verifiedWithdrawal.sharesDelta = _calculateSharesDelta({
            newAmountWei: withdrawalAmountWei,
            currentAmountWei: currentValidatorRestakedBalanceWei
        });
    }


    // now that the validator has been proven to be withdrawn, we can set their restaked balance to 0
    validatorInfo.restakedBalanceGwei = 0;
    validatorInfo.status = VALIDATOR_STATUS.WITHDRAWN;
    validatorInfo.mostRecentBalanceUpdateTimestamp = withdrawalHappenedTimestamp;

    _validatorPubkeyHashToInfo[validatorPubkeyHash] = validatorInfo;

    emit FullWithdrawalRedeemed(validatorIndex, withdrawalHappenedTimestamp, recipient, withdrawalAmountGwei);

    return verifiedWithdrawal;
}

function _calculateRestakedBalanceGwei(uint64 amountGwei) internal view returns (uint64) {
    if (amountGwei <= RESTAKED_BALANCE_OFFSET_GWEI) {
        return 0;
    }
    uint64 effectiveBalanceGwei = uint64(((amountGwei - RESTAKED_BALANCE_OFFSET_GWEI) / GWEI_TO_WEI) * GWEI_TO_WEI);
    return uint64(MathUpgradeable.min(MAX_VALIDATOR_BALANCE_GWEI, effectiveBalanceGwei));
}