# Migration's leave function allows leaving a committed proposal

Migration.leave lacks an isCommited check, letting users withdraw original contributions from a failed committed proposal and drain assets owed to others.

- Payout: Contest reward
- Severity: high
- Program: Fractional
- Credited: kenzo
- Disclosed: 2022-09-27
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-07-fractional-findings/issues/379
- Library page: https://bugbountylibrary.com/r/c4%3A2022-07-fractional%3AH-20
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** Migration.sol leave() smart contract function
**Root cause:** leave() never checks proposal.isCommited, so it returns the original contribution amount even after a committed proposal failed and should pay out the buyout-adjusted amount via withdrawContributions.
**Steps:**
1. Commit a migration proposal and let it fail.
2. Call `Migration.leave()` instead of `withdrawContributions()`.
3. Observe leave() returns the full original contribution without checking `proposal.isCommited`.
4. Repeat to withdraw more than the adjusted amount owed, creating a deficit for other users.
**How found:** Code review of Migration.sol's leave() function, noticing it lacks the isCommited check that withdrawContributions relies on, making it a duplicate withdrawal path with wrong accounting.
**Exploitation:** After a committed proposal fails, a user calls leave() to reclaim their full original contribution rather than the buyout-adjusted amount, extracting more than deserved and leaving a shortfall for other contributors.
**Insight:** When two functions both return user funds, diff their precondition checks; a missing state guard on the alternate path lets users pick the more favorable payout.
