QuantAMM

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

Missing Validation for disableUnbalancedLiquidity Flag When Hook Requires It

Summary

The QuantAMMWeightedPoolFactory contract fails to validate that disableUnbalancedLiquidity is set to true when a pool's hook has enableHookAdjustedAmounts enabled, allowing pools to be created with invalid configurations that could lead to unexpected behavior.

Vulnerability Details

Root cause

In QuantAMMWeightedPoolFactory, the createWithoutArgs and create functions do not validate the relationship between disableUnbalancedLiquidity and hook settings.

function createWithoutArgs(NewPoolParams memory params) external returns (address pool) {
if (params.roleAccounts.poolCreator != address(0)) {
revert StandardPoolWithCreator();
}
LiquidityManagement memory liquidityManagement = getDefaultLiquidityManagement();
liquidityManagement.enableDonation = params.enableDonation;
// disableUnbalancedLiquidity must be set to true if a hook has the flag enableHookAdjustedAmounts = true.
liquidityManagement.disableUnbalancedLiquidity = params.disableUnbalancedLiquidity;
// ... rest of the code
}

The root of the problem is that there is no validation that disableUnbalancedLiquidity should be true when the hook has the flag enableHookAdjustedAmounts=true.

Although there are comments indicating the need for validation:

// disableUnbalancedLiquidity must be set to true if a hook has the flag enableHookAdjustedAmounts = true.

Factory directly uses the value of `params.disableUnbalancedLiquidity` without validation.

POC

Add this to QuantAMMWeightedPoolFactory.t.sol and run it forge test --match-test testLiquidityManagementValidation -vvvv.

function testLiquidityManagementValidation() public {
QuantAMMWeightedPoolFactory.NewPoolParams memory params = _createPoolParams();
// Test case: disableUnbalancedLiquidity = false when hooks require true
params.disableUnbalancedLiquidity = false;
// Create pool with incorrect disableUnbalancedLiquidity setting
address pool = quantAMMWeightedPoolFactory.createWithoutArgs(params);
assertTrue(pool != address(0), "Pool should be created");
// Setup for initialize
vm.startPrank(bob);
dai.approve(address(router), poolInitAmount);
IERC20[] memory tokens = new IERC20[]();
tokens[0] = dai;
tokens[1] = usdc;
uint256[] memory amounts = new uint256[]();
amounts[0] = poolInitAmount;
amounts[1] = poolInitAmount/2; // Unbalanced amounts
// Should revert due to unbalanced liquidity but doesn't
router.initialize(
pool,
tokens,
amounts,
0,
false,
bytes("")
);
// Verify pool was initialized with unbalanced liquidity
IQuantAMMWeightedPool quantPool = IQuantAMMWeightedPool(pool);
IQuantAMMWeightedPool.QuantAMMWeightedPoolDynamicData memory data = quantPool.getQuantAMMWeightedPoolDynamicData();
assertTrue(data.isPoolInitialized, "Pool should not be initialized with unbalanced liquidity");
vm.stopPrank();
}

Trace:

liquidityManagement: LiquidityManagement({
disableUnbalancedLiquidity: false,
enableAddLiquidityCustom: false,
enableRemoveLiquidityCustom: false,
enableDonation: true
})

When creating the pool, disableUnbalancedLiquidity is set to false when it should be true.

exactAmountsIn: [1000000000000000000000 [1e21], 500000000000000000000 [5e20]]

The pool was successfully initialized with unbalanced liquidity. The first token is 1000 units (1e21) while the second token is only 500 units (5e20).

emit LiquidityAdded(
pool: QuantAMMWeightedPool: [0x7814E22228978ad44183eF7c577EA1c37596a752],
liquidityProvider: bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e],
kind: 1,
totalSupply: 707106781186533381755 [7.071e20],
amountsAddedRaw: [1000000000000000000000 [1e21], 500000000000000000000 [5e20]],
swapFeeAmountsRaw: [0, 0]
)

There is no validation or revert when adding unbalanced liquidity.

isPoolInitialized: true,
isPoolPaused: false,
isPoolInRecoveryMode: false

Pool successfully initialized and status confirmed.

Impact

  • Pools can be created with invalid configurations

  • Unbalanced liquidity operations could be allowed when they should be restricted

Tools Used

  • Manual review

  • Foundry

Recommendations

Add validation in the factory's create functions.

function createWithoutArgs(NewPoolParams memory params) external returns (address pool) {
LiquidityManagement memory liquidityManagement = getDefaultLiquidityManagement();
liquidityManagement.enableDonation = params.enableDonation;
// Add validation for hook configuration
if (params.poolHooksContract != address(0)) {
HooksConfig memory hooksConfig = _vault.getPoolHooksConfig(params.poolHooksContract);
if (hooksConfig.enableHookAdjustedAmounts) {
require(params.disableUnbalancedLiquidity, "Unbalanced liquidity must be disabled with adjusted amounts hook");
}
}
liquidityManagement.disableUnbalancedLiquidity = params.disableUnbalancedLiquidity;
...
}
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.

Appeal created

juggernaut63 Submitter
10 months ago
n0kto Lead Judge
10 months ago
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!