- Introduced Aggregator.sol for Chainlink-compatible oracle functionality, including round-based updates and access control. - Added OracleWithCCIP.sol to extend Aggregator with CCIP cross-chain messaging capabilities. - Created .gitmodules to include OpenZeppelin contracts as a submodule. - Developed a comprehensive deployment guide in NEXT_STEPS_COMPLETE_GUIDE.md for Phase 2 and smart contract deployment. - Implemented Vite configuration for the orchestration portal, supporting both Vue and React frameworks. - Added server-side logic for the Multi-Cloud Orchestration Portal, including API endpoints for environment management and monitoring. - Created scripts for resource import and usage validation across non-US regions. - Added tests for CCIP error handling and integration to ensure robust functionality. - Included various new files and directories for the orchestration portal and deployment scripts.
356 lines
14 KiB
Solidity
356 lines
14 KiB
Solidity
// 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("==========================================");
|
|
}
|
|
}
|
|
|