Deployment Script Uses Wrong Flags Causing Hook To Be Unusable
Description
Uniswap V4 hooks encode their required permissions as flags in the hook's contract address. The BaseHook constructor validates that the address flags match the permissions returned by getHookPermissions(). If they do not match, deployment reverts with HookAddressNotValid.
The deployment script uses BEFORE_INITIALIZE_FLAG but the hook's getHookPermissions() returns beforeInitialize: false and afterInitialize: true. This mismatch causes the hook deployment to always revert.
function run() public {
@> uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_INITIALIZE_FLAG);
}
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
@> beforeInitialize: false,
@> afterInitialize: true,
beforeSwap: true,
});
}
Risk
Likelihood: High
Impact: High
Proof Of Concept
The following test case provides a proof of concept
function test_flagMismatch() public {
bytes memory constructorArgs = abi.encode(
manager,
PHASE1_DURATION,
PHASE2_DURATION,
PHASE1_LIMIT_BPS,
PHASE2_LIMIT_BPS,
PHASE1_COOLDOWN,
PHASE2_COOLDOWN,
PHASE1_PENALTY_BPS,
PHASE2_PENALTY_BPS
);
uint160 correctFlags = uint160(Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG);
(address hookAddress, bytes32 salt) =
HookMiner.find(address(this), correctFlags, type(TokenLaunchHook).creationCode, constructorArgs);
uint160 scriptFlags = uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_INITIALIZE_FLAG);
(address hookAddressWithScriptFlags, bytes32 saltWithScriptFlags) =
HookMiner.find(address(this), scriptFlags, type(TokenLaunchHook).creationCode, constructorArgs);
assertNotEq(hookAddress, hookAddressWithScriptFlags, "Hook addresses with different flags match");
vm.expectRevert();
TokenLaunchHook hook = new TokenLaunchHook{salt: saltWithScriptFlags}(
manager,
PHASE1_DURATION,
PHASE2_DURATION,
PHASE1_LIMIT_BPS,
PHASE2_LIMIT_BPS,
PHASE1_COOLDOWN,
PHASE2_COOLDOWN,
PHASE1_PENALTY_BPS,
PHASE2_PENALTY_BPS
);
}
Recommended Mitigation
A fix to deployment script should be made:
function run() public {
// hook contracts must have specific flags encoded in the address
- uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_INITIALIZE_FLAG);
+ uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_INITIALIZE_FLAG);