How one blind request becomes one fill
The grid floor from above, an orange cable routed between two hatches
Plan of the floor

The hook

The DarkHooks hook is the dark pool inside Uniswap v4. Each listed pair has a v4 pool, base against USDG, that holds no liquidity and never shows a curve. Every swap in it carries one agent's signed two-sided quote as hookData; in beforeSwap the hook settles that quote through DarkDesk.fill, with itself as the taker, and hands the result back to the PoolManager as a BeforeSwapDelta. The swap is a real Uniswap v4 swap through the real PoolManager, routable by the Universal Router or any v4 router, and the side still exists only inside it.

Universal Routersettle, swap, takePoolManagercalls beforeSwapDarkHooksquote in hookDataDarkDesk.fillboth legs, one txamountToSwap after the hook: 0. The pool's price and liquidity never change.
A DarkHooks swap: the router settles the input, the PoolManager calls the hook, the hook fills the quote on DarkDesk and pays the output back, the router takes it.

Addresses

DarkHooks0xb9662EA9819b6068d62d771dB3A473E5012B2888, Sourcify exact match, deployed in 0xaa217bb82f6e…
PoolManager0x8366a39CC670B4001A1121B8F6A443A643e40951
Universal Router0x8876789976dEcBfCbBbe364623C63652db8C0904
Permit20x000000000022D473030F116dDEE9F6B43aC78BA3

Pools

PairPool idKey
NVDA / USDG0x78f73e72be97586c68a14b2295490b90f397e4911504bceadff01d2bdf31e6f9fee 0, tick spacing 1, hooks DarkHooks
SPY / USDG0x57471330dfd7725b4f67072b5ffb16d8623fce9a7ec85978dc11cbeb170e2c19fee 0, tick spacing 1, hooks DarkHooks
TSLA / USDG0x8ac43cf2408a7f40da5a0b41fe300f6d04f8dbdfc4c009d91c339354211f6928fee 0, tick spacing 1, hooks DarkHooks
WETH / USDG0x721c2241f8eaa9d835d9c1cada2a8b7c4e523489036e2c84b033b046ee997d56fee 0, tick spacing 1, hooks DarkHooks

Permissions

  • beforeInitialize: only the owner, only fee 0 and tick spacing 1, only a listed base against USDG.
  • beforeAddLiquidity: always reverts, so nothing ever feeds the curve.
  • beforeSwap with beforeSwapReturnDelta: the specified delta always consumes the whole amountSpecified, so the PoolManager's own swap math runs on zero and the pool's price never moves.

The address ends in the permission bits 0x2888, mined with the CREATE2 deployer.

Modes

SwapSide on DarkDeskAmounts
Exact in, base inSELL at the bidYou pay the base amount, you receive the USDG leg minus the fee.
Exact out, base outBUY at the askYou receive the base amount, you pay the USDG leg plus the fee.
Exact in, USDG inBUY at the askThe hook buys the largest amount the input pays for, capped at the quote's size. At most 2 micro-USDG of rounding stays with the hook; more than that reverts.
Exact out, USDG outnoneNot supported: reverts.

A swap with no hookData, a quote for another pair, an expired or already used quote, or a maker that cannot pay reverts the whole swap: nothing is left half done. DarkDesk charges its 5 bps fee to its caller, which is the hook, so the swapper pays it inside the swap's amounts. The $DARKHOOKS fee waiver looks at the caller, so it applies to direct fills, not to v4 swaps.

Through the Universal Router

Three v4 actions in one V4_SWAP command, exact in both ways. The input is paid before the swap, and the output is taken with the quoted amount as the minimum.

commands = 0x10 (V4_SWAP)
actions  = 0x0b SETTLE(tokenIn, amountIn, payerIsUser = true)
           0x06 SWAP_EXACT_IN_SINGLE(poolKey, zeroForOne, amountIn, amountOutMinimum,
                                     minHopPriceX36 = 0, hookData = abi.encode(Quote, signature))
           0x0f TAKE_ALL(tokenOut, amountOutMinimum)
deadline = the quote's expiry

The wallet approves the input token to Permit2 and Permit2 to the Universal Router once per token. The SDK builds everything locally:

import { BUY, SELL, best, requestQuotes, prepareV4, swapV4, PAIRS, USDG } from "./darkhooks.mjs";

await prepareV4(account, USDG);            // once per token
await prepareV4(account, PAIRS.NVDA);
const rfq = await requestQuotes("https://darkhooks.app", "NVDA", "0.0002");   // no side
const receipt = await swapV4(account, best(rfq, BUY), BUY);       // a v4 swap

Calldata from the API

POST /api/darkhooks/swap with { quote, signature, side, amount? } returns ready-to-sign Universal Router calldata, the amounts and the approvals needed. The MCP endpoint has the same as darkhooks_swap_calldata, and GET /api/darkhooks lists the pools. Asking the server for calldata tells it your side a moment before you send; the app and the SDK build the calldata in your own browser or process instead.

Tested

Sixteen tests on a fork of the live chain, against the real PoolManager and DarkDesk: each direction with the base as currency0 and as currency1, each exact-in and exact-out mode, no hookData, expired and reused quotes, a quote for the wrong pair, exact out for USDG, overpaying, a maker that cannot pay, adding liquidity, who may initialize, the fee, and the pool's price unchanged after a swap. The first swaps on the live pools: a buy and a sell of NVDA through the Universal Router.