QuantAMM

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

Token pool can misallocate assets due to negative weight in `AntiMomentumUpdateRule`

Summary

BalancerV3 pools rely on nonnegative weights to determine proportional ownership of liquidity. In BalancerV3, each token is assigned a weight that must be >= 0. Negative weights would represent a “negative allocation” of an asset, which does not make sense in typical AMM math.

Problem is the current logic allow a negative weight to be set.

Vulnerability Details

The code in AntiMomentumUpdateRule.sol’s _getWeights() method:

https://github.com/Cyfrin/2024-12-quantamm/blob/a775db4273eb36e7b4536c5b60207c9f17541b92/pkg/pool-quantamm/contracts/rules/AntimomentumUpdateRule.sol#L82

if (locals.kappa.length == 1) {
// ...
int256 res = int256(_prevWeights[locals.i]) +
int256(locals.kappa[0]).mul(locals.normalizationFactor - locals.newWeights[locals.i]);
// ❌ No require(res >= 0) here
newWeightsConverted[locals.i] = res;
...
} else {
// ✅ For multi-kappa:
require(res >= 0, "Invalid weight");
}

In the single-κ branch above, there is no require(res >= 0). If _prevWeights[locals.i] or locals.newWeights[locals.i] are large enough in opposite directions, res can become negative. Balancer pools cannot handle negative weights, so this can lead to invalid pool states or reverts down the line.

The pool’s formula for token prices and invariant depends on positive weights. Negative weights cause reverts or incorrect spot prices. This disrupts trading and can freeze the pool.

Impact

  • Misallocation of assets can lead to loss of funds.

Tools Used

Manual Review & BalancerV3 docs

Recommendations

Require nonnegative final weights when the locals.kappa.length == 1:

if (locals.kappa.length == 1) {
locals.normalizationFactor /= int256(_prevWeights.length);
// w(t − 1) + κ ·(ℓp(t) − 1/p(t) · ∂p(t)/∂t)
for (locals.i = 0; locals.i < _prevWeights.length; ) {
int256 res = int256(_prevWeights[locals.i]) +
int256(locals.kappa[0]).mul(locals.normalizationFactor - locals.newWeights[locals.i]);
+ require(res >= 0, "Invalid weight");
newWeightsConverted[locals.i] = res;
unchecked {
++locals.i;
}
}
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.

Give us feedback!