# Unsafe cast in ConcentratedLiquidityPool.burn leads to attack

Unsafe uint128-to-int128 cast in Sushi ConcentratedLiquidityPool.burn lets attacker pass amount=2^128-1, minting free LP and redeeming huge token0/1 amounts to drain pool.

- Payout: Audit contest
- Severity: high
- Program: Sushi
- Found by: cmichel
- Disclosed: 2021-11-30
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2021-09-sushitrident-2-findings/issues/50
- Library page: https://bugbountylibrary.com/r/c4%3A2021-09-sushitrident-2%3AH-01
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** ConcentratedLiquidityPool.burn (Solidity AMM contract)
**Root cause:** burn casts caller-controlled uint128 amount to int128 without SafeCast; 2^128-1 wraps to -1, so -int128(amount)=1 adds liquidity while the unmodified uint128 amount is used to compute redeemed token0/1 amounts.
**Steps:**
1. Call `burn(lower, upper, amount)` with `amount = 2**128 - 1` so `-int128(amount)` evaluates to 1, passing the MAX_TICK_LIQUIDITY check in `_updatePosition`.
2. Choose `lower`/`upper` tick range so redeemed amount0/amount1 stay below current pool balance (avoids transfer revert).
3. Position is credited 1 liquidity unit but redemption uses the huge uint128 amount, yielding outsized token0/token1 payouts.
4. Repeat/withdraw to drain reserves; POC hardhat test steals ~99% of token0 reserves.
**How found:** Code review of burn noticed the explicit `-int128(amount)` cast on caller-controlled input; recognized uint128 max wraps to -1 under two's complement, then reasoned through the competing constraints (MAX_TICK_LIQUIDITY vs pool balance) to pick a viable amount and tick range.
**Exploitation:** Attacker calls burn with max uint128 amount and a crafted tick range; the signed cast wraps to -1, adding liquidity instead of removing, while the unsigned amount drives redemption of nearly the entire token0 pool balance to the attacker.
**Insight:** In Solidity 0.8.x, explicit type casts still silently truncate/wrap—grep for `int128(`, `uint128(`, `int256(` on user-controlled values; a negation of a cast value is a classic sign-flip primitive. When a wrapped value must satisfy two bounds, tune a secondary parameter (here tick range) to fit both.

