Borrower deposit, withdraw, deposit will reinit omniChainData.cdsPoolValue, getting profit stuck for cds depositors
Summary
BorrowLib::calculateRatio() sets omniChainData.cdsPoolValue to previousData.totalCDSPool + netPLCdsPool, disregarding any past value of omniChainData.cdsPoolValue, when the noOfDeposits is null. noOfDeposits is omniChainData.totalNoOfDepositIndices, which is increased and decrease on borrower deposit and withdraw, respectively. Hence, it's possible to make it null again while still having cds depositors and positive long contributions to omniChainData.cdsPoolValue.
Root Cause
In BorrowLib:199, omniChainData.cdsPoolValue is overwritten.
Internal pre-conditions
None.
External pre-conditions
None.
Attack Path
- Consider a cds depositor of 1000 USDa and a ETH price of 1000 USD / ETH.
- Borrower deposits 1 ETH and borrows 800 USDa (consider no option fees). Strike price is 1050 USD / ETH.
- Price goes up to 1050 USD / ETH.
- Consider that no interest on the debt accrued and the borrower withdraws.
omniChainData.cdsPoolValuewill increase to1000 + 1 (1050 - 1000) = 1050. The cumulative value in cds is updated, adding1 (1050 - 1000) / 1000 = 0.05. - Another borrower comes in, deposits 1 wei of ETH. Note that it will calculate the ratio, and
omniChainData.totalNoOfDepositIndicesis 0 now. So,previousData.cdsPoolValue = currentCDSPoolValue;is set, andcurrentCDSPoolValue = previousData.totalCDSPool + netPLCdsPool = 1000 + 0 = 1000.netPLCdsPool == 0because the price is 1050 now and has not changed. - Cds depositor withdraws (ignore 1 wei borrow, it's irrelevant), with a profit of
1000 * 0.05 = 50USD, butpreviousData.cdsPoolValueis 1000 USD, as the cds depositor tries to withdraw 1050 USD, it underflows.
Impact
Cds depositor can not withdraw 50 USDa profit (and whole deposit, until someone else deposits cds and this user steals cds from the next depositor).
PoC
See above explanation.
Mitigation
Consider track if there is pending net cds pool.