Core Contracts

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

Missing Stale-Price Validation in the getNFTPrice Function

Summary

In the LendingPool contract, specifically within the getNFTPrice function, the code comments imply a verification step to determine whether the NFT price is outdated. However, in practice, the contract only checks if the price is zero, disregarding the lastUpdateTimestamp returned by the oracle. This creates a risk of relying on stale price data if the oracle ceases to update its values. Consequently, the system may continue to trust an NFT’s valuation even when the underlying market information is no longer current, potentially leading to miscalculations in loan issuance, collateral valuation, and liquidation processes.

Vulnerability Details

The getNFTPrice function is intended to determine whether an NFT’s price is valid and up to date. Although the accompanying comment indicates a check for stale data, no actual timestamp validation is performed. Instead, the function only reverts if the returned price is zero, ignoring the lastUpdateTimestamp value provided by the oracle. As a result, if the oracle stops publishing updates—thereby leaving a nonzero but outdated price—this outdated price will be mistakenly treated as valid by the contract. Such reliance on stale data may lead to incorrect calculations of collateral requirements, under- or overvaluation of assets, and potential liquidity risks for the protocol.

Impact

Relying on potentially stale NFT price data can lead to significant mispricing of collateral within the protocol. If the oracle data is not updated and the contract fails to recognize it as outdated, users could borrow against an artificially high valuation. This situation risks destabilizing the lending ecosystem, as it allows for undercollateralized loans and may trigger unexpected liquidations, ultimately undermining the protocol’s financial integrity.

Proof of Concept

The getNFTPrice method states in its comments that it checks if the price is "stale" (outdated). However, the implementation only verifies that the price is nonzero, never using the lastUpdateTimestamp to confirm the price is recent. Consequently, outdated data could be used unknowingly.

Code Analysis

Below is a condensed snippet of the contract highlighting the relevant section:

/**
* Checks if the price is stale (?)
*/
function getNFTPrice(uint256 tokenId) public view returns (uint256) {
// (!) The following code only checks if `price != 0`
// without using `lastUpdateTimestamp` to confirm its recency.
(uint256 price, uint256 lastUpdateTimestamp) = priceOracle.getLatestPrice(tokenId);
if (price == 0) revert InvalidNFTPrice();
// The comment suggests a "stale price" check, but there's no actual logic
// to verify if `lastUpdateTimestamp` is recent.
return price;
}

Explanation

The comment claims a check for stale pricing, yet no such validation is performed. This inconsistency can mislead developers or auditors into believing there is protection against outdated price data. If the price feed halts updates (e.g., due to a frozen feed), price remains > 0 and the contract continues using an outdated value without any error.

Vulnerable Scenario

  1. The price feed stops updating (e.g., it becomes frozen).

  2. price remains greater than 0 but lastUpdateTimestamp is no longer current.

  3. The contract fails to recognize the stale data, permitting deposits, withdrawals, or liquidations using an inaccurate NFT price.

Such a scenario may lead to loan imbalances or liquidity risk if collateral was overvalued based on a defunct or frozen feed.

Confirmation

  • The contract comment suggests a stale-check mechanism, yet no verification against lastUpdateTimestamp exists.

  • The logic only reverts if price == 0, making no comparison involving the feed’s update timestamp.

  • Therefore, the contract can rely on outdated price data without alerting users or blocking operations.

Tools Used

Manual Code Review: The code was manually inspected to identify logical discrepancies between the stated functionality and its actual implementation.

Recommendations

Implement a genuine “stale price” validation by comparing block.timestamp against the oracle’s lastUpdateTimestamp, ensuring it does not exceed a predefined threshold.

function getNFTPrice(uint256 tokenId) public view returns (uint256) {
(uint256 price, uint256 lastUpdateTimestamp) = priceOracle.getLatestPrice(tokenId);
+ // Add a stale-check condition to validate that the data is fresh:
+ uint256 maxPriceAge = 1 hours; // Example threshold
+ require(
+ block.timestamp - lastUpdateTimestamp <= maxPriceAge,
+ "Stale price: lastUpdateTimestamp is too old"
+ );
if (price == 0) revert InvalidNFTPrice();
return price;
}
  • Add a dedicated timestamp verification to ensure the reported price is within an acceptable age range, preventing reliance on obsolete oracle data.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month 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 about 1 month 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.