Stratax Contracts

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

Stale Oracle Prices - No Freshness Check

Author Revealed upon completion

Root + Impact

Location: src/StrataxOracle.sol:70

Description

StrataxOracle.getPrice() queries Chainlink via latestRoundData() and uses the returned price to size flash loans, borrow amounts, and collateral withdrawals across the protocol.

The function discards updatedAt, roundId, and answeredInRound the fields required to confirm the price is fresh and from a completed round. A stale price is silently accepted as valid.

// src/StrataxOracle.sol:70
(, int256 answer,,,) = priceFeed.latestRoundData(); // @> updatedAt, roundId, answeredInRound all discarded
require(answer > 0, "Invalid price from oracle"); // @> only checks sign, not freshness

Risk

Likelihood:

  • Chainlink feeds go stale during L2 sequencer downtime, network congestion, or feed deprecation — the last returned price persists indefinitely with no on-chain signal of staleness

  • answeredInRound < roundId indicates an incomplete round; this case is never validated

Impact:

  • A stale, inflated collateral price allows opening over-leveraged positions beyond protocol safety margins

  • A stale deflated collateral price during unwind causes insufficient collateral withdrawal, trapping user funds in Aave

Proof of Concept

// 1. Owner sets WETH price feed on deployment
// 2. Chainlink sequencer goes offline — latestRoundData returns last known price from 6 hours ago
// 3. Owner calls calculateOpenParams() — stale price accepted without revert
// 4. Borrow amount miscalculated — position opened with incorrect leverage ratio
// 5. Health factor lower than expected, position at risk of liquidation

Recommended Mitigation

- (, 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, "Round not complete");
+ require(answeredInRound >= roundId, "Stale round");
+ require(block.timestamp - updatedAt < STALENESS_THRESHOLD, "Price too stale");

Support

FAQs

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

Give us feedback!