QuantAMM

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

Potential for Weights to Become Negative or Exceed One During Updates

Summary:

The calculateBlockNormalisedWeight function may compute weights that become negative or exceed 1e18 due to the application of multipliers over time without proper clamping. This can violate the requirement that individual weights must remain within the range [0, 1e18], and the assumption that "At any given block the pool is a fixed weighted balancer pool." Negative weights or weights exceeding one can lead to incorrect calculations and significant financial discrepancies.


Root Cause:

When calculating updated weights, the function does not enforce bounds to ensure that each weight remains within the acceptable range. While the _calculateMultiplerAndSetWeights function attempts to compute the lastTimestampThatInterpolationWorks to prevent weights from hitting guard rails, there might be discrepancies due to rounding errors or unforeseen edge cases.

Vulnerable Code:

In calculateBlockNormalisedWeight:

function calculateBlockNormalisedWeight(
int256 weight,
int256 multiplier,
uint256 timeSinceLastUpdate
) internal pure returns (uint256) {
int256 multiplierScaled18 = multiplier * 1e18;
if (multiplier > 0) {
return uint256(weight) + FixedPoint.mulDown(uint256(multiplierScaled18), timeSinceLastUpdate);
} else {
return uint256(weight) - FixedPoint.mulUp(uint256(-multiplierScaled18), timeSinceLastUpdate);
}
}

No checks are performed to ensure 0 <= weight <= 1e18 after the calculation.


Attack Path:

  1. Multiplier Misalignment:

    • Due to timing differences or rounding errors, the calculated multiplier * timeSinceLastUpdate may exceed expected bounds.

    • Weights may inadvertently become negative or exceed 1e18 after the addition or subtraction.

  2. Violation of Weight Bounds:

    • Negative weights or weights greater than 1e18 are invalid in the context of a weighted pool.

    • The pool's calculations for swaps and other operations become invalid.

  3. Exploitation and Errors:

    • An attacker might exploit this by manipulating timeSinceLastUpdate or the multiplier to cause weights to go out of bounds.

    • Even without malicious intent, normal operation could result in invalid weights due to lack of clamping.


Proof of Concept (PoC):

Suppose:

  • Initial weight is 0.05e18 (5%)

  • multiplier is negative due to strategy adjustments, e.g., -0.01e18

  • timeSinceLastUpdate is large, say 10, due to delayed updates

Calculations:

uint256 subtractionAmount = FixedPoint.mulUp(uint256(-(-0.01e18 * 1e18)), 10);
// subtractionAmount = 0.01e18 * 10 = 0.1e18
uint256 newWeight = 0.05e18 - 0.1e18; // Results in underflow, negative weight

Similarly, for positive multipliers, the weight may exceed 1e18.


Recommendation:

Implement clamping in calculateBlockNormalisedWeight to ensure weights remain within the bounds [0, 1e18].

Modified Code:

function calculateBlockNormalisedWeight(
int256 weight,
int256 multiplier,
uint256 timeSinceLastUpdate
) internal pure returns (uint256) {
int256 multiplierScaled18 = multiplier * 1e18;
int256 updatedWeight;
if (multiplier > 0) {
updatedWeight = weight + int256(FixedPoint.mulDown(uint256(multiplierScaled18), timeSinceLastUpdate));
} else {
updatedWeight = weight - int256(FixedPoint.mulUp(uint256(-multiplierScaled18), timeSinceLastUpdate));
}
// Clamp weight between 0 and 1e18
if (updatedWeight < 0) {
updatedWeight = 0;
} else if (updatedWeight > int256(FixedPoint.ONE)) {
updatedWeight = int256(FixedPoint.ONE);
}
return uint256(updatedWeight);
}
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!