QuantAMM

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

Underflow in _calculateCurrentBlockWeight Can Lead to Invalid Pool State

Description

The _calculateCurrentBlockWeight function in QuantAMMWeightedPool.sol contains an underflow vulnerability when the multiplier is negative. The issue occurs in the following calculation:

return uint256(weight) - FixedPoint.mulUp(uint256(-multiplierScaled18), timeSinceLastUpdate);

The function calculates the current block weight by subtracting a scaled multiplier times the time since the last update from the initial weight when the multiplier is negative. However, if timeSinceLastUpdate is large enough, the multiplication result could exceed uint256(weight), causing an underflow.

Impact

The underflow vulnerability has severe implications:

  1. Invalid Pool State: When the underflow occurs, the token weights will exceed 100% (1e18), violating a fundamental invariant of the pool that weights must sum to exactly 100%.

  2. Economic Exploitation: Incorrect weights directly affect swap calculations and pool operations. An attacker could:

    • Execute trades at incorrect prices

    • Manipulate liquidity ratios

    • Extract value from the pool through arbitrage

  3. Persistent Impact: Since the weights are used in critical paths like onSwap() and _getNormalizedWeights(), the invalid state affects all subsequent pool operations until fixed.

Mitigation

To fix this vulnerability, implement one or more of the following measures:

Add Weight Validation:

function _calculateCurrentBlockWeight(
int256 weight,
int256 multiplier,
uint256 timeSinceLastUpdate
) internal pure returns (uint256) {
int256 multiplierScaled18 = multiplier * 1e18;
if (multiplier > 0) {
return uint256(weight) + FixedPoint.mulDown(uint256(multiplierScaled18), timeSinceLastUpdate);
} else {
uint256 subtraction = FixedPoint.mulUp(uint256(-multiplierScaled18), timeSinceLastUpdate);
require(subtraction <= uint256(weight), "Weight underflow");
return uint256(weight) - subtraction;
}
}

Time Bound Check:

require(
timeSinceLastUpdate <= maxUpdateInterval,
"Time since last update exceeds maximum"
);

Weight Bounds Check:

uint256 newWeight;
if (multiplier > 0) {
newWeight = uint256(weight) + FixedPoint.mulDown(uint256(multiplierScaled18), timeSinceLastUpdate);
} else {
newWeight = uint256(weight) - FixedPoint.mulUp(uint256(-multiplierScaled18), timeSinceLastUpdate);
}
require(newWeight <= FixedPoint.ONE, "Weight exceeds 100%");
return newWeight;

The recommended approach is to implement both the time bound check and weight bounds check to provide multiple layers of protection against the underflow condition.

Updates

Lead Judging Commences

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