code-423n4 / 2024-06-size-findings

0 stars 0 forks source link

borrower that get matched through `liquidateWithReplacement()` will unfairly get more Cash than he should. #327

Closed howlbot-integration[bot] closed 2 months ago

howlbot-integration[bot] commented 2 months ago

Lines of code

https://github.com/code-423n4/2024-06-size/blob/8850e25fb088898e9cf86f9be1c401ad155bea86/src/libraries/actions/LiquidateWithReplacement.sol#L146-L162

Vulnerability details

Impact

the new borrower that will get matched in the liquidateWithReplacement() will not be charged any swapFees

N.B According to whitepaper, a fix swapfee (0.5%) charged annually (apr) on all transaction that cause the exchange of cash vs credit and paid by cash recipient

This will lead to borrowers that get matched through the liquidateWithReplacement() call to unfairly get more cashamountIn for the creditAmountOut they will incure, and will also cause the protocol to lose yield on that newly created position.

Proof Of Concept

The size protocol offers the pssibility to seamlessly liquidate an unhealthy borrower and instead of giving the cash directly to the lender, the protcol enables one of its bots to replace the unhealthy borrower with a new borrow to enable the protocol to earn extra cash. The code snippet that handles how much cash the newlty matched borrower wil recieved is the follwoing https://github.com/code-423n4/2024-06-size/blob/8850e25fb088898e9cf86f9be1c401ad155bea86/src/libraries/actions/LiquidateWithReplacement.sol#L146-L162

        issuanceValue = Math.mulDivDown(debtPositionCopy.futureValue, PERCENT, PERCENT + ratePerTenor);
        liquidatorProfitBorrowToken = debtPositionCopy.futureValue - issuanceValue;

        debtPosition.borrower = params.borrower;
        debtPosition.futureValue = debtPositionCopy.futureValue;
        debtPosition.liquidityIndexAtRepayment = 0;

        emit Events.UpdateDebtPosition(
            params.debtPositionId,
            debtPosition.borrower,
            debtPosition.futureValue,
            debtPosition.liquidityIndexAtRepayment
        );

        state.data.debtToken.mint(params.borrower, debtPosition.futureValue);
        state.data.borrowAToken.transferFrom(address(this), params.borrower, issuanceValue);
        state.data.borrowAToken.transferFrom(address(this), state.feeConfig.feeRecipient, liquidatorProfitBorrowToken);
    }

As we can see from the code snippet the newly matched borrower will recieve issuanceValue, which doesn't include any swapFee, leading to a loss of yield for the protocol and an unfair scenarios for other borrowers who get mathed through buyCreditMarket() and sellCreditMarket()

Recomendation

Include the swapFees when computing how much tokens to send to the borrower and to the feeRecipient

-        issuanceValue = Math.mulDivDown(debtPositionCopy.futureValue, PERCENT, PERCENT + ratePerTenor);
+#@audit issuanceValue doesn't include the fees and will be sen to feeRecipient when deducting from futureValue
+        (issuanceValue,_) =state.getCashAmountOut({
+            creditAmountIn: debtPositionCopy.futureValue,
+            maxCredit: debtPositionCopy.futureValue,
+            ratePerTenor: ratePerTenor,
+            tenor: tenor
+        })
        liquidatorProfitBorrowToken = debtPositionCopy.futureValue - issuanceValue;

        debtPosition.borrower = params.borrower;
        debtPosition.futureValue = debtPositionCopy.futureValue;
        debtPosition.liquidityIndexAtRepayment = 0;

        emit Events.UpdateDebtPosition(
            params.debtPositionId,
            debtPosition.borrower,
            debtPosition.futureValue,
            debtPosition.liquidityIndexAtRepayment
        );

        state.data.debtToken.mint(params.borrower, debtPosition.futureValue);
        state.data.borrowAToken.transferFrom(address(this), params.borrower, issuanceValue);
        state.data.borrowAToken.transferFrom(address(this), state.feeConfig.feeRecipient, liquidatorProfitBorrowToken);
    }

Assessed type

Context

c4-judge commented 2 months ago

hansfriese marked the issue as satisfactory