QuantAMM

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

Inconsistent weight validation in update rules allows negative weights when using scalar kappa

Summary

In several update rule contracts (MomentumUpdateRule, PowerChannelUpdateRule, DifferenceMomentumUpdateRule), there is an inconsistent validation of weight values. When kappa is provided as a vector, weights are validated to be non-negative, but this validation is missing when kappa is a scalar value. This inconsistency could lead to negative intermediate weights, as well as weights which are greater than 100%.

Vulnerability Details

The issue exists in multiple update rules. Here's an example from MomentumUpdateRule:

if (locals.kappaStore.length == 1) {
// SCALAR KAPPA CASE - no validation
locals.normalizationFactor /= int256(locals.prevWeightLength);
for (locals.i = 0; locals.i < locals.prevWeightLength; ) {
int256 res = int256(_prevWeights[locals.i]) +
locals.kappaStore[0].mul(locals.newWeights[locals.i] - locals.normalizationFactor);
newWeightsConverted[locals.i] = res; // No check if res >= 0
}
} else {
// VECTOR KAPPA CASE - includes validation
for (locals.i = 0; locals.i < _prevWeights.length; ) {
locals.res = int256(_prevWeights[locals.i]) +
locals.kappaStore[locals.i].mul(locals.newWeights[locals.i] - locals.normalizationFactor);
require(locals.res >= 0, "Invalid weight"); // Has check for res >= 0
newWeightsConverted[locals.i] = locals.res;
}
}

Calculation for new weight can be written as:

prevWeight + kappa * (gradient - avgGradient) < 0

Negative weights can easily occur if kappa is big enough.. Example scenario producing negative weight:

prevWeight = 0.2e18 // 0.2
kappa = 2e18 // 2.0
gradient = -0.5e18 // -0.5
avgGradient = 0.5e18 // 0.5
result = 0.2 + 2.0 * (-0.5 - 0.5) = 0.2 - 2.0 = -1.8

This negative result would be accepted in the scalar kappa case but rejected in the vector kappa case.

Impact

While negative intermediate weights should be eventually corrected through clamping and normalization in _guardQuantAMMWeights, this inconsistency introduces several issues:

  1. Inconsistent behavior between scalar and vector kappa modes

  2. Unnecessary complexity in dealing with negative intermediates

  3. Risk of unexpected behavior in normalization calculations

  4. Makes code harder to reason about as weights should conceptually never be negative

Impact is rated as Medium because:

  • There are recovery mechanisms through clamping and normalization

  • No direct fund loss

  • But introduces unnecessary complexity and inconsistent validation, and skews the calculations

Tools Used

Manual review

Recommendations

  1. Add consistent validation across both scalar and vector cases:

if (locals.kappaStore.length == 1) {
locals.normalizationFactor /= int256(locals.prevWeightLength);
for (locals.i = 0; locals.i < locals.prevWeightLength; ) {
int256 res = int256(_prevWeights[locals.i]) +
locals.kappaStore[0].mul(locals.newWeights[locals.i] - locals.normalizationFactor);
+ require(res >= 0, "Invalid weight");
newWeightsConverted[locals.i] = res;
}
}
Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

invalid_weights_can_be_negative_or_extreme_values

_clampWeights will check that these weights are positive and in the boundaries before writing them in storage.

Support

FAQs

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