QuantAMM

QuantAMM
49,600 OP
View results
Submission Details
Severity: low
Invalid

Integer Overflow in Price Calculation Due to Intermediate Value Limitations in MultiHopOracle

Summary

The MultiHopOracle contract exhibits a critical vulnerability in its price calculation mechanism that creates potential overflow conditions within the _getData() function. At the core of this vulnerability lies a mathematical computation pattern that, despite Solidity >=0.8.0's checked arithmetic, remains susceptible to overflow:

https://github.com/Cyfrin/2024-12-quantamm/blob/main/pkg/pool-quantamm/contracts/MultiHopOracle.sol#L48

data = (data * oracleRes) / 10 ** 18; // Vulnerable line

The contract's mathematical architecture introduces a critical vulnerability point during intermediate calculations. When executing price computations, the multiplication step occurs before the scaling division, creating scenarios where intermediate results can exceed type boundaries. This flaw is particularly insidious as the int216 type constraints become insufficient for handling these intermediate values, especially during periods of high market volatility or when processing multi-hop oracle paths.

In QuantAMM's TFMM context, this vulnerability assumes heightened significance as it can cause the entire oracle system to become unavailable precisely when it's most needed. During periods of high market volatility where prices experience significant movements, these calculations will revert due to overflow, making price data completely inaccessible. This unavailability occurs at the exact moments when accurate price data is most crucial for the protocol's operation.

The impact manifests as a complete denial of service for price feeds during extreme market conditions. When price calculations revert due to overflow, pools depending on these oracle paths cannot:

  • Execute their regular weight adjustment strategies

  • Process user transactions requiring price data

  • Perform critical rebalancing operations

This creates a particularly dangerous scenario where the protocol becomes paralyzed during high volatility events, exactly when active management is most needed.

Proof of Concept

Consider this scenario:
Scenario:

  1. Market conditions are normal with:

    • ETH price at $2,000

    • BTC price at $50,000

  2. A major market event occurs causing extreme volatility:

    • ETH price spikes 100x to $200,000

    • BTC price spikes 50x to $2,500,000

  3. Bob, a liquidity provider, tries to:

    • Remove liquidity from a pool during this volatility

    • The pool attempts to calculate current values using the oracle

  4. The MultiHopOracle attempts price calculation:

    • First multiplication exceeds int216 bounds

    • Transaction reverts due to overflow

  5. As a result:

    • Bob cannot withdraw his liquidity

    • The pool cannot execute weight updates

    • All operations requiring price data fail

  6. The protocol becomes effectively frozen during this critical market period

  7. Operations only resume once prices return to ranges that don't cause overflow

  8. Several users like Bob remain trapped in their positions during the most volatile period

int216 max value = 2^215 - 1 (since it's signed)
≈ 5.2e64
During Extreme Market Conditions:
ETH = $2000 * 100 = $200,000
BTC = $50000 * 50 = $2,500,000
In oracle calculation with 18 decimals:
ETH value = 200,000 * 10^18 = 2e23
BTC value = 2,500,000 * 10^18 = 2.5e24
Intermediate calculation (ETH/BTC):
step 1: ETH * 10^18 = 2e23 * 10^18 = 2e41 (safe)
step 2: (2e41 * 2.5e24) = 5e65 > 5.2e64 (OVERFLOW!)
The multiplication exceeds int216 before division can occur, causing revert

Recommended mitigation steps

  1. Implement safe math operations with wider types:

function _getData() internal view override returns (int216 data, uint40 timestamp) {
HopConfig memory firstOracle = oracles[0];
(data, timestamp) = firstOracle.oracle.getData();
for (uint i = 1; i < oracles.length; ) {
HopConfig memory oracleConfig = oracles[i];
(int216 oracleRes, uint40 oracleTimestamp) = oracleConfig.oracle.getData();
if (oracleConfig.invert) {
// Safe multiplication then division
int256 intermediate = int256(data) * int256(10 ** 18);
require(intermediate <= type(int256).max && intermediate >= type(int256).min, "Mul overflow");
intermediate = intermediate / int256(oracleRes);
require(intermediate <= type(int216).max && intermediate >= type(int216).min, "Result overflow");
data = int216(intermediate);
} else {
int256 intermediate = int256(data) * int256(oracleRes);
require(intermediate <= type(int256).max && intermediate >= type(int256).min, "Mul overflow");
intermediate = intermediate / int256(10 ** 18);
require(intermediate <= type(int216).max && intermediate >= type(int216).min, "Result overflow");
data = int216(intermediate);
}
unchecked { ++i; }
}
}
  1. Add price range validation:

struct PriceBounds {
int216 minPrice;
int216 maxPrice;
}
mapping(uint256 => PriceBounds) public oracleBounds;
function validatePrice(uint256 oracleIndex, int216 price) internal view {
PriceBounds memory bounds = oracleBounds[oracleIndex];
require(price >= bounds.minPrice && price <= bounds.maxPrice, "Price out of bounds");
}
Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

Informational or Gas / Admin is trusted / Pool creation is trusted / User mistake / Suppositions

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelyhood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Support

FAQs

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