Uniswap v4 Hook Security: Architecture, Common Vulnerabilities, and Best Practices

iconMetaEra
Share
AI summary iconSummary
Uniswap v4 enables programmable liquidity through Hooks, but due to vulnerabilities such as permission encoding and access control flaws, it has become a prime target for attacks; be wary of risks arising from improper use of Async Hooks and accounting logic.

Author and source: Beosin

Since the mainnet launch of Uniswap v4, the Hook mechanism has become one of the most talked-about innovations in DeFi. On the Base chain, the memecoin launch platform Flaunch uses Hooks to implement fixed presale pricing and automated listing liquidation mechanisms; the liquidity protocol Bunni v2 employs Hooks to build programmable liquidity and re-collateralization models; this year, tokens centered around Hook-based strategies such as SATO, uPEG (Unipeg), and Slonks have achieved multi-fold gains within a short period.

Alongside the thriving ecosystem of Hook, attacks exploiting defects in Hook implementations are significantly increasing. This article begins with Uniswap v4’s Hook mechanism and gradually analyzes its core call stack to help projects understand potential vulnerabilities.

Uniswap v4 Hook Security

1. Introduction

The most significant architectural change in Uniswap v4 compared to v3 is the introduction of the Hook mechanism: it allows developers to attach custom contracts to liquidity pool lifecycle events, enabling arbitrary logic to be injected at points such as swaps, adding or removing liquidity, and initialization.

The key changes in v4 are as follows:

Singleton pattern: The state of all pools is centrally managed by a single PoolManager contract, eliminating the need to deploy separate contracts for each pool.

- Flash accounting: Intermediate balance changes during the transaction are recorded only in transient storage and settled collectively upon transaction completion.

- Hook mechanism: Each pool can be linked to a Hook contract, and PoolManager will callback this contract at key points (such as beforeInitialize, beforeSwap, afterAddLiquidity, etc.).

- Hook cannot be replaced: Once the pool is initialized, the bound Hook address is permanently fixed (the Hook address bound to the pool cannot be modified, but whether the Hook contract itself can be upgraded depends on its implementation).

In the v3 era, developers only needed to trust the Uniswap protocol itself; in the v4 era, the security of each pool depends on the Hook it is bound to. Hooks transform the AMM from a fixed financial primitive into a programmable financial infrastructure, but the security model has shifted from “protocol-level” to “pool-level” fragmentation.

2. Hook Architecture 2.1 PoolManager and unlock/callback Model

The core contract in v4 is the singleton PoolManager. Any state-changing operation on a pool (swap, adding or removing liquidity) must first call PoolManager.unlock() to obtain a one-time callback permission, then complete the specific action within unlockCallback(). At the end of the entire process, PoolManager verifies that the ledger is balanced:

Revert immediately when NonzeroDeltaCount != 0; this is a core constraint of v4 flash accounting. Any hook may temporarily imbalance the ledger during execution, but it must settle the imbalance before the transaction ends, or the entire transaction will be reverted.

Each pool is uniquely identified by a PoolKey structure, which includes the hooks field:

The PoolId is calculated using keccak256(PoolKey), so different hook addresses result in different pools. This also means that the PoolManager does not verify whether a hook address has been used for another pool; the same hook contract can be bound to multiple pools simultaneously.

2.2 Hook Permission Bit Encoding in Address

One counterintuitive design in v4 is that a Hook's permissions are not determined by a variable within the contract, but rather by the deployment address of the Hook contract.

PoolManager determines whether a Hook needs to be invoked at a specific lifecycle point by checking the lowest 14 bits of the Hook address:

For example, BEFORE_SWAP_FLAG = 1 << 7. If the 7th bit of the Hook address is set to 1, the PoolManager will call the Hook's beforeSwap() method before the swap; otherwise, even if the Hook contract implements beforeSwap(), it will never be called by the PoolManager.

