January 8, 2025
Protecting Uniswap v4 Against Unknown Attacks
Part One: A Threat Model
With over $2.5 trillion in all-time volume, Uniswap Protocol is a leading force in DeFi innovation. From its origins as one of the first automated market makers (AMM) to v3’s concentrated liquidity, Uniswap has continuously redefined on-chain trading and remains a cornerstone of the decentralized finance ecosystem.
Uniswap v4 builds on this legacy by introducing key innovations—aggregating all pools into a single contract for reduced gas costs and enabling custom swap logic through hooks. While these advancements bring significant benefits, they also introduce new security challenges.
In this blog, we’ll explore the threat model for Uniswap v4, analyze potential attack vectors, and evaluate how the protocol's test suite addresses them.
The Critical Role of Threat Modeling in Protocol Security
Before delving into the specifics of Uniswap v4, it is essential to understand why threat modeling forms the cornerstone of any security analysis for DeFi systems.
Threat modeling serves as a blueprint for identifying risks and safeguarding assets in blockchain protocols. It begins with a comprehensive understanding of the protocol’s purpose, core functionalities, and key players. This includes mapping out assets, such as user funds, and determining their control under varying circumstances. For decentralized exchanges (DEXs), lending pools, and bridges, protecting user funds and ensuring solvency are top priorities.
New features often introduce unforeseen risks. Actions by users, administrators, or third-party integrations can lead to unpredictable interactions, creating potential vulnerabilities. Effective threat modeling scrutinizes these scenarios, evaluates worst-case outcomes, and identifies where the protocol might fail under stress or malicious intent.
Key components of threat modeling include:
- Defining Invariants: Establishing rules that specify disallowed behaviors and essential guarantees (e.g., solvency).
- Analyzing Interaction Patterns: Identifying potential vulnerabilities arising from user actions or external integrations.
- Applying Formal Verification: Using invariants as a basis to mathematically prove protocol security or identify vulnerabilities through counterexamples.
By providing a structured approach to understanding and mitigating risks, threat modeling lays the foundation for secure and resilient systems.
Uniswap v4 Key Innovations
Uniswap v3 revolutionized constant-product pools by introducing concentrated liquidity positions. Building on this foundation, v4 introduces three key innovations.
- Aggregation of pools into a single contract: Uniswap v4 introduces the Pool Manager, a central contract that consolidates the logic for all pool types and aggregates their liquidity under a single address. This architecture reduces the need for multiple token transfers across separate pool contracts, offering potential gas savings for external interactions.
- Introduction of hooks: The core internal operations of the pool—such as depositing, withdrawing liquidity, and swapping—use the established code from Uniswap v3. With v4, however, pool deployers can modify these operations through hooks, enabling custom behaviors like specialized pricing curves or dynamic fee structures.
- Flash accounting: Flash balancing is a mechanism designed to minimize direct token transfers during interactions within Uniswap pools. Instead of transferring tokens for each operation, flash balances are updated for every user and token based on the outcome of the transaction. To ensure integrity, any transaction in the Pool Manager must leave all flash balances for accounts and tokens at zero by its final step. Any operations involving the protocol must begin with a call to lock() and conclude with unlock(), where account settlement is performed.
This approach leverages transient storage, a new feature in the Solidity compiler, which acts as a type of memory unique to each contract. Transient storage is cheaper to modify than standard storage and resets to zero at the end of every transaction, making it an efficient choice for implementing flash accounting.
Flash accounting significantly improves routing efficiency between pools by minimizing token transfers to only the end of a transaction. This mechanism integrates seamlessly with the customizable hooks feature, enabling any AMM to join the system and benefit from its gas-saving design.
In other words, the default pool (with empty hooks) in Uniswap v4 functions in the same way a Uniswap v3 pool does. One may interact seamlessly with multiple pools in the same transaction, and delay the token transfers (setting the balances) to the very end. Additionally, customizable hooks enable the pricing curves of pools to be adjusted, allowing for alternatives to the traditional constant-product model.
This is just the tip of the iceberg. The new upgrade introduces many paths and aspects to explore. We will not provide further deep analysis of the protocol and examples but rather focus on its security aspects.
What Could Go Wrong?
The more complex the system, the higher the chances that something will go wrong. However, many vulnerabilities of complex systems can be prevented by designing the codebase well. This applies not only to clearly written code but also to architecting how the system's properties will be enforced. The Uniswap team did an excellent job in that regard.
Wrong accounting
As previously discussed, every transaction in the system is deemed successful only if the flash balances for all tokens and addresses return to zero. Positive and negative flash balances are reconciled by either receiving tokens from or paying tokens to the protocol, respectively.
Without adequate bound safety checks, the protocol might incorrectly assume that a user is entitled to an inflated token balance, leading to the misallocation of assets. For example, during a withdrawal operation, an error in the calculation could result in the user receiving an excessive balance of two tokens when burning liquidity tokens. In such a case, the protocol might erroneously allocate this surplus to the user. When flash balances are finalized, the excess would effectively be deducted from the rightful holdings of other liquidity providers.
Flash accounting correctness enforcement is designed very well. At the end of a successful interaction with Uniswap v4, the unlock call ensures all user debts and credits are fully settled. If any discrepancies remain, such as outstanding credits or debits, the session reverts to prevent incorrect accounting. Flash accounting operations are strictly confined to the unlock invocation, enabling seamless reversion of all operations if settlement conditions are not met.
So, we know the introduction of flash accounting does not introduce additional risks, provided that:
- Credits and debits are correctly handled within the Flash accounting system, ensuring that no new balances are created unintentionally. Responsibility for enforcing this lies primarily with the implementation of the pools and hooks, as discussed in the “Pool Solvency Independence” section below.
- Flash accounting actions occur only while the protocol state is unlocked. This ensures that the invariant of no outstanding credits or debts can be maintained at the end of every operation.
- The system enforces the invariant that all credits and debts are resolved before the session concludes.
The second and third properties of the v4 codebase can be relatively easily verified, either by hand or using Certora’s Prover.
Malicious hooks
The new custom hooks feature enables anyone to deploy a pool with an associated hook contract, allowing additional functionality to be integrated into the pool’s core operations, such as swaps and liquidity modifications. These hooks can, for instance, introduce extra fees for liquidity deposits or modify the pricing curve during swap operations.
While hooks open up possibilities for innovation, they also present significant risks, as a hook contract can execute arbitrary logic. This means a malicious hook could potentially behave in harmful ways—for example, redirecting all swap funds to itself. Users must exercise caution and understand the behavior of custom pools. We strongly recommend interacting only with hooks that have undergone professional auditing to ensure their trustworthiness and transparency.
Even if a hook behaves maliciously, it cannot create arbitrary token balances; the user's and the pool's balances constrain its actions. Essentially, the hook acts as another participant in the pool, operating alongside the end user and sharing the same flash accounting framework.
This means the hook can only redistribute the user’s existing balance within the contract without generating new tokens out of thin air. For example, if a hook adds X tokens to itself, the user must account for this and effectively pay the additional X tokens from their balance.
If that hadn’t been true, anyone could have implemented a hook that simply overrides the basic Uniswap v3 accounting math and would drain funds from the protocol.
Pool solvency independence
Unlike Uniswap v3, which isolates funds within individual pools, Uniswap v4 consolidates all pool assets into a single contract. While this design reduces operational overhead and transaction costs, it introduces a critical concern: a flawed or malicious operation in one pool could potentially compromise funds from another. To mitigate this risk, it is essential to ensure that each pool maintains separate reserves and that any misbehavior—particularly involving custom hooks—remains confined to the affected pool without impacting others.
Uniswap v4 does not explicitly store pool reserve balances. Instead, reserves are inferred from the underlying v3 pool’s state, which calculates liquidity positions based on the current price and splits reserves between the two tokens in the pair. This design avoids the need for direct balance storage while still ensuring solvency through the v3 logic.
Storing balances would require costly state changes for nearly every pool operation, such as swaps or liquidity management. By relying on v3’s mechanism to enforce solvency, Uniswap avoids these overheads. However, this reliance introduces a risk: if the v3 logic has any flaws—no matter how obscure—that allow a single pool to become insolvent, funds from other pools could potentially be stolen, as the system does not enforce strict reserve separation.
As previously noted, hooks in Uniswap v4 can perform nearly arbitrary swaps. However, unlike the native v3 pool implementation, v4 enforces checks to ensure that hook deposits cannot be negative, safeguarding the system’s overall solvency. The system maintains its solvency by guaranteeing all deposits are correctly accounted for—whether as liquidity deposits in the v3 pool logic or within the balances of users and hooks.
From the user’s perspective, a pool in Uniswap v4 consists of the built-in v3 logic combined with the hook. In this view, the solvency of a pool is not entirely independent of other pools, as issues within a hook could potentially violate solvency assumptions. This underscores the importance of trusting the hook's implementation. It is only guaranteed that one v4 pool cannot impact another if their sole interaction is through Uniswap v4 itself.
Reentrancy
Reentrancy in smart contracts is a common pattern where external calls are invoked inside a standard function call in a contract that allows to take advantage of an “intermediate” state of the contract. The externally called contract could be entering the same original contract repeatedly and exploiting some bad state of the system before the function ends its flow. Reentrancy locks are already very well-known and ubiquitous mechanisms to disallow such behavior and could be easily implemented.
In Uniswap v4, reentrancy is pivotal in enabling flash operations, which are intentionally triggered within the unlock() callback. This design ensures that the contract can revert all actions, such as swapping, depositing, or withdrawing if their combined effects do not balance correctly.
Despite its utility, allowing reentrancy carries inherent risks. The core issue with reentrancy as an exploit vector lies not in the act of invoking external contracts but in doing so while the system's state is inconsistent. This inconsistency may lead to incorrect assumptions by the EVM or cached states that fail to account for changes during the reentrant call.
Reentrancy-resistant systems aim to ensure that a contract's state is always correct when yielding control to external contracts. In Uniswap v4, this principle is applied rigorously, particularly when interacting with hooks or transferring tokens. Hooks are designed to execute either at the start of an operation before any state changes occur, or at the end after the operation’s effects have been fully realized. This approach minimizes the risk of inconsistencies in the state.
Uniswap v4 limits external interactions to well-defined entry points: beforeActionX, afterActionX, and unlock() callbacks. Before a beforeActionX callback, no state modifications have taken place, so the system is guaranteed to still be consistent. Similarly, after an afterActionX callback, the state and flash accounting balances are not altered based on prior balance readings, preserving system integrity.
Balance updates in Uniswap v4 are inherently reentrancy secure since they do not affect the system's behavior apart from causing reverts when balances are insufficient. Hooks are also safe, provided they are not both reentrant and rely on flash balance readings. Finally, the unlock() callback avoids caching or leaving the state in an incoherent form, so it is also robust against reentrancy exploits.
When reasoning about reentrant systems, we need to inductively reason about the preservation of state invariants. That is, we do not care about the total number of invocations or the total state change that occurred, but rather about the effect of any single action on the arbitrary state it started from.
Conclusion
Uniswap v4’s innovative features bring enhanced flexibility and efficiency to DeFi trading but also introduce new security challenges. Through comprehensive threat modeling, we’ve identified key risks and outlined the mechanisms designed to mitigate them.
In the next part of this series, we’ll explore how formal verification is applied to address these challenges, providing mathematical assurance of protocol security and reinforcing user trust.
Stay tuned for Part Two: What We Learned from Uniswap v4 Audits
Read More About Uniswap v4 Security from our latest Uniswap core verification report.