# MainVault.rebalanceXChain doesn't check that savedTotalUnderlying >= reservedFunds

MainVault.rebalanceXChain sends funds to xController without ensuring remaining balance covers reservedFunds, so a loss during cross-chain transfer leaves users unable to withdraw.

- Payout: Contest reward
- Severity: medium
- Program: Derby
- Credited: rvierdiiev
- Disclosed: 2022-10-17
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2023-01-derby-judging/issues/107
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-01-derby%3AM-37
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** MainVault cross-chain rebalance in Derby yield optimiser
**Root cause:** rebalanceXChain pulls and sends amountToSendXChain based only on getVaultBalance(), never verifying savedTotalUnderlying minus sent amount still covers reservedFunds, so a loss between setXChainAllocation and rebalanceXChain drains the reserved buffer.
**Steps:**
1. xController calls setXChainAllocation, setting state=SendingFundsXChain and amountToSendXChain=800 while reservedFunds=200 and totalUnderlying=1000
2. Between that call and rebalanceXChain, a loss occurs so totalUnderlying drops to 800
3. Call `rebalanceXChain(_slippage,_relayerFee)`; it pulls funds and sends 800 to xController
4. Vault balance is now 0, below reservedFunds=200; settleReservedFunds leaves users unable to withdraw
**How found:** Code review of rebalanceXChain's balance checks versus the reservedFunds accounting updated in setXChainAllocation; noticed the function only compares amountToSendXChain to getVaultBalance() and never to reservedFunds.
**Exploitation:** Precondition: a loss in provider underlying between setXChainAllocation and rebalanceXChain. The function sends the full amountToSendXChain without reserving the withdrawal buffer, leaving reservedFunds uncovered and breaking user withdrawals.
**Insight:** When a contract maintains a reserved/withdrawal buffer, audit every outflow path for a check that post-transfer balance still covers the reserve; here the sibling rebalance() pulls reservedFunds after depositing but rebalanceXChain omits the equivalent guard.
