QuantAMM

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

lpTokenDepositValueChange Calculation is Inaccurate

Summary

The UpliftOnlyExample contract has a critical issue in its fee calculation logic where the lack of a scaling factor causes severe truncation of fees, resulting in significantly reduced or zero fees being charged even when profits are made. This impacts all fee recipients and the protocol's revenue model.

Vulnerability Details

In the UpliftOnlyExample contract, when calculating fees based on profit, the code performs a division operation on the profit value. In any case where the profit is less then 100% the result will be 0. and in every case where it is above 100% the fee will be significantly less then the intended amount.

(int256(localData.lpTokenDepositValueNow) - int256(localData.lpTokenDepositValue)) / int256(localData.lpTokenDepositValue);

The issue arises because:

  1. The calculation subtracts the initial deposit value from the current value

  2. The result is used directly in division of the initial deposit value

For example:

(int256(localData.lpTokenDepositValueNow) - int256(localData.lpTokenDepositValue)) / int256(localData.lpTokenDepositValue);
(150 - 100) / 100;
(50) / 100;
0;

When this happens the fee rate will be as if there was no profit which is significantly less then the intended fee rate.

feePerLP = (uint256(minWithdrawalFeeBps) * 1e18) / 10000;

The entire purpose of the uplift fee is to charge fees based on the profit of the LP position. This is undermined by the missing scaling factor which will cause the fee to be significantly less then the intended amount impacting all recipients of fees.

POC

By following the below directions you will see 0 for lpTokenDepositValueChange while there was a 50% price increase.

lpTokenDepositValueChange below
0

If you put this log here:

console.log("lpTokenDepositValueChange below");
console.logInt(localData.lpTokenDepositValueChange);

And the this PoC in the UpliftExample.t.sol test file

function testBadCalculation() public {
// Add liquidity so bob has BPT to remove liquidity.
uint256[] memory maxAmountsIn = [dai.balanceOf(bob), usdc.balanceOf(bob)].toMemoryArray();
vm.prank(bob);
upliftOnlyRouter.addLiquidityProportional(pool, maxAmountsIn, bptAmount, false, bytes(""));
vm.stopPrank();
assertEq(upliftOnlyRouter.getUserPoolFeeData(pool, bob).length, 1, "bptAmount mapping should be 1");
assertEq(upliftOnlyRouter.getUserPoolFeeData(pool, bob)[0].amount, bptAmount, "bptAmount mapping should be 0");
assertEq(
upliftOnlyRouter.getUserPoolFeeData(pool, bob)[0].blockTimestampDeposit,
block.timestamp,
"bptAmount mapping should be 0"
);
assertEq(
upliftOnlyRouter.getUserPoolFeeData(pool, bob)[0].lpTokenDepositValue,
500000000000000000,
"should match sum(amount * price)"
);
assertEq(upliftOnlyRouter.getUserPoolFeeData(pool, bob)[0].upliftFeeBps, 200, "fee");
int256[] memory prices = new int256[]();
for (uint256 i = 0; i < tokens.length; ++i) {
prices[i] = int256(i) * 1.5e18; // Make the price 1.5 times higher
}
updateWeightRunner.setMockPrices(pool, prices);
uint256 nftTokenId = 0;
uint256[] memory minAmountsOut = [uint256(0), uint256(0)].toMemoryArray();
BaseVaultTest.Balances memory balancesBefore = getBalances(bob);
vm.startPrank(bob);
upliftOnlyRouter.removeLiquidityProportional(bptAmount, minAmountsOut, false, pool);
vm.stopPrank();
}

Impact

Loss of yield

The vulnerability results in:

  • Fees being severely undercharged

  • Protocol and fee recipients losing significant revenue

  • Economic model of the protocol being undermined

  • All participants who should receive fees (admin, protocol, etc.) being affected

Tools Used

Manual Review

Recommendations

Modify the fee calculation to ensure that lpTokenDepositValueChange accurately reflects the profit of the LP position.

Updates

Lead Judging Commences

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

finding_onAfterRemoveLiquidity_lpTokenDepositValueChange_rounding_error_100%_minimum

Likelihood: High, every call to the function (withdraw) Impact: Low/Medium, uplift fees will be applied only when the price of one asset is doubled but fixed fees will still be collected.

Support

FAQs

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