Stratax Contracts

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

Stale Chainlink Price Is Accepted, Causing Wrong Leverage/Unwind Decisions

Author Revealed upon completion

Root + Impact

Description

  • the oracle should return a recent, valid Chainlink price so leverage sizing and unwind math are based on current market conditions.

  • getPrice() accepts any positive answer but does not validate staleness (updatedAt) or round completeness (answeredInRound >= roundId), so outdated data is treated as valid.

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();
require(answer > 0, "Invalid price from oracle");
// @> Missing stale-data checks (updatedAt / answeredInRound)
price = uint256(answer);
}

Risk

Likelihood:

  • Chainlink feeds naturally have periods where updatedAt is old during low volatility or feed disruption.

  • Protocol operations that call oracle pricing (calculateOpenParams, unwind calculations) can run while old rounds are still returned.

Impact:

  • Position sizing can be materially wrong (borrow too high/too low).

  • Unwind calculations can use incorrect collateral/debt conversion, increasing loss/revert risk.

Proof of Concept

Recommended Mitigation

Get and check the updateAt to make sure the price is as fresh as possible

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, "Stale round");
+ require(updatedAt != 0, "Round not complete");
+ require(block.timestamp - updatedAt <= 1 days, "Price stale"); // or any time the protocol chooses to use
price = uint256(answer);
}

Support

FAQs

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

Give us feedback!