LLMs.txt: agent-readable Markdown index of this site at /llms.txt

Deploy a Hook

Deploy a new DualPool hook through the factory and verify its provenance.

Context

New DualPool hooks are deployed through the AllowlistedFactory, the family's canonical CREATE2 deployer and discovery registry:

NetworkAddress
Ethereum: 10x0000000000077769C332e0D3ed8bC8E02A0cE108

Deploying through the factory is what makes a hook discoverable: the factory emits a Deployed event, records the hook in its onchain registry (allDeployments, isFromFactory), and the hook's own factory() getter points back at the factory, so aggregators and routers can verify provenance from either side.

The factory only accepts creation code whose keccak256 hash is on an immutable allowlist fixed at its construction. A registered hook is therefore a bit-exact build of the audited DualPool bytecode. Deployment is permissionless: anyone can deploy a hook and choose its owner, so registration attests bytecode provenance, not operator trustworthiness.

The bytecode is pinned

The allowlist pins exact compiler output. Build from the pinned toolchain in v4-hooks-public; any change to the source, compiler version, or settings changes the creation-code hash and the factory rejects it with CreationCodeNotAllowed. The deploy script checks your build against the factory before broadcasting, so drift fails fast.

1. Choose the Constructor Parameters

The hook's constructor takes four values, fixed for the hook's lifetime:

ParameterMeaning
poolManagerThe canonical v4 PoolManager on the target chain, 0x000000000004444c5dc75cB358380D2e3dE08A90 on Ethereum (all chains: v4 deployments)
maxGasGas budget the hook declares for getIndicativeQuote staticcalls; routers cap their calls at this (script default 800000)
ownerInitial hook owner, the operator trust principal; transferable via Ownable2Step, renounce disabled
maxMinDepositBlocksCeiling for the per-pool minDepositBlocks deposit lock (script default 3600)

One hook instance serves many pools: if you already operate a DualPool hook, you usually want a new pool on it, not a new hook.

2. Mine a Salt

The factory deploys via CREATE2, and v4 requires a hook's address to encode its permission flags in the low 14 bits (address & 0x3fff == 0x2ac0 for DualPool). The factory is the CREATE2 deployer, so salts must be mined against the factory address; a salt mined for any other deployer resolves to a different address and reverts.

From a checkout of v4-hooks-public:

FACTORY=0x0000000000077769C332e0D3ed8bC8E02A0cE108 \
POOL_MANAGER=0x000000000004444c5dc75cB358380D2e3dE08A90 \
HOOK_OWNER=<owner> \
./script/mine_dualpool_salt.sh

The miner computes the init-code hash from your current build and the exact constructor arguments, searches for the flag bits plus a leading-zero vanity prefix (MIN_LEADING_ZERO_NIBBLES, default 6), and independently re-verifies every match. Constructor arguments are part of the mined address: change any of them and the salt is invalid.

For testnets you can skip offchain mining entirely by setting MIN_LEADING_ZERO_NIBBLES=0 at deploy time; the deploy script then mines a flags-only salt in-EVM.

3. Deploy

FACTORY=0x0000000000077769C332e0D3ed8bC8E02A0cE108 \
POOL_MANAGER=0x000000000004444c5dc75cB358380D2e3dE08A90 \
HOOK_OWNER=<owner> \
HOOK_SALT=<mined salt> \
forge script script/DeployDualPoolHook.s.sol --rpc-url $RPC_URL --broadcast --account <signer>

Before broadcasting, the script verifies that the factory allowlists your build's creation-code hash and that the salt resolves to an address with the correct flag bits and vanity prefix. After broadcasting, it confirms the deployed address, hook.factory(), and the factory registration.

Programmatic deployers can call the factory directly; constructor arguments are passed as ABI-encoded bytes:

bytes memory constructorArgs = abi.encode(poolManager, maxGas, owner, maxMinDepositBlocks);

address hook = factory.deploy(type(DualPoolHook).creationCode, constructorArgs, salt);

factory.computeAddress(creationCode, constructorArgs, salt) predicts the address without deploying.

4. Verify

# The hook points back at the factory
cast call <hook> "factory()(address)" --rpc-url $RPC_URL

# The factory vouches for the hook
cast call 0x0000000000077769C332e0D3ed8bC8E02A0cE108 \
  "isFromFactory(address)(bool)" <hook> --rpc-url $RPC_URL

Both checks must pass. A hook deployed outside the factory reports its direct deployer from factory() and is not registered; routers that anchor trust on the factory will not pick it up.

Where to Go Next