# `MarginDex::execute_limit_order` will always revert

MarginDex.execute_limit_order clears the limit order in storage before calling _remove_limit_order, so the zero-address lookup yields an empty uids array and the loop reverts, blocking all limit order execution.

- Payout: Contest reward
- Severity: medium
- Program: Unstoppable
- Credited: Dug
- Disclosed: 2023-06-21
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2023-06-unstoppable-judging/issues/111
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-06-unstoppable%3AM-8
- Tags: domain:web3 impact:dos kind:contest platform:sherlock sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:dos

**Target:** MarginDex Vyper contract, execute_limit_order / _remove_limit_order
**Root cause:** execute_limit_order sets self.limit_orders[_uid] = empty(LimitOrder) before calling _remove_limit_order, which re-reads the now-empty order (account = zero address) and iterates an empty limit_order_uids array, hitting the raise branch.
**Steps:**
1. Call `MarginDex.execute_limit_order(_uid)` for any valid limit order.
2. Function executes `self.limit_orders[_uid] = empty(LimitOrder)` clearing storage.
3. Then calls `self._remove_limit_order(_uid)`, which reads `order = self.limit_orders[_uid]` (now empty, account = 0x0).
4. `uids = self.limit_order_uids[order.account]` returns empty DynArray.
5. Loop `for i in range(1024)` reaches `i == len(uids) - 1` (i.e. 0 == -1) and executes `raise`, reverting the whole tx.
**How found:** Code reading of execute_limit_order and _remove_limit_order revealed the order was zeroed before the removal helper re-read it, causing the uids lookup to use the zero address.
**Insight:** When a helper re-reads state that the caller just mutated, trace the value flow: clearing a struct before a removal routine that derives its index from that struct silently switches to the zero-address bucket and reverts.
