# previewWithdraw calculates shares wrongly

Rubicon BathToken previewWithdraw subtracted the fee instead of adding it, returning too few shares and making withdraw() always revert.

- Payout: Contest reward
- Severity: medium
- Program: Rubicon
- Credited: kenzo
- Disclosed: 2022-08-01
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-05-rubicon-findings/issues/140
- Library page: https://bugbountylibrary.com/r/c4%3A2022-05-rubicon%3AM-11
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:fee-bypass

**Target:** ERC-4626 vault (BathToken) previewWithdraw/withdraw
**Root cause:** previewWithdraw computes shares from assets minus fee, but a user withdrawing a fixed asset amount must burn more shares to cover the fee, not fewer.
**Steps:**
1. Read BathToken.previewWithdraw: `_fee = assets.mul(feeBPS).div(10000); amountWithdrawn = assets.sub(_fee); shares = convertToShares(amountWithdrawn)`
2. Observe shares returned is smaller than needed to yield `assets` after fee deduction
3. Call `withdraw(assets,to,from)`: it takes expectedShares=previewWithdraw(assets), calls _withdraw which deducts fee, so assetsReceived < assets
4. require(assetsReceived >= assets) fails, reverting every withdraw() call
5. Fix: shares = convertToShares(assets.add(assets.mul(feeBPS).div(10000-feeBPS)))
**How found:** Code review of the ERC-4626 preview/withdraw accounting, noticing the fee was subtracted rather than grossed up, then tracing the revert in withdraw().
**Insight:** In fee-on-withdraw vaults, previewWithdraw must gross up assets by fee/(1-fee) so the burned shares cover both payout and fee; subtracting the fee under-reports shares and breaks the withdraw invariant check.
