The QuantAMMWeightedPool contract manages a pool of tokens where each token has a normalized weight. These weights can change over time based on certain multipliers and update intervals. A fundamental invariant of such pools is that the weights of all tokens must:
be greater than zero and the total of all weights must equal one (when normalized).
Maintaining these invariants is crucial for the correct operation of the pool, ensuring fairness in trading and accurate price calculations. Let's see how this is broken below.
The issue arises because the calculateBlockNormalisedWeight function does not properly handle negative multipliers, leading to negative weights or weights that deviate from expected bounds.
When multiplier is negative, the function enters the else block. It attempts to calculate the new weight by subtracting the product of the negative multiplier and timeSinceLastUpdate from the current weight.
newWeight = uint256(weight) - FixedPoint.mulUp(uint256(-multiplierScaled18), timeSinceLastUpdate);
Since multiplier is negative, -multiplierScaled18 becomes positive. When the product of -multiplierScaled18 and timeSinceLastUpdate is greater than weight, the subtraction results in a negative value.
example:
Let's say weight = 0.1e18 (representing 10% weight in 18 decimal fixed-point format).
multiplier = -0.05e18 (negative multiplier).
timeSinceLastUpdate = 3.
Calculating multiplierScaled18:
product = FixedPoint.mulUp(uint256(-multiplierScaled18), timeSinceLastUpdate)
= FixedPoint.mulUp(0.05e36, 3)
= 0.15e36
newWeight = uint256(0.1e18) - 0.15e36
This results in a negative value, which is not valid when casting to uint256.
Casting a negative int256 to uint256 does not throw an error but results in a very large number due to underflow.
The negative value wraps around to a large positive value (2^256 - value). This will lead to unintended and invalid weights.
Casting a negative int256 to uint256 does not throw an error but results in a very large number due to underflow which is unintended.
Manual Review
After computing the new weight, check if it remains within acceptable bounds (greater than zero and less than or equal to 1e18)
Utilize safe math operations that handle overflows and underflows.
For signed integers, use libraries that provide safe arithmetic operations, such as PRBMathSD59x18.
_clampWeights will check that these weights are positive and in the boundaries before writing them in storage.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.