QuantAMM

QuantAMM
49,600 OP
View results
Submission Details
Severity: low
Invalid

Single asset pool DoS via underflow in variance calculation

Summary

Single-asset pools in the QuantAMM system are vulnerable to a DoS when updating weights due to improper array access in _calculateQuantAMMVariance.

Vulnerability Details

When CalculateNewWeights is called with n=1 asset, the following sequence triggers an array out-of-bounds error:

// In _calculateQuantAMMVariance
locals.n = 1;
locals.nMinusOne = 0;
locals.notDivisibleByTwo = true; // 1 % 2 != 0
if (locals.notDivisibleByTwo) {
unchecked { --locals.nMinusOne; } // Underflows to 2^256-1 @audit ❌
}
// Attempts to access array with massive index
locals.intermediateVarianceState[2^256-1] // Out of bounds ❌

Impact

Single-asset pools are completely blocked from functioning as they cannot update weights, effectively making them unusable with no possible workaround or alternative implementation available within the protocol.

Proof of Concept

Add the testSingleAssetPoolDoS test in pkg/pool-quantamm/test/foundry/rules/UpdateRule.t.sol and run it ;)

function testSingleAssetPoolDoS() public {
vm.startPrank(owner);
uint256 NUM_ASSETS = 2; // set to 2 initially
int256[] memory movingAverages = new int256[]();
movingAverages[0] = int256(1e18);
movingAverages[1] = int256(1e18);
int256[] memory initialValues = new int256[]();
initialValues[0] = int256(1e18);
initialValues[1] = int256(1e18);
// Initialize with 2 assets first
updateRule.initialisePoolRuleIntermediateValues(
address(updateRule),
movingAverages,
initialValues,
NUM_ASSETS
);
// Now try to calculate weights with 1 asset - @audit poc
int256[][] memory parameters = new int256[][]();
parameters[0] = new int256[]();
parameters[0][0] = int256(1e18);
uint64[] memory lambdas = new uint64[]();
lambdas[0] = uint64(0.7e18);
int256[] memory prevWeights = new int256[]();
prevWeights[0] = int256(1e18);
int256[] memory data = new int256[]();
data[0] = int256(1e18);
vm.expectRevert(); // will revert for single asset (n=1) @audit-poc
updateRule.CalculateNewWeights(
prevWeights,
data,
address(updateRule),
parameters,
lambdas,
uint64(0.1e18),
uint64(0.9e18)
);
vm.stopPrank();
}

Recommended Mitigation

Add special handling for single-asset pools:

function _calculateQuantAMMVariance(...) internal {
if (locals.n == 1) {
// Direct calculation for single asset ✅
return calculateSingleAssetVariance(...);
}
// Existing logic for n > 1
}

Updates

Lead Judging Commences

n0kto Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas / Admin is trusted / Pool creation is trusted / User mistake / Suppositions

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelyhood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Appeal created

0xtheblackpanther Submitter
5 months ago
honour Auditor
5 months ago
n0kto Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas / Admin is trusted / Pool creation is trusted / User mistake / Suppositions

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelyhood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Support

FAQs

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