https://github.com/Cyfrin/2024-12-quantamm/blob/a775db4273eb36e7b4536c5b60207c9f17541b92/pkg/pool-quantamm/contracts/rules/base/QuantammVarianceBasedRule.sol#L51-L192
function _calculateQuantAMMVariance(
int256[] memory _newData,
QuantAMMPoolParameters memory _poolParameters
) internal returns (int256[] memory) {
QuantAMMVarianceLocals memory locals;
locals.n = _poolParameters.numberOfAssets;
locals.finalState = new int256[](locals.n);
locals.intermediateVarianceState = _quantAMMUnpack128Array(
intermediateVarianceStates[_poolParameters.pool],
locals.n
);
locals.nMinusOne = locals.n - 1;
locals.notDivisibleByTwo = locals.n % 2 != 0;
locals.convertedLambda = int256(_poolParameters.lambda[0]);
locals.oneMinusLambda = ONE - locals.convertedLambda;
if (_poolParameters.lambda.length == 1) {
if (locals.notDivisibleByTwo) {
unchecked {
--locals.nMinusOne;
}
}
for (uint i; i < locals.nMinusOne; ) {
locals.intermediateState =
locals.convertedLambda.mul(locals.intermediateVarianceState[i]) +
(_newData[i] - _poolParameters.movingAverage[locals.n + i])
.mul(_newData[i] - _poolParameters.movingAverage[i])
.div(TENPOWEIGHTEEN);
locals.intermediateVarianceState[i] = locals.intermediateState;
locals.finalState[i] = locals.oneMinusLambda.mul(locals.intermediateState);
unchecked {
locals.secondIndex = i + 1;
}
locals.intermediateState =
locals.convertedLambda.mul(locals.intermediateVarianceState[locals.secondIndex]) +
(_newData[locals.secondIndex] - _poolParameters.movingAverage[locals.n + locals.secondIndex])
.mul(_newData[locals.secondIndex] - _poolParameters.movingAverage[locals.secondIndex])
.div(TENPOWEIGHTEEN);
locals.intermediateVarianceState[locals.secondIndex] = locals.intermediateState;
intermediateVarianceStates[_poolParameters.pool][locals.storageIndex] = _quantAMMPackTwo128(
locals.intermediateVarianceState[i],
locals.intermediateVarianceState[locals.secondIndex]
);
locals.finalState[locals.secondIndex] = locals.oneMinusLambda.mul(locals.intermediateState);
unchecked {
i += 2;
++locals.storageIndex;
}
}
if (locals.notDivisibleByTwo) {
unchecked {
++locals.nMinusOne;
}
locals.intermediateState =
locals.convertedLambda.mul(locals.intermediateVarianceState[locals.nMinusOne]) +
(_newData[locals.nMinusOne] - _poolParameters.movingAverage[locals.n + locals.nMinusOne])
.mul(_newData[locals.nMinusOne] - _poolParameters.movingAverage[locals.nMinusOne])
.div(TENPOWEIGHTEEN);
locals.intermediateVarianceState[locals.nMinusOne] = locals.intermediateState;
locals.finalState[locals.nMinusOne] = locals.oneMinusLambda.mul(locals.intermediateState);
intermediateVarianceStates[_poolParameters.pool][locals.storageIndex] = locals
.intermediateVarianceState[locals.nMinusOne];
}
} else {
for (uint i; i < locals.nMinusOne; ) {
unchecked {
locals.convertedLambda = int256(_poolParameters.lambda[i]);
locals.oneMinusLambda = ONE - locals.convertedLambda;
}
locals.intermediateState =
locals.convertedLambda.mul(locals.intermediateVarianceState[i]) +
(_newData[i] - _poolParameters.movingAverage[locals.n + i])
.mul(_newData[i] - _poolParameters.movingAverage[i])
.div(TENPOWEIGHTEEN);
locals.intermediateVarianceState[i] = locals.intermediateState;
locals.finalState[i] = locals.oneMinusLambda.mul(locals.intermediateState);
unchecked {
locals.secondIndex = i + 1;
locals.convertedLambda = int256(_poolParameters.lambda[i + 1]);
locals.oneMinusLambda = ONE - locals.convertedLambda;
}
locals.intermediateState =
locals.convertedLambda.mul(locals.intermediateVarianceState[locals.secondIndex]) +
(_newData[locals.secondIndex] - _poolParameters.movingAverage[locals.n + locals.secondIndex])
.mul(_newData[locals.secondIndex] - _poolParameters.movingAverage[locals.secondIndex])
.div(TENPOWEIGHTEEN);
locals.intermediateVarianceState[locals.secondIndex] = locals.intermediateState;
intermediateVarianceStates[_poolParameters.pool][locals.storageIndex] = _quantAMMPackTwo128(
locals.intermediateVarianceState[i],
locals.intermediateVarianceState[locals.secondIndex]
);
locals.finalState[locals.secondIndex] = locals.oneMinusLambda.mul(locals.intermediateState);
unchecked {
i += 2;
++locals.storageIndex;
}
}
if (locals.notDivisibleByTwo) {
unchecked {
++locals.nMinusOne;
locals.convertedLambda = int256(_poolParameters.lambda[locals.nMinusOne]);
locals.oneMinusLambda = ONE - locals.convertedLambda;
}
locals.intermediateState =
locals.convertedLambda.mul(locals.intermediateVarianceState[locals.nMinusOne]) +
(_newData[locals.nMinusOne] - _poolParameters.movingAverage[locals.n + locals.nMinusOne])
.mul(_newData[locals.nMinusOne] - _poolParameters.movingAverage[locals.nMinusOne])
.div(TENPOWEIGHTEEN);
locals.intermediateVarianceState[locals.nMinusOne] = locals.intermediateState;
locals.finalState[locals.nMinusOne] = locals.oneMinusLambda.mul(locals.intermediateState);
intermediateVarianceStates[_poolParameters.pool][locals.storageIndex] = locals
.intermediateVarianceState[locals.nMinusOne];
}
}
return locals.finalState;
}
For scalar lambda, locals.nMinusOne is decremented correctly for odd-asset pools. However, for vector lambda, this decrement is missing, causing an out-of-bounds error when accessing _poolParameters.lambda[locals.nMinusOne]
.
The missing decrement for vector lambda causes an out-of-bounds error, leading to a Denial of Service (DoS) that disrupts operations like CalculateNewWeights in odd-asset pools.