SC03:2026 - Price Oracle Manipulation

Description

Price oracle manipulation describes any situation where a smart contract relies on price or valuation data that can be directly or indirectly influenced by an attacker, causing the protocol to make decisions based on incorrect values. Oracles are trust boundaries: the contract implicitly trusts that the price it receives reflects real-world or on-chain market conditions. When that trust is violated—whether by manipulation, staleness, or misconfiguration—protocol behavior is distorted.

This affects all contract types that consume price data: DeFi lending and borrowing (collateral valuation, liquidation), AMMs and DEXes (spot and TWAP-based pricing), yield vaults (NAV calculations, share valuation), liquid staking and derivatives (ETH/stake price feeds), NFT and token valuations (floor price oracles), and cross-chain bridges (asset pricing for mint/burn ratios). On non-EVM chains (e.g., Move, Solana), similar patterns apply wherever external price sources feed into on-chain logic.

Few areas to focus on:

Attackers exploit:

Example (Vulnerable Oracle Usage)

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

interface IPriceFeed {
    function latestAnswer() external view returns (int256);
}

contract VulnerableOracleLending {
    IPriceFeed public priceFeed; // single-point oracle
    mapping(address => uint256) public collateralEth;
    mapping(address => uint256) public debtUsd;

    constructor(IPriceFeed _feed) {
        priceFeed = _feed;
    }

    function depositCollateral() external payable {
        collateralEth[msg.sender] += msg.value;
    }

    function borrow(uint256 amountUsd) external {
        int256 price = priceFeed.latestAnswer(); // no sanity checks, no delay
        require(price > 0, "bad price");

        uint256 collateralUsd = (collateralEth[msg.sender] * uint256(price)) / 1e8;
        // Allows borrowing up to 100% of collateral value – overly generous
        require(collateralUsd >= amountUsd, "insufficient collateral");

        debtUsd[msg.sender] += amountUsd;
        // transfer stablecoin (omitted)
    }
}

Issues:

Example (Hardened Oracle Integration)

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

interface IAggregatorV3 {
    function latestRoundData()
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        );
}

contract RobustOracleLending {
    IAggregatorV3 public immutable priceFeed;
    mapping(address => uint256) public collateralEth;
    mapping(address => uint256) public debtUsd;

    uint256 public constant MAX_DELAY = 1 hours;
    uint256 public constant COLLATERAL_FACTOR_BPS = 7500; // 75%

    constructor(IAggregatorV3 _feed) {
        priceFeed = _feed;
    }

    function _getSafePrice() internal view returns (uint256) {
        (, int256 answer, , uint256 updatedAt, uint80 answeredInRound) =
            priceFeed.latestRoundData();
        require(answer > 0, "bad answer");
        require(updatedAt != 0 && block.timestamp - updatedAt <= MAX_DELAY, "stale price");
        require(answeredInRound != 0, "incomplete round");
        return uint256(answer);
    }

    function depositCollateral() external payable {
        require(msg.value > 0, "no collateral");
        collateralEth[msg.sender] += msg.value;
    }

    function borrow(uint256 amountUsd) external {
        uint256 price = _getSafePrice();
        uint256 collateralUsd = (collateralEth[msg.sender] * price) / 1e8;
        uint256 maxBorrow = (collateralUsd * COLLATERAL_FACTOR_BPS) / 10_000;
        require(debtUsd[msg.sender] + amountUsd <= maxBorrow, "exceeds limit");

        debtUsd[msg.sender] += amountUsd;
        // transfer stablecoin (omitted)
    }
}

Security Improvements:

In 2025, pure oracle-only mega-exploits were less frequent, but oracle manipulation was often one component in multi-vector attacks.

2025 Case Studies

Best Practices & Mitigations