#!/bin/bash # Diagnose and fix LINK token deployment issues set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" source "$PROJECT_ROOT/scripts/lib/address-inventory.sh" cd "$PROJECT_ROOT" load_explorer_runtime_env RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}" ACCOUNT=$(cast wallet address "$PRIVATE_KEY" 2>/dev/null || echo "") FOUND_LINK="" if [ -z "$ACCOUNT" ]; then echo "Error: PRIVATE_KEY not set or invalid" exit 1 fi echo "╔══════════════════════════════════════════════════════════════╗" echo "║ LINK TOKEN DEPLOYMENT DIAGNOSTIC ║" echo "╚══════════════════════════════════════════════════════════════╝" echo "" echo "Account: $ACCOUNT" echo "RPC: $RPC_URL" echo "" # Check network echo "=== Network Status ===" BLOCK=$(cast block-number --rpc-url "$RPC_URL" 2>/dev/null || echo "0") CHAIN_ID=$(cast chain-id --rpc-url "$RPC_URL" 2>/dev/null || echo "0") echo "Block: $BLOCK" echo "Chain ID: $CHAIN_ID" echo "" # Check account nonce and recent transactions echo "=== Account Status ===" NONCE=$(cast nonce "$ACCOUNT" --rpc-url "$RPC_URL" 2>/dev/null || echo "0") BALANCE=$(cast balance "$ACCOUNT" --rpc-url "$RPC_URL" 2>/dev/null || echo "0") BALANCE_ETH=$(cast --from-wei "$BALANCE" ether 2>/dev/null || echo "0") echo "Nonce: $NONCE" echo "Balance: $BALANCE_ETH ETH" echo "" # Check CCIP Router for fee token echo "=== Checking CCIP Router for Fee Token ===" CCIP_ROUTER="$(resolve_address_value CCIP_ROUTER_ADDRESS CCIP_ROUTER_ADDRESS 0x42DAb7b888Dd382bD5Adcf9E038dBF1fD03b4817)" ROUTER_CODE=$(cast code "$CCIP_ROUTER" --rpc-url "$RPC_URL" 2>/dev/null || echo "") if [ -n "$ROUTER_CODE" ] && [ "$ROUTER_CODE" != "0x" ]; then echo "✓ CCIP Router exists" FEE_TOKEN_RAW=$(cast call "$CCIP_ROUTER" "feeToken()" --rpc-url "$RPC_URL" 2>/dev/null || echo "") if [ -n "$FEE_TOKEN_RAW" ] && [ "$FEE_TOKEN_RAW" != "0x" ]; then LINK_FROM_ROUTER=$(echo "$FEE_TOKEN_RAW" | sed 's/0x000000000000000000000000//' | sed 's/^0x//' | sed 's/^/0x/') echo "Router fee token: $LINK_FROM_ROUTER" CODE=$(cast code "$LINK_FROM_ROUTER" --rpc-url "$RPC_URL" 2>/dev/null || echo "") if [ -n "$CODE" ] && [ "$CODE" != "0x" ] && [ ${#CODE} -gt 100 ]; then echo "✓✓✓ LINK token EXISTS at router address!" FOUND_LINK="$LINK_FROM_ROUTER" else echo "✗ Router references LINK, but contract not deployed" fi fi fi echo "" # Check if LINK token exists at any known address echo "=== Checking Known LINK Addresses ===" KNOWN_LINKS=( "$(resolve_address_value LINK_TOKEN LINK_TOKEN 0x514910771AF9Ca656af840dff83E8264EcF986CA)" "0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF" "0x07dE1f489E1bfCE2c326066a9DFc10e731CBA0CB" "0x514910771AF9Ca656af840dff83E8264EcF986CA" ) if [ -z "$FOUND_LINK" ]; then for LINK_ADDR in "${KNOWN_LINKS[@]}"; do CODE=$(cast code "$LINK_ADDR" --rpc-url "$RPC_URL" 2>/dev/null || echo "") if [ -n "$CODE" ] && [ "$CODE" != "0x" ] && [ ${#CODE} -gt 100 ]; then echo "✓ Found LINK at: $LINK_ADDR" NAME=$(cast call "$LINK_ADDR" "name()" --rpc-url "$RPC_URL" 2>/dev/null || echo "") SYMBOL=$(cast call "$LINK_ADDR" "symbol()" --rpc-url "$RPC_URL" 2>/dev/null || echo "") echo " Name: $NAME" echo " Symbol: $SYMBOL" FOUND_LINK="$LINK_ADDR" break else echo "✗ No contract at: $LINK_ADDR" fi done fi echo "" # If found, use it if [ -n "$FOUND_LINK" ]; then echo "=== Using Existing LINK Token ===" echo "LINK Token: $FOUND_LINK" persist_inventory_value "LINK_TOKEN" "$FOUND_LINK" || true persist_inventory_value "CCIP_CHAIN138_FEE_TOKEN" "$FOUND_LINK" || true echo "✓ Updated address inventory" # Check balance BALANCE=$(cast call "$FOUND_LINK" "balanceOf(address)" "$ACCOUNT" --rpc-url "$RPC_URL" 2>/dev/null || echo "0") BALANCE_ETH=$(cast --from-wei "$BALANCE" ether 2>/dev/null || echo "0") echo "Account Balance: $BALANCE_ETH LINK" # Try to mint if balance is low if (( $(echo "$BALANCE_ETH < 20" | bc -l 2>/dev/null || echo 1) )); then echo "" echo "=== Attempting to Mint ===" FORCE_GAS="3000000000" CURRENT_NONCE=$(cast nonce "$ACCOUNT" --rpc-url "$RPC_URL") echo "Minting 1M LINK with nonce $CURRENT_NONCE..." MINT_OUTPUT=$(cast send "$FOUND_LINK" "mint(address,uint256)" "$ACCOUNT" $(cast --to-wei 1000000 ether) \ --rpc-url "$RPC_URL" \ --private-key "$PRIVATE_KEY" \ --gas-price "$FORCE_GAS" \ --nonce "$CURRENT_NONCE" \ --legacy 2>&1 || echo "FAILED") if echo "$MINT_OUTPUT" | grep -qE "(blockHash|transactionHash)"; then echo "✓ Mint transaction sent" TX_HASH=$(echo "$MINT_OUTPUT" | grep -oE "0x[0-9a-f]{64}" | head -1) echo "Transaction: $TX_HASH" echo "Waiting 15 seconds for confirmation..." sleep 15 else echo "⚠ Mint may not be available (standard LINK token)" echo "You may need to acquire LINK from another source" fi fi exit 0 fi # If not found, try fresh deployment echo "=== No Existing LINK Found - Deploying Fresh ===" echo "" TEMP_DIR=$(mktemp -d) cd "$TEMP_DIR" # Create minimal project forge init --no-git --force . > /dev/null 2>&1 # Create MockLinkToken cat > src/MockLinkToken.sol << 'EOF' // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; contract MockLinkToken { string public name = "Chainlink Token"; string public symbol = "LINK"; uint8 public decimals = 18; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; uint256 public totalSupply; event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); function mint(address to, uint256 amount) external { balanceOf[to] += amount; totalSupply += amount; emit Transfer(address(0), to, amount); } function transfer(address to, uint256 amount) external returns (bool) { require(balanceOf[msg.sender] >= amount, "insufficient balance"); balanceOf[msg.sender] -= amount; balanceOf[to] += amount; emit Transfer(msg.sender, to, amount); return true; } function transferFrom(address from, address to, uint256 amount) external returns (bool) { require(balanceOf[from] >= amount, "insufficient balance"); require(allowance[from][msg.sender] >= amount, "insufficient allowance"); balanceOf[from] -= amount; balanceOf[to] += amount; allowance[from][msg.sender] -= amount; emit Transfer(from, to, amount); return true; } function approve(address spender, uint256 amount) external returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } } EOF # Create deployment script cat > script/DeployLink.s.sol << 'EOF' // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {Script, console} from "forge-std/Script.sol"; import {MockLinkToken} from "../src/MockLinkToken.sol"; contract DeployLink is Script { function run() external { uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); vm.startBroadcast(deployerPrivateKey); MockLinkToken link = new MockLinkToken(); console.log("LINK_TOKEN_ADDRESS", address(link)); // Mint 1M LINK to deployer link.mint(vm.addr(deployerPrivateKey), 1_000_000e18); console.log("Minted 1M LINK"); vm.stopBroadcast(); } } EOF # Build echo "Building contract..." forge build > /dev/null 2>&1 # Deploy with very high gas FORCE_GAS="5000000000" # 5 gwei CURRENT_NONCE=$(cast nonce "$ACCOUNT" --rpc-url "$RPC_URL") echo "Deploying with:" echo " Gas: $FORCE_GAS wei ($(echo "scale=2; $FORCE_GAS / 1000000000" | bc) gwei)" echo " Nonce: $CURRENT_NONCE" echo "" DEPLOY_OUTPUT=$(forge script script/DeployLink.s.sol:DeployLink \ --rpc-url "$RPC_URL" \ --private-key "$PRIVATE_KEY" \ --broadcast \ --skip-simulation \ --gas-price "$FORCE_GAS" \ --legacy \ -vv 2>&1 || echo "FAILED") NEW_LINK=$(echo "$DEPLOY_OUTPUT" | grep -oE "LINK_TOKEN_ADDRESS[[:space:]]+0x[0-9a-fA-F]{40}" | awk '{print $2}') if [ -z "$NEW_LINK" ]; then NEW_LINK=$(echo "$DEPLOY_OUTPUT" | grep -oE "0x[0-9a-fA-F]{40}" | head -1) fi if [ -n "$NEW_LINK" ] && [ ${#NEW_LINK} -eq 42 ]; then echo "✓✓✓ LINK deployed: $NEW_LINK" echo "$NEW_LINK" > /tmp/link_address.txt # Update address inventory cd "$PROJECT_ROOT" persist_inventory_value "LINK_TOKEN" "$NEW_LINK" || true persist_inventory_value "CCIP_CHAIN138_FEE_TOKEN" "$NEW_LINK" || true echo "✓ Updated address inventory" # Wait and verify echo "" echo "Waiting 30 seconds for network confirmation..." sleep 30 CODE=$(cast code "$NEW_LINK" --rpc-url "$RPC_URL" 2>/dev/null || echo "") if [ -n "$CODE" ] && [ "$CODE" != "0x" ] && [ ${#CODE} -gt 100 ]; then echo "✓✓✓ Contract CONFIRMED!" NAME=$(cast call "$NEW_LINK" "name()" --rpc-url "$RPC_URL" 2>/dev/null || echo "") SYMBOL=$(cast call "$NEW_LINK" "symbol()" --rpc-url "$RPC_URL" 2>/dev/null || echo "") echo " Name: $NAME" echo " Symbol: $SYMBOL" BALANCE=$(cast call "$NEW_LINK" "balanceOf(address)" "$ACCOUNT" --rpc-url "$RPC_URL" 2>/dev/null || echo "0") BALANCE_ETH=$(cast --from-wei "$BALANCE" ether 2>/dev/null || echo "0") echo " Balance: $BALANCE_ETH LINK" if (( $(echo "$BALANCE_ETH < 20" | bc -l 2>/dev/null || echo 1) )); then echo "" echo "⚠ Balance low, waiting for mint to confirm..." for i in {1..12}; do sleep 5 BALANCE=$(cast call "$NEW_LINK" "balanceOf(address)" "$ACCOUNT" --rpc-url "$RPC_URL" 2>/dev/null || echo "0") BALANCE_ETH=$(cast --from-wei "$BALANCE" ether 2>/dev/null || echo "0") if (( $(echo "$BALANCE_ETH >= 20" | bc -l 2>/dev/null || echo 0) )); then echo "✓ Balance confirmed: $BALANCE_ETH LINK" break fi done fi else echo "⚠ Contract not yet confirmed" echo "Code length: ${#CODE}" echo "This may take additional time. Check again with:" echo " cast code $NEW_LINK --rpc-url $RPC_URL" fi else echo "✗ Deployment failed" echo "Output:" echo "$DEPLOY_OUTPUT" | grep -E "(Error|error|FAILED|revert)" | head -10 fi # Cleanup rm -rf "$TEMP_DIR"