Summary
The DifferenceMomentumUpdateRule::_getWeightslack a check for (locals.res >= 0) when the (locals.kappaStore.length == 1).
Vulnerability Details
In the _getWeights function, when locals.kappaStore.length == 1, it does not check whether the calculated weights are greater than 0 before storing the value. This could result in invalid or incorrect weight values being stored.
https://github.com/Cyfrin/2024-12-quantamm/blob/a775db4273eb36e7b4536c5b60207c9f17541b92/pkg/pool-quantamm/contracts/rules/DifferenceMomentumUpdateRule.sol#L118
if (locals.kappaStore.length == 1) {
locals.normalizationFactor /= int256(locals.prevWeightLength);
for (locals.i = 0; locals.i < locals.prevWeightLength; ) {
int256 res = int256(_prevWeights[locals.i]) +
locals.kappaStore[0].mul(locals.newWeights[locals.i] - locals.normalizationFactor);
-> Not checked ->
newWeightsConverted[locals.i] = res;
unchecked {
++locals.i;
}
}
} else {
int256 sumKappa;
for (locals.i = 0; locals.i < locals.kappaStore.length; ) {
sumKappa += locals.kappaStore[locals.i];
unchecked {
++locals.i;
}
}
locals.normalizationFactor = locals.normalizationFactor.div(sumKappa);
for (locals.i = 0; locals.i < _prevWeights.length; ) {
locals.res =
int256(_prevWeights[locals.i]) +
locals.kappaStore[locals.i].mul(locals.newWeights[locals.i] - locals.normalizationFactor);
-> checked -> require(locals.res >= 0, "Invalid weight");
newWeightsConverted[locals.i] = locals.res;
unchecked {
++locals.i;
}
}
}
Impact
Weights that are less than 0 will also be stored and passed further for calculation, leading to incorrect results
Tools Used
manual review
Recommendations
if (locals.kappaStore.length == 1) {
//scalar logic separate to vector for efficiency
locals.normalizationFactor /= int256(locals.prevWeightLength);
// To avoid intermediate overflows (because of normalization), we only downcast in the end to an uint6
// κ · ( (EWMA_short - EWMA_long) / EWMA_long − ℓp(t))
for (locals.i = 0; locals.i < locals.prevWeightLength; ) {
int256 res = int256(_prevWeights[locals.i]) +
locals.kappaStore[0].mul(locals.newWeights[locals.i] - locals.normalizationFactor);
+ require(res >= 0, "Invalid weight");
newWeightsConverted[locals.i] = res;
unchecked {
++locals.i;
}
}
} else {
//vector logic separate to vector for efficiency
int256 sumKappa;
for (locals.i = 0; locals.i < locals.kappaStore.length; ) {
sumKappa += locals.kappaStore[locals.i];
unchecked {
++locals.i;
}
}
locals.normalizationFactor = locals.normalizationFactor.div(sumKappa);
// To avoid intermediate overflows (because of normalization), we only downcast in the end to an uint6
for (locals.i = 0; locals.i < _prevWeights.length; ) {
locals.res =
int256(_prevWeights[locals.i]) +
locals.kappaStore[locals.i].mul(locals.newWeights[locals.i] - locals.normalizationFactor);
require(locals.res >= 0, "Invalid weight");
newWeightsConverted[locals.i] = locals.res;
unchecked {
++locals.i;
}
}
}