- Backend REST/gateway/track routes, analytics, Blockscout proxy paths. - Frontend wallet and liquidity surfaces; MetaMask token list alignment. - Deployment docs, verification scripts, address inventory updates. Check: go build ./... under backend/ (pass). Made-with: Cursor
207 lines
6.9 KiB
Bash
Executable File
207 lines
6.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
BASE_URL="${1:-https://explorer.d-bis.org}"
|
|
|
|
python3 - "$BASE_URL" <<'PY'
|
|
import re
|
|
import sys
|
|
|
|
import requests
|
|
|
|
|
|
base = sys.argv[1].rstrip("/")
|
|
session = requests.Session()
|
|
session.headers.update({"User-Agent": "ExplorerHealthCheck/2.0"})
|
|
|
|
failed = False
|
|
|
|
html_checks = [
|
|
"/",
|
|
"/home",
|
|
"/blocks",
|
|
"/transactions",
|
|
"/addresses",
|
|
"/bridge",
|
|
"/routes",
|
|
"/weth",
|
|
"/tokens",
|
|
"/pools",
|
|
"/watchlist",
|
|
"/more",
|
|
"/analytics",
|
|
"/operator",
|
|
"/system",
|
|
"/liquidity",
|
|
"/wallet",
|
|
"/snap/",
|
|
"/docs.html",
|
|
"/privacy.html",
|
|
"/terms.html",
|
|
"/acknowledgments.html",
|
|
]
|
|
|
|
json_checks = [
|
|
"/api/v2/stats",
|
|
"/api/config/token-list",
|
|
"/api/config/networks",
|
|
"/api/config/capabilities",
|
|
"/config/CHAIN138_RPC_CAPABILITIES.json",
|
|
"/config/topology-graph.json",
|
|
"/config/mission-control-verify.example.json",
|
|
"/explorer-api/v1/features",
|
|
"/explorer-api/v1/ai/context?q=cUSDT",
|
|
"/explorer-api/v1/track1/bridge/status",
|
|
"/explorer-api/v1/mission-control/liquidity/token/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22/pools",
|
|
"/token-aggregation/api/v1/routes/tree?chainId=138&tokenIn=0x93E66202A11B1772E55407B32B44e5Cd8eda7f22&tokenOut=0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1&amountIn=1000000",
|
|
"/token-aggregation/api/v1/routes/matrix",
|
|
"/token-aggregation/api/v1/routes/ingestion?fromChainId=138&routeType=swap",
|
|
"/token-aggregation/api/v1/routes/partner-payloads?partner=0x&amount=1000000&includeUnsupported=true",
|
|
]
|
|
|
|
asset_checks = [
|
|
"/token-icons/cUSDC.png",
|
|
"/token-icons/cUSDT.png",
|
|
"/token-icons/cXAUC.png",
|
|
"/token-icons/cXAUT.png",
|
|
]
|
|
|
|
|
|
def mark_failure(message):
|
|
global failed
|
|
failed = True
|
|
print(message)
|
|
|
|
|
|
print("== Public routes ==")
|
|
for path in html_checks:
|
|
url = base + path
|
|
try:
|
|
resp = session.get(url, timeout=20, allow_redirects=True)
|
|
ctype = resp.headers.get("content-type", "")
|
|
print(f"{resp.status_code:>3} {path} [{ctype[:60]}]")
|
|
if resp.status_code >= 400:
|
|
failed = True
|
|
except Exception as exc:
|
|
mark_failure(f"ERR {path} [{exc}]")
|
|
|
|
print("\n== JSON and API surfaces ==")
|
|
for path in json_checks:
|
|
url = base + path
|
|
try:
|
|
resp = session.get(url, timeout=20, allow_redirects=True)
|
|
ctype = resp.headers.get("content-type", "")
|
|
print(f"{resp.status_code:>3} {path} [{ctype[:60]}]")
|
|
if resp.status_code >= 400:
|
|
failed = True
|
|
continue
|
|
if "json" not in ctype:
|
|
failed = True
|
|
print(f" expected JSON content-type, got: {ctype}")
|
|
continue
|
|
payload = resp.json()
|
|
if path == "/api/config/capabilities":
|
|
if payload.get("chainId") != 138:
|
|
mark_failure(" capabilities JSON does not report chainId 138")
|
|
wallet_support = payload.get("walletSupport", {})
|
|
if wallet_support.get("walletWatchAsset") is not True:
|
|
mark_failure(" capabilities JSON does not advertise walletWatchAsset")
|
|
elif path == "/api/config/token-list":
|
|
tokens = payload.get("tokens", [])
|
|
if not tokens:
|
|
mark_failure(" token list is empty")
|
|
elif path == "/api/config/networks":
|
|
chains = payload.get("chains", [])
|
|
if not chains:
|
|
mark_failure(" networks payload does not include any chains")
|
|
elif path == "/explorer-api/v1/features":
|
|
if "features" not in payload:
|
|
mark_failure(" features payload is missing the features key")
|
|
elif path == "/explorer-api/v1/track1/bridge/status":
|
|
data = payload.get("data", {})
|
|
relays = data.get("ccip_relays", {})
|
|
expected_relays = {
|
|
"avax",
|
|
"avax_cw",
|
|
"avax_to_138",
|
|
"bsc",
|
|
"mainnet_cw",
|
|
"mainnet_weth",
|
|
}
|
|
missing = sorted(expected_relays - set(relays))
|
|
if missing:
|
|
mark_failure(f" bridge status is missing relay keys: {', '.join(missing)}")
|
|
if data.get("status") not in {"operational", "paused", "degraded"}:
|
|
mark_failure(" bridge status payload does not include a recognized overall status")
|
|
except Exception as exc:
|
|
mark_failure(f"ERR {path} [{exc}]")
|
|
|
|
print("\n== Static assets ==")
|
|
for path in asset_checks:
|
|
url = base + path
|
|
try:
|
|
resp = session.get(url, timeout=20, allow_redirects=True)
|
|
ctype = resp.headers.get("content-type", "")
|
|
print(f"{resp.status_code:>3} {path} [{ctype[:60]}]")
|
|
if resp.status_code >= 400:
|
|
failed = True
|
|
except Exception as exc:
|
|
mark_failure(f"ERR {path} [{exc}]")
|
|
|
|
print("\n== Mission-control SSE ==")
|
|
stream_url = base + "/explorer-api/v1/mission-control/stream"
|
|
try:
|
|
with session.get(stream_url, timeout=(20, 20), stream=True) as resp:
|
|
ctype = resp.headers.get("content-type", "")
|
|
print(f"{resp.status_code:>3} /explorer-api/v1/mission-control/stream [{ctype[:60]}]")
|
|
if resp.status_code >= 400:
|
|
failed = True
|
|
else:
|
|
lines = []
|
|
for raw in resp.iter_lines(decode_unicode=True):
|
|
if raw:
|
|
lines.append(raw)
|
|
if len(lines) >= 2:
|
|
break
|
|
if not any(line.startswith("event:") for line in lines):
|
|
mark_failure(" mission-control stream did not emit an event line")
|
|
if not any(line.startswith("data:") for line in lines):
|
|
mark_failure(" mission-control stream did not emit a data line")
|
|
except Exception as exc:
|
|
mark_failure(f"ERR /explorer-api/v1/mission-control/stream [{exc}]")
|
|
|
|
print("\n== Internal href targets from homepage ==")
|
|
try:
|
|
home = session.get(base + "/", timeout=20).text
|
|
hrefs = sorted(set(re.findall(r'href=\"([^\"]+)\"', home)))
|
|
for href in hrefs:
|
|
if href.startswith("/") and not href.startswith("//"):
|
|
resp = session.get(base + href, timeout=20, allow_redirects=True)
|
|
print(f"{resp.status_code:>3} {href}")
|
|
if resp.status_code >= 400:
|
|
failed = True
|
|
except Exception as exc:
|
|
mark_failure(f"ERR homepage href sweep failed: {exc}")
|
|
|
|
print("\n== External explorer roots referenced by bridge surfaces ==")
|
|
external_roots = [
|
|
"https://etherscan.io/",
|
|
"https://bscscan.com/",
|
|
"https://polygonscan.com/",
|
|
"https://subnets.avax.network/c-chain",
|
|
"https://basescan.org/",
|
|
"https://arbiscan.io/",
|
|
"https://optimistic.etherscan.io/",
|
|
]
|
|
for url in external_roots:
|
|
try:
|
|
resp = session.get(url, timeout=20, allow_redirects=True)
|
|
print(f"{resp.status_code:>3} {url}")
|
|
except Exception as exc:
|
|
print(f"ERR {url} [{exc}]")
|
|
|
|
if failed:
|
|
sys.exit(1)
|
|
PY
|