The _repay function, and the finalizeLiquidation function both minus the DebtToken burned from the reserve asset scaled balance. This causes wrong accounting in which even though the borrower burns the entire DebtToken minted for them when they initially borrow the reserve asset, the user.scaledDebtBalance will never be 0. This will trap the borrower's NFT in the protocol.
In the _repay function, the borrower repays (with interest) the reserve asset loan that they had taken. It has the following lines of code:
As shown above, the user.scaledDebtBalance minuses the amountBurned from it.
In this function, the amountBurned is the number of DebtToken owned by the borrower that is burned according to the amount of loan they want to pay back.
The user.scaledDebtBalance is the amount of reserve asset + Interest that the user needs to pay back to fully pay the loan.
So, as you can see, the DebtToken burned is minused from the reserve asset + Interest which is wrong.
To drive this point home, lets look at the borrow function where the scaledDebtBalance is updated initially:
As shown above, the amount is the amount of reserve asset the borrower wants to borrow from the platform, and the scaledAmount is the amount / usageIndex which includes the current interest.
Hence, we can see that the amount of reserve asset + interest is added to the scaledDebtBalance in the borrow function, but the amount of DebtTokens burned is minused from the scaledDebtBalance in the _repay function.
The same bug is also present in the finalizeLiquidation function as shown below:
Since the number of DebtTokens is actually the 1:1 representation of the number of reserve asset borrowed initially without the interest, this error will ensure that the user.scaledDebtBalance will never reach 0 even if the borrower burns all of the DebtTokens they received. If it never reaches 0, the borrower will never be able to withdraw their NFT they transferred as collateral.
To showcase this, imagine a situation where a borrower has burned all of their DebtTokens, but the scaledDebtBalance still has some value in it. When they call the withdrawNFT function, it will trigger the following code snippet:
In our hypothetical situation, the userDebt will be the interest amount left after removing all the DebtToken burned. Let's say that the borrower only has 1 NFT as collateral. So the comparison shown above will be as follows:
The function will always revert due to the amount leftover in the scaledDebtBalance.
The borrower will never be able to pay back the debt because they will not have any more DebtTokens, and they will not be able to remove their real estate NFT without clearing the debt.
Manual Review
Since the scaledDebtBalance increases over time because of the usageIndex, I am not sure how to implement this correctly. Currently, the scaledDebtBalance only stores the interest when they initially borrow the tokens, but this will not hold true with time.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.