Skip to main content
Helpful?

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

  1. Installation: The template requires Foundry. Install dependencies using:
forge install
  1. 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:

  1. 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
}

  1. 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
});
}

  1. 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;
}
  1. 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

Helpful?