chore: sync submodule state (parent ref update)

Made-with: Cursor
This commit is contained in:
defiQUG
2026-03-02 12:14:13 -08:00
parent 43a7b88e2a
commit 041fae1574
223 changed files with 12940 additions and 11756 deletions

View File

@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# Apply nginx fix for ERR_TOO_MANY_REDIRECTS (X-Forwarded-Proto) and /snap/ on VMID 5000.
# Run from Proxmox host that has VMID 5000, or set EXPLORER_VM_HOST=root@<node-ip> to run via SSH.
# After running, reload nginx inside the VM so the new config is active.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
EXPLORER_MONOREPO="$(cd "$SCRIPT_DIR/.." && pwd)"
VMID="${EXPLORER_VMID:-5000}"
EXPLORER_NODE="${EXPLORER_VM_HOST:-}"
if [[ -n "$EXPLORER_NODE" && "$EXPLORER_NODE" == *"@"* ]]; then
SSH_TARGET="$EXPLORER_NODE"
else
SSH_TARGET="root@${EXPLORER_NODE:-192.168.11.12}"
fi
# If we're not on the Proxmox host (no pct), copy script to host, push into VM, and run inside VM
if ! command -v pct &>/dev/null || ! pct list 2>/dev/null | grep -q "^$VMID "; then
if [[ -n "${EXPLORER_VM_HOST:-}" ]]; then
echo "Running on Proxmox node via SSH: $SSH_TARGET (applying fix inside VMID $VMID)"
scp -o StrictHostKeyChecking=no -o ConnectTimeout=10 \
"$SCRIPT_DIR/fix-nginx-serve-custom-frontend.sh" \
"$SSH_TARGET:/tmp/fix-nginx-explorer.sh" 2>/dev/null || true
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 "$SSH_TARGET" \
"pct push $VMID /tmp/fix-nginx-explorer.sh /tmp/fix-nginx-explorer.sh --perms 0755 && pct exec $VMID -- bash /tmp/fix-nginx-explorer.sh"
exit $?
else
echo "Run this on the Proxmox host that has VMID $VMID, or set EXPLORER_VM_HOST=root@<node-ip>"
exit 1
fi
fi
echo "=============================================="
echo "Apply nginx explorer fix (VMID $VMID)"
echo "=============================================="
echo ""
# Copy the fix script into the VM and run it (prefer custom frontend config with /snap/ and X-Forwarded-Proto)
FIX_SCRIPT="$SCRIPT_DIR/fix-nginx-serve-custom-frontend.sh"
if [[ ! -f "$FIX_SCRIPT" ]]; then
echo "Missing $FIX_SCRIPT"
exit 1
fi
pct push "$VMID" "$FIX_SCRIPT" /tmp/fix-nginx-serve-custom-frontend.sh
pct exec "$VMID" -- bash /tmp/fix-nginx-serve-custom-frontend.sh
echo ""
echo "Done. Verify: curl -sI https://explorer.d-bis.org/snap/ (expect 200, no redirect loop)."

View File

@@ -0,0 +1,68 @@
#!/usr/bin/env bash
# Add nginx proxy for token-aggregation service at /api/v1/ on explorer.d-bis.org (VMID 5000).
# Run on the explorer VM. Requires token-aggregation running (default port 3000).
# Chain 138 Snap companion site (GATSBY_SNAP_API_BASE_URL=https://explorer.d-bis.org) then gets
# market data, swap quotes, and bridge routes from this API.
# Usage: [TOKEN_AGG_PORT=3000] [CONFIG_FILE=/etc/nginx/sites-available/blockscout] bash apply-nginx-token-aggregation-proxy.sh
set -euo pipefail
TOKEN_AGG_PORT="${TOKEN_AGG_PORT:-3000}"
CONFIG_FILE="${CONFIG_FILE:-/etc/nginx/sites-available/blockscout}"
if [ ! -f "$CONFIG_FILE" ]; then
echo "Config not found: $CONFIG_FILE. Set CONFIG_FILE or run from explorer VM." >&2
exit 1
fi
if grep -q 'location /api/v1/' "$CONFIG_FILE"; then
echo "Config already has location /api/v1/. No change."
nginx -t 2>/dev/null && nginx -s reload 2>/dev/null && echo "Nginx reloaded." || true
exit 0
fi
BACKUP="$CONFIG_FILE.bak.$(date +%Y%m%d-%H%M%S)"
cp -a "$CONFIG_FILE" "$BACKUP"
echo "Backed up to $BACKUP"
SNIP=$(mktemp)
trap 'rm -f "$SNIP"' EXIT
cat > "$SNIP" << 'SNIPEOF'
# Token-aggregation API (Chain 138 Snap: market data, swap quote, bridge routes)
location /api/v1/ {
proxy_pass http://127.0.0.1:TOKEN_AGG_PORT/api/v1/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
add_header Access-Control-Allow-Origin *;
}
# API endpoint (for Blockscout API)
SNIPEOF
sed -i "s|TOKEN_AGG_PORT|$TOKEN_AGG_PORT|g" "$SNIP"
# Insert snippet before " # API endpoint (for Blockscout API)"
MARKER=' # API endpoint (for Blockscout API)'
python3 << PY
marker = '''$MARKER'''
with open("$CONFIG_FILE") as f:
content = f.read()
with open("$SNIP") as f:
block = f.read()
if 'location /api/v1/' in content:
print('Already has /api/v1/.')
raise SystemExit(0)
if marker not in content:
print('Marker not found in config.')
raise SystemExit(1)
content = content.replace(marker, block, 1)
with open("$CONFIG_FILE", 'w') as f:
f.write(content)
print('Inserted /api/v1/ proxy.')
PY
echo "Inserted location /api/v1/ proxy to 127.0.0.1:$TOKEN_AGG_PORT"
nginx -t && nginx -s reload && echo "Nginx reloaded. Test: curl -sI https://explorer.d-bis.org/api/v1/chains" || { echo "Nginx test/reload failed. Restore: cp $BACKUP $CONFIG_FILE" >&2; exit 1; }

