Summary
The function getNFTPrice wrongly reverts when the price is at 0
Vulnerability Details
Observe the following code
* @notice Gets the current price of an NFT from the oracle
* @param tokenId The token ID of the NFT
* @return The price of the NFT
*
* Checks if the price is stale
*/
function getNFTPrice(uint256 tokenId) public view returns (uint256) {
(uint256 price, uint256 lastUpdateTimestamp) = priceOracle.getLatestPrice(tokenId);
if (price == 0) revert InvalidNFTPrice();
return price;
}
The function getNFTPrice uses an oracle to fetch the price of the real estate NFT asset. Upon closer inspection you can see that the price is casted as an uint256. This can present a major issue as this NFT is representing a real world asset, not an ERC20 token.
This means that much like real estate, it is entirely pausible for the NFT to have a price at or even below 0. This can happen maliciously by taking out real world loans on the NFT or in a real estate market crash
[link](https://www.quickenloans.com/learn/negative-equity#:~:text=Negative equity is when your,home or missing mortgage payments.)
In such a case where the price of the NFT reaches 0, it will be impossible to liquidate the owner of that NFT shown here
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/pools/LendingPool/LendingPool.sol#L556-L572
* @notice Gets the total collateral value of a user
* @param userAddress The address of the user
* @return The total collateral value
*/
function getUserCollateralValue(address userAddress) public view returns (uint256) {
UserData storage user = userData[userAddress];
uint256 totalValue = 0;
for (uint256 i = 0; i < user.nftTokenIds.length; i++) {
uint256 tokenId = user.nftTokenIds[i];
uint256 price = getNFTPrice(tokenId);
totalValue += price;
}
return totalValue;
}
Here we can see that the getNFTprice is called in the function getUserCollateralValue. This function is called during the function CalculateHealthfactor which is then called in the function initiateLiquidation
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/pools/LendingPool/LendingPool.sol#L447-L463
function initiateLiquidation(address userAddress) external nonReentrant whenNotPaused {
if (isUnderLiquidation[userAddress]) revert UserAlreadyUnderLiquidation();
ReserveLibrary.updateReserveState(reserve, rateData);
UserData storage user = userData[userAddress];
uint256 healthFactor = calculateHealthFactor(userAddress);
if (healthFactor >= healthFactorLiquidationThreshold) revert HealthFactorTooLow();
isUnderLiquidation[userAddress] = true;
liquidationStartTime[userAddress] = block.timestamp;
emit LiquidationInitiated(msg.sender, userAddress);
}
Impact
The protocol falsely assumes that the price of real estate will always remain positive and will not be able to liquidate NFT assets with negative or 0 value, and thus failing to liquidate bad debt.
Tools Used
Manual Review
Recommendations
allow the oracle to report negative and 0 prices so that negative value assets can be liquidated