Vanguard

First Flight #56
Beginner FriendlyDeFiFoundry
0 EXP
Submission Details
Impact: high
Likelihood: high

Deployment Script Uses Wrong Flags Causing Hook To Be Unusable

Author Revealed upon completion

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.

// script/deployLaunchHook.s.sol
function run() public {
// hook contracts must have specific flags encoded in the address
@> uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_INITIALIZE_FLAG);
// ...
}
// src/TokenLaunchHook.sol
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
@> beforeInitialize: false,
@> afterInitialize: true,
// ...
beforeSwap: true,
// ...
});
}

Risk

Likelihood: High

  • This occurs very time the deployment script is executed

  • The mismatch is hardcoded - there is no conditiona logic that could avoid it

Impact: High

  • The hook cannot be deployed using the provided deployment script

  • Any automated deployment pipelines using the script will fail

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);
// Assert different address is produced
assertNotEq(hookAddress, hookAddressWithScriptFlags, "Hook addresses with different flags match");
// Try deployment with script's flags
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);

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!