Vanguard

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

Deployment will revert due to flag mismatch

Author Revealed upon completion

Root +Impact

Description

  • DeployHookScript is expected to deploy the hook contract without issue, but will instead revert

  • The deployment will revert with a HookAddressNotValid error. This is due to a flag mismatch:
    In DeployHookScript two flags are set:
    uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_INITIALIZE_FLAG);
    and used to derive the hook address:
    (address hookAddress, bytes32 salt) = HookMiner.find(CREATE2_FACTORY, flags, type(TokenLaunchHook).creationCode, constructorArgs);
    However, in TokenLaunchHook::getHookPermissions different flags/permissions are set, specifically:
    afterInitialize: true and beforeSwap: true .
    This will cause the deployment to revert

Risk

Likelihood

  • High: The contract will not be deployed

Impact

  • Low: The contract won't deploy, causing the team do delay launch

Proof of concept

The following test shows deployments with both incorrect and correct flags:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {Test, console2} from "forge-std/Test.sol";
import {TokenLaunchHook} from "../src/TokenLaunchHook.sol";
import {Hooks} from "v4-core/libraries/Hooks.sol";
import {HookMiner} from "v4-periphery/src/utils/HookMiner.sol";
import {Deployers} from "@uniswap/v4-core/test/utils/Deployers.sol";
import {DeployHookScript} from "../script/deployLaunchHook.s.sol";
contract DeploymentTest is Test, Deployers {
// Constants from deployLaunchHook.s.sol
uint256 constant PHASE1_DURATION = 100;
uint256 constant PHASE2_DURATION = 200;
uint256 constant PHASE1_LIMIT_BPS = 100;
uint256 constant PHASE2_LIMIT_BPS = 300;
uint256 constant PHASE1_COOLDOWN = 5;
uint256 constant PHASE2_COOLDOWN = 3;
uint256 constant PHASE1_PENALTY_BPS = 500;
uint256 constant PHASE2_PENALTY_BPS = 200;
function setUp() public {
deployFreshManagerAndRouters();
}
function test_ExistingDeploymentScript_Fails_DueToFlagMismatch() public {
// Reproduce the exact deployment logic from the script
bytes memory creationCode = type(TokenLaunchHook).creationCode;
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
);
// WRONG FLAGS from deployLaunchHook.s.sol line 31
uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_INITIALIZE_FLAG);
console2.log("Mining address with WRONG flags from script...");
(address hookAddress, bytes32 salt) =
HookMiner.find(address(this), flags, creationCode, constructorArgs);
console2.log("Expected hook address:", hookAddress);
console2.log("Attempting deployment with script's flags...");
console2.log("");
// Reverts with HookAddressNotValid due to flag mismatch
vm.expectRevert(abi.encodeWithSelector(Hooks.HookAddressNotValid.selector, hookAddress));
new TokenLaunchHook{salt: salt}(
manager,
PHASE1_DURATION,
PHASE2_DURATION,
PHASE1_LIMIT_BPS,
PHASE2_LIMIT_BPS,
PHASE1_COOLDOWN,
PHASE2_COOLDOWN,
PHASE1_PENALTY_BPS,
PHASE2_PENALTY_BPS
);
}
function test_DeploymentWithCorrectFlags_Succeeds() public {
// PoC: Demonstrate successful deployment with CORRECT flags
bytes memory creationCode = type(TokenLaunchHook).creationCode;
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
);
// CORRECT FLAGS matching getHookPermissions(): AFTER_INITIALIZE + BEFORE_SWAP
uint160 correctFlags = uint160(Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG);
(address hookAddress, bytes32 salt) =
HookMiner.find(address(this), correctFlags, creationCode, constructorArgs);
console2.log("Expected hook address:", hookAddress);
console2.log("Attempting deployment with correct flags...");
// Should succeed with correct flags
TokenLaunchHook hook = new TokenLaunchHook{salt: salt}(
manager,
PHASE1_DURATION,
PHASE2_DURATION,
PHASE1_LIMIT_BPS,
PHASE2_LIMIT_BPS,
PHASE1_COOLDOWN,
PHASE2_COOLDOWN,
PHASE1_PENALTY_BPS,
PHASE2_PENALTY_BPS
);
// Verify deployment
assertEq(address(hook), hookAddress, "Hook address should match mined address");
assertEq(hook.phase1Duration(), PHASE1_DURATION, "Phase 1 duration should match");
assertEq(hook.currentPhase(), 0, "Initial phase should be 0 before pool initialization");
}
}

Recommended mitigation

Replace the BEFORE_INITIALIZE_FLAG with AFTER_INITIALIZE_FLAG in DeployHookScript

- 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!