#!/usr/bin/env bash # Deploy All Contracts in Proper Order # This script deploys the blockchain infrastructure (if needed) and all contracts in the correct order set -e # Script directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../lib/init.sh" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" cd "$PROJECT_ROOT" log_info "=== Deploy All Contracts in Proper Order ===" # Check if .env exists if [ ! -f .env ]; then log_error "Error: .env file not found" echo "Creating .env from .env.example..." if [ -f .env.example ]; then cp .env.example .env log_success "✅ Created .env from .env.example" log_warn "Please configure .env with your values before continuing" exit 1 else log_error "Error: .env.example not found" exit 1 fi fi # Load environment variables source .env # Check PRIVATE_KEY if [ -z "$PRIVATE_KEY" ]; then log_error "Error: PRIVATE_KEY not set in .env" exit 1 fi # Check RPC_URL if [ -z "$RPC_URL" ]; then log_warn "RPC_URL not set in .env" log_warn "Options:" echo "1. Start local Anvil testnet (for testing)" echo "2. Set RPC_URL to existing blockchain endpoint" echo "3. Deploy blockchain infrastructure first (Azure/Kubernetes)" read -p "Choose option (1/2/3): " choice case $choice in 1) log_warn "Starting local Anvil testnet..." # Start Anvil in background anvil --chain-id 138 --port 8545 > /tmp/anvil.log 2>&1 & ANVIL_PID=$! echo "Anvil started with PID: $ANVIL_PID" sleep 2 # Set RPC_URL RPC_URL="http://localhost:8545" 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 } update_env "RPC_URL" "$RPC_URL" log_success "✅ RPC_URL set to: ${RPC_URL}" log_warn "Note: Anvil is running in background. Kill it with: kill $ANVIL_PID" ;; 2) read -p "Enter RPC URL: " RPC_URL 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 } update_env "RPC_URL" "$RPC_URL" log_success "✅ RPC_URL set to: ${RPC_URL}" ;; 3) log_warn "Deploying blockchain infrastructure..." echo "This requires Azure credentials and Terraform" echo "Please follow the infrastructure deployment guide:" echo " docs/DEPLOYMENT_ORDER.md" echo "After infrastructure is deployed, set RPC_URL in .env and run this script again" exit 0 ;; *) log_error "Invalid choice" exit 1 ;; esac 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" echo "Please ensure the blockchain is deployed and RPC endpoint is accessible" exit 1 fi log_success "✅ RPC endpoint is accessible" # 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 address from forge script output extract_address() { local output="$1" echo "$output" | grep -oE "0x[a-fA-F0-9]{40}" | tail -1 } # Function to deploy contract deploy_contract() { local script_name=$1 local contract_name=$2 local env_var=$3 local additional_args=$4 log_warn "Deploying ${contract_name}..." # Run deployment script local output=$(forge script "$script_name" \ --rpc-url "$RPC_URL" \ --broadcast \ --private-key "$PRIVATE_KEY" \ -vvv \ $additional_args 2>&1) # Extract deployed address local address=$(extract_address "$output") if [ -z "$address" ]; then log_error "Error: Failed to extract address for ${contract_name}" echo "Output: $output" return 1 fi log_success "✅ ${contract_name} deployed at: ${address}" update_env "$env_var" "$address" echo "$address" } # Deployment Order: # 1. Mock LINK Token (if needed, or use existing) # 2. CCIP Router # 3. WETH9 # 4. WETH10 # 5. CCIPWETH9Bridge # 6. CCIPWETH10Bridge # 7. Oracle Aggregator # 8. CCIPSender/Receiver log_success "=== Starting Contract Deployment ===" # Step 1: Deploy Mock LINK Token (if needed) if [ -z "$CCIP_FEE_TOKEN" ] || [ "$CCIP_FEE_TOKEN" = "0x0000000000000000000000000000000000000000" ]; then log_warn "Step 1: Deploying Mock LINK Token..." LINK_TOKEN_ADDRESS=$(deploy_contract "script/DeployMockLinkToken.s.sol:DeployMockLinkToken" "Mock LINK Token" "CCIP_FEE_TOKEN") if [ -z "$LINK_TOKEN_ADDRESS" ]; then log_error "Error: Failed to deploy Mock LINK Token" exit 1 fi CCIP_FEE_TOKEN="$LINK_TOKEN_ADDRESS" # Fund deployer with LINK tokens log_warn "Funding deployer with LINK tokens..." DEPLOYER_ADDRESS=$(cast wallet address --private-key "$PRIVATE_KEY" 2>/dev/null || echo "") if [ -n "$DEPLOYER_ADDRESS" ]; then cast send "$CCIP_FEE_TOKEN" "mint(address,uint256)" "$DEPLOYER_ADDRESS" "1000000e18" \ --rpc-url "$RPC_URL" \ --private-key "$PRIVATE_KEY" \ > /dev/null 2>&1 || echo "Note: LINK token minting may need manual execution" fi else log_success "✅ CCIP Fee Token already configured: ${CCIP_FEE_TOKEN}" fi # Step 2: Deploy CCIP Router if [ -z "$CCIP_ROUTER" ] || [ "$CCIP_ROUTER" = "0x0000000000000000000000000000000000000000" ]; then log_warn "Step 2: Deploying CCIP Router..." # CCIP Router configuration BASE_FEE=${CCIP_BASE_FEE:-"1000000000000000"} # 0.001 ETH DATA_FEE_PER_BYTE=${CCIP_DATA_FEE_PER_BYTE:-"1000000000"} # 1 gwei per byte # Deploy CCIP Router CCIP_ROUTER_ADDRESS=$(forge script script/DeployCCIPRouter.s.sol:DeployCCIPRouter \ --sig "run(address,uint256,uint256)" "$CCIP_FEE_TOKEN" "$BASE_FEE" "$DATA_FEE_PER_BYTE" \ --rpc-url "$RPC_URL" \ --broadcast \ --private-key "$PRIVATE_KEY" \ -vvv 2>&1 | grep -oE "0x[a-fA-F0-9]{40}" | tail -1) if [ -n "$CCIP_ROUTER_ADDRESS" ]; then update_env "CCIP_ROUTER" "$CCIP_ROUTER_ADDRESS" CCIP_ROUTER="$CCIP_ROUTER_ADDRESS" log_success "✅ CCIP Router deployed at: ${CCIP_ROUTER}" # Add supported chains (default: Ethereum Mainnet chain selector) log_warn "Configuring CCIP Router..." ETHEREUM_CHAIN_SELECTOR="5009297550715157269" cast send "$CCIP_ROUTER" "addSupportedChain(uint64)" "$ETHEREUM_CHAIN_SELECTOR" \ --rpc-url "$RPC_URL" \ --private-key "$PRIVATE_KEY" \ > /dev/null 2>&1 || echo "Note: Chain configuration may need manual execution" else log_error "Error: Failed to deploy CCIP Router" exit 1 fi else log_success "✅ CCIP Router already configured: ${CCIP_ROUTER}" fi # Step 3: Deploy WETH9 if [ -z "$WETH9_ADDRESS" ] || [ "$WETH9_ADDRESS" = "0x0000000000000000000000000000000000000000" ] || [ "${DEPLOY_WETH9:-true}" = "true" ]; then log_warn "Step 3: Deploying WETH9..." WETH9_ADDRESS=$(deploy_contract "script/DeployWETH.s.sol:DeployWETH" "WETH9" "WETH9_ADDRESS") if [ -z "$WETH9_ADDRESS" ]; then log_error "Error: Failed to deploy WETH9" exit 1 fi else log_success "✅ WETH9 already configured: ${WETH9_ADDRESS}" fi # Step 4: Deploy WETH10 if [ -z "$WETH10_ADDRESS" ] || [ "$WETH10_ADDRESS" = "0x0000000000000000000000000000000000000000" ] || [ "${DEPLOY_WETH10:-true}" = "true" ]; then log_warn "Step 4: Deploying WETH10..." WETH10_ADDRESS=$(deploy_contract "script/DeployWETH10.s.sol:DeployWETH10" "WETH10" "WETH10_ADDRESS") if [ -z "$WETH10_ADDRESS" ]; then log_error "Error: Failed to deploy WETH10" exit 1 fi else log_success "✅ WETH10 already configured: ${WETH10_ADDRESS}" fi # Step 5: Deploy CCIPWETH9Bridge if [ "${DEPLOY_BRIDGES:-true}" = "true" ]; then log_warn "Step 5: Deploying CCIPWETH9Bridge..." if [ -z "$CCIP_ROUTER" ] || [ -z "$WETH9_ADDRESS" ] || [ -z "$CCIP_FEE_TOKEN" ]; then log_error "Error: Missing required configuration for CCIPWETH9Bridge" echo "Required: CCIP_ROUTER, WETH9_ADDRESS, CCIP_FEE_TOKEN" exit 1 fi # Update .env temporarily for deployment script export CCIP_ROUTER export WETH9_ADDRESS export CCIP_FEE_TOKEN CCIPWETH9BRIDGE_ADDRESS=$(deploy_contract "script/DeployCCIPWETH9Bridge.s.sol:DeployCCIPWETH9Bridge" "CCIPWETH9Bridge" "CCIPWETH9BRIDGE_ADDRESS") if [ -z "$CCIPWETH9BRIDGE_ADDRESS" ]; then log_error "Error: Failed to deploy CCIPWETH9Bridge" exit 1 fi else log_success "✅ CCIPWETH9Bridge deployment skipped" fi # Step 6: Deploy CCIPWETH10Bridge if [ "${DEPLOY_BRIDGES:-true}" = "true" ]; then log_warn "Step 6: Deploying CCIPWETH10Bridge..." if [ -z "$CCIP_ROUTER" ] || [ -z "$WETH10_ADDRESS" ] || [ -z "$CCIP_FEE_TOKEN" ]; then log_error "Error: Missing required configuration for CCIPWETH10Bridge" echo "Required: CCIP_ROUTER, WETH10_ADDRESS, CCIP_FEE_TOKEN" exit 1 fi # Update .env temporarily for deployment script export CCIP_ROUTER export WETH10_ADDRESS export CCIP_FEE_TOKEN CCIPWETH10BRIDGE_ADDRESS=$(deploy_contract "script/DeployCCIPWETH10Bridge.s.sol:DeployCCIPWETH10Bridge" "CCIPWETH10Bridge" "CCIPWETH10BRIDGE_ADDRESS") if [ -z "$CCIPWETH10BRIDGE_ADDRESS" ]; then log_error "Error: Failed to deploy CCIPWETH10Bridge" exit 1 fi else log_success "✅ CCIPWETH10Bridge deployment skipped" fi # Step 7: Deploy Oracle Aggregator log_warn "Step 7: Deploying Oracle Aggregator..." ORACLE_DESCRIPTION=${ORACLE_DESCRIPTION:-"ETH/USD Price Feed"} ORACLE_HEARTBEAT=${ORACLE_HEARTBEAT:-"60"} ORACLE_DEVIATION_THRESHOLD=${ORACLE_DEVIATION_THRESHOLD:-"50"} # Get deployer address DEPLOYER_ADDRESS=$(cast wallet address --private-key "$PRIVATE_KEY" 2>/dev/null || echo "") if [ -z "$DEPLOYER_ADDRESS" ]; then log_warn "Warning: Could not get deployer address. Using script default" # Extract address from private key (first 42 chars after 0x) DEPLOYER_ADDRESS="0x$(echo "$PRIVATE_KEY" | cut -c3-42)" fi ORACLE_ADDRESS=$(deploy_contract "script/DeployOracle.s.sol:DeployOracle" "Oracle Aggregator" "ORACLE_AGGREGATOR_ADDRESS") if [ -z "$ORACLE_ADDRESS" ]; then log_error "Error: Failed to deploy Oracle Aggregator" exit 1 fi # Step 8: Summary log_success "=== Deployment Summary ===" log_success "CCIP Router: ${CCIP_ROUTER}" log_success "CCIP Fee Token: ${CCIP_FEE_TOKEN}" log_success "WETH9: ${WETH9_ADDRESS}" log_success "WETH10: ${WETH10_ADDRESS}" if [ -n "$CCIPWETH9BRIDGE_ADDRESS" ]; then log_success "CCIPWETH9Bridge: ${CCIPWETH9BRIDGE_ADDRESS}" fi if [ -n "$CCIPWETH10BRIDGE_ADDRESS" ]; then log_success "CCIPWETH10Bridge: ${CCIPWETH10BRIDGE_ADDRESS}" fi log_success "Oracle Aggregator: ${ORACLE_ADDRESS}" log_success "=== Deployment Complete ===" log_success "All contract addresses have been updated in .env file" # Save deployment addresses to a file DEPLOYMENT_FILE="deployment-addresses-$(date +%Y%m%d-%H%M%S).txt" cat > "$DEPLOYMENT_FILE" << EOF # Contract Deployment Addresses # Deployed on: $(date) # Chain ID: $(cast chain-id --rpc-url "$RPC_URL" 2>/dev/null || echo "unknown") # RPC URL: $RPC_URL CCIP_ROUTER=$CCIP_ROUTER CCIP_FEE_TOKEN=$CCIP_FEE_TOKEN WETH9_ADDRESS=$WETH9_ADDRESS WETH10_ADDRESS=$WETH10_ADDRESS CCIPWETH9BRIDGE_ADDRESS=$CCIPWETH9BRIDGE_ADDRESS CCIPWETH10BRIDGE_ADDRESS=$CCIPWETH10BRIDGE_ADDRESS ORACLE_AGGREGATOR_ADDRESS=$ORACLE_ADDRESS EOF log_success "Deployment addresses saved to: ${DEPLOYMENT_FILE}"