QuantAMM

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

100% Allocation to Admin Poses Risks to Liquidity Providers

Summary

The adminFeePercent parameter in the fee distribution logic can be set to 100%, which allocates all collected fees to the admin. This configuration poses several risks, including reduced incentives for liquidity providers (LPs) and operational imbalance.

Vulnerability Details

The adminFeePercent can be set to 100% results in all fees being allocated to the admin, with no portion reinvested into the pool

The setQuantAMMUpliftFeeTake function allows adminFeePercent to be set up to 100% (1e18).

function setQuantAMMUpliftFeeTake(uint256 _quantAMMUpliftFeeTake) external{
require(msg.sender == quantammAdmin, "ONLYADMIN");
require(_quantAMMUpliftFeeTake <= 1e18, "Uplift fee must be less than 100%");
uint256 oldSwapFee = quantAMMSwapFeeTake;
quantAMMSwapFeeTake = _quantAMMUpliftFeeTake;
emit UpliftFeeTakeSet(oldSwapFee, _quantAMMUpliftFeeTake);
}

The loop calculates the exitFee for each token and splits it into admin fees (accruedQuantAMMFees) and fees to be donated back to the pool (accruedFees).
If adminFeePercent is set to 100%, the entire exit fee for each token will be allocated to accruedQuantAMMFees, leaving accruedFees as zero.

hookAdjustedAmountsOutRaw = localData.amountsOutRaw;
localData.tokens = _vault.getPoolTokens(localData.pool);
>> localData.adminFeePercent = IUpdateWeightRunner(_updateWeightRunner).getQuantAMMUpliftFeeTake();
// Charge fees proportional to the `amountOut` of each token.
for (uint256 i = 0; i < localData.amountsOutRaw.length; i++) {
uint256 exitFee = localData.amountsOutRaw[i].mulDown(localData.feePercentage);
if (localData.adminFeePercent > 0) {
>> localData.accruedQuantAMMFees[i] = exitFee.mulDown(localData.adminFeePercent);
}
>> localData.accruedFees[i] = exitFee - localData.accruedQuantAMMFees[i];
hookAdjustedAmountsOutRaw[i] -= exitFee;
// Fees don't need to be transferred to the hook, because donation will redeposit them in the Vault.
// In effect, we will transfer a reduced amount of tokensOut to the caller, and leave the remainder
// in the pool balance.
}

The conditional check if (localData.adminFeePercent != 1e18) ensures that fees are only donated back to the pool if adminFeePercent is less than 100%. Setting it to 100% bypasses this donation logic.

if (localData.adminFeePercent != 1e18) {
_vault.addLiquidity(
AddLiquidityParams({
pool: localData.pool,
to: msg.sender, // It would mint BPTs to router, but it's a donation so no BPT is minted
maxAmountsIn: localData.accruedFees, // Donate all accrued fees back to the pool (i.e. to the LPs)
minBptAmountOut: 0, // Donation does not return BPTs, any number above 0 will revert
kind: AddLiquidityKind.DONATION,
userData: bytes("") // User data is not used by donation, so we can set it to an empty string
})
);
}

Impact

  1. With 100% of fees going to the admin, LPs receive no benefit from fee reinvestment, potentially discouraging liquidity provision.

  2. The condition if (localData.adminFeePercent != 1e18) prevents any fees from being donated back to the pool, as it would not be met when adminFeePercent is 100%.

  3. The reinvestment of fees back into the pool is crucial for maintaining liquidity and incentivizing participation. Setting adminFeePercent to 100% disrupts this balance.

Tools Used

Manual Review

Recommendations

Limit adminFeePercent to a value less than 100% to ensure that a portion of the fees is always reinvested into the pool, benefiting all liquidity providers.

function setQuantAMMUpliftFeeTake(uint256 _quantAMMUpliftFeeTake) external{
require(msg.sender == quantammAdmin, "ONLYADMIN");
- require(_quantAMMUpliftFeeTake <= 1e18, "Uplift fee must be less than 100%");
+ require(_quantAMMUpliftFeeTake < 1e18, "Uplift fee must be less than 100%");
uint256 oldSwapFee = quantAMMSwapFeeTake;
quantAMMSwapFeeTake = _quantAMMUpliftFeeTake;
emit UpliftFeeTakeSet(oldSwapFee, _quantAMMUpliftFeeTake);
}
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.

Give us feedback!