uniswap-v4-hooks

Secure Uniswap v4 hook development assistant. Activates when: - Working with Uniswap v4 hooks, PoolManager, or hook contracts - Keywords: uniswap, v4, hook, hooks, beforeSwap, afterSwap, PoolManager, IHooks, BaseHook - Creating, reviewing, or auditing hook contracts - Implementing custom AMM logic, dynamic fees, or swap modifications - User says "uniswap hooks" or invokes "/uniswap-v4-hooks"

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "uniswap-v4-hooks" with this command: npx skills add igoryuzo/uniswapv4-hooks-skill/igoryuzo-uniswapv4-hooks-skill-uniswap-v4-hooks

This skill guides secure development of Uniswap v4 hooks. Hooks are external smart contracts that intercept pool operations at specific lifecycle points. Security is paramount—hook vulnerabilities can drain user funds.

Security Thinking

Before writing ANY hook code, assess the threat model:

Who calls your hook?

  • Only PoolManager should call hook functions
  • msg.sender in a hook is ALWAYS PoolManager, never the user
  • The sender parameter is the router, not the end user

What state is exposed?

  • Hooks execute mid-transaction—state can be manipulated between callbacks
  • Reentrancy is possible through external calls
  • Shared storage across pools using the same hook can break unexpectedly

What can go wrong with deltas?

  • Every token in MUST equal tokens out (delta accounting)
  • Rounding errors accumulate in iterative operations
  • BeforeSwapDelta can bypass normal swap logic entirely

CRITICAL: The NoOp Rug Pull Vector

Hooks with BEFORE_SWAP_RETURNS_DELTA_FLAG can steal user funds. This is the most dangerous hook permission.

// MALICIOUS EXAMPLE - DO NOT USE
// This hook takes user tokens and returns nothing
function beforeSwap(...) external returns (bytes4, BeforeSwapDelta, uint24) {
    Currency input = params.zeroForOne ? key.currency0 : key.currency1;

    // Take all user funds
    input.take(poolManager, address(this), uint256(-params.amountSpecified), false);

    // Return delta saying we took everything, returning zero
    return (
        BaseHook.beforeSwap.selector,
        toBeforeSwapDelta(int128(-params.amountSpecified), 0), // THEFT
        0
    );
}

When beforeSwapReturnDelta: true:

  • The hook can completely replace swap logic
  • Pool math is SKIPPED if delta equals amountSpecified
  • User funds flow to the hook, not through the AMM curve

Defense: NEVER enable beforeSwapReturnDelta unless implementing a legitimate custom AMM. Users should verify hook permissions before swapping.

Permission Flags (Address Encoding)

Hook permissions are encoded in the contract address. The address must have specific bits set:

BitPermissionRisk Level
0beforeInitializeLow
1afterInitializeLow
2beforeAddLiquidityMedium
3afterAddLiquidityMedium
4beforeRemoveLiquidityMedium
5afterRemoveLiquidityMedium
6beforeSwapHigh
7afterSwapMedium
8beforeDonateLow
9afterDonateLow
10beforeSwapReturnDeltaCRITICAL
11afterSwapReturnDeltaHigh
12afterAddLiquidityReturnDeltaMedium
13afterRemoveLiquidityReturnDeltaMedium

Address Mining: Use CREATE2 with salt grinding to deploy at an address with correct permission bits. Tools: hook-mine-and-sinker, v4-hook-address-miner.

Access Control Pattern

// REQUIRED: Verify caller is PoolManager
modifier onlyPoolManager() {
    require(msg.sender == address(poolManager), "Not PoolManager");
    _;
}

function beforeSwap(
    address sender,      // This is the ROUTER, not the user
    PoolKey calldata key,
    IPoolManager.SwapParams calldata params,
    bytes calldata hookData
) external onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24) {
    // ...
}

Identifying the Actual User (msg.sender Problem)

The sender parameter is the router contract, NOT the end user. To get the actual user:

// 1. Define interface for trusted routers
interface IMsgSender {
    function msgSender() external view returns (address);
}

// 2. Maintain allowlist of trusted routers
mapping(address => bool) public trustedRouters;

