QuantAMM

QuantAMM
49,600 OP
View results
Submission Details
Severity: medium
Valid

The `_clampWeights` function can make weight computation to go over the max guard rail or min guard rail

Summary

Weights can go out of bounds during _clampWeights function

Vulnerability Details

The function to constrain the weights after being calculated is the following one:

function _clampWeights(
int256[] memory _weights,
int256 _absoluteWeightGuardRail
) internal pure returns (int256[] memory) {
unchecked {
uint weightLength = _weights.length;
if (weightLength == 1) {
return _weights;
}
int256 absoluteMin = _absoluteWeightGuardRail;
int256 absoluteMax = ONE -
(PRBMathSD59x18.fromInt(int256(_weights.length - 1)).mul(_absoluteWeightGuardRail));
int256 sumRemainerWeight = ONE;
int256 sumOtherWeights;
for (uint i; i < weightLength; ++i) {
if (_weights[i] < absoluteMin) {
_weights[i] = absoluteMin;
sumRemainerWeight -= absoluteMin;
} else if (_weights[i] > absoluteMax) {
_weights[i] = absoluteMax;
sumOtherWeights += absoluteMax;
}
}
if (sumOtherWeights != 0) {
int256 proportionalRemainder = sumRemainerWeight.div(sumOtherWeights);
for (uint i; i < weightLength; ++i) {
if (_weights[i] != absoluteMin) {
_weights[i] = _weights[i].mul(proportionalRemainder);
}
}
}
}
return _weights;
}

This function first limits the weights to be in the interval [absoluteWeightGuardRail, 1e18 - (_weights.length - 1) * absoluteWeightGuardRail] but then tries to redistribute the extracted weights with other weights. However, this operation can make some weights to over the max ot below the minimum depending on the proportionalRemainder.
For example, let's consider the following example:

absoluteWeightGuardRail = 0.20
minWeight = 0.20
maxWeight = 0.40
computedWeights = [0.25, 0.20, 0.39, 0.41]
after first loop:
computedWeights = [0.25, 0.20, 0.39, 0.40]
sumRemainerWeight = 1e18
sumOtherWeights = 0.40e18
proportionalRemainder = 2.5e18
after second loop:
computedWeights = [0.625, 0.20, 0.975, 1]

As we can see, the weights that were different from the absoluteMin went really high surpassing the maximum limit.
But they can also go below minimum:

absoluteWeightGuardRail = 0.05
minWeight = 0.05
maxWeight = 0.85
computedWeights = [0.06, 0.10, 0.90, 0.90]
after first loop:
computedWeights = [0.06, 0.10, 0.85, 0.85]
sumRemainerWeight = 1e18
sumOtherWeights = 1.70e18
proportionalRemainder = 0.588e18
after second loop:
computedWeights = [0.03, 0.0588, 0.4998, 0.4998]

As we can see, the first weight went below minimum

Impact

Medium, it can break some system invariants, but for lack of time it has not been checked

Tools Used

Manual review

Recommendations

It should not redistribute the weights after limiting them

Updates

Lead Judging Commences

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

finding_clampWeights_normalizeWeightUpdates_incorrect_calculation_of_sumOtherWeights_proportionalRemainder

Likelihood: Medium/High, when a weight is above absoluteMax. Impact: Low/Medium, weights deviate much faster, and sum of weights also.

Support

FAQs

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