# Stableswap calculator fails due to 0 value swaps

Stableswap liquidity calculator calls estimate_swap with in_amt=0 before the zero-check, underflowing and reverting get_liquidity so pools earn no rewards.

- Payout: Contest reward
- Severity: medium
- Program: Aquarius
- Credited: carrotsmuggler
- Disclosed: 2025-09-01
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_aquarius_aug2025.pdf#3.2.5
- Library page: https://bugbountylibrary.com/r/cantina%3Aaquarius_aug2025%3A3.2.5
- Tags: domain:web3 impact:dos kind:contest platform:cantina sev:medium tech:rust technique:fuzzing technique:source-review vuln:accounting-error vuln:dos vuln:reward-distribution

**Target:** Soroban stableswap_pool.rs liquidity calculator (get_liquidity)
**Root cause:** The convergence loop calls estimate_swap before checking in_amt==0; exponential decay (in_amt*100/125) can reach 0, and estimate_swap with 0 input yields y<xp[j], underflowing the stableswap math.
**Steps:**
1. Init stableswap pool with tokens [token1, token2] and amp=10.
2. Deposit initial liquidity [1e10, 1e6].
3. Call router.get_liquidity(tokens, pool_hash) with CALCULATOR_FLAG on.
4. Calculator loop decays in_amt via `in_amt * 100 / 125` until it hits 0.
5. estimate_swap is invoked with in_amt=0 before the zero-check, producing y<xp[j] and an underflow panic.
**How found:** Reading the convergence loop and noticing the `in_amt == 0 || depth == 0` guard sits after the estimate_swap call, then constructing a deposit/swap composition that drives in_amt to 0.
**Exploitation:** Not an attack; a functional pool's get_liquidity reverts, so reward assignment fails for that pool while swaps still work through the router.
**Insight:** In iterative numeric solvers, guard clauses placed after the function call they protect are dead on the final iteration; check decay-to-zero before invoking the estimator, and fuzz input compositions that make the loop terminate at the boundary.
