Stratax Contracts

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

Missing Stale Price Checks in `StrataxOracle`

Author Revealed upon completion

Root + Impact

Description

  • The protocol relies on Chainlink oracles to value assets for critical LTV and health factor calculations.

  • The oracle integration retrieves price data without verifying the data timestamp, accepting potentially frozen or outdated prices during network downtime.

// src/StrataxOracle.sol:70
// @> Root cause: updatedAt is discarded, allowing stale prices to be used
(, int256 answer,,,) = priceFeed.latestRoundData();

Risk

Likelihood:

  • Chainlink feeds stop updating during extreme network congestion or L2 outages.

  • No validation exists to reject these stale periods.

Impact:

  • Undercollateralized positions are created during market crashes if old high prices are used.

  • Users are unfairly liquidated if prices freeze at low values during a recovery.

Proof of Concept

The following PoC demonstrates the missing validation in StrataxOracle.getPrice. It shows that updatedAt time is not retrieved or checked, meaning even if the last update was days ago (stale), the contract still accepts the price and returns it as valid.

// In StrataxOracle.sol
function getPrice(address _token) public view returns (uint256 price) {
address priceFeedAddress = priceFeeds[_token];
// ...
// @> Vulnerability: updatedAt is ignored
(, int256 answer,,,) = priceFeed.latestRoundData();
// Only checked:
require(answer > 0, "Invalid price from oracle");
price = uint256(answer);
}

Recommended Mitigation

Update the latestRoundData call to retrieve all return values, including updatedAt and answeredInRound. Implement checks to ensure updatedAt is within a reasonable threshold (e.g., the feed's heartbeat or 1 hour) and that the round data is complete.

- (, int256 answer,,,) = priceFeed.latestRoundData();
+ (uint80 roundId, int256 answer, , uint256 updatedAt, uint80 answeredInRound) = priceFeed.latestRoundData();
+ require(updatedAt > 0, "Round not complete");
+ require(block.timestamp - updatedAt < 3600, "Stale price"); // e.g. 1 hour timeout
+ require(answeredInRound >= roundId, "Stale round");

Support

FAQs

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

Give us feedback!