View File

@@ -92,7 +92,7 @@ fi
# Check Bridge Contracts
log_info ""
log_info "4. Bridge Contracts"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
WETH9_BRIDGE_BYTECODE=$(cast code "$WETH9_BRIDGE" --rpc-url "$RPC_URL" 2>/dev/null || echo "")

View File

@@ -29,7 +29,7 @@ fi
# Configuration
RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
# All destination chains

View File

@@ -39,7 +39,7 @@ fi
# Contract addresses
LINK_TOKEN="0x326C977E6efc84E512bB9C30f76E30c160eD06FB"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
# Get account

View File

@@ -10,7 +10,7 @@ source "$PROJECT_ROOT/.env" 2>/dev/null || source "$PROJECT_ROOT/../.env" 2>/dev
RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
LINK_TOKEN="${LINK_TOKEN:-}"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
ACCOUNT=$(cast wallet address "$PRIVATE_KEY")

View File

@@ -105,7 +105,7 @@ WETH9_CONFIGURED=0
WETH10_CONFIGURED=0
RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
declare -A CHAIN_SELECTORS=(

View File

@@ -125,7 +125,7 @@ echo ""
# Step 4: Check bridge balances (if token exists)
if [ "${TOKEN_EXISTS:-false}" = "true" ]; then
echo "=== Step 4: Checking Bridge LINK Balances ==="
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
WETH9_LINK=$(cast call "$LINK_TOKEN" "balanceOf(address)" "$WETH9_BRIDGE" --rpc-url "$RPC_URL" 2>/dev/null || echo "0")

View File

@@ -30,7 +30,7 @@ fi
# Configuration
RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
# Parse arguments

View File

@@ -31,7 +31,7 @@ fi
# Configuration
RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
# Check PRIVATE_KEY

View File

@@ -44,7 +44,7 @@ WETH9_MAINNET_BRIDGE="0x2A0840e5117683b11682ac46f5CF5621E67269E3"
WETH10_MAINNET_BRIDGE="0xb7721dD53A8c629d9f1Ba31a5819AFe250002b03"
# Bridge addresses on ChainID 138
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
log_info "========================================="

View File

@@ -46,7 +46,7 @@ WETH9_MAINNET_BRIDGE="0x2A0840e5117683b11682ac46f5CF5621E67269E3"
WETH10_MAINNET_BRIDGE="0xb7721dD53A8c629d9f1Ba31a5819AFe250002b03"
# Bridge addresses on ChainID 138
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
log_info "========================================="

View File

@@ -0,0 +1,67 @@
#!/usr/bin/env bash
# Deploy explorer config (token list, networks) to VMID 5000 for /api/config/* endpoints.
# Run from repo root. Requires Proxmox host access (pct exec) or SSH to explorer container.
#
# Usage:
# From Proxmox host: pct exec 5000 -- bash -c 'mkdir -p /var/www/html/config'
# Then: ./scripts/deploy-explorer-config-to-vmid5000.sh
#
# Or run inside VMID 5000:
# pct push 5000 /path/to/DUAL_CHAIN_TOKEN_LIST.tokenlist.json /var/www/html/config/
# pct push 5000 /path/to/DUAL_CHAIN_NETWORKS.json /var/www/html/config/
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
CONFIG_SRC="$REPO_ROOT/explorer-monorepo/backend/api/rest/config/metamask"
VMID="${EXPLORER_VMID:-5000}"
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.12}"
EXEC_MODE="${EXEC_MODE:-pct}"
if [ ! -f "$CONFIG_SRC/DUAL_CHAIN_TOKEN_LIST.tokenlist.json" ]; then
echo "Error: Token list not found at $CONFIG_SRC/DUAL_CHAIN_TOKEN_LIST.tokenlist.json" >&2
exit 1
fi
if [ ! -f "$CONFIG_SRC/DUAL_CHAIN_NETWORKS.json" ]; then
echo "Error: Networks config not found at $CONFIG_SRC/DUAL_CHAIN_NETWORKS.json" >&2
exit 1
fi
echo "Deploying explorer config to VMID $VMID..."
echo " Token list: $CONFIG_SRC/DUAL_CHAIN_TOKEN_LIST.tokenlist.json"
echo " Networks: $CONFIG_SRC/DUAL_CHAIN_NETWORKS.json"
echo ""
case "$EXEC_MODE" in
pct)
if command -v pct &>/dev/null; then
pct exec "$VMID" -- mkdir -p /var/www/html/config
pct push "$VMID" "$CONFIG_SRC/DUAL_CHAIN_TOKEN_LIST.tokenlist.json" /var/www/html/config/DUAL_CHAIN_TOKEN_LIST.tokenlist.json
pct push "$VMID" "$CONFIG_SRC/DUAL_CHAIN_NETWORKS.json" /var/www/html/config/DUAL_CHAIN_NETWORKS.json
echo "Done. Verify: curl -s https://explorer.d-bis.org/api/config/token-list | jq '.tokens | length'"
else
echo "pct not available. Use EXEC_MODE=ssh or run manually inside VMID 5000:"
echo " mkdir -p /var/www/html/config"
echo " # Copy DUAL_CHAIN_TOKEN_LIST.tokenlist.json and DUAL_CHAIN_NETWORKS.json to /var/www/html/config/"
exit 1
fi
;;
ssh)
CONTAINER_IP="${EXPLORER_IP:-192.168.11.140}"
TMP_DIR=$(mktemp -d)
trap "rm -rf $TMP_DIR" EXIT
cp "$CONFIG_SRC/DUAL_CHAIN_TOKEN_LIST.tokenlist.json" "$TMP_DIR/"
cp "$CONFIG_SRC/DUAL_CHAIN_NETWORKS.json" "$TMP_DIR/"
scp "$TMP_DIR/DUAL_CHAIN_TOKEN_LIST.tokenlist.json" "root@$CONTAINER_IP:/var/www/html/config/" 2>/dev/null || {
echo "SSH to $CONTAINER_IP failed. Ensure config dir exists: ssh root@$CONTAINER_IP 'mkdir -p /var/www/html/config'"
exit 1
}
scp "$TMP_DIR/DUAL_CHAIN_NETWORKS.json" "root@$CONTAINER_IP:/var/www/html/config/"
echo "Done. Verify: curl -s https://explorer.d-bis.org/api/config/token-list | jq '.tokens | length'"
;;
*)
echo "Unknown EXEC_MODE=$EXEC_MODE. Use pct or ssh." >&2
exit 1
;;
esac

