Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Valid

Missing price freshness validation in `LendingPool::getNFTPrice` leads to acceptance of outdated price values

Summary

The LendingPool contract fails to validate the freshness of NFT price feeds when performing critical lending operations. While it retrieves both price and timestamp from RAACHousePrices::getLatestPrice, it only checks if the price is non-zero, ignoring the lastUpdateTimestamp. This allows stale prices to be used in critical operations like borrowing, liquidations, and collateral valuations.

Vulnerability Details

The LendingPool::getNFTPrice function retrieves price data but doesn't validate the timestamp:

function getNFTPrice(uint256 tokenId) public view returns (uint256) {
(uint256 price, uint256 lastUpdateTimestamp) = priceOracle.getLatestPrice(tokenId);
if (price == 0) revert InvalidNFTPrice();
return price; // @audit no staleness check on lastUpdateTimestamp
}

This function is used in critical operations that affect the protocol's solvency, particularly:

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); // Uses potentially stale price
totalValue += price;
}
return totalValue;
}

Proof of Concept

  • Oracle updates NFT price:

// t=1000
priceOracle.setHousePrice(1, 100 ether);
  • Price becomes stale but getUserCollateral still uses it:

// t=1000 + 30 days (price is 30 days old)
uint256 collateralValue = lendingPool.getUserCollateralValue(user);
// Returns 100 ether despite price being 30 days old
lendingPool.borrow(60 ether); // Borrows against stale collateral value

Impact

Stale prices accepted in critical lending functions:

  • borrow() - Could allow borrowing against outdated collateral values

  • withdrawNFT() - May permit withdrawals based on stale valuations

  • initiateLiquidation() - Could delay/prevent liquidations if using outdated prices

Tools Used

Manual review

Recommendations

Add staleness checks in getNFTPrice:

function getNFTPrice(uint256 tokenId) public view returns (uint256) {
(uint256 price, uint256 lastUpdateTimestamp) = priceOracle.getLatestPrice(tokenId);
if (price == 0) revert InvalidNFTPrice();
+ if (block.timestamp - lastUpdateTimestamp > 24 hours) revert StalePriceOracle(); //@audit 24 hours could be adjusted based on protocol needs
return price;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

LendingPool::getNFTPrice or getPrimeRate doesn't validate timestamp staleness despite claiming to, allowing users to exploit outdated collateral values during price drops

inallhonesty Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

LendingPool::getNFTPrice or getPrimeRate doesn't validate timestamp staleness despite claiming to, allowing users to exploit outdated collateral values during price drops

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.