Use Case & Applications

Ensures oracle data is fresh and updated within an acceptable time window, preventing the use of stale price data that could lead to incorrect valuations and financial losses. Critical for lending protocols (collateral valuation, liquidations), DEXs (price discovery, arbitrage detection), yield farming protocols (reward calculations), insurance protocols (premium calculations), and options/derivatives protocols (settlement). Stale oracle data can indicate service disruption or attacks on oracle infrastructure, potentially leading to significant protocol vulnerabilities.
  • Loopscale: Hack caused by a stale oracle. It was on Solana, so not relevant to Credible Layer, but the same idea applies.

Explanation

Monitors oracle timestamp updates when critical protocol functions are called:
  • forkPostState(): Check oracle’s last update time after transaction
  • registerCallTrigger(): Trigger on functions that rely on oracle data (e.g., DEX swaps)
  • Verify oracle’s last update time is within defined maximum time window
Upon functions that rely on oracle data, the assertion ensures transactions only proceed with fresh oracle data, preventing operations with stale pricing information. For more information about cheatcodes, see the Cheatcodes Documentation.

Code Example

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {Assertion} from "credible-std/Assertion.sol";
import {PhEvm} from "credible-std/PhEvm.sol";

contract OracleLivenessAssertion is Assertion {
    // Maximum time window (in seconds) that oracle data can be considered fresh
    // This is a constant that should be adjusted based on the protocol's requirements
    uint256 public constant MAX_UPDATE_WINDOW = 10 minutes;

    function triggers() external view override {
        // Register trigger for the swap function which relies on oracle data
        registerCallTrigger(this.assertionOracleLiveness.selector, IDex.swap.selector);
    }

    // Assert that the oracle has been updated within the specified time window
    function assertionOracleLiveness() external {
        // Get the assertion adopter address
        IDex adopter = IDex(ph.getAssertionAdopter());

        // Get the current state to check the oracle's last update time
        ph.forkPostTx();

        // Check if the oracle has been updated within the maximum allowed window
        uint256 lastUpdateTime = IOracle(adopter.oracle()).lastUpdated();
        uint256 currentTime = block.timestamp;

        // Verify the oracle data is fresh (updated within the time window)
        require(currentTime - lastUpdateTime <= MAX_UPDATE_WINDOW, "Oracle not updated within the allowed time window");
    }
}

interface IOracle {
    function lastUpdated() external view returns (uint256);
}

interface IDex {
    function swap(address tokenIn, address tokenOut, uint256 amountIn) external returns (uint256);
    function oracle() external view returns (IOracle);
}
Note: Full examples with tests available in the Phylax Assertion Examples Repository.