QuantAMM

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

Incorrect Weight Calculation Due to Rounding Error in Block Multiplier

Summary

In QuantAMMWeightedPool.sol::calculateBlockNormalisedWeight(), when the multiplier is negative, the function uses mulUp instead of mulDown, causing potential weight calculation errors.

Vulnerability Details

Proof of Concept:

** Deploy the contract with initial weights and negative multipliers:

forge create --rpc-url http://localhost:8545 src/QuantAMMWeightedPool.sol:QuantAMMWeightedPool

** Create a test file to demonstrate the weight calculation error:

function testIncorrectWeightCalculation() public {
// Setup initial weights
int256[] memory weights = new int256[]();
weights[0] = 0.5e18;
weights[1] = 0.5e18;
// Set negative multiplier
int256 multiplier = -0.1e18;
uint256 timeElapsed = 100;
// Calculate weight with current implementation
uint256 incorrectWeight = pool.calculateBlockNormalisedWeight(weights[0], multiplier, timeElapsed);
// Calculate expected weight
uint256 expectedWeight = uint256(weights[0]) - (uint256(-multiplier) * timeElapsed / 1e18);
assertFalse(incorrectWeight == expectedWeight, "Weight calculation should be incorrect due to mulUp");
}

Impact

  • There is a critical vulnerability in the weight validation that can severely impact the protocol's functionality. The issue lies in the calculateBlockNormalisedWeight() function where the incorrect use of mulUp vs mulDown for negative multipliers leads to:

    1. Incorrect weight calculations that compound over time

    2. Potential pool imbalances

    3. Deviation from intended pool behavior

    4. Possible economic exploitation through arbitrage

    This is particularly critical because:

    • Weight calculations are fundamental to the entire AMM mechanism

    • The error compounds with each weight update

    • It affects all trades involving negative multipliers

    • Could lead to significant economic losses

Tools Used:

VSCodium

Foundry

Recommendations

function calculateBlockNormalisedWeight(
int256 weight,
int256 multiplier,
uint256 timeSinceLastUpdate
) internal pure returns (uint256) {
int256 multiplierScaled18 = multiplier * 1e18;
// Always use mulDown for consistent rounding behavior
if (multiplier > 0) {
return uint256(weight) + FixedPoint.mulDown(uint256(multiplierScaled18), timeSinceLastUpdate);
} else {
return uint256(weight) - FixedPoint.mulDown(uint256(-multiplierScaled18), timeSinceLastUpdate);
}
// This ensures consistent rounding behavior regardless of multiplier sign
// mulDown rounds toward zero, providing more predictable and fair weight calculations
// This also prevents potential manipulation through rounding differences
}
Updates

Lead Judging Commences

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

Give us feedback!