Uniswap v4 Hook 解析:架构设计、常见漏洞与防护实践
2026-06-1718:10
Beosin
2026-06-17 18:10
Beosin
2026-06-17 18:10
收藏文章
订阅专栏

Uniswap v4 Hook 解析:架构设计、常见漏洞与防护实践

 

自 Uniswap v4 主网上线后,Hook 机制成为 DeFi 最受关注的创新之一。Base 链上的 memecoin 发射平台 Flaunch 利用 Hook 实现固定预售价格与自动清算上线机制;流动性协议 Bunni v2 用 Hook 构建可编程流动性与再抵押模型;今年 SATO、uPEG(Unipeg)、Slonks 等围绕 Hook 玩法的代币也在短期内创下数十倍涨幅。

 

在 Hook 生态繁荣的另一面,针对 Hook 实现缺陷的攻击也在显著上升。本文将从 Uniswap v4 的 Hook 机制入手,逐步分析其核心调用栈,帮助项目方理解其中可能存在的漏洞。


Uniswap v4 Hook 安全

 

1. 简介

 

Uniswap v4 相对 v3 最显著的架构变化就是引入了 Hook(钩子)机制:允许开发者将自定义合约挂载到流动性池的生命周期事件上,在 swap、加减流动性、初始化等节点注入任意逻辑。

 

v4 的几个关键变更如下:

 

- Singleton 模式:所有池子的状态由单一的 PoolManager 合约集中管理,不再为每个池子部署独立合约

- Flash accounting:交易过程中的中间余额变化只在 transient storage 中记账,仅在交易结束时统一结算

- Hook 机制:每个池子可以绑定一个 Hook 合约,PoolManager 会在关键节点(beforeInitialize、beforeSwap、afterAddLiquidity 等)回调该合约

- Hook 不可更换:一旦池子初始化完成,绑定的 Hook 地址永久固定 ( 池子绑定的 Hook 地址不可修改,但 Hook 合约自身是否可升级取决于其实现方式 )

 

在 v3 时期,开发者只需要信任 Uniswap 协议本身;而在 v4 时期,每个池子的安全性取决于它所绑定的 Hook。Hook 把 AMM 从一个固定的金融原语,拓展成了一个可编程的金融基础设施,但安全模型也从”协议级”碎片化到了”池子级”。

 

 

2. Hook 架构

 

2.1 PoolManager 与 unlock/callback 模型

 

v4 的核心合约是单例的 PoolManager。任何对池子的状态变更操作(swap、加减流动性)都必须先调用 PoolManager.unlock(),获得一次性的回调权限后,在 unlockCallback() 中完成具体动作。整个流程结束时,PoolManager 会验证账本是否平衡:

 

 

NonzeroDeltaCount != 0 时直接 revert,这是 v4 flash accounting 的核心约束。任何 Hook 在执行过程中可以临时让账目失衡,但在交易结束前必须自行 settle,否则整笔交易回滚。

 

 

每个池子由 PoolKey 结构唯一标识,其中包含 hooks 字段:

 

 

PoolId 由 keccak256(PoolKey) 计算得出,因此 hooks 地址不同会产生不同的池子。这同时意味着,PoolManager 不会校验某个 Hook 地址是否曾被用于其他池子,同一个 Hook 合约可以被多个池子同时绑定。

 

 

2.2 Hook 权限位编码在地址中

 

 

v4 的一个反直觉设计是:Hook 的权限不是由合约内部的某个变量决定的,而是由 Hook 合约的部署地址决定的。

 

PoolManager 通过检查 Hook 地址的低 14 个比特位来判断该 Hook 是否需要在某个生命周期点被调用:

 

 

例如 BEFORE_SWAP_FLAG = 1 << 7。若 Hook 地址第 7 位为 1,PoolManager 在 swap 前会调用该 Hook 的 beforeSwap();否则即使 Hook 合约实现了 beforeSwap(),也永远不会被 PoolManager 调用。

 

这意味着 Hook 部署时必须通过 CREATE2 + salt 计算出地址,构造出一个低位与目标权限完全一致的地址。Uniswap 官方提供了 HookMiner 工具用于此目的:

 

 

权限位与函数实现不一致时会产生两类问题:

 

 

(1) 实现了某个 hook 函数,但地址未编码对应权限位——PoolManager 永远不会调用该函数,逻辑形同虚设

 

