Stratax Contracts

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

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

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.

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!