Cohort Order Book: an O(1) fully on-chain limit order book via generational fungible liquidity

Cohort Order Book: an O(1) fully on-chain limit order book via generational fungible liquidity

Draft for Ethereum Magicians. Looking for critique, especially on the one-sided-add justification in §4 and the FIFO concerns in §6.

TL;DR

A fully on-chain limit order book where orders at each price point are fungibilized into time-ordered “cohorts.” Fills touch ≤2 cohorts per price point → O(1) (no per-order loop), partial fills are exact, and cancellation is just “withdraw your own position.” The design’s real justification isn’t gas — it’s that it’s the clean way to accept one-sided liquidity adds to a partially-filled price level, which pro-rata AMMs cannot do without either two-sided adds or subtle rebasing math. The cost is a coarse, tunable batch-FIFO. I want to pressure-test whether that price is acceptable — and whether this is worth building at all.


1. Motivation

On-chain order books are gas-hostile: filling loops over resting orders, O(n) in the number of orders. That’s why on-chain trading converged on AMMs, which fungibilize liquidity per price level (O(1) fills) — but in doing so give up clean limit-order semantics.

The specific semantic that breaks is one-sided adds to a partially-filled level (§4). This post proposes a structure that keeps O(1) fills and clean one-sided adds, and is honest that the price is a coarse FIFO.

2. Core idea — cohorts

At each price point (tick), resting liquidity lives in cohorts — fungible batches (one fungible token per cohort). At most two are live at any time:

  • Staging — where new orders land; not yet being filled.
  • Open — the batch currently being filled; frozen (no new orders join it).

3. Mechanism

  1. Place → mint a share of the current staging cohort.
  2. Promote → on the first fill at this price, staging is frozen → open, and a fresh staging opens.
  3. Fill → consume open, tracked by a single fill fraction (filled / frozen total).
  4. Settle → when open is fully consumed it settles (redeemable), and the next staging promotes.

Because only one open + one staging are ever live, a fill touches ≤2 cohorts per price point → O(1) (settled cohorts just await redemption, O(1) per LP). A trade sweeping multiple ticks is O(ticks crossed), same as any tick-based AMM.

Freeze-on-fill is what gives exact partial-fill accounting: each cohort has a single clean fill fraction, and new orders never inherit someone else’s partial fill.

Worked example (one tick): Alice 100 + Bob 50 → Staging C1=150. Fill 90 → C1 freezes (60% filled), new Staging C2. Carol 30 → C2=30. Fill 60 → C1 fully settles. Fill 20 → C2 promotes, consumed 67%.

4. Why generations at all? — the one-sided-add problem

This is the actual justification, and it’s easy to miss.

A limit order is one-sided (you post a single token at a price). In a pro-rata AMM (Uniswap V3 range order), once a tick is partially crossed (price inside the range), you can only add liquidity in the current two-token ratio — you cannot cleanly top up one-sided. In practice, V3 range-order users open a new position per add during a partial fill — i.e. they manually create generations.

You can pool one-sided adds with a per-LP accumulator, but consuming fills rebase the remaining one-sided balance while accruing the counter-asset — a rebasing-token-plus-rewards accounting that is doable but subtle and error-prone.

Cohorts automate the “new position per add” workaround: freeze the partially-filled batch, route fresh one-sided liquidity to a clean 0%-filled batch. That’s the real value.

The same structure that enables this — draining the open batch before promoting the next — is also what imposes a batch-FIFO ordering across cohorts. That’s the main tradeoff, discussed in §6.

5. Prior art (this is not novel in isolation)

  • Ambient (CrocSwap) “knockout liquidity” is essentially this: on-chain limit orders as concentrated liquidity that fully converts when crossed, using pivots/epochs — which are cohorts by another name — precisely to handle one-sided adds + partial fills. This post is largely a generalization/formalization of that pattern as a standalone order book.
  • Trader Joe Liquidity Book — fungible per-bin liquidity (the recurring, two-sided endpoint).
  • Uniswap V3/V4 range orders + feeGrowth — the pro-rata accumulator alternative in production.

If there’s prior art that handles one-sided adds more cleanly, I’d love pointers.

6. Known concerns / where I expect pushback

Being upfront, because these are real:

  1. FIFO on-chain is arguably not real FIFO. Intra-block ordering is set by builders (MEV), so “time priority” is partly auctioned, not earned. The promotion boundary (“first fill freezes the cohort”) is itself a potential griefing/MEV surface (dust-fill to freeze a rival cohort; snipe into staging before a known large fill). Mitigation: coarsen cohorts (promote on a volume/time threshold, not every first fill) so it’s pro-rata within large batches and FIFO only at rare boundaries — but does that fully address it?
  2. Front-of-queue adverse selection. The oldest cohort fills first, i.e. gets picked off first by toxic flow. “Priority” may be a poisoned chalice.
  3. Fresh-liquidity discouragement. Back cohorts fill last, so why add depth right before a move? Pro-rata welcomes liquidity anytime.
  4. Capital + adverse selection. A one-shot resting order earns nothing while waiting and is a free option to takers (the classic reason on-chain order books lost to AMMs). This is independent of the cohort structure.
  5. Is one-sided limit UX even worth building given off-chain intents (CoW/1inch/UniswapX: gasless, no lockup, free cancel) and AMM range orders already serve most limit-order demand?

7. Discussion questions

  1. Does keeping O(1) fills + clean one-sided adds necessarily force batch-FIFO, or is there a pro-rata construction that avoids it?
  2. Does coarsening cohorts (threshold-based promotion) reduce the FIFO/MEV concerns to de minimis, or just move them?
  3. Is there a cleaner accounting for one-sided adds under a pro-rata accumulator that I’m underrating (rebase index done right)?
  4. Given Ambient already ships the pattern — is there value in a standalone, generalized order-book form, or is “limit orders as a layer inside an AMM” the only version worth having?
  5. For a term-structure / maturity-bucketed market, a far-dated limit is a composite of per-period orders that must fill atomically — does the cohort model extend, or does that push you back to intent/solver execution?

Thanks in advance for tearing into it.

Two questions:

  1. Does this become more interesting if you take the possibility of mev out of it?

  2. I’ve been looking for some kind of order book demo for ethgent, is this in a deployable state?

Hello!

  1. Yes it could be, but this order book is not solving for MEV problem, we simply solves it with slippage protection mechanism like all swap protocols now.

  2. Well it’s something I am writing now.