This guide explores how to customize liquidity management in Uniswap V4 using hook functions. We'll walk through practical implementations for adding and removing liquidity while maintaining security and efficiency.
Understanding Liquidity Hooks
Uniswap V4 introduces four powerful hook functions to customize liquidity operations:
Before/After Liquidity Addition:
beforeAddLiquidityafterAddLiquidity
Before/After Liquidity Removal:
beforeRemoveLiquidityafterRemoveLiquidity
These hooks enable developers to:
๐ Enhance pool functionality with custom logic
- Track liquidity changes
- Implement custom fee structures
- Add security checks
- Create reward mechanisms
Setting Up the Contract Structure
Solidity Version and Dependencies
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {BaseHook} from "v4-periphery/src/utils/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 {PoolId, PoolIdLibrary} from "v4-core/src/types/PoolId.sol";Contract Initialization
contract LiquidityHook is BaseHook {
using PoolIdLibrary for PoolKey;
mapping(PoolId => uint256 count) public beforeAddLiquidityCount;
mapping(PoolId => uint256 count) public beforeRemoveLiquidityCount;
constructor(IPoolManager _poolManager) BaseHook(_poolManager) {}
}Implementing Hook Permissions
Properly declaring hook permissions is crucial for security and functionality:
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
beforeAddLiquidity: true,
beforeRemoveLiquidity: true,
// Other permissions set to false
...
});
}BeforeAddLiquidity Implementation
Core Functionality
function _beforeAddLiquidity(
address,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata,
bytes calldata
) internal override returns (bytes4) {
beforeAddLiquidityCount[key.toId()]++;
return BaseHook.beforeAddLiquidity.selector;
}Key Parameters
| Parameter | Description |
|---|---|
sender | Original transaction caller |
key | Pool identification data |
params | Liquidity modification details |
hookData | Custom data from liquidity provider |
BeforeRemoveLiquidity Implementation
Core Functionality
function _beforeRemoveLiquidity(
address,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata,
bytes calldata
) internal override returns (bytes4) {
beforeRemoveLiquidityCount[key.toId()]++;
return BaseHook.beforeRemoveLiquidity.selector;
}Key Parameters
| Parameter | Description |
|---|---|
sender | Original transaction caller |
key | Pool identification data |
params | Liquidity modification details |
hookData | Custom data from liquidity provider |
๐ Learn advanced liquidity management techniques
Complete Contract Example
Here's the full implementation combining all components:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {BaseHook} from "v4-periphery/src/utils/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 {PoolId, PoolIdLibrary} from "v4-core/src/types/PoolId.sol";
contract LiquidityHook is BaseHook {
using PoolIdLibrary for PoolKey;
mapping(PoolId => uint256 count) public beforeAddLiquidityCount;
mapping(PoolId => uint256 count) public beforeRemoveLiquidityCount;
constructor(IPoolManager _poolManager) BaseHook(_poolManager) {}
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
beforeAddLiquidity: true,
beforeRemoveLiquidity: true,
// Other permissions omitted for brevity
...
});
}
function _beforeAddLiquidity(
address,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata,
bytes calldata
) internal override returns (bytes4) {
beforeAddLiquidityCount[key.toId()]++;
return BaseHook.beforeAddLiquidity.selector;
}
function _beforeRemoveLiquidity(
address,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata,
bytes calldata
) internal override returns (bytes4) {
beforeRemoveLiquidityCount[key.toId()]++;
return BaseHook.beforeRemoveLiquidity.selector;
}
}FAQ Section
What are the main benefits of using liquidity hooks?
Liquidity hooks allow for custom logic execution during liquidity operations, enabling features like automated fee adjustments, security checks, and reward distribution.
How do I ensure my hook contract is secure?
- Keep hook logic simple and efficient
- Thoroughly test all edge cases
- Implement proper access controls
- Monitor gas usage carefully
Can hooks affect transaction costs?
Yes, complex hook logic can increase gas costs. Always optimize your code and consider gas implications.
What's the difference between before/after hooks?
Before hooks execute prior to the core operation (for validation/pre-processing), while after hooks run post-operation (for cleanup/tracking).
๐ Discover more DeFi development resources
How do I extend this basic example?
You could add:
- Fee calculation logic
- Liquidity provider rewards
- Time-based restrictions
- Advanced security checks
Remember that these examples demonstrate core concepts but should be enhanced for production use with proper security measures and additional functionality.