# Last Holder Can’t Exit, Zero‑Supply Unstake Reverts

Burning the last Cabal LST supply makes process_*_unstake divide by zero, permanently locking the final staker's INIT/LP.

- Payout: Contest reward
- Severity: medium
- Program: Cabal
- Credited: maxzuvex
- Disclosed: 2025-05-28
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2025-04-cabal-liquid-staking-token#m-05
- Library page: https://bugbountylibrary.com/r/c4%3A2025-04-cabal-liquid-staking-token%3AM-05
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:dos

**Target:** Move staking contract process_xinit_unstake/process_lp_unstake
**Root cause:** initiate_unstake burns the LST in one transaction, then the async process_*_unstake reads fungible_asset::supply as 0 and calls bigdecimal::from_ratio_u128(unstake_amount, 0), aborting with EDIVISION_BY_ZERO.
**Steps:**
1. Mint exactly 1 Cabal-LPT to Alice so total supply = 1.
2. Alice calls `initiate_unstake(pool_idx, 1)`; burn(1) sets supply to 0 and queues `process_lp_unstake`.
3. Worker executes `process_lp_unstake(assets_signer, @alice, pool_idx, 1)`.
4. Inside, `sx_supply = fungible_asset::supply(meta)` returns 0; `bigdecimal::from_ratio_u128(1, 0)` aborts.
5. Every retry hits supply==0 again; funds locked.
**How found:** Code reading of process_*_unstake revealed the ratio computation uses post-burn supply, which is zero when the last holder unstakes the entire supply.
**Insight:** In async two-phase unstake designs, check whether the ratio/denominator is computed from state mutated by the first phase (burn) rather than a snapshot; the last-holder edge case (supply==0) is a classic DoS.