This means that during Hook deployment, the address must be calculated using CREATE2 + salt to construct an address whose lower bits exactly match the target permissions. Uniswap provides the HookMiner tool for this purpose:

When permission bits are inconsistent with function implementations, two types of issues can arise:

(1) A hook function has been implemented, but the address is not encoded with the corresponding permission bit—PoolManager will never call this function, making the logic effectively meaningless.

(2) The address encodes a permission bit, but the hook does not implement the corresponding function—this may cause PoolManager to revert during the callback, resulting in a DOS attack or failed return value validation, preventing the associated operation from executing.

This also serves as a natural limitation of Hook upgrades: if Hooks are upgradable via a proxy, the deployment address remains unchanged during the upgrade, meaning only the implementation of existing hook functions can be modified, not new hook types added. To reserve future extensibility, all potential permission bits must be pre-allocated at the initial deployment.

2.3 BaseHook and a Commonly Overlooked Access Control Trap

The BaseHook abstract contract provided by the Uniswap v4 periphery allows developers to inherit it to implement custom hooks. An important role of BaseHook is to provide the onlyPoolManager modifier for the unlockCallback() function:

However—there is a very easily overlooked design trap—early versions of BaseHook only applied onlyPoolManager to unlockCallback and provided no protection for other hook callbacks (such as beforeSwap, afterSwap, beforeAddLiquidity, etc.). Access control for these functions must be explicitly implemented by the hook developer.

3. Review of Hook Lifecycle Code

Using an exact-input swap as an example, the following analyzes the complete call stack from the user initiating the transaction to settlement.

3.1 Pool Initialization and Hook Binding

Anyone can call PoolManager.initialize() to create a new pool:

isValidHookAddress only verifies the compatibility of the address permission bit and the fee field; it does not check whether the Hook is already in use by another pool, nor does it verify whether the Hook is willing to accept this PoolKey. If the Hook was not designed with a whitelist or single-pool binding logic in beforeInitialize, anyone can construct a new pool using the same Hook with any arbitrary token pair and trigger all subsequent Hook callbacks.

3.2 beforeSwap and BeforeSwapDelta

The swap flow entry point is PoolManager.swap(), which calls Hooks.beforeSwap() before executing the core swap logic:

The return value of beforeSwap is a tuple (bytes4, BeforeSwapDelta, uint24):

- bytes4: Must equal IHooks.beforeSwap.selector; otherwise, PoolManager reverts immediately

- BeforeSwapDelta: The delta adjustment made by the hook for the specified and unspecified tokens in this swap

- uint24: Dynamic LP fee rate coverage value (effective only when dynamic fees are enabled for the pool)

BeforeSwapDelta is an alias for int256, where the upper 128 bits represent the delta of the specified token (the token the user specified), and the lower 128 bits represent the delta of the unspecified token:

Note that the semantics of BeforeSwapDelta require the Hook to return a positive value when charging fees and a negative value when refunding tokens. Developers often reverse the sign incorrectly. Additionally, the correspondence between specified and unspecified depends on the signs of params.zeroForOne and amountSpecified; a slight mistake in implementation can lead to token misalignment.

PoolManager will directly add the specifiedDelta returned by beforeSwap to amountToSwap:

This line implies a key semantic: the Hook can intercept the swap amount. When hookDeltaSpecified equals -params.amountSpecified, amountToSwap is set directly to zero, meaning the Hook fully takes over the swap—this is known as an Async Hook or Custom Curve Hook.

The Async Hook is the highest-risk design pattern in v4: it essentially replaces Uniswap’s swap logic with the Hook’s own logic. If the Hook contains vulnerabilities or is malicious, user funds will no longer be protected by Uniswap’s native pricing logic and will instead rely primarily on the correctness of the Hook’s implementation.

3.3 Delta Settlement and NonzeroDeltaCount

The deltas returned by beforeSwap and afterSwap do not trigger immediate transfers; instead, they are recorded in the PoolManager's internal ledger:

