QuantAMM

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

Missing Weight Sum Validation in setWeights() Allows Price Manipulation Through Incorrect Total Weights

Summary

The setWeights function in the QuantAMMWeightedPool contract fails to validate that weights sum to FixedPoint.ONE (100%), a critical invariant for weighted pools. While this validation exists in the _setInitialWeights function during pool initialization, its omission in setWeights creates a vulnerability in subsequent weight updates.

The contract fails to enforce the foundational weighted pool requirement that weights must sum to 100% (FixedPoint.ONE) in the setWeights function:

https://github.com/Cyfrin/2024-12-quantamm/blob/a775db4273eb36e7b4536c5b60207c9f17541b92/pkg/pool-quantamm/contracts/QuantAMMWeightedPool.sol#L617

function setWeights(
int256[] calldata _weights,
address _poolAddress,
uint40 _lastInterpolationTimePossible
) external override {
require(msg.sender == address(updateWeightRunner), "ONLYUPDW");
require(_weights.length == _totalTokens * 2, "WLDL");
// No validation for weights summing to FixedPoint.ONE
...
}

The missing validation introduces a critical vulnerability in the pool's arithmetic model. Without enforcing the total weight constraint:

Weights summing to less than 100% lead to systematic underpricing of pool assets, as each token's relative weight appears smaller than intended. This creates exploitable arbitrage opportunities through buy/sell loops.

Conversely, weights exceeding 100% cause asset overpricing, enabling profitable manipulation through repeated sell/buy cycles.

The persistent nature of weights means incorrect values remain active until manually updated, potentially allowing extended exploitation periods. This directly threatens pool stability and liquidity provider capital.

For example, if weights sum to 95%, an attacker could profit by repeatedly:

  1. Buying tokens at artificially low prices due to deflated weights

  2. Selling them back when market prices normalize

  3. Extracting value from the pool with each cycle

Recommended Mitigation Steps

Add explicit validation that weights sum to FixedPoint.ONE in the setWeights function:

function setWeights(
int256[] calldata _weights,
address _poolAddress,
uint40 _lastInterpolationTimePossible
) external override {
require(msg.sender == address(updateWeightRunner), "ONLYUPDW");
require(_weights.length == _totalTokens * 2, "WLDL");
// Validate sum of weights equals ONE
int256 normalizedSum;
uint256 halfLength = _weights.length / 2;
for (uint256 i = 0; i < halfLength; i++) {
normalizedSum += _weights[i];
}
// Ensure weights sum to ONE
if (uint256(normalizedSum) != FixedPoint.ONE) {
revert NormalizedWeightInvariant();
}
// Rest of the function...
}
Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

invalid_sum_of_weights_can_exceeds_one_no_guard

According the sponsor and my understanding, sum of weights does not have to be exactly 1 to work fine. So no real impact here. Please provide a PoC showing a realistic impact if you disagree. This PoC cannot contains negative weights because they will be guarded per clampWeights.

Support

FAQs

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