QuantAMM

QuantAMM
49,600 OP
View results
Submission Details
Severity: high
Valid

The intermediate states of gradient calculation are not stored properly in QuantammGradientBasedRule contract resulting in the failure of the QuantAMM rules.

Summary

The function _calculateQuantAMMGradient in QuantammGradientBasedRule.sol contains a critical indexing error when handling the pools with more than 3 assets and with each asset having different lambda(vector lambda case). The error causes the intermediate states of gradient calculation for the assets 3 onwards to be stored at incorrect array index. This results in the erroneous gradient calculation which is further used in many QuantAMM contracts – leading to the failure of the QuantAMM rules for momentum, anti momentum, power channel and channel following.

Vulnerability Details

In the function _calculateQuantAMMGradient of the QuantammGradientBasedRule contract, the computed intermediateGradientState are packed by calling another internal function _quantAMMPackTwo128 in the QuantAMMStorage contract.

When the _poolParameters.lambda is a vector, there is an error in the storage of intermediateGradientStates for the pool whenever the number of assets in the pool exceed 3 in number (i.e. for 4 and above). This is due to the fact that the index variable used for the storage of the packed two consecutive intermediateGradientStates is incorrectly used as the loop variable itself (which increments by 2 in each iteration of the loop) instead of the storage index variable ‘locals.storageArrayIndex’ which increments by 1 in each iteration of the loop as required.

https://github.com/Cyfrin/2024-12-quantamm/blob/a775db4273eb36e7b4536c5b60207c9f17541b92/pkg/pool-quantamm/contracts/rules/base/QuantammGradientBasedRule.sol#L148-L163

locals.secondIntermediateValue =
convertedLambda.mul(locals.intermediateGradientState[locals.secondIndex]) +
(_newData[locals.secondIndex] - _poolParameters.movingAverage[locals.secondIndex]).div(
oneMinusLambda
);
locals.finalValues[locals.secondIndex] = locals.mulFactor.mul(locals.secondIntermediateValue);
intermediateGradientStates[_poolParameters.pool][i] = _quantAMMPackTwo128(
locals.intermediateGradientState[i],
locals.secondIntermediateValue
);
unchecked {
i += 2;
++locals.storageArrayIndex;
}

Impact

Very High
The packed calculated gradients will not get stored in the desired locations leading to all further/future computations erroneous. It will even corrupt other parameter(s) which are mapped in the memory immediately following this array.

If the intermediateGradientState states (each one is 256 bit integer containing the required data in the least significant 128 bits of the 256 bit integers) for a pool with 8 tokens to be packed are
[1], [2], [3], [4], [5], [6], [7], [8],

It is expected that they are stored as
[1, 2], [3, 4], [5, 6], [7, 8].

Here in the output, the odd numbered tokens are occupying the Most significant 128 bits and the even numbered tokens are occupying the least significant 128 bits of the 256 bit integers

Due to the indexing error in the contract, the intermediateGradientState states gets stored as
[1, 2], [_, ], [3, 4], [, ], [5, 6], [, _], [7, 8].

This will impact the MomentumUpdateRule.sol, AntimomentumUpdateRule.sol,

Tools Used

Manual review

Recommended Mitigation

Just the storage index is to be corrected (correction is in the left hand side of the equation at line number 156 of the codebase) from the present

intermediateGradientStates[_poolParameters.pool][i]
to
intermediateGradientStates[_poolParameters.pool][ storageArrayIndex].

Corrected portion of the code is given below.

locals.secondIntermediateValue =
convertedLambda.mul(locals.intermediateGradientState[locals.secondIndex]) +
(_newData[locals.secondIndex] - _poolParameters.movingAverage[locals.secondIndex]).div(
oneMinusLambda
);
locals.finalValues[locals.secondIndex] = locals.mulFactor.mul(locals.secondIntermediateValue);
intermediateGradientStates[_poolParameters.pool][ storageArrayIndex] = _quantAMMPackTwo128( // @audit modified
locals.intermediateGradientState[i],
locals.secondIntermediateValue
);
unchecked {
i += 2;
++locals.storageArrayIndex;
}
Updates

Lead Judging Commences

n0kto Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding_gradient_rules_more_than_3_assets_and_1_lambda_will_DoS_the_update

Likelihood: Medium/High, assets>4, lambdas > 1. Impact: Medium/High, DoS update but pool works fine. Pool with 5 assets will use incorrect weights.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.