QuantAMM

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

Delayed Weight Normalization Validation Enables Front-Running Attacks on Pool Deployment

Description

In the QuantAMMWeightedPoolFactory contract, pool creation lacks upfront validation that weights sum to FixedPoint.ONE (1e18). This is particularly concerning for Balancer V3's QuantAMM pools, where weight normalization is crucial for the correct functioning of their automated weight adjustment mechanisms.

The vulnerability lies in the delayed validation approach - weights are only checked after pool deployment in _setInitialWeights(). This creates several serious issues in the context of Balancer's architecture. Most critically, since QuantAMM pools rely on sophisticated weight interpolation and automated adjustments over time, any deviation from proper weight normalization could compound through the pool's lifecycle, potentially destabilizing the entire automated market making strategy. Additionally, the lack of early validation creates an attack vector where malicious actors could front-run pool deployments with invalid weights, forcing legitimate deployments to fail after incurring significant gas costs.

https://github.com/Cyfrin/2024-12-quantamm/blob/a775db4273eb36e7b4536c5b60207c9f17541b92/pkg/pool-quantamm/contracts/QuantAMMWeightedPoolFactory.sol#L131

function create(NewPoolParams memory params) external returns (address pool, bytes memory poolArgs) {
// ... other code ...
pool = _create(poolArgs, params.salt);
QuantAMMWeightedPool(pool).initialize(
params._initialWeights,
params._poolSettings,
params._initialMovingAverages,
params._initialIntermediateValues,
params._oracleStalenessThreshold
);

Recommended Mitigation Steps

  1. Add weight validation in the factory before pool creation:

function validateWeights(int256[] memory weights) internal pure {
int256 weightSum = 0;
for (uint256 i = 0; i < weights.length; i++) {
require(weights[i] >= 0, "Negative weight");
weightSum += weights[i];
}
// Use a small tolerance for rounding errors
int256 deviation = weightSum - int256(FixedPoint.ONE);
require(
deviation >= -1 && deviation <= 1,
"Weights must sum to ONE"
);
}
function create(NewPoolParams memory params) external returns (address pool, bytes memory poolArgs) {
validateWeights(params._initialWeights);
// Rest of creation logic...
}
  1. Consider adding a helper function for weight normalization:

function normalizeWeights(int256[] memory weights) public pure returns (int256[] memory) {
int256 sum = 0;
for (uint256 i = 0; i < weights.length; i++) {
sum += weights[i];
}
int256[] memory normalized = new int256[]();
for (uint256 i = 0; i < weights.length; i++) {
normalized[i] = (weights[i] * int256(FixedPoint.ONE)) / sum;
}
return normalized;
}
  1. Add explicit weight bounds checks:

require(
weights[i] >= MIN_WEIGHT && weights[i] <= MAX_WEIGHT,
"Weight out of bounds"
);

These changes ensure weight validation happens early in the pool creation process, preventing unnecessary gas costs and maintaining the economic invariants of the system.

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.