Stratax Contracts

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

StrataxOracle accepts stale or invalid Chainlink rounds without freshness checks

Description

  • Normal behavior: oracle reads should validate round freshness and consistency (updatedAt, answeredInRound, etc.) before returning prices.

  • Issue: getPrice only checks answer > 0 and does not validate freshness or round integrity. Any positive stale round is accepted and consumed by leverage/unwind calculations.

// src/StrataxOracle.sol
function getPrice(address _token) public view returns (uint256 price) {
...
@> (, int256 answer,,,) = priceFeed.latestRoundData();
@> require(answer > 0, "Invalid price from oracle");
@> price = uint256(answer);
}

Risk

Likelihood:

  • Reason 1 // Chainlink feeds can lag updates during market stress or data-source degradation.

  • Reason 2 // This oracle function is directly used by position sizing paths and executes frequently.

Impact:

  • Impact 1 // Borrow/withdraw sizing may be based on stale prices and become unsafe.

  • Impact 2 // Users can face higher revert rate, worse execution, or liquidation exposure.

Proof of Concept

This PoC deploys a mock Chainlink feed with valid decimals but intentionally stale/inconsistent round metadata (updatedAt = 1, answeredInRound < roundId). getPrice still returns the value, demonstrating that freshness and round-integrity are not enforced.

// test/poc/StrataxVulnerabilities.t.sol
function testPoC_StaleOracleRoundIsAcceptedByStrataxOracle() public {
StrataxOracle strataxOracle = new StrataxOracle();
MockAggregator agg = new MockAggregator(8);
// stale/inconsistent round accepted by current implementation
agg.setRoundData(200, 2_000e8, 1, 1, 199);
strataxOracle.setPriceFeed(address(collateralToken), address(agg));
uint256 price = strataxOracle.getPrice(address(collateralToken));
assertEq(price, 2_000e8);
}

Recommended Mitigation

The mitigation adds standard Chainlink guardrails: positive answer, non-zero/fresh updatedAt, and round consistency (answeredInRound >= roundId). This blocks stale rounds from propagating into leverage and unwind sizing.

- (, int256 answer,,,) = priceFeed.latestRoundData();
- require(answer > 0, "Invalid price from oracle");
+ (uint80 roundId, int256 answer,, uint256 updatedAt, uint80 answeredInRound) = priceFeed.latestRoundData();
+ require(answer > 0, "Invalid price from oracle");
+ require(updatedAt != 0 && updatedAt + MAX_STALENESS >= block.timestamp, "Stale price");
+ require(answeredInRound >= roundId, "Stale round");
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!