{
  "generated": "2026-08-27T22:51:17.573Z",
  "hooks": [
    {
      "slug": "allowlist-swap",
      "name": "AllowlistSwap",
      "kind": "pattern",
      "tagline": "Gate-controlled per-pool swap allowlist; beforeSwap reverts for unlisted traders.",
      "description": "First-party teaching hook inspired by Uniswap Permissioned Pools. An immutable gate address sets\nallowed[poolId][trader]. beforeSwap resolves the trader from hookData (abi.encode address) or the\nswap sender and reverts Unauthorized if not listed. No permissions-adapter stack — minimal allowlist\ngate only. Experimental. Forge tests in test/AllowlistSwap.t.sol. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/AllowlistSwap.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/AllowlistSwap.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\nfunction _beforeSwap(address sender, PoolKey calldata key, SwapParams calldata, bytes calldata hookData)\n    internal view override returns (bytes4, BeforeSwapDelta, uint24)\n{\n    address trader = traderOf(sender, hookData);\n    if (!allowed[key.toId()][trader]) revert Unauthorized();\n    return (this.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);\n}\n",
      "categories": [
        "compliance"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "beforeSwap"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "anti-sandwich",
      "name": "AntiSandwichHook",
      "kind": "pattern",
      "tagline": "Checkpoint top-of-block pool state and cap one swap direction to that price.",
      "description": "OpenZeppelin’s AntiSandwichHook (MIT) saves Slot0 and tick liquidity at the first swap\nof a block. Later swaps in the protected direction cannot fill better than that\nbeginning-of-block price; the other direction still follows xy=k. Permissions need\nbeforeSwap, afterSwap, and afterSwapReturnDelta. Inherit it and implement\n_handleCollectedFees. Large tick walks can OOG on small tickSpacing — read the file\nwarnings. Experimental library code, not an audit. Prefer this over inventing MEV\nprotection from scratch.\n",
      "source": {
        "repo": "https://github.com/OpenZeppelin/uniswap-hooks",
        "path": "src/general/AntiSandwichHook.sol",
        "url": "https://github.com/OpenZeppelin/uniswap-hooks/blob/master/src/general/AntiSandwichHook.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Uniswap Hooks — excerpt. Full file: source.url\nfunction getHookPermissions() public pure virtual override returns (Hooks.Permissions memory permissions) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: false,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: true,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\n// First swap in a block: checkpoint Slot0 + ticks.\n// Later !zeroForOne swaps: _getTargetUnspecified simulates against that checkpoint\n// and afterSwap enforces the target via afterSwapReturnDelta.\n",
      "categories": [
        "mev-protection"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "beforeSwap",
        "afterSwap",
        "afterSwapReturnDelta"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://github.com/OpenZeppelin/uniswap-hooks",
      "website": "https://v4hooks.com/learn/openzeppelin-hooks",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "anti-snipe",
      "name": "AntiSnipe",
      "kind": "pattern",
      "tagline": "Launch guard — max buy (% supply), snipe tax on exact-input buys for N blocks after init.",
      "description": "First-party teaching hook inspired by Spark's Anti-Snipe launchpad block (usespark.fun/builder).\nafterInitialize records graduationBlock and requires native currency0 + dynamic fee. beforeSwap\napplies snipeTax via OVERRIDE_FEE_FLAG on exact-input buys while inGuardWindow; after guard, baseLpFee.\nafterSwap caps token output to maxBuyBps of totalSupply using afterSwapReturnDelta (FullMath.mulDiv\navoids supply overflow). Per-swap cap only — not per-wallet. Sells skip tax and cap. Experimental.\nForge tests in test/AntiSnipe.t.sol. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/AntiSnipe.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/AntiSnipe.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// v4hooks first-party — excerpt. Full file: source.url\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: true,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction _beforeSwap(address, PoolKey calldata key, SwapParams calldata params, bytes calldata)\n    internal\n    view\n    override\n    returns (bytes4, BeforeSwapDelta, uint24)\n{\n    bool isExactInputBuy = params.zeroForOne && params.amountSpecified < 0;\n    uint24 fee = feeFor(key.toId(), isExactInputBuy);\n    return (this.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, fee | LPFeeLibrary.OVERRIDE_FEE_FLAG);\n}\n",
      "categories": [
        "launchpads",
        "mev-protection",
        "dynamic-fees"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "beforeSwap",
        "afterSwap",
        "afterSwapReturnDelta"
      ],
      "properties": [
        "dynamic-fee",
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "auto-burn",
      "name": "AutoBurn",
      "kind": "pattern",
      "tagline": "Burns burnBps of token output on exact-input buys via afterSwapReturnDelta.",
      "description": "First-party teaching hook inspired by Spark Auto Burn. afterInitialize requires native currency0.\nafterSwap on exact-input buys (zeroForOne, amountSpecified < 0) takes burnBps of token1 output from\nthe swapper via poolManager.take, transfers to DEAD, and returns the same amount as afterSwap\nreturn delta. Sells and exact-output swaps skip. Teaches output-side hook accounting without\ncomposing extra LP fee. Experimental. Forge tests in test/AutoBurn.t.sol. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/AutoBurn.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/AutoBurn.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// v4hooks first-party — excerpt. Full file: source.url\nfunction _afterSwap(address, PoolKey calldata key, SwapParams calldata params, BalanceDelta delta, bytes calldata)\n    internal\n    override\n    returns (bytes4, int128)\n{\n    if (!params.zeroForOne || params.amountSpecified >= 0) return (this.afterSwap.selector, 0);\n    int128 tokenOut = delta.amount1();\n    if (tokenOut <= 0) return (this.afterSwap.selector, 0);\n    uint256 burnAmount = (uint256(int256(tokenOut)) * burnBps) / 10_000;\n    if (burnAmount == 0) return (this.afterSwap.selector, 0);\n    key.currency1.take(poolManager, address(this), burnAmount, false);\n    IERC20(Currency.unwrap(key.currency1)).transfer(DEAD, burnAmount);\n    return (this.afterSwap.selector, burnAmount.toInt128());\n}\n",
      "categories": [
        "launchpads"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "afterSwap",
        "afterSwapReturnDelta"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "back-geo-oracle",
      "name": "BackGeoOracle",
      "kind": "pattern",
      "tagline": "Geomean oracle that backruns its own pool so the recorded price is not stale.",
      "description": "A geometric-mean TWAP oracle that solves the usual problem with in-pool oracles: the\nprice you record is the price after someone traded against you, which is exactly the\nprice an attacker chose. BackGeoOracle answers that by backrunning the swap itself in\nafterSwap and recording the corrected price, so the observation reflects a rebalanced\npool rather than the post-trade extreme.\n\nPermissions are wide but each one is doing something specific. beforeInitialize is a\nconfiguration gate: it reverts unless fee is 0 and tickSpacing is MAX_TICK_SPACING, so\nonly one oracle pool can exist per pair and liquidity cannot fragment.\nafterInitialize seeds the observation array. beforeAddLiquidity rejects anything that\nis not full range — minUsableTick to maxUsableTick — and updates the pool first;\nbeforeRemoveLiquidity updates too, so observations stay current across liquidity moves.\n\nbeforeSwap reverts on exact-output swaps (only exactIn is supported) and refreshes the\npool. The interesting one is afterSwap, which calls _backrun to compute a corrective\ndelta, then settles it with afterSwapReturnDelta — inverting zeroForOne and the sign of\namountSpecified while keeping the specified currency the same. The fee for that\ncorrection is charged in the unspecified currency, the one the user is buying.\n\nEvery callback is onlyPoolManager. The full-range and zero-fee constraints mean this is\na purpose-built oracle pool, not a hook you bolt onto a trading pool.\n\nRigoBlock has an audit for this one — linked below — which makes it unusual among\nresearch-grade oracle hooks. Read the report, not just the excerpt.\n",
      "source": {
        "repo": "https://github.com/RigoBlock/back-geo-oracle",
        "path": "src/BackGeoOracle.sol",
        "url": "https://github.com/RigoBlock/back-geo-oracle/blob/548e6a643ebf8c1671d2aaa85c979e86bcac979e/src/BackGeoOracle.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// RigoBlock/back-geo-oracle — excerpt. Full file: source.url\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: true,\n        afterInitialize: true,\n        beforeAddLiquidity: true,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: true,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: true,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction _beforeInitialize(address, PoolKey calldata key, uint160)\n    internal view override onlyPoolManager returns (bytes4)\n{\n    // This is to limit the fragmentation of pools using this oracle hook. In other words,\n    // there may only be one pool per pair of tokens that use this hook. The tick spacing is set to the maximum\n    // because we only allow max range liquidity in this pool.\n    if (key.fee != 0 || key.tickSpacing != TickMath.MAX_TICK_SPACING) {\n        revert OnlyOneOraclePoolAllowed();\n    }\n    return BaseHook.beforeInitialize.selector;\n}\n\nfunction _beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata)\n    internal override onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24)\n{\n    // only exactIn swaps are supported\n    if (params.amountSpecified >= 0) {\n        revert NotExactIn();\n    }\n    _updatePool(key);\n    return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);\n}\n\nfunction _afterSwap(address sender, PoolKey calldata key, IPoolManager.SwapParams calldata params,\n    BalanceDelta swapDelta, bytes calldata)\n    internal override onlyPoolManager returns (bytes4, int128)\n{\n    // the unspecified currency is always the one user is buying, so we charge a fee to settle the backrun\n    (BalanceDelta hookDelta, bool isBackrun) = _backrun(key, params, swapDelta);\n    // ... settles the corrective delta via afterSwapReturnDelta\n",
      "categories": [
        "oracles",
        "mev-protection"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "beforeInitialize",
        "afterInitialize",
        "beforeAddLiquidity",
        "beforeRemoveLiquidity",
        "beforeSwap",
        "afterSwap",
        "afterSwapReturnDelta"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "audit_url": "https://github.com/RigoBlock/back-geo-oracle/blob/main/audits/33Audits_audit_back_geo_oracle.pdf",
      "docs": "https://mirror.xyz/rigoblock.eth/yKAD5uYyH0KwfdsOxzt0MyppkFJZzXkxAFeufPGVA2M",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "base-dynamic-fee",
      "name": "BaseDynamicFee",
      "kind": "pattern",
      "tagline": "Set the pool LP fee afterInitialize, then poke updateDynamicLPFee when conditions change.",
      "description": "OpenZeppelin’s BaseDynamicFee (MIT) is the smallest dynamic-fee building block. afterInitialize\nreverts unless key.fee is the dynamic-fee flag, then calls poolManager.updateDynamicLPFee with\n_getFee(key). Inherit it, implement _getFee, and call _poke from a guarded keeper path when you\nwant to refresh. Permissions are afterInitialize only — this does not override a per-swap fee\nin beforeSwap. Experimental library code. Copy the full file including the NotDynamicFee revert.\nListing is not an audit.\n",
      "source": {
        "repo": "https://github.com/OpenZeppelin/uniswap-hooks",
        "path": "src/fee/BaseDynamicFee.sol",
        "url": "https://github.com/OpenZeppelin/uniswap-hooks/blob/master/src/fee/BaseDynamicFee.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Uniswap Hooks — excerpt. Full file: source.url\nfunction _getFee(PoolKey calldata key) internal virtual returns (uint24);\n\nfunction _afterInitialize(address, PoolKey calldata key, uint160, int24)\n    internal\n    virtual\n    override\n    returns (bytes4)\n{\n    if (!key.fee.isDynamicFee()) revert NotDynamicFee();\n    poolManager.updateDynamicLPFee(key, _getFee(key));\n    return this.afterInitialize.selector;\n}\n\nfunction _poke(PoolKey calldata key) internal virtual {\n    poolManager.updateDynamicLPFee(key, _getFee(key));\n}\n\nfunction getHookPermissions() public pure virtual override returns (Hooks.Permissions memory permissions) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: false,\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "dynamic-fees"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize"
      ],
      "properties": [
        "dynamic-fee",
        "vanilla-swap"
      ],
      "docs": "https://github.com/OpenZeppelin/uniswap-hooks",
      "website": "https://v4hooks.com/learn/openzeppelin-hooks",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "base-hook",
      "name": "BaseHook",
      "kind": "pattern",
      "tagline": "Inherit this, declare permissions, gate callbacks with onlyPoolManager.",
      "description": "Start every custom Uniswap v4 hook from OpenZeppelin’s BaseHook (MIT). PoolManager\nonly calls functions whose bits are set in the hook address. BaseHook stores the\nPoolManager, reverts NotPoolManager unless msg.sender is that singleton, and routes\neach IHooks entrypoint to an internal _callback that reverts HookNotImplemented until\nyou override it. getHookPermissions must match those overrides or constructor\nvalidation fails. Flip the bits you need, implement the matching _beforeSwap (or other)\nfunction, and keep return-delta flags in sync. Listing is not an audit. The library is\nexperimental — still run the secure-hooks checklist before you ship.\n",
      "source": {
        "repo": "https://github.com/OpenZeppelin/uniswap-hooks",
        "path": "src/base/BaseHook.sol",
        "url": "https://github.com/OpenZeppelin/uniswap-hooks/blob/master/src/base/BaseHook.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Uniswap Hooks — excerpt. Full file: source.url\nabstract contract BaseHook is IHooks {\n    IPoolManager public immutable poolManager;\n    error HookNotImplemented();\n    error NotPoolManager();\n\n    constructor(IPoolManager _poolManager) {\n        poolManager = _poolManager;\n        _validateHookAddress(this);\n    }\n\n    modifier onlyPoolManager() {\n        if (msg.sender != address(poolManager)) revert NotPoolManager();\n        _;\n    }\n\n    function getHookPermissions() public pure virtual returns (Hooks.Permissions memory permissions);\n\n    function beforeSwap(address sender, PoolKey calldata key, SwapParams calldata params, bytes calldata hookData)\n        external\n        onlyPoolManager\n        returns (bytes4, BeforeSwapDelta, uint24)\n    {\n        return _beforeSwap(sender, key, params, hookData);\n    }\n\n    function _beforeSwap(address, PoolKey calldata, SwapParams calldata, bytes calldata)\n        internal\n        virtual\n        returns (bytes4, BeforeSwapDelta, uint24)\n    {\n        revert HookNotImplemented();\n    }\n}\n",
      "categories": [
        "wrappers"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "beforeSwap"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://github.com/OpenZeppelin/uniswap-hooks",
      "website": "https://v4hooks.com/learn/openzeppelin-hooks",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "base-v4-hook",
      "name": "BaseV4Hook",
      "kind": "pattern",
      "tagline": "Abstract base that re-implements v4 pool internals inside the hook for custom curves.",
      "description": "Most custom-curve hooks bolt a NoOp beforeSwap onto a normal hook and manage reserves\nby hand. BaseV4Hook takes the other route: it inherits the PoolManager's own machinery —\nProtocolFees, NoDelegateCall, ERC6909Claims, Extsload and Exttload — and runs v4-like\nliquidity logic inside the hook, using Pool, Position, CurrencyDelta and CurrencyReserves\ndirectly. If you want a pool that behaves like v4 but on a different curve, this is the\nscaffold that gives you the accounting for free.\n\nIt is abstract on purpose. The external beforeSwap is implemented, gated by\nonlyByPoolManager, and its job is to repackage IPoolManager.SwapParams into a\nPool.SwapParams struct — adding tickSpacing from the key and an lpFeeOverride of 0 —\nbefore delegating to an internal _beforeSwap that you must implement. Your curve goes\nthere and returns the BeforeSwapDelta.\n\nPermissions are the three a custom curve needs, and the file annotates each one:\nbeforeAddLiquidity because liquidity must be deposited into the hook directly rather\nthan the PoolManager, beforeSwap as the custom curve handler, and beforeSwapReturnDelta\nto skip the PoolManager swap. Nothing else is claimed.\n\nRead the NatSpec on beforeSwap before you use it. It states the delta sign convention —\npositive means the hook is owed or took currency, negative means it owes or sent — and\nthe three conditions an lp fee override must satisfy: dynamic fee pool, the 23rd bit\n(0x400000) set, and a value at or below 1,000,000. Those are the rules people get wrong.\n\nUnaudited. Inheriting core internals means you inherit their invariants too.\n",
      "source": {
        "repo": "https://github.com/cairoeth/base-v4-hook",
        "path": "src/BaseV4Hook.sol",
        "url": "https://github.com/cairoeth/base-v4-hook/blob/a784e215a4fc1ff1d0f3e4697b764ab035c121b8/src/BaseV4Hook.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// cairoeth/base-v4-hook — excerpt. Full file: source.url\nabstract contract BaseV4Hook is BaseHook, ProtocolFees, NoDelegateCall, ERC6909Claims, Extsload, Exttload {\n    using PoolIdLibrary for PoolKey;\n    using Pool for *;\n    using Position for mapping(bytes32 => Position.Info);\n    using CurrencyDelta for Currency;\n    using CurrencyReserves for Currency;\n    using LPFeeLibrary for uint24;\n\n/// @return BeforeSwapDelta The hook's delta in specified and unspecified currencies.\n///         Positive: the hook is owed/took currency, negative: the hook owes/sent currency\n/// @return uint24 Optionally override the lp fee, only used if three conditions are met:\n///         1. the Pool has a dynamic fee, 2. the value's 2nd highest bit is set\n///         (23rd bit, 0x400000), and 3. the value is less than or equal to the maximum fee (1 million)\nfunction beforeSwap(address sender, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata)\n    external virtual override onlyByPoolManager returns (bytes4, BeforeSwapDelta, uint24)\n{\n    return _beforeSwap(\n        sender,\n        key,\n        Pool.SwapParams({\n            tickSpacing: key.tickSpacing,\n            zeroForOne: params.zeroForOne,\n            amountSpecified: params.amountSpecified,\n            sqrtPriceLimitX96: params.sqrtPriceLimitX96,\n            lpFeeOverride: 0\n        })\n    );\n}\n\n/// @dev Execute swap with custom logic — implement this in your subclass\nfunction _beforeSwap(address sender, PoolKey calldata key, Pool.SwapParams memory params)\n    internal virtual returns (bytes4, BeforeSwapDelta, uint24);\n\nfunction getHookPermissions() public pure virtual override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: false,\n        beforeAddLiquidity: true, // -- liquidity must be deposited here directly -- //\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true, // -- custom curve handler -- //\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: true, // -- enable custom curve by skipping poolmanager swap -- //\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "wrappers",
        "lp-management"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "beforeAddLiquidity",
        "beforeSwap",
        "beforeSwapReturnDelta"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "buyback",
      "name": "Buyback",
      "kind": "pattern",
      "tagline": "Treasury-funded market buy when pool tick is at or below thresholdTick.",
      "description": "First-party rewrite inspired by atj3097/buyback-hook. Native-pair pool; fund the hook with ETH.\nsetThreshold stores a tick floor; buyback() unlocks a zeroForOne swap when current tick <= threshold.\nFixed ungated-callback issues from the legacy repo. Experimental. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/Buyback.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/Buyback.sol"
      },
      "solidity": "function getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: false,\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "launchpads"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://github.com/atj3097/buyback-hook",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "clanker",
      "name": "Clanker",
      "kind": "product",
      "tagline": "Dynamic-fee launch hook: beforeSwap sets fee, claims, runs an MEV module, then takes protocol delta.",
      "description": "ClankerHook is MIT production code. getHookPermissions enables initialize, add-liquidity\n(blocked while the MEV module is hot), beforeSwap/afterSwap, and both swap return-delta\nbits so the hook can mint protocol fee into its PoolManager account. _beforeSwap sets the\nLP fee, claims, optionally runs mevModule.beforeSwap, then uses BeforeSwapDelta on paired\ntoken. Do not reimplement Clanker to “launch a coin” — use their factory. Excerpt is how\na launchpad hook takes fee-on-delta. Full file at the permalink. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/clanker-devco/v4-contracts",
        "path": "src/hooks/ClankerHook.sol",
        "url": "https://github.com/clanker-devco/v4-contracts/blob/main/src/hooks/ClankerHook.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: true,\n        afterInitialize: false,\n        beforeAddLiquidity: true,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: true,\n        afterSwapReturnDelta: true,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction _beforeSwap(\n    address,\n    PoolKey calldata poolKey,\n    IPoolManager.SwapParams calldata swapParams,\n    bytes calldata mevModuleSwapData\n) internal virtual override returns (bytes4, BeforeSwapDelta delta, uint24) {\n    _setFee(poolKey, swapParams);\n    _hookFeeClaim(poolKey);\n    _lpLockerFeeClaim(poolKey);\n    _runMevModule(poolKey, swapParams, mevModuleSwapData);\n    // mint protocol fee as BeforeSwapDelta when swapping for/against the clanker\n    return (BaseHook.beforeSwap.selector, delta, 0);\n}\n",
      "categories": [
        "launchpads",
        "dynamic-fees",
        "mev-protection"
      ],
      "chains": [
        "base"
      ],
      "license": "MIT",
      "flags": [
        "beforeInitialize",
        "beforeAddLiquidity",
        "beforeSwap",
        "afterSwap",
        "beforeSwapReturnDelta",
        "afterSwapReturnDelta"
      ],
      "properties": [
        "dynamic-fee",
        "vanilla-swap"
      ],
      "website": "https://www.clanker.world/",
      "docs": "https://clanker.gitbook.io/documentation/references/core-contracts/v4",
      "status": "production",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "constant-sum",
      "name": "Constant sum",
      "kind": "pattern",
      "tagline": "Replace xy=k with x+y=k so every swap fills exactly 1:1, using a NoOp beforeSwap.",
      "description": "The cleanest example of a v4 custom curve. The pool's own math is never used — the hook\nintercepts the swap in beforeSwap, returns a BeforeSwapDelta that fully accounts for\nboth sides, and the PoolManager skips its concentrated-liquidity swap entirely. That is\nwhat beforeSwapReturnDelta buys you, and this is the smallest readable demonstration\nof it.\n\nThe swap body is short. It picks input and output currency from zeroForOne, treats a\nnegative amountSpecified as exact-input, and — because the curve is x+y=k — uses the\nsame amount for both sides. It then calls poolManager.mint to take the input currency\nas ERC-6909 into the hook's reserves and poolManager.burn to pay the output currency\nout of them. The returned delta is the mirror of that: for exact input the specified\ndelta is positive and the unspecified negative, and for exact output the signs invert.\nThe comments in the file spell the sign convention out line by line, which is the part\nworth reading twice — getting it backwards is the classic custom-curve bug.\n\nbeforeAddLiquidity reverts with \"No v4 Liquidity allowed\". That is deliberate, not an\noversight: reserves live in the hook as ERC-6909 claims, so ordinary v4 liquidity would\nbe stranded and unusable by the curve. Any custom-curve hook has to make the same\nchoice.\n\nNote the file is named Counter.sol — leftover from the v4-template scaffold — even\nthough the contract does constant-sum swaps. Unaudited example code.\n",
      "source": {
        "repo": "https://github.com/saucepoint/v4-constant-sum",
        "path": "src/Counter.sol",
        "url": "https://github.com/saucepoint/v4-constant-sum/blob/cce8f6cfb614e19144b1858348b23f56c241f50d/src/Counter.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// saucepoint/v4-constant-sum — excerpt. Full file: source.url\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: false,\n        beforeAddLiquidity: true,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: true,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction _beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata)\n    internal override returns (bytes4, BeforeSwapDelta, uint24)\n{\n    // determine inbound/outbound token based on 0->1 or 1->0 swap\n    (Currency inputCurrency, Currency outputCurrency) =\n        params.zeroForOne ? (key.currency0, key.currency1) : (key.currency1, key.currency0);\n\n    bool isExactInput = params.amountSpecified < 0;\n\n    // tokens are always swapped 1:1, so use amountSpecified to determine both input and output amounts\n    uint256 amount = isExactInput ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n\n    // take the input token, as ERC6909, from the PoolManager\n    poolManager.mint(address(this), inputCurrency.toId(), amount);\n\n    // pay the output token, as ERC6909, to the PoolManager\n    poolManager.burn(address(this), outputCurrency.toId(), amount);\n\n    int128 tokenAmount = amount.toInt128();\n    // exact input:  specifiedDelta positive, unspecifiedDelta negative\n    // exact output: specifiedDelta negative, unspecifiedDelta positive\n    BeforeSwapDelta returnDelta =\n        isExactInput ? toBeforeSwapDelta(tokenAmount, -tokenAmount) : toBeforeSwapDelta(-tokenAmount, tokenAmount);\n\n    return (BaseHook.beforeSwap.selector, returnDelta, 0);\n}\n\n/// @notice No liquidity will be managed by v4 PoolManager\nfunction _beforeAddLiquidity(address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata)\n    internal pure override returns (bytes4)\n{\n    revert(\"No v4 Liquidity allowed\");\n}\n",
      "categories": [
        "wrappers"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "beforeAddLiquidity",
        "beforeSwap",
        "beforeSwapReturnDelta"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "doppler",
      "name": "Doppler",
      "kind": "product",
      "tagline": "Whetstone bonding-curve hook: rebalance slugs before swap, migrate when proceeds cap hit.",
      "description": "Doppler is Whetstone Research production code (BUSL-1.1). The hook runs a time-epoch bonding\ncurve: beforeSwap rebalances concentrated liquidity slugs when a new epoch starts, tracks\ntotalTokensSold and totalProceeds, and blocks swaps before startTime or after migration.\nbeforeAddLiquidity reverts (CannotAddLiquidity); LPs are the hook's slug positions, not external\nadds. Study for launchpad / LBP-style v4 hooks. Do not deploy without a Whetstone license.\nExcerpt shows permissions and epoch gate in beforeSwap. Full file at the permalink. Not an audit.\n",
      "source": {
        "repo": "https://github.com/whetstoneresearch/doppler",
        "path": "src/initializers/Doppler.sol",
        "url": "https://github.com/whetstoneresearch/doppler/blob/main/src/initializers/Doppler.sol"
      },
      "solidity": "// SPDX-License-Identifier: BUSL-1.1\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: true,\n        afterInitialize: true,\n        beforeAddLiquidity: true,\n        beforeRemoveLiquidity: false,\n        afterAddLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: true,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction _beforeSwap(...) internal override returns (bytes4, BeforeSwapDelta, uint24) {\n    if (block.timestamp < startingTime) revert CannotSwapBeforeStartTime();\n    // rebalance slug liquidity when epoch advances, then pool executes swap\n}\n",
      "categories": [
        "launchpads",
        "lp-management"
      ],
      "chains": [
        "base"
      ],
      "license": "BUSL-1.1",
      "flags": [
        "beforeInitialize",
        "afterInitialize",
        "beforeAddLiquidity",
        "beforeSwap",
        "afterSwap",
        "beforeDonate"
      ],
      "properties": [
        "dynamic-fee",
        "vanilla-swap"
      ],
      "website": "https://whetstone.cc/",
      "status": "production",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "dual-pool",
      "name": "DualPool",
      "kind": "product",
      "tagline": "Uniswap ALF JIT hook: deploy multi-range LP around each swap, vault via ERC4626.",
      "description": "DualPoolHook is Uniswap Labs production code from v4-hooks-public. On each swap it runs a JIT\ncycle: beforeSwap locks LP ops, redeploys liquidity across weighted tick buckets, then afterSwap\ntears down positions and re-deposits surplus to ERC4626 vaults. Pool fee is static from PoolKey;\nthe hook does not override fees. Study this for JIT + vault rehypothecation, not as a drop-in\nALF deployment. Full file at the permalink. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/Uniswap/v4-hooks-public",
        "path": "src/alf/DualPoolHook.sol",
        "url": "https://github.com/Uniswap/v4-hooks-public/blob/main/src/alf/DualPoolHook.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: true,\n        afterInitialize: false,\n        beforeAddLiquidity: true,\n        beforeRemoveLiquidity: true,\n        afterAddLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\n// beforeSwap: set JIT lock → deploy bucket LP → [swap] → afterSwap: remove LP, settle, vault\n",
      "categories": [
        "lp-management",
        "wrappers"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "beforeInitialize",
        "beforeAddLiquidity",
        "beforeRemoveLiquidity",
        "beforeSwap",
        "afterSwap"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://github.com/Uniswap/v4-hooks-public",
      "status": "production",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "eulerswap",
      "name": "EulerSwap",
      "kind": "product",
      "tagline": "Custom curve via beforeSwapReturnDelta: take input to Euler vaults, pay output, skip CLAMM math.",
      "description": "Euler’s UniswapHook (BUSL-1.1) quotes amountIn/amountOut off the v3 tick curve, returns a\nBeforeSwapDelta so PoolManager’s concentrated liquidity is unused, takes the input to vaults\nand settles the output from vaults. beforeInitialize / beforeAddLiquidity / beforeDonate stay\nenabled so BaseHook’s default revert blocks stray inits, CLAMM LP, and donations. Study this\nfor “hook is the AMM.” Do not paste BUSL into a new MIT repo without reading the license.\nFull UniswapHook.sol at the permalink. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/euler-xyz/euler-swap",
        "path": "src/UniswapHook.sol",
        "url": "https://github.com/euler-xyz/euler-swap/blob/master/src/UniswapHook.sol"
      },
      "solidity": "// SPDX-License-Identifier: BUSL-1.1\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: true,\n        afterInitialize: false,\n        beforeAddLiquidity: true,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: false,\n        beforeDonate: true,\n        afterDonate: false,\n        beforeSwapReturnDelta: true,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\n// take input to Euler vaults, pay output, offset the CLAMM swap\nfunction _beforeSwap(address sender, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata)\n    internal\n    override\n    nonReentrant\n    returns (bytes4, BeforeSwapDelta, uint24)\n{\n    // QuoteLib.computeQuote → poolManager.take(input) → vaults → poolManager.settle(output)\n    BeforeSwapDelta returnDelta = isExactInput\n        ? toBeforeSwapDelta(amountIn.toInt128(), -(amountOut.toInt128()))\n        : toBeforeSwapDelta(-(amountOut.toInt128()), amountIn.toInt128());\n    return (BaseHook.beforeSwap.selector, returnDelta, 0);\n}\n",
      "categories": [
        "lending",
        "lp-management"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "BUSL-1.1",
      "flags": [
        "beforeInitialize",
        "beforeAddLiquidity",
        "beforeSwap",
        "beforeDonate",
        "beforeSwapReturnDelta"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "website": "https://app.euler.finance/swap",
      "status": "production",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "fee-router",
      "name": "FeeRouter",
      "kind": "pattern",
      "tagline": "afterSwap skims protocolBps of output to an immutable treasury address.",
      "description": "First-party teaching hook for fee collect-and-route on every swap. afterSwap takes protocolBps of\nthe swap output token via take + ERC20 transfer to treasury. Similar shape to referral-fee.yml but\nfixed recipient instead of hookData referrer. Experimental. Forge tests in test/FeeRouter.t.sol.\nListing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/FeeRouter.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/FeeRouter.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\nfunction _afterSwap(...) internal override returns (bytes4, int128) {\n    uint256 pay = (uint256(int256(out)) * protocolBps) / 10_000;\n    outCur.take(poolManager, address(this), pay, false);\n    IERC20(Currency.unwrap(outCur)).transfer(treasury, pay);\n    return (this.afterSwap.selector, pay.toInt128());\n}\n",
      "categories": [
        "creator-economy",
        "dynamic-fees"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterSwap",
        "afterSwapReturnDelta"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "flaunch",
      "name": "Flaunch",
      "kind": "product",
      "tagline": "PositionManager hook: beforeSwap runs the internal swap pool; afterSwap distributes creator fees.",
      "description": "Flaunch’s PositionManager is a Uniswap v4 hook (MIT). Permissions: block external\ninitialize, InternalSwapPool on beforeSwap + beforeSwapReturnDelta, FeeDistributor and\nBidWall on afterSwap + afterSwapReturnDelta, liquidity/donate event tracking. You do not\ncopy this to “build Flaunch” — you read how a launchpad declares a wide permission mask\nand splits work across callbacks. Full PositionManager.sol is large; start at\ngetHookPermissions then follow the comments in square brackets. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/flayerlabs/flaunchgg-contracts",
        "path": "src/contracts/PositionManager.sol",
        "url": "https://github.com/flayerlabs/flaunchgg-contracts/blob/main/src/contracts/PositionManager.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: true, // Prevent initialize\n        afterInitialize: false,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: true, // [EventTracking]\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: true, // [EventTracking]\n        beforeSwap: true, // [InternalSwapPool]\n        afterSwap: true, // [FeeDistributor], [InternalSwapPool], [BidWall], [EventTracking]\n        beforeDonate: false,\n        afterDonate: true, // [EventTracking]\n        beforeSwapReturnDelta: true, // [InternalSwapPool]\n        afterSwapReturnDelta: true, // [FeeDistributor]\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "launchpads",
        "creator-economy"
      ],
      "chains": [
        "base"
      ],
      "license": "MIT",
      "flags": [
        "beforeInitialize",
        "afterAddLiquidity",
        "afterRemoveLiquidity",
        "beforeSwap",
        "afterSwap",
        "afterDonate",
        "beforeSwapReturnDelta",
        "afterSwapReturnDelta"
      ],
      "properties": [
        "custom-swap-data"
      ],
      "website": "https://flaunch.gg/",
      "status": "production",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "full-range",
      "name": "Full range",
      "kind": "pattern",
      "tagline": "Force v2-style full-range LP: only the hook can modify liquidity, users mint a pool ERC20 instead.",
      "description": "FullRange’s beforeInitialize requires tickSpacing 60 and deploys a per-pool ERC20 receipt.\nbeforeAddLiquidity reverts unless sender is the hook, so LPs must call addLiquidity on the\nhook (min to max tick). beforeSwap marks that fees have accrued. This is how you wrap a v4\npool as constant-product inventory. UNLICENSED Uniswap example — take the full file, including\nrebalance/donate dust. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/Uniswap/v4-periphery",
        "path": "contracts/hooks/examples/FullRange.sol",
        "url": "https://github.com/Uniswap/v4-periphery/blob/example-contracts/contracts/hooks/examples/FullRange.sol"
      },
      "solidity": "// SPDX-License-Identifier: UNLICENSED\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: true,\n        afterInitialize: false,\n        beforeAddLiquidity: true,\n        beforeRemoveLiquidity: false,\n        afterAddLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction beforeAddLiquidity(\n    address sender,\n    PoolKey calldata,\n    IPoolManager.ModifyLiquidityParams calldata,\n    bytes calldata\n) external view override returns (bytes4) {\n    if (sender != address(this)) revert SenderMustBeHook();\n    return FullRange.beforeAddLiquidity.selector;\n}\n",
      "categories": [
        "lp-management",
        "wrappers"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "UNLICENSED",
      "flags": [
        "beforeInitialize",
        "beforeAddLiquidity",
        "beforeSwap"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "geomean-oracle",
      "name": "Geomean oracle",
      "kind": "pattern",
      "tagline": "Write a geometric-mean observation before any action that can move price or liquidity.",
      "description": "The Uniswap GeomeanOracle example allows one oracle pool per pair (fee 0, max tick spacing).\nafterInitialize seeds the observation cardinality. beforeAddLiquidity forces full-range\npositions and writes a sample. beforeRemoveLiquidity always reverts so liquidity stays\nlocked. beforeSwap updates the oracle then lets the swap proceed. Read observe() offchain\nor from another contract. UNLICENSED example — copy the full file including the Oracle\nlibrary. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/Uniswap/v4-periphery",
        "path": "contracts/hooks/examples/GeomeanOracle.sol",
        "url": "https://github.com/Uniswap/v4-periphery/blob/example-contracts/contracts/hooks/examples/GeomeanOracle.sol"
      },
      "solidity": "// SPDX-License-Identifier: UNLICENSED\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: true,\n        afterInitialize: true,\n        beforeAddLiquidity: true,\n        beforeRemoveLiquidity: true,\n        afterAddLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata)\n    external\n    override\n    onlyByManager\n    returns (bytes4, BeforeSwapDelta, uint24)\n{\n    _updatePool(key);\n    return (GeomeanOracle.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);\n}\n\nfunction beforeRemoveLiquidity(address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata)\n    external\n    view\n    override\n    onlyByManager\n    returns (bytes4)\n{\n    revert OraclePoolMustLockLiquidity();\n}\n",
      "categories": [
        "oracles"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "UNLICENSED",
      "flags": [
        "beforeInitialize",
        "afterInitialize",
        "beforeAddLiquidity",
        "beforeRemoveLiquidity",
        "beforeSwap"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://docs.uniswap.org/contracts/v4/concepts/hooks",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "iceberg-order",
      "name": "Iceberg",
      "kind": "pattern",
      "tagline": "Encrypted limit orders — size and direction stay hidden until the tick is crossed.",
      "description": "Iceberg is the familiar v4 tick-crossing limit order, with the order book held in\nFhenix CoFHE ciphertext instead of plaintext storage. Permissions are afterInitialize,\nbeforeSwap and afterSwap; every callback is gated by onlyByManager.\n\nafterInitialize records the starting lower tick for the pool. afterSwap recomputes the\nticks crossed since the last swap and walks them, filling resting orders in the\ndirection opposite the swap — the file notes that a zeroForOne swap makes the pool gain\ntoken0, so limit fills invert the swap direction. That part is the standard\nLimitOrderHook shape.\n\nThe FHE part lives in beforeSwap. Order amounts are euint128 handles rather than\nuint128, so a fill cannot settle until the coprocessor has decrypted the amount.\nbeforeSwap drains a per-pool decryption Queue, peeking each pending handle and\nexecuting whatever has become available before the swap proceeds. Balances are held as\nIFHERC20, an encrypted ERC-20, so a resting order's size is never readable on-chain.\n\nTwo consequences worth understanding before copying it. Fills are asynchronous: an\norder becomes claimable a decryption round after the tick is crossed, not in the same\ntransaction. And beforeSwap does unbounded work proportional to queue depth, so a\ncongested pool pays for other people's pending decryptions — the same\ntick-walk gas exposure the plaintext limit order hooks have, plus the queue.\n\nDepends on Fhenix CoFHE, so it only runs where that coprocessor is deployed.\nExperimental, unaudited.\n",
      "source": {
        "repo": "https://github.com/marronjo/iceberg-cofhe",
        "path": "src/Iceberg.sol",
        "url": "https://github.com/marronjo/iceberg-cofhe/blob/3dab6b2e1f12897d3b8c8edd93d369c9c46dfcd2/src/Iceberg.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// marronjo/iceberg-cofhe — excerpt. Full file: source.url\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterAddLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction _beforeSwap(\n    address,\n    PoolKey calldata key,\n    SwapParams calldata,\n    bytes calldata\n) internal override onlyByManager returns (bytes4, BeforeSwapDelta, uint24) {\n\n    Queue queue = getPoolQueue(key);\n\n    //if nothing in decryption queue, continue\n    //otherwise try execute trades\n    while(!queue.isEmpty()){\n\n        euint128 liquidityHandle = queue.peek();\n        // ... drains handles that CoFHE has finished decrypting\n\nfunction _afterSwap(\n    address,\n    PoolKey calldata key,\n    SwapParams calldata params,\n    BalanceDelta,\n    bytes calldata\n) internal override onlyByManager returns (bytes4, int128) {\n    (int24 tickLower, int24 lower, int24 upper) = _getCrossedTicks(key.toId(), key.tickSpacing);\n    if (lower > upper) return (BaseHook.afterSwap.selector, 0);\n\n    // note that a zeroForOne swap means that the pool is actually gaining token0, so limit\n    // order fills are the opposite of swap fills, hence the inversion below\n    bool zeroForOne = !params.zeroForOne;\n\n    for (; lower <= upper; lower += key.tickSpacing) {\n        // ... fills resting encrypted orders at each crossed tick\n",
      "categories": [
        "limit-orders"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "beforeSwap",
        "afterSwap"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "website": "https://www.fhenix.io/",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "jit-vault",
      "name": "JitVault",
      "kind": "pattern",
      "tagline": "JIT lock blocks LP ops during swap; optional output skim accrues to vault balances.",
      "description": "First-party teaching hook inspired by Uniswap DualPoolHook. beforeSwap sets jitActive; beforeAddLiquidity\nand beforeRemoveLiquidity revert while active. afterSwap clears the lock and optionally skims skimBps\nof swap output into per-pool vault0/vault1 balances held by the hook. No ERC4626 or bucket deployment —\nstudy the lifecycle gate only. Experimental. Forge tests in test/JitVault.t.sol. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/JitVault.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/JitVault.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\nfunction _beforeSwap(...) internal override returns (bytes4, BeforeSwapDelta, uint24) {\n    jitActive[key.toId()] = true;\n    return (this.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);\n}\n\nfunction _afterSwap(...) internal override returns (bytes4, int128) {\n    jitActive[id] = false;\n    // skim skimBps of output into vault0/vault1\n}\n",
      "categories": [
        "lp-management",
        "mev-protection"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "beforeAddLiquidity",
        "beforeRemoveLiquidity",
        "beforeSwap",
        "afterSwap",
        "afterSwapReturnDelta"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "limit-order",
      "name": "Limit order",
      "kind": "pattern",
      "tagline": "Rest liquidity at a tick and fill it in afterSwap when the pool trades through that price.",
      "description": "Uniswap’s LimitOrder example records the last tick on afterInitialize. On afterSwap it walks\nticks the swap just crossed and fills any epoch resting at those ticks by removing that\none-tick-wide liquidity. Users call place() to add an order; withdraw() after Fill. Flags\nmust be afterInitialize + afterSwap. This is the example-contracts file, UNLICENSED, not a\nproduction CLOB. Production forks add cancel, partial fill, and gas accounting. Copy the\nfull Solidity from the permalink. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/Uniswap/v4-periphery",
        "path": "contracts/hooks/examples/LimitOrder.sol",
        "url": "https://github.com/Uniswap/v4-periphery/blob/example-contracts/contracts/hooks/examples/LimitOrder.sol"
      },
      "solidity": "// SPDX-License-Identifier: UNLICENSED\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterAddLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: false,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction afterSwap(\n    address,\n    PoolKey calldata key,\n    IPoolManager.SwapParams calldata params,\n    BalanceDelta,\n    bytes calldata\n) external override onlyByManager returns (bytes4, int128) {\n    (int24 tickLower, int24 lower, int24 upper) = _getCrossedTicks(key.toId(), key.tickSpacing);\n    if (lower > upper) return (LimitOrder.afterSwap.selector, 0);\n    bool zeroForOne = !params.zeroForOne;\n    for (; lower <= upper; lower += key.tickSpacing) {\n        _fillEpoch(key, lower, zeroForOne);\n    }\n    setTickLowerLast(key.toId(), tickLower);\n    return (LimitOrder.afterSwap.selector, 0);\n}\n",
      "categories": [
        "limit-orders"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "UNLICENSED",
      "flags": [
        "afterInitialize",
        "afterSwap"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://docs.uniswap.org/contracts/v4/concepts/hooks",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "limit-order-hook",
      "name": "LimitOrderHook",
      "kind": "pattern",
      "tagline": "Place out-of-range liquidity, fill on afterSwap tick cross, cancel or withdraw via unlock.",
      "description": "OpenZeppelin’s LimitOrderHook (MIT) is the library-shaped fill-on-cross. afterInitialize stores\nlast tick; afterSwap walks crossed ticks and _fillOrder. Users call placeOrder, cancelOrder, and\nwithdraw through unlockCallback. Fees accrue per liquidity unit so late joiners do not steal\nearlier fees. Compare with Uniswap’s UNLICENSED LimitOrder example — this file adds cancel and\nfee accounting. Experimental library code. Copy the full file from the permalink. Listing is\nnot an audit.\n",
      "source": {
        "repo": "https://github.com/OpenZeppelin/uniswap-hooks",
        "path": "src/general/LimitOrderHook.sol",
        "url": "https://github.com/OpenZeppelin/uniswap-hooks/blob/master/src/general/LimitOrderHook.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Uniswap Hooks — excerpt. Full file: source.url\nfunction getHookPermissions() public pure virtual override returns (Hooks.Permissions memory permissions) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterAddLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: false,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction _afterSwap(address, PoolKey calldata key, SwapParams calldata params, BalanceDelta, bytes calldata)\n    internal\n    virtual\n    override\n    returns (bytes4, int128)\n{\n    (int24 tickLower, int24 lower, int24 upper) = _getCrossedTicks(key.toId(), key.tickSpacing);\n    if (lower > upper) return (this.afterSwap.selector, 0);\n    _tickLowerLasts[key.toId()] = tickLower;\n    bool zeroForOne = !params.zeroForOne;\n    for (; lower <= upper; lower += key.tickSpacing) {\n        _fillOrder(key, lower, zeroForOne);\n    }\n    return (this.afterSwap.selector, 0);\n}\n",
      "categories": [
        "limit-orders"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "afterSwap"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://github.com/OpenZeppelin/uniswap-hooks",
      "website": "https://v4hooks.com/learn/openzeppelin-hooks",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "liquidity-bootstrapping",
      "name": "Liquidity bootstrapping",
      "kind": "pattern",
      "tagline": "Linear-decay token sale via beforeSwapReturnDelta (LBP-style custom curve).",
      "description": "First-party rewrite inspired by kadenzipfel uni-lbp (AGPL). configureSale sets start/end time\nand price bounds. beforeSwapReturnDelta mints/burns ERC-6909 for ETH→token sales at currentPrice.\nbeforeAddLiquidity reverts — no v4 LP during the sale phase. Teaching cut of an LBP launch curve.\nListing is not an audit. Forge tests under test/LiquidityBootstrapping.t.sol.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/LiquidityBootstrapping.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/LiquidityBootstrapping.sol"
      },
      "solidity": "function getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: false,\n        beforeAddLiquidity: true,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: true,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "launchpads"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "beforeAddLiquidity",
        "beforeSwap",
        "beforeSwapReturnDelta"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://github.com/kadenzipfel/uni-lbp",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "liquidity-lock",
      "name": "Liquidity lock",
      "kind": "pattern",
      "tagline": "afterAddLiquidity sets unlock time; beforeRemoveLiquidity reverts until lock expires.",
      "description": "First-party rewrite inspired by Hookathon C1 LiquidityLock / timelock LP. lockDuration is added\nto block.timestamp on each net add keyed by the liquidity sender (router in tests). Removes\nrevert with StillLocked until unlock. Launchpad LP commitment pattern. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/LiquidityLock.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/LiquidityLock.sol"
      },
      "solidity": "function getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: false,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: true,\n        beforeRemoveLiquidity: true,\n        afterRemoveLiquidity: false,\n        beforeSwap: false,\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "launchpads",
        "lp-management"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterAddLiquidity",
        "beforeRemoveLiquidity"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "liquidity-penalty",
      "name": "LiquidityPenaltyHook",
      "kind": "pattern",
      "tagline": "Withhold JIT LP fees in afterAddLiquidity and donate a linear penalty on early remove.",
      "description": "OpenZeppelin’s LiquidityPenaltyHook (MIT) deters just-in-time liquidity. afterAddLiquidity\nrecords lastAddedLiquidityBlock and, if the position was topped up inside blockNumberOffset,\ntakes feeDelta into the hook via afterAddLiquidityReturnDelta. afterRemoveLiquidity settles\nwithheld fees, then if the offset has not elapsed, donates a linear remaining-blocks penalty\nto in-range LPs and returns the rest through afterRemoveLiquidityReturnDelta. Long-term LPs\nwho add continuously must wait the offset before collecting fees. Experimental library code;\nlow-liquidity pools can still be gamed. Copy the full file. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/OpenZeppelin/uniswap-hooks",
        "path": "src/general/LiquidityPenaltyHook.sol",
        "url": "https://github.com/OpenZeppelin/uniswap-hooks/blob/master/src/general/LiquidityPenaltyHook.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Uniswap Hooks — excerpt. Full file: source.url\nfunction getHookPermissions() public pure virtual override returns (Hooks.Permissions memory permissions) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: false,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: true,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: true,\n        beforeSwap: false,\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: true,\n        afterRemoveLiquidityReturnDelta: true\n    });\n}\n\nfunction _afterAddLiquidity(\n    address sender,\n    PoolKey calldata key,\n    ModifyLiquidityParams calldata params,\n    BalanceDelta, /* delta */\n    BalanceDelta feeDelta,\n    bytes calldata\n) internal virtual override returns (bytes4, BalanceDelta) {\n    PoolId poolId = key.toId();\n    bytes32 positionKey = Position.calculatePositionKey(sender, params.tickLower, params.tickUpper, params.salt);\n    if (_getBlockNumber() - getLastAddedLiquidityBlock(poolId, positionKey) < blockNumberOffset) {\n        _updateLastAddedLiquidityBlock(poolId, positionKey);\n        _takeFeesToHook(key, positionKey, feeDelta);\n        return (this.afterAddLiquidity.selector, feeDelta);\n    }\n    _updateLastAddedLiquidityBlock(poolId, positionKey);\n    return (this.afterAddLiquidity.selector, BalanceDeltaLibrary.ZERO_DELTA);\n}\n",
      "categories": [
        "mev-protection",
        "lp-management"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterAddLiquidity",
        "afterRemoveLiquidity",
        "afterAddLiquidityReturnDelta",
        "afterRemoveLiquidityReturnDelta"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://github.com/OpenZeppelin/uniswap-hooks",
      "website": "https://v4hooks.com/learn/openzeppelin-hooks",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "lp-rewards",
      "name": "LpRewards",
      "kind": "pattern",
      "tagline": "Pull buy-side token rewards, then flush() donates pending balance to in-range LPs.",
      "description": "First-party teaching hook inspired by Spark LP Rewards. Spark routes an ETH-side buy cut; this\nversion takes rewardBps of token output on exact-input buys (same afterSwapReturnDelta path as\nAutoBurn) into pending[poolId], then flush() unlocks and calls poolManager.donate + settle so\nfeeGrowthGlobal rises for LPs. Deferred donate avoids NoLiquidityToReceiveFees when a large buy\nwalks price out of range mid-callback. Contrast liquidity-penalty.yml (donate on JIT remove).\nExperimental. Forge tests in test/LpRewards.t.sol. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/LpRewards.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/LpRewards.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// v4hooks first-party — excerpt. Full file: source.url\nfunction _afterSwap(address, PoolKey calldata key, SwapParams calldata params, BalanceDelta delta, bytes calldata)\n    internal\n    override\n    returns (bytes4, int128)\n{\n    if (!params.zeroForOne || params.amountSpecified >= 0) return (this.afterSwap.selector, 0);\n    uint256 reward = (uint256(int256(delta.amount1())) * rewardBps) / 10_000;\n    if (reward == 0) return (this.afterSwap.selector, 0);\n    key.currency1.take(poolManager, address(this), reward, false);\n    pending[key.toId()] += reward;\n    return (this.afterSwap.selector, reward.toInt128());\n}\n\nfunction flush(PoolKey calldata key) external {\n    poolManager.unlock(abi.encode(key));\n}\n",
      "categories": [
        "launchpads",
        "lp-management"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "afterSwap",
        "afterSwapReturnDelta"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "median-oracle",
      "name": "Median oracle",
      "kind": "pattern",
      "tagline": "Ring buffer of post-swap ticks with onchain medianTick view.",
      "description": "First-party rewrite inspired by saucepoint median-oracles (MIT). afterSwap pushes floored tick\ninto a 16-sample ring per pool. medianTick sorts the active window on read — teaching cut of a\nmanipulation-resistant oracle vs TWAP geomean examples. Experimental. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/MedianOracle.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/MedianOracle.sol"
      },
      "solidity": "function getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: false,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: false,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "oracles"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterSwap"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://github.com/saucepoint/median-oracles",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "mev-donate",
      "name": "MEV donate",
      "kind": "pattern",
      "tagline": "Skim swap output to pending, then flush() donates to in-range LPs.",
      "description": "First-party rewrite inspired by FairArbooors (EthLondon) and Detox. afterSwapReturnDelta takes\nskimBps of swap output into pending balances per pool; flush() unlocks a donate + settle path\nso in-range LPs receive the skim (same unlock pattern as LpRewards). Skims both swap\ndirections. Teaching cut of MEV-to-LP redistribution — not a production MEV auction. Experimental.\nListing is not an audit. Forge tests under test/MevDonate.t.sol.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/MevDonate.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/MevDonate.sol"
      },
      "solidity": "function getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: false,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: false,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: true,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "mev-protection",
        "lp-management"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterSwap",
        "afterSwapReturnDelta"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://github.com/LVooooors/ETHGlobalLondon",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "mev-window",
      "name": "MevWindow",
      "kind": "pattern",
      "tagline": "Elevated swap fee and blocked add-liquidity for mevBlocks after pool init.",
      "description": "First-party teaching hook inspired by Clanker MEV module window. afterInitialize records launchBlock;\nwhile block.number < launchBlock + mevBlocks, beforeSwap returns mevFee and beforeAddLiquidity\nreverts MevWindowActive. After the window, baseFee applies and LP adds resume. Requires dynamic\nfee pool. Experimental. Forge tests in test/MevWindow.t.sol. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/MevWindow.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/MevWindow.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\nfunction feeFor(PoolId poolId) public view returns (uint24) {\n    return inMevWindow(poolId) ? mevFee : baseFee;\n}\n\nfunction _beforeAddLiquidity(...) internal view override returns (bytes4) {\n    if (inMevWindow(key.toId())) revert MevWindowActive();\n    return this.beforeAddLiquidity.selector;\n}\n",
      "categories": [
        "launchpads",
        "mev-protection",
        "dynamic-fees"
      ],
      "chains": [
        "base"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "beforeAddLiquidity",
        "beforeSwap"
      ],
      "properties": [
        "dynamic-fee",
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "nth-buy-pot",
      "name": "NthBuyPot",
      "kind": "pattern",
      "tagline": "Deterministic Nth-buy jackpot — potBps per buy, counter advances once per block, claim-backed.",
      "description": "First-party teaching hook inspired by Spark Nth-buy Pot. afterInitialize requires native currency0.\nOn exact-input buys, afterSwap pulls potBps of token output into the hook (afterSwapReturnDelta) and\nincrements buyCount at most once per block; when buyCount reaches nthBuy, winner is set from\nabi.encode(trader) in hookData (else swap sender). claim() sends accumulated token1 to the winner.\nNo random draw — public counter, no permissionless flush. Exact-input buys only. Experimental.\nForge tests in test/NthBuyPot.t.sol. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/NthBuyPot.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/NthBuyPot.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// v4hooks first-party — excerpt. Full file: source.url\nfunction _afterSwap(\n    address sender,\n    PoolKey calldata key,\n    SwapParams calldata params,\n    BalanceDelta delta,\n    bytes calldata hookData\n) internal override returns (bytes4, int128) {\n    if (!params.zeroForOne || params.amountSpecified >= 0) return (this.afterSwap.selector, 0);\n    PotState storage state = pots[key.toId()];\n    if (block.number != state.lastCountBlock) {\n        state.lastCountBlock = block.number;\n        state.buyCount++;\n        if (state.buyCount == nthBuy) state.winner = traderOf(sender, hookData);\n    }\n    uint256 contribution = (uint256(int256(delta.amount1())) * potBps) / 10_000;\n    key.currency1.take(poolManager, address(this), contribution, false);\n    return (this.afterSwap.selector, contribution.toInt128());\n}\n",
      "categories": [
        "launchpads"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "afterSwap",
        "afterSwapReturnDelta"
      ],
      "properties": [
        "custom-swap-data",
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "permissioned-pools",
      "name": "Permissioned Pools",
      "kind": "product",
      "tagline": "Per-currency allowlist enforced on swap and add-liquidity via permissions adapters.",
      "description": "PermissionedHooks is Uniswap v4-periphery production code. Pools must include at least one\nverified IPermissionsAdapter currency; beforeInitialize rejects unverified adapters.\nbeforeSwap and beforeAddLiquidity call the adapter to authorize msgSender() reported by\nregistered wrapper contracts. afterSwap emits a router-compatible Swap event for indexers.\nUse Uniswap's permissioned pool stack — do not reimplement KYC here. Excerpt shows permissions\nand initialize gate. Full file at the permalink. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/Uniswap/v4-hooks-public",
        "path": "src/permissioned-pools/PermissionedHooks.sol",
        "url": "https://github.com/Uniswap/v4-hooks-public/blob/main/src/permissioned-pools/PermissionedHooks.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory permissions) {\n    permissions.beforeInitialize = true;\n    permissions.beforeSwap = true;\n    permissions.afterSwap = true;\n    permissions.beforeAddLiquidity = true;\n}\n\nfunction _beforeInitialize(address, PoolKey calldata key, uint160) internal view override returns (bytes4) {\n    address currency0 = Currency.unwrap(key.currency0);\n    address currency1 = Currency.unwrap(key.currency1);\n    bool currency0IsAdapter = PERMISSIONS_ADAPTER_FACTORY.permissionsAdapterOf(currency0) != address(0);\n    bool currency1IsAdapter = PERMISSIONS_ADAPTER_FACTORY.permissionsAdapterOf(currency1) != address(0);\n    // requires at least one verified adapter; rejects unverified adapter currencies\n}\n",
      "categories": [
        "compliance",
        "wrappers"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "beforeInitialize",
        "beforeAddLiquidity",
        "beforeSwap",
        "afterSwap"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://docs.uniswap.org/contracts/v4/overview",
      "status": "production",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "referral-fee",
      "name": "Referral fee",
      "kind": "pattern",
      "tagline": "abi.encode(referrer) in hookData pays refBps of swap output to the referrer.",
      "description": "First-party rewrite inspired by mergd/ref-fee-hook (MIT). afterSwapReturnDelta takes refBps of\noutput token and transfers to the referrer decoded from hookData. Zero referrer skips the skim.\nExperimental referral split pattern. Listing is not an audit. Forge tests under test/ReferralFee.t.sol.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/ReferralFee.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/ReferralFee.sol"
      },
      "solidity": "function getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: false,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: false,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: true,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "creator-economy"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterSwap",
        "afterSwapReturnDelta"
      ],
      "properties": [
        "custom-swap-data",
        "vanilla-swap"
      ],
      "docs": "https://github.com/mergd/ref-fee-hook",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "rehypothecation",
      "name": "ReHypothecationHook",
      "kind": "pattern",
      "tagline": "Park idle LP in yield sources, JIT a hook-owned position on beforeSwap, unwind afterSwap.",
      "description": "OpenZeppelin’s ReHypothecationHook (MIT) parks idle LP in external yield sources (ERC-4626 or\nlending) and JIT-injects a hook-owned full-range position on beforeSwap, then unwinds it on\nafterSwap and settles PoolManager deltas back to the vaults. Users mint and redeem the hook’s\nERC20 via addReHypothecatedLiquidity; they do not add liquidity through PoolManager.\nbeforeInitialize binds a single PoolKey. You implement getCurrencyYieldSource and the deposit\nor withdraw adapters. Experimental: yield-source risk, PoolManager reserve timing, and leveraged\nliquidity notes are in the file. Copy the full file. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/OpenZeppelin/uniswap-hooks",
        "path": "src/general/ReHypothecationHook.sol",
        "url": "https://github.com/OpenZeppelin/uniswap-hooks/blob/master/src/general/ReHypothecationHook.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Uniswap Hooks — excerpt. Full file: source.url\nfunction getHookPermissions() public pure virtual override returns (Hooks.Permissions memory permissions) {\n    return Hooks.Permissions({\n        beforeInitialize: true,\n        afterInitialize: false,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction _beforeSwap(address, PoolKey calldata, SwapParams calldata, bytes calldata)\n    internal\n    virtual\n    override\n    returns (bytes4, BeforeSwapDelta, uint24)\n{\n    uint256 liquidityToUse = _getLiquidityToUse();\n    if (liquidityToUse > 0) _modifyLiquidity(liquidityToUse.toInt256());\n    return (this.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);\n}\n\nfunction _afterSwap(address, PoolKey calldata key, SwapParams calldata, BalanceDelta, bytes calldata)\n    internal\n    virtual\n    override\n    returns (bytes4, int128)\n{\n    uint128 liquidity = _getHookPositionLiquidity();\n    if (liquidity > 0) {\n        _modifyLiquidity(-liquidity.toInt256());\n        _resolveHookDelta(key.currency0);\n        _resolveHookDelta(key.currency1);\n    }\n    return (this.afterSwap.selector, 0);\n}\n",
      "categories": [
        "lending",
        "lp-management"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "beforeInitialize",
        "beforeSwap",
        "afterSwap"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://github.com/OpenZeppelin/uniswap-hooks",
      "website": "https://v4hooks.com/learn/openzeppelin-hooks",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "sell-guard",
      "name": "Sell guard",
      "kind": "pattern",
      "tagline": "Sell cooldown blocks rapid dumps; repeat sells in a window pay ramp fee.",
      "description": "First-party rewrite inspired by FairTrade (ETHNYC) and SafeSwap. Native-pair dynamic fee pool.\nbeforeSwap enforces cooldownBlocks between sells and ramps fee after repeat sells in windowBlocks.\nTrader keyed via abi.encode(trader) in hookData. Complements buy-side AntiSnipe. Experimental.\nListing is not an audit. Forge tests under test/SellGuard.t.sol.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/SellGuard.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/SellGuard.sol"
      },
      "solidity": "function getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "launchpads",
        "mev-protection"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "beforeSwap",
        "afterSwap"
      ],
      "properties": [
        "dynamic-fee",
        "custom-swap-data",
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "slippage-fee",
      "name": "SlippageFeeHook",
      "kind": "pattern",
      "tagline": "Simulate the swap, measure the tick it would move, then price the fee off that slippage.",
      "description": "A dynamic fee that reacts to the size of the swap in front of it rather than to\nhistorical volatility. Before the real swap runs, the hook simulates it, reads how far\nthe tick would move, and charges accordingly — so a swap that walks the pool pays more\nthan one that barely moves the price.\n\nThe mechanism is the part worth studying. Solidity has no way to ask \"what would this\nswap do\" without doing it, so _getExactSingleFee reads the current tick from\npoolManager.getSlot0, then calls this._getTickAfterExactSingle in a try/catch. That\ninner call performs the swap and deliberately reverts with SlippageFeeHook__RevertWithTick,\npacking the resulting tick into the revert reason. The catch block pulls the selector\nand the tick straight out of the returndata with assembly, and anything other than the\nexpected selector is rethrown as SlippageFeeHook__UnexpectedRevert. Reverting unwinds\nthe simulated swap; the tick delta survives in memory. That is the whole trick.\n\nRe-entrancy is handled by identity, not a lock: _beforeSwap checks whether sender is\nthe hook itself and charges zero fee if so, which is what keeps the simulation from\nrecursing into another simulation. Worth understanding before adapting it — the guard\nis that one comparison.\n\nafterInitialize refuses to attach to a pool whose key.fee is not dynamic, which is the\ncorrect place for that check. The fee returned from beforeSwap is OR'd with\nLPFeeLibrary.OVERRIDE_FEE_FLAG, the 23rd-bit flag v4 requires for a per-swap override.\n\nNote the simulate-then-revert pattern doubles the swap work, so gas is roughly twice a\nplain swap. Single-file, unaudited example.\n",
      "source": {
        "repo": "https://github.com/dennnis0204/slippage-fee-hook",
        "path": "src/SlippageFeeHook.sol",
        "url": "https://github.com/dennnis0204/slippage-fee-hook/blob/a6764f52a0b92b1f17c40fd918d3caf86ba1dded/src/SlippageFeeHook.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// dennnis0204/slippage-fee-hook — excerpt. Full file: source.url\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory permissions) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction _getExactSingleFee(ExactSingleParams memory params) internal returns (uint24 fee) {\n    (, int24 currentTick,,) = poolManager.getSlot0(params.poolKey.toId());\n\n    bytes4 selector;\n    int24 tickAfter;\n\n    // simulate the swap; it reverts with the resulting tick packed into the reason\n    try this._getTickAfterExactSingle(params) {}\n    catch (bytes memory reason) {\n        assembly (\"memory-safe\") {\n            selector := mload(add(reason, 0x20))\n            tickAfter := mload(add(reason, 0x24))\n        }\n    }\n\n    if (selector != SlippageFeeHook__RevertWithTick.selector) {\n        revert SlippageFeeHook__UnexpectedRevert();\n    }\n\n    fee = _calculateFee(currentTick, tickAfter, params.zeroForOne);\n}\n\nfunction _beforeSwap(address sender, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata)\n    internal override returns (bytes4, BeforeSwapDelta, uint24)\n{\n    uint24 fee;\n    /// @dev Sets the fee for the swap simulation via this hook\n    if (sender == address(this)) {\n        fee = 0;\n    } else {\n        ExactSingleParams memory exactSingleParams = ExactSingleParams({\n            poolKey: key,\n            zeroForOne: params.zeroForOne,\n            amountSpecified: params.amountSpecified\n        });\n        fee = _getExactSingleFee(exactSingleParams);\n    }\n    return (this.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, fee | LPFeeLibrary.OVERRIDE_FEE_FLAG);\n}\n\n/**\n * @dev Check that the pool key has a dynamic fee.\n */\nfunction _afterInitialize(address, PoolKey calldata key, uint160, int24) internal pure override returns (bytes4) {\n    if (!key.fee.isDynamicFee()) revert SlippageFeeHook__NotDynamicFee();\n    return this.afterInitialize.selector;\n}\n",
      "categories": [
        "dynamic-fees",
        "mev-protection"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "beforeSwap"
      ],
      "properties": [
        "dynamic-fee",
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "sniper-tax",
      "name": "SniperTax",
      "kind": "pattern",
      "tagline": "Launch LP fee decays linearly from startFee to baseFee over duration seconds.",
      "description": "First-party teaching hook inspired by Zora ZoraV4CoinHook launch fee decay. afterInitialize records\nlaunchTime; beforeSwap returns a fee that linearly decays from startFee (e.g. 99%) to baseFee (e.g. 1%)\nover duration seconds. Requires a dynamic-fee pool. Contrast anti-snipe.yml which caps buy size in\nblocks. Experimental. Forge tests in test/SniperTax.t.sol. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/SniperTax.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/SniperTax.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\nfunction feeFor(PoolId poolId) public view returns (uint24) {\n    uint256 elapsed = block.timestamp - launchTime[poolId];\n    if (elapsed >= duration) return baseFee;\n    uint256 delta = uint256(startFee) - uint256(baseFee);\n    return uint24(uint256(baseFee) + (delta * (duration - elapsed)) / duration);\n}\n",
      "categories": [
        "launchpads",
        "dynamic-fees",
        "mev-protection"
      ],
      "chains": [
        "base"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "beforeSwap"
      ],
      "properties": [
        "dynamic-fee",
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "stop-loss",
      "name": "Stop-loss",
      "kind": "pattern",
      "tagline": "Tick-walk fill-on-cross stop loss via unlock-wrapped market sells.",
      "description": "First-party rewrite of saucepoint v4-stoploss (MIT). afterInitialize snapshots tickLowerLast.\nregisterStop records size at a trigger tick. afterSwap walks downward crosses and fillStopLoss\nruns an unlock-wrapped market sell. Uses 14 permission fields and OpenZeppelin BaseHook.\nSimplified vs original: no ERC1155 receipts. Experimental teaching hook. Listing is not an audit.\nForge tests under test/StopLoss.t.sol.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/StopLoss.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/StopLoss.sol"
      },
      "solidity": "function getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: false,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "limit-orders"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "afterSwap"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://github.com/saucepoint/v4-stoploss",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "sucker-punch",
      "name": "SuckerPunch",
      "kind": "pattern",
      "tagline": "Free ETH-pair buys, hold-time sell fee decay, same-block sell taxed as MEV.",
      "description": "First-party rewrite of PopFendi’s EthLondon 2024 SuckerPunch (MIT). afterInitialize\nrequires a native currency0 pair and a dynamic fee pool. beforeSwap returns a per-swap\nfee with OVERRIDE_FEE_FLAG: buys are free, same-block sells pay MEV_FEE, never-bought\nsells pay BASE_FEE, and holders decay linearly to MIN_FEE over 90 days. afterSwap\nrecords buy inventory keyed by abi.encode(trader) in hookData (else the swap sender).\nFixes vs the original: 14 permission fields, OpenZeppelin BaseHook onlyPoolManager,\nno tx.origin, no updateDynamicSwapFee, and hold-fee math that does not underflow.\nExperimental. Listing is not an audit. Forge tests live under test/SuckerPunch.t.sol.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/SuckerPunch.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/SuckerPunch.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// v4hooks first-party rewrite — excerpt. Full file: source.url\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction _beforeSwap(address sender, PoolKey calldata key, SwapParams calldata params, bytes calldata hookData)\n    internal\n    view\n    override\n    returns (bytes4, BeforeSwapDelta, uint24)\n{\n    if (!key.currency0.isAddressZero()) revert NotNativePair();\n    address trader = traderOf(sender, hookData);\n    bool isBuy = params.zeroForOne;\n    uint24 fee = feeFor(key.toId(), trader, isBuy);\n    return (this.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, fee | LPFeeLibrary.OVERRIDE_FEE_FLAG);\n}\n",
      "categories": [
        "dynamic-fees",
        "mev-protection"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "beforeSwap",
        "afterSwap"
      ],
      "properties": [
        "dynamic-fee",
        "custom-swap-data",
        "vanilla-swap"
      ],
      "docs": "https://github.com/popfendi/suckerpunch",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "super-dca",
      "name": "Super DCA",
      "kind": "product",
      "tagline": "Dynamic LP fee in beforeSwap from who is swapping; pool must include the DCA token.",
      "description": "SuperDCAGauge (Apache-2.0) requires the Super DCA token in the pair and tickSpacing 60 in\n_beforeInitialize, then dynamic fees after init. _beforeSwap only accepts verified routers,\nreads msgSender, and returns fee | OVERRIDE_FEE_FLAG (0 for internal, keeper vs external).\nLiquidity callbacks also exist for gauge accounting. This is how to do allowlisted fee\ntiers, not a generic TWAMM you should copy blindly. Full SuperDCAGauge.sol at the permalink.\nListing is not an audit.\n",
      "source": {
        "repo": "https://github.com/Super-DCA-Tech/super-dca-gauge",
        "path": "src/SuperDCAGauge.sol",
        "url": "https://github.com/Super-DCA-Tech/super-dca-gauge/blob/master/src/SuperDCAGauge.sol"
      },
      "solidity": "// SPDX-License-Identifier: Apache-2.0\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: true,\n        afterInitialize: true,\n        beforeAddLiquidity: true,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: true,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction _beforeSwap(address sender, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata hookData)\n    internal\n    override\n    returns (bytes4, BeforeSwapDelta, uint24)\n{\n    if (!verifiedRouters[sender]) revert SuperDCAGauge__UnauthorizedRouter();\n    address swapper = IMsgSender(sender).msgSender();\n    uint24 fee = isInternalAddress[swapper] ? internalFee : swapper == keeper ? keeperFee : externalFee;\n    if (!isInternalAddress[swapper] && swapper != keeper) _handleDistributionAndSettlement(key, hookData);\n    return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, fee | LPFeeLibrary.OVERRIDE_FEE_FLAG);\n}\n",
      "categories": [
        "twamm",
        "dynamic-fees",
        "ve"
      ],
      "chains": [
        "base",
        "ethereum"
      ],
      "license": "Apache-2.0",
      "flags": [
        "beforeInitialize",
        "afterInitialize",
        "beforeAddLiquidity",
        "beforeRemoveLiquidity",
        "beforeSwap"
      ],
      "properties": [
        "dynamic-fee"
      ],
      "website": "https://superdca.org/",
      "status": "production",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "surge-fee",
      "name": "SurgeFee",
      "kind": "pattern",
      "tagline": "LP fee scales with swap size vs pool liquidity — no simulate-and-revert.",
      "description": "First-party teaching hook inspired by Spark Surge Fees. Contrast with slippage-fee.yml: that hook\nsimulates the swap and reverts with the post-trade tick to price the fee; SurgeFee reads pool\nliquidity from StateLibrary and adds surgeFactor * (tradeSize / liquidity) to baseFee in beforeSwap,\ncapped at maxFee. No try/catch simulation — cheaper gas, coarser signal (size vs liquidity, not tick\nwalk). Inherits OpenZeppelin BaseOverrideFee. Requires dynamic fee pool. Experimental. Forge tests\nin test/SurgeFee.t.sol. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/SurgeFee.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/SurgeFee.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// v4hooks first-party — excerpt. Full file: source.url\nfunction feeFor(PoolKey calldata key, SwapParams calldata params) public view returns (uint24) {\n    uint128 liquidity = poolManager.getLiquidity(key.toId());\n    if (liquidity == 0) return baseFee;\n    uint256 tradeSize = params.amountSpecified < 0\n        ? uint256(-params.amountSpecified)\n        : uint256(params.amountSpecified);\n    uint256 surge = (tradeSize * uint256(surgeFactor)) / uint256(liquidity);\n    uint256 total = uint256(baseFee) + surge;\n    if (total > maxFee) return maxFee;\n    return uint24(total);\n}\n\nfunction _getFee(address, PoolKey calldata key, SwapParams calldata params, bytes calldata)\n    internal\n    view\n    override\n    returns (uint24)\n{\n    return feeFor(key, params);\n}\n",
      "categories": [
        "dynamic-fees",
        "launchpads"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "beforeSwap"
      ],
      "properties": [
        "dynamic-fee",
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "take-profits",
      "name": "Take profits",
      "kind": "pattern",
      "tagline": "Tick-walk fill-on-cross take profit on upward price crosses.",
      "description": "First-party rewrite inspired by LearnWeb3 take-profits hook. Mirror of StopLoss: registerProfit\nat a tick, afterSwap fills on upward crosses with unlock-wrapped swaps. 14 permission fields,\nOpenZeppelin BaseHook, current v4 API. No ERC1155 layer in this teaching cut. Listing is not\nan audit. Forge tests under test/TakeProfit.t.sol.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/TakeProfit.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/TakeProfit.sol"
      },
      "solidity": "function getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: false,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "limit-orders"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "afterSwap"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://github.com/LearnWeb3DAO/uniswap-v4-take-profits-hook",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "trading-days",
      "name": "Trading days",
      "kind": "pattern",
      "tagline": "Revert beforeSwap outside NY weekday cash hours (9:30–16:00 ET, fixed UTC-5).",
      "description": "First-party rewrite inspired by horsefacts trading-days (MIT). beforeSwap reverts on weekends\nor outside the cash session. First open swap of the day emits DingDingDing. Simplified vs the\noriginal: no holiday/DST libraries — weekday + hour gate only. 14 permission fields, OpenZeppelin\nBaseHook. Listing is not an audit. Forge tests under test/TradingDays.t.sol.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/TradingDays.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/TradingDays.sol"
      },
      "solidity": "function getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: false,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "rwa",
        "compliance"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "beforeSwap"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "docs": "https://github.com/horsefacts/trading-days",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "trailing-stop",
      "name": "Trailing stop",
      "kind": "pattern",
      "tagline": "Peak-tick trailing stop with trail fee on sells after retrace.",
      "description": "First-party rewrite inspired by Hookathon C1 Trailing Hook. armTrail sets peak tick per trader.\nafterSwap updates peak on buys; beforeSwap applies TRAIL_FEE when a sell retraces trailDistance\nticks from peak. Dynamic fee pool with OVERRIDE_FEE_FLAG. Experimental. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/TrailingStop.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/TrailingStop.sol"
      },
      "solidity": "function getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "limit-orders"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "beforeSwap",
        "afterSwap"
      ],
      "properties": [
        "dynamic-fee",
        "custom-swap-data",
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "twamm",
      "name": "TWAMM",
      "kind": "pattern",
      "tagline": "Execute large orders over time by settling virtual TWAMM flow before each swap and LP change.",
      "description": "Uniswap’s example TWAMM hook runs executeTWAMMOrders in beforeInitialize, beforeAddLiquidity,\nand beforeSwap so long-lived orders accrue between blocks and settle against the pool instead\nof dumping in one swap. Permissions must enable those three callbacks. The rest of the file\n(order storage, expiration) lives in the linked Solidity — copy the full contract from\nv4-periphery example-contracts, do not ship this excerpt. The example is UNLICENSED research\ncode, not production. Confirm bytecode on hooklist if you route size. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/Uniswap/v4-periphery",
        "path": "contracts/hooks/examples/TWAMM.sol",
        "url": "https://github.com/Uniswap/v4-periphery/blob/example-contracts/contracts/hooks/examples/TWAMM.sol"
      },
      "solidity": "// SPDX-License-Identifier: UNLICENSED\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: true,\n        afterInitialize: false,\n        beforeAddLiquidity: true,\n        beforeRemoveLiquidity: false,\n        afterAddLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction beforeAddLiquidity(address, PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata, bytes calldata)\n    external\n    override\n    onlyByManager\n    returns (bytes4)\n{\n    executeTWAMMOrders(key);\n    return BaseHook.beforeAddLiquidity.selector;\n}\n\nfunction beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata)\n    external\n    override\n    onlyByManager\n    returns (bytes4, BeforeSwapDelta, uint24)\n{\n    executeTWAMMOrders(key);\n    return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);\n}\n",
      "categories": [
        "twamm"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "UNLICENSED",
      "flags": [
        "beforeInitialize",
        "beforeAddLiquidity",
        "beforeSwap"
      ],
      "properties": [
        "vanilla-swap",
        "custom-swap-data"
      ],
      "docs": "https://blog.uniswap.org/v4-twamm-hook",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "v2-pair",
      "name": "V2 pair hook",
      "kind": "pattern",
      "tagline": "Run xy=k inside the hook: mint LP on the ERC20, NoOp the CLAMM swap with return-delta.",
      "description": "hensha256’s V2PairHook implements Uniswap v2 constant-product math inside a v4 hook instead of\nconcentrating liquidity. beforeInitialize binds factory, fee 0, tickSpacing 1, and the pair\ncurrencies. beforeAddLiquidity always reverts so LPs mint() on the hook ERC20. beforeSwap mints\nand burns ERC-6909 claims, prices with _getAmountOut, and returns a specified delta that NoOps\nthe CLAMM; afterSwap returns the unspecified delta from transient storage. Flags include both\nreturn-delta bits. UNLICENSED research code. Distinct from Uniswap FullRange, which still uses\nthe concentrated AMM. Copy the full file. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/hensha256/v2-on-v4",
        "path": "src/V2PairHook.sol",
        "url": "https://github.com/hensha256/v2-on-v4/blob/main/src/V2PairHook.sol"
      },
      "solidity": "// SPDX-License-Identifier: UNLICENSED\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: true,\n        afterInitialize: false,\n        beforeAddLiquidity: true,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: true,\n        afterSwapReturnDelta: true,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction beforeAddLiquidity(address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata)\n    external\n    pure\n    override\n    returns (bytes4)\n{\n    revert AddLiquidityDirectToHook();\n}\n\nfunction beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata)\n    external\n    override\n    poolManagerOnly\n    returns (bytes4, int128)\n{\n    bool exactIn = (params.amountSpecified < 0);\n    uint256 amountIn;\n    uint256 amountOut;\n    if (exactIn) {\n        amountIn = uint256(-params.amountSpecified);\n        amountOut = _getAmountOut(params.zeroForOne, amountIn);\n    } else {\n        amountOut = uint256(params.amountSpecified);\n        amountIn = _getAmountIn(params.zeroForOne, amountOut);\n    }\n    (Currency inputCurrency, Currency outputCurrency) = _getInputOutput(key, params.zeroForOne);\n    poolManager.mint(address(this), CurrencyLibrary.toId(inputCurrency), amountIn);\n    poolManager.burn(address(this), CurrencyLibrary.toId(outputCurrency), amountOut);\n    return (IHooks.beforeSwap.selector, int128(-params.amountSpecified));\n}\n",
      "categories": [
        "wrappers",
        "lp-management"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "UNLICENSED",
      "flags": [
        "beforeInitialize",
        "beforeAddLiquidity",
        "beforeSwap",
        "afterSwap",
        "beforeSwapReturnDelta",
        "afterSwapReturnDelta"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "velp",
      "name": "veLP",
      "kind": "pattern",
      "tagline": "Curve-style vote-escrow on a v4 position: lock ticks, block early withdraw in beforeModifyPosition.",
      "description": "kadenzipfel ports VotingEscrow onto a Uniswap v4 hook. afterInitialize stores the PoolId.\nbeforeModifyPosition lets unrelated ticks through, but if sender is modifying their locked\nrange it requires liquidityDelta > 0 until lock end. afterModifyPosition checkpoints ve\nweight from the position’s liquidity. Early Hooks.Calls API (modifyPosition, not add/remove).\nAGPL-3.0 — copy the full VotingEscrow.sol. Map modifyPosition to today’s\nbeforeAddLiquidity / beforeRemoveLiquidity if you port forward. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/kadenzipfel/veLP",
        "path": "src/VotingEscrow.sol",
        "url": "https://github.com/kadenzipfel/veLP/blob/main/src/VotingEscrow.sol"
      },
      "solidity": "// SPDX-License-Identifier: AGPL-3.0-or-later\nfunction getHooksCalls() public pure override returns (Hooks.Calls memory) {\n    return Hooks.Calls({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeModifyPosition: true,\n        afterModifyPosition: true,\n        beforeSwap: false,\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false\n    });\n}\n\nfunction beforeModifyPosition(\n    address sender,\n    PoolKey calldata,\n    IPoolManager.ModifyPositionParams calldata modifyPositionParams\n) external view override poolManagerOnly returns (bytes4) {\n    LockTicks memory lockTicks_ = lockTicks[sender];\n    if (\n        lockTicks_.lowerTick != modifyPositionParams.tickLower\n            || lockTicks_.upperTick != modifyPositionParams.tickUpper\n    ) {\n        return VotingEscrow.beforeModifyPosition.selector;\n    }\n    LockedBalance memory locked_ = locked[sender];\n    require(\n        modifyPositionParams.liquidityDelta > 0 || locked_.end <= block.timestamp,\n        \"Can't withdraw before lock end\"\n    );\n    return VotingEscrow.beforeModifyPosition.selector;\n}\n",
      "categories": [
        "ve",
        "lp-management"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "AGPL-3.0-or-later",
      "flags": [
        "afterInitialize",
        "beforeAddLiquidity",
        "afterAddLiquidity",
        "beforeRemoveLiquidity",
        "afterRemoveLiquidity"
      ],
      "properties": [
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "purefi-verifier",
      "name": "VerifierHook",
      "kind": "pattern",
      "tagline": "Gate every pool action behind an off-chain AML proof checked in the callback.",
      "description": "PureFi's VerifierHook is a compliance gate wired into almost every callback v4 offers:\nbeforeAddLiquidity, afterAddLiquidity, beforeRemoveLiquidity, afterRemoveLiquidity,\nbeforeSwap, afterSwap and beforeDonate. Seven bits is a lot of surface, and the reason\nis that a compliance rule which only covers swaps is trivially sidestepped by moving\nliquidity instead.\n\nThe hook holds an immutable IPureFiVerifier and a PureFiHookWhitelist. Callers hand it\na signed PureFi package — an off-chain AML/KYC attestation — which the callback decodes\nthrough PureFiDataLibrary and checks against a ruleId mapping before letting the action\nthrough. Roles are OpenZeppelin AccessControl: ISSUER, MARKET_MAKER, ROUTER and QUOTER,\nplus a routersWhitelist so only sanctioned routers can reach the pool at all.\n\nThat last part is the important structural detail. The proof travels as hookData, and\nthe repo ships its own PureFiSwapRouter and PureFiModifyLiquidityRouter to carry it, so\nthis is not a pool you can trade against with a stock router — the swap path is part of\nthe product. Read the routers alongside the hook; the hook alone will not tell you how\nthe proof gets there.\n\nCopy the shape, not the dependency. Gating all four liquidity callbacks plus both swap\ncallbacks is the transferable idea; swap IPureFiVerifier for whatever attestation source\nyou actually trust and the rest of the structure still holds.\n\nUnaudited in this repository.\n",
      "source": {
        "repo": "https://github.com/purefiprotocol/purefi-verifier-hook",
        "path": "src/VerifierHook.sol",
        "url": "https://github.com/purefiprotocol/purefi-verifier-hook/blob/1efdca6703a473942a02d4a166f0f5292bcecf5b/src/VerifierHook.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\n// purefiprotocol/purefi-verifier-hook — excerpt. Full file: source.url\ncontract VerifierHook is BaseHook, AccessControl {\n    IPureFiVerifier public immutable verifier;\n    PureFiHookWhitelist public immutable whitelist;\n\n    mapping(address => bool) private routersWhitelist;\n    mapping(uint32 => bool) public ruleId;\n\n    bytes32 public constant ISSUER = keccak256('ISSUER');\n    bytes32 public constant MARKET_MAKER = keccak256('MARKET_MAKER');\n    bytes32 public constant ROUTER = keccak256('ROUTER');\n    bytes32 public constant QUOTER = keccak256('QUOTER');\n\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: false,\n        beforeAddLiquidity: true,\n        afterAddLiquidity: true,\n        beforeRemoveLiquidity: true,\n        afterRemoveLiquidity: true,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: true,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\n// Every gated callback decodes a signed PureFi package out of hookData via\n// PureFiDataLibrary and checks it against ruleId before allowing the action.\n// The proof is delivered by the repo's own PureFiSwapRouter /\n// PureFiModifyLiquidityRouter — a stock router cannot reach this pool.\n",
      "categories": [
        "compliance"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "beforeAddLiquidity",
        "afterAddLiquidity",
        "beforeRemoveLiquidity",
        "afterRemoveLiquidity",
        "beforeSwap",
        "afterSwap",
        "beforeDonate"
      ],
      "properties": [
        "custom-swap-data"
      ],
      "website": "https://purefi.io/",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "volatility-oracle",
      "name": "Volatility oracle",
      "kind": "pattern",
      "tagline": "Require a dynamic fee on initialize, then ramp LP fee by elapsed time after deploy.",
      "description": "Uniswap’s VolatilityOracle example is a teaching dynamic-fee hook, not a live volatility\nfeed. beforeInitialize requires key.fee.isDynamicFee(). afterInitialize calls setFee, which\nstarts at 3000 and adds 100 bps per elapsed minute via poolManager.updateDynamicLPFee.\nUNLICENSED example-contracts file. Use it to learn the initialize-plus-poke pattern; do not\ntreat the contract name as an oracle. Copy the full file from the permalink. Listing is not\nan audit.\n",
      "source": {
        "repo": "https://github.com/Uniswap/v4-periphery",
        "path": "contracts/hooks/examples/VolatilityOracle.sol",
        "url": "https://github.com/Uniswap/v4-periphery/blob/example-contracts/contracts/hooks/examples/VolatilityOracle.sol"
      },
      "solidity": "// SPDX-License-Identifier: UNLICENSED\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: true,\n        afterInitialize: true,\n        beforeAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterAddLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: false,\n        afterSwap: false,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\nfunction beforeInitialize(address, PoolKey calldata key, uint160, bytes calldata)\n    external\n    pure\n    override\n    returns (bytes4)\n{\n    if (!key.fee.isDynamicFee()) revert MustUseDynamicFee();\n    return VolatilityOracle.beforeInitialize.selector;\n}\n\nfunction setFee(PoolKey calldata key) public {\n    uint24 startingFee = 3000;\n    uint32 lapsed = _blockTimestamp() - deployTimestamp;\n    uint24 fee = startingFee + (uint24(lapsed) * 100) / 60; // 100 bps a minute\n    manager.updateDynamicLPFee(key, fee); // initial fee 0.30%\n}\n",
      "categories": [
        "dynamic-fees"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "UNLICENSED",
      "flags": [
        "beforeInitialize",
        "afterInitialize"
      ],
      "properties": [
        "dynamic-fee",
        "vanilla-swap"
      ],
      "docs": "https://docs.uniswap.org/contracts/v4/concepts/hooks",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "volatility-fee",
      "name": "VolatilityFee",
      "kind": "pattern",
      "tagline": "LP fee rises with |currentTick − lastTick| since the previous swap.",
      "description": "First-party teaching hook inspired by Clanker-style tick-accumulator dynamic fees. Stores lastTick\nper pool; beforeSwap reads current tick from StateLibrary and adds tickFactor × |Δtick| to baseFee,\ncapped at maxFee. afterSwap updates lastTick. Contrast surge-fee.yml (size vs liquidity) and\nvolatility-oracle.yml (time ramp). Requires dynamic fee pool. Experimental. Forge tests in\ntest/VolatilityFee.t.sol. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/VolatilityFee.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/VolatilityFee.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\nfunction feeFor(PoolKey calldata key) public view returns (uint24) {\n    (, int24 tick,,) = poolManager.getSlot0(key.toId());\n    uint256 move = _absTickDelta(tick, lastTick[key.toId()]);\n    uint256 total = uint256(baseFee) + move * uint256(tickFactor);\n    if (total > maxFee) return maxFee;\n    return uint24(total);\n}\n",
      "categories": [
        "dynamic-fees"
      ],
      "chains": [
        "base"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "beforeSwap",
        "afterSwap"
      ],
      "properties": [
        "dynamic-fee",
        "vanilla-swap"
      ],
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "volume-tier",
      "name": "Volume tier",
      "kind": "pattern",
      "tagline": "Cumulative swap notional unlocks lower dynamic fees for high-volume traders.",
      "description": "First-party rewrite inspired by EthLondon Royalty Swap (Keinberger/royalty-swap). afterSwap\naccumulates amountSpecified notional per trader (hookData address). beforeSwap returns tiered\nfee with OVERRIDE_FEE_FLAG at tier1/tier2 breakpoints. Distinct from SuckerPunch hold-time fees\nand SurgeFee size/liquidity scaling. Experimental. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/CryptoGnome/v4hooks",
        "path": "contracts/VolumeTier.sol",
        "url": "https://github.com/CryptoGnome/v4hooks/blob/main/contracts/VolumeTier.sol"
      },
      "solidity": "function getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeAddLiquidity: false,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n",
      "categories": [
        "dynamic-fees"
      ],
      "chains": [
        "ethereum"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "beforeSwap",
        "afterSwap"
      ],
      "properties": [
        "dynamic-fee",
        "custom-swap-data",
        "vanilla-swap"
      ],
      "docs": "https://github.com/Keinberger/royalty-swap",
      "status": "experimental",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    },
    {
      "slug": "zora-coin-hook",
      "name": "Zora Coin Hook",
      "kind": "product",
      "tagline": "Creator coin hook: launch fee decay, LP positions on init, fee swap + reward distribution.",
      "description": "ZoraV4CoinHook is Zora production code. On pool init it seeds multi-range LP from coin config.\nbeforeSwap returns a dynamic launch fee that decays from ~99% to 1% over ~10 seconds after coin\ncreation (LAUNCH_FEE_START → LP_FEE_V4). afterSwap collects LP fees, swaps to backing currency,\nand distributes rewards. Supports hook upgrades via IUpgradeableV4Hook migration paths. Use Zora's\nfactory — do not reimplement to launch a coin. Excerpt shows permissions and launch fee calc.\nFull file at the permalink. Listing is not an audit.\n",
      "source": {
        "repo": "https://github.com/ourzora/zora-protocol",
        "path": "packages/coins/src/hooks/ZoraV4CoinHook.sol",
        "url": "https://github.com/ourzora/zora-protocol/blob/main/packages/coins/src/hooks/ZoraV4CoinHook.sol"
      },
      "solidity": "// SPDX-License-Identifier: MIT\nfunction getHookPermissions() public pure override returns (Hooks.Permissions memory) {\n    return Hooks.Permissions({\n        beforeInitialize: false,\n        afterInitialize: true,\n        beforeAddLiquidity: true,\n        afterAddLiquidity: false,\n        beforeRemoveLiquidity: false,\n        afterRemoveLiquidity: false,\n        beforeSwap: true,\n        afterSwap: true,\n        beforeDonate: false,\n        afterDonate: false,\n        beforeSwapReturnDelta: false,\n        afterSwapReturnDelta: false,\n        afterAddLiquidityReturnDelta: false,\n        afterRemoveLiquidityReturnDelta: false\n    });\n}\n\n/// Fee decays linearly from LAUNCH_FEE_START (99%) to LP_FEE_V4 (1%) over LAUNCH_FEE_DURATION.\nfunction _calculateLaunchFee(address coin) internal view returns (uint24 fee) {\n    // elapsed since IHasCreationInfo.creationTime(); returns OVERRIDE_FEE_FLAG | fee\n}\n",
      "categories": [
        "launchpads",
        "creator-economy",
        "dynamic-fees"
      ],
      "chains": [
        "base"
      ],
      "license": "MIT",
      "flags": [
        "afterInitialize",
        "beforeAddLiquidity",
        "beforeSwap",
        "afterSwap"
      ],
      "properties": [
        "dynamic-fee",
        "vanilla-swap"
      ],
      "website": "https://zora.co/",
      "docs": "https://docs.zora.co/",
      "status": "production",
      "added": "2026-08-27",
      "updated": "2026-08-27"
    }
  ]
}