A Uniswap V4 hook is a custom smart contract that plugs into the AMM lifecycle — before swaps, after swaps, before liquidity changes, after liquidity changes — giving developers unprecedented control over pool behavior. That power cuts both ways. One misconfigured hook and you've handed an attacker a direct line to every dollar in the pool. Uniswap V4 launched with over $1B in TVL migrated from V3 within weeks. That's a lot of money sitting behind code most people haven't audited.
I've gone through the V4 architecture, the PoolManager contract, and a stack of early hook implementations. What I found surprised even me — and I audit contracts for a living. The vulnerability surface here isn't just new, it's fundamentally different from anything that came before in AMM design. Let me walk you through exactly what's at risk.
What Is Uniswap V4 and Why Do Hooks Change the Security Model?
Uniswap V4 moves all pools into a single singleton contract called PoolManager. Instead of deploying a new pair contract for every token pair (V2/V3 style), every pool lives in one contract and is identified by a PoolKey struct. This reduces gas significantly — but it also means a bug in one pool's hook can have implications for the shared singleton state.
Hooks are external contracts that PoolManager calls at defined lifecycle points:
beforeInitialize/afterInitializebeforeAddLiquidity/afterAddLiquiditybeforeRemoveLiquidity/afterRemoveLiquiditybeforeSwap/afterSwapbeforeDonate/afterDonate
The hook's address determines which callbacks it implements — encoded in the address itself via CREATE2 mining. That's a clever design, but it creates a new class of problem: the hook is trusted by the pool unconditionally. PoolManager calls your hook contract and expects it to behave. There is no sandbox.
What Are the Real Attack Vectors in Uniswap V4 Hooks?
Everyone's focused on reentrancy because that's the classic AMM boogeyman. Fair. But the real killers in V4 are subtler. Let me give you four attack surfaces that will wreck unprepared protocols.
1. Hook-Level Reentrancy via the Delta Accounting System
V4 uses a flash accounting model — balances are tracked as deltas, and the caller must settle by the end of the transaction. The lock mechanism in PoolManager is meant to prevent reentrant calls. But if your hook calls back into PoolManager through a different code path during a callback, you can manipulate intermediate state before deltas are settled.
According to Rekt News, reentrancy-adjacent vulnerabilities have drained over $2.3B from DeFi protocols since 2020. V4's new accounting model doesn't eliminate this class — it reshapes it.
2. Malicious Hooks as Backdoors
This one targets traders and LPs, not developers. If you're interacting with a V4 pool, you are implicitly trusting its hook. A malicious afterSwap hook can redirect funds, inflate prices, or even call back into your wallet if you've granted approvals. The Rug Pull as a hook — this is the new meta.
Think about what Venom Protocol did to NFT traders in 2022 — they embedded malicious logic in a contract that looked like a standard marketplace, draining $1.8M from users who approved it. V4 hooks create the same social engineering opportunity with a technical wrapper that looks legitimate on the surface.
3. Access Control on Hook Admin Functions
Everyone checks for reentrancy. The real killer is access control — it's responsible for 3x more total DeFi losses than reentrancy and nobody builds their hook audit checklist around it. Most hook developers add admin functions for fee updates, oracle configs, or whitelist management. If those functions aren't properly gated, you've shipped a protocol with a god-mode backdoor.
4. Price Oracle Manipulation Through Hook Logic
Hooks that read or write price data mid-swap create new TWAP manipulation surfaces. If your hook uses slot0 price data (spot price, not TWAP) during a beforeSwap callback to make decisions — say, a dynamic fee hook — an attacker can sandwich that logic with a flash loan. Mango Markets lost $117M to oracle price manipulation. V4 hooks give attackers a new insertion point for the same playbook.
What Does a Vulnerable Hook Look Like in Solidity?
Here's a realistic example of a dynamic fee hook with a critical access control vulnerability. This pattern shows up constantly in early hook implementations.
Vulnerable Pattern (Solidity 0.8.24):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {BaseHook} from "v4-periphery/BaseHook.sol";
import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol";
import {PoolKey} from "v4-core/types/PoolKey.sol";
import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "v4-core/types/BeforeSwapDelta.sol";
contract VulnerableFeeHook is BaseHook {
uint24 public dynamicFee;
address public feeController;
constructor(IPoolManager _manager, address _feeController) BaseHook(_manager) {
feeController = _feeController;
dynamicFee = 3000; // 0.3%
}
// ❌ VULNERABLE: No access control. Anyone can call this.
function setFee(uint24 newFee) external {
dynamicFee = newFee;
}
// ❌ VULNERABLE: Uses spot price (slot0) — manipulatable mid-block
function beforeSwap(
address,
PoolKey calldata key,
IPoolManager.SwapParams calldata params,
bytes calldata
) external override returns (bytes4, BeforeSwapDelta, uint24) {
// Reading spot price to make fee decisions — flash loan bait
(uint160 sqrtPriceX96,,,) = poolManager.getSlot0(key.toId());
// Fee logic based on manipulatable price
uint24 fee = sqrtPriceX96 > 1e18 ? 500 : dynamicFee;
return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, fee);
}
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
beforeInitialize: false,
afterInitialize: false,
beforeAddLiquidity: false,
afterAddLiquidity: false,
beforeRemoveLiquidity: false,
afterRemoveLiquidity: false,
beforeSwap: true,
afterSwap: false,
beforeDonate: false,
afterDonate: false,
beforeSwapReturnDelta: false,
afterSwapReturnDelta: false,
afterAddLiquidityReturnDelta: false,
afterRemoveLiquidityReturnDelta: false
});
}
}
Two problems here. First, setFee has zero access control — any wallet can call it and set the fee to 100% (the max), effectively bricking the pool or extracting value from traders. Second, reading slot0 in beforeSwap to influence fee logic means a flash loan attacker can manipulate that price in the same block, making your fee calculation return whatever they want.
Fixed Version (Solidity 0.8.24, OpenZeppelin 5.x):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {BaseHook} from "v4-periphery/BaseHook.sol";
import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol";
import {PoolKey} from "v4-core/types/PoolKey.sol";
import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "v4-core/types/BeforeSwapDelta.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
contract SecureFeeHook is BaseHook, Ownable {
uint24 public dynamicFee;
// TWAP observation window — manipulation-resistant
uint32 public constant TWAP_SECONDS = 1800; // 30 minutes
// ✅ Fee bounds prevent griefing
uint24 public constant MIN_FEE = 100; // 0.01%
uint24 public constant MAX_FEE = 10000; // 1%
event FeeUpdated(uint24 oldFee, uint24 newFee);
constructor(
IPoolManager _manager,
address _owner
) BaseHook(_manager) Ownable(_owner) {
dynamicFee = 3000;
}
// ✅ Access control: only owner can update fee
// ✅ Bounds check: fee cannot be set to extreme values
function setFee(uint24 newFee) external onlyOwner {
require(newFee >= MIN_FEE && newFee <= MAX_FEE, "Fee out of bounds");
emit FeeUpdated(dynamicFee, newFee);
dynamicFee = newFee;
}
function beforeSwap(
address,
PoolKey calldata,
IPoolManager.SwapParams calldata,
bytes calldata
) external override returns (bytes4, BeforeSwapDelta, uint24) {
// ✅ Use pre-configured fee — don't read manipulatable on-chain price
// If you need price-based fee logic, use a TWAP with sufficient window
// and validate observation cardinality before reading
return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, dynamicFee);
}
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
beforeInitialize: false,
afterInitialize: false,
beforeAddLiquidity: false,
afterAddLiquidity: false,
beforeRemoveLiquidity: false,
afterRemoveLiquidityReturnDelta: false,
beforeRemoveLiquidity: false,
afterRemoveLiquidity: false,
beforeSwap: true,
afterSwap: false,
beforeDonate: false,
afterDonate: false,
beforeSwapReturnDelta: false,
afterSwapReturnDelta: false,
afterAddLiquidityReturnDelta: false,
afterRemoveLiquidityReturnDelta: false
});
}
}
The fix is straightforward but the thinking behind it matters. Ownable from OpenZeppelin 5.x replaces the old two-step pattern with a cleaner constructor-based owner assignment. Bounds on fees prevent a compromised or rogue owner from griefing the pool even if access control fails. And we've removed the spot price dependency entirely.
How Would I Audit a V4 Hook Before It Ships?
Here's my actual walkthrough — what I look at in the first 20 minutes of a hook audit.
Step 1: Map every external call inside hook callbacks. Any call to an external contract inside beforeSwap, afterSwap, etc. is a reentrancy candidate. V4's lock mechanism helps, but hook-to-hook interactions and callbacks through token contracts are still live surfaces. Draw the call graph. Be paranoid.
Step 2: Audit every state-changing function for access control. Not just the obvious admin functions — look for anything that modifies dynamicFee, whitelist mappings, oracle addresses, or pause flags. Run Slither's access-control detector and read every finding manually. Static tools catch the pattern — they miss the economic context that makes it catastrophic. That's where human judgment (or AI-assisted analysis) matters.
Step 3: Check delta settlement assumptions. Does the hook ever assume a specific delta value will be returned without validating it? Does it trust return values from afterSwap without checking for manipulation? The flash accounting model is elegant but it requires strict discipline about what you trust at each callback point.
Step 4: Trace the hook's interactions with the singleton PoolManager. Since all pools share one contract, any hook that can affect PoolManager's global state — through locks, currency deltas, or protocol fees — is a systemic risk. One bad hook shouldn't be able to freeze unrelated pools, but misconfigured implementations can create unexpected interactions.
Step 5: Verify hook address encoding matches declared permissions. The hook address encodes which callbacks are active. If the address claims beforeSwap is disabled but the contract has that function, something's wrong — either in deployment or in the off-chain tooling used to mine the address. Uniswap's HookMiner library has specific requirements; verify them.
According to Trail of Bits' 2023 DeFi security report, access control misconfigurations and unchecked external calls account for over 60% of critical findings in AMM-adjacent contracts. V4 hooks are both — they're external calls with privileged access.
What Should DeFi Users and Traders Check Before Using a V4 Pool?
You don't need to read Solidity to protect yourself. Here's what to check:
- Look up the hook contract address on Etherscan. Is it verified? Unverified hooks are immediate red flags.
- Check who deployed the hook and whether the deployer has upgrade permissions or a proxy pattern attached. An upgradeable hook is a hook the team can change after you deposit.
- Look for a recent audit from a named firm. Not a tweet saying "we take security seriously." An actual report with findings.
- Check whether the hook has admin keys — and whether those keys are behind a multisig or timelock. A single EOA with god-mode over a hook controlling millions is a rug waiting to happen.
If you can't find answers to these questions in under five minutes, don't ape in.
Run Your Hook Through SmartContractAuditor.ai Before You Deploy
Paste your hook contract into SmartContractAuditor.ai before you deploy. It flags access control gaps, reentrancy patterns, and oracle manipulation risks — and explains exactly why each finding is dangerous, in plain English. Not a raw Slither output you have to decode yourself. Actual context. Takes 30 seconds. The Mango Markets team didn't have that. You do.
Frequently Asked Questions
What is a Uniswap V4 hook and why is it a security risk?
A Uniswap V4 hook is a smart contract that plugs into the AMM's swap and liquidity lifecycle — executing custom logic before or after key operations. Because PoolManager calls the hook contract unconditionally and trusts its return values, a malicious or buggy hook can manipulate pool state, redirect funds, or exploit intermediate delta balances. The hook is trusted by design, which means there's no built-in protection against bad hook logic.
How does a hook-based reentrancy attack work in Uniswap V4?
In V4, all operations go through a locking mechanism in PoolManager — but hooks can still make external calls during their callbacks. If a hook calls an external contract that itself calls back into the pool or the hook during execution, intermediate state (like unsettled currency deltas) can be read or manipulated in inconsistent ways. The attack doesn't look like classic reentrancy but achieves similar outcomes: draining value by exploiting state that hasn't finalized yet.
Has a Uniswap hook ever been exploited in production?
V4 is recent enough that large-scale hook exploits are still emerging. However, the attack patterns are well-precedented — Mango Markets lost $117M to oracle price manipulation in 2022, and that exact vector (reading manipulatable spot prices to influence protocol logic) applies directly to hooks that read slot0 data during swap callbacks. The architecture is new; the vulnerability classes are not.
What is the difference between Uniswap V3 and V4 security risks?
In V3, each pool is an isolated contract — a bug in one pool doesn't directly affect others. In V4, all pools share a single PoolManager singleton, and hooks are external contracts with privileged callback access. This means a malicious hook could potentially affect global state, and the attack surface includes not just pool math but all custom logic that developers attach through hooks. V4 is more composable and more dangerous.
Is a Uniswap V4 hook audit different from a standard smart contract audit?
Yes, significantly. Hook audits require understanding V4's flash accounting model, delta settlement mechanics, lock behavior, and how hook address encoding maps to declared permissions — none of which exist in standard ERC-20 or older AMM audits. You also need to analyze the hook's interaction with the singleton PoolManager and any external protocols it touches during callbacks. Standard audit checklists miss most of this. Auditors need V4-specific knowledge or tooling to catch the real risks.
Further Reading
Related Articles
Continue exploring smart contract security with these related insights
Price Oracle Manipulation: How Attackers Drain DeFi Protocols Through Fake Price Feeds
$116 million. That's what Mango Markets lost in October 2022 because one person manipulated the price of their own token to drain the treasury. The oracle wasn't hacked — it was fed lies, and the protocol believed every one of them.
Flash Loan Attacks in 2026: How AI Detects Them Before Deployment
$197M. Gone in 13 minutes. The Euler Finance attacker used a single flash loan to break an assumption every developer on that team thought was mathematically impossible. This is how flash loan attacks actually work — and how AI catches them before you deploy.
Sandwich Attacks and MEV: Why Your DeFi Users Are Getting Robbed On Every Swap
$1.3 billion extracted from DeFi users through MEV in 2023 alone — and most of the victims had no idea it was happening. Sandwich attacks are the silent tax on every swap your users make. If you're building a DEX, a router, or any protocol that touches Uniswap pools, this is the vulnerability you're probably shipping right now.
Explore more insights on smart contract security andblockchain vulnerabilities