Limit Orders
Limit orders enable traders to place resting liquidity at specific price levels, combining the precision of traditional order books with the capital efficiency of concentrated liquidity AMMs. Unlike passive liquidity provision across ranges, limit orders represent directional bets at exact prices—buy orders below market, sell orders above market.
For RWA markets where professional collectors want precise execution at target prices (e.g., "buy FP Journe tokens at exactly $4,950"), limit orders provide familiar trading UX while maintaining full AMM composability and atomic settlement.
Why limit orders matter for RWAs
Traditional AMM liquidity provision requires:
- Two-sided capital: Provide both tokens (RWA + USDC)
- Range management: Monitor and adjust positions as price moves
- IL exposure: Accept impermanent loss from rebalancing
- Continuous engagement: Active management for optimal returns
This works well for professional market makers, but creates friction for occasional traders who simply want to:
- Buy at a target price: "I'll buy 50 tokens if price drops to $4,900"
- Sell at a profit target: "I'll sell 100 tokens if price hits $5,200"
- Set and forget: Place order and walk away until filled
Limit orders provide this functionality while maintaining the gas efficiency and atomicity of AMM trades.
How limit orders work in CL AMMs
Single-tick concentrated positions
A limit order is a one-tick-wide concentrated liquidity position placed entirely outside the current price:
Sell order (ask): Place liquidity in a single tick above current price
- You deposit only the token you want to sell (e.g., RWA tokens)
- When price crosses that tick, your tokens get swapped for USDC
- Order "fills" when price moves through that level
Buy order (bid): Place liquidity in a single tick below current price
- You deposit only the quote currency (e.g., USDC)
- When price crosses that tick, your USDC gets swapped for RWA tokens
- Order "fills" when price moves down through that level
This single-sided placement is a fundamental constraint of CL AMMs, not a design choice. In any CL AMM:
- Positions above current price can only hold the base asset (RWA tokens) → sell liquidity
- Positions below current price can only hold the quote asset (USDC) → buy liquidity
- Positions at current price require both assets to be immediately active
This is why limit orders naturally map to CL AMM mechanics: sell orders above market, buy orders below market. You cannot place buy orders above current price or sell orders below—that would require holding the "wrong" asset for that price range.
Example: Current RWA/USDC price is $5,000.
Sell order at $5,200:
- Deposit 50 RWA tokens at tick corresponding to $5,200
- When price rises to $5,200, tokens automatically swap for ~$260k USDC
- You've sold at your target price
Buy order at $4,900:
- Deposit $245k USDC at tick corresponding to $4,900
- When price falls to $4,900, USDC automatically swaps for ~50 RWA tokens
- You've bought at your target price
Automatic execution via swaps
Limit orders don't require matching engines or off-chain keepers. They execute atomically during normal swaps:
- Trader A submits market buy order (buying RWA with USDC)
- Swap routing hits resting limit orders (sell orders at various price levels)
- Limit orders provide liquidity, automatically "filling" in the process
- Trader A gets tokens, limit order owners get USDC at their target prices
This is pure on-chain execution with zero reliance on external infrastructure.
Batching for gas efficiency
A naive implementation where each limit order is a separate liquidity position would be gas-prohibitive—removing 100 small positions during a swap would cost hundreds of thousands of gas.
Instead, limit orders use epoch batching:
Epoch structure
All limit orders at the same (tick, side) combination are grouped into a single epoch:
- Same tick: e.g., all orders at $5,200
- Same side: e.g., all sell orders (0-for-1 direction)
Orders in the same epoch are fungible and fill pro-rata based on liquidity contribution.
Example: Three traders place sell orders at $5,200:
- Alice: 50 RWA tokens
- Bob: 30 RWA tokens
- Charlie: 20 RWA tokens
These combine into one epoch with 100 RWA tokens total liquidity. When price crosses $5,200 and 60 tokens fill:
- Alice receives: (50/100) × 60 = 30 tokens worth of USDC
- Bob receives: (30/100) × 60 = 18 tokens worth of USDC
- Charlie receives: (20/100) × 60 = 12 tokens worth of USDC
Gas efficiency: Filling 100 orders at the same price costs the same gas as filling 1 order—constant gas per tick crossed, regardless of number of individual orders batched.
Epoch lifecycle
Active epoch: Has unfilled liquidity, waiting for price to cross
- Orders can be placed (increasing epoch liquidity)
- Orders can be cancelled (decreasing epoch liquidity)
- Epoch provides liquidity during swaps
Filled epoch: Price has crossed, liquidity has been swapped
- No new orders accepted into this epoch (would start next epoch)
- Users claim their filled proceeds pro-rata
- Historical record remains for accounting
Anti-DoS protections
Gas efficiency depends on batching many orders into few epochs. A malicious actor could attack this by placing dust orders spread across many ticks, forcing swaps to cross dozens of epochs and pay high gas.
The protocol implements multiple defenses:
Minimum notional thresholds
Each limit order must meet a minimum size denominated in the deposited token. The threshold varies based on collection characteristics:
| Collection Liquidity | USDC Min | RWA Token Min | Rationale |
|---|---|---|---|
| Deep (>$10M TVL) | $10 | $10 equivalent | High volume collections, balance accessibility with spam protection |
| Standard (10M TVL) | $25 | $25 equivalent | Typical collections, moderate protection |
| Thin (<$1M TVL) | $100 | $100 equivalent | Low liquidity collections, strong spam protection needed |
Enforcement: When a user places an order, the protocol calculates the exact token amount owed to the pool for that one-tick position. If below the threshold, the transaction reverts.
Why this works: Prevents dust epochs from ever becoming active. An attacker would need to place minimum-sized orders (at least $10–$100) across many ticks, making the attack economically infeasible due to capital lockup and gas costs.
What determines the threshold? Collections with deeper liquidity can afford lower minimums because:
- Higher trading volume means more orders batch naturally
- Stronger network effects (more traders → more epoch batching)
- Lower relative cost of processing additional epochs
Smaller collections need higher minimums to compensate for:
- Fewer natural batching opportunities
- Higher gas cost relative to trading volume
- Greater vulnerability to spam attacks
Example: On a standard RWA collection ($5M TVL) with $25 USDC minimum:
- ✅ Alice places buy order for 5 tokens at $5,000 → deposits $25k USDC
- ✅ Bob places buy order for 1 token at $100 → deposits $100 USDC
- ❌ Charlie tries buy order for 0.2 tokens at $100 → deposits $20 USDC → reverts (below $25 minimum)
Placement stride (coarser tick grid)
Standard CL AMM pools allow liquidity placement at every tick (e.g., every 1 basis point). Limit orders use a coarser grid, restricting placement to every Nth tick:
| Pool Fee Tier | Base Tick Spacing | Limit Order Stride | Effective Spacing |
|---|---|---|---|
| 0.05% | 10 bps | 4× | 40 bps |
| 0.30% | 60 bps | 4× | 240 bps (~2.4%) |
| 1.00% | 200 bps | 2× | 400 bps (~4%) |
Mechanism: Only ticks where (tick / tickSpacing) % stride == 0 are valid for limit orders. Other ticks reject limit order placement.
Why this works: Collapses would-be dust spread across adjacent ticks into fewer, larger epochs. An attacker must concentrate capital into fewer price points to achieve the same number of epochs.
Trade-off: Slightly fewer price points available for limit orders (e.g., every ~40 bps instead of every 10 bps), but massively fewer epochs crossed per swap.
Example: Pool with 10 bp tick spacing and 4× stride:
- ✅ Tick 40 (4 × 10 bp): Valid limit order tick
- ✅ Tick 80 (8 × 10 bp): Valid limit order tick
- ❌ Tick 50 (5 × 10 bp): Invalid for limit orders (standard LP positions still allowed)
- ❌ Tick 70 (7 × 10 bp): Invalid for limit orders
This reduces the maximum number of limit order epochs a swap can cross by the stride factor (4×), making DoS attacks 4× more expensive.
Swapper rebates
Even with minimum notional and placement stride, crossing many epochs incurs gas costs for the swapper. To neutralize this friction, the protocol offers fill rebates:
Mechanism: When a limit order epoch fills during a swap, a small percentage (e.g., 5 basis points = 0.05%) of the filled proceeds is rebated to the swapper who triggered the execution.
Example: Swap crosses $5,200 sell epoch with 100 RWA tokens:
- Epoch receives $520k USDC from swap
- Rebate: 0.05% × $520k = $260 USDC → sent to swapper
- Epoch net proceeds: $519,740 USDC → distributed pro-rata to limit order owners
Gas offset: $260 rebate compensates swapper for marginal gas cost of processing that epoch. Large swaps crossing many epochs accumulate rebates, making routing through the pool attractive rather than penalizing.
Economic impact on limit order owners: Negligible—5 bps is smaller than typical bid-ask spread. Limit order owners still get favorable execution at their target price, just slightly reduced by the rebate (equivalent to a tiny execution fee).
Why this matters: Without rebates, swappers might avoid routing through pools with many resting limit orders to minimize gas. Rebates ensure that liquidity attracts flow rather than repelling it.