Vanguard

First Flight #56
Beginner FriendlyDeFiFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

Hook logic bypass due to mismatch between address flags and implemented hook functions

Root + Impact


Description

  • In Uniswap v4, the PoolManager uses a bitmasking system on the hook's address to determine which callback functions (like afterInitialize or beforeSwap) should be executed. For a hook's logic to run, the deployment script must mine a salt that produces an address where the specific bits corresponding to those functions are "flipped on."

    The current deployment script incorrectly mines for the BEFORE_INITIALIZE flag, but the TokenLaunchHook.sol contract implements the _afterInitialize internal function.

// in file deployLaunchHook.s.sol::Line no. 29
@> uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_INITIALIZE_FLAG);
// ... mining logic ...
(address hookAddress, bytes32 salt) = HookMiner.find( CREATE2_FACTORY,
@> flags, type(TokenLaunchHook).creationCode, constructorArgs );

Risk

Likelihood:

  • This will occur every time the script is used for deployment.

  • The mining process is deterministic; it will successfully find an address that satisfies the BEFORE_INITIALIZE bit. However, because the contract logic resides in afterInitialize, the PoolManager will look at the hook's address, see that the AFTER_INITIALIZE bit is 0, and skip the call entirely.

Impact:

  • Permanent Denial of Protection: The launchStartBlock and initialLiquidity variables will never be initialized because _afterInitialize is never called.

  • Broken Core Logic: Since launchStartBlock remains 0, the beforeSwap function will revert on every single trade due to the if (launchStartBlock == 0) revert PoolNotInitialized(); check, making the pool unusable.

Proof of Concept-
1. The developer runs the script.
2. The miner finds an address like 0x01... (where the 1st bit is for BEFORE_INITIALIZE).
3. The pool is initialized via PoolManager.
4. PoolManager checks the hook address for the AFTER_INITIALIZE bit (the 3rd bit).
5. The bit is not set. PoolManager skips the hook and completes initialization.
6.A user attempts to swap.
- beforeSwap runs, sees launchStartBlock is still the default 0, and reverts the user's transaction.

Recommended Mitigation

Update the flags in the deployment script to match the actual function overrides implemented in the contract.

- uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_INITIALIZE_FLAG);
+ uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_INITIALIZE_FLAG);
Updates

Appeal created

chaossr Lead Judge 17 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Deploy Script Uses Wrong Hook Flag

Support

FAQs

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

Give us feedback!