# Trade fees can be avoided in limit orders

Flatmoney limit-order close snapshots trade fee at announcement; user opens tiny position, announces limit order, then enlarges additionalSize to dodge fees owed to LPs.

- Payout: Contest reward
- Severity: high
- Program: Flatmoney
- Credited: 0xLogos
- Disclosed: 2023-12-11
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2023-12-flatmoney-judging/issues/212
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-12-flatmoney%3AH-8
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:evm tech:solidity technique:race-window technique:source-review vuln:business-logic vuln:fee-bypass

**Target:** LimitOrder.sol / LeverageModule executeClose on Flatcoin vault
**Root cause:** Trade fee is computed from position.additionalSize at announceLimitOrder time and stored in _limitOrderClose, but additionalSize can be increased via executeAdjust before execution, so the stored fee no longer reflects the actual size being closed.
**Steps:**
1. Open a minimal leverage position via announceAndExecuteLeverageOpen (e.g. margin 0.05e18, additionalSize 0.1e18).
2. Call limitOrderProxy.announceLimitOrder with priceUpperThreshold = 2x current price; fee stored based on tiny additionalSize.
3. Call announceAndExecuteLeverageAdjust to raise additionalSizeAdjustment (e.g. 2400e18) and margin.
4. Wait for price to hit threshold; keeper calls limitOrderProxy.executeLimitOrder(tokenId, priceUpdateData).
5. executeClose uses the stale stored tradeFee, so the 2400e18 size adjustment pays no trade fee; compare profit vs expected-behaviour ordering.
**How found:** Code reading of LimitOrder.sol fee calculation at announcement vs LeverageModule.executeClose usage, noticing additionalSize is mutable via executeAdjust between announce and execute; confirmed with two forge tests differing only in operation order.
**Exploitation:** Trader sequences announce-limit-order before the size-increasing adjust so the fee snapshot is taken on the small size; on execution the full enlarged position closes while only the tiny fee is deducted, leaving the avoided fee (2400 rETH * 0.1%) as extra profit at LP expense.
**Insight:** When a fee/price is snapshotted at announce time, check every field it depends on for mutability between announce and execute (size, rate, oracle) and test reordering the adjust call relative to the announce call.
