The user overpays the USDA amount for downside protection while withdrawing
Summary
The protocol provides >= 20% downside protection on the collateral depending on the volatility of collateral. However, users should pay USDA amount for downside protection, while withdrawing and protocol doesn't cover downside protection.
Root Cause
In the BorrowLib.sol withdraw() function, users return usda and receive collateral. Although protocol covers 20% downside protection, users should return amount of downsideProtected.
Let's see line 867 and line 878, and calculate total usda amount users should hold: burnValue + (borrowerDebt - depositDetail.borrowedAmount) + discountedCollateral = depositDetail.borrowedAmount - discountedCollateral + (borrowerDebt - depositDetail.borrowedAmount) + discountedCollateral = borrowerDebt.
As result, users should return full debt(borrowerDebt) and the protocol doesn't cover downsideProtected.
function withdraw(
ITreasury.DepositDetails memory depositDetail,
IBorrowing.BorrowWithdraw_Params memory params,
IBorrowing.Interfaces memory interfaces
) external returns (IBorrowing.BorrowWithdraw_Result memory) {
...
// Calculate the USDa to burn
867 uint256 burnValue = depositDetail.borrowedAmount - discountedCollateral;
// Burn the USDa from the Borrower
bool success = interfaces.usda.burnFromUser(msg.sender, burnValue);
if (!success) revert IBorrowing.Borrow_BurnFailed();
...
//Transfer the remaining USDa to the treasury
878 bool transfer = interfaces.usda.transferFrom(
msg.sender,
address(interfaces.treasury),
(borrowerDebt - depositDetail.borrowedAmount) + discountedCollateral
);
if (!transfer) revert IBorrowing.Borrow_USDaTransferFailed();
...
}Internal pre-conditions
External pre-conditions
Attack Path
Impact
Users should return usda amount for downside protection and the protocol doesn't cover downside protection.
Mitigation
Deduct downside protection.
function withdraw(
ITreasury.DepositDetails memory depositDetail,
IBorrowing.BorrowWithdraw_Params memory params,
IBorrowing.Interfaces memory interfaces
) external returns (IBorrowing.BorrowWithdraw_Result memory) {
...
// Calculate the USDa to burn
uint256 burnValue = depositDetail.borrowedAmount - discountedCollateral;
// Burn the USDa from the Borrower
bool success = interfaces.usda.burnFromUser(msg.sender, burnValue);
if (!success) revert IBorrowing.Borrow_BurnFailed();
...
//Transfer the remaining USDa to the treasury
bool transfer = interfaces.usda.transferFrom(
msg.sender,
address(interfaces.treasury),
- (borrowerDebt - depositDetail.borrowedAmount) + discountedCollateral
+ (borrowerDebt - depositDetail.borrowedAmount - downsideProtected) + discountedCollateral
);
if (!transfer) revert IBorrowing.Borrow_USDaTransferFailed();
...
}