Description
-
The deploy script should validate chain‑specific infrastructure before mining/deploying the hook—e.g., that poolManager, positionManager, swapRouter, and (if used) CREATE2_FACTORY are non‑zero, point to deployed contracts on the active chainid, and that the script’s mining/deploy path uses the same deployer it mined for. This prevents misdeployments on unsupported networks.
-
BaseScript derives addresses from AddressConstants.get* (block.chainid) but performs no validation that they are non‑zero or that code exists at those addresses. It proceeds to label and use them. [github.com]
-
DeployHookScript mines and deploys without any chain‑level sanity checks (and even references a commented CREATE2_FACTORY constant in other parts of the file, reported separately). There’s no guard to ensure the script is running on a supported chain or that the components are valid.
constructor() {
poolManager = IPoolManager(AddressConstants.getPoolManagerAddress(block.chainid));
positionManager = IPositionManager(payable(AddressConstants.getPositionManagerAddress(block.chainid)));
swapRouter = IUniswapV4Router04(payable(AddressConstants.getV4SwapRouterAddress(block.chainid)));
deployerAddress = getDeployer();
(currency0, currency1) = getCurrencies();
}
contract DeployHookScript is BaseScript {
function run() public {
}
}
Risk
Likelihood: Medium
Running the script on an unsupported chain (or with an incomplete AddressConstants set) is common during development and CI. In such cases, the getters can return zero addresses or wrong endpoints, and the script still runs.
Impact: Medium
-
Silent misdeployment or unusable hook: The script may mine/deploy with invalid infra, leading to a hook that cannot be attached to valid pools or that reverts at initialization/usage. This wastes gas and delays launches.
-
Hard‑to‑debug CI failures: Build pipelines may pass compilation but fail later at runtime due to invalid infra addresses not being asserted early.
Proof of Concept
The PoC deploys a small exposer for BaseScript and switches to a bogus chain id.
It demonstrates that BaseScript happily constructs with zero addresses (no throws), proving there are no chain safety checks.
pragma solidity ^0.8.26;
import {Test} from "forge-std/Test.sol";
import {BaseScript} from "../script/base.s.sol";
contract BaseScriptExposer is BaseScript {
function getAddrs()
external
view
returns (address pm, address pos, address router, address deployer)
{
pm = address(poolManager);
pos = address(positionManager);
router = address(swapRouter);
deployer = deployerAddress;
}
}
contract NoChainSafetyChecksTest is Test {
function test_BaseScript_AllowsZeroInfraOnUnsupportedChain() public {
vm.chainId(123456);
BaseScriptExposer e = new BaseScriptExposer();
(address pm, address pos, address router, ) = e.getAddrs();
assertEq(pm, address(0), "poolManager should be zero on unsupported chain");
assertEq(pos, address(0), "positionManager should be zero on unsupported chain");
assertEq(router, address(0),"swapRouter should be zero on unsupported chain");
}
}
Recommended Mitigation
// script/base.s.sol
constructor() {
- poolManager = IPoolManager(AddressConstants.getPoolManagerAddress(block.chainid));
- positionManager = IPositionManager(payable(AddressConstants.getPositionManagerAddress(block.chainid)));
- swapRouter = IUniswapV4Router04(payable(AddressConstants.getV4SwapRouterAddress(block.chainid)));
+ address pm = AddressConstants.getPoolManagerAddress(block.chainid);
+ address pos = AddressConstants.getPositionManagerAddress(block.chainid);
+ address router = AddressConstants.getV4SwapRouterAddress(block.chainid);
+
+ require(pm != address(0), "BaseScript: unsupported chain - poolManager");
+ require(pos != address(0), "BaseScript: unsupported chain - positionManager");
+ require(router != address(0),"BaseScript: unsupported chain - swapRouter");
+ require(pm.code.length > 0, "BaseScript: poolManager has no code");
+ require(pos.code.length > 0, "BaseScript: positionManager has no code");
+ require(router.code.length > 0,"BaseScript: swapRouter has no code");
+
+ poolManager = IPoolManager(pm);
+ positionManager = IPositionManager(payable(pos));
+ swapRouter = IUniswapV4Router04(payable(router));
deployerAddress = getDeployer();
(currency0, currency1) = getCurrencies();
}