Stratax Contracts

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

Oracle Manipulation / Unsafe Oracle Read (Single latestRoundData Read + Price Integrity Risk)

Author Revealed upon completion

Oracle Manipulation / Unsafe Oracle Read (Single latestRoundData Read + Price Integrity Risk)

Description:

The price is taken directly from latestRoundData() with minimal validation (answer > 0). This pattern can be unsafe depending on how StrataxOracle::getPrice is used (e.g., lending, liquidation, swaps), because it does not validate round completeness/freshness or other oracle safety conditions.

@> (, int256 answer,,,) = priceFeed.latestRoundData();

Impact:

  • If downstream logic relies on StrataxOracle::getPrice for critical accounting, an attacker may exploit oracle weaknesses (stale data, downtime, or manipulated underlying market feeding the oracle).

  • Missing checks can lead to using outdated or invalid rounds, causing incorrect pricing and potential loss of funds.

  • Increased systemic risk when used in sensitive protocols (borrow/repay, collateral valuation, mint/redeem).

Proof of Concept:

Current code only checks:

require(answer > 0, "Invalid price from oracle");

But does not verify:

  • updatedAt is recent (staleness)

  • answeredInRound >= roundId (round completeness)

  • roundId != 0

  • (optional) L2 sequencer uptime checks (if deployed on L2s)

Recommended Mitigation:

Perform stronger validation on oracle data and enforce staleness bounds:

error StrataxOracle__StalePrice();
error StrataxOracle__IncompleteRound();
error StrataxOracle__InvalidOracleAnswer();
uint256 internal constant MAX_PRICE_AGE = 1 hours;
function getPrice(address _token) public view returns (uint256 price) {
if (_token == address(0)) revert StrataxOracle__ZeroTokenAddress();
address priceFeedAddress = priceFeeds[_token];
if (priceFeedAddress == address(0)) revert StrataxOracle__PriceFeedNotSet();
AggregatorV3Interface priceFeed = AggregatorV3Interface(priceFeedAddress);
(uint80 roundId, int256 answer,, uint256 updatedAt, uint80 answeredInRound) =
priceFeed.latestRoundData();
if (answer <= 0) revert StrataxOracle__InvalidOracleAnswer();
if (answeredInRound < roundId) revert StrataxOracle__IncompleteRound();
if (updatedAt == 0 || block.timestamp - updatedAt > MAX_PRICE_AGE) revert StrataxOracle__StalePrice();
price = uint256(answer);
}

If StrataxOracle::getPrice is used for high-stakes value transfers, also consider:

  • using Chainlink’s recommended L2 sequencer uptime feed checks (on supported L2s),

  • adding a circuit breaker / sanity bounds (max deviation),

  • or using TWAP/medianization where applicable.

Support

FAQs

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

Give us feedback!