Files
explorer-monorepo/scripts/wrap-and-bridge-to-ethereum.sh
2026-03-02 12:14:13 -08:00

289 lines
11 KiB
Bash
Executable File

#!/usr/bin/env bash
# Wrap ETH to WETH9 and bridge to Ethereum Mainnet
# Usage: ./wrap-and-bridge-to-ethereum.sh [amount_in_eth] [private_key]
# Example: ./wrap-and-bridge-to-ethereum.sh 1.0 0x...
# Or set PRIVATE_KEY in .env file: ./wrap-and-bridge-to-ethereum.sh 1.0
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 .env exists
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}"
WETH9_ADDRESS="0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
ETHEREUM_MAINNET_SELECTOR="5009297550715157269"
# Parse arguments
AMOUNT="${1:-}"
PRIVATE_KEY_ARG="${2:-}"
# Use provided private key or from environment
if [ -n "$PRIVATE_KEY_ARG" ]; then
PRIVATE_KEY="$PRIVATE_KEY_ARG"
elif [ -z "${PRIVATE_KEY:-}" ]; then
log_error "PRIVATE_KEY not provided. Usage: $0 <amount_in_eth> [private_key]"
log_info "Or set PRIVATE_KEY in .env file"
exit 1
fi
if [ -z "$AMOUNT" ]; then
log_error "Amount not provided. Usage: $0 <amount_in_eth> [private_key]"
log_info "Example: $0 1.0 0x..."
exit 1
fi
# Validate amount is a positive number
if ! echo "$AMOUNT" | grep -qE '^[0-9]+\.?[0-9]*$' || (( $(echo "$AMOUNT <= 0" | bc -l 2>/dev/null || echo 1) )); then
log_error "Invalid amount: $AMOUNT. Must be a positive number."
exit 1
fi
log_info "========================================="
log_info "Wrap ETH to WETH9 and Bridge to Ethereum Mainnet"
log_info "========================================="
log_info ""
log_info "Configuration:"
log_info " Amount: $AMOUNT ETH"
log_info " WETH9 Address: $WETH9_ADDRESS"
log_info " Bridge Address: $WETH9_BRIDGE"
log_info " Destination: Ethereum Mainnet (Selector: $ETHEREUM_MAINNET_SELECTOR)"
log_info " RPC URL: $RPC_URL"
log_info ""
# Get deployer address from private key
DEPLOYER=$(cast wallet address --private-key "$PRIVATE_KEY" 2>/dev/null || echo "")
if [ -z "$DEPLOYER" ]; then
log_error "Failed to get address from private key"
exit 1
fi
log_info "Wallet Address: $DEPLOYER"
log_info ""
# Get optimal gas price function
get_optimal_gas() {
if [ -f "$SCRIPT_DIR/get-optimal-gas-from-api.sh" ]; then
api_gas=$("$SCRIPT_DIR/get-optimal-gas-from-api.sh" "proposed" 2>/dev/null || echo "")
if [ -n "$api_gas" ] && [ "$api_gas" != "0" ]; then
echo "scale=0; $api_gas * 1.5 / 1" | bc 2>/dev/null || echo "$api_gas"
return 0
fi
fi
current_gas=$(cast gas-price --rpc-url "$RPC_URL" 2>/dev/null || echo "1000000000")
echo "scale=0; $current_gas * 1.5 / 1" | bc 2>/dev/null || echo "$current_gas"
}
# Step 1: Check ETH balance
log_info "Step 1: Checking ETH balance..."
ETH_BALANCE=$(cast balance "$DEPLOYER" --rpc-url "$RPC_URL" 2>/dev/null || echo "0")
ETH_BALANCE_ETH=$(echo "scale=6; $ETH_BALANCE / 1000000000000000000" | bc 2>/dev/null || echo "0")
log_info "ETH Balance: $ETH_BALANCE_ETH ETH"
# Convert amount to wei
AMOUNT_WEI=$(cast --to-wei "$AMOUNT" ether 2>/dev/null || echo "")
if [ -z "$AMOUNT_WEI" ]; then
log_error "Failed to convert amount to wei"
exit 1
fi
# Check if sufficient balance (need amount + gas fees, estimate 0.01 ETH for gas)
REQUIRED_ETH=$(echo "scale=6; $AMOUNT + 0.01" | bc 2>/dev/null || echo "$AMOUNT")
if (( $(echo "$ETH_BALANCE_ETH < $REQUIRED_ETH" | bc -l 2>/dev/null || echo 1) )); then
log_error "Insufficient ETH balance. Need at least $REQUIRED_ETH ETH (including gas fees)"
log_info "Current balance: $ETH_BALANCE_ETH ETH"
exit 1
fi
log_success "✓ Sufficient ETH balance"
log_info ""
# Step 2: Check WETH9 balance
log_info "Step 2: Checking WETH9 balance..."
WETH9_BAL=$(cast call "$WETH9_ADDRESS" "balanceOf(address)" "$DEPLOYER" --rpc-url "$RPC_URL" 2>/dev/null || echo "0")
WETH9_BAL_ETH=$(echo "scale=6; $WETH9_BAL / 1000000000000000000" | bc 2>/dev/null || echo "0")
log_info "WETH9 Balance: $WETH9_BAL_ETH WETH"
log_info ""
# Step 3: Wrap ETH to WETH9 if needed
if [ "$WETH9_BAL" = "0" ] || (( $(echo "$WETH9_BAL < $AMOUNT_WEI" | bc -l 2>/dev/null || echo 1) )); then
log_info "Step 3: Wrapping ETH to WETH9..."
# Calculate how much to wrap
if [ "$WETH9_BAL" = "0" ]; then
WRAP_AMOUNT_WEI="$AMOUNT_WEI"
WRAP_AMOUNT_ETH="$AMOUNT"
else
NEEDED_WEI=$(echo "$AMOUNT_WEI - $WETH9_BAL" | bc 2>/dev/null || echo "$AMOUNT_WEI")
NEEDED_ETH=$(echo "scale=6; $NEEDED_WEI / 1000000000000000000" | bc 2>/dev/null || echo "$AMOUNT")
WRAP_AMOUNT_WEI="$NEEDED_WEI"
WRAP_AMOUNT_ETH="$NEEDED_ETH"
log_info "Need to wrap $NEEDED_ETH ETH (have $WETH9_BAL_ETH WETH, need $AMOUNT WETH)"
fi
# Get current nonce
CURRENT_NONCE=$(cast nonce "$DEPLOYER" --rpc-url "$RPC_URL" 2>/dev/null || echo "0")
# Get optimal gas price
OPTIMAL_GAS=$(get_optimal_gas)
# Wrap ETH by calling deposit() with value
log_info "Calling WETH9.deposit() with $WRAP_AMOUNT_ETH ETH..."
WRAP_TX=$(cast send "$WETH9_ADDRESS" "deposit()" \
--value "$WRAP_AMOUNT_WEI" \
--rpc-url "$RPC_URL" \
--private-key "$PRIVATE_KEY" \
--gas-price "$OPTIMAL_GAS" \
--nonce "$CURRENT_NONCE" \
2>&1 || echo "")
if echo "$WRAP_TX" | grep -qE "transactionHash"; then
TX_HASH=$(echo "$WRAP_TX" | grep -oE "transactionHash[[:space:]]+0x[0-9a-fA-F]{64}" | awk '{print $2}')
log_success "✓ Wrap transaction sent: $TX_HASH"
log_info "Waiting for confirmation..."
sleep 15
# Verify WETH9 balance after wrap and check 1:1 ratio
WETH9_BAL_AFTER=$(cast call "$WETH9_ADDRESS" "balanceOf(address)" "$DEPLOYER" --rpc-url "$RPC_URL" 2>/dev/null || echo "0")
WETH9_BAL_AFTER_ETH=$(echo "scale=6; $WETH9_BAL_AFTER / 1000000000000000000" | bc 2>/dev/null || echo "0")
log_info "WETH9 Balance after wrap: $WETH9_BAL_AFTER_ETH WETH"
# Verify 1:1 ratio
WETH9_RECEIVED=$(echo "$WETH9_BAL_AFTER - $WETH9_BAL" | bc 2>/dev/null || echo "0")
if [ "$WETH9_RECEIVED" = "$WRAP_AMOUNT_WEI" ]; then
log_success "✓ 1:1 ratio verified: Received exactly $WRAP_AMOUNT_ETH WETH9 for $WRAP_AMOUNT_ETH ETH"
else
log_warn "⚠ Ratio verification: Expected $WRAP_AMOUNT_WEI wei, received $WETH9_RECEIVED wei"
log_warn " This may indicate a non-1:1 ratio. Please investigate."
log_info " Run ./scripts/verify-weth9-ratio.sh for detailed verification"
fi
else
log_error "Failed to wrap ETH"
log_info "Transaction output: $WRAP_TX"
exit 1
fi
else
log_success "✓ WETH9 balance sufficient ($WETH9_BAL_ETH WETH)"
fi
log_info ""
# Step 4: Check and approve bridge if needed
log_info "Step 4: Checking bridge allowance..."
ALLOW=$(cast call "$WETH9_ADDRESS" "allowance(address,address)" "$DEPLOYER" "$WETH9_BRIDGE" --rpc-url "$RPC_URL" 2>/dev/null || echo "0")
if [ "$ALLOW" = "0" ] || (( $(echo "$ALLOW < $AMOUNT_WEI" | bc -l 2>/dev/null || echo 1) )); then
log_info "Approving bridge to spend $AMOUNT WETH9..."
# Get current nonce
CURRENT_NONCE=$(cast nonce "$DEPLOYER" --rpc-url "$RPC_URL" 2>/dev/null || echo "0")
# Get optimal gas price
OPTIMAL_GAS=$(get_optimal_gas)
# Approve bridge (approve max amount to avoid future approvals)
MAX_UINT256="115792089237316195423570985008687907853269984665640564039457584007913129639935"
APPROVE_TX=$(cast send "$WETH9_ADDRESS" "approve(address,uint256)" "$WETH9_BRIDGE" "$MAX_UINT256" \
--rpc-url "$RPC_URL" \
--private-key "$PRIVATE_KEY" \
--gas-price "$OPTIMAL_GAS" \
--nonce "$CURRENT_NONCE" \
2>&1 || echo "")
if echo "$APPROVE_TX" | grep -qE "transactionHash"; then
TX_HASH=$(echo "$APPROVE_TX" | grep -oE "transactionHash[[:space:]]+0x[0-9a-fA-F]{64}" | awk '{print $2}')
log_success "✓ Approval transaction sent: $TX_HASH"
log_info "Waiting for confirmation..."
sleep 15
else
log_error "Failed to approve bridge"
log_info "Transaction output: $APPROVE_TX"
exit 1
fi
else
log_success "✓ Bridge already approved"
fi
log_info ""
# Step 5: Calculate CCIP fee
log_info "Step 5: Calculating CCIP bridge fee..."
FEE=$(cast call "$WETH9_BRIDGE" "calculateFee(uint64,uint256)" "$ETHEREUM_MAINNET_SELECTOR" "$AMOUNT_WEI" --rpc-url "$RPC_URL" 2>/dev/null || echo "0")
if [ "$FEE" = "0" ] || [ -z "$FEE" ]; then
log_warn "Could not calculate fee, proceeding anyway..."
FEE_ETH="0"
else
FEE_ETH=$(echo "scale=10; $FEE / 1000000000000000000" | bc 2>/dev/null || echo "0")
log_info "CCIP Fee: $FEE_ETH ETH ($FEE wei)"
fi
log_info ""
# Step 6: Send cross-chain transfer to Ethereum Mainnet
log_info "Step 6: Sending cross-chain transfer to Ethereum Mainnet..."
log_info " Recipient: $DEPLOYER (same address on Ethereum Mainnet)"
log_info " Amount: $AMOUNT WETH9"
log_info " Destination Chain Selector: $ETHEREUM_MAINNET_SELECTOR"
# Get current nonce
CURRENT_NONCE=$(cast nonce "$DEPLOYER" --rpc-url "$RPC_URL" 2>/dev/null || echo "0")
# Get optimal gas price
OPTIMAL_GAS=$(get_optimal_gas)
# Send cross-chain transfer
SEND_TX=$(cast send "$WETH9_BRIDGE" "sendCrossChain(uint64,address,uint256)" \
"$ETHEREUM_MAINNET_SELECTOR" \
"$DEPLOYER" \
"$AMOUNT_WEI" \
--rpc-url "$RPC_URL" \
--private-key "$PRIVATE_KEY" \
--gas-price "$OPTIMAL_GAS" \
--nonce "$CURRENT_NONCE" \
2>&1 || echo "")
if echo "$SEND_TX" | grep -qE "transactionHash"; then
TX_HASH=$(echo "$SEND_TX" | grep -oE "transactionHash[[:space:]]+0x[0-9a-fA-F]{64}" | awk '{print $2}')
log_success "✓ Cross-chain transfer sent: $TX_HASH"
log_info ""
log_info "Transaction Details:"
log_info " Transaction Hash: $TX_HASH"
log_info " Amount: $AMOUNT WETH9"
log_info " Destination: Ethereum Mainnet"
log_info " Recipient: $DEPLOYER"
log_info ""
log_info "You can monitor the transaction at:"
log_info " https://explorer.d-bis.org/tx/$TX_HASH"
log_info ""
log_success "Process completed successfully!"
log_info ""
log_info "Note: The bridge transfer may take a few minutes to complete on Ethereum Mainnet."
log_info "Monitor the destination chain for the receipt of WETH9 tokens."
else
log_error "Failed to send cross-chain transfer"
log_info "Transaction output: $SEND_TX"
exit 1
fi