How to Check if Oracle Prices Are Stale

 / 
1

To check whether an oracle price is stale, the key is to compare the timestamp (updatedAt/timestamp) returned by the data source with the current block time, rather than just looking at the price number itself. Chainlink official documentation and multiple security audit reports repeatedly emphasize that you must perform a freshness check using the updatedAt field returned by latestRoundData, and ignore stale data.

Step 1: Find the Data Read Entry Point of the Oracle Contract

Locate the method in the smart contract you want to check that reads the oracle price:

  • If you have the contract code, find the part that calls the oracle price. The Chainlink official documentation gives a standard example: using the latestRoundData() method of the AggregatorV3Interface interface.
  • If you are viewing the contract on a block explorer (such as Etherscan), switch to the "Read Contract" tab, find functions like latestRoundData or getPrice, and click to see the raw returned data.
  • Key point: Distinguish between "seeing the price" and "verifying freshness". Just looking at answer is not enough; you must also get updatedAt and answeredInRound.

Completion check: You have found the entry point to read the return values of latestRoundData and know where to get the updatedAt timestamp.

Step 2: Check the Timestamp — The Core Criterion

Convert the updatedAt timestamp returned by the oracle into a human-readable time and then compare it with the current block time:

  • One of the return values of latestRoundData is updatedAt, which is a Unix timestamp (seconds since 1970).
  • A complete check should include:
    • Ensure the updatedAt timestamp is not zero (zero indicates the data round is not complete).
    • Set an acceptable time difference threshold (e.g., 1 hour). If block.timestamp - updatedAt exceeds this threshold, the price is stale.
  • The heartbeat interval of oracles varies across different chains. For example, on Arbitrum it is usually 1 hour. You can go to data.chain.link to check the heartbeat configuration of the specific data source.

Completion check: You have calculated the time difference of the current price and determined whether it falls within your acceptable range.

Step 3: Additional Checks — Verify Round Integrity

In addition to the timestamp, you also need to verify whether the data round is valid to avoid being affected by "round rollback":

  • Check that answeredInRound is greater than or equal to roundId. If not, it means the current answer may have been inherited from a previous round, and the data reliability is compromised.
  • Also ensure that price (answer) is greater than zero.

A complete security check reference code is as follows:

(uint80 roundId, int256 price, , uint256 updatedAt, uint80 answeredInRound) = feed.latestRoundData();
require(price > 0, "Chainlink price <= 0");
require(updatedAt != 0, "Incomplete round");
require(answeredInRound >= roundId, "Stale price");
// and then add timestamp freshness check
require(block.timestamp - updatedAt < validPeriod, "Freshness check failed");

Completion check: You can ensure the price read satisfies the three conditions of "positive number", "round integrity", and "timestamp freshness".

Prerequisites

You need to know which oracle data source the target contract is using (such as the contract address for the ETH/USD data source) and which chain it is deployed on. Different chains may have different data source addresses and heartbeat configurations.

Common Failure Reasons

  • Using the deprecated latestAnswer interface: Many projects have exposed this issue. latestAnswer does not provide a timestamp, making it impossible to judge whether the data is fresh.
  • Only checking the price sign, ignoring time and round: Even if the price is positive, it could be several hours old. During sharp market fluctuations, this can cause serious deviations.
  • Mistaking DEX spot prices for oracle prices: The price source and trading logic of on-chain perpetual exchanges need to be carefully distinguished. Whether liquidation uses oracle prices or trading prices directly impacts liquidation safety.

Risk Reminder

  • Capital risk: If you rely on stale oracle prices, liquidation logic and borrowing health calculations will be distorted. The most direct consequence: your position may be liquidated when it shouldn't be, or not liquidated when it should, resulting in bad debt.
  • Account risk: No direct account risk.
  • Compliance risk: None.

Completion Mark

You have successfully called latestRoundData() and checked the updatedAt timestamp. If you can compute a specific number of seconds since the current time and state whether it exceeds a reasonable range, you have completed the check. If you find that a particular oracle quote is significantly stale, it is recommended not to use that price for any trading decisions, and you can consider checking whether the corresponding project has enabled a backup price feed or paused the protocol.