Skip to content

Oracle staleness risk: deprecated latestAnswer() used without freshness check #811

Description

@shunfeng8421

Summary

The assetPrice() function in contracts/Auditor.sol:356 uses Chainlink's deprecated latestAnswer() instead of latestRoundData(), with no staleness check on the returned price.

Vulnerability Details

// contracts/Auditor.sol:353-358
function assetPrice(IPriceFeed priceFeed) public view returns (uint256) {
    if (address(priceFeed) == BASE_FEED) return basePrice;
    int256 price = priceFeed.latestAnswer();  // <-- DEPRECATED, no staleness check
    if (price <= 0) revert InvalidPrice();
    return uint256(price) * baseFactor;
}

The IPriceFeed interface only exposes latestAnswer():

// contracts/utils/IPriceFeed.sol:7
function latestAnswer() external view returns (int256);

Risk

  • latestAnswer() is explicitly deprecated by Chainlink
  • No check on updatedAt timestamp — contract will use stale prices indefinitely if oracle stops updating
  • Affects ALL liquidation calculations (liquidate(), accountLiquidity()) through assetPrice()
  • During network congestion or oracle downtime, stale prices could lead to unfair liquidations

Recommended Fix

Update IPriceFeed to include latestRoundData() and add staleness validation:

function assetPrice(IPriceFeed priceFeed) public view returns (uint256) {
    if (address(priceFeed) == BASE_FEED) return basePrice;
    (, int256 price, , uint256 updatedAt, ) = priceFeed.latestRoundData();
    require(price > 0, "Invalid price");
    require(block.timestamp - updatedAt < STALENESS_THRESHOLD, "Stale price");
    return uint256(price) * baseFactor;
}

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions