BLK ———

Aggregator integration

Our pools are ordinary Uniswap v4 pools whose hook replaces the curve. If you already route v4, you need three things: a way to find the pools, an exact quote, and the swap encoding you already use.

1. Discover pools

Every pool is created by PropFactory and registered on the hook:

  • PropFactory.PoolCreated(poolId, currency0, currency1, creator)
  • PropHook.PoolRegistered(poolId, idx, currency0, currency1)
  • the v4 PoolManager.Initialize event, with hooks = PropHook

The PoolKey is { currency0, currency1, fee: 0, tickSpacing: 1, hooks: PropHook }. Native HYPE is currency0 = address(0).

2. Quote exactly

Call the hook's views in the block you intend to execute in:

(uint256 amountOut, address maker) = PropHook.quoteExactIn(key, zeroForOne, amountIn);
(uint256 amountIn,  address maker) = PropHook.quoteExactOut(key, zeroForOne, amountOut);

They run the swap's own pricing code and match execution to the wei. The official V4Quoter (quoteExactInputSingle / quoteExactOutputSingle) returns the same amounts, so an existing v4 quoting path works unchanged. Quotes revert (NoEligibleQuote, Expired, CapExceeded, …) when no maker can fill; treat that as "no liquidity at this size".

Quotes move every block. Makers refresh as their reference price moves (the reference bot: on every 1.5 bp move and at least every 10 L2 blocks, about 1.2 s) and every quote widens with age. Quote and submit within a few blocks and always set a minimum output (exact-in) or a maximum input (exact-out). There is no public mempool, so you cannot be front-run by a mempool watcher, but transactions in the same block are ordered by priority fee.

3. Execute

Universal Router

Use the V4_SWAP command with SWAP_EXACT_IN_SINGLE (or SWAP_EXACT_OUT_SINGLE), then SETTLE_ALL and TAKE_ALL, exactly as for any v4 pool:

import { encodeUniversalSwap } from '@quotient/sdk';

const { commands, inputs, value } = encodeUniversalSwap({
  key, zeroForOne, exactIn: true, amount: amountIn, limit: minOut,
});
await universalRouter.write.execute([commands, inputs, deadline], { value });

ERC-20 input goes through Permit2 as usual. Native HYPE input is sent as msg.value.

Direct PoolManager integration

If you call PoolManager.swap yourself inside unlock, pass sqrtPriceLimitX96 = MIN_SQRT_PRICE + 1 for zeroForOne and MAX_SQRT_PRICE − 1 otherwise (the pool's price never moves). After swap returns, settle the input currency and take the output currency as for any v4 swap. No liquidity is ever added to these pools; do not route liquidity operations to them.

Verified on testnet

Experiment E4 compared four paths on the same quote, in both directions, exact-in and exact-out, at $10, $100 and $1,000: the hook's views, the official V4Quoter, real swaps through the Universal Router, and real swaps through a minimal third-party-style router (SimpleRouter: unlock → swap → settle/take, no knowledge of the hook). All 12 cases agreed to the wei across all four paths.

Gas

Measured median per swap: 180k gas through the Universal Router (Permit2 path) and 164k through a minimal router. Most of it is the v4 unlock/settle path; the hook adds one storage read per maker considered.

Checklist

  • index PoolRegistered (or PoolCreated) to discover pools
  • quote with quoteExactIn / quoteExactOut or V4Quoter, in the execution block
  • treat quote reverts as zero liquidity
  • always set minOut / maxIn
  • execute with Universal Router V4_SWAP, or your own v4 swap path