# Ether can stuck when an operators validators are removed due to an user front-running

Front-running a validator-removal withdrawal leaves residual ETH in the EigenPod below the scrape threshold, permanently stuck unless validators are re-added.

- Payout: Contest reward
- Severity: medium
- Program: Rio Network Core Protocol
- Credited: hash
- Disclosed: 2024-02-14
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2024-02-rio-network-core-protocol-judging/issues/45
- Library page: https://bugbountylibrary.com/r/sherlock%3A2024-02-rio-network-core-protocol%3AM-2
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:race-window vuln:business-logic vuln:dos

**Target:** RioLRTOperatorDelegator / EigenPod scrape logic
**Root cause:** queueOperatorStrategyExit withdraws the full EigenPod share balance, but a user withdrawal queued first reduces the amount actually swept, leaving a residual below MIN_EXCESS_FULL_WITHDRAWAL_ETH_FOR_SCRAPE that scrapeExcessFullWithdrawalETHFromEigenPod refuses to sweep.
**Steps:**
1. Deposit 64 ETH into an operator with two validators and rebalance into EigenLayer.
2. Verify validator credentials and process full exits so 64 ETH sits in the EigenPod.
3. User calls `requestWithdrawal(ETH_ADDRESS, 63 ether)` before the admin's cap-removal tx.
4. Admin removes validator cap, triggering `queueOperatorStrategyExit` for the full 64 ETH.
5. User's 63 ETH settles; 1 ETH remains idle in EigenPod.
6. `scrapeExcessFullWithdrawalETHFromEigenPod()` reverts with `INSUFFICIENT_EXCESS_FULL_WITHDRAWAL_ETH()` because 1 ETH <= 1 ETH threshold.
**How found:** Reviewer traced the full-withdrawal path in queueOperatorStrategyExit and noticed the residual amount after a partial user withdrawal falls under MIN_EXCESS_FULL_WITHDRAWAL_ETH_FOR_SCRAPE, blocking the only sweep function.
**Exploitation:** Attacker (or any user) front-runs the admin's validator-removal transaction with a withdrawal request sized just under the operator's full balance, so the queued full exit only sweeps the user's amount and leaves a dust remainder below the scrape threshold, permanently locking it.
**Insight:** When a contract sweeps 'the entire balance' but a user-triggered partial withdrawal can run first, check whether the leftover dust falls below a hardcoded minimum threshold that gates the recovery function — that gap is a permanent lock.
