QuantAMM

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

Guard Rail Bypass in QuantAMM Weight Clamping Due to Incorrect Normalization Logic

Description

The _clampWeights function in QuantAMMMathGuard.sol contains a critical flaw in its weight normalization logic that allows weights to exceed their maximum bounds. The issue occurs during the rescaling phase:

https://github.com/Cyfrin/2024-12-quantamm/blob/a775db4273eb36e7b4536c5b60207c9f17541b92/pkg/pool-quantamm/contracts/rules/base/QuantammMathGuard.sol#L36

// First clamps to absoluteMax
if (_weights[i] > absoluteMax) {
_weights[i] = absoluteMax;
sumOtherWeights += absoluteMax;
}
// Later rescales ALL non-minimum weights, including those at absoluteMax
if (_weights[i] != absoluteMin) {
_weights[i] = _weights[i].mul(proportionalRemainder);
}

The issue is that the function fails to maintain the maximum bound during weight normalization. After clamping weights to absoluteMax, these same weights are incorrectly included in the rescaling step because the function only excludes weights at absoluteMin. This causes weights that were clamped to absoluteMax to be multiplied by proportionalRemainder, potentially pushing them above their intended maximum bounds.

Impact

The weight clamping bypass fundamentally undermines QuantAMM's risk management infrastructure. When a pool performs weight adjustments, particularly during high volatility periods, weights can exceed their maximum bounds due to subsequent rescaling. This creates a direct violation of the protocol's guard rail system.

The implications ripple through the entire pool's operation. Since weights can exceed their intended maximum bounds, the pool's risk profile becomes distorted. BTFs using trend-following or mean-reversion strategies may develop over-concentrated positions that violate their design parameters. These enlarged weight ranges affect the pool's pricing function and trading behavior in ways that weren't accounted for in the protocol's economic model.

The likelihood of this happening is high as it occurs in normal operation whenever weights need clamping to absoluteMax while requiring upward rescaling of remaining weights to maintain the sum-to-one constraint.

Recommended Mitigation Steps

function _clampWeights(
int256[] memory _weights,
int256 _absoluteWeightGuardRail
) internal pure returns (int256[] memory) {
uint weightLength = _weights.length;
if (weightLength == 1) return _weights;
int256 absoluteMin = _absoluteWeightGuardRail;
int256 absoluteMax = ONE - (PRBMathSD59x18.fromInt(int256(weightLength - 1)).mul(_absoluteWeightGuardRail));
int256 sumRemainerWeight = ONE;
int256 sumOtherWeights;
// Track which weights are at their bounds
bool[] memory isAtBound = new bool[]();
for (uint i; i < weightLength; ++i) {
if (_weights[i] < absoluteMin) {
_weights[i] = absoluteMin;
sumRemainerWeight -= absoluteMin;
isAtBound[i] = true;
} else if (_weights[i] > absoluteMax) {
_weights[i] = absoluteMax;
sumOtherWeights += absoluteMax;
isAtBound[i] = true;
}
}
if (sumOtherWeights != 0) {
int256 proportionalRemainder = sumRemainerWeight.div(sumOtherWeights);
for (uint i; i < weightLength; ++i) {
// Only rescale weights that aren't at either bound
if (!isAtBound[i]) {
_weights[i] = _weights[i].mul(proportionalRemainder);
}
}
}
return _weights;
}

The fix properly tracks and excludes both minimum and maximum-bound weights from rescaling, ensuring guard rails are maintained throughout the normalization process. This should be implemented with high priority as it affects a core safety mechanism of the protocol.

Updates

Lead Judging Commences

n0kto Lead Judge 10 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.