inspiration

every public dex leaks every order before it executes. bots front run, sandwich, and extract mev. traders broadcast their size and intent to the whole chain. penumbra proved sealed batch swaps work on a privacy chain, and we wanted to bring that mechanism to midnight, scoped down to fit compact's fixed computation circuits.

what it does

noctis is a dark pool dex. trades clear in discrete batches at a single uniform price.

  1. commit: orders go on chain only as hash(sk, side, amount, limitTick, salt). nothing else is public.
  2. reveal: a zk proof shows the order matches a live commitment, and the amount folds into public per tick demand/supply aggregates. observers see totals per price tick, never who bid what. nullifiers stop double reveals.
  3. settle: anyone calls settleBatch(). a fixed 16 tick scan picks the clearing tick that maximizes matched volume.
  4. claim: traders prove ownership of a filled order via a claim nullifier that cannot be linked back to the commitment.

there is no ordering inside a batch, so front running and sandwiching are impossible by construction, not by trust.

on top of the pool:

  • private vault: shielded note based balances. deposit once, fund orders from private notes, so even your balance stays off chain.
  • batch price oracle: records (batchId, clearingTick, clearedVolume) history and exposes a twap. batch auction prices are hard to manipulate, which makes this a usable mev resistant price feed for other midnight contracts.

how we built it

  • compact contracts on midnight: commit/reveal/settle/claim circuits, all fixed loops (fixed batch size plus a 16 tick price grid) for bounded constraint counts and fast proofs
  • order privacy via commitment hashes plus domain separated reveal and claim nullifiers
  • react + vite frontend with lace wallet, live batch ui, and a depth chart
  • local stack runs a proof server, indexer, and node through docker compose

challenges

fitting a real auction into compact's fixed computation model was the big one. no dynamic loops meant designing around a fixed price grid and batch size from day one. making reveal privacy preserving took a few tries too: amounts fold into aggregate curves so only per tick totals ever go public. and keeping claim unlinkable from commitment while still blocking double spends needed careful nullifier domain separation.

what we learned

fixed circuits force better mechanism design. the constraints that felt limiting at first (16 ticks, fixed batches) ended up producing a cleaner, cheaper, provably fair auction.

what's next

multi pair pools, partial fill carryover between batches, real token bridging, and opening the oracle feed to other midnight protocols.

Built With

Share this project:

Updates