// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {Script, console} from "forge-std/Script.sol"; import {WETH} from "../contracts/tokens/WETH.sol"; import {WETH10} from "../contracts/tokens/WETH10.sol"; import {CCIPWETH9Bridge} from "../contracts/ccip/CCIPWETH9Bridge.sol"; import {CCIPWETH10Bridge} from "../contracts/ccip/CCIPWETH10Bridge.sol"; /** * @title DeployAll - Canonical Multichain Deployment Script * @notice Deploys all contracts (WETH9, WETH10, CCIP Bridges, CCIPLogger) to multiple chains * @dev Chain-aware deployment that adapts to different network configurations * * Supported Chains: * - Ethereum Mainnet (chainId 1): Only deploys CCIPLogger (other contracts already deployed) * - Cronos (chainId 25): Deploys all contracts * - BSC (chainId 56): Deploys all contracts * - Polygon PoS (chainId 137): Deploys all contracts * - Gnosis Chain (chainId 100): Deploys all contracts * - Avalanche (chainId 43114): Deploys all contracts * - Base (chainId 8453): Deploys all contracts * - Arbitrum (chainId 42161): Deploys all contracts * - Optimism (chainId 10): Deploys all contracts */ contract DeployAll is Script { // Chain IDs uint256 constant MAINNET = 1; uint256 constant CRONOS = 25; uint256 constant BSC = 56; uint256 constant POLYGON = 137; uint256 constant GNOSIS = 100; uint256 constant AVALANCHE = 43114; uint256 constant BASE = 8453; uint256 constant ARBITRUM = 42161; uint256 constant OPTIMISM = 10; // CCIP Configuration per chain struct CCIPConfig { address router; address linkToken; uint64 chainSelector; } // WETH Configuration per chain struct WETHConfig { address weth9; address weth10; } // Deployment addresses (will be populated during deployment) struct DeploymentAddresses { address weth9; address weth10; address ccipWETH9Bridge; address ccipWETH10Bridge; address ccipLogger; } DeploymentAddresses public deployed; function run() external { uint256 chainId = block.chainid; // Get deployer address - when using --private-key flag, use msg.sender // Otherwise get from env (PRIVATE_KEY should have 0x prefix in .env) address deployer; try vm.envUint("PRIVATE_KEY") returns (uint256 pk) { deployer = vm.addr(pk); } catch { // If env read fails, use msg.sender (when --private-key is used from CLI) deployer = msg.sender; } console.log("=========================================="); console.log("Multichain Deployment Script"); console.log("=========================================="); console.log("Chain ID:", chainId); console.log("Deployer:", deployer); console.log("Deployer Balance:", deployer.balance / 1e18, "ETH/Native"); // Get chain-specific configuration CCIPConfig memory ccipConfig = getCCIPConfig(chainId); WETHConfig memory wethConfig = getWETHConfig(chainId); console.log("\nCCIP Configuration:"); console.log(" Router:", ccipConfig.router); console.log(" LINK Token:", ccipConfig.linkToken); console.log(" Chain Selector:", ccipConfig.chainSelector); console.log("\nWETH Configuration:"); console.log(" WETH9:", wethConfig.weth9); console.log(" WETH10:", wethConfig.weth10); // startBroadcast() will use --private-key from CLI if provided // Otherwise it will try to use PRIVATE_KEY from env vm.startBroadcast(); // Determine deployment strategy based on chain if (chainId == MAINNET) { // Ethereum Mainnet: Only deploy CCIPLogger console.log("\n=== Ethereum Mainnet Deployment ==="); console.log("Skipping WETH9, WETH10, and Bridges (already deployed)"); console.log("Deploying CCIPLogger only..."); deployed.ccipLogger = deployCcipLogger(ccipConfig); } else { // All other chains: Deploy everything console.log("\n=== Full Deployment for Chain", chainId, "==="); // Deploy WETH9 if needed if (wethConfig.weth9 == address(0)) { console.log("\nDeploying WETH9..."); deployed.weth9 = deployWETH9(); } else { console.log("\nUsing existing WETH9 at:", wethConfig.weth9); deployed.weth9 = wethConfig.weth9; } // Deploy WETH10 if needed if (wethConfig.weth10 == address(0)) { console.log("\nDeploying WETH10..."); deployed.weth10 = deployWETH10(); } else { console.log("\nUsing existing WETH10 at:", wethConfig.weth10); deployed.weth10 = wethConfig.weth10; } // Deploy CCIP Bridges console.log("\nDeploying CCIPWETH9Bridge..."); deployed.ccipWETH9Bridge = deployCCIPWETH9Bridge( ccipConfig.router, deployed.weth9, ccipConfig.linkToken ); console.log("\nDeploying CCIPWETH10Bridge..."); deployed.ccipWETH10Bridge = deployCCIPWETH10Bridge( ccipConfig.router, deployed.weth10, ccipConfig.linkToken ); // Deploy CCIPLogger console.log("\nDeploying CCIPLogger..."); deployed.ccipLogger = deployCcipLogger(ccipConfig); } vm.stopBroadcast(); // Print deployment summary printDeploymentSummary(chainId); } /** * @notice Get CCIP configuration for a specific chain */ function getCCIPConfig(uint256 chainId) internal view returns (CCIPConfig memory) { if (chainId == MAINNET) { return CCIPConfig({ router: vm.envAddress("CCIP_ETH_ROUTER"), linkToken: vm.envAddress("CCIP_ETH_LINK_TOKEN"), chainSelector: uint64(vm.envUint("ETH_MAINNET_SELECTOR")) }); } else if (chainId == CRONOS) { return CCIPConfig({ router: vm.envAddress("CCIP_CRONOS_ROUTER"), linkToken: vm.envAddress("CCIP_CRONOS_LINK_TOKEN"), chainSelector: uint64(vm.envUint("CRONOS_SELECTOR")) }); } else if (chainId == BSC) { return CCIPConfig({ router: vm.envAddress("CCIP_BSC_ROUTER"), linkToken: vm.envAddress("CCIP_BSC_LINK_TOKEN"), chainSelector: uint64(vm.envUint("BSC_SELECTOR")) }); } else if (chainId == POLYGON) { return CCIPConfig({ router: vm.envAddress("CCIP_POLYGON_ROUTER"), linkToken: vm.envAddress("CCIP_POLYGON_LINK_TOKEN"), chainSelector: uint64(vm.envUint("POLYGON_SELECTOR")) }); } else if (chainId == GNOSIS) { return CCIPConfig({ router: vm.envAddress("CCIP_GNOSIS_ROUTER"), linkToken: vm.envAddress("CCIP_GNOSIS_LINK_TOKEN"), chainSelector: uint64(vm.envUint("GNOSIS_SELECTOR")) }); } else if (chainId == AVALANCHE) { return CCIPConfig({ router: vm.envAddress("CCIP_AVALANCHE_ROUTER"), linkToken: vm.envAddress("CCIP_AVALANCHE_LINK_TOKEN"), chainSelector: uint64(vm.envUint("AVALANCHE_SELECTOR")) }); } else if (chainId == BASE) { return CCIPConfig({ router: vm.envAddress("CCIP_BASE_ROUTER"), linkToken: vm.envAddress("CCIP_BASE_LINK_TOKEN"), chainSelector: uint64(vm.envUint("BASE_SELECTOR")) }); } else if (chainId == ARBITRUM) { return CCIPConfig({ router: vm.envAddress("CCIP_ARBITRUM_ROUTER"), linkToken: vm.envAddress("CCIP_ARBITRUM_LINK_TOKEN"), chainSelector: uint64(vm.envUint("ARBITRUM_SELECTOR")) }); } else if (chainId == OPTIMISM) { return CCIPConfig({ router: vm.envAddress("CCIP_OPTIMISM_ROUTER"), linkToken: vm.envAddress("CCIP_OPTIMISM_LINK_TOKEN"), chainSelector: uint64(vm.envUint("OPTIMISM_SELECTOR")) }); } else { revert("Unsupported chain"); } } /** * @notice Get WETH configuration for a specific chain */ function getWETHConfig(uint256 chainId) internal returns (WETHConfig memory) { if (chainId == MAINNET) { return WETHConfig({ weth9: vm.envAddress("WETH9_MAINNET"), weth10: vm.envAddress("WETH10_MAINNET") }); } else if (chainId == CRONOS) { address weth9 = vm.envOr("WETH9_CRONOS", address(0)); address weth10 = vm.envOr("WETH10_CRONOS", address(0)); return WETHConfig({weth9: weth9, weth10: weth10}); } else if (chainId == BSC) { address weth9 = vm.envOr("WETH9_BSC", address(0)); address weth10 = vm.envOr("WETH10_BSC", address(0)); return WETHConfig({weth9: weth9, weth10: weth10}); } else if (chainId == POLYGON) { address weth9 = vm.envOr("WETH9_POLYGON", address(0)); address weth10 = vm.envOr("WETH10_POLYGON", address(0)); return WETHConfig({weth9: weth9, weth10: weth10}); } else if (chainId == GNOSIS) { address weth9 = vm.envOr("WETH9_GNOSIS", address(0)); address weth10 = vm.envOr("WETH10_GNOSIS", address(0)); return WETHConfig({weth9: weth9, weth10: weth10}); } else if (chainId == AVALANCHE) { address weth9 = vm.envOr("WETH9_AVALANCHE", address(0)); address weth10 = vm.envOr("WETH10_AVALANCHE", address(0)); return WETHConfig({weth9: weth9, weth10: weth10}); } else if (chainId == BASE) { address weth9 = vm.envOr("WETH9_BASE", address(0)); address weth10 = vm.envOr("WETH10_BASE", address(0)); return WETHConfig({weth9: weth9, weth10: weth10}); } else if (chainId == ARBITRUM) { address weth9 = vm.envOr("WETH9_ARBITRUM", address(0)); address weth10 = vm.envOr("WETH10_ARBITRUM", address(0)); return WETHConfig({weth9: weth9, weth10: weth10}); } else if (chainId == OPTIMISM) { address weth9 = vm.envOr("WETH9_OPTIMISM", address(0)); address weth10 = vm.envOr("WETH10_OPTIMISM", address(0)); return WETHConfig({weth9: weth9, weth10: weth10}); } else { revert("Unsupported chain"); } } /** * @notice Deploy WETH9 contract */ function deployWETH9() internal returns (address) { WETH weth9 = new WETH(); return address(weth9); } /** * @notice Deploy WETH10 contract */ function deployWETH10() internal returns (address) { WETH10 weth10 = new WETH10(); return address(weth10); } /** * @notice Deploy CCIPWETH9Bridge */ function deployCCIPWETH9Bridge( address router, address weth9, address feeToken ) internal returns (address) { CCIPWETH9Bridge bridge = new CCIPWETH9Bridge(router, weth9, feeToken); return address(bridge); } /** * @notice Deploy CCIPWETH10Bridge */ function deployCCIPWETH10Bridge( address router, address weth10, address feeToken ) internal returns (address) { CCIPWETH10Bridge bridge = new CCIPWETH10Bridge(router, weth10, feeToken); return address(bridge); } /** * @notice Deploy CCIPLogger * @dev Note: This assumes CCIPLogger contract exists. If it uses Hardhat/OpenZeppelin, * you may need to deploy it separately using the Hardhat script. */ function deployCcipLogger(CCIPConfig memory config) internal returns (address) { // TODO: If CCIPLogger is a Solidity contract in this repo, deploy it here // For now, this is a placeholder. CCIPLogger may need to be deployed via Hardhat // due to OpenZeppelin dependencies. // Example deployment (adjust based on actual CCIPLogger contract): // CCIPLogger logger = new CCIPLogger( // config.router, // vm.envOr("AUTHORIZED_SIGNER", address(0)), // config.chainSelector // ); // return address(logger); console.log("WARNING: CCIPLogger deployment not implemented in Foundry script."); console.log("Please deploy CCIPLogger using the Hardhat script:"); console.log(" npm run deploy:logger:mainnet (for mainnet)"); console.log(" Or create a chain-specific deployment script."); return address(0); // Placeholder } /** * @notice Print deployment summary */ function printDeploymentSummary(uint256 chainId) internal view { console.log("\n=========================================="); console.log("Deployment Summary - Chain ID:", chainId); console.log("=========================================="); if (chainId != MAINNET) { console.log("WETH9:", deployed.weth9); console.log("WETH10:", deployed.weth10); console.log("CCIPWETH9Bridge:", deployed.ccipWETH9Bridge); console.log("CCIPWETH10Bridge:", deployed.ccipWETH10Bridge); } console.log("CCIPLogger:", deployed.ccipLogger); console.log("\n=========================================="); console.log("Next Steps:"); console.log("1. Verify contracts on explorer"); console.log("2. Update .env with deployed addresses"); console.log("3. Configure bridge destinations"); console.log("4. Test cross-chain transfers"); console.log("=========================================="); } }