Skip to main content
Helpful?

Hook Deployment

Hook Flags

As mentioned in Concept of Hooks, hook contracts indicate their implemented functions by encoding its behavior in the address of the contract. The PoolManager uses these permissions to determine which hook functions to call for a given pool.

Each hook function e.g. beforeSwap - corresponds to a certain flag. For example, the beforeSwap function is correlated to the BEFORE_SWAP_FLAG which has a value of 1 << 7.

These flags represent specific bits in the address of the hook smart contract - and the value of the bit (a one or a zero) represents whether that flag is true or false. An example:

Addresses on Ethereum are 20 bytes long (160 bits). So for example the address:

0x00000000000000000000000000000000000000C0

represented in binary is:

0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 1100 0000

In binary it goes from right-to-left - so the trailing 8 bits of this address are 1100 0000 where:

1st Bit to 6th Bit = 0

7th Bit and 8th Bit = 1

The AFTER_SWAP flag is represented by the 7th bit - which is set to 1 for the example contract address. In the PoolManager swap execution flow, it will observe the flag and make a call to the hook's afterSwap function.

Similarly, the 8th bit which is also a 1, actually corresponds to the BEFORE_SWAP i.e. the beforeSwap hook function - which will also be called by the PoolManager during a swap workflow.

A full list of all flags can be found here.

Hook Miner

Because of encoded addresses, hook developers must mine an address to a their particular pattern

For local testing, deployCodeTo cheatcode in Foundry can be used to deploy hook contract to any address.

But when deploying hooks to an actual network, address mining is required to find the proper deployment address There is a helper library HookMiner.sol that can be used to mine for correct addresses.

Let's see it in action for a Foundry script. We will make use of the example - Points Hook from Building Your First Hook.

First we set up the contract for Foundry script and import the relevant dependencies:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "forge-std/Script.sol";
import {Hooks} from "v4-core/src/libraries/Hooks.sol";
import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol";

import {Constants} from "./base/Constants.sol";
import {PointsHook} from "../src/PointsHook.sol";
import {HookMiner} from "../test/utils/HookMiner.sol";

/// @notice Mines the address and deploys the PointsHook.sol Hook contract
contract PointsHookScript is Script, Constants {
function setUp() public {}

function run() public {

Specify the flags needed to be encoded in the address:

uint160 flags = uint160(
Hooks.AFTER_ADD_LIQUIDITY_FLAG | Hooks.AFTER_SWAP_FLAG
);

Mine the address by finding a salt that produces a hook address with the desired flags, use the Foundry deterministic deployer when deploying via Foundry script:

bytes memory constructorArgs = abi.encode(POOLMANAGER);
(address hookAddress, bytes32 salt) =
HookMiner.find(CREATE2_DEPLOYER, flags, type(PointsHook).creationCode, constructorArgs);

Deploy the hook using CREATE2 with the salt, and compare the deployed address with the address mined:

vm.broadcast();
PointsHook pointsHook = new PointsHook{salt: salt}(IPoolManager(POOLMANAGER));
require(address(pointsHook) == hookAddress, "PointsHookScript: hook address mismatch");

A Complete Foundry Script Contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "forge-std/Script.sol";
import {Hooks} from "v4-core/src/libraries/Hooks.sol";
import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol";

import {Constants} from "./base/Constants.sol";
import {PointsHook} from "../src/PointsHook.sol";
import {HookMiner} from "../test/utils/HookMiner.sol";

/// @notice Mines the address and deploys the PointsHook.sol Hook contract
contract PointsHookScript is Script, Constants {
function setUp() public {}

function run() public {
// hook contracts must have specific flags encoded in the address
uint160 flags = uint160(
Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_SWAP_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG
| Hooks.BEFORE_REMOVE_LIQUIDITY_FLAG
);

// Mine a salt that will produce a hook address with the correct flags
bytes memory constructorArgs = abi.encode(POOLMANAGER);
(address hookAddress, bytes32 salt) =
HookMiner.find(CREATE2_DEPLOYER, flags, type(PointsHook).creationCode, constructorArgs);

// Deploy the hook using CREATE2
vm.broadcast();
PointsHook pointsHook = new PointsHook{salt: salt}(IPoolManager(POOLMANAGER));
require(address(pointsHook) == hookAddress, "PointsHookScript: hook address mismatch");
}
}
Helpful?