# Attacker can post-running attack to prevent LiquidityPool from hedging by orders of PerpMarket

Attacker front-runs after executePerpOrders with a tiny trade, forcing LiquidityPool to submit a small delayed order and blocking queued hedge orders, leaving trades unhedged.

- Payout: Contest reward
- Severity: medium
- Program: Polynomial Protocol
- Credited: KIntern\_NA
- Disclosed: 2023-08-01
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-03-polynomial-findings/issues/114
- Library page: https://bugbountylibrary.com/r/c4%3A2023-03-polynomial%3AM-12
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:solidity technique:race-window technique:source-review vuln:business-logic vuln:dos

**Target:** LiquidityPool / Synthetix PerpMarket delayed order hedging
**Root cause:** _placeDelayedOrder submits only the current trade's sizeDelta instead of queuedPerpSize + sizeDelta, so any new order occupies the single pending-order slot and placeQueuedOrder (which requires order.sizeDelta == 0) cannot flush the accumulated hedge size.
**Steps:**
1. Wait for authority to call executePerpOrders, resetting pool's delayed order sizeDelta to 0.
2. Post-run open/close a small position on LiquidityPool, triggering _hedge -> _placeDelayedOrder.
3. Since oldSize==0 and status==0, pool submits submitOffchainDelayedOrderWithTracking(sizeDelta) with the small sizeDelta.
4. Pool's order.sizeDelta is now nonzero, so placeQueuedOrder reverts on require(order.sizeDelta == 0); queuedPerpSize accumulates.
5. Repeat after each executePerpOrders to keep pool perpetually unhedged.
**How found:** Code reading of _placeDelayedOrder and placeQueuedOrder: noticed the single-order-slot guard (oldSize != 0) and that placeQueuedOrder requires order.sizeDelta == 0, then reasoned that any fresh small order after executePerpOrders blocks the queued hedge.
**Exploitation:** Attacker monitors executePerpOrders and immediately submits a minimal trade, occupying the pool's single delayed-order slot with a tiny sizeDelta. Subsequent trades accumulate into queuedPerpSize and cannot be submitted until the next executePerpOrders, which the attacker again post-runs. Net effect: pool's real exposure stays unhedged, exposing it to price risk.
**Insight:** When a contract keeps a single pending-order slot and a separate queue, check whether a small attacker-triggered order can occupy the slot and starve the queue; post-run (back-run) the permissioned flush function to reset the slot repeatedly.
