QuantAMM

QuantAMM
49,600 OP
View results
Submission Details
Severity: medium
Valid

Denial of Service via Manipulation of `poolsFeeData` in `addLiquidityProportional` Function

Summary

The addLiquidityProportional function in UpliftOnlyExample contract is vulnerable to a Denial of Service (DoS) attack, where malicious actors can exploit the poolsFeeData mapping to permanently block users from interacting with liquidity pools or transferring LPNFT tokens. This is achieved by exceeding the limit of 100 entries per pool and user and also adding invalid zero-value entries to the mapping. These vulnerabilities can lead to gas exhaustion, disrupting core functionalities.

Vulnerability Details

Issue 1: Exceeding poolsFeeData Limit via transferFrom

  • Root Cause: The transferFrom function allows adding entries to poolsFeeData for a recipient without checking the length of the array.

  • Exploit: An attacker transfers LPNFT tokens to a victim, incrementally increasing the length of poolsFeeData until it exceeds the limit of 100
    entries.

  • Result:

    1. The victim cannot call addLiquidityProportional anymore for the specific pool.

    2. Transferring LPNFT tokens may revert due to gas exhaustion when looping through the oversized array if the attaker made it large enough.

Issue 2: Zero-Value Deposits Circumventing _MINIMUM_TRADE_AMOUNT

  • Root Cause: The _ensureValidTradeAmount function only reverts for liquidity amounts below is below _MINIMUM_TRADE_AMOUNT and greater than 0. Deposits of exactly 0 are not validated, allowing entries with zero-value trades to be added to poolsFeeData.

  • Exploit: An attacker repeatedly calls addLiquidityProportional with zero-value deposits, inflating the poolsFeeData array.

  • Result: This made the attack easier for the attacker as the attacker increases the array size without donating funds, leading to gas inefficiency and potential DoS when interacting with the mapping.

POC

function testAddLiquidityCanExceedLimitUsingTransferFrom() public {
LPNFT lpNft = upliftOnlyRouter.lpNFT();
uint256[] memory maxAmountsIn = [dai.balanceOf(bob), usdc.balanceOf(bob)].toMemoryArray();
vm.startPrank(bob);
uint256 bptAmountDeposit = bptAmount / 100;
for (uint256 i = 0; i < 100; i++) {
upliftOnlyRouter.addLiquidityProportional(pool, maxAmountsIn, bptAmountDeposit, false, bytes(""));
skip(1 days);
}
vm.stopPrank();
uint256[] memory maxAmountsIn2 = [uint(0), uint(0)].toMemoryArray();
vm.startPrank(alice);
uint256 bptAmountDeposit2 = uint(0);
for (uint256 i = 0; i < 1000; i++) {
upliftOnlyRouter.addLiquidityProportional(pool, maxAmountsIn2, bptAmountDeposit2, false, bytes(""));
lpNft.transferFrom(alice, bob, 101+i);
skip(1 days);
}
vm.stopPrank();
console.log("bob totaldeposits", upliftOnlyRouter.getUserPoolFeeData(pool, bob).length);
}

Additionally, adding liquidity for the user is permanently blocked once the poolsFeeData for such user contain more than 100 dust data (0 amounts) this is because the removeLiquidityProportional does not work with 0 inputs. it reverts with division by 0 error

uint256[] memory minAmountsOut = [uint(0), uint(0)].toMemoryArray();
vm.startPrank(bob);
upliftOnlyRouter.removeLiquidityProportional(uint(0), minAmountsOut, false, pool);
vm.stopPrank();

Impact

  1. DoS on Adding Liquidity:
    Users are permanently blocked from adding liquidity to a pool once the poolsFeeData length exceeds the limit of 100 entries.

  2. DoS on Token Transfers:
    If poolsFeeData grows too large, transferring LPNFT tokens will revert due to gas exhaustion when iterating through the array.

  3. Gas Inefficiency:
    Zero-value deposits unnecessarily inflate the size of poolsFeeData, increasing gas costs for legitimate operations.

Tools Used

manual audit

Recommendations

  1. Add Length Check in transferFrom
    Ensure poolsFeeData length does not exceed 100 when transferring LPNFT tokens.

  2. Explicitly Reject Zero Deposits
    Modify addLiquidityProportional to reject amounts of 0 explicitly

Updates

Lead Judging Commences

n0kto Lead Judge 11 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding_afterUpdate_does_not_check_limit_NFT_per_user

Likelihood: Medium/High, anyone can receive an unlimited NFT number but will cost creation of LP tokens and sending them. Impact: Low/Medium, DoS the afterUpdate and addLiquidityProportional but will be mitigable on-chain because a lot of those NFT can be burn easily in onAfterRemoveLiquidity.

Support

FAQs

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

Give us feedback!