# Exchange rate rounding allows users to get more shares more than intended

Opal Omnipool rounds exchange rate down on both deposits and withdrawals, letting a depositor receive excess shares and dilute prior holders.

- Payout: Contest reward
- Severity: medium
- Program: Opal
- Credited: J4X98
- Disclosed: 2024-03-30
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_opal_feb2024.pdf#3.3.12
- Library page: https://bugbountylibrary.com/r/cantina%3Aopal_feb2024%3A3.3.12
- Tags: domain:web3 impact:funds-loss kind:contest platform:cantina sev:medium tech:solidity technique:source-review vuln:business-logic vuln:precision-loss vuln:share-inflation

**Target:** Omnipool.sol ERC4626-style share vault
**Root cause:** _exchangeRate() always uses divDown; correct for withdrawals but on deposits a rounded-down rate inflates sharesReceived = amountIn/exchangeRate.
**Steps:**
1. Pool has totalUnderlying=110, lpSupply=100 after 10% yield.
2. Attacker deposits amountIn=100.
3. exchangeRate = divDown(110/100) = 1.
4. sharesReceived = 100/1 = 100 shares instead of ~90.9.
5. Attacker withdraws 100 shares, receiving ~110 underlying, stealing ~5 from prior depositor.
**How found:** Code review of _exchangeRate() rounding direction; noticed divDown is safe for withdraw (shares*rate) but unsafe for deposit (amountIn/rate), then built a numeric example.
**Exploitation:** Deposit at a moment when totalUnderlying/lpSupply rounds down significantly (small share supply or favorable ratio), receive inflated shares, then withdraw to capture value from existing holders.
**Insight:** In ERC4626-style vaults, check rounding direction separately per operation: rate used as divisor on deposit must round up, as multiplier on withdraw must round down; test with small share supplies where integer division truncates heavily.
