# The constant product invariant can be broken.

Basin Well.sync() lets reserves grow without minting LP tokens, breaking the x*y=k^2 invariant and causing removeLiquidityImbalanced to underflow-revert.

- Payout: Contest reward
- Severity: medium
- Program: Basin
- Credited: qpzm
- Disclosed: 2023-10-05
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-07-basin-findings/issues/191
- Library page: https://bugbountylibrary.com/r/c4%3A2023-07-basin%3AM-05
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:chained technique:source-review vuln:accounting-error vuln:dos

**Target:** Well.sol / ConstantProduct2.sol AMM pool
**Root cause:** Well.sync() updates reserves from token balances but never mints/burns LP supply, so tokenSupply^2 no longer equals reserves[0]*reserves[1]*EXP_PRECISION; ConstantProduct2.calcReserve assumes equality.
**Steps:**
1. Deploy Well with no initial liquidity (totalSupply=0).
2. Transfer 100 of token0 and 100 of token1 directly to the Well.
3. Call `addLiquidityEqualAmount(user, 1)` to mint 1e6 LP.
4. Call `well.sync()` so reserves become [101,101] while totalSupply stays 1e6.
5. Call `well.removeLiquidityImbalanced(type(uint256).max, [1,0], user, type(uint256).max)`; `totalSupply() - _calcLpTokenSupply(...)` underflows and reverts.
**How found:** Code reading of Well.sync() and ConstantProduct2.calcReserve revealed the invariant tokenSupply^2 == reserves[0]*reserves[1]*EXP_PRECISION is assumed but not enforced on sync; PoC test confirmed underflow.
**Exploitation:** Attacker donates tokens to the Well and calls sync() to inflate reserves without LP supply, permanently (until next addLiquidity/swapFrom) making removeLiquidityImbalanced revert via arithmetic underflow. Attacker loses the donation; no direct fund theft.
**Escalation:** Judge noted DoS is reversible by any subsequent addLiquidity/swapFrom, and users can bypass via removeLiquidity + removeLiquidityOneToken; downgraded High→Medium.
**Insight:** In AMMs, any function that mutates reserves (sync, skim, donation) must also reconcile LP supply; check every reserve-update path against the invariant the math library assumes.
