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.
- commit: orders go on chain only as
hash(sk, side, amount, limitTick, salt). nothing else is public. - 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.
- settle: anyone calls
settleBatch(). a fixed 16 tick scan picks the clearing tick that maximizes matched volume. - 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
- compact
- docker
- javascript
- react
- vite
Log in or sign up for Devpost to join the conversation.