A decentralized exchange where orders stay private until settlement. Traders encrypt orders to the operator and prove validity using zero-knowledge proofs, without revealing the pair, price, or size to any external observer.
Rust · Solidity · ZK Circuits (halo2 / arkworks)
https://front-five-flax.vercel.app/
On a normal DEX, your orders sit in a public mempool. Anyone can see them, front-run them, sandwich them. This project takes a different approach: orders are encrypted to the engine operator and matched in periodic batch auctions. The operator proves via ZK that every auction was executed correctly. Settlement happens in batches with aggregated ZK proofs verified on-chain.
Three things we care about:
- Orders are invisible to external observers before settlement.
- Every matched trade comes with a ZK proof that the batch auction was computed correctly.
- Price emerges from the protocol itself — each auction round produces a clearing price with no oracle dependency.
| Typical DEX | This project | |
|---|---|---|
| Order visibility | Public mempool, front-runnable | Private until settlement |
| Proof system | None | ZK-SNARK per order batch |
| Matching model | Continuous on-chain (expensive) | Off-chain periodic batch auction |
| Price discovery | Visible order book | Clearing price after each auction round |
| Settlement | Immediate per-order | Batched, gas-efficient |
| Trust model | Trustless (but transparent) | Semi-trusted operator + ZK proof of correct execution |
| Stack | Solidity only | Rust + Solidity + ZK |
flowchart TB
subgraph Client["Trader (Client)"]
A1["Generate ZK proof locally\n(Rust WASM / native CLI)"]
A2["Encrypt order to\noperator public key"]
end
subgraph API["API Gateway (Rust, tonic + axum)"]
B["gRPC + REST Server"]
B1["Verify proof format\n+ validate commitment"]
B2["Rate limiting / Auth"]
end
subgraph Engine["Matching Engine (Rust, tokio)"]
C["Decrypter seam\n(ECIES; pluggable)"]
D["Collect orders into\ntime-bounded batch"]
D2["Compute clearing price\n+ match crossing orders\n+ self-match prevention"]
ES["Event Store\n(append-only, bincode + fsync;\nciphertext + commitment + proof;\nno plaintext)"]
EX["Expiry sweep\n(TTL-based)"]
end
subgraph Aggregator["Proof Aggregator"]
F["ProofAggregator trait\n(pluggable: subprocess / FFI / RPC;\nnoop default + subprocess impl)"]
G["Aggregate ZK proofs\ninto single batch proof"]
end
subgraph Contracts["Settlement Layer (EVM)"]
H["DarkPool.sol\nEscrow + Settlement"]
I["Verifier.sol\nOn-chain proof verification"]
end
subgraph Frontend["Demo Frontend (Next.js)"]
J["Auction history\nclearing prices\naggregated depth"]
end
A1 --> A2
A2 -- "POST /order\n{commitment, proof, encrypted_payload}" --> B
B --> B1 --> B2
B2 -- "gRPC" --> C
C --> D
D -- "Every N seconds\n(batch auction tick)" --> D2
D2 -- "OrderMatched events" --> ES
D -- "OrderPlaced events" --> ES
D2 -- "Matched pairs" --> F
F --> G
G -- "Aggregated proof + batch" --> H
H --> I
I -- "Verify → Release escrow\n→ Transfer tokens" --> K["Settlement Event\n+ Clearing Price"]
B -. "gRPC server-stream\n(StreamAuctions)" .-> J
K -. "On-chain events" .-> J
- Trader submits a Pedersen commitment to the order parameters and locks collateral in escrow.
- Trader runs a Rust circuit locally, gets back a ZK proof that the order is valid.
- Trader encrypts the full order to the operator's public key and submits commitment + proof + encrypted payload.
- The operator decrypts in memory, collects orders into a time-bounded batch (default: 5s), and runs a batch auction — computing a clearing price and matching all crossing orders. Plaintext orders exist only in engine RAM during the auction window. The event log is an append-only file (bincode-encoded, fsync per append) containing ciphertext + commitment + proof only — never plaintext.
- Matched pairs are handed to a pluggable
ProofAggregator(subprocess / FFI / RPC), then submitted on-chain via a pluggableSubmitter. The Solidity verifier checks the aggregated proof and transfers tokens atomically.
Status note. ECIES decryption and the subprocess proof aggregator are implemented. The
alloy-basedEthSubmitterexists indp-settlement, but bootstrap glue from--eth-rpcto a live submitter is not yet wired indarkpool-server; setting the flag today logs a warning and runs with the noop submitter. All seams are pluggable. The engine, API, event store, auction, and expiry logic are production-shape; circuits and on-chain wiring need their final integrations before mainnet.
- Periodic batch auction (default: every 5 seconds). All orders in the same round are treated equally — no temporal advantage.
- Clearing price computed as the price that maximizes matched volume.
- Partial fills are supported. Residual quantity carries over to the next auction round.
- Orders expire after a configurable TTL (default: 10 min).
- Orders from the same commitment key cannot match each other.
- Minimum order size is enforced at the circuit level, not in the engine.
- Batches hold up to 256 matched pairs. Cap is enforced by the settlement contract; the engine hands the aggregator whatever matched in the current auction round.
- If the aggregated proof fails verification, the entire batch is rejected. No partial settlement.
- Collateral is locked in escrow at commitment time and released atomically at settlement (enforced in
DarkPool.sol). - 0.05% protocol fee is taken from the taker side (enforced in
DarkPool.sol).
- Nobody outside the operator can determine the price or size of a pending order from on-chain data.
- The matching engine operator can see decrypted order contents but is cryptographically bound to execute auctions correctly via ZK proofs. This mirrors institutional dark pools in TradFi.
- After settlement, the clearing price and trade amounts become visible but individual orders are unlinkable to wallet addresses without additional info.
| Layer | Language | What it does |
|---|---|---|
| ZK Circuit | Rust (halo2 / arkworks) | Generates and verifies proofs of order validity |
| Matching Engine | Rust (tokio) | Batch auction logic, clearing price computation, event sourcing (crates/dp-engine/) |
| Event Store | Rust (bincode + fsync) | Append-only event log for state reconstruction and auditability (crates/dp-event/) |
| Proof Aggregator | Rust (subprocess) | Combines individual proofs into a single batch proof (crates/dp-aggregator/) |
| Settlement Contract | Solidity | On-chain proof verification, token transfers, escrow |
| API Gateway | Rust (tonic gRPC + axum REST) | Order submission and status endpoints, auth, rate-limit (crates/dp-api/) |
| Demo Frontend | TypeScript / Next.js | Auction history, clearing prices, aggregated depth |
cargo build --release --workspace
cargo test --workspace
cargo run --release --bin darkpool-serverdarkpool-server is the operator binary. Common flags (see crates/dp-api/src/config.rs for the full list):
--grpc-addr 0.0.0.0:9090 # gRPC listen addr
--http-addr 0.0.0.0:8080 # REST listen addr
--auction-interval 5s # batch auction tick
--event-log /var/lib/darkpool.log # durable event log (omit → in-memory)
--operator-key /etc/darkpool/op.key # ECIES private key (omit → noop decrypter)
--aggregator-bin /usr/local/bin/dp-aggregate # proof aggregator subprocess (omit → noop)
--eth-rpc wss://... # settlement RPC (currently warns; submitter wiring deferred)
--api-keys k1,k2 # comma-separated API keys (omit → no auth)
--rate-limit 100 --rate-burst 20 --rate-stale-after 5m
crates/
├── dp-types/ # Shared domain types (Order, Fill, Side, EventType)
├── dp-crypto/ # ECIES decrypter + commitment computation
├── dp-event/ # Append-only event store (MemStore + FileStore, bincode + fsync)
├── dp-book/ # Order book projection + depth aggregation
├── dp-auction/ # Batch auction, clearing price, self-match prevention
├── dp-aggregator/ # Pluggable proof aggregator (noop + subprocess)
├── dp-settlement/ # EthSubmitter (alloy) + BatchSettled watcher
├── dp-engine/ # Orchestrator: tick loop, recovery, batch lifecycle
└── dp-api/ # tonic gRPC + axum REST, validation, auth, rate-limit
# includes the `darkpool-server` binary entrypoint
front/ # Next.js demo UI
Cargo.toml
Hedge funds, market makers, and DeFi protocols that need MEV protection and don't want their order flow visible to the world.