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:
The updated weight of the first asset becomes less than the minimum, and the current weight of the asset equals the minimum.
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.
Adjustment to weights in _normalizeWeightUpdates() can break guard rails.
This causes issues in _calculateMultiplerSetWeights() when currentWeights[i] is already at its minimum.
The lastTimestampThatInterpolationWorks becomes block.timestamp, leading to stale weight data.
Users will get stale weight values.
Manual Review
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.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.