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.Initializeevent, withhooks = 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(orPoolCreated) to discover pools - quote with
quoteExactIn/quoteExactOutorV4Quoter, 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