#!/usr/bin/env bash # Deploy contracts in parallel where dependencies allow # Uses .env file for configuration set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../lib/init.sh" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" cd "$PROJECT_ROOT" # Load environment variables if [ ! -f .env ]; then log_error "Error: .env file not found" echo "Please create .env file with required variables" exit 1 fi source .env # Check required variables if [ -z "$PRIVATE_KEY" ]; then log_error "Error: PRIVATE_KEY not set in .env" exit 1 fi if [ -z "$RPC_URL" ]; then log_error "Error: RPC_URL not set in .env" exit 1 fi # Verify RPC endpoint is accessible log_warn "Verifying RPC endpoint..." if ! curl -s -X POST "$RPC_URL" -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' > /dev/null 2>&1; then log_error "Error: RPC endpoint is not accessible" exit 1 fi log_success "✅ RPC endpoint is accessible" # Export variables for parallel execution export PRIVATE_KEY export RPC_URL export CCIP_ROUTER="${CCIP_ROUTER:-}" export CCIP_FEE_TOKEN="${CCIP_FEE_TOKEN:-0x0000000000000000000000000000000000000000}" export ORACLE_DESCRIPTION="${ORACLE_DESCRIPTION:-ETH/USD Price Feed}" export ORACLE_HEARTBEAT="${ORACLE_HEARTBEAT:-60}" export ORACLE_DEVIATION_THRESHOLD="${ORACLE_DEVIATION_THRESHOLD:-50}" # Function to update .env file update_env() { local key=$1 local value=$2 if grep -q "^${key}=" .env; then sed -i "s|^${key}=.*|${key}=${value}|" .env else echo "${key}=${value}" >> .env fi log_success "✅ Updated .env: ${key}=${value}" } # Function to extract deployed address from forge output extract_address() { local output=$1 echo "$output" | grep -oE "0x[a-fA-F0-9]{40}" | tail -1 || echo "" } log_success "=== Starting Parallel Contract Deployment ===" # Phase 1: Independent contracts (can be deployed in parallel) log_warn "Phase 1: Deploying independent contracts in parallel..." # Deploy Multicall, WETH9, and WETH10 in parallel { log_warn "Deploying Multicall..." MULTICALL_OUTPUT=$(forge script script/DeployMulticall.s.sol:DeployMulticall \ --rpc-url "$RPC_URL" \ --broadcast \ --private-key "$PRIVATE_KEY" \ --verify 2>&1) MULTICALL_ADDRESS=$(extract_address "$MULTICALL_OUTPUT") if [ -n "$MULTICALL_ADDRESS" ]; then update_env "MULTICALL_ADDRESS" "$MULTICALL_ADDRESS" log_success "✅ Multicall deployed at: $MULTICALL_ADDRESS" fi } & MULTICALL_PID=$! { log_warn "Deploying WETH9..." WETH9_OUTPUT=$(forge script script/DeployWETH.s.sol:DeployWETH \ --rpc-url "$RPC_URL" \ --broadcast \ --private-key "$PRIVATE_KEY" \ --verify 2>&1) WETH9_ADDRESS=$(extract_address "$WETH9_OUTPUT") if [ -n "$WETH9_ADDRESS" ]; then update_env "WETH9_ADDRESS" "$WETH9_ADDRESS" log_success "✅ WETH9 deployed at: $WETH9_ADDRESS" fi } & WETH9_PID=$! { log_warn "Deploying WETH10..." WETH10_OUTPUT=$(forge script script/DeployWETH10.s.sol:DeployWETH10 \ --rpc-url "$RPC_URL" \ --broadcast \ --private-key "$PRIVATE_KEY" \ --verify 2>&1) WETH10_ADDRESS=$(extract_address "$WETH10_OUTPUT") if [ -n "$WETH10_ADDRESS" ]; then update_env "WETH10_ADDRESS" "$WETH10_ADDRESS" log_success "✅ WETH10 deployed at: $WETH10_ADDRESS" fi } & WETH10_PID=$! # Wait for Phase 1 to complete wait $MULTICALL_PID wait $WETH9_PID wait $WETH10_PID # Reload .env to get deployed addresses source .env # Phase 2: CCIP Router (if needed) if [ -z "$CCIP_ROUTER" ] || [ "$CCIP_ROUTER" = "0x0000000000000000000000000000000000000000" ]; then log_warn "Phase 2: Deploying CCIP Router..." CCIP_ROUTER_OUTPUT=$(forge script script/DeployCCIPRouter.s.sol:DeployCCIPRouter \ --sig "run(address,uint256,uint256)" \ "$CCIP_FEE_TOKEN" \ "1000000000000000" \ "1000000000" \ --rpc-url "$RPC_URL" \ --broadcast \ --private-key "$PRIVATE_KEY" \ --verify 2>&1) CCIP_ROUTER=$(extract_address "$CCIP_ROUTER_OUTPUT") if [ -n "$CCIP_ROUTER" ]; then update_env "CCIP_ROUTER" "$CCIP_ROUTER" log_success "✅ CCIP Router deployed at: $CCIP_ROUTER" fi else log_success "✅ CCIP Router already configured: $CCIP_ROUTER" fi # Reload .env source .env # Phase 3: Bridge contracts (can be deployed in parallel if dependencies are met) if [ "${DEPLOY_BRIDGES:-true}" = "true" ] && [ -n "$CCIP_ROUTER" ] && [ -n "$WETH9_ADDRESS" ] && [ -n "$WETH10_ADDRESS" ]; then log_warn "Phase 3: Deploying CCIP bridges in parallel..." { log_warn "Deploying CCIPWETH9Bridge..." export CCIP_ROUTER WETH9_ADDRESS CCIP_FEE_TOKEN BRIDGE9_OUTPUT=$(forge script script/DeployCCIPWETH9Bridge.s.sol:DeployCCIPWETH9Bridge \ --rpc-url "$RPC_URL" \ --broadcast \ --private-key "$PRIVATE_KEY" \ --verify 2>&1) CCIPWETH9BRIDGE_ADDRESS=$(extract_address "$BRIDGE9_OUTPUT") if [ -n "$CCIPWETH9BRIDGE_ADDRESS" ]; then update_env "CCIPWETH9BRIDGE_ADDRESS" "$CCIPWETH9BRIDGE_ADDRESS" log_success "✅ CCIPWETH9Bridge deployed at: $CCIPWETH9BRIDGE_ADDRESS" fi } & BRIDGE9_PID=$! { log_warn "Deploying CCIPWETH10Bridge..." export CCIP_ROUTER WETH10_ADDRESS CCIP_FEE_TOKEN BRIDGE10_OUTPUT=$(forge script script/DeployCCIPWETH10Bridge.s.sol:DeployCCIPWETH10Bridge \ --rpc-url "$RPC_URL" \ --broadcast \ --private-key "$PRIVATE_KEY" \ --verify 2>&1) CCIPWETH10BRIDGE_ADDRESS=$(extract_address "$BRIDGE10_OUTPUT") if [ -n "$CCIPWETH10BRIDGE_ADDRESS" ]; then update_env "CCIPWETH10BRIDGE_ADDRESS" "$CCIPWETH10BRIDGE_ADDRESS" log_success "✅ CCIPWETH10Bridge deployed at: $CCIPWETH10BRIDGE_ADDRESS" fi } & BRIDGE10_PID=$! wait $BRIDGE9_PID wait $BRIDGE10_PID else log_warn "⚠️ Bridge deployment skipped (DEPLOY_BRIDGES=false or missing dependencies)" fi # Phase 4: Oracle and MultiSig (can be deployed in parallel if MultiSig owners are set) log_warn "Phase 4: Deploying Oracle and MultiSig in parallel..." # Deploy Oracle (independent) { export ORACLE_DESCRIPTION ORACLE_HEARTBEAT ORACLE_DEVIATION_THRESHOLD ORACLE_OUTPUT=$(forge script script/DeployOracle.s.sol:DeployOracle \ --rpc-url "$RPC_URL" \ --broadcast \ --private-key "$PRIVATE_KEY" \ --verify 2>&1) # Extract addresses (Oracle deploys both aggregator and proxy) ORACLE_AGGREGATOR_ADDRESS=$(echo "$ORACLE_OUTPUT" | grep -i "Aggregator" | grep -oE "0x[a-fA-F0-9]{40}" | head -1) ORACLE_PROXY_ADDRESS=$(echo "$ORACLE_OUTPUT" | grep -i "Proxy" | grep -oE "0x[a-fA-F0-9]{40}" | tail -1) if [ -n "$ORACLE_AGGREGATOR_ADDRESS" ]; then update_env "ORACLE_AGGREGATOR_ADDRESS" "$ORACLE_AGGREGATOR_ADDRESS" log_success "✅ Oracle Aggregator deployed at: $ORACLE_AGGREGATOR_ADDRESS" fi if [ -n "$ORACLE_PROXY_ADDRESS" ]; then update_env "ORACLE_PROXY_ADDRESS" "$ORACLE_PROXY_ADDRESS" log_success "✅ Oracle Proxy deployed at: $ORACLE_PROXY_ADDRESS" fi } & ORACLE_PID=$! # Deploy MultiSig (independent if owners are set) if [ -z "$MULTISIG_OWNERS" ]; then log_warn "⚠️ MULTISIG_OWNERS not set. Skipping MultiSig deployment." log_warn "Set MULTISIG_OWNERS in .env (comma-separated addresses) to deploy." MULTISIG_PID="" else { export OWNERS="$MULTISIG_OWNERS" MULTISIG_OUTPUT=$(forge script script/DeployMultiSig.s.sol:DeployMultiSig \ --rpc-url "$RPC_URL" \ --broadcast \ --private-key "$PRIVATE_KEY" \ --verify 2>&1) MULTISIG_ADDRESS=$(extract_address "$MULTISIG_OUTPUT") if [ -n "$MULTISIG_ADDRESS" ]; then update_env "MULTISIG_ADDRESS" "$MULTISIG_ADDRESS" log_success "✅ MultiSig deployed at: $MULTISIG_ADDRESS" fi } & MULTISIG_PID=$! fi # Wait for parallel deployments to complete wait $ORACLE_PID if [ -n "$MULTISIG_PID" ]; then wait $MULTISIG_PID fi # Reload .env to get all new addresses source .env # Final summary log_success "=== Deployment Summary ===" source .env log_success "Multicall: ${MULTICALL_ADDRESS:-N/A}" log_success "WETH9: ${WETH9_ADDRESS:-N/A}" log_success "WETH10: ${WETH10_ADDRESS:-N/A}" log_success "CCIP Router: ${CCIP_ROUTER:-N/A}" log_success "CCIPWETH9Bridge: ${CCIPWETH9BRIDGE_ADDRESS:-N/A}" log_success "CCIPWETH10Bridge: ${CCIPWETH10BRIDGE_ADDRESS:-N/A}" log_success "Oracle Aggregator: ${ORACLE_AGGREGATOR_ADDRESS:-N/A}" log_success "Oracle Proxy: ${ORACLE_PROXY_ADDRESS:-N/A}" log_success "MultiSig: ${MULTISIG_ADDRESS:-N/A}" log_success "" log_success "=== Parallel Deployment Complete ===" log_success "All addresses have been updated in .env file"