Summary:
In the QuantAMMWeightedPool contract, the dynamic calculation of weights via the calculateBlockNormalisedWeight function does not ensure that the sum of normalized weights equals one (in 18 decimal fixed-point format) at every block. This oversight can lead to situations where, at a specific block, the weights do not sum to one, violating the assumption that "At any given block the pool is a fixed weighted balancer pool." This may result in incorrect swap calculations, deviation from expected pool behavior, and potential financial discrepancies for users.
Root Cause:
The calculateBlockNormalisedWeight function computes each token's weight individually based on the last known weight, a multiplier, and the elapsed time since the last update. However, it does not include a normalization step to adjust all weights in proportion so that their sum equals one after the individual weights have been updated. Over time, due to rounding errors or cumulative adjustments, the total weight may drift away from the expected total of FixedPoint.ONE (1e18), causing the pool to violate the fixed-sum weight invariant.
Vulnerable Code:
In QuantAMMWeightedPool.sol:
In _getNormalizedWeights function, after calculating individual weights, there is no normalization step:
Attack Path:
Weight Drift Over Time:
Due to the absence of normalization, individual weights may increase or decrease based on multipliers and time, causing the total weight sum to deviate from one.
Small rounding errors can accumulate, especially over prolonged periods or with frequent updates.
Invariant Violation:
At a given block, the weights do not sum to one, causing the pool to no longer represent a proper weighted balancer pool.
This violates the fundamental assumption that "At any given block the pool is a fixed weighted balancer pool."
Incorrect Swap Calculations:
Swap functions rely on correct weights summing to one to compute exchange rates accurately.
Deviations can lead to incorrect exchange rates, unfair trades, and potential financial loss for users or arbitrage opportunities for attackers.
Potential for Exploitation:
Malicious actors might exploit the deviation to perform arbitrage trades.
Users may receive less value than expected when swapping or withdrawing liquidity.
Proof of Concept (PoC):
Consider a pool with two tokens, where initial weights are both 0.5e18 (representing 50% each), summing to 1e18.
Assume that due to multipliers and elapsed time, the weights adjust as follows:
After Updates:
Token A weight: 0.5000001e18
Token B weight: 0.5000001e18
Total Weight Sum: 1.0000002e18
The total weights now exceed 1e18. This leads to the pool invariant being broken.
Alternatively, if weights adjust to:
After Updates:
Token A weight: 0.499999e18
Token B weight: 0.499999e18
Total Weight Sum: 0.999998e18
The total weights are now less than 1e18, again violating the pool's invariant.
This discrepancy can cause the pool to calculate incorrect prices for swaps, leading to users receiving incorrect amounts.
Recommendation:
Implement a normalization step after calculating the adjusted weights to ensure that the sum of all weights equals FixedPoint.ONE (1e18). This can be done by calculating the total weight sum of the individually adjusted weights and then scaling each weight proportionally.
Modified Code:
Update the _getNormalizedWeights function as follows:
By normalizing the weights after calculation, we ensure that at every block, the sum of the weights is exactly 1e18
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.
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.