Skip to content

Dark pool · settles on Uniswap v4

Visible size
is a tax.

Every order you broadcast tells the market what you need before you get it. Here, an order is a signature, not a transaction: it costs nothing, touches no chain, and dies on a bumped nonce. Nothing is public until you choose to settle.

Open the terminal
14:22:07WETH/USDC42.000@ mid0x7f3a…c19d14:22:11not disclosed14:22:19WETH/USDC6.500@ mid0x21b8…4e0214:22:24not disclosed14:22:26not disclosed14:22:31WETH/USDC128.400@ mid0xd0c5…9a7714:22:38WETH/USDC17.250@ mid0x9e14…b3f114:22:44not disclosed14:22:52WETH/USDC3.080@ mid0x4a67…2d8014:22:58not disclosed14:23:05WETH/USDC71.900@ mid0xbb92…07ce14:23:09not disclosed

§ 01

The split below is the whole product.

A worked example, not a live book. The terminal shows what actually settles.

Solid means it crossed against someone who was already there: filled at the reference mid, no fee, no impact, nothing in the mempool. Hatched means it went to the Uniswap v4 pool like any other swap. Put 200 WETH through it and the two halves come out like this.

Crossed at the mid

128.40WETH

Price
$3,142.80 — the reference
Slippage
0 bps
Fee
0 bps
Impact
none — the curve never moves

You find out that your order filled. You never find out who was on the other side, and they never find out your size.

Routed to the pool

71.60WETH

Price
$3,125.55 — after impact
Slippage
54.9 bps
Fee
5 bps
Impact
the pool reprices

Ordinary, public, and visible in the mempool before it lands. This leg is the price of not waiting for a counterparty.

Blended that is 19.7 bps, against 143.5 bps if the whole 200 had gone to the pool.

$7,786 kept

§ 02

Four steps. Only the last one touches a chain.

  1. 01

    You sign. Nothing happens.

    The order is a typed message sitting with the matching engine. No transaction, no gas, no approval spent, nothing in the mempool. It exists as a signature you can kill by bumping your nonce.

    hashIntent(intent)
      → 0x9f2c…41ab   // EIP-712, domain "Kryptis" v1
  2. 02

    Someone takes the other side. Or nobody does.

    Orders are matched against each other, never against a book anyone can read. A 128.4 WETH bid meets a 200 WETH offer and 128.4 crosses. The other 71.6 is not filled here, and we don't pretend otherwise.

    sell 200.000 WETH   ┐
                        ├─ cross 128.400 @ mid
    buy  128.400 WETH   ┘
    remainder 71.600  → public pool
  3. 03

    The match is attested.

    Settlement carries a proof that the engine matched what it says it matched. Right now that proof is an attestation over the intent hash. It is a placeholder, the verifier is swappable, and a Groth16 circuit drops in without changing a byte of the ABI.

    _verifyAttestation(proof, intentHash)
      require(bytes32(proof) == intentHash)   // placeholder
      // → Groth16Verifier.verifyProof(a, b, c, input)
  4. 04

    It settles in one call, or not at all.

    The contract re-checks the deadline, the nonce, the signer, the proof and the quote, then moves the tokens. Eleven checks. Any one of them fails and the whole thing reverts — you lose the gas and nothing else.

    executeIntent(intent, signature, proof)
      → IntentSettled(intentHash, trader, tokenIn, …)

§ 03

We hide your order before it fills, not after.

This is the part most privacy pitches skip, so here it is up front. Settlement is an ordinary ERC-20 transfer emitted by a contract anyone can read on Etherscan. What you get back is the window before the fill — the window where a searcher would have front-run you and the market would have learned your size.

On-chain, permanently

  • IntentSettledthe event, with both token addresses
  • traderthe settling address — it is msg.sender
  • amountIn / amountOutthe exact sizes that moved
  • intentHashthe digest, flagged as settled forever

Never published

  • your order, before it fillsnothing in the mempool to snipe
  • resting depthyou learn your fill, not the book
  • cancellationsa bumped nonce says nothing
  • what didn't crossunfilled size is never disclosed

Set in the coarse cut of Redaction, because that is what the chain gets: the shape of a thing, not the thing.

§ 04

One file. Eleven checks.

Read it before you sign anything. There is no admin path to your funds — the only privileged call moves the pool's own inventory, and the price feed is owner-set, which is fine for a testnet and not fine for money.

Solidity
0.8.24
Standards
EIP-712 · ERC-20 · ReentrancyGuard
Settlement
atomic — one call, or it reverts
Verifier
attestation placeholder
Audit
none — research software
Read the source

The guard rail

if (intent.amountIn == 0)                revert ZeroAmount();
if (intent.tokenIn == intent.tokenOut)   revert SameToken();
if (block.timestamp > intent.deadline)   revert Expired();
if (intent.trader != msg.sender)         revert Unauthorized();
if (intent.nonce <= usedNonces[trader])  revert NonceUsed();
if (intent.slippageBps != 0)             revert Slippage();
if (settled[intentHash])                 revert AlreadySettled();

address signer = ECDSA.recover(intentHash, signature);
if (signer != intent.trader)             revert BadSignature();
if (!_verifyAttestation(proof, hash))    revert InvalidProof();

amountOut = quote(tokenIn, tokenOut, amountIn);
if (amountOut < intent.minAmountOut)     revert Slippage();

Line six is the opinionated one. The contract refuses any order that asks for slippage tolerance. If it can't fill you at the quoted price it doesn't fill you.

Don't take our word for it.

The pool, the deploy scripts and the tests are in the repository. Point it at a testnet and settle an order end to end. It takes about five minutes and costs nothing.

cd contracts
forge test
forge script script/DeployKryptis.s.sol \
  --rpc-url $SEPOLIA_RPC_URL --broadcast