(2) 地址编码了某个权限位,但 hook 没有实现对应函数——PoolManager 在回调时可能发生 revert 实现 DOS 或返回值校验失败,导致相关操作无法执行。

 

这同时是 Hook 升级的天然障碍:若 Hook 通过代理可升级,部署地址在升级时不变,因此升级后只能修改既有 hook 函数的实现,而不能新增 hook 类型。要预留未来扩展能力,必须在初始部署时把所有可能用到的权限位预先挖出来。

 

 

2.3 BaseHook 与一个被普遍忽视的访问控制陷阱

 

Uniswap v4 periphery 旧版本提供的 BaseHook 抽象合约,开发者可以继承它来实现自定义 Hook。BaseHook 的一个重要作用是为 unlockCallback() 函数提供 onlyPoolManager 修饰符:

 

 

但是——这里存在一个非常容易被忽视的设计陷阱——早期版本的 BaseHook 仅为 unlockCallback 添加了 onlyPoolManager,对其他 hook 回调函数(beforeSwap、afterSwap、beforeAddLiquidity 等)没有任何保护。这些函数的访问控制必须由 Hook 开发者自己显式添加。

 

 

3. Hook 生命周期代码走读

 

以一次 exact-input swap 为例,下面分析从用户发起交易到结算的完整调用栈。

 

3.1 池子初始化与 Hook 绑定

 

任何人都可以调用 PoolManager.initialize() 创建新池子:

 

 

 

isValidHookAddress 只校验地址权限位与 fee 字段的兼容性,不校验 Hook 是否已经在其他池子中被使用,也不校验该 Hook 是否”愿意”接受这个 PoolKey。如果 Hook 设计时没有在 beforeInitialize 里加白名单或单池绑定逻辑,任何人都可以构造一个使用相同 Hook、但 token pair 任意的新池子并触发 Hook 后续的所有回调。

 

3.2 beforeSwap 与 BeforeSwapDelta

 

swap 流程入口是 PoolManager.swap(),它在执行核心 swap 逻辑前会调用 Hooks.beforeSwap():

 

 

beforeSwap 的返回值是一个三元组 (bytes4, BeforeSwapDelta, uint24):

- bytes4:必须等于 IHooks.beforeSwap.selector,否则 PoolManager 直接 revert

- BeforeSwapDelta:Hook 在本次 swap 中对 specified token 和 unspecified token 的 delta 调整

- uint24:动态 LP 费率覆盖值(仅当池子开启动态费率时生效)

 

BeforeSwapDelta 是 int256 的别名,高 128 位是 specified token 的 delta(即用户指定数量的那个 token),低 128 位是 unspecified token 的 delta:

 

 

需要注意,BeforeSwapDelta 的语义是 Hook 收取费用应当返回正值,Hook 退还代币应当返回负值。开发者很容易把符号搞反;同时,specified 与 unspecified 的对应关系取决于 params.zeroForOne 与 amountSpecified 的正负,写法稍有不慎就会出现 token 错位。

 

PoolManager 会将 beforeSwap 返回的 specifiedDelta 直接累加到 amountToSwap 上:

 

 

这一行隐含了一个关键语义:Hook 可以截留 swap 数额。当 hookDeltaSpecified 等于 -params.amountSpecified 时,amountToSwap 直接归零,相当于 Hook 完全接管了这笔 swap——这就是所谓的 Async Hook 或 Custom Curve Hook。

 

Async Hook 是 v4 中风险最高的一类设计模式:它本质上是用 Hook 自己的逻辑替换了 Uniswap 的 swap 逻辑。如果 Hook 存在漏洞或本身就是恶意的,用户资金将不再受到 Uniswap 原生定价逻辑的约束,而主要依赖 Hook 自身实现的正确性。

 

3.3 Delta 结算与 NonzeroDeltaCount

 

beforeSwap 和 afterSwap 返回的 delta 不会立即触发转账,而是被记录到 PoolManager 的内部账本中:

 

 

每当一笔 token 的累计 delta 从零变为非零,NonzeroDeltaCount 自增;变回零时自减。如 2.1 所述,unlock() 结束时若 NonzeroDeltaCount != 0,整笔交易 revert。

 

Hook 通过 settle()(向 PoolManager 转账)和 take()(从 PoolManager 取出)两个动作平衡自己的 delta:

 

 

