Deployment script uses wrong hook permission flag causing deployment failure
Description
The deployment script mines for a hook address with incorrect permission flags. It uses BEFORE_INITIALIZE_FLAG but the hook contract declares afterInitialize: true:
Deployment script (deployLaunchHook.s.sol):
uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_INITIALIZE_FLAG);
Hook contract (TokenLaunchHook.sol):
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
@> beforeInitialize: false,
@> afterInitialize: true,
beforeSwap: true,
});
}
In Uniswap V4, hook permissions are encoded in the hook's address. The HookMiner.find() function searches for a salt that produces an address with specific permission bits set. The TokenLaunchHook contract inherits the Uniswap V4 BaseHook contract, which then inherits the Uniswap V4 Hooks library. In the BaseHook contract, there is a validateHookAddress() call in the constructor. This in turn calls Hooks.validateHookPermissions(_this,getHookPermissions()), which is used to ensure the deployed hooks address causes the intended hooks to be called, and will revert not.
This means that with the incorrect flag configuration in DeployLaunchHook.s.sol, deployments will always revert.
From BaseHook.sol:
constructor(IPoolManager _manager) ImmutableState(_manager) {
validateHookAddress(this);
}
function validateHookAddress(BaseHook _this) internal pure virtual {
Hooks.validateHookPermissions(_this, getHookPermissions());
}
From Hooks.sol:
function validateHookPermissions(IHooks self, Permissions memory permissions) internal pure {
if (
permissions.beforeInitialize != self.hasPermission(BEFORE_INITIALIZE_FLAG)
|| permissions.afterInitialize != self.hasPermission(AFTER_INITIALIZE_FLAG)
|| permissions.beforeAddLiquidity != self.hasPermission(BEFORE_ADD_LIQUIDITY_FLAG)
|| permissions.afterAddLiquidity != self.hasPermission(AFTER_ADD_LIQUIDITY_FLAG)
|| permissions.beforeRemoveLiquidity != self.hasPermission(BEFORE_REMOVE_LIQUIDITY_FLAG)
|| permissions.afterRemoveLiquidity != self.hasPermission(AFTER_REMOVE_LIQUIDITY_FLAG)
|| permissions.beforeSwap != self.hasPermission(BEFORE_SWAP_FLAG)
|| permissions.afterSwap != self.hasPermission(AFTER_SWAP_FLAG)
|| permissions.beforeDonate != self.hasPermission(BEFORE_DONATE_FLAG)
|| permissions.afterDonate != self.hasPermission(AFTER_DONATE_FLAG)
|| permissions.beforeSwapReturnDelta != self.hasPermission(BEFORE_SWAP_RETURNS_DELTA_FLAG)
|| permissions.afterSwapReturnDelta != self.hasPermission(AFTER_SWAP_RETURNS_DELTA_FLAG)
|| permissions.afterAddLiquidityReturnDelta != self.hasPermission(AFTER_ADD_LIQUIDITY_RETURNS_DELTA_FLAG)
|| permissions.afterRemoveLiquidityReturnDelta
!= self.hasPermission(AFTER_REMOVE_LIQUIDITY_RETURNS_DELTA_FLAG)
) {
HookAddressNotValid.selector.revertWith(address(self));
}
}
Risk
Likelihood:
Impact:
Proof of Concept
Add the following test to TokenLaunchHookUnit.t.sol
function test_IncorrectHookFlags() public {
address CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
bytes memory creationCode = type(TokenLaunchHook).creationCode;
bytes memory constructorArgs = abi.encode(
manager,
phase1Duration,
phase2Duration,
phase1LimitBps,
phase2LimitBps,
phase1Cooldown,
phase2Cooldown,
phase1PenaltyBps,
phase2PenaltyBps
);
uint160 flags = uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG);
(, bytes32 salt) = HookMiner.find(CREATE2_FACTORY, flags, creationCode, constructorArgs);
vm.expectRevert();
TokenLaunchHook antiBotHookTwo = new TokenLaunchHook{salt: salt}(
manager,
phase1Duration,
phase2Duration,
phase1LimitBps,
phase2LimitBps,
phase1Cooldown,
phase2Cooldown,
phase1PenaltyBps,
phase2PenaltyBps
);
}
Recommended Mitigation
Fix the deployment script to use the correct flag:
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);
// Mine a salt that will produce a hook address with the correct flags
// ...
}