// 3. Query router safely in hook
function afterSwap(
    address sender,  // This is the router
    PoolKey calldata key,
    IPoolManager.SwapParams calldata params,
    BalanceDelta delta,
    bytes calldata hookData
) external override returns (bytes4, int128) {
    // CRITICAL: Only trust verified routers
    if (!trustedRouters[sender]) {
        revert UntrustedRouter(sender);
    }

    // Safe to query actual user
    try IMsgSender(sender).msgSender() returns (address user) {
        // Use `user` for rewards, tracking, etc.
    } catch {
        revert RouterDoesNotImplementMsgSender();
    }

    return (this.afterSwap.selector, 0);
}

NEVER trust tx.origin for authentication (only acceptable for anti-gaming checks like self-referral prevention).

Delta Accounting Rules

Deltas track what the hook owes or is owed. They MUST net to zero.

// Taking tokens FROM PoolManager (hook receives tokens)
currency.take(poolManager, address(this), amount, false);

// Settling tokens TO PoolManager (hook sends tokens)
currency.settle(poolManager, address(this), amount, false);

// INVARIANT: All deltas must balance before unlock completes

Common Delta Bugs:

  • Forgetting to settle after taking
  • Rounding in wrong direction (always round against the user/hook)
  • Not handling both swap directions (zeroForOne true AND false)

Hook Implementation Checklist

Before ANY hook implementation:

  • Access Control: Only PoolManager can call hook functions
  • Delta Balance: Every take has a corresponding settle
  • Router Verification: Never trust sender without allowlist check
  • Overflow Protection: Use mulDiv for price math, never raw multiplication
  • Reentrancy Guards: Add if making external calls
  • Token Type Handling: Document unsupported tokens (rebasing, fee-on-transfer)
  • Permission Flags: Minimal permissions needed for functionality

Base Hook Template

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {BaseHook} from "v4-periphery/src/base/hooks/BaseHook.sol";
import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol";
import {PoolKey} from "v4-core/types/PoolKey.sol";
import {BalanceDelta} from "v4-core/types/BalanceDelta.sol";
import {Hooks} from "v4-core/libraries/Hooks.sol";

contract MyHook is BaseHook {
    constructor(IPoolManager _manager) BaseHook(_manager) {}

    function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
        return Hooks.Permissions({
            beforeInitialize: false,
            afterInitialize: false,
            beforeAddLiquidity: false,
            afterAddLiquidity: false,
            beforeRemoveLiquidity: false,
            afterRemoveLiquidity: false,
            beforeSwap: false,
            afterSwap: true,  // Enable only what you need
            beforeDonate: false,
            afterDonate: false,
            beforeSwapReturnDelta: false,  // DANGEROUS - enable with caution
            afterSwapReturnDelta: false,
            afterAddLiquidityReturnDelta: false,
            afterRemoveLiquidityReturnDelta: false
        });
    }

    function _afterSwap(
        address sender,
        PoolKey calldata key,
        IPoolManager.SwapParams calldata params,
        BalanceDelta delta,
        bytes calldata hookData
    ) internal override returns (bytes4, int128) {
        // Your logic here
        return (this.afterSwap.selector, 0);
    }
}

Common Vulnerability Patterns

1. Overflow in Price Calculations

// BAD: Can overflow
uint256 price = uint256(sqrtPriceX96) ** 2;

// GOOD: Use mulDiv
uint256 price = FullMath.mulDiv(uint256(sqrtPriceX96), uint256(sqrtPriceX96), 1 << 96);

2. Incorrect Fee Calculation

// BAD: Simple addition is wrong
uint256 totalFee = protocolFee + lpFee;

// GOOD: Protocol fee taken first, then LP fee from remainder
// protocolFee + lpFee * (1_000_000 - protocolFee) / 1_000_000

3. Missing Timestamp Validation

// BAD: No future check
function submitOrder(uint256 expiration) external {
    orders[msg.sender] = Order(expiration, ...);
}

// GOOD: Validate expiration is in future
function submitOrder(uint256 expiration) external {
    require(expiration > block.timestamp, "Expiration must be in future");
    require(expiration % interval == 0, "Must align to interval");
    orders[msg.sender] = Order(expiration, ...);
}

4. Trusting Sender Without Verification

// BAD: sender could be malicious contract
address user = sender;

// GOOD: Verify router and query actual user
require(trustedRouters[sender], "Untrusted router");
address user = IMsgSender(sender).msgSender();

Token Handling Hazards

