borrowing::withdraw() at a loss will increase downside protected and misscalculate option fees
Summary
borrowing::withdraw() at a loss increases the downside protected, here. Then, whenever someone deposits to borrow, option fees are added to the cumulative rate, which is calculated based on omniChainData.totalCdsDepositedAmountWithOptionFees - omniChainData.downsideProtected. As it calculates the rate based on a decreased amount, but the rate is then multiplied by the full amount checkpointed at the cds deposit (normalized), extra option fees will be given to cds depositors.
Root Cause
In CDS.sol:680, downside protected is subtracted from totalCdsDepositedAmountWithOptionFees.
Internal pre-conditions
None.
External pre-conditions
None.
Attack Path
- Consider a eth price of 1000 USD / ETH.
- Cds deposits 1000 USDa.
- Borrower deposits 1 ETH and borrows 800 USDa.
- Price drops to 900 USD / ETH.
- Borrower withdraws, setting downside protected to 100. Now, assume that the first borrower did not charge option fees, for simplicity, and the rate is 1 (ignore precision).
- Another borrower deposits, charging 50 USD option fees given by
uint128 percentageChange = _fees / netCDSPoolValue; = 50 / (1000 - 100) = 50 / 900.currentCumulativeRate = 1 + 50 / 900 = 950 / 900. Here is the downsideProtection subtracted and the percentage calculation here. - Cds depositor withdraws (after borrower withdraws or there is enough ratio), calculating option fees given by
cdsDepositDetails.normalizedAmount omniChainData.lastCumulativeRate) - cdsDepositDetails.depositedAmount, wherecdsDepositDetails.normalizedAmount = 1000 / 1 = 1000, yielding1000 / 1 950 / 900 - 1000 = 55.555, more than the 50 USD option fees.
Impact
Cds depositors gets more option fees than it should, stealing USDa from other users in the protocol, such as other cds depositors that will not be able to withdraw due to lack of liquidity.
PoC
See the calculations above.
Mitigation
The downside protection must not be taken into account when calculating the option fees.