QuantAMM

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

Oracle Cascade Failure in UpdateWeightRunner

Description

The UpdateWeightRunner contract implements a weight update mechanism that relies on oracle data for multiple assets in a pool. While the contract includes a backup oracle system, it has a critical design flaw in its failure handling:

function _getData(address _pool, bool internalCall) private view returns (int256[] memory outputData) {
// ... initialization code ...
for (uint i; i < oracleLength; ) {
OracleData memory oracleResult;
oracleResult = _getOracleData(OracleWrapper(optimisedOracles[i]));
if (oracleResult.timestamp > block.timestamp - oracleStalenessThreshold) {
outputData[i] = oracleResult.data;
} else {
// Try backup oracles
for (uint j = 1; j < numAssetOracles; ) {
oracleResult = _getOracleData(OracleWrapper(poolBackupOracles[_pool][i][j]));
if (oracleResult.timestamp > block.timestamp - oracleStalenessThreshold) {
break;
} else if (j == numAssetOracles - 1) {
// All oracles failed
revert("No fresh oracle values available");
}
}
}
}
}

The contract implements an "all-or-nothing" approach where if any single asset's oracles (both primary and all backups) fail to provide fresh data, the entire weight update process reverts. This creates a cascade failure where one problematic asset prevents updates for all assets in the pool.

Impact

The cascade failure mechanism creates several critical vulnerabilities:

  1. Systemic Risk

    • A single asset's oracle failure prevents weight updates for ALL assets

    • Even if other assets have perfectly functioning oracles, their weights remain stale

    • This affects the entire pool's operation and pricing efficiency

  2. Manipulation Vector

    • Attackers can target the weakest oracle in the pool

    • If they succeed in making one asset's oracles stale:

      • The entire pool becomes stuck with old weights

      • This creates predictable price discrepancies

      • These discrepancies can be exploited through trading

  3. Economic Loss

    • Pools operate with stale weights during oracle failures

    • This leads to suboptimal trading prices

    • LPs suffer from poor price execution

    • Protocol loses fees from reduced trading efficiency

For example, in a pool with ETH, BTC, and LINK:

  • If LINK's oracles fail (primary + backups)

  • ETH and BTC weights can't update

  • Even if ETH/BTC ratio changes significantly

  • Pool remains stuck with old weights

  • Traders can exploit the known price discrepancy

Mitigation

Two recommended approaches to address this issue:

  1. Last Known Good Value with Decay

    • Store the last valid value received from each asset's oracles

    • When an asset's oracles fail, use its last known good value but with a time-based decay factor

    • The decay ensures the stale value becomes more conservative over time

    • This allows the pool to continue operating while incentivizing quick oracle fixes

    • Example: A 1% decay per hour would gradually move the weight towards a neutral position

  2. Partial Updates with Risk Thresholds

    • Allow weight updates to proceed even if some assets have stale oracles

    • Implement a risk threshold system where:

      • If < 20% of assets have stale oracles: continue normally with last good values

      • If 20-50% have stale oracles: enter a conservative mode with reduced weight changes

      • If > 50% have stale oracles: revert to prevent unsafe operations

    • This provides graceful degradation while maintaining safety bounds

The first approach is simpler to implement and provides good safety, while the second offers more flexibility but requires more complex risk management. Either approach would be significantly better than the current all-or-nothing system.

Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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