Whenever the cumulative delta of a token changes from zero to non-zero, NonzeroDeltaCount increments; when it returns to zero, it decrements. As described in 2.1, if NonzeroDeltaCount is not zero when unlock() ends, the entire transaction is reverted.

Hook balances its delta through two actions: settle() (transferring to PoolManager) and take() (withdrawing from PoolManager):

The security semantics of this mechanism are clear: everyone must eventually settle their accounts. However, it only guarantees “account conservation,” not “account correctness.” If a Hook returns a maliciously crafted delta in beforeSwap, the PoolManager will faithfully record that delta as long as it is ultimately settled—meaning the transaction succeeds even if the Hook fabricates business state to make the system incorrectly believe the attacker holds certain asset rights, which the PoolManager cannot detect at the business logic level.

Previously, the Cork Protocol security incident occurred due to a vulnerability in its Hook, despite having been audited by four auditing firms prior to the attack. Upon post-incident review, we found:

Three out of four audits did not include the CorkHook contract in their scope.

The only auditor of CorkHook identified some code issues and submitted improvement suggestions, but did not fully address access control issues.

Another auditor explicitly recommended in their report: "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." This recommendation, viewed in hindsight, is highly targeted.

This reveals a new audit blind spot in the v4 Hook era: the explosive growth in protocol complexity has made scope definition itself a security decision. The interaction chains between Hooks and other protocol contracts are extremely long; auditing the Hook contract in isolation is insufficient to uncover cross-contract compositional vulnerabilities. Conversely, auditing surrounding contracts while excluding the Hook from scope risks overlooking the largest attack surface of the v4 era.

5. Reflection

By comparing the protocol mechanism and the Cork attack side by side, several core principles of the v4 Hook security model can be identified:

(1) If the Hook callback function depends on the call context provided by PoolManager, it should explicitly restrict calls to be made only by PoolManager. BaseHook does not handle this for developers; this is the most common design pitfall that conflicts with general smart contract audit practices in v4.

(2) The binding relationship between Hook and pool is not restricted by PoolManager. Developers must implement a pool whitelist or single-pool binding themselves in beforeInitialize.

(3) The permission bits of the hook address must strictly match the function implementation. The calculated address should include all potential permission bits that may be extended in the future.

(4) The Async/Custom Curve Hook is a fully custom swap implementation. It has no Uniswap protocol-level protections and must be audited to the standard of a "completely autonomous financial contract."

(5) The "conservation" in delta accounting does not equal "correctness." NonzeroDeltaCount == 0 only ensures that the ledger is ultimately balanced, not that its contents have not been maliciously manipulated.

(6) Token type confusion across markets is a new attack surface in the v4 era. When protocols allow users to create markets, semantic validation of tokens is essential and cannot rely solely on interface checks.

Each Hook is an independent trust domain, and the security of each pool is determined by its associated Hook. As a result, the complexity of Hook security audits is no longer about reviewing a single codebase, but about auditing a complete sub-protocol—a shift that requires a methodological upgrade for both project teams and auditors.

Reference materials

(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, among the world’s first blockchain security companies specializing in formal verification, offers a comprehensive “Security + Compliance” ecosystem. With offices in over 10 countries and regions, Beosin provides end-to-end blockchain compliance and security services, including pre-launch code audits, real-time security monitoring and threat blocking, asset recovery, virtual asset anti-money laundering (AML), and regulatory-compliant assessments tailored to local requirements. Please reach out to us via the message box on our official account.

Disclaimer: The information on this page may have been obtained from third parties and does not necessarily reflect the views or opinions of KuCoin. This content is provided for general informational purposes only, without any representation or warranty of any kind, nor shall it be construed as financial or investment advice. KuCoin shall not be liable for any errors or omissions, or for any outcomes resulting from the use of this information. Investments in digital assets can be risky. Please carefully evaluate the risks of a product and your risk tolerance based on your own financial circumstances. For more information, please refer to our Terms of Use and Risk Disclosure.