Thorns in the Rose: Exploring Security Risks in Uniswap v4's Novel Hook Mechanism

This is the first article of our series exploring security risks in Uniswap v4’s hook mechanism! In this article, we provide a comprehensive overview and foundational understanding for our readers.

Thorns in the Rose: Exploring Security Risks in Uniswap v4's Novel Hook Mechanism

Uniswap v4 is on the way! The team has ambitious plans to introduce a host of new features[1], including (theoretically) an unlimited number of pools and dynamic fee rates for each trading pair, singleton, flash accounting, hooks, and support for ERC1155. Leveraging the transient storage introduced by EIP-1153, Uniswap v4 is expected to launch following Ethereum's Cancun upgrade.

Among these innovations, the hook mechanism has been garnering significant attention due to its powerful potential. It allows for the execution of specific code at particular points during the operation of a pool, considerably enhancing the extensibility and flexibility of the pools.

However, the hook mechanism can also be a double-edged sword. While it is powerful and flexible, the challenge of utilizing it securely cannot be overlooked. This complexity inevitably opens up new potential attack vectors. With an aim to contribute to the community from a security perspective, our goal is to present a series of articles examining the security issues and concerns related to this mechanism in a systematic manner. We believe that these insights will shed light on building secure Uniswap v4 hooks.

This article serves as the first in this series, providing a comprehensive overview and foundational understanding for our readers. Stay tuned for more insightful discussions!

The Mechanism of Uniswap v4

Before delving into the details, we need to have a basic understanding of the mechanism of Uniswap v4. According to the official announcement[1] and the whitepaper[2], hooks, the singleton architecture and flash accounting are three key features that enable pool customization and efficient routing across many pools.

Hooks

Hooks in v4 is designed to allow anyone to make tradeoff decisions through hooks which are contracts that run at various points of a pool action's lifecycle. By doing so, it is possible to customize pools that natively support dynamic fees, add on-chain limit orders, or act as a time-weighted average market maker (TWAMM) to spread out large orders over time.

Currently, there are eight hook callbacks, arranged into four groups (each group contains a pair of callbacks):

  • beforeInitialize/afterInitialize
  • beforeModifyPosition/afterModifyPosition
  • beforeSwap/afterSwap
  • beforeDonate/afterDonate

The below shows the flow of the swap hooks provided by the whitepaper[2].

Figure 1: Swap Hook Flow

The Uniswap team has provided some examples (e.g., the TWAMM Hook[3]) to demonstrate the use, and the participants in the community also make their contribution. The official doc[4] links to the repo Awesome Uniswap v4 Hooks[5], which collects more hook examples.

Singleton, Flash Accounting and Lock Mechanism

The singleton architecture and the flash accounting are designed to improve performance by reducing costs and ensuring efficiency. Specifically, it introduces a new singleton contract, where all pools live within a single smart contract. This singleton design relies on a PoolManager to store and manage all the states for all pools.

Differing from earlier versions of the Uniswap Protocol where operations such as swapping or liquidity addition involved direct token transfers, Uniswap v4 introduces flash accounting along with a lock mechanism.

Specifically, the lock mechanism operates in the following manner:

  1. A locker contract requests a lock on the PoolManager.
  2. The PoolManager adds the locker's address to lockData queue and invokes its lockAcquired callback.
  3. The locker executes its logic in the callback. During a locker's execution, its interactions with pools may result in non-zero currency deltas. However, by the end of the execution, all deltas must be settled to zero. Besides, if the lockData queue is not empty, only the last locker can perform operations.
  4. Afterward, the PoolManager checks the state of the lockData queue and currency deltas. Upon verification, the PoolManager will remove the locker.

In summary, the lock mechanism prevents concurrent access and ensures settlement. Lockers queue for locks, then executes via the lockAcquired callback. Before and after pool actions, specified hook callbacks are invoked. Finally, PoolManager checks the state.

This approach means that operations adjust an internal net balance (i.e., delta), rather than executing immediate transfers. Any modifications are recorded in the pool's internal balance, with actual transfers taking place at the end of the operation (i.e., lock). This process guarantees no outstanding tokens, thereby preserving solvency.

Due to the lock mechanism, an EOA (External Owned Account) cannot directly interact with the PoolManager. Instead, any interaction must go through a contract. The contract works as an intermediate locker to request a lock before any pool actions. There are mainly two contract interaction scenarios:

  • The locker contract comes from the official repository or was deployed by users. In these cases, we can view the interactions as being through a router.

  • The locker and hook are integrated into the same contract or controlled by a third-party entity. For this scenario, we can view the interactions as being through a hook. Hence, the hook plays the dual role of locker and callback handler.

Threat Models

The threat models need to be determined before discussing the corresponding security issues. Basically, there are certain considerations that arise when using hooks:

  • Threat model I: The hooks are benign but vulnerable.
  • Threat model II: The hooks are malicious.

In the following sections, we will discuss potential security issues/concerns based on the threat models.

Security Concerns in Threat Model I

Threat model I focuses on vulnerabilities related to hooks themselves. Obviously, this threat model assumes that the developers and their hooks are benign. However, existing known vulnerabilities of smart contracts could occur in the hooks as well. For example, if the hook is implemented as an upgradeable contract, it may suffer from some vulnerabilities that are similar to the UUPSUpgradeable vulnerability of OZ's library[6].

Given these considerations, we opt to concentrate on potential vulnerabilities specific to v4. Specifically, in Uniswap v4, hooks are customizable smart contracts capable of executing logic before or after core pool actions (including initialize, modifyPosition, swap, and donate). Hooks are expected to implement the standard hook interface, but they are also allowed to incorporate customized logic. Therefore, our scope is confined to the logic associated with the standard hook interface. We can then summarize how hooks are likely to employ these standard hook functions to pinpoint potential sources of vulnerabilities.

