Vanguard

First Flight #56
Beginner FriendlyDeFiFoundry
0 EXP
Submission Details
Impact: medium
Likelihood: medium

No chain-specific safety checks

Author Revealed upon completion

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.

// script/base.s.sol (excerpt)
constructor() {
poolManager = IPoolManager(AddressConstants.getPoolManagerAddress(block.chainid)); // @> no non-zero check
positionManager = IPositionManager(payable(AddressConstants.getPositionManagerAddress(block.chainid))); // @> no code check
swapRouter = IUniswapV4Router04(payable(AddressConstants.getV4SwapRouterAddress(block.chainid))); // @> no validation
deployerAddress = getDeployer(); // returns first wallet from vm.getWallets()
(currency0, currency1) = getCurrencies();
// No require(...) assertions that the addresses are valid for the current chain // @>
}
// script/deployLaunchHook.s.sol (excerpt)
contract DeployHookScript is BaseScript {
function run() public {
// ... mines salt and deploys ...
// No assert/require that poolManager/positionManager/swapRouter/CREATE2 factory are valid
}
}

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.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {Test} from "forge-std/Test.sol";
// Import the script base used by DeployHookScript
import {BaseScript} from "../script/base.s.sol"; // matches the actual lowercase file name
contract BaseScriptExposer is BaseScript {
// Expose the derived addresses for testing
function getAddrs()
external
view
returns (address pm, address pos, address router, address deployer)
{
// poolManager, positionManager, swapRouter are immutable interface-typed
pm = address(poolManager);
pos = address(positionManager);
router = address(swapRouter);
deployer = deployerAddress;
}
}
contract NoChainSafetyChecksTest is Test {
function test_BaseScript_AllowsZeroInfraOnUnsupportedChain() public {
// Switch to a bogus, unsupported chain id so AddressConstants.get*(chainid) returns zeroes
vm.chainId(123456);
// Deploy the exposer; constructor of BaseScript runs here without validation
BaseScriptExposer e = new BaseScriptExposer();
(address pm, address pos, address router, ) = e.getAddrs();
// If there were safety checks, construction would revert on zero addresses.
// Instead, we observe zeros and no revert, proving missing guards.
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

  • Add explicit validation in BaseScript (and call sites) so misconfiguration fails fast.

// 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();
}

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!