Build your own hook
Whenever starting on a new project, it is always a good idea to use a framework which can assist in development and that can make the setup process easier. Similarly, when building your own hooks, will would want to use a few utilities to 1) Mining the correct salt for your hook address 2) Using a router which acquire the lock and then call various functions on the pool manager 3) Deploying the hook to a deterministic address
Many of these steps are already taken care by a few template repositories which are listed here
Hooks Template
One of the most popular templates is the v4-template which is created by Sauce.
You can clone the repository and then follow the instructions in the README to get started.
Getting Started
- Installation: The template requires Foundry. Install dependencies using:
forge install
- Testing: Run tests with:
forge test
Counter Hook
The template includes an example hook (Counter.sol
), a test for it, and scripts for deploying hooks.
Here are some of the things to note about the Counter hook:
- It extends the
BaseHook
contract which is defined in the v4-periphery repository.
import {BaseHook} from "v4-periphery/BaseHook.sol";
contract Counter is BaseHook {
// ... hook code here
}
- The hook gets called before and after every swap and modify position call. This is done as part of
getHookPermissions
function
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
beforeInitialize: false,
afterInitialize: false,
beforeAddLiquidity: true,
afterAddLiquidity: false,
beforeRemoveLiquidity: true,
afterRemoveLiquidity: false,
beforeSwap: true,
afterSwap: true,
beforeDonate: false,
afterDonate: false,
noOp: false,
accessLock: false
});
}
- Corresponding to the hooks calls, the hook implements the following functions:
function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata)
external
override
returns (bytes4)
{
beforeSwapCount[key.toId()]++;
return BaseHook.beforeSwap.selector;
}
function afterSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, BalanceDelta, bytes calldata)
external
override
returns (bytes4)
{
afterSwapCount[key.toId()]++;
return BaseHook.afterSwap.selector;
}
function beforeAddLiquidity(
address,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata,
bytes calldata
) external override returns (bytes4) {
beforeAddLiquidityCount[key.toId()]++;
return BaseHook.beforeAddLiquidity.selector;
}
function beforeRemoveLiquidity(
address,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata,
bytes calldata
) external override returns (bytes4) {
beforeRemoveLiquidityCount[key.toId()]++;
return BaseHook.beforeRemoveLiquidity.selector;
}
- In each of these functions, Counter hook increments a counter. You can see this in the
beforeSwap
function:
function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata)
external
override
returns (bytes4)
{
beforeSwapCount[key.toId()]++;
return BaseHook.beforeSwap.selector;
}
Additional Resources
- v4-periphery: For advanced hook implementations.
- v4-core: The core repository for Uniswap v4.