Normal behavior: In Uniswap v4, hook permissions are encoded directly into the hook address via specific bit flags. These permissions determine which lifecycle callbacks (e.g., afterInitialize, beforeSwap) the PoolManager is allowed to invoke. A hook must encode exactly the same permissions that it declares in getHookPermissions(). To prevent misconfiguration, Uniswap v4 provides abstract base hook implementations (e.g., BaseHook) that validate this alignment at deployment time.
Specific issue: TokenLaunchHook declares afterInitialize as enabled in getHookPermissions() and implements critical launch initialization logic inside afterInitialize. However, the deployment script mines the hook address without encoding AFTER_INITIALIZE_FLAG and instead encodes BEFORE_INITIALIZE_FLAG, which the hook explicitly does not declare or implement.
This creates a structural mismatch between:
the permissions encoded in the hook address, and
the permissions required by the hook’s implemented functions.
As a result, the hook violates the Uniswap v4 permission invariant and is deployed in an invalid configuration.
Additionally, the hook does not inherit from BaseHook and does not perform any runtime permission validation (e.g., validateHookPermissions), allowing this invalid configuration to be deployed unchecked.
Likelihood:
Occurs deterministically on every deployment using the current deployment script, as the hook address is always mined with incorrect permissions.
Manifests immediately at pool initialization time, since hook execution eligibility is derived from the address-encoded flags.
Impact:
In strict Uniswap v4 implementations, pool initialization reverts by design, preventing the pool from being created at all.
In permissive implementations, the afterInitialize callback is silently skipped, leaving:
launchStartTime == 0
initialLiquidity == 0
This results in a permanently uninitialized launch state, causing the phased launch logic and swap restrictions to malfunction or be bypassed entirely.
This issue is a structural violation of the Uniswap v4 hook permission model and can be demonstrated without executing any swaps or writing test code.
TokenLaunchHook implements the afterInitialize function and declares afterInitialize: true in getHookPermissions().
The deployment script mines the hook address without encoding AFTER_INITIALIZE_FLAG and instead encodes BEFORE_INITIALIZE_FLAG.
The hook does not inherit from BaseHook and does not validate permission alignment during deployment.
Pool initialization reverts in strict implementations, or afterInitialize is never executed, leaving launchStartTime and initialLiquidity equal to zero.
Ensure that the permissions encoded in the hook address exactly match the permissions declared by the hook.
Remove BEFORE_INITIALIZE_FLAG
Add AFTER_INITIALIZE_FLAG
Inherit from BaseHook (or OpenZeppelin’s equivalent) to enforce permission validation at deployment time.
Alternatively, explicitly call validateHookPermissions() in the constructor to ensure the deployed hook address encodes the intended permissions.
Add deployment or test-time assertions verifying: launchStartTime != 0 after pool initialization.
https://www.cyfrin.io/blog/uniswap-v4-hooks-security-deep-dive#hooks-and-permissions
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.