Unsupported token types (document explicitly if your hook doesn't handle):

  • Fee-on-transfer: Actual received amount differs from transfer amount
  • Rebasing: Balance changes without transfers
  • ERC-777: Reentrancy via transfer hooks
  • Pausable: Transfers can revert unexpectedly
  • Blocklist: Some addresses may be blocked
// If handling non-standard tokens, validate actual balances
uint256 balanceBefore = token.balanceOf(address(this));
token.transferFrom(user, address(this), amount);
uint256 actualReceived = token.balanceOf(address(this)) - balanceBefore;

Testing Requirements

Use Foundry for comprehensive testing:

// Invariant test: Deltas always balance
function invariant_deltasBalance() public {
    assertEq(poolManager.currencyDelta(address(hook), currency0), 0);
    assertEq(poolManager.currencyDelta(address(hook), currency1), 0);
}

// Fuzz test: No overflow in price calculations
function testFuzz_priceCalculation(uint160 sqrtPriceX96) public {
    vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_PRICE);
    vm.assume(sqrtPriceX96 <= TickMath.MAX_SQRT_PRICE);
    // Should not revert
    hook.calculatePrice(sqrtPriceX96);
}

// Test both swap directions
function test_swapZeroForOne() public { ... }
function test_swapOneForZero() public { ... }

Risk Assessment (Self-Score)

Before deployment, score your hook (0-33 scale):

DimensionScore RangeYour Hook
Code Complexity0-5
Custom Math0-5
External Dependencies0-3
External Liquidity0-3
TVL Potential0-5
Team Maturity0-3
Upgradeability0-3
Autonomous Updates0-3
Price-Impacting0-3

Risk Tiers:

  • Low (0-6): 1 audit + AI static analysis
  • Medium (7-17): 1-2 audits + bug bounty recommended
  • High (18-33): 2 audits (1 math specialist) + mandatory bug bounty + monitoring

Production Hooks Reference

Allowlisted hooks on Uniswap (as of Jan 2025):

  • Flaunch (meme coin launchpad, 100% fees to creators)
  • EulerSwap (lending-powered DEX)
  • Zaha Studios TWAMM (time-weighted orders)
  • Coinbase Verified Pools
  • Panoptic Oracle Hook
  • AEGIS Dynamic Fee Mechanism

Study these for patterns: https://raw.githubusercontent.com/fewwwww/awesome-uniswap-hooks/refs/heads/main/README.md

NEVER Do This

  • NEVER enable beforeSwapReturnDelta without understanding the rug pull risk
  • NEVER trust sender parameter without router verification
  • NEVER use unchecked blocks for price/amount math
  • NEVER assume ERC20 standard behavior for all tokens
  • NEVER skip delta settlement
  • NEVER deploy without address permission verification
  • NEVER store sensitive state readable mid-swap
  • NEVER use tx.origin for authentication

Resources

Audit Checklist (Pre-Deployment)

  • All hook functions check msg.sender == poolManager
  • Deltas verified to net zero in all paths
  • No overflow possible in math operations
  • Router allowlist implemented if identifying users
  • Token types explicitly documented
  • Reentrancy guards on external calls
  • Timestamp validations in place
  • Permission flags minimal and correct
  • Fuzz tests pass for edge cases
  • Invariant tests for delta accounting
  • Both swap directions tested
  • Fee calculations match Uniswap's formula

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

compliance-evidence-assembler

把审计所需证据整理成目录、清单和缺失项,便于后续评审。;use for compliance, evidence, audit workflows;do not use for 伪造证据, 替代正式审计结论.

Archived SourceRecently Updated
Security

skillguard-hardened

Security guard for OpenClaw skills, developed and maintained by rose北港(小红帽 / 猫猫帽帽). Audits installed or incoming skills with local rules plus Zenmux AI intent review, then recommends pass, warn, block, or quarantine.

Archived SourceRecently Updated
Security

api-contract-auditor

审查 API 文档、示例和字段定义是否一致,输出 breaking change 风险。;use for api, contract, audit workflows;do not use for 直接改线上接口, 替代契约测试平台.

Archived SourceRecently Updated
Security

ai-workflow-red-team-lite

对 AI 自动化流程做轻量红队演练,聚焦误用路径、边界失败和数据泄露风险。;use for red-team, ai, workflow workflows;do not use for 输出可直接滥用的攻击脚本, 帮助破坏系统.

Archived SourceRecently Updated