Developers
Build a hook
Use AvenFairValue in your own hook.
AvenFairValue is a library of internal functions, inlined at compile time. No runtime dependency, nothing to call.
MyHook.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
// forge install SATISH-JALAN/Aven then remap aven/=lib/Aven/contracts/
import {BaseHook} from "uniswap-hooks/base/BaseHook.sol";
import {AvenFairValue as FV} from "aven/src/AvenFairValue.sol";
contract MyHook is BaseHook {
// Permissions must include beforeSwap; the pool must use DYNAMIC_FEE_FLAG.
function _beforeSwap(address, PoolKey calldata key, SwapParams calldata p, bytes calldata)
internal override returns (bytes4, BeforeSwapDelta, uint24)
{
(uint160 sqrtP,,,) = poolManager.getSlot0(key.toId());
// Gas-capped, never reverts: a bad read gives ok = false.
(bool ok, uint64 oracleRaw, uint64 markRaw) = FV.readPrices(cfg.perpIndex, params.precompileGas);
FV.Breakdown memory b = FV.compute(cfg, params, FV.Inputs({
oracleRaw: oracleRaw, markRaw: markRaw, anchorMarkRaw: _anchor,
sqrtPriceX96: sqrtP, sqrtPriceTopX96: _topOfBlock(sqrtP),
zeroForOne: p.zeroForOne, readOk: ok
}));
// Always set the override flag: a dynamic pool's stored LP fee is 0.
return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA,
b.fee | LPFeeLibrary.OVERRIDE_FEE_FLAG);
}
}Keep a top-of-block price and a mark anchor per pool, like AvenHook.sol does.
Quote from TypeScript#
Routers need no changes: the hook implements IDynamicFeeHook.getFee, and V4Quoter simulates the hook.
quote.ts
import { createPublicClient, http } from "viem";
import { avenHookAbi } from "@aven/core/abi";
const client = createPublicClient({ transport: http("https://rpc.hyperliquid.xyz/evm") });
// Exactly what the next swap in this block pays, with every input (I6).
const b = await client.readContract({
address: "0xfdd13cA6D7FcF4Ca710Bc8C77546592F42CF6080",
abi: avenHookAbi,
functionName: "previewFee",
args: [poolKey, /* zeroForOne */ true],
});
// Or Uniswap's IDynamicFeeHook: both directions, size-independent.
const [feeZeroForOne, feeOneForZero] = await client.readContract({
address: "0xfdd13cA6D7FcF4Ca710Bc8C77546592F42CF6080", abi: avenHookAbi, functionName: "getFee", args: [poolKey],
});Gas#
Estimate gas with a margin
The block's first swap writes extra state. Use the estimate × 1.3 + 30k.Aven · MIT · ETHGlobal MumbaiEdit on GitHub ↗