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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions