Stratax Contracts

First Flight #57
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Severity: medium
Valid

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

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);
}
Updates

Lead Judging Commences

izuman Lead Judge 16 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Price feed has no staleness check

StrataxOracle contract fails to check if the price is stale, which can mess up swap calculations.

Support

FAQs

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

Give us feedback!