v4-security-foundations

安装量: 254
排名: #3440

安装

npx skills add https://github.com/uniswap/uniswap-ai --skill v4-security-foundations
v4 Hook Security Foundations
Security-first guide for building Uniswap v4 hooks. Hook vulnerabilities can drain user funds—understand these concepts before writing any hook code.
Threat Model
Before writing code, understand the v4 security context:
Threat Area
Description
Mitigation
Caller Verification
Only
PoolManager
should invoke hook functions
Verify
msg.sender == address(poolManager)
Sender Identity
msg.sender
always equals PoolManager, never the end user
Use
sender
parameter for user identity
Router Context
The
sender
parameter identifies the router, not the user
Implement router allowlisting
State Exposure
Hook state is readable during mid-transaction execution
Avoid storing sensitive data on-chain
Reentrancy Surface
External calls from hooks can enable reentrancy
Use reentrancy guards; minimize external calls
Permission Flags Risk Matrix
All 14 hook permissions with associated risk levels:
Permission Flag
Risk Level
Description
Security Notes
beforeInitialize
LOW
Called before pool creation
Validate pool parameters
afterInitialize
LOW
Called after pool creation
Safe for state initialization
beforeAddLiquidity
MEDIUM
Before LP deposits
Can block legitimate LPs
afterAddLiquidity
LOW
After LP deposits
Safe for tracking/rewards
beforeRemoveLiquidity
HIGH
Before LP withdrawals
Can trap user funds
afterRemoveLiquidity
LOW
After LP withdrawals
Safe for tracking
beforeSwap
HIGH
Before swap execution
Can manipulate prices
afterSwap
MEDIUM
After swap execution
Can observe final state
beforeDonate
LOW
Before donations
Access control only
afterDonate
LOW
After donations
Safe for tracking
beforeSwapReturnDelta
CRITICAL
Returns custom swap amounts
NoOp attack vector
afterSwapReturnDelta
HIGH
Modifies post-swap amounts
Can extract value
afterAddLiquidityReturnDelta
HIGH
Modifies LP token amounts
Can shortchange LPs
afterRemoveLiquidityReturnDelta
HIGH
Modifies withdrawal amounts
Can steal funds
Risk Thresholds
LOW
Unlikely to cause fund loss
MEDIUM
Requires careful implementation
HIGH
Can cause fund loss if misimplemented
CRITICAL
Can enable complete fund theft
CRITICAL: NoOp Rug Pull Attack
The
BEFORE_SWAP_RETURNS_DELTA
permission (bit 10) is the most dangerous hook permission. A malicious hook can:
Return a delta claiming it handled the entire swap
PoolManager accepts this and settles the trade
Hook keeps all input tokens without providing output
User loses entire swap amount
Attack Pattern
// MALICIOUS - DO NOT USE
function
beforeSwap
(
address
,
PoolKey
calldata
,
IPoolManager
.
SwapParams
calldata
params
,
bytes
calldata
)
external
override
returns
(
bytes4
,
BeforeSwapDelta
,
uint24
)
{
// Claim to handle the swap but steal tokens
int128
amountSpecified
=
int128
(
params
.
amountSpecified
)
;
BeforeSwapDelta delta
=
toBeforeSwapDelta
(
amountSpecified
,
0
)
;
return
(
BaseHook
.
beforeSwap
.
selector
,
delta
,
0
)
;
}
Detection
Before interacting with ANY hook that has
beforeSwapReturnDelta: true
:
Audit the hook code
- Verify legitimate use case
Check ownership
- Is it upgradeable? By whom?
Verify track record
- Has it been audited by reputable firms?
Start small
- Test with minimal amounts first
Legitimate Uses
NoOp patterns are valid for:
Just-in-time liquidity (JIT)
Custom AMM curves
Intent-based trading systems
RFQ/PMM integrations
But each requires careful implementation and audit.
Delta Accounting Fundamentals
v4 uses a credit/debit system through the PoolManager:
Core Invariant
For every transaction: sum(deltas) == 0
The PoolManager tracks what each address owes or is owed. At transaction end, all debts must be settled.
Key Functions
Function
Purpose
Direction
take(currency, to, amount)
Withdraw tokens from PoolManager
You receive tokens
settle(currency)
Pay tokens to PoolManager
You send tokens
sync(currency)
Update PoolManager balance tracking
Preparation for settle
Settlement Pattern
// Correct pattern: sync before settle
poolManager
.
sync
(
currency
)
;
currency
.
transfer
(
address
(
poolManager
)
,
amount
)
;
poolManager
.
settle
(
currency
)
;
Common Mistakes
Forgetting sync
Settlement fails without sync
Wrong order
Must sync → transfer → settle
Partial settlement
Leaves transaction in invalid state
Double settlement
Causes accounting errors Access Control Patterns PoolManager Verification Every hook callback MUST verify the caller: modifier onlyPoolManager ( ) { require ( msg . sender == address ( poolManager ) , "Not PoolManager" ) ; _ ; } function beforeSwap ( address sender , PoolKey calldata key , IPoolManager . SwapParams calldata params , bytes calldata hookData ) external override onlyPoolManager returns ( bytes4 , BeforeSwapDelta , uint24 ) { // Safe to proceed } Why This Matters Without this check: Anyone can call hook functions directly Attackers can manipulate hook state Funds can be drained through fake callbacks Router Verification Patterns The sender parameter is the router, not the end user. For hooks that need user identity: Allowlisting Pattern mapping ( address => bool ) public allowedRouters ; function beforeSwap ( address sender , // This is the router PoolKey calldata key , IPoolManager . SwapParams calldata params , bytes calldata hookData ) external override onlyPoolManager returns ( bytes4 , BeforeSwapDelta , uint24 ) { require ( allowedRouters [ sender ] , "Router not allowed" ) ; // Proceed with swap } User Identity via hookData function beforeSwap ( address sender , PoolKey calldata key , IPoolManager . SwapParams calldata params , bytes calldata hookData ) external override onlyPoolManager returns ( bytes4 , BeforeSwapDelta , uint24 ) { // Decode user address from hookData (router must include it) address user = abi . decode ( hookData , ( address ) ) ; // CAUTION: Router must be trusted to provide accurate user } msg.sender Trap // WRONG - msg.sender is always PoolManager in hooks function beforeSwap ( . . . ) external { require ( msg . sender == someUser ) ; // Always fails or wrong } // CORRECT - Use sender parameter function beforeSwap ( address sender , . . . ) external { require ( allowedRouters [ sender ] , "Invalid router" ) ; } Token Handling Hazards Not all tokens behave like standard ERC-20s: Token Type Hazard Mitigation Fee-on-transfer Received amount < sent amount Measure actual balance changes Rebasing Balance changes without transfers Avoid storing raw balances ERC-777 Transfer callbacks enable reentrancy Use reentrancy guards Pausable Transfers can be blocked Handle transfer failures gracefully Blocklist Specific addresses blocked Test with production addresses Low decimals Precision loss in calculations Use appropriate scaling Safe Balance Check Pattern function safeTransferIn ( IERC20 token , address from , uint256 amount ) internal returns ( uint256 received ) { uint256 balanceBefore = token . balanceOf ( address ( this ) ) ; token . safeTransferFrom ( from , address ( this ) , amount ) ; received = token . balanceOf ( address ( this ) ) - balanceBefore ; } Base Hook Template Start with all permissions disabled. Enable only what you need: // SPDX-License-Identifier: MIT pragma solidity ^ 0.8.24 ; import { BaseHook } from "v4-periphery/src/base/hooks/BaseHook.sol" ; import { Hooks } from "v4-core/src/libraries/Hooks.sol" ; import { IPoolManager } from "v4-core/src/interfaces/IPoolManager.sol" ; import { PoolKey } from "v4-core/src/types/PoolKey.sol" ; import { BeforeSwapDelta , BeforeSwapDeltaLibrary } from "v4-core/src/types/BeforeSwapDelta.sol" ; contract SecureHook is BaseHook { constructor ( IPoolManager _poolManager ) BaseHook ( _poolManager ) { } 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 : false , // Enable only if needed afterSwap : false , // Enable only if needed beforeDonate : false , afterDonate : false , beforeSwapReturnDelta : false , // DANGER: NoOp attack vector afterSwapReturnDelta : false , // DANGER: Can extract value afterAddLiquidityReturnDelta : false , afterRemoveLiquidityReturnDelta : false } ) ; } // Implement only the callbacks you enabled above } See references/base-hook-template.md for a complete implementation template. Security Checklist Before deploying any hook:

Check
Status
1
All hook callbacks verify
msg.sender == poolManager
[ ]
2
Router allowlisting implemented if needed
[ ]
3
No unbounded loops that can cause OOG
[ ]
4
Reentrancy guards on external calls
[ ]
5
Delta accounting sums to zero
[ ]
6
Fee-on-transfer tokens handled
[ ]
7
No hardcoded addresses
[ ]
8
Slippage parameters respected
[ ]
9
No sensitive data stored on-chain
[ ]
10
Upgrade mechanisms secured (if applicable)
[ ]
11
beforeSwapReturnDelta
justified if enabled
[ ]
12
Fuzz testing completed
[ ]
13
Invariant testing completed
[ ]
Gas Budget Guidelines
Hook callbacks execute inside the PoolManager's transaction context. Excessive gas consumption can make swaps revert or become economically unviable.
Gas Budgets by Callback
Callback
Target Budget
Hard Ceiling
Notes
beforeSwap
< 50,000 gas
150,000 gas
Runs on every swap; keep lean
afterSwap
< 30,000 gas
100,000 gas
Analytics/tracking only
beforeAddLiquidity
< 50,000 gas
200,000 gas
May include access control
afterAddLiquidity
< 30,000 gas
100,000 gas
Reward tracking
beforeRemoveLiquidity
< 50,000 gas
200,000 gas
Lock validation
afterRemoveLiquidity
< 30,000 gas
100,000 gas
Tracking/accounting
Callbacks with external calls
< 100,000 gas
300,000 gas
External DEX routing, oracles
Common Gas Pitfalls
Unbounded loops
Iterating over dynamic arrays (e.g., all active positions) can exceed block gas limits. Cap array sizes or use pagination.
SSTORE in hot paths
Each new storage slot costs ~20,000 gas. Prefer transient storage (
tstore
/
tload
) for data that doesn't persist beyond the transaction. Requires Solidity >= 0.8.24 with EVM target set to
cancun
or later.
External calls
Each cross-contract call adds ~2,600 gas base cost plus the callee's execution. Batch calls where possible.
String operations
Avoid
string
manipulation in callbacks; use
bytes32
for identifiers.
Redundant reads
Cache poolManager calls — repeated getSlot0() or getLiquidity() reads cost gas each time. Measuring Gas

Profile a specific hook callback with Foundry

forge test --match-test test_beforeSwapGas --gas-report

Snapshot gas usage across all tests

forge snapshot --match-contract MyHookTest Risk Scoring System Calculate your hook's risk score (0-33): Category Points Criteria Permissions 0-14 Sum of enabled permission risk levels External Calls 0-5 Number and type of external interactions State Complexity 0-5 Amount of mutable state Upgrade Mechanism 0-5 Proxy, admin functions, etc. Token Handling 0-4 Non-standard token support Audit Tier Recommendations Score Risk Level Recommendation 0-5 Low Self-audit + peer review 6-12 Medium Professional audit recommended 13-20 High Professional audit required 21-33 Critical Multiple audits required Absolute Prohibitions Never do these things in a hook: Never trust msg.sender for user identity - It's always PoolManager Never enable beforeSwapReturnDelta without understanding NoOp attacks Never store passwords, keys, or PII on-chain Never use transfer() for ETH - Use call{value:}("") Never assume token decimals - Always query the token Never use block.timestamp for randomness Never hardcode gas limits in calls Never ignore return values from external calls Never use tx.origin for authorization - It's a phishing vector; malicious contracts can relay calls with the original user's tx.origin Pre-Deployment Audit Checklist

Item Required For 1 Code review by security-focused developer All hooks 2 Unit tests for all callbacks All hooks 3 Fuzz testing with Foundry All hooks 4 Invariant testing Hooks with delta returns 5 Fork testing on mainnet All hooks 6 Gas profiling All hooks 7 Formal verification Critical hooks 8 Slither/Mythril analysis All hooks 9 External audit Medium+ risk hooks 10 Bug bounty program High+ risk hooks 11 Monitoring/alerting setup All production hooks See references/audit-checklist.md for detailed audit requirements. Production Hook References Learn from audited, production hooks: Project Description Notable Security Features Flaunch Token launch platform Multi-sig admin, timelocks EulerSwap Lending integration Isolated risk per market Zaha TWAMM Time-weighted AMM Gradual execution reduces MEV Bunni LP management Concentrated liquidity guards External Resources Official Documentation v4-core Repository v4-periphery Repository Uniswap v4 Docs Hook Permissions Guide Security Resources Trail of Bits Audits Certora v4 Analysis ABDK Consulting Community v4-hooks-skill by @igoryuzo - Community skill that inspired this guide v4hooks.dev - Community hook resources Additional References Base Hook Template - Complete implementation starter Vulnerabilities Catalog - Common patterns and mitigations Audit Checklist - Detailed pre-deployment checklist

返回排行榜