Stratax Contracts

First Flight #57
Beginner FriendlyDeFi
100 EXP
Submission Details
Impact: high
Likelihood: medium

Missing Circuit Breaker Check Allows Invalid Oracle Prices

Author Revealed upon completion

Root + Impact

Description

Chainlink has a circuit breaker mechanism that triggers during extreme price movements. When activated, answeredInRound will be less than roundId, signaling that the price data is unreliable. The protocol never checks this condition, accepting potentially manipulated or incorrect prices during these critical periods.

function getPrice(address _token) public view returns (uint256 price) {
AggregatorV3Interface priceFeed = AggregatorV3Interface(priceFeedAddress);
@> (, int256 answer,,,) = priceFeed.latestRoundData(); // answeredInRound not checked
require(answer > 0, "Invalid price from oracle");
price = uint256(answer);
}

Risk

Likelihood:

  • Circuit breakers activate during 50%+ price moves in crypto markets

  • Flash crashes and black swan events trigger this protection

  • Happens multiple times per year in volatile markets

Impact:

  • Protocol uses unreliable prices during the most critical moments

  • Users open/close positions at incorrect prices

  • Liquidations execute at wrong prices, causing user loss

  • Protocol becomes insolvent if bad prices persist

Proof of Concept

// Scenario: Flash crash triggers circuit breaker
// 1. ETH crashes from $3000 to $1500 in minutes
// 2. Chainlink circuit breaker activates (answeredInRound < roundId)
// 3. getPrice() still returns the last price without validation
// 4. User unwinds position thinking ETH is still $3000
// 5. Swap executes at real $1500 price, user loses 50% instantly

Recommended Mitigation

function getPrice(address _token) public view returns (uint256 price) {
address priceFeedAddress = priceFeeds[_token];
require(priceFeedAddress != address(0), "Price feed not set for token");
AggregatorV3Interface priceFeed = AggregatorV3Interface(priceFeedAddress);
- (, int256 answer,,,) = priceFeed.latestRoundData();
+ (uint80 roundId, int256 answer,, uint256 updatedAt, uint80 answeredInRound) = priceFeed.latestRoundData();
require(answer > 0, "Invalid price from oracle");
+ require(answeredInRound >= roundId, "Circuit breaker triggered");
+ require(block.timestamp - updatedAt <= 3600, "Stale price");
price = uint256(answer);
}

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!