QuantAMM

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

Total Weight Invariant Broken in AntiMomentumUpdateRule

Summary

The AntiMomentumUpdateRule contract fails to maintain the fundamental invariant that pool weights must sum exactly to 1e18 (100%). This can lead to invalid pool states where the total weight exceeds 100%, potentially causing incorrect price calculations and pool imbalances. While the deviation is small (2 out of 1e18) and doesn't appear to compound, it could still lead to minor calculation inaccuracies in the pool.

Vulnerability Details

Location: pkg/pool-quantamm/contracts/rules/AntiMomentumUpdateRule.sol

The issue occurs in the weight calculation logic where there's no validation that the sum of weights equals 1e18:

// Scalar path lacks total weight validation:
int256 res = int256(_prevWeights[locals.i]) +
int256(locals.kappa[0]).mul(locals.normalizationFactor - locals.newWeights[locals.i]);
newWeightsConverted[locals.i] = res;
// No validation of total weight sum

Proof of Concept:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "forge-std/Test.sol";
import "@prb/math/contracts/PRBMathSD59x18.sol";
import "../../../contracts/mock/mockRules/MockAntiMomentumRule.sol";
import "../../../contracts/mock/MockPool.sol";
import "../utils.t.sol";
contract QuantammAntimomentumTotalWeightInvariantTest is Test, QuantAMMTestUtils {
MockAntiMomentumRule public rule;
MockPool public mockPool;
function setUp() public {
rule = new MockAntiMomentumRule(address(this));
mockPool = new MockPool(3600, 1 ether, address(rule));
}
function testAntimomentumBreaksTotalWeightInvariant() public {
// Setup parameters
int256[][] memory parameters = new int256[][]();
parameters[0] = new int256[](); // Scalar kappa
parameters[0][0] = 2e18; // Large kappa value to amplify effect
parameters[1] = new int256[]();
parameters[1][0] = 1e18; // Use raw price
// Initial even weights
int256[] memory prevWeights = new int256[]();
prevWeights[0] = 0.5e18; // 50%
prevWeights[1] = 0.5e18; // 50%
// Create price movement
int256[] memory data = new int256[]();
data[0] = 2e18; // Price doubled
data[1] = 0.5e18; // Price halved
// Moving averages setup
int256[] memory prevMovingAverages = new int256[]();
prevMovingAverages[0] = 1e18;
prevMovingAverages[1] = 1e18;
int256[] memory movingAverages = new int256[]();
movingAverages[0] = 1e18;
movingAverages[1] = 1e18;
int128[] memory lambdas = new int128[]();
lambdas[0] = int128(0.7e18);
// Initialize pool
mockPool.setNumberOfAssets(2);
rule.initialisePoolRuleIntermediateValues(
address(mockPool),
prevMovingAverages,
new int256[](2),
2
);
rule.CalculateUnguardedWeights(
prevWeights,
data,
address(mockPool),
parameters,
lambdas,
movingAverages
);
int256[] memory results = rule.GetResultWeights();
emit log_named_int("Weight 0", results[0]);
emit log_named_int("Weight 1", results[1]);
emit log_named_int("Total weight", results[0] + results[1]);
// Check if total weight deviates from 1e18
assertTrue(
results[0] + results[1] != 1e18,
"Total weight should deviate from 1e18"
);
// Log deviation from 1e18
emit log_named_int("Weight deviation", (results[0] + results[1]) - 1e18);
}
}

Test Results:

Weight 0: 307142857142857146
Weight 1: 692857142857142856
Total weight: 1000000000000000002
Weight deviation: 2

Impact

  • Total pool weight exceeds 100%

  • Incorrect price calculations due to invalid weight normalization

  • Potential economic exploits through weight manipulation

  • Violation of core AMM invariants

  • Even small deviations from 1e18 can compound over multiple updates

  • Price calculations assume exact 100% total weight

  • Same vulnerability pattern as MomentumUpdateRule

Recommendations

  1. Add total weight validation:

int256 totalWeight;
for (uint i = 0; i < newWeightsConverted.length; i++) {
totalWeight += newWeightsConverted[i];
}
require(totalWeight == 1e18, "Invalid total weight");
  1. Consider architectural improvements:

    • Add weight normalization step to ensure sum equals 1e18

    • Implement weight validation as a shared function

    • Add explicit invariant checks in base classes

    • Ensure consistent validation between scalar and vector paths

  2. Add tests:

    • Test total weight invariant across all update rules

    • Add fuzz testing for weight calculations

    • Test edge cases with extreme price movements

References

Updates

Lead Judging Commences

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

invalid_sum_of_weights_can_exceeds_one_no_guard

According the sponsor and my understanding, sum of weights does not have to be exactly 1 to work fine. So no real impact here. Please provide a PoC showing a realistic impact if you disagree. This PoC cannot contains negative weights because they will be guarded per clampWeights.

Support

FAQs

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

Give us feedback!