#!/usr/bin/env bash # Deploy LINK Token Contract on ChainID 138 # This script deploys a standard ERC20 LINK token contract # Usage: ./deploy-link-token.sh set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[✓]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } # Load environment variables if [ -f "$PROJECT_ROOT/.env" ]; then source "$PROJECT_ROOT/.env" elif [ -f "$PROJECT_ROOT/../.env" ]; then source "$PROJECT_ROOT/../.env" fi # Configuration RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}" EXPECTED_LINK_ADDRESS="0x326C977E6efc84E512bB9C30f76E30c160eD06FB" if [ -z "${PRIVATE_KEY:-}" ]; then log_error "PRIVATE_KEY not found in .env" exit 1 fi DEPLOYER=$(cast wallet address "$PRIVATE_KEY" 2>/dev/null || echo "") if [ -z "$DEPLOYER" ]; then log_error "Could not derive address from PRIVATE_KEY" exit 1 fi log_info "=========================================" log_info "Deploy LINK Token Contract" log_info "=========================================" log_info "" log_info "Network: ChainID 138" log_info "RPC: $RPC_URL" log_info "Deployer: $DEPLOYER" log_info "Expected Address: $EXPECTED_LINK_ADDRESS" log_info "" # Check if contract already exists log_info "Checking if LINK token already exists..." EXISTING_CODE=$(cast code "$EXPECTED_LINK_ADDRESS" --rpc-url "$RPC_URL" 2>/dev/null || echo "") if [ -n "$EXISTING_CODE" ] && [ "$EXISTING_CODE" != "0x" ]; then log_warn "Contract already exists at $EXPECTED_LINK_ADDRESS" log_info "Checking if it's a valid ERC20 token..." # Try to call standard ERC20 functions NAME=$(cast call "$EXPECTED_LINK_ADDRESS" "name()" --rpc-url "$RPC_URL" 2>/dev/null || echo "") SYMBOL=$(cast call "$EXPECTED_LINK_ADDRESS" "symbol()" --rpc-url "$RPC_URL" 2>/dev/null || echo "") if [ -n "$NAME" ] && [ -n "$SYMBOL" ]; then log_success "✓ Valid ERC20 token found!" log_info " Name: $NAME" log_info " Symbol: $SYMBOL" log_info "" log_info "No deployment needed. Contract is already deployed." exit 0 else log_warn "Contract exists but doesn't appear to be a valid ERC20 token" log_info "Proceeding with deployment to expected address..." fi fi # Check if we need to use CREATE2 for deterministic address log_info "" log_info "Note: To deploy to the expected address ($EXPECTED_LINK_ADDRESS)," log_info "you may need to use CREATE2 with specific salt, or deploy to a" log_info "different address and update configurations." log_info "" # Create temporary contract file TEMP_DIR=$(mktemp -d) trap "rm -rf $TEMP_DIR" EXIT log_info "Creating LINK token contract..." cat > "$TEMP_DIR/LinkToken.sol" << 'EOF' // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract LinkToken is ERC20 { constructor() ERC20("Chainlink Token", "LINK") { // Initial supply: 1 billion LINK (1,000,000,000 * 10^18) _mint(msg.sender, 1_000_000_000 * 10**18); } } EOF log_info "Contract source created at: $TEMP_DIR/LinkToken.sol" log_info "" log_warn "⚠️ This script requires Foundry and OpenZeppelin contracts" log_warn "⚠️ For production, use the official Chainlink LINK token contract" log_warn "⚠️ This is a simplified ERC20 implementation" log_info "" # Check if forge is available if ! command -v forge &> /dev/null; then log_error "Foundry (forge) is not installed" log_info "Install Foundry: https://book.getfoundry.sh/getting-started/installation" log_info "" log_info "Alternative: Deploy using Remix IDE or Hardhat" log_info "Contract source saved to: $TEMP_DIR/LinkToken.sol" exit 1 fi # Check if source project exists SOURCE_PROJECT="/home/intlc/projects/smom-dbis-138" if [ ! -d "$SOURCE_PROJECT" ]; then log_warn "Source project not found: $SOURCE_PROJECT" log_info "Creating minimal Foundry project for deployment..." cd "$TEMP_DIR" forge init --no-git --force . > /dev/null 2>&1 || true # Install OpenZeppelin forge install OpenZeppelin/openzeppelin-contracts --no-git --no-commit > /dev/null 2>&1 || true # Copy contract mkdir -p src cp LinkToken.sol src/ log_info "Minimal project created" DEPLOY_DIR="$TEMP_DIR" else log_info "Using source project: $SOURCE_PROJECT" cd "$SOURCE_PROJECT" # Check if MockLinkToken exists if [ -f "contracts/tokens/MockLinkToken.sol" ]; then log_info "Found MockLinkToken contract, using it instead" USE_MOCK=true else USE_MOCK=false # Copy contract to source project if needed if [ ! -f "contracts/LinkToken.sol" ]; then mkdir -p contracts cp "$TEMP_DIR/LinkToken.sol" contracts/ log_info "Contract copied to source project" fi fi DEPLOY_DIR="$SOURCE_PROJECT" fi # Deploy contract log_info "" log_info "Deploying LINK token contract..." log_info "This may take a few minutes..." # Get optimal gas price if [ -f "$SCRIPT_DIR/get-optimal-gas-from-api.sh" ]; then OPTIMAL_GAS=$("$SCRIPT_DIR/get-optimal-gas-from-api.sh" "proposed" 2>/dev/null || echo "") if [ -z "$OPTIMAL_GAS" ] || [ "$OPTIMAL_GAS" = "0" ]; then CURRENT_GAS=$(cast gas-price --rpc-url "$RPC_URL" 2>/dev/null || echo "1000000000") OPTIMAL_GAS=$(echo "scale=0; $CURRENT_GAS * 1.5 / 1" | bc 2>/dev/null || echo "$CURRENT_GAS") fi else CURRENT_GAS=$(cast gas-price --rpc-url "$RPC_URL" 2>/dev/null || echo "1000000000") OPTIMAL_GAS=$(echo "scale=0; $CURRENT_GAS * 1.5 / 1" | bc 2>/dev/null || echo "$CURRENT_GAS") fi log_info "Using gas price: $(echo "scale=2; $OPTIMAL_GAS / 1000000000" | bc) gwei" # Deploy using forge create (direct deployment, avoids compilation issues) cd "$DEPLOY_DIR" if [ "${USE_MOCK:-false}" = true ]; then CONTRACT_PATH="contracts/tokens/MockLinkToken.sol:MockLinkToken" log_info "Deploying MockLinkToken using forge create..." # Deploy directly - forge create should handle dependencies DEPLOY_OUTPUT=$(forge create \ "$CONTRACT_PATH" \ --rpc-url "$RPC_URL" \ --private-key "$PRIVATE_KEY" \ --gas-price "$OPTIMAL_GAS" \ 2>&1 || echo "FAILED") else if [ "$DEPLOY_DIR" = "$TEMP_DIR" ]; then CONTRACT_PATH="src/LinkToken.sol:LinkToken" else CONTRACT_PATH="contracts/LinkToken.sol:LinkToken" fi log_info "Deploying LinkToken using forge create..." DEPLOY_OUTPUT=$(forge create \ "$CONTRACT_PATH" \ --rpc-url "$RPC_URL" \ --private-key "$PRIVATE_KEY" \ --gas-price "$OPTIMAL_GAS" \ 2>&1 || echo "FAILED") fi # Extract deployed address from output (handle both forge script and forge create formats) DEPLOYED_ADDRESS="" TX_HASH="" if echo "$DEPLOY_OUTPUT" | grep -qE "Deployed to:"; then DEPLOYED_ADDRESS=$(echo "$DEPLOY_OUTPUT" | grep -oE "Deployed to: 0x[0-9a-fA-F]{40}" | awk '{print $3}') TX_HASH=$(echo "$DEPLOY_OUTPUT" | grep -oE "Transaction hash: 0x[0-9a-fA-F]{64}" | awk '{print $3}') elif echo "$DEPLOY_OUTPUT" | grep -qE "Mock LINK Token deployed at:"; then DEPLOYED_ADDRESS=$(echo "$DEPLOY_OUTPUT" | grep -oE "Mock LINK Token deployed at: 0x[0-9a-fA-F]{40}" | awk '{print $5}') TX_HASH=$(echo "$DEPLOY_OUTPUT" | grep -oE "0x[0-9a-fA-F]{64}" | head -1) fi if [ -n "$DEPLOYED_ADDRESS" ] && [ "$DEPLOYED_ADDRESS" != "0x0000000000000000000000000000000000000000" ]; then log_success "✓ LINK token deployed successfully!" log_info "" log_info "Contract Address: $DEPLOYED_ADDRESS" log_info "Transaction Hash: $TX_HASH" log_info "" # Verify deployment log_info "Verifying deployment..." sleep 5 NAME=$(cast call "$DEPLOYED_ADDRESS" "name()" --rpc-url "$RPC_URL" 2>/dev/null || echo "") SYMBOL=$(cast call "$DEPLOYED_ADDRESS" "symbol()" --rpc-url "$RPC_URL" 2>/dev/null || echo "") TOTAL_SUPPLY=$(cast call "$DEPLOYED_ADDRESS" "totalSupply()" --rpc-url "$RPC_URL" 2>/dev/null || echo "0") DEPLOYER_BALANCE=$(cast call "$DEPLOYED_ADDRESS" "balanceOf(address)" "$DEPLOYER" --rpc-url "$RPC_URL" 2>/dev/null || echo "0") if [ -n "$NAME" ] && [ -n "$SYMBOL" ]; then log_success "✓ Deployment verified!" log_info " Name: $NAME" log_info " Symbol: $SYMBOL" log_info " Total Supply: $(cast --from-wei "$TOTAL_SUPPLY" ether) LINK" log_info " Deployer Balance: $(cast --from-wei "$DEPLOYER_BALANCE" ether) LINK" log_info "" if [ "$DEPLOYED_ADDRESS" != "$EXPECTED_LINK_ADDRESS" ]; then log_warn "⚠️ Deployed address differs from expected address" log_info " Expected: $EXPECTED_LINK_ADDRESS" log_info " Deployed: $DEPLOYED_ADDRESS" log_info "" log_info "Update your .env file:" log_info " LINK_TOKEN=$DEPLOYED_ADDRESS" log_info " CCIP_CHAIN138_LINK_TOKEN=$DEPLOYED_ADDRESS" else log_success "✓ Deployed to expected address!" fi log_info "" log_info "Next steps:" log_info "1. Update .env file with LINK token address" log_info "2. Fund bridge contracts: ./scripts/fund-bridge-contracts.sh 10" log_info "3. Verify contract on explorer: https://explorer.d-bis.org/address/$DEPLOYED_ADDRESS" exit 0 else log_error "Deployment verification failed" log_error "Contract may not be a valid ERC20 token" exit 1 fi else log_error "Deployment failed" log_error "$DEPLOY_OUTPUT" exit 1 fi