Summary
Incorrect weight clamping in `QuantammMathGuard.sol` will cause incorrect weight calculations for the protocol as the weights are not properly clamped and normalized.
Vulnerability Details
## Root Cause
In `QuantammMathGuard.sol#L32-L70` and `QuantammMathGuard.sol#L14-L30`, the `_clampWeights()` function clamps weights incorrectly, leading to weights that are not properly clamped and normalized.
```solidity
function _guardQuantAMMWeights(
int256[] memory _weights,
int256[] calldata _prevWeights,
int256 _epsilonMax,
int256 _absoluteWeightGuardRail
) internal pure returns (int256[] memory guardedNewWeights) {
_weights = _clampWeights(_weights, _absoluteWeightGuardRail);
guardedNewWeights = _normalizeWeightUpdates(_prevWeights, _weights, _epsilonMax);
}
```
## Internal pre-conditions
1. Admin needs to call `_guardQuantAMMWeights` to set `weights` to be clamped and normalized.
2. `weights` array to contain values that need clamping and normalization.
## External pre-conditions
1. `weights` array needs to contain values that exceed the `absoluteWeightGuardRail`.
## Attack Path
1. Admin calls `_guardQuantAMMWeights` with weights that need clamping.
2. The `_clampWeights` function clamps weights incorrectly.
3. The protocol uses these incorrectly clamped weights, leading to broken weight calculations.
Impact
The protocol suffers from incorrect weight calculations, leading to potential financial losses or incorrect protocol behavior.
Tools Used
Manual
PoC
int256[] memory weights = [0.1e18, 0.23e18, 0.23e18, 0.44e18];
int256 absoluteWeightGuardRail = 0.2e18;
int256[] memory clampedWeights = _clampWeights(weights, absoluteWeightGuardRail);
Recommendations
Modify `QuantammMathGuard.sol#_clampWeights()` function as follows:
```solidity
function _clampWeights(
int256[] memory _weights,
int256 _absoluteWeightGuardRail
) internal pure returns (int256[] memory) {
unchecked {
uint weightLength = _weights.length;
if (weightLength == 1) {
return _weights;
}
int256 absoluteMin = _absoluteWeightGuardRail;
int256 absoluteMax = ONE - (PRBMathSD59x18.fromInt(int256(_weights.length - 1)).mul(_absoluteWeightGuardRail));
int256 aver;
int256 w_min = _weights[0], w_max = _weights[0];
for (uint i = 0; i < weightLength; i++) {
aver += _weights[i];
if (_weights[i] < w_min) w_min = _weights[i];
if (_weights[i] > w_max) w_max = _weights[i];
}
if (w_min >= absoluteMin && w_max <= absoluteMax) {
return _weights;
}
aver /= weightLength;
int256 d_min = aver - w_min;
int256 d_max = w_max - aver;
int256 new_aver = int256(1e18).div(int256(weightLength)); // 1 / n;
int256 n_min = new_aver - absoluteMin;
int256 n_max = absoluteMax - new_aver;
int256 rate;
if (d_min != 0) {
rate = n_min.div(d_min);
} else {
rate = type(int256).max;
}
if (d_max != 0) {
int256 t_rate = n_max.div(d_max);
if (t_rate < rate) {
rate = t_rate;
}
}
for (uint i = 0; i < weightLength; i++) {
_weights[i] = (_weights[i] - aver).mul(rate) + new_aver;
}
}
return _weights;
}
```