# Protocol allows creating broken tri-crypto CPMM pools

MANTRA pool manager lets anyone create 3+ token constant-product pools by omitting slippage tolerance, breaking the x*y=k invariant and enabling arbitrage.

- Payout: Audit contest
- Severity: high
- Program: MANTRA
- Credited: carrotsmuggler
- Disclosed: 2025-03-31
- Write-up quality: 7/10
- Original: https://code4rena.com/reports/2024-11-mantra-dex#h-01
- Library page: https://bugbountylibrary.com/r/c4%3A2024-11-mantra-dex%3AH-01
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:incomplete-fix technique:source-review vuln:accounting-error vuln:business-logic

**Target:** CosmWasm pool-manager contract (create_pool / compute_swap)
**Root cause:** create_pool never enforces a 2-token limit for ConstantProduct pools; the only length check lives inside assert_slippage_tolerance, which is skipped entirely when slippage_tolerance is None. compute_swap still applies the 2-token Uniswap formula to multi-token pools.
**Steps:**
1. Call `create_pool` with `PoolType::ConstantProduct` and 3 denoms (e.g. uwhale, uluna, uusd) and `slippage_tolerance = None`.
2. Call `provide_liquidity` with all 3 coins and `slippage_tolerance = None`; LP shares mint successfully.
3. Call `execute_swap_operations` with `MantraSwap{token_in_denom: uwhale, token_out_denom: uusd}`; swap executes using the 2-token formula.
4. Observe pool invariant x*y*z=k is not maintained, allowing arbitrage between pairs as pool grows imbalanced.
**How found:** Code review: noticed assert_slippage_tolerance hardcodes `deposits.len() != 2 || pools.len() != 2`, implying CPMM is 2-token only, then saw the check is gated behind `if let Some(slippage_tolerance)` and thus bypassable by passing None.
**Exploitation:** Attacker creates a 3-token CPMM pool without slippage tolerance, seeds liquidity, then arbitrages the mispriced pairs (x-y vs y-z) since compute_swap ignores the third reserve, draining value from LPs.
**Insight:** When a length/type guard lives inside an optional-parameter branch, check whether omitting that parameter skips the guard entirely; also verify math formulas match the pool cardinality the code actually permits.
