QuantAMM

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

Stale Weights Due to Improper Weight Normalization and Guard Rail Violation

Summary

In the _normalizeWeightUpdates() function, the guarded weights are adjusted by subtracting any excess from the first element of the weights if the total exceeds 1e18.

_newWeights[0] = _newWeights[0] + (ONE - newWeightsSum);

The comments in the code acknowledge that this adjustment may occasionally break the guard rails but consider the impact to be minimal. However, if the first weight is already at the minimum value of the guard rail (absoluteWeightGuardRail), this adjustment can push it below the minimum value.

When this occurs:

  1. The updated weight of the first asset becomes less than the minimum, and the current weight of the asset equals the minimum.

  2. These values are passed to the _calculateMultiplerSetWeights() function.

The function calculates blockMultiplier as:

int256 blockMultiplier = (local.updatedWeights[i] - local.currentWeights[i]) / local.updateInterval;

If the blockMultiplier is negative (because currentWeights > updatedWeights), the logic proceeds to calculate weightBetweenTargetAndMax:

weightBetweenTargetAndMax = local.currentWeights[i] - local.absoluteWeightGuardRail18;

Since currentWeights[i] is already at the minimum, weightBetweenTargetAndMax becomes 0. This sets currentLastInterpolationPossible to 0:

if (currentLastInterpolationPossible < int256(type(int40).max) - int256(int40(uint40(block.timestamp))))
this condition will always be true

As a result, the lastTimestampThatInterpolationWorks value is set to block.timestamp + 0.

In QuantAMMWeightedPool.sol, this value leads to stale weights when onSwap() or _getNormalisedWeight() is called. Due to the following condition:

if (block.timestamp >= variables.lastPossibleInterpolationTime) {
multiplierTime = variables.lastPossibleInterpolationTime;
}

The timestamp comparison always returns true, resulting in stale weight data for the users because the multiplier time will be same time when setting the weights and timeSinceLastUpdate will always be 0.

Vulnerability Details

  1. Adjustment to weights in _normalizeWeightUpdates() can break guard rails.

  2. This causes issues in _calculateMultiplerSetWeights() when currentWeights[i] is already at its minimum.

  3. The lastTimestampThatInterpolationWorks becomes block.timestamp, leading to stale weight data.

Impact

Users will get stale weight values.

Tools Used

Manual Review

Recommendations

Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding_normalizeWeightUpdates_leads_to_stale_weights

Likelihood: Medium/High when a weight is clamp/normalize to the minimum. Until weights change again. Impact: Low/Medium, stale data because lastTimestampThatInterpolationWorks do not change. MultiplierTime in onSwap will stay the same.

Appeal created

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

Support

FAQs

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