QuantAMM

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

A weight cannot be set to 1e18 manually at its full potential. Protocol looses its efficiency and oversight may cause impermanent loss and inconsistent LP returns.

Summary

In the contract UpdateWeightRunner, there's a function called setWeightsManually which is only admin and pool manager protected. The function is a breakglass function to allow the admin or the pool manager to set the quantammAdmins weights manually. Everything is okay but an admin or a pool manager can't set weight to 1e18. This oversight may emerge some potential issues i.e., impermanent loss amplification, pool imbalance, inconsistent LP returns, economic exploits, etc.

Vulnerability Details

UpdateWeightRunner::setWeightsManually:

function setWeightsManually(
int256[] calldata _weights,
address _poolAddress,
uint40 _lastInterpolationTimePossible,
uint256 _numberOfAssets
) external {
uint256 poolRegistryEntry = QuantAMMWeightedPool(_poolAddress).poolRegistry();
if (poolRegistryEntry & MASK_POOL_OWNER_UPDATES > 0) {
require(msg.sender == poolRuleSettings[_poolAddress].poolManager, "ONLYMANAGER");
} else if (poolRegistryEntry & MASK_POOL_QUANTAMM_ADMIN_UPDATES > 0) {
require(msg.sender == quantammAdmin, "ONLYADMIN");
} else {
revert("No permission to set weight values");
}
//though we try to keep manual overrides as open as possible for unknown unknows
//given how the math library works weights it is easiest to define weights as 18dp
//even though technically G3M works of the ratio between them so it is not strictly necessary
//CYFRIN L-02
for (uint256 i; i < _weights.length; i++) {
if (i < _numberOfAssets) {
require(_weights[i] > 0, "Negative weight not allowed");
@> // @info: conditional operation should be <= instead of <, therefore the condition should be _weights[i] <= 1e18
@> require(_weights[i] < 1e18, "greater than 1 weight not allowed");
------------------------------------^
}
}
IQuantAMMWeightedPool(_poolAddress).setWeights(_weights, _poolAddress, _lastInterpolationTimePossible);
emit SetWeightManual(msg.sender, _poolAddress, _weights, _lastInterpolationTimePossible);
}

It's clear that admin or pool manager can't set weights to 100% or 1e18. Issue is clear and bare to see so it doesn't need any PoC therefore code itself is a PoC.

Impact

  1. Pool Imbalance
    Effect: The pool's total weight would not sum to 1 (100%), leading to undefined or erroneous behavior in the pricing and trading functions.
    Impact: This can distort the relative valuation of assets in the pool, causing incorrect pricing during swaps.

  2. Erroneous Token Distribution
    Effect: The misaligned weights will directly affect how liquidity is distributed among tokens in the pool.
    Impact:
    Overweighted tokens may dominate liquidity.
    Underweighted tokens may have insufficient liquidity, increasing slippage during trades.

  3. Impermanent Loss Amplification
    Effect: Incorrect weights can exacerbate impermanent loss by deviating from the pool's intended strategy.
    Impact: LPs may face higher losses compared to properly weighted pools, discouraging participation.

  4. Failed Operations

  5. Economic Exploits

  6. Inconsistent LP Returns
    Effect: The pool's reward mechanisms (e.g., fees or incentives) may no longer function as intended if weights are incorrect.
    Impact: LPs could earn disproportionate rewards or face penalties unrelated to their contributions.

  7. Protocol Instability

Tools Used

  1. Manual Review

  2. Chat-gpt (asked its gradual impacts)

Recommendations

Mitigation to this oversight is simple. Update the code as we updated below...

UpdateWeightRunner::setWeightsManually:

function setWeightsManually(
int256[] calldata _weights,
address _poolAddress,
uint40 _lastInterpolationTimePossible,
uint256 _numberOfAssets
) external {
uint256 poolRegistryEntry = QuantAMMWeightedPool(_poolAddress).poolRegistry();
if (poolRegistryEntry & MASK_POOL_OWNER_UPDATES > 0) {
require(msg.sender == poolRuleSettings[_poolAddress].poolManager, "ONLYMANAGER");
} else if (poolRegistryEntry & MASK_POOL_QUANTAMM_ADMIN_UPDATES > 0) {
require(msg.sender == quantammAdmin, "ONLYADMIN");
} else {
revert("No permission to set weight values");
}
//though we try to keep manual overrides as open as possible for unknown unknows
//given how the math library works weights it is easiest to define weights as 18dp
//even though technically G3M works of the ratio between them so it is not strictly necessary
//CYFRIN L-02
for (uint256 i; i < _weights.length; i++) {
if (i < _numberOfAssets) {
require(_weights[i] > 0, "Negative weight not allowed");
- require(_weights[i] < 1e18, "greater than 1 weight not allowed");
+ require(_weights[i] < 1e18, "greater than 1 weight not allowed");
}
}
IQuantAMMWeightedPool(_poolAddress).setWeights(_weights, _poolAddress, _lastInterpolationTimePossible);
emit SetWeightManual(msg.sender, _poolAddress, _weights, _lastInterpolationTimePossible);
}
Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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

Give us feedback!