View File

@@ -80,7 +80,7 @@ echo ""
# Step 4b: Deploy favicon and apple-touch-icon
echo "=== Step 4b: Deploying icons ==="
for ASSET in apple-touch-icon.png favicon.ico; do
for ASSET in explorer-spa.js apple-touch-icon.png favicon.ico; do
SRC="${FRONTEND_PUBLIC}/${ASSET}"
if [ ! -f "$SRC" ]; then
echo "⚠️ Skip $ASSET (not found)"

View File

@@ -0,0 +1,76 @@
#!/usr/bin/env bash
# Deploy Chain 138 Snap companion site to VMID 5000 at /var/www/html/snap/
# Build first (from repo root): cd metamask-integration/chain138-snap && bash scripts/build-snap-site-for-explorer.sh
# Or set BUILD_FIRST=1 to build from this script (requires pnpm in metamask-integration/chain138-snap).
set -euo pipefail
VMID="${EXPLORER_VMID:-5000}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Repo root (parent of explorer-monorepo)
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
SNAP_ROOT="${SNAP_ROOT:-$REPO_ROOT/metamask-integration/chain138-snap}"
SNAP_PUBLIC="${SNAP_PUBLIC:-$SNAP_ROOT/packages/site/public}"
[ -f "$REPO_ROOT/.env" ] && source "$REPO_ROOT/.env" 2>/dev/null || true
[ -f "$REPO_ROOT/config/ip-addresses.conf" ] && source "$REPO_ROOT/config/ip-addresses.conf" 2>/dev/null || true
PROXMOX_R630_02="${EXPLORER_VM_HOST:-root@${PROXMOX_R630_02:-${PROXMOX_HOST_R630_02:-192.168.11.12}}}"
if [[ "$PROXMOX_R630_02" != *"@"* ]]; then PROXMOX_R630_02="root@$PROXMOX_R630_02"; fi
echo "=========================================="
echo "Deploy Snap site to VMID $VMID (/var/www/html/snap/)"
echo "=========================================="
# Optional: build first
if [ "${BUILD_FIRST:-0}" = "1" ]; then
if [ -d "$SNAP_ROOT" ]; then
echo "Building Snap site..."
(cd "$SNAP_ROOT" && bash scripts/build-snap-site-for-explorer.sh)
else
echo "❌ SNAP_ROOT not found: $SNAP_ROOT (set BUILD_FIRST=0 and build manually)"
exit 1
fi
fi
if [ ! -d "$SNAP_PUBLIC" ] || [ -z "$(ls -A "$SNAP_PUBLIC" 2>/dev/null)" ]; then
echo "❌ Snap site build not found at $SNAP_PUBLIC"
echo " Build first: cd $SNAP_ROOT && bash scripts/build-snap-site-for-explorer.sh"
echo " Or run with BUILD_FIRST=1"
exit 1
fi
# Detect execution context (run from Proxmox host or remote; not from inside VM)
if [ -f "/proc/1/cgroup" ] && grep -q "lxc" /proc/1/cgroup 2>/dev/null; then
echo "❌ Run this script from the Proxmox host or from your dev machine with EXPLORER_VM_HOST=root@<node-ip>"
exit 1
fi
if command -v pct &>/dev/null && pct list 2>/dev/null | grep -q "^$VMID "; then
DEPLOY_METHOD="pct"
run_in_vm() { pct exec $VMID -- "$@"; }
else
DEPLOY_METHOD="remote"
run_in_vm() { ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$PROXMOX_R630_02" "pct exec $VMID -- $*"; }
fi
echo "Deploy method: $DEPLOY_METHOD"
run_in_vm "mkdir -p /var/www/html/snap"
run_in_vm "chown -R www-data:www-data /var/www/html/snap" 2>/dev/null || true
TMP_TAR="/tmp/snap-site-$$.tar"
cleanup() { rm -f "$TMP_TAR"; }
trap cleanup EXIT
( cd "$SNAP_PUBLIC" && tar cf "$TMP_TAR" . )
if [ "$DEPLOY_METHOD" = "remote" ]; then
scp -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$TMP_TAR" "$PROXMOX_R630_02:/tmp/snap-site.tar"
ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$PROXMOX_R630_02" "pct push $VMID /tmp/snap-site.tar /tmp/snap-site.tar --perms 0644 && pct exec $VMID -- bash -c 'cd /var/www/html/snap && tar xf /tmp/snap-site.tar && rm /tmp/snap-site.tar && chown -R www-data:www-data /var/www/html/snap'"
ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$PROXMOX_R630_02" "rm -f /tmp/snap-site.tar"
echo "✅ Snap site deployed via $PROXMOX_R630_02"
else
pct push $VMID "$TMP_TAR" /tmp/snap-site.tar
run_in_vm "bash -c 'cd /var/www/html/snap && tar xf /tmp/snap-site.tar && rm /tmp/snap-site.tar && chown -R www-data:www-data /var/www/html/snap'"
echo "✅ Snap site deployed"
fi
echo ""
echo "Snap site should be at: https://explorer.d-bis.org/snap/"
echo "Ensure nginx serves /snap/ from /var/www/html/snap/ (see fix-nginx-serve-custom-frontend.sh)."

View File

@@ -47,6 +47,9 @@ log_step "Step 2: Deploying frontend files..."
sshpass -p "$PASSWORD" scp -o StrictHostKeyChecking=no \
"$REPO_ROOT/frontend/public/index.html" \
root@"$IP":/var/www/html/index.html
[ -f "$REPO_ROOT/frontend/public/explorer-spa.js" ] && sshpass -p "$PASSWORD" scp -o StrictHostKeyChecking=no \
"$REPO_ROOT/frontend/public/explorer-spa.js" \
root@"$IP":/var/www/html/explorer-spa.js
log_success "Frontend deployed"

View File

@@ -32,7 +32,7 @@ fi
# Configuration
RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
WETH9_ADDRESS="0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
ETHEREUM_MAINNET_SELECTOR="5009297550715157269"
# Parse arguments

View File

@@ -0,0 +1,121 @@
/**
* Explorer Frontend E2E Tests
* Tests all links and path-based routing on explorer.d-bis.org
* Run: npx playwright test explorer-monorepo/scripts/e2e-explorer-frontend.spec.ts --project=chromium
*/
import { test, expect } from '@playwright/test';
const EXPLORER_URL = process.env.EXPLORER_URL || 'https://explorer.d-bis.org';
const ADDRESS_TEST = '0x99b3511a2d315a497c8112c1fdd8d508d4b1e506';
test.describe('Explorer Frontend - Path-Based URLs', () => {
test('address path /address/0x... loads address detail', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/address/${ADDRESS_TEST}`, { waitUntil: 'domcontentloaded', timeout: 20000 });
await page.waitForLoadState('domcontentloaded');
const hasBreadcrumb = await page.locator('#addressDetailBreadcrumb').count() > 0;
const bodyText = await page.locator('body').textContent().catch(() => '') || '';
expect(hasBreadcrumb || bodyText.length > 100).toBe(true);
expect(bodyText).toMatch(/Address|Balance|Transaction|0x99|Explorer|detail/i);
});
test('root path loads homepage', async ({ page }) => {
await page.goto(EXPLORER_URL, { waitUntil: 'load', timeout: 25000 });
// Logo and nav are always in the HTML; home view or stats appear once SPA runs
const body = await page.locator('body').textContent();
expect(body).toMatch(/SolaceScanScout|Explorer|Chain|Block|Transaction/i);
});
test('blocks path /blocks loads blocks list', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/blocks`, { waitUntil: 'domcontentloaded', timeout: 15000 });
await expect(page.locator('text=Block').first()).toBeVisible({ timeout: 8000 });
});
test('transactions path /transactions loads transactions list', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/transactions`, { waitUntil: 'domcontentloaded', timeout: 15000 });
await expect(page.locator('text=Transaction').first()).toBeVisible({ timeout: 8000 });
});
test('bridge path /bridge loads and shows bridge or explorer', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/bridge`, { waitUntil: 'networkidle', timeout: 20000 });
const url = page.url();
const body = await page.locator('body').textContent();
expect(url).toMatch(/\/bridge/);
expect(body).toMatch(/Bridge|SolaceScanScout|Explorer/i);
});
test('weth path /weth loads and shows WETH or explorer', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/weth`, { waitUntil: 'networkidle', timeout: 20000 });
const url = page.url();
const body = await page.locator('body').textContent();
expect(url).toMatch(/\/weth/);
expect(body).toMatch(/WETH|SolaceScanScout|Explorer/i);
});
test('watchlist path /watchlist loads and shows watchlist or explorer', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/watchlist`, { waitUntil: 'networkidle', timeout: 20000 });
const url = page.url();
const body = await page.locator('body').textContent();
expect(url).toMatch(/\/watchlist/);
expect(body).toMatch(/Watchlist|SolaceScanScout|Explorer/i);
});
});
test.describe('Explorer Frontend - Nav Links (path-based routing)', () => {
test('Root shows home content', async ({ page }) => {
await page.goto(EXPLORER_URL, { waitUntil: 'load', timeout: 20000 });
await expect(page.locator('#homeView').or(page.getByText('Latest Blocks')).first()).toBeVisible({ timeout: 10000 });
});
test('Blocks route /blocks loads', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/blocks`, { waitUntil: 'load', timeout: 20000 });
await expect(page).toHaveURL(/\/blocks/, { timeout: 5000 });
await expect(page.getByText('Block').first()).toBeVisible({ timeout: 8000 });
});
test('Transactions route /transactions loads', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/transactions`, { waitUntil: 'load', timeout: 20000 });
await expect(page).toHaveURL(/\/transactions/, { timeout: 5000 });
await expect(page.getByText('Transaction').first()).toBeVisible({ timeout: 8000 });
});
test('Bridge route /bridge has correct URL', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/bridge`, { waitUntil: 'load', timeout: 20000 });
await expect(page).toHaveURL(/\/bridge/, { timeout: 5000 });
});
test('WETH route /weth has correct URL', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/weth`, { waitUntil: 'load', timeout: 20000 });
await expect(page).toHaveURL(/\/weth/, { timeout: 5000 });
});
test('MetaMask Snap link has correct href', async ({ page }) => {
await page.goto(EXPLORER_URL, { waitUntil: 'domcontentloaded', timeout: 20000 });
const snapLink = page.locator('a[href="/snap/"]').first();
await expect(snapLink).toBeVisible({ timeout: 8000 });
await expect(snapLink).toHaveAttribute('href', '/snap/');
});
});
test.describe('Explorer Frontend - Breadcrumbs & Detail Links', () => {
test('Address page breadcrumb links work', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/address/${ADDRESS_TEST}`, { waitUntil: 'networkidle', timeout: 15000 });
await page.waitForSelector('#addressDetailBreadcrumb', { state: 'attached', timeout: 15000 });
const homeLink = page.locator('#addressDetailBreadcrumb a[href="/home"], #addressDetailBreadcrumb a[href="#/home"]').first();
if (await homeLink.isVisible({ timeout: 2000 }).catch(() => false)) {
await homeLink.click();
await page.waitForTimeout(500);
expect(page.url()).toContain('home');
}
});
test('Block number link from list opens block detail', async ({ page }) => {
await page.goto(`${EXPLORER_URL}/blocks`, { waitUntil: 'networkidle', timeout: 15000 });
const blockLink = page.locator('a[href^="#/block/"], [onclick*="showBlockDetail"]').first();
if (await blockLink.isVisible({ timeout: 3000 })) {
await blockLink.click();
await page.waitForTimeout(1000);
await expect(page.locator('#blockDetail, .block-detail')).toBeVisible({ timeout: 3000 });
}
});
});

View File

@@ -62,18 +62,19 @@ test_content() {
local url=$1
local search_term=$2
local description=$3
local extra_opts="${4:-}"
local content=$(curl -s -L --connect-timeout 10 --max-time 30 "$url" 2>/dev/null || echo "")
local content=$(curl -s -L --max-redirs 3 $extra_opts --connect-timeout 10 --max-time 30 "$url" 2>/dev/null || echo "")
if [ -z "$content" ]; then
log_test "FAIL" "$description (Empty response)"
return 1
elif echo "$content" | grep -qi "$search_term"; then
elif grep -qi "$search_term" <<< "$content"; then
log_test "PASS" "$description (Found: $search_term)"
return 0
else
# Show first 100 chars for debugging
PREVIEW=$(echo "$content" | head -c 100 | tr -d '\n')
PREVIEW=$(printf '%.100s' "$content" | tr -d '\n')
log_test "FAIL" "$description (Not found: $search_term, got: $PREVIEW...)"
return 1
fi
@@ -118,8 +119,13 @@ else
log_test "WARN" "HTTPS homepage returned HTTP $HTTPS_CODE"
fi
# Test HTTP redirect
test_http_response "http://explorer.d-bis.org" "301\|302" "HTTP to HTTPS redirect"
# Test HTTP redirect (301 or 302)
REDIRECT_CODE=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 10 "http://explorer.d-bis.org" 2>/dev/null || echo "000")
if [ "$REDIRECT_CODE" = "301" ] || [ "$REDIRECT_CODE" = "302" ]; then
log_test "PASS" "HTTP to HTTPS redirect (HTTP $REDIRECT_CODE)"
else
log_test "FAIL" "HTTP to HTTPS redirect (Expected 301 or 302, got $REDIRECT_CODE)"
fi
# Test direct IP access (internal)
test_http_response "$BASE_URL:80/" "200" "Direct IP access (port 80)"
@@ -318,10 +324,12 @@ else
log_test "FAIL" "API stats structure invalid"
fi
# Check for chain ID
# Check for chain ID (optional; Blockscout may use different field)
if echo "$STATS_JSON" | jq -e '.chain_id' >/dev/null 2>&1; then
CHAIN_ID=$(echo "$STATS_JSON" | jq -r '.chain_id // "unknown"')
log_test "PASS" "API returns chain ID ($CHAIN_ID)"
elif echo "$STATS_JSON" | jq -e '.total_blocks' >/dev/null 2>&1; then
log_test "PASS" "API returns stats (chain ID optional)"
else
log_test "WARN" "API does not return chain ID"
fi
@@ -329,19 +337,56 @@ fi
echo ""
# ============================================
# 10. Error Handling Tests
# 10. Path-Based URL & Link Tests (SPA routing)
# ============================================
echo "=== 10. Error Handling Tests ==="
echo "=== 10. Path-Based URL & Link Tests ==="
# Test 404 handling (use internal URL)
test_http_response "$BASE_URL:80/nonexistent-page" "404" "404 error handling"
# Use BASE_URL:80 for path tests (HTTP SPA paths served by nginx)
PATH_TEST_BASE="$BASE_URL:80"
PATH_CURL_EXTRA=""
# SPA serves index.html for all paths - verify path-based routing is present
test_content "$PATH_TEST_BASE/address/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506" "fromPath" "Path-based routing code present (address URL)" "$PATH_CURL_EXTRA"
test_content "$PATH_TEST_BASE/address/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506" "SolaceScanScout" "Address path serves SPA shell" "$PATH_CURL_EXTRA"
test_content "$PATH_TEST_BASE/blocks" "SolaceScanScout" "Blocks path serves SPA" "$PATH_CURL_EXTRA"
test_content "$PATH_TEST_BASE/transactions" "SolaceScanScout" "Transactions path serves SPA" "$PATH_CURL_EXTRA"
test_content "$PATH_TEST_BASE/bridge" "SolaceScanScout" "Bridge path serves SPA" "$PATH_CURL_EXTRA"
test_content "$PATH_TEST_BASE/weth" "SolaceScanScout" "WETH path serves SPA" "$PATH_CURL_EXTRA"
test_content "$PATH_TEST_BASE/watchlist" "SolaceScanScout" "Watchlist path serves SPA" "$PATH_CURL_EXTRA"
# Verify nav links exist in HTML (use same base)
test_content "$PATH_TEST_BASE/" "#/home" "Home nav link present" "$PATH_CURL_EXTRA"
test_content "$PATH_TEST_BASE/" "#/blocks" "Blocks nav link present" "$PATH_CURL_EXTRA"
test_content "$PATH_TEST_BASE/" "#/transactions" "Transactions nav link present" "$PATH_CURL_EXTRA"
test_content "$PATH_TEST_BASE/" "#/bridge" "Bridge nav link present" "$PATH_CURL_EXTRA"
test_content "$PATH_TEST_BASE/" "#/weth" "WETH nav link present" "$PATH_CURL_EXTRA"
test_content "$PATH_TEST_BASE/" "/snap/" "MetaMask Snap nav link present" "$PATH_CURL_EXTRA"
# Verify applyHashRoute handles pathname
test_content "$PATH_TEST_BASE/" "location.pathname" "Path-based route detection present" "$PATH_CURL_EXTRA"
echo ""
# ============================================
# 11. Error Handling Tests
# ============================================
echo "=== 11. Error Handling Tests ==="
# SPA fallback: unknown paths return index.html (200) or redirect (301/302)
FALLBACK_CODE=$(curl -s -o /dev/null -w '%{http_code}' --max-redirs 2 --connect-timeout 10 "$BASE_URL:80/nonexistent-page" 2>/dev/null || echo "000")
if [ "$FALLBACK_CODE" = "200" ] || [ "$FALLBACK_CODE" = "301" ] || [ "$FALLBACK_CODE" = "302" ]; then
log_test "PASS" "SPA fallback for unknown paths (HTTP $FALLBACK_CODE)"
else
log_test "WARN" "SPA fallback returned HTTP $FALLBACK_CODE (expected 200 or redirect)"
fi
# Test API error handling (use internal URL)
API_ERROR=$(curl -s -L --connect-timeout 10 --max-time 30 "$BASE_URL:80/api/v2/invalid-endpoint" 2>/dev/null || echo "")
if echo "$API_ERROR" | grep -qiE "error|404|not found"; then
log_test "PASS" "API error handling works"
API_ERROR_CODE=$(curl -s -o /dev/null -w '%{http_code}' -L --connect-timeout 10 --max-time 30 "$BASE_URL:80/api/v2/invalid-endpoint" 2>/dev/null || echo "000")
if [ "$API_ERROR_CODE" = "404" ] || [ "$API_ERROR_CODE" = "400" ] || echo "$API_ERROR" | grep -qiE "error|404|not found"; then
log_test "PASS" "API error handling works (HTTP $API_ERROR_CODE)"
else
log_test "WARN" "API error handling unclear"
log_test "WARN" "API error handling unclear (HTTP $API_ERROR_CODE)"
fi
echo ""

View File

@@ -31,7 +31,7 @@ fi
# Configuration
RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
ETHEREUM_MAINNET_SELECTOR="5009297550715157269"

View File

@@ -42,7 +42,7 @@ echo "=== Step 5: Creating Clean Configuration ==="
CONFIG_FILE="/etc/nginx/sites-available/blockscout"
cat > "$CONFIG_FILE" << 'EOF'
# HTTP server - redirect to HTTPS
# HTTP server - redirect to HTTPS only when not already behind HTTPS proxy (avoids ERR_TOO_MANY_REDIRECTS when NPMplus forwards to :80)
server {
listen 80;
listen [::]:80;
@@ -54,9 +54,28 @@ server {
try_files $uri =404;
}
# Redirect all other HTTP to HTTPS
# When NPMplus (or similar) forwards HTTPS traffic to this port as HTTP, do NOT redirect back to HTTPS (avoids ERR_TOO_MANY_REDIRECTS)
set $redirect_to_https 1;
if ($http_x_forwarded_proto = "https") { set $redirect_to_https 0; }
if ($http_x_forwarded_proto = "HTTPS") { set $redirect_to_https 0; }
location /snap/ {
alias /var/www/html/snap/;
try_files $uri $uri/ /snap/index.html =404;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
location = /snap { rewrite ^ /snap/ last; }
location / {
return 301 https://$host$request_uri;
if ($redirect_to_https = 1) { return 301 https://$host$request_uri; }
proxy_pass http://127.0.0.1:4000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
}
}
@@ -66,16 +85,9 @@ server {
listen [::]:443 ssl http2;
server_name explorer.d-bis.org 192.168.11.140;
# SSL configuration (if certificates exist)
# SSL configuration (nginx does not allow ssl_certificate inside if; use Let's Encrypt or self-signed)
ssl_certificate /etc/letsencrypt/live/explorer.d-bis.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/explorer.d-bis.org/privkey.pem;
# Fallback to self-signed if Let's Encrypt not available
if (!-f /etc/letsencrypt/live/explorer.d-bis.org/fullchain.pem) {
ssl_certificate /etc/nginx/ssl/blockscout.crt;
ssl_certificate_key /etc/nginx/ssl/blockscout.key;
}
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
@@ -92,6 +104,14 @@ server {
access_log /var/log/nginx/blockscout-access.log;
error_log /var/log/nginx/blockscout-error.log;
# Chain 138 MetaMask Snap companion (serve from disk; do not proxy to Blockscout)
location = /snap { rewrite ^ /snap/ last; }
location /snap/ {
alias /var/www/html/snap/;
try_files $uri $uri/ /snap/index.html =404;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
# Blockscout Explorer endpoint - proxy to Blockscout
location / {
proxy_pass http://127.0.0.1:4000;
@@ -109,6 +129,33 @@ server {
proxy_connect_timeout 75s;
}
# Token-aggregation API at /api/v1/ (Chain 138 Snap: market data, swap quote, bridge). Service runs on port 3001.
location /api/v1/ {
proxy_pass http://127.0.0.1:3001/api/v1/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
add_header Access-Control-Allow-Origin *;
}
# Explorer config API (token list, networks) - serve from /var/www/html/config/
# Deploy files with: ./scripts/deploy-explorer-config-to-vmid5000.sh
location = /api/config/token-list {
default_type application/json;
add_header Access-Control-Allow-Origin *;
add_header Cache-Control "public, max-age=3600";
alias /var/www/html/config/DUAL_CHAIN_TOKEN_LIST.tokenlist.json;
}
location = /api/config/networks {
default_type application/json;
add_header Access-Control-Allow-Origin *;
add_header Cache-Control "public, max-age=3600";
alias /var/www/html/config/DUAL_CHAIN_NETWORKS.json;
}
# API endpoint (for Blockscout API)
location /api/ {
proxy_pass http://127.0.0.1:4000;
@@ -143,6 +190,16 @@ EOF
echo "✅ Clean configuration created: $CONFIG_FILE"
echo ""
# Step 5.5: Ensure config directory exists for /api/config/token-list and /api/config/networks
echo "=== Step 5.5: Config Directory for Token List ==="
mkdir -p /var/www/html/config
if [ -f "/var/www/html/config/DUAL_CHAIN_TOKEN_LIST.tokenlist.json" ]; then
echo "Config files already present in /var/www/html/config/"
else
echo "Note: Run deploy-explorer-config-to-vmid5000.sh from repo root to deploy token list. /api/config/* will 404 until then."
fi
echo ""
# Step 6: Enable the site
echo "=== Step 6: Enabling Blockscout Site ==="
ln -sf "$CONFIG_FILE" /etc/nginx/sites-enabled/blockscout

View File

@@ -54,6 +54,19 @@ server {
add_header Content-Type application/json;
}
# When NPMplus forwards HTTPS to this port as HTTP, do NOT redirect to HTTPS (avoids ERR_TOO_MANY_REDIRECTS)
set $redirect_http_to_https 1;
if ($http_x_forwarded_proto = "https") { set $redirect_http_to_https 0; }
if ($http_x_forwarded_proto = "HTTPS") { set $redirect_http_to_https 0; }
# Snap companion (must be before catch-all so /snap/ is served from disk)
location = /snap { rewrite ^ /snap/ last; }
location /snap/ {
alias /var/www/html/snap/;
try_files $uri $uri/ /snap/index.html;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
# Serve custom frontend for root path (no-cache so fixes show after refresh)
# CSP with unsafe-eval required by ethers.js v5 (NPM proxies to port 80)
location = / {
@@ -81,9 +94,19 @@ server {
add_header Cache-Control "public, immutable";
}
# All other requests redirect to HTTPS
# SPA paths on HTTP (for internal/LAN tests) - serve index.html before redirect
location ~ ^/(address|tx|block|token|tokens|blocks|transactions|bridge|weth|watchlist|nft|home|analytics|operator)(/|$) {
root /var/www/html;
try_files /index.html =404;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
# All other requests: redirect to HTTPS only when not already behind HTTPS proxy
location / {
return 301 https://$host$request_uri;
if ($redirect_http_to_https = 1) { return 301 https://$host$request_uri; }
root /var/www/html;
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
}
@@ -182,6 +205,14 @@ server {
proxy_connect_timeout 75s;
}
# SPA paths: /address, /tx, /block, /token, /tokens, /blocks, /transactions, /bridge, /weth, /watchlist, /nft, /home, /analytics, /operator
# Must serve index.html so path-based routing works (regex takes precedence over proxy)
location ~ ^/(address|tx|block|token|tokens|blocks|transactions|bridge|weth|watchlist|nft|home|analytics|operator)(/|$) {
root /var/www/html;
try_files /index.html =404;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
# All other paths serve custom frontend (SPA fallback via try_files)
location / {
root /var/www/html;

View File

@@ -34,12 +34,21 @@ $EXEC_PREFIX df -h /
echo ""
$EXEC_PREFIX docker system df 2>/dev/null || true
echo ""
echo "=== Journal vacuum (keep 1d, max 100M) ==="
$EXEC_PREFIX journalctl --vacuum-time=1d 2>/dev/null || true
$EXEC_PREFIX journalctl --vacuum-size=100M 2>/dev/null || true
echo ""
echo "=== Old backups (keep last 2) ==="
$EXEC_PREFIX sh -c 'ls -t /var/www/html/index.html.backup.* 2>/dev/null | tail -n +3 | xargs -r rm -f' 2>/dev/null || true
$EXEC_PREFIX sh -c 'ls -t /etc/nginx/sites-available/blockscout.backup.* 2>/dev/null | tail -n +3 | xargs -r rm -f' 2>/dev/null || true
echo ""
echo "=== Truncate syslog + logrotate ==="
$EXEC_PREFIX sh -c ': > /var/log/syslog 2>/dev/null; logrotate -f /etc/logrotate.conf 2>/dev/null' || true
echo ""
echo "=== Pruning unused Docker data (safe: no container prune) ==="
# Do NOT use 'docker system prune' - it removes stopped containers (e.g. Blockscout)
$EXEC_PREFIX docker image prune -f 2>/dev/null || true
$EXEC_PREFIX docker builder prune -f 2>/dev/null || true
# Optional: uncomment to also prune unused volumes (risk if you use named volumes for data)
# $EXEC_PREFIX docker volume prune -f 2>/dev/null || true
echo ""
echo "=== Disk usage after ==="
$EXEC_PREFIX df -h /

View File

@@ -27,7 +27,7 @@ RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
LINK_TOKEN="${LINK_TOKEN:-0x73ADaF7dBa95221c080db5631466d2bC54f6a76B}"
WETH9="0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
WETH10="0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
PASSED=0

View File

@@ -30,7 +30,7 @@ fi
# Configuration
RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
LINK_TOKEN="${LINK_TOKEN:-0x326C977E6efc84E512bB9C30f76E30c160eD06FB}"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
AMOUNT_PER_BRIDGE="${1:-10}" # Default 10 LINK per bridge

View File

@@ -58,7 +58,7 @@ OUTPUT_FILE="${1:-docs/CCIP_STATUS_REPORT_$(date +%Y%m%d_%H%M%S).md}"
# Bridge status
echo "### Bridge Contracts"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
WETH9_BRIDGE_BYTECODE=$(cast code "$WETH9_BRIDGE" --rpc-url "$RPC_URL" 2>/dev/null || echo "")

View File

@@ -0,0 +1,53 @@
#!/usr/bin/env node
/**
* Generate favicon.ico and apple-touch-icon.png from frontend/public/icon.svg
* Run from repo root: node scripts/generate-favicons.js
*/
const fs = require('fs');
const path = require('path');
const PUBLIC = path.join(__dirname, '..', 'frontend', 'public');
const SVG_PATH = path.join(PUBLIC, 'icon.svg');
const APPLE_TOUCH_PATH = path.join(PUBLIC, 'apple-touch-icon.png');
const FAVICON_PATH = path.join(PUBLIC, 'favicon.ico');
async function main() {
let sharp, toIco;
try {
sharp = require('sharp');
toIco = require('to-ico');
} catch (e) {
console.error('Missing dependencies. Run: npm install sharp to-ico --save-dev');
process.exit(1);
}
if (!fs.existsSync(SVG_PATH)) {
console.error('SVG not found:', SVG_PATH);
process.exit(1);
}
const svgBuffer = fs.readFileSync(SVG_PATH);
// Apple touch icon: 180x180 PNG
await sharp(svgBuffer)
.resize(180, 180)
.png()
.toFile(APPLE_TOUCH_PATH);
console.log('Wrote', APPLE_TOUCH_PATH);
// Favicon: multi-size ICO (16, 32, 48)
const sizes = [16, 32, 48];
const pngBuffers = await Promise.all(
sizes.map((size) =>
sharp(svgBuffer).resize(size, size).png().toBuffer()
)
);
const icoBuffer = await toIco(pngBuffers);
fs.writeFileSync(FAVICON_PATH, icoBuffer);
console.log('Wrote', FAVICON_PATH);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});

View File

@@ -34,7 +34,7 @@ RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
LINK_TOKEN="0x326C977E6efc84E512bB9C30f76E30c160eD06FB"
WETH9="0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
WETH10="0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
if [ -z "${PRIVATE_KEY:-}" ]; then

View File

@@ -32,7 +32,7 @@ fi
# Configuration
RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
LINK_TOKEN="0x326C977E6efc84E512bB9C30f76E30c160eD06FB"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
if [ -z "${PRIVATE_KEY:-}" ]; then

View File

@@ -29,7 +29,7 @@ fi
# Configuration
RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
LINK_TOKEN="0x326C977E6efc84E512bB9C30f76E30c160eD06FB"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
ALERT_THRESHOLD="${1:-1.0}" # Default 1 LINK

View File

@@ -132,7 +132,7 @@ log_info "5. Contract Deployments"
ROUTER="0x8078A09637e47Fa5Ed34F626046Ea2094a5CDE5e"
SENDER="0x105F8A15b819948a89153505762444Ee9f324684"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
ROUTER_BYTECODE=$(cast code "$ROUTER" --rpc-url "$RPC_URL" 2>/dev/null || echo "")

View File

@@ -56,7 +56,7 @@ log_success " RPC accessible (block: $BLOCK_NUMBER)"
# Check bridge configuration
log_info " Checking bridge configuration..."
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
DEST=$(cast call "$WETH9_BRIDGE" "destinations(uint64)" "$DESTINATION_SELECTOR" --rpc-url "$RPC_URL" 2>/dev/null || echo "")
DEST_CLEAN=$(echo "$DEST" | grep -oE "^0x[0-9a-fA-F]{40}$" | head -1 || echo "")
if [ -z "$DEST_CLEAN" ] || echo "$DEST_CLEAN" | grep -qE "^0x0+$"; then

View File

@@ -92,7 +92,7 @@ fi
# A.3: Bridge Destinations
log_info ""
log_info "A.3: Bridge Destination Configuration"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
declare -A CHAIN_SELECTORS=(

View File

@@ -29,7 +29,7 @@ fi
# Configuration
RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
WETH10_BRIDGE="0xe0E93247376aa097dB308B92e6Ba36bA015535D0"
# Destination chain configurations

View File

@@ -31,7 +31,7 @@ fi
# Configuration
RPC_URL="${RPC_URL_138:-http://192.168.11.250:8545}"
WETH9_ADDRESS="0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
WETH9_BRIDGE="0x89dd12025bfCD38A168455A44B400e913ED33BE2"
WETH9_BRIDGE="0x971cD9D156f193df8051E48043C476e53ECd4693"
ETHEREUM_MAINNET_SELECTOR="5009297550715157269"
# Parse arguments