In LendingPool::withdrawNFT
-> a user is able to withdraw their nft even if the withdraw would leave their account not only liquidatable but worse, insolvent.
A user is able to deposit their NFT and use the NFT as collateral, and with that collateral, borrow from the protocol.
The protocol imposes a limit on the amount the user can borrow by implementing liquidityThreshold
-> which is 80%.
If a user is trying to withdraw their NFT, they should only be able to do so if the amount of their collateral after withdrawing the NFT will not leave their total collateral beneath the liquidation threshold -> but that is not implemented correctly and a user can withdraw their NFT even if it leaves them under the liquidation threshold.
The problem is that the liquidationThreshold
is applied to the users total debt, instead of their collateral value. This means that the amount of debt they have is multiplied by 80%, which results in a smaller value. Using this smaller value, the wrong check allows the user to withdraw their nft even when they shouldnt be able to.
Lets assume:
User has 2 NFT's as collateral
Nft 1 = 150 ; NFT 2 = 50
User has 160 total debt -> the liquidity threshold is 80% , so the user is able to borrow up to 80% of their 200 in collateral, which is 160
User initiates withdrawNFT
-> withdrawing NFT 2 which is worth 50
The function operates as follows:
collateralValue
. - nftValue
. = 150
userDebt
. * liquidationThreshold
. ->. 160 * * 80%. = 128
The if
statement reverts only if 150 < 128
The function does not revert, and the user withdraws their NFT
This leaves the user total collateral equal to 150
total debt = 160
The user is now not only above the liquidation threshold and liquidatable, but they are also insolvent by 10. This is bad debt for the protocol and an immediate loss the protocol has to pay.
The protocol correctly applies liquidationThreshold
to the collateralValue
here in this function which calculates a users health factor. It then compares the collateral value AFTER the liquidationThreshold
has been applied to the collateral, against the total debt a user has. This is how the liquidationThreshold
should be applied, to the collateral.
A user is able to deposit NFT's, borrow up to the maximum amount that the liquidationThreshold
allows them. Then withdraw their nft, leaving their position insolvent.
The user leaves with a profit (in the above scenario, the user ends up with 210 in value, when initially starting with 200).
The protocol endures financial loss due to the bad debt.
The issue gets more severe as the values of NFT scale up.
Manual Review
In withdrawNFT
apply the liquidationThreshold
to the users total collateralValue
.
IF that was done correctly, the if
statement would have checked the following:
150 * 80% = 120. (collateral - nft)
160 (total debt)
if
120 < 160 -> revert
The function would revert and the user would not be able to withdraw their nft.
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.