QuantAMM

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

Array packing underflow in QuantAMM pool initialization

Summary

A potential arithmetic underflow bug exists in QuantAMM's pool initialization process when attempting to initialize a pool with a single asset. The bug occurs in _quantAMMPack128Array when calculating sourceArrayLengthMinusTwo = sourceArrayLength - 2 for single-element arrays.

function _quantAMMPack128Array(int256[] memory _sourceArray) internal pure returns (int256[] memory targetArray) {
uint sourceArrayLength = _sourceArray.length;
uint targetArrayLength = sourceArrayLength;
uint storageIndex;
require(_sourceArray.length != 0, "LEN0");
if (_sourceArray.length % 2 == 0) {
// extra code ...
} else {
// ... extra code
targetArray = new int256[](targetArrayLength);
uint sourceArrayLengthMinusTwo = sourceArrayLength - 2; // @audit can underflow if sourceArrayLength is 1
// extra code ...
}
}

Impact

Pool creators will experience failed transactions and waste gas when attempting to initialize single-asset pools, leading to actual financial costs every time the revert happens due to the underflow.

Proof of concept

Add testSingleElementArrayBug test in pkg/pool-quantamm/test/foundry/rules/UpdateRule.t.sol and run the test :)

function testSingleElementArrayBug() public {
vm.startPrank(owner);
// Setup single element arrays
int256[] memory singleMovingAverage = new int256[]();
singleMovingAverage[0] = PRBMathSD59x18.fromInt(1);
int256[] memory singleAlpha = new int256[]();
singleAlpha[0] = PRBMathSD59x18.fromInt(1);
// Initialize with numberOfAssets = 1
updateRule.initialisePoolRuleIntermediateValues(
address(mockPool),
singleMovingAverage,
singleAlpha,
1 // This triggers the bug condition
);
// Create minimum required params for CalculateNewWeights
int256[][] memory parameters = new int256[][]();
parameters[0] = new int256[]();
parameters[0][0] = PRBMathSD59x18.fromInt(1);
uint64[] memory lambdas = new uint64[]();
lambdas[0] = uint64(0.7e18);
int256[] memory prevWeights = new int256[]();
prevWeights[0] = PRBMathSD59x18.fromInt(1);
int256[] memory data = new int256[]();
data[0] = PRBMathSD59x18.fromInt(1);
// This should revert due to the underflow in _quantAMMPack128Array
updateRule.CalculateNewWeights(
prevWeights,
data,
address(mockPool),
parameters,
lambdas,
uint64(0.1e18),
uint64(0.1e18)
);
vm.stopPrank();
}

The result logs of underflow:

[17536] UpdateRuleTest::testSingleElementArrayBug()
├─ [0] VM::startPrank(0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf)
│ └─ ← [Return]
├─ [4174] MockUpdateRule::initialisePoolRuleIntermediateValues(0x0000000000000000000000000000000000000000, [1000000000000000000 [1e18]], [1000000000000000000 [1e18]], 1)
│ └─ ← [Revert] panic: arithmetic underflow or overflow (0x11)
└─ ← [Revert] panic: arithmetic underflow or overflow (0x11)
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 14.86ms (2.54ms CPU time)
Ran 1 test suite in 1.77s (14.86ms CPU time): 0 tests passed, 1 failed, 0 skipped (1 total tests)
Failing tests:
Encountered 1 failing test in test/foundry/rules/UpdateRule.t.sol:UpdateRuleTest
[FAIL: panic: arithmetic underflow or overflow (0x11)] testSingleElementArrayBug() (gas: 17536)
Encountered a total of 1 failing tests, 0 tests succeeded

Recommendation

Add validation in UpdateRule.sol:

function initialisePoolRuleIntermediateValues(
address _poolAddress,
int256[] memory _newMovingAverages,
int256[] memory _newInitialValues,
uint _numberOfAssets
) external {
require(_numberOfAssets >= 2, "MIN_ASSETS"); // Add minimum asset check
require(msg.sender == _poolAddress || msg.sender == updateWeightRunner, "UNAUTH");
_setInitialMovingAverages(_poolAddress, _newMovingAverages, _numberOfAssets);
_setInitialIntermediateValues(_poolAddress, _newInitialValues, _numberOfAssets);
}

If single-asset pools are intended to be supported, modify the array packing logic in _quantAMMPack128Array to handle single-element arrays safely.

Updates

Lead Judging Commences

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.

Appeal created

0xtheblackpanther Submitter
4 months ago
honour Auditor
4 months ago
n0kto Lead Judge
4 months ago
n0kto Lead Judge 3 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.