关于 v4-hook-generator
v4-hook-generator 是一项技能,通过 OpenZeppelin Contracts Wizard 配置并生成 Uniswap v4 hook 合约。它帮助 Solidity 开发者选择 hook 类型和参数、组装 MCP 请求,并安排后续安全审查。
主要功能
- 01比较适用于不同目标的 14 种 hook
- 02配置权限、工具库、份额和访问控制
- 03组装规范的 MCP 工具调用 JSON
- 04选择 hook 专用数值参数
- 05遵循生成到安全审查的流程
适用场景
- 01搭建费用、预言机或 MEV 防护 hook
- 02为自定义 hook 逻辑选择权限
- 03准备 generate_hook 请求
- 04在实现前权衡配置选项
MCP 相关
指导构造 OpenZeppelin Contracts Wizard MCP 工具 `generate_hook` 的请求,该工具用于生成 Solidity hook 代码。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ uniswap/ uniswap-ai --skill v4-hook-generator安装后在 Claude Code 中输入/v4-hook-generator确认已加载。
详细介绍
v4 钩子生成器
通过 OpenZeppelin Contracts Wizard MCP 工具生成 Uniswap v4 钩子合约。本技能将指导你选择合适的钩子类型、配置权限和实用工具、组装规范的 MCP JSON,并调用 MCP 工具生成可直接编译的 Solidity 代码。
安全配套技能:生成的钩子代码涉及处理资金的合约。生成后请立即应用
v4-security-foundations技能,在部署到任何网络之前审计权限、差额记账和访问控制。
何时使用此技能
在以下情况下使用此技能:
- 从头搭建新的 Uniswap v4 钩子合约
- 针对特定用例(费用、MEV 保护、预言机等)选择合适的基础钩子类型
- 配置钩子权限、实用工具库、份额和访问控制
- 生成调用 OpenZeppelin Contracts Wizard 的规范 MCP 工具调用 JSON
- 在确定实现方案之前,了解不同钩子配置选项之间的权衡
前置/配套技能:v4-security-foundations — 编写自定义逻辑前以及部署前都要运行此技能。钩子配置不当可能导致用户资金被盗。
钩子类型决策表
选择与主要目标匹配的基础钩子类型。如果钩子有多个目标,请选择覆盖最关键问题的类型,并在此基础上叠加其他逻辑。
| 目标 | 使用钩子 |
|---|---|
| 自定义交换逻辑 | BaseHook |
| 异步/延迟交换 | BaseAsyncSwap |
| 钩子自有流动性 | BaseCustomAccounting |
| 自定义曲线 | BaseCustomCurve |
| 动态 LP 费用 | BaseDynamicFee |
| 动态交换费用 | BaseOverrideFee |
| 交换后费用 | BaseDynamicAfterFee |
| 固定钩子费用 | BaseHookFee |
| MEV 保护 | AntiSandwichHook |
| JIT 保护 | LiquidityPenaltyHook |
| 限价订单 | LimitOrderHook |
| 闲置资金收益 | ReHypothecationHook |
| 预言机 | BaseOracleHook |
| 兼容 V3 的预言机 | OracleHookWithV3Adapters |
选择建议:
BaseHook是通用的起点——只有在专用类型的内置逻辑能带来明确价值时,才选择专用类型。BaseCustomCurve会替换整个 AMM 数学逻辑;仅在实现新颖的定价算法时使用。AntiSandwichHook和LiquidityPenaltyHook都用于应对 MEV,但针对的对象不同(交易者与 JIT LP)。请明确要缓解哪种攻击途径。- 如果下游集成需要兼容 Uniswap v3
IUniswapV3Pool的预言机接口,则适合使用OracleHookWithV3Adapters。
最简决策清单
调用 MCP 工具之前,请确认以下六项决定:
- 钩子类型——从上方的决策表中选择
- 要启用的权限——仅启用逻辑实际使用的回调(
beforeSwap、afterSwap等) - 实用工具库——按需使用
currencySettler、safeCast、transientStorage - 份额——
false、ERC20、ERC6909或ERC1155 - 访问控制——
ownable、roles或managed - 钩子输入项——
blockNumberOffset、maxAbsTickDelta(仅适用于会用到这些参数的钩子类型)
权限配置
共 14 个权限标志,并说明何时启用。请先将所有标志设为 false,仅启用钩子逻辑所需的标志。每启用一项权限,都会扩大钩子的攻击面,并要求在钩子的已部署地址中设置特定的位(见下方的地址编码说明)。
| 权限标志 | 启用时机 | 风险 |
|---|---|---|
beforeInitialize | 需要验证或限制池创建参数时 | 低 |
afterInitialize | 需要在创建池后设置状态时 | 低 |
beforeAddLiquidity | 需要限制或转换 LP 存款时 | 中 |
afterAddLiquidity | 需要跟踪 LP 仓位或分发奖励时 | 低 |
beforeRemoveLiquidity | 需要设置锁定期或收取退出费用时 | 高 |
afterRemoveLiquidity | 需要为记账跟踪仓位移除情况时 | 低 |
beforeSwap | 需要修改交换行为、应用动态费用或阻止交换时 | 高 |
afterSwap | 需要观察最终交换状态以供预言机或记账使用时 | 中 |
beforeDonate | 需要限制哪些地址可以向池捐赠时 | 低 |
afterDonate | 需要跟踪捐赠事件时 | 低 |
beforeSwapReturnDelta | 实现自定义 AMM 曲线或 JIT 流动性时(严重:NoOp 攻击途径——见 v4-security-foundations) | 严重 |
afterSwapReturnDelta | 需要从交换输出中提取钩子费用时 | 高 |
afterAddLiquidityReturnDelta | 需要在存款时调整 LP 代币数量 | 高 |
afterRemoveLiquidityReturnDelta | 需要调整提款金额时 | 高 |
地址编码说明:权限以位的形式编码在钩子合约的已部署地址中。部署时必须在地址中设置正确的位,否则 PoolManager 将回滚。使用 HookMiner(来自 v4-periphery)挖掘能生成正确位模式地址的盐值。部署后绝不可更改权限,因为地址不可变。
实用工具库选择
生成的 hook 可选包含三个实用工具库。只包含 hook 逻辑实际用到的库。
| 库 | 包含条件 |
|---|---|
currencySettler | hook 会在自身与 PoolManager 之间转移代币(例如,自定义记账、收取费用) |
safeCast | hook 执行的算术运算在不同整数类型转换时可能溢出 |
transientStorage | hook 需要在单笔交易内的回调之间传递数据,但不将数据持久化到存储中(要求 EVM Cancun 或更高版本,Solidity >= 0.8.24) |
指南:
- 启用
beforeSwapReturnDelta、afterSwapReturnDelta、afterAddLiquidityReturnDelta或afterRemoveLiquidityReturnDelta时,几乎总是需要currencySettler——它提供settle和take辅助函数,用于实现正确的sync → transfer → settle顺序。 transientStorage是一种节省 Gas 的替代方案,可用于存储交易内状态。 使用它可以将标志或值从beforeSwap传递到afterSwap,避免为冷 SSTORE 支付 20k Gas。- 只要计算涉及从
int256/uint256转换得出的金额,就建议使用safeCast, 特别是在计算费用时。
份额配置
shares 选项控制生成的 hook 是否会发行代表用户份额的代币
(例如,hook 自有流动性池中的 LP 头寸)。
| 选项 | 说明 | 适用场景 |
|---|---|---|
false | 无份额代币——hook 不会跟踪已存入资产的所有权 | 不持有用户资金的简单 hook |
ERC20 | 同质化份额代币——一个代币代表用户对 hook 持有的所有资产的按比例所有权 | 由 hook 管理、份额可互换的流动性池 |
ERC6909 | 多代币(精简版)——一个合约管理多个代币 ID,开销低于 ERC1155 | hook 高效管理多个不同资产类别 |
ERC1155 | 多代币(标准版)——完整支持 ERC1155,并支持元数据 URI | hook 需要广泛兼容钱包和交易市场 |
权衡:
false:字节码最小,无份额记账开销;适用于费用 hook 和预言机。ERC20:最简单的同质化份额;具备良好的 DeFi 可组合性(例如,可用作抵押品)。ERC6909:采用精简接口的高效多代币;新协议设计的首选方案。ERC1155:生态支持最广(钱包、区块浏览器、NFT 交易市场);每次转账的 Gas 成本高于 ERC6909。
访问控制选项
access 选项决定生成的钩子的构造函数形式和管理界面。
| 选项 | 构造函数形式 | 适用场景 |
|---|---|---|
ownable | constructor(IPoolManager, address initialOwner) | 单个所有者控制所有管理功能 |
roles | constructor(IPoolManager, address admin) | 通过细粒度权限设置多个角色(OpenZeppelin AccessControl) |
managed | constructor(IPoolManager, address authority) | 由外部权限合约管理权限(OpenZeppelin AccessManaged) |
使用建议:
ownable最简单——一个地址即可执行所有特权操作。适用于早期阶段的钩子和个人工具。roles通过 OpenZeppelinAccessControl添加ADMIN_ROLE、PAUSER_ROLE等角色。当不同团队成员需要不同权限时使用(例如,keeper 机器人可以更新费用,但不能升级合约)。managed将所有权限检查委托给单独的AccessManager合约。当你需要在多个合约间建立统一的治理层,或希望对管理操作设置时间锁时使用。
注意:更改
access选项会改变构造函数签名。请相应更新部署脚本和初始化逻辑。使用ownable时,请确保initialOwner不是零地址——自 v5 起,OpenZeppelin 的Ownable遇到零地址时会回滚。
钩子输入参考
某些钩子类型接受用于调整行为的数值配置输入。这些输入通过 MCP 工具调用中的 inputs 对象传入。
| 输入 | 类型 | 使用方 | 说明 |
|---|---|---|---|
blockNumberOffset | uint256 | AntiSandwichHook、LiquidityPenaltyHook | 三明治/JIT 检测窗口开启前的区块数 |
maxAbsTickDelta | int24 | AntiSandwichHook | 在 MEV 保护触发前,每个区块允许的最大 tick 变化量 |
对于不使用这些输入的钩子类型,请省略 inputs 字段或传入空对象 {}。向 MCP 工具传入不支持的输入不会导致错误,但这些值会被忽略。
MCP 工具调用(规范格式)
OpenZeppelin Contracts Wizard 提供了一个 generate_hook MCP 工具。以下是规范的 JSON 架构——根据上文各部分的决定填写每个字段,然后将此对象作为工具参数传入。
{
"hook": "BaseHook",
"name": "MyHook",
"pausable": false,
"currencySettler": true,
"safeCast": true,
"transientStorage": false,
"shares": { "options": false },
"permissions": {
"beforeInitialize": false,
"afterInitialize": false,
"beforeAddLiquidity": false,
"beforeRemoveLiquidity": false,
"afterAddLiquidity": false,
"afterRemoveLiquidity": false,
"beforeSwap": true,
"afterSwap": false,
"beforeDonate": false,
"afterDonate": false,
"beforeSwapReturnDelta": false,
"afterSwapReturnDelta": false,
"afterAddLiquidityReturnDelta": false,
"afterRemoveLiquidityReturnDelta": false
},
"inputs": {
"blockNumberOffset": 1,
"maxAbsTickDelta": 100
},
"access": "ownable",
"info": { "license": "MIT" }
}
字段说明:
hook:字符串——决策表中的 14 种钩子类型之一name:字符串——Solidity 合约名称(PascalCase,不含空格)pausable:布尔值——使用 OpenZeppelinPausable包装钩子;添加pause()/unpause()管理函数shares.options:false|"ERC20"|"ERC6909"|"ERC1155"access:"ownable"|"roles"|"managed"info.license:SPDX 许可证标识符——开源钩子使用"MIT"inputs:对于不使用blockNumberOffset/maxAbsTickDelta的钩子类型,请省略或传入{}
工作流:收集 → 配置 → 生成 → 保护
每次使用此技能时,都请按顺序执行以下步骤。
步骤 1:收集需求
询问用户(或根据上下文推断):
- Hook 的主要目标是什么?(映射到决策表。)
- Hook 需要拦截哪些生命周期事件?(映射到权限。)
- Hook 是否会持有或转移用户资金?(决定
currencySettler和shares。) - 谁管理 Hook?(单一所有者、基于角色的团队,还是外部治理?)
- Hook 是否需要在单笔交易的多个回调之间传递状态?
- 这是用于支持 EVM Cancun 的链吗?(
transientStorage的必需条件。)
步骤 2:选择 Hook 类型
使用决策表确定最合适的单一 Hook 类型。如果用户的目标对应多种类型,请解释取舍并让用户确认。记录选定的类型及原因。
步骤 3:配置全部六项决策
按最简决策清单逐项处理:
- 将
hook设为选定的类型。 - 设置每个权限标志——默认值为
false,仅在逻辑需要时启用。 - 根据工具库指南设置
currencySettler、safeCast、transientStorage。 - 根据 shares 指南设置
shares.options。 - 根据访问控制指南设置
access。 - 仅当 Hook 类型使用
blockNumberOffset或maxAbsTickDelta时,才设置inputs。
步骤 4:组装并调用 MCP 工具
根据步骤 3 构造 JSON 对象,并使用该对象调用 OpenZeppelin Contracts Wizard MCP 工具。 该工具会返回 Solidity 源代码——不会自动写入文件。
收到生成的代码后:
- 向用户展示代码。
- 说明生成代码中的关键部分(构造函数、
getHookPermissions、已启用的回调)。 - 说明任何所需的手动步骤(使用 HookMiner 挖掘地址;如果
access为roles或managed,则更新构造函数参数对应的部署脚本)。
步骤 5:落实安全基础要求
在部署代码之前,务必提醒用户并调用 v4-security-foundations:
- 确认所有已启用的回调都会检查
msg.sender == address(poolManager)。 - 审查所有已启用的
*ReturnDelta权限是否存在 NoOp 攻击风险。 - 确认每条执行路径上的 delta 会计总和均为零。
- 按照
v4-security-foundations执行完整的部署前审计清单。
重要说明
- 访问控制会改变构造函数的形式:选择
ownable会添加initialOwner参数;选择roles会添加admin参数;选择managed会添加authority参数。相应地更新部署脚本和工厂合约。 - 权限编码在 Hook 地址中:每个已启用的权限标志都对应 Hook 已部署地址低位字节中的特定比特位。PoolManager 会在每次回调时验证这些比特位。使用
v4-periphery中的HookMiner挖掘部署 salt,以生成匹配的地址。 - MCP 只返回代码——不会写入文件:生成的 Solidity 代码会以字符串形式返回。你必须自行将其写入磁盘(例如
packages/contracts/src/hooks/MyHook.sol)。
相关技能
v4-security-foundations— 生成后运行此项。 Uniswap v4 Hook 的安全审计:权限风险矩阵、NoOp 攻击模式、delta 会计、访问控制验证,以及完整的部署前审计清单。生成的 Hook 代码在完成此审查前绝不能部署。viem-integration— 部署生成的 Hook 合约,并使用 viem/wagmi 与其交互v4-sdk-integration— 通过 Uniswap v4 SDK 与已部署的 Hook 交互