Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands - CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround - CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check - NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere - MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates - LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference Co-authored-by: Cursor <cursoragent@cursor.com>
1280 lines
51 KiB
Bash
Executable File
1280 lines
51 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Add WETH9 and WETH10 Wrap/Unwrap Utilities to Explorer
|
|
# Enables users to wrap/unwrap native ETH directly from the explorer
|
|
|
|
set -euo pipefail
|
|
|
|
# Load IP configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
|
|
|
|
|
|
IP="${IP:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}"
|
|
DOMAIN="${DOMAIN:-explorer.d-bis.org}"
|
|
PASSWORD="${PASSWORD:-L@kers2010}"
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
CYAN='\033[0;36m'
|
|
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"; }
|
|
log_step() { echo -e "${CYAN}[STEP]${NC} $1"; }
|
|
|
|
exec_container() {
|
|
local cmd="$1"
|
|
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no root@"$IP" "bash -c '$cmd'" 2>&1
|
|
}
|
|
|
|
echo "════════════════════════════════════════════════════════"
|
|
echo "Add WETH9/WETH10 Wrap/Unwrap Utilities to Explorer"
|
|
echo "════════════════════════════════════════════════════════"
|
|
echo ""
|
|
|
|
# Step 1: Read current explorer HTML
|
|
log_step "Step 1: Reading current explorer interface..."
|
|
sshpass -p "$PASSWORD" scp -o StrictHostKeyChecking=no root@"$IP":/var/www/html/index.html /tmp/blockscout-current.html
|
|
log_success "Current explorer interface backed up"
|
|
|
|
# Step 2: Create enhanced explorer with wrap/unwrap utilities
|
|
log_step "Step 2: Creating enhanced explorer with WETH utilities..."
|
|
|
|
cat > /tmp/blockscout-with-weth-utilities.html <<'WETH_HTML_EOF'
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Chain 138 Explorer | d-bis.org | Bridge Monitoring & WETH Utilities</title>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<script src="https://cdn.ethers.io/lib/ethers-5.7.2.umd.min.js"></script>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
:root {
|
|
--primary: #667eea;
|
|
--secondary: #764ba2;
|
|
--success: #10b981;
|
|
--warning: #f59e0b;
|
|
--danger: #ef4444;
|
|
--bridge-blue: #3b82f6;
|
|
--dark: #1f2937;
|
|
--light: #f9fafb;
|
|
--border: #e5e7eb;
|
|
--text: #111827;
|
|
--text-light: #6b7280;
|
|
}
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
background: var(--light);
|
|
color: var(--text);
|
|
line-height: 1.6;
|
|
}
|
|
.navbar {
|
|
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
|
color: white;
|
|
padding: 1rem 2rem;
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 1000;
|
|
}
|
|
.nav-container {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
.logo {
|
|
font-size: 1.5rem;
|
|
font-weight: bold;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
.nav-links {
|
|
display: flex;
|
|
gap: 2rem;
|
|
list-style: none;
|
|
}
|
|
.nav-links a {
|
|
color: white;
|
|
text-decoration: none;
|
|
transition: opacity 0.2s;
|
|
}
|
|
.nav-links a:hover { opacity: 0.8; }
|
|
.search-box {
|
|
flex: 1;
|
|
max-width: 600px;
|
|
margin: 0 2rem;
|
|
}
|
|
.search-input {
|
|
width: 100%;
|
|
padding: 0.75rem 1rem;
|
|
border: none;
|
|
border-radius: 8px;
|
|
font-size: 1rem;
|
|
background: rgba(255,255,255,0.2);
|
|
color: white;
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
.search-input::placeholder { color: rgba(255,255,255,0.7); }
|
|
.search-input:focus {
|
|
outline: none;
|
|
background: rgba(255,255,255,0.3);
|
|
}
|
|
.container {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
padding: 2rem;
|
|
}
|
|
.stats-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
gap: 1.5rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
.stat-card {
|
|
background: white;
|
|
padding: 1.5rem;
|
|
border-radius: 12px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
transition: transform 0.2s, box-shadow 0.2s;
|
|
}
|
|
.stat-card:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
}
|
|
.stat-card.bridge-card {
|
|
border-left: 4px solid var(--bridge-blue);
|
|
}
|
|
.stat-label {
|
|
color: var(--text-light);
|
|
font-size: 0.875rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
.stat-value {
|
|
font-size: 2rem;
|
|
font-weight: bold;
|
|
color: var(--primary);
|
|
}
|
|
.stat-value.bridge-value {
|
|
color: var(--bridge-blue);
|
|
}
|
|
.card {
|
|
background: white;
|
|
border-radius: 12px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
padding: 2rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
.card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 1.5rem;
|
|
padding-bottom: 1rem;
|
|
border-bottom: 2px solid var(--border);
|
|
}
|
|
.card-title {
|
|
font-size: 1.5rem;
|
|
font-weight: bold;
|
|
color: var(--text);
|
|
}
|
|
.tabs {
|
|
display: flex;
|
|
gap: 1rem;
|
|
margin-bottom: 1.5rem;
|
|
border-bottom: 2px solid var(--border);
|
|
flex-wrap: wrap;
|
|
}
|
|
.tab {
|
|
padding: 1rem 1.5rem;
|
|
background: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
color: var(--text-light);
|
|
border-bottom: 3px solid transparent;
|
|
transition: all 0.2s;
|
|
}
|
|
.tab.active {
|
|
color: var(--primary);
|
|
border-bottom-color: var(--primary);
|
|
font-weight: 600;
|
|
}
|
|
.bridge-tab.active {
|
|
color: var(--bridge-blue);
|
|
border-bottom-color: var(--bridge-blue);
|
|
}
|
|
.weth-tab.active {
|
|
color: var(--success);
|
|
border-bottom-color: var(--success);
|
|
}
|
|
.table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
}
|
|
.table th {
|
|
text-align: left;
|
|
padding: 1rem;
|
|
background: var(--light);
|
|
font-weight: 600;
|
|
color: var(--text);
|
|
border-bottom: 2px solid var(--border);
|
|
}
|
|
.table td {
|
|
padding: 1rem;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.table tr:hover { background: var(--light); }
|
|
.hash {
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.875rem;
|
|
color: var(--primary);
|
|
word-break: break-all;
|
|
}
|
|
.hash:hover { text-decoration: underline; cursor: pointer; }
|
|
.badge {
|
|
display: inline-block;
|
|
padding: 0.25rem 0.75rem;
|
|
border-radius: 20px;
|
|
font-size: 0.875rem;
|
|
font-weight: 600;
|
|
}
|
|
.badge-success { background: #d1fae5; color: var(--success); }
|
|
.badge-warning { background: #fef3c7; color: var(--warning); }
|
|
.badge-danger { background: #fee2e2; color: var(--danger); }
|
|
.badge-chain {
|
|
background: #dbeafe;
|
|
color: var(--bridge-blue);
|
|
}
|
|
.loading {
|
|
text-align: center;
|
|
padding: 3rem;
|
|
color: var(--text-light);
|
|
}
|
|
.loading i {
|
|
font-size: 2rem;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
@keyframes spin {
|
|
from { transform: rotate(0deg); }
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
.error {
|
|
background: #fee2e2;
|
|
color: var(--danger);
|
|
padding: 1rem;
|
|
border-radius: 8px;
|
|
margin: 1rem 0;
|
|
}
|
|
.success {
|
|
background: #d1fae5;
|
|
color: var(--success);
|
|
padding: 1rem;
|
|
border-radius: 8px;
|
|
margin: 1rem 0;
|
|
}
|
|
.bridge-chain-card {
|
|
background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%);
|
|
padding: 1.5rem;
|
|
border-radius: 12px;
|
|
margin-bottom: 1rem;
|
|
}
|
|
.weth-card {
|
|
background: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);
|
|
padding: 1.5rem;
|
|
border-radius: 12px;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
.weth-form {
|
|
background: white;
|
|
padding: 1.5rem;
|
|
border-radius: 8px;
|
|
margin-top: 1rem;
|
|
}
|
|
.form-group {
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
.form-label {
|
|
display: block;
|
|
margin-bottom: 0.5rem;
|
|
font-weight: 600;
|
|
color: var(--text);
|
|
}
|
|
.form-input {
|
|
width: 100%;
|
|
padding: 0.75rem;
|
|
border: 2px solid var(--border);
|
|
border-radius: 8px;
|
|
font-size: 1rem;
|
|
transition: border-color 0.2s;
|
|
}
|
|
.form-input:focus {
|
|
outline: none;
|
|
border-color: var(--primary);
|
|
}
|
|
.form-input-group {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
}
|
|
.form-input-group .form-input {
|
|
flex: 1;
|
|
}
|
|
.btn {
|
|
padding: 0.75rem 1.5rem;
|
|
border: none;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
font-weight: 600;
|
|
transition: all 0.2s;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
.btn-primary {
|
|
background: var(--primary);
|
|
color: white;
|
|
}
|
|
.btn-primary:hover { background: var(--secondary); }
|
|
.btn-bridge {
|
|
background: var(--bridge-blue);
|
|
color: white;
|
|
}
|
|
.btn-bridge:hover { background: #2563eb; }
|
|
.btn-success {
|
|
background: var(--success);
|
|
color: white;
|
|
}
|
|
.btn-success:hover { background: #059669; }
|
|
.btn-warning {
|
|
background: var(--warning);
|
|
color: white;
|
|
}
|
|
.btn-warning:hover { background: #d97706; }
|
|
.btn:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
.balance-display {
|
|
background: var(--light);
|
|
padding: 1rem;
|
|
border-radius: 8px;
|
|
margin-bottom: 1rem;
|
|
}
|
|
.balance-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 0.5rem 0;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.balance-row:last-child {
|
|
border-bottom: none;
|
|
}
|
|
.balance-label {
|
|
color: var(--text-light);
|
|
}
|
|
.balance-value {
|
|
font-weight: bold;
|
|
color: var(--text);
|
|
}
|
|
.chain-name {
|
|
font-size: 1.25rem;
|
|
font-weight: bold;
|
|
color: var(--bridge-blue);
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
.chain-info {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
gap: 1rem;
|
|
margin-top: 1rem;
|
|
}
|
|
.chain-stat {
|
|
font-size: 0.875rem;
|
|
}
|
|
.chain-stat-label {
|
|
color: var(--text-light);
|
|
}
|
|
.chain-stat-value {
|
|
font-weight: bold;
|
|
color: var(--text);
|
|
margin-top: 0.25rem;
|
|
}
|
|
.detail-view {
|
|
display: none;
|
|
}
|
|
.detail-view.active { display: block; }
|
|
.info-row {
|
|
display: flex;
|
|
padding: 1rem;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.info-label {
|
|
font-weight: 600;
|
|
min-width: 200px;
|
|
color: var(--text-light);
|
|
}
|
|
.info-value {
|
|
flex: 1;
|
|
word-break: break-all;
|
|
}
|
|
.metamask-status {
|
|
padding: 1rem;
|
|
border-radius: 8px;
|
|
margin-bottom: 1rem;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
.metamask-status.connected {
|
|
background: #d1fae5;
|
|
color: var(--success);
|
|
}
|
|
.metamask-status.disconnected {
|
|
background: #fee2e2;
|
|
color: var(--danger);
|
|
}
|
|
@media (max-width: 768px) {
|
|
.nav-container { flex-direction: column; gap: 1rem; }
|
|
.search-box { max-width: 100%; margin: 0; }
|
|
.nav-links { flex-wrap: wrap; justify-content: center; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<nav class="navbar">
|
|
<div class="nav-container">
|
|
<div class="logo">
|
|
<i class="fas fa-cube"></i>
|
|
<span>Chain 138 Explorer</span>
|
|
</div>
|
|
<div class="search-box">
|
|
<input type="text" class="search-input" id="searchInput" placeholder="Search by address, transaction hash, or block number...">
|
|
</div>
|
|
<ul class="nav-links">
|
|
<li><a href="#" onclick="showHome(); return false;"><i class="fas fa-home"></i> Home</a></li>
|
|
<li><a href="#" onclick="showBlocks(); return false;"><i class="fas fa-cubes"></i> Blocks</a></li>
|
|
<li><a href="#" onclick="showTransactions(); return false;"><i class="fas fa-exchange-alt"></i> Transactions</a></li>
|
|
<li><a href="#" onclick="showBridgeMonitoring(); return false;"><i class="fas fa-bridge"></i> Bridge</a></li>
|
|
<li><a href="#" onclick="showWETHUtilities(); return false;"><i class="fas fa-coins"></i> WETH</a></li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="container" id="mainContent">
|
|
<!-- Home View -->
|
|
<div id="homeView">
|
|
<div class="stats-grid" id="statsGrid">
|
|
<!-- Stats loaded dynamically -->
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2 class="card-title">Latest Blocks</h2>
|
|
<button class="btn btn-primary" onclick="showBlocks()">View All</button>
|
|
</div>
|
|
<div id="latestBlocks">
|
|
<div class="loading"><i class="fas fa-spinner"></i> Loading blocks...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2 class="card-title">Latest Transactions</h2>
|
|
<button class="btn btn-primary" onclick="showTransactions()">View All</button>
|
|
</div>
|
|
<div id="latestTransactions">
|
|
<div class="loading"><i class="fas fa-spinner"></i> Loading transactions...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- WETH Utilities View -->
|
|
<div id="wethView" class="detail-view">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2 class="card-title"><i class="fas fa-coins"></i> WETH9 & WETH10 Utilities</h2>
|
|
<button class="btn btn-success" onclick="refreshWETHBalances()"><i class="fas fa-sync-alt"></i> Refresh</button>
|
|
</div>
|
|
|
|
<!-- MetaMask Connection Status -->
|
|
<div id="metamaskStatus" class="metamask-status disconnected">
|
|
<i class="fas fa-wallet"></i>
|
|
<span>MetaMask not connected</span>
|
|
<button class="btn btn-success" onclick="connectMetaMask()" style="margin-left: auto;">Connect MetaMask</button>
|
|
</div>
|
|
|
|
<div class="tabs">
|
|
<button class="tab weth-tab active" onclick="showWETHTab('weth9')">WETH9</button>
|
|
<button class="tab weth-tab" onclick="showWETHTab('weth10')">WETH10</button>
|
|
<button class="tab weth-tab" onclick="showWETHTab('info')">Information</button>
|
|
</div>
|
|
|
|
<!-- WETH9 Tab -->
|
|
<div id="weth9Tab" class="weth-tab-content">
|
|
<div class="weth-card">
|
|
<div class="chain-name">WETH9 Token</div>
|
|
<div style="color: var(--text-light); margin-bottom: 1rem;">
|
|
Contract: <span class="hash" onclick="showAddressDetail('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')" style="cursor: pointer;">0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2</span>
|
|
</div>
|
|
|
|
<div class="balance-display" id="weth9Balance">
|
|
<div class="balance-row">
|
|
<span class="balance-label">ETH Balance:</span>
|
|
<span class="balance-value" id="weth9EthBalance">-</span>
|
|
</div>
|
|
<div class="balance-row">
|
|
<span class="balance-label">WETH9 Balance:</span>
|
|
<span class="balance-value" id="weth9TokenBalance">-</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="weth-form">
|
|
<h3 style="margin-bottom: 1rem;">Wrap ETH → WETH9</h3>
|
|
<div class="form-group">
|
|
<label class="form-label">Amount (ETH)</label>
|
|
<div class="form-input-group">
|
|
<input type="number" class="form-input" id="weth9WrapAmount" placeholder="0.0" step="0.000001" min="0">
|
|
<button class="btn btn-primary" onclick="setMaxWETH9('wrap')">MAX</button>
|
|
</div>
|
|
</div>
|
|
<button class="btn btn-success" onclick="wrapWETH9()" id="weth9WrapBtn" disabled>
|
|
<i class="fas fa-arrow-right"></i> Wrap ETH to WETH9
|
|
</button>
|
|
</div>
|
|
|
|
<div class="weth-form">
|
|
<h3 style="margin-bottom: 1rem;">Unwrap WETH9 → ETH</h3>
|
|
<div class="form-group">
|
|
<label class="form-label">Amount (WETH9)</label>
|
|
<div class="form-input-group">
|
|
<input type="number" class="form-input" id="weth9UnwrapAmount" placeholder="0.0" step="0.000001" min="0">
|
|
<button class="btn btn-primary" onclick="setMaxWETH9('unwrap')">MAX</button>
|
|
</div>
|
|
</div>
|
|
<button class="btn btn-warning" onclick="unwrapWETH9()" id="weth9UnwrapBtn" disabled>
|
|
<i class="fas fa-arrow-left"></i> Unwrap WETH9 to ETH
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- WETH10 Tab -->
|
|
<div id="weth10Tab" class="weth-tab-content" style="display: none;">
|
|
<div class="weth-card">
|
|
<div class="chain-name">WETH10 Token</div>
|
|
<div style="color: var(--text-light); margin-bottom: 1rem;">
|
|
Contract: <span class="hash" onclick="showAddressDetail('0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f')" style="cursor: pointer;">0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f</span>
|
|
</div>
|
|
|
|
<div class="balance-display" id="weth10Balance">
|
|
<div class="balance-row">
|
|
<span class="balance-label">ETH Balance:</span>
|
|
<span class="balance-value" id="weth10EthBalance">-</span>
|
|
</div>
|
|
<div class="balance-row">
|
|
<span class="balance-label">WETH10 Balance:</span>
|
|
<span class="balance-value" id="weth10TokenBalance">-</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="weth-form">
|
|
<h3 style="margin-bottom: 1rem;">Wrap ETH → WETH10</h3>
|
|
<div class="form-group">
|
|
<label class="form-label">Amount (ETH)</label>
|
|
<div class="form-input-group">
|
|
<input type="number" class="form-input" id="weth10WrapAmount" placeholder="0.0" step="0.000001" min="0">
|
|
<button class="btn btn-primary" onclick="setMaxWETH10('wrap')">MAX</button>
|
|
</div>
|
|
</div>
|
|
<button class="btn btn-success" onclick="wrapWETH10()" id="weth10WrapBtn" disabled>
|
|
<i class="fas fa-arrow-right"></i> Wrap ETH to WETH10
|
|
</button>
|
|
</div>
|
|
|
|
<div class="weth-form">
|
|
<h3 style="margin-bottom: 1rem;">Unwrap WETH10 → ETH</h3>
|
|
<div class="form-group">
|
|
<label class="form-label">Amount (WETH10)</label>
|
|
<div class="form-input-group">
|
|
<input type="number" class="form-input" id="weth10UnwrapAmount" placeholder="0.0" step="0.000001" min="0">
|
|
<button class="btn btn-primary" onclick="setMaxWETH10('unwrap')">MAX</button>
|
|
</div>
|
|
</div>
|
|
<button class="btn btn-warning" onclick="unwrapWETH10()" id="weth10UnwrapBtn" disabled>
|
|
<i class="fas fa-arrow-left"></i> Unwrap WETH10 to ETH
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Information Tab -->
|
|
<div id="wethInfoTab" class="weth-tab-content" style="display: none;">
|
|
<div class="card">
|
|
<h3>About WETH9 and WETH10</h3>
|
|
<div style="margin-top: 1rem; line-height: 1.8;">
|
|
<p><strong>WETH9</strong> and <strong>WETH10</strong> are wrapped versions of native ETH (Ether) that allow you to use ETH in smart contracts and DeFi protocols.</p>
|
|
|
|
<h4 style="margin-top: 1.5rem; margin-bottom: 0.5rem;">What is Wrapping?</h4>
|
|
<p>Wrapping ETH converts your native ETH into an ERC-20 token (WETH9 or WETH10) that can be used in DeFi applications, smart contracts, and cross-chain bridging.</p>
|
|
|
|
<h4 style="margin-top: 1.5rem; margin-bottom: 0.5rem;">Contract Addresses</h4>
|
|
<ul style="margin-left: 2rem; margin-top: 0.5rem;">
|
|
<li><strong>WETH9:</strong> <span class="hash">0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2</span></li>
|
|
<li><strong>WETH10:</strong> <span class="hash">0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f</span></li>
|
|
</ul>
|
|
|
|
<h4 style="margin-top: 1.5rem; margin-bottom: 0.5rem;">How to Use</h4>
|
|
<ol style="margin-left: 2rem; margin-top: 0.5rem;">
|
|
<li>Connect your MetaMask wallet</li>
|
|
<li>Select WETH9 or WETH10 tab</li>
|
|
<li>Enter the amount to wrap or unwrap</li>
|
|
<li>Confirm the transaction in MetaMask</li>
|
|
</ol>
|
|
|
|
<h4 style="margin-top: 1.5rem; margin-bottom: 0.5rem;">Cross-Chain Bridging</h4>
|
|
<p>Both WETH9 and WETH10 can be bridged to other chains using the CCIP bridge contracts:</p>
|
|
<ul style="margin-left: 2rem; margin-top: 0.5rem;">
|
|
<li><strong>WETH9 Bridge:</strong> <span class="hash">0x971cD9D156f193df8051E48043C476e53ECd4693</span></li>
|
|
<li><strong>WETH10 Bridge:</strong> <span class="hash">0xe0E93247376aa097dB308B92e6Ba36bA015535D0</span></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bridge Monitoring View and other views (keep existing code) -->
|
|
<div id="bridgeView" class="detail-view">
|
|
<!-- Bridge monitoring content (from previous implementation) -->
|
|
</div>
|
|
|
|
<!-- Other views -->
|
|
<div id="blocksView" class="detail-view">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2 class="card-title">All Blocks</h2>
|
|
</div>
|
|
<div id="blocksList">
|
|
<div class="loading"><i class="fas fa-spinner"></i> Loading blocks...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="transactionsView" class="detail-view">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2 class="card-title">All Transactions</h2>
|
|
</div>
|
|
<div id="transactionsList">
|
|
<div class="loading"><i class="fas fa-spinner"></i> Loading transactions...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="blockDetailView" class="detail-view">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<button class="btn btn-secondary" onclick="showBlocks()"><i class="fas fa-arrow-left"></i> Back</button>
|
|
<h2 class="card-title">Block Details</h2>
|
|
</div>
|
|
<div id="blockDetail"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="transactionDetailView" class="detail-view">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<button class="btn btn-secondary" onclick="showTransactions()"><i class="fas fa-arrow-left"></i> Back</button>
|
|
<h2 class="card-title">Transaction Details</h2>
|
|
</div>
|
|
<div id="transactionDetail"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="addressDetailView" class="detail-view">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<button class="btn btn-secondary" onclick="showHome()"><i class="fas fa-arrow-left"></i> Back</button>
|
|
<h2 class="card-title">Address Details</h2>
|
|
</div>
|
|
<div id="addressDetail"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const API_BASE = '/api';
|
|
const RPC_URL = 'https://rpc-core.d-bis.org'; // Chain 138 RPC
|
|
let currentView = 'home';
|
|
let provider = null;
|
|
let signer = null;
|
|
let userAddress = null;
|
|
|
|
// WETH Contract Addresses
|
|
const WETH9_ADDRESS = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
|
|
const WETH10_ADDRESS = '0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f';
|
|
|
|
// WETH ABI (Standard ERC-20 + WETH functions)
|
|
const WETH_ABI = [
|
|
"function deposit() payable",
|
|
"function withdraw(uint256 wad)",
|
|
"function balanceOf(address account) view returns (uint256)",
|
|
"function transfer(address to, uint256 amount) returns (bool)",
|
|
"function approve(address spender, uint256 amount) returns (bool)",
|
|
"function allowance(address owner, address spender) view returns (uint256)",
|
|
"function totalSupply() view returns (uint256)",
|
|
"function name() view returns (string)",
|
|
"function symbol() view returns (string)",
|
|
"function decimals() view returns (uint8)",
|
|
"event Deposit(address indexed dst, uint256 wad)",
|
|
"event Withdrawal(address indexed src, uint256 wad)"
|
|
];
|
|
|
|
// Initialize
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
checkMetaMaskConnection();
|
|
loadStats();
|
|
loadLatestBlocks();
|
|
});
|
|
|
|
// MetaMask Connection
|
|
async function checkMetaMaskConnection() {
|
|
if (typeof window.ethereum !== 'undefined') {
|
|
try {
|
|
const accounts = await window.ethereum.request({ method: 'eth_accounts' });
|
|
if (accounts.length > 0) {
|
|
await connectMetaMask();
|
|
}
|
|
} catch (error) {
|
|
console.error('Error checking MetaMask:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function connectMetaMask() {
|
|
if (typeof window.ethereum === 'undefined') {
|
|
alert('MetaMask is not installed! Please install MetaMask to use WETH utilities.');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Request account access
|
|
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
|
|
userAddress = accounts[0];
|
|
|
|
// Connect to Chain 138
|
|
await switchToChain138();
|
|
|
|
// Setup provider and signer
|
|
provider = new ethers.providers.Web3Provider(window.ethereum);
|
|
signer = provider.getSigner();
|
|
|
|
// Update UI
|
|
const statusEl = document.getElementById('metamaskStatus');
|
|
statusEl.className = 'metamask-status connected';
|
|
statusEl.innerHTML = `
|
|
<i class="fas fa-check-circle"></i>
|
|
<span>Connected: ${shortenHash(userAddress)}</span>
|
|
<button class="btn btn-warning" onclick="disconnectMetaMask()" style="margin-left: auto;">Disconnect</button>
|
|
`;
|
|
|
|
// Enable buttons
|
|
document.getElementById('weth9WrapBtn').disabled = false;
|
|
document.getElementById('weth9UnwrapBtn').disabled = false;
|
|
document.getElementById('weth10WrapBtn').disabled = false;
|
|
document.getElementById('weth10UnwrapBtn').disabled = false;
|
|
|
|
// Load balances
|
|
await refreshWETHBalances();
|
|
|
|
// Listen for account changes
|
|
window.ethereum.on('accountsChanged', (accounts) => {
|
|
if (accounts.length === 0) {
|
|
disconnectMetaMask();
|
|
} else {
|
|
connectMetaMask();
|
|
}
|
|
});
|
|
|
|
// Listen for chain changes
|
|
window.ethereum.on('chainChanged', () => {
|
|
switchToChain138();
|
|
});
|
|
} catch (error) {
|
|
console.error('Error connecting MetaMask:', error);
|
|
alert('Failed to connect MetaMask: ' + error.message);
|
|
}
|
|
}
|
|
|
|
async function switchToChain138() {
|
|
const chainId = '0x8A'; // 138 in hex
|
|
try {
|
|
await window.ethereum.request({
|
|
method: 'wallet_switchEthereumChain',
|
|
params: [{ chainId }],
|
|
});
|
|
} catch (switchError) {
|
|
// If chain doesn't exist, add it
|
|
if (switchError.code === 4902) {
|
|
try {
|
|
await window.ethereum.request({
|
|
method: 'wallet_addEthereumChain',
|
|
params: [{
|
|
chainId,
|
|
chainName: 'Chain 138',
|
|
nativeCurrency: {
|
|
name: 'ETH',
|
|
symbol: 'ETH',
|
|
decimals: 18
|
|
},
|
|
rpcUrls: [RPC_URL],
|
|
blockExplorerUrls: ['https://explorer.d-bis.org']
|
|
}],
|
|
});
|
|
} catch (addError) {
|
|
console.error('Error adding chain:', addError);
|
|
throw addError;
|
|
}
|
|
} else {
|
|
throw switchError;
|
|
}
|
|
}
|
|
}
|
|
|
|
function disconnectMetaMask() {
|
|
provider = null;
|
|
signer = null;
|
|
userAddress = null;
|
|
|
|
const statusEl = document.getElementById('metamaskStatus');
|
|
statusEl.className = 'metamask-status disconnected';
|
|
statusEl.innerHTML = `
|
|
<i class="fas fa-wallet"></i>
|
|
<span>MetaMask not connected</span>
|
|
<button class="btn btn-success" onclick="connectMetaMask()" style="margin-left: auto;">Connect MetaMask</button>
|
|
`;
|
|
|
|
document.getElementById('weth9WrapBtn').disabled = true;
|
|
document.getElementById('weth9UnwrapBtn').disabled = true;
|
|
document.getElementById('weth10WrapBtn').disabled = true;
|
|
document.getElementById('weth10UnwrapBtn').disabled = true;
|
|
}
|
|
|
|
async function refreshWETHBalances() {
|
|
if (!userAddress) return;
|
|
|
|
try {
|
|
// Get ETH balance
|
|
const ethBalance = await provider.getBalance(userAddress);
|
|
const ethBalanceFormatted = formatEther(ethBalance);
|
|
|
|
// Get WETH9 balance
|
|
const weth9Contract = new ethers.Contract(WETH9_ADDRESS, WETH_ABI, provider);
|
|
const weth9Balance = await weth9Contract.balanceOf(userAddress);
|
|
const weth9BalanceFormatted = formatEther(weth9Balance);
|
|
|
|
// Get WETH10 balance
|
|
const weth10Contract = new ethers.Contract(WETH10_ADDRESS, WETH_ABI, provider);
|
|
const weth10Balance = await weth10Contract.balanceOf(userAddress);
|
|
const weth10BalanceFormatted = formatEther(weth10Balance);
|
|
|
|
// Update UI
|
|
document.getElementById('weth9EthBalance').textContent = ethBalanceFormatted + ' ETH';
|
|
document.getElementById('weth9TokenBalance').textContent = weth9BalanceFormatted + ' WETH9';
|
|
document.getElementById('weth10EthBalance').textContent = ethBalanceFormatted + ' ETH';
|
|
document.getElementById('weth10TokenBalance').textContent = weth10BalanceFormatted + ' WETH10';
|
|
} catch (error) {
|
|
console.error('Error refreshing balances:', error);
|
|
}
|
|
}
|
|
|
|
function setMaxWETH9(type) {
|
|
if (type === 'wrap') {
|
|
const ethBalance = document.getElementById('weth9EthBalance').textContent.replace(' ETH', '');
|
|
document.getElementById('weth9WrapAmount').value = parseFloat(ethBalance).toFixed(6);
|
|
} else {
|
|
const wethBalance = document.getElementById('weth9TokenBalance').textContent.replace(' WETH9', '');
|
|
document.getElementById('weth9UnwrapAmount').value = parseFloat(wethBalance).toFixed(6);
|
|
}
|
|
}
|
|
|
|
function setMaxWETH10(type) {
|
|
if (type === 'wrap') {
|
|
const ethBalance = document.getElementById('weth10EthBalance').textContent.replace(' ETH', '');
|
|
document.getElementById('weth10WrapAmount').value = parseFloat(ethBalance).toFixed(6);
|
|
} else {
|
|
const wethBalance = document.getElementById('weth10TokenBalance').textContent.replace(' WETH10', '');
|
|
document.getElementById('weth10UnwrapAmount').value = parseFloat(wethBalance).toFixed(6);
|
|
}
|
|
}
|
|
|
|
async function wrapWETH9() {
|
|
const amount = document.getElementById('weth9WrapAmount').value;
|
|
if (!amount || parseFloat(amount) <= 0) {
|
|
alert('Please enter a valid amount');
|
|
return;
|
|
}
|
|
|
|
if (!signer) {
|
|
alert('Please connect MetaMask first');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const weth9Contract = new ethers.Contract(WETH9_ADDRESS, WETH_ABI, signer);
|
|
const amountWei = ethers.utils.parseEther(amount);
|
|
|
|
const btn = document.getElementById('weth9WrapBtn');
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing...';
|
|
|
|
const tx = await weth9Contract.deposit({ value: amountWei });
|
|
const receipt = await tx.wait();
|
|
|
|
btn.innerHTML = '<i class="fas fa-check"></i> Success!';
|
|
document.getElementById('weth9WrapAmount').value = '';
|
|
await refreshWETHBalances();
|
|
|
|
setTimeout(() => {
|
|
btn.innerHTML = '<i class="fas fa-arrow-right"></i> Wrap ETH to WETH9';
|
|
btn.disabled = false;
|
|
}, 3000);
|
|
} catch (error) {
|
|
console.error('Error wrapping WETH9:', error);
|
|
alert('Failed to wrap WETH9: ' + error.message);
|
|
document.getElementById('weth9WrapBtn').innerHTML = '<i class="fas fa-arrow-right"></i> Wrap ETH to WETH9';
|
|
document.getElementById('weth9WrapBtn').disabled = false;
|
|
}
|
|
}
|
|
|
|
async function unwrapWETH9() {
|
|
const amount = document.getElementById('weth9UnwrapAmount').value;
|
|
if (!amount || parseFloat(amount) <= 0) {
|
|
alert('Please enter a valid amount');
|
|
return;
|
|
}
|
|
|
|
if (!signer) {
|
|
alert('Please connect MetaMask first');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const weth9Contract = new ethers.Contract(WETH9_ADDRESS, WETH_ABI, signer);
|
|
const amountWei = ethers.utils.parseEther(amount);
|
|
|
|
const btn = document.getElementById('weth9UnwrapBtn');
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing...';
|
|
|
|
const tx = await weth9Contract.withdraw(amountWei);
|
|
const receipt = await tx.wait();
|
|
|
|
btn.innerHTML = '<i class="fas fa-check"></i> Success!';
|
|
document.getElementById('weth9UnwrapAmount').value = '';
|
|
await refreshWETHBalances();
|
|
|
|
setTimeout(() => {
|
|
btn.innerHTML = '<i class="fas fa-arrow-left"></i> Unwrap WETH9 to ETH';
|
|
btn.disabled = false;
|
|
}, 3000);
|
|
} catch (error) {
|
|
console.error('Error unwrapping WETH9:', error);
|
|
alert('Failed to unwrap WETH9: ' + error.message);
|
|
document.getElementById('weth9UnwrapBtn').innerHTML = '<i class="fas fa-arrow-left"></i> Unwrap WETH9 to ETH';
|
|
document.getElementById('weth9UnwrapBtn').disabled = false;
|
|
}
|
|
}
|
|
|
|
async function wrapWETH10() {
|
|
const amount = document.getElementById('weth10WrapAmount').value;
|
|
if (!amount || parseFloat(amount) <= 0) {
|
|
alert('Please enter a valid amount');
|
|
return;
|
|
}
|
|
|
|
if (!signer) {
|
|
alert('Please connect MetaMask first');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const weth10Contract = new ethers.Contract(WETH10_ADDRESS, WETH_ABI, signer);
|
|
const amountWei = ethers.utils.parseEther(amount);
|
|
|
|
const btn = document.getElementById('weth10WrapBtn');
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing...';
|
|
|
|
const tx = await weth10Contract.deposit({ value: amountWei });
|
|
const receipt = await tx.wait();
|
|
|
|
btn.innerHTML = '<i class="fas fa-check"></i> Success!';
|
|
document.getElementById('weth10WrapAmount').value = '';
|
|
await refreshWETHBalances();
|
|
|
|
setTimeout(() => {
|
|
btn.innerHTML = '<i class="fas fa-arrow-right"></i> Wrap ETH to WETH10';
|
|
btn.disabled = false;
|
|
}, 3000);
|
|
} catch (error) {
|
|
console.error('Error wrapping WETH10:', error);
|
|
alert('Failed to wrap WETH10: ' + error.message);
|
|
document.getElementById('weth10WrapBtn').innerHTML = '<i class="fas fa-arrow-right"></i> Wrap ETH to WETH10';
|
|
document.getElementById('weth10WrapBtn').disabled = false;
|
|
}
|
|
}
|
|
|
|
async function unwrapWETH10() {
|
|
const amount = document.getElementById('weth10UnwrapAmount').value;
|
|
if (!amount || parseFloat(amount) <= 0) {
|
|
alert('Please enter a valid amount');
|
|
return;
|
|
}
|
|
|
|
if (!signer) {
|
|
alert('Please connect MetaMask first');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const weth10Contract = new ethers.Contract(WETH10_ADDRESS, WETH_ABI, signer);
|
|
const amountWei = ethers.utils.parseEther(amount);
|
|
|
|
const btn = document.getElementById('weth10UnwrapBtn');
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing...';
|
|
|
|
const tx = await weth10Contract.withdraw(amountWei);
|
|
const receipt = await tx.wait();
|
|
|
|
btn.innerHTML = '<i class="fas fa-check"></i> Success!';
|
|
document.getElementById('weth10UnwrapAmount').value = '';
|
|
await refreshWETHBalances();
|
|
|
|
setTimeout(() => {
|
|
btn.innerHTML = '<i class="fas fa-arrow-left"></i> Unwrap WETH10 to ETH';
|
|
btn.disabled = false;
|
|
}, 3000);
|
|
} catch (error) {
|
|
console.error('Error unwrapping WETH10:', error);
|
|
alert('Failed to unwrap WETH10: ' + error.message);
|
|
document.getElementById('weth10UnwrapBtn').innerHTML = '<i class="fas fa-arrow-left"></i> Unwrap WETH10 to ETH';
|
|
document.getElementById('weth10UnwrapBtn').disabled = false;
|
|
}
|
|
}
|
|
|
|
function showWETHTab(tab) {
|
|
document.querySelectorAll('.weth-tab-content').forEach(el => el.style.display = 'none');
|
|
document.querySelectorAll('.weth-tab').forEach(el => el.classList.remove('active'));
|
|
|
|
document.getElementById(`${tab}Tab`).style.display = 'block';
|
|
event.target.classList.add('active');
|
|
}
|
|
|
|
function showWETHUtilities() {
|
|
showView('weth');
|
|
if (userAddress) {
|
|
refreshWETHBalances();
|
|
}
|
|
}
|
|
|
|
function showBridgeMonitoring() {
|
|
showView('bridge');
|
|
}
|
|
|
|
function showHome() {
|
|
showView('home');
|
|
loadStats();
|
|
loadLatestBlocks();
|
|
}
|
|
|
|
function showBlocks() {
|
|
showView('blocks');
|
|
}
|
|
|
|
function showTransactions() {
|
|
showView('transactions');
|
|
}
|
|
|
|
function showView(viewName) {
|
|
currentView = viewName;
|
|
document.querySelectorAll('.detail-view').forEach(v => v.classList.remove('active'));
|
|
document.getElementById('homeView').style.display = viewName === 'home' ? 'block' : 'none';
|
|
if (viewName !== 'home') {
|
|
document.getElementById(`${viewName}View`).classList.add('active');
|
|
}
|
|
}
|
|
|
|
async function fetchAPI(url) {
|
|
try {
|
|
const response = await fetch(url);
|
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error('API Error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function loadStats() {
|
|
try {
|
|
const stats = await fetchAPI(`${API_BASE}/v2/stats`);
|
|
const statsGrid = document.getElementById('statsGrid');
|
|
if (statsGrid) {
|
|
statsGrid.innerHTML = `
|
|
<div class="stat-card">
|
|
<div class="stat-label">Total Blocks</div>
|
|
<div class="stat-value">${formatNumber(stats.total_blocks)}</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Total Transactions</div>
|
|
<div class="stat-value">${formatNumber(stats.total_transactions)}</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Total Addresses</div>
|
|
<div class="stat-value">${formatNumber(stats.total_addresses)}</div>
|
|
</div>
|
|
<div class="stat-card bridge-card">
|
|
<div class="stat-label">Bridge Contracts</div>
|
|
<div class="stat-value bridge-value">2 Active</div>
|
|
</div>
|
|
`;
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to load stats:', error);
|
|
}
|
|
}
|
|
|
|
async function loadLatestBlocks() {
|
|
const container = document.getElementById('latestBlocks');
|
|
if (!container) return;
|
|
|
|
try {
|
|
const blockData = await fetchAPI(`${API_BASE}?module=block&action=eth_block_number`);
|
|
const latestBlock = parseInt(blockData.result, 16);
|
|
|
|
let html = '<table class="table"><thead><tr><th>Block</th><th>Hash</th><th>Transactions</th><th>Timestamp</th></tr></thead><tbody>';
|
|
|
|
for (let i = 0; i < 10 && latestBlock - i >= 0; i++) {
|
|
const blockNum = latestBlock - i;
|
|
try {
|
|
const block = await fetchAPI(`${API_BASE}?module=block&action=eth_get_block_by_number&tag=0x${blockNum.toString(16)}&boolean=false`);
|
|
if (block.result) {
|
|
const timestamp = new Date(parseInt(block.result.timestamp, 16) * 1000).toLocaleString();
|
|
const txCount = block.result.transactions.length;
|
|
html += `<tr onclick="showBlockDetail('${blockNum}')" style="cursor: pointer;">
|
|
<td>${blockNum}</td>
|
|
<td class="hash">${shortenHash(block.result.hash)}</td>
|
|
<td>${txCount}</td>
|
|
<td>${timestamp}</td>
|
|
</tr>`;
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
html += '</tbody></table>';
|
|
container.innerHTML = html;
|
|
} catch (error) {
|
|
container.innerHTML = `<div class="error">Failed to load blocks: ${error.message}</div>`;
|
|
}
|
|
}
|
|
|
|
function showBlockDetail(blockNumber) {
|
|
alert(`Block ${blockNumber} detail view - to be implemented`);
|
|
}
|
|
|
|
function showAddressDetail(address) {
|
|
showView('addressDetail');
|
|
}
|
|
|
|
function handleSearch(query) {
|
|
query = query.trim();
|
|
if (!query) return;
|
|
|
|
if (/^0x[a-fA-F0-9]{40}$/.test(query)) {
|
|
showAddressDetail(query);
|
|
} else if (/^0x[a-fA-F0-9]{64}$/.test(query)) {
|
|
alert(`Transaction ${query} - to be implemented`);
|
|
} else if (/^\d+$/.test(query)) {
|
|
showBlockDetail(query);
|
|
} else {
|
|
alert('Invalid search. Enter an address, transaction hash, or block number.');
|
|
}
|
|
}
|
|
|
|
function formatNumber(num) {
|
|
return parseInt(num || 0).toLocaleString();
|
|
}
|
|
|
|
function shortenHash(hash, length = 10) {
|
|
if (!hash || hash.length <= length * 2 + 2) return hash;
|
|
return hash.substring(0, length + 2) + '...' + hash.substring(hash.length - length);
|
|
}
|
|
|
|
function formatEther(wei, unit = 'ether') {
|
|
if (typeof wei === 'string' && wei.startsWith('0x')) {
|
|
wei = BigInt(wei);
|
|
}
|
|
const weiNum = typeof wei === 'bigint' ? Number(wei) : parseFloat(wei);
|
|
const ether = weiNum / Math.pow(10, unit === 'gwei' ? 9 : 18);
|
|
return ether.toFixed(6).replace(/\.?0+$/, '');
|
|
}
|
|
|
|
// Search input handler
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const searchInput = document.getElementById('searchInput');
|
|
if (searchInput) {
|
|
searchInput.addEventListener('keypress', (e) => {
|
|
if (e.key === 'Enter') {
|
|
handleSearch(e.target.value);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|
|
WETH_HTML_EOF
|
|
|
|
# Step 3: Upload enhanced explorer
|
|
log_step "Step 3: Uploading enhanced explorer with WETH utilities..."
|
|
sshpass -p "$PASSWORD" scp -o StrictHostKeyChecking=no /tmp/blockscout-with-weth-utilities.html root@"$IP":/var/www/html/index.html
|
|
log_success "Enhanced explorer with WETH utilities uploaded"
|
|
|
|
echo ""
|
|
log_success "WETH9/WETH10 Wrap/Unwrap Utilities added!"
|
|
echo ""
|
|
log_info "Features added:"
|
|
log_info " ✅ WETH9 Wrap/Unwrap Interface"
|
|
log_info " ✅ WETH10 Wrap/Unwrap Interface"
|
|
log_info " ✅ MetaMask Integration"
|
|
log_info " ✅ Real-time Balance Display"
|
|
log_info " ✅ MAX Button for Quick Selection"
|
|
log_info " ✅ Transaction Status Feedback"
|
|
log_info " ✅ Automatic Balance Refresh"
|
|
log_info " ✅ Chain 138 Network Detection"
|
|
log_info " ✅ User-friendly Forms"
|
|
log_info " ✅ Information Tab with Instructions"
|
|
echo ""
|
|
log_info "Contract Addresses:"
|
|
log_info " WETH9: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
|
|
log_info " WETH10: 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f"
|
|
echo ""
|
|
log_info "Access: https://explorer.d-bis.org/"
|
|
log_info "Click 'WETH' in the navigation to access wrap/unwrap utilities"
|
|
echo ""
|
|
|