这套机制带来的安全语义是清晰的:所有人最终都必须把账平掉。但它只保证了”账目守恒”,并不保证”账目正确”。如果 Hook 在 beforeSwap 中返回了一个被恶意构造的 delta,PoolManager 会忠实地按这个 delta 记账,只要最终被 settle 平掉,交易就成功——即使这意味着 Hook 可以通过伪造业务状态,使系统错误地认为攻击者拥有某些资产权益,而 PoolManager 无法识别这种业务层面的错误。

 

此前 Cork Protocol 安全事件便是因为其 Hook 存在漏洞,而在被攻击前它已经经过了四家审计公司的审计。事后复盘我们发现:

 

- 四家审计中有三家的 scope 不包含 CorkHook 合约

- 唯一审计了 CorkHook 的一家识别出了部分代码问题并提交了改进建议,但未完全覆盖到访问控制问题

- 另有一家审计方在其报告中明确建议:“an interesting follow-up engagement would be to prove the invariants for the CorkHook functions that are being invoked by different components verified within the scope of this engagement”。这一建议从事后复盘角度看具有较强针对性。

 

这暴露出 v4 Hook 时代一个新的审计盲区:协议复杂度的爆炸式增长导致 scope 划定本身成为一个安全决策。Hook 与协议其他合约的交互链路非常长,单独审 Hook 合约不足以发现跨合约的组合性问题;反过来,审周边合约而把 Hook 放在 scope 外,则会漏掉 v4 时代最大的攻击面。

 

5. 反思

 

把协议机制和 Cork 攻击复盘并列来看,可以归纳出 v4 Hook 安全模型的几个核心要点:

 

(1) 如果 Hook 回调函数依赖 PoolManager 提供的调用上下文,应显式限制仅由 PoolManager 调用。BaseHook 不会替开发者做这件事,这是 v4 与一般合约审计经验最容易冲突的设计陷阱

 

(2) Hook 与池子的绑定关系不受 PoolManager 限制。开发者必须自行在 beforeInitialize 中实现池子白名单或单池绑定

 

(3) Hook 地址的权限位必须与函数实现严格一致。计算出的地址应当把未来可能扩展的所有权限位预先包含进去

 

(4) Async / Custom Curve Hook 本质上是完全自定义的 swap 实现。它没有任何 Uniswap 协议层面的保护,必须按”完全自治的金融合约”标准审计

 

(5) Delta 会计的”守恒”不等于”正确”。NonzeroDeltaCount == 0 只能保证账本最终平衡,不能保证账本的内容未被恶意操纵

 

(6) 跨市场的代币类型混淆是 v4 时代的新型攻击面。当协议允许用户创建市场时,对代币的语义校验是必须的,不能仅依赖接口检查

 

每个 Hook 都是一个独立的信任域,每个池子的安全性由其绑定的 Hook 决定。Hook 安全审计的复杂度因此也不再是”审一份代码”,而是”审一个完整的子协议”——这一变化对项目方和审计方都意味着方法论上的升级。

 

参考资料

(1) Cork Protocol. “May 28 2025 Exploit Post-Mortem.” 2025-06-04. https://www.cork.tech/blog/post-mortem

(2) OWASP Smart Contract Security Top 10 2026, SC01: Access Control Vulnerabilities. https://scs.owasp.org/sctop10/SC01-AccessControlVulnerabilities/

(3) Uniswap v4 Whitepaper. https://app.uniswap.org/whitepaper-v4.pdf

(4) Uniswap v4-core. https://github.com/Uniswap/v4-core

(5) Uniswap v4-periphery. https://github.com/Uniswap/v4-periphery

 

Beosin 作为全球最早一批从事形式化验证的区块链安全公司,主打”安全 + 合规“全生态业务,在全球 10 多个国家和地区设立了分部,业务涵盖项目上线前的代码安全审计、项目运行时的安全风险监控与阻断、被盗追回、虚拟资产反洗钱(AML)以及符合各地监管要求的合规评估等“一站式”区块链合规产品 + 安全服务。欢迎点击公众号留言框,与我们联系。

【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。

专栏文章
查看更多
数据请求中

推荐专栏

数据请求中

一起「遇见」未来

DOWNLOAD FORESIGHT NEWS APP

Download QR Code