Description
-
phase1LimitBps and phase2LimitBps are validated to not exceed 10000 (100%), but they can be set to 0.
-
A zero limit means maxSwapAmount = 0, and any swap would exceed the limit, triggering penalties immediately.
constructor(...) BaseHook(_poolManager) {
if (
_phase1LimitBps > 10000 || _phase2LimitBps > 10000 ||
_phase1PenaltyBps > 10000 || _phase2PenaltyBps > 10000
) revert InvalidConstructorParams();
phase1LimitBps = _phase1LimitBps;
phase2LimitBps = _phase2LimitBps;
}
In _beforeSwap:
uint256 maxSwapAmount = (initialLiquidity * phaseLimitBps) / 10000;
if (!applyPenalty && addressSwappedAmount[sender] + swapAmount > maxSwapAmount) {
applyPenalty = true;
}
Risk
Likelihood:
Impact:
Proof of Concept
Deploy hook with 0 limit values - constructor accepts them without validation.
function test_ZeroLimitAllowed() public {
bytes memory creationCode = type(TokenLaunchHook).creationCode;
bytes memory constructorArgs = abi.encode(
manager,
phase1Duration,
phase2Duration,
0,
0,
phase1Cooldown,
phase2Cooldown,
phase1PenaltyBps,
phase2PenaltyBps
);
uint160 flags = uint160(Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG);
(, bytes32 salt) = HookMiner.find(address(this), flags, creationCode, constructorArgs);
TokenLaunchHook strictHook = new TokenLaunchHook{salt: salt}(
manager,
phase1Duration,
phase2Duration,
0,
0,
phase1Cooldown,
phase2Cooldown,
phase1PenaltyBps,
phase2PenaltyBps
);
assertEq(strictHook.phase1LimitBps(), 0, "Zero limit accepted");
}
Recommended Mitigation
Add zero-check for limit parameters in constructor.
constructor(...) BaseHook(_poolManager) {
if (_phase1Duration == 0 || _phase2Duration == 0) revert InvalidConstructorParams();
+ if (_phase1LimitBps == 0 || _phase2LimitBps == 0) revert InvalidConstructorParams();
if (
_phase1LimitBps > 10000 || _phase2LimitBps > 10000 ||
_phase1PenaltyBps > 10000 || _phase2PenaltyBps > 10000
) revert InvalidConstructorParams();
// ...
}