Stratax Contracts

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

Atomic Loop Reverts on Single Bad Entry (Single Failure Blocks Batch Update + DoS-on-Batch Risk)

Author Revealed upon completion

Atomic Loop Reverts on Single Bad Entry (Single Failure Blocks Batch Update + DoS-on-Batch Risk)

Description:

Inside StrataxOracle::setPriceFeeds, each iteration calls StrataxOracle::_setPriceFeed() which can revert on invalid inputs or failing decimals check. A single invalid item causes the entire batch to revert, preventing valid updates in the same transaction.

@> for (uint256 i = 0; i < _tokens.length; i++) {
@> _setPriceFeed(_tokens[i], _priceFeeds[i]);
@> emit PriceFeedUpdated(_tokens[i], _priceFeeds[i]);
@> }

And StrataxOracle::_setPriceFeed contains multiple require() statements that can revert:

@> require(_token != address(0), "Invalid token address");
@> require(_priceFeed != address(0), "Invalid price feed address");
@> require(priceFeed.decimals() == 8, "Price feed must have 8 decimals");

Impact:

  • Batch update becomes “all-or-nothing”: one bad pair blocks all other valid pairs.

  • Operational DoS risk for batch maintenance (especially when updating many feeds).

  • Poor resilience: operators must retry with smaller batches or pre-validate off-chain.

Proof of Concept:

If one _priceFeed is address(0) (or returns decimals != 8), the whole transaction reverts and none of the other valid feeds are updated.

Recommended Mitigation:

Option A (preferred): pre-validate all entries first, then apply updates (still atomic but fails early with better guarantees):

for (uint256 i = 0; i < len; i++) {
_validateFeed(_tokens[i], _priceFeeds[i]); // no state changes
}
for (uint256 i = 0; i < len; i++) {
_setPriceFeedUnchecked(_tokens[i], _priceFeeds[i]); // state changes only
emit PriceFeedUpdated(_tokens[i], _priceFeeds[i]);
}

Option B: make the batch best-effort (process valid entries, skip invalid), and emit an event for failures:

event PriceFeedUpdateFailed(address token, address priceFeed, bytes reason);
for (uint256 i = 0; i < len; i++) {
try this.__setPriceFeedExternal(_tokens[i], _priceFeeds[i]) {
emit PriceFeedUpdated(_tokens[i], _priceFeeds[i]);
} catch (bytes memory reason) {
emit PriceFeedUpdateFailed(_tokens[i], _priceFeeds[i], reason);
}
}

Note: try/catch requires an external call boundary (e.g., a dedicated external function). This increases gas, so choose based on operational needs.

Support

FAQs

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

Give us feedback!