# `reduce_position` doesn’t update margin mapping correctly

reduce_position lowers position.margin_amount but never credits the user's margin mapping, so reduced margin becomes permanently unwithdrawable.

- Payout: Contest reward
- Severity: high
- Program: Unstoppable
- Credited: 0xbepresent
- Disclosed: 2023-06-21
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2023-06-unstoppable-judging/issues/143
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-06-unstoppable%3AH-4
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:solidity technique:source-review vuln:accounting-error

**Target:** Margin DEX Vault contract (Vyper) reduce_position function
**Root cause:** reduce_position decrements position.margin_amount proportionally but never adds reduce_margin_by_amount back to self.margin[account][debt_token], so the accounting mapping diverges from the position state.
**Steps:**
1. Call `reduce_position` on a position, which swaps position tokens to debt tokens via `_swap`.
2. Observe `reduce_margin_by_amount` computed from margin_debt_ratio and subtracted from `position.margin_amount`.
3. Note `self.margin[position.account][position.debt_token]` is never incremented by `reduce_margin_by_amount`.
4. Attempt to withdraw the freed margin; the mapping balance is too low, so funds are stuck.
**How found:** Code review of reduce_position accounting: noticed margin_amount reduced but the corresponding margin mapping not credited, breaking the invariant that mapping equals withdrawable margin.
**Exploitation:** Any user calling reduce_position permanently strands the reduced margin portion; no attacker profit, loss accrues to the position owner.
**Insight:** When a function mutates a per-position struct field that mirrors a global accounting mapping, check both are updated in lockstep; here margin_amount and self.margin must stay in sync.
