Core Contracts

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

Stale Oracle Prices in Collateral Valuation

Summary

The function checks if price == 0 but ignores lastUpdateTimestamp. If the oracle hasn’t updated the price recently (e.g., due to a malfunction), the contract uses stale data.

Vulnerability Details

The contract uses getNFTPrice(tokenId) to retrieve an NFT's value

function getNFTPrice(uint256 tokenId) public view returns (uint256) {
(uint256 price, uint256 lastUpdateTimestamp) = priceOracle.getLatestPrice(tokenId);
if (price == 0) revert InvalidNFTPrice();
return price;
}

The function checks if price == 0 but ignores lastUpdateTimestamp. If the oracle hasn’t updated the price recently (e.g., due to a malfunction), the contract uses stale data.

When a user withdraws an NFT, the contract checks if their remaining collateral is sufficient:

// In withdrawNFT():
uint256 collateralValue = getUserCollateralValue(msg.sender);
uint256 nftValue = getNFTPrice(tokenId);
if (collateralValue - nftValue < userDebt.percentMul(liquidationThreshold)) {
revert WithdrawalWouldLeaveUserUnderCollateralized();
}

If nftValue is stale (overvalued), collateralValue - nftValue becomes artificially low. This allows withdrawals that should be blocked, leaving the user undercollateralized with current prices. The contract does not validate if the oracle’s lastUpdateTimestamp is recent enough. Stale prices can be used for critical collateral checks.

Example:

User deposits an NFT with a stale price of 100 ETH (actual market value: 50 ETH).

They borrow 80 ETH (assuming liquidationThreshold = 80%80 ETH * 80% = 64 ETH collateral required).

  • User tries to withdraw the NFT.

  • getNFTPrice returns 100 ETH (stale).

If the oracle updates the price to 50 ETH after getNFTPrice is called but before the NFT is transferred:

Post-withdrawal, the user’s actual collateral is 0 ETH, but their debt remains 80 ETH.

The protocol is left with an undercollateralized loan.

Impact

The contract does not validate if the oracle’s lastUpdateTimestamp is recent enough. Stale prices can be used for critical collateral checks

Tools Used

Foundry

Recommendations

function getNFTPrice(uint256 tokenId) public view returns (uint256) {
(uint256 price, uint256 lastUpdateTimestamp) = priceOracle.getLatestPrice(tokenId);
if (price == 0) revert InvalidNFTPrice();
// Ensure price is not older than 24 hours
if (block.timestamp - lastUpdateTimestamp > 24 hours) revert StalePrice();
return price;
}
Updates

Lead Judging Commences

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

Give us feedback!