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