QuantAMM

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

GradientBasedRules will not work for >=4 assets with vector lambdas

Summary

UpdateRules based on GradientBasedRulewill revert/produce incorrect result when if numAssets >= 4 and lambda.length > 1. It's due to a simple mistake in updating intermediateGradientStates.

Vulnerability Details

Root Cause

We have the following in QuantammGradientBasedRule.sol:156

intermediateGradientStates[_poolParameters.pool][i] = _quantAMMPackTwo128( // @audit i should be replaced by locals.storageArrayIndex
locals.intermediateGradientState[i],
locals.secondIntermediateValue
);
unchecked {
i += 2;
++locals.storageArrayIndex;
}

IntermediateGradientStates is a mapping to store quantum packed gradient values. If something is quantum packed, it means it will shrink rougly by half in size.

So IntermediateGradientStates[pool].length <= ceil(numAssets / 2)

In the above codebase, i is incrased up to numAssets - 2. So if numAssets >= 4, intermediateGradientStates[_poolParameters.pool][i] will panic with array out of bound error (2 > 1)

Indeed, i should be replaced by locals.storageArrayIndex

PS: Interestingly, for numAssets = 5, array OOB error is not observed, because storage length is ceil(5/2) = 3and iis increased up to 3. So there won't be OOB error, but due to the wrong usage of storage index, intermediate values will be wrong.

POC

Put the following content to QuantAMMMomentum.sol and run forge test QuantAMMMomentum --match-test testRevertWithFourAssetsAndVectorLambdas -vvv

function testRevertWithFourAssetsAndVectorLambdas() public {
uint256 numAssets = 4;
// Define local variables for the parameters
int256[][] memory parameters = new int256[][]();
parameters[0] = new int256[]();
parameters[0][0] = PRBMathSD59x18.fromInt(1);
parameters[1] = new int256[]();
parameters[1][0] = PRBMathSD59x18.fromInt(1);
parameters[2] = new int256[]();
parameters[2][0] = PRBMathSD59x18.fromInt(1);
parameters[3] = new int256[]();
parameters[3][0] = PRBMathSD59x18.fromInt(1);
int256[] memory previousAlphas = new int256[]();
previousAlphas[0] = PRBMathSD59x18.fromInt(1);
previousAlphas[1] = PRBMathSD59x18.fromInt(2);
previousAlphas[2] = PRBMathSD59x18.fromInt(3);
int256[] memory prevMovingAverages = new int256[]();
int256[] memory movingAverages = new int256[]();
movingAverages[0] = 0.9e18;
int128[] memory lambdas = new int128[]();
lambdas[0] = int128(0.7e18);
lambdas[1] = int128(0.7e18);
lambdas[2] = int128(0.7e18);
lambdas[3] = int128(0.7e18);
int256[] memory prevWeights = new int256[]();
prevWeights[0] = 0.2e18;
prevWeights[1] = 0.2e18;
prevWeights[2] = 0.2e18;
prevWeights[3] = 0.2e18;
int256[] memory data = new int256[]();
int256[] memory expectedResults = new int256[]();
/**
* vm.expectRevert doesn't work for internal test functions, so just copied runInitialUpdate and inserted vm.expectRevert
*/
mockPool.setNumberOfAssets(numAssets);
vm.startPrank(owner);
rule.initialisePoolRuleIntermediateValues(address(mockPool), prevMovingAverages, previousAlphas, numAssets);
vm.expectRevert(stdError.indexOOBError);
rule.CalculateUnguardedWeights(prevWeights, data, address(mockPool), parameters, lambdas, movingAverages);
vm.stopPrank();
}

Impact

  • All gradient based rules (Antimomentum, ChannelFollowing, DifferenceMomentum, PowerChannel etc) won't work with >=4 assets and lambdas parameters

    • If numAssets is 4, 6, 8, and lambdas.length > 1, update weights will revert with index out of bound error

    • For numAssets 5, 7 and lambdas.length > 1, weights calculation will be incorrect because wrong index is used for intermediate value storage

Tools Used

Foundry

Recommendations

Apply the following patch

diff --git a/pkg/pool-quantamm/contracts/rules/base/QuantammGradientBasedRule.sol b/pkg/pool-quantamm/contracts/rules/base/QuantammGradientBasedRule.sol
index e6bbcdc..f1f6d9f 100644
--- a/pkg/pool-quantamm/contracts/rules/base/QuantammGradientBasedRule.sol
+++ b/pkg/pool-quantamm/contracts/rules/base/QuantammGradientBasedRule.sol
@@ -153,7 +153,7 @@ abstract contract QuantAMMGradientBasedRule is ScalarRuleQuantAMMStorage {
locals.finalValues[locals.secondIndex] = locals.mulFactor.mul(locals.secondIntermediateValue);
- intermediateGradientStates[_poolParameters.pool][i] = _quantAMMPackTwo128(
+ intermediateGradientStates[_poolParameters.pool][locals.storageArrayIndex] = _quantAMMPackTwo128(
locals.intermediateGradientState[i],
locals.secondIntermediateValue
);
Updates

Lead Judging Commences

n0kto Lead Judge 10 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.

Give us feedback!