Vanguard

First Flight #56
Beginner FriendlyDeFiFoundry
0 EXP
Submission Details
Impact: high
Likelihood: high

initialLiquidity Sandwich Attack Permanently Griefs Token Launch - maxSwapAmount Based on Manipulable Value

Author Revealed upon completion

Root + Impact

Description

Root + Impact

Description

The initialLiquidity value is set when the first swap occurs (re-read from pool if it was 0 at initialization):

function _beforeSwap(...) {
if (initialLiquidity == 0) {
uint128 liquidity = StateLibrary.getLiquidity(poolManager, key.toId());
initialLiquidity = uint256(liquidity); // @> Can be sandwiched!
}
}

This can be permanently manipulated via sandwich attack on the first swap:

  1. LP adds 1,000,000 tokens of liquidity

  2. Attacker front-runs first swap, removes 99.99% of liquidity

  3. First swap triggers initialLiquidity = getLiquidity() = only 1,000 tokens

  4. Attacker back-runs, re-adds their liquidity

  5. initialLiquidity is now permanently griefed to 0.1% of actual

Risk

Impact: HIGH - maxSwapAmount = (initialLiquidity * limitBps) / 10000. With griefed initialLiquidity:

  • If limitBps = 100 (1%), maxSwap = 10 tokens instead of 10,000

  • ALL users are permanently limited to tiny swaps

  • Token launch is completely griefed

Likelihood: HIGH - Sandwich attacks are trivial for MEV bots. Cost is only gas.

Proof of Concept

function testInitialLiquiditySandwich() public {
// 1. LP adds 1,000,000 liquidity
poolManager.modifyLiquidity(poolKey, ModifyLiquidityParams({
liquidityDelta: 1_000_000e18
}));
// 2. Attacker removes 99.99%
vm.prank(attacker);
poolManager.modifyLiquidity(poolKey, ModifyLiquidityParams({
liquidityDelta: -999_999e18
}));
// 3. First swap sets initialLiquidity = 1,000
vm.prank(victim);
poolManager.swap(poolKey, swapParams);
// 4. Attacker re-adds liquidity
// 5. Result: maxSwap is 1000x smaller than intended FOREVER
}

Recommended Mitigation

Use maximum observed liquidity (can only increase):

function _beforeSwap(...) {
uint128 currentLiquidity = StateLibrary.getLiquidity(poolManager, key.toId());
if (currentLiquidity > initialLiquidity) {
initialLiquidity = currentLiquidity; // Only increases
}
}
  • Describe the normal behavior in one or more sentences

  • Explain the specific issue or problem in one or more sentences

// Root cause in the codebase with @> marks to highlight the relevant section

Risk

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

  • Reason 2

Impact:

  • Impact 1

  • Impact 2

Proof of Concept

Recommended Mitigation

- remove this code
+ add this code

Support

FAQs

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

Give us feedback!