276 lines
9.7 KiB
Bash
Executable File
276 lines
9.7 KiB
Bash
Executable File
#!/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
|
|
|