Broadly speaking, hooks can fall into two categories:

  • Hooks acting as custodians of user funds. In these cases, attackers may target the hook to transfer funds, leading to asset losses.
  • Hooks storing critical state data relied upon by users or other protocols. For these hooks, attackers might deliberately alter critical states. The erroneous states, when used by other users or protocols, introduce potential risks.

Note that hooks that don't fit into these two categories are not within the scope of our discussion.

Despite the limited scope, it's still not feasible to enumerate all the possibilities here. As there are no actual applications as of this writing, we decide to go through the Awesome Uniswap v4 Hooks repo for some insights.

After thoroughly examining the repository (with commit hash 3a0a444922f26605ec27a41929f3ced924af6075), we have identified several critical vulnerabilities. These primarily stem from the risky interactions between hooks, PoolManager, and external third parties. The vulnerabilities fall into two main categories: flawed access control and improper input validation. The findings are summarized in the table below:

# of flawed projects # of flawed access control # of improper input validation
8 6 2

Overall, we identified 22 pertinent projects (excluding some that seemed unrelated to Uniswap v4). Out of these, 8 (or 36%) were deemed vulnerable. Specifically, flawed access control was detected in 6 of these vulnerable projects, while 2 were prone to untrusted external calls.

Flawed Access Control

In this discussion, we focus on access control issues related to v4 that stem from v4's callback functions, which include 8 hook callbacks and the lock callback. Of course, other cases might need to be verified as well. However, these cases depend on the design, which is beyond the scope specified by our earlier discussion.

These functions should only be invoked by the PoolManager, not by other addresses (including EOAs and contracts). For example, consider a situation where a reward is distributed by the pool key. If the corresponding function can be invoked by arbitrary accounts, the reward could be claimed mistakenly.

Given this, it's crucial to establish robust access control mechanisms for hooks, especially since they can be invoked by parties other than the pool itself. Through careful management of access permissions, pools can substantially minimize the risk of unauthorized or malicious interactions with the hooks.

Improper Input Validation

In Uniswap v4, due to the lock mechanism, users must obtain a lock through a contract before executing any pool actions. This ensures that the contract currently interacting is the latest locker.

Despite this, there remains a potential attack scenario, i.e., untrusted external call due to improper input validation in some vulnerable hook implementations:

  • First, the hook does not validate the pool with which users intend to interact. This could be a malicious pool carrying fake tokens and executing harmful logic.
  • Second, some crucial hook functions allow arbitrary external invocations.

Untrusted external call is extremely dangerous because it can lead to various types of attacks, including the well-known reentrancy issues.

To exploit such vulnerable hooks, an attacker could register a malicious pool for their fake tokens, then invoke the hook to execute actions on the pool. When interacting with the pool, the malicious token logic hijacks the control flow to facilitate misbehaviors.

Mitigation for Threat Model I

To circumvent such issues with hooks, it's essential to validate interactions by appropriately enforcing necessary access control for sensitive external/public functions, and verification for input arguments. Besides, a reentrancy guard might be helpful to ensure that the hook cannot be repeatedly executed within the standard logic flow. By putting in place suitable safeguards, pools can mitigate the risks associated with this type of threat.

Security Concerns in Threat-Model II

In this threat model, the developers and their hooks are assumed to be malicious. Given the extensive range of possibilities, we have chosen to focus primarily on issues related to v4. Consequently, the key consideration is whether the provided hooks are able to handle the crypto assets that users have transferred or approved.

Based on the method of accessing the hooks, which determines the potential permissions granted to the hooks, we can classify hooks into two distinct categories:

  • Managed Hooks: Hooks are not the entry point. Users must interact with the hooks via a router, likely provided by Uniswap.
  • Standalone Hooks: Hooks are the entry point, allowing users to directly interact with them.
Figure 2: Examples of Malicious Hooks

Managed Hooks

In the case of Managed Hooks, the users' crypto assets (including native tokens and others) are transferred or approved to the router. Due to the balance check enforced by the PoolManager, it is not straightforward for malicious hooks to directly harvest these assets.

However, potential attack surfaces still exist. For instance, the fee management mechanism in v4 could potentially be manipulated by attackers via hooks.

Standalone Hooks

The situation becomes more complex when hooks are used as the entry point in the form of Standalone Hooks. In this scenario, hooks gain more power as users can interact with them directly. Theoretically, hooks can perform any action they want through this interaction.

Under the v4 scenario, the validation of the code logic is a critical point. The main concern is whether the code logic can be manipulated. Hooks can be implemented as upgradeable, meaning a hook that seems safe could potentially be upgraded to a malicious one later, posing a significant risk. This includes:

  • Upgradeable proxies, which can be directly exploited;
  • Equipped with self-destructing logic, which can be exploited due to the combined use of selfdestruct and create2.

Mitigation for Threat Model II

It's essential to assess whether the hooks are malicious. Specifically, for managed hooks, we should concentrate on the behavior of fee management; while for standalone hooks, the principal concern is whether they are upgradable or not.

Conclusion

In this article, we initially provide a brief summary of the core mechanisms of Uniswap v4 that relate to the security issues of v4 hooks. Following that, we define two threat models and offer a high-level discussion on their respective security issues. In the upcoming articles of this series, we will furnish detailed analyses of those security issues under each threat model. Stay tuned!

Reference

[1] Our Vision for Uniswap V4

[2] Uniswap v4 whitepaper draft

[3] Uniswap v4 TWAMM Hook

[4] Hook Examples

[5] Awesome Uniswap v4 Hooks

[6] UUPSUpgradeable Vulnerability Post-mortem

Read the Other Article in This Series

Sign up for the latest updates