Prune explorer dead ends and harden route smoke

This commit is contained in:
defiQUG
2026-03-28 15:15:23 -07:00
parent bf83ff7776
commit e5f038c9db
3 changed files with 137 additions and 57 deletions

View File

@@ -2232,29 +2232,77 @@
}
window._showAddresses = renderAddressesView;
// Analytics view (Track 3+)
function buildAnalyticsViewHtml() {
var html = '';
html += '<div style="display:grid; gap:1rem;">';
html += '<div class="card" style="margin:0; box-shadow:none;">';
html += '<div class="card-header" style="align-items:flex-start; gap:0.75rem;">';
html += '<div>';
html += '<h3 class="card-title" style="margin-bottom:0.25rem;"><i class="fas fa-chart-line"></i> Live Network Analytics</h3>';
html += '<div style="color:var(--text-light); line-height:1.55;">Analytics surfaces are consolidated into the live explorer dashboards instead of a separate unfinished panel. Use this page as a hub to the active gas, block, bridge, and route monitoring views.</div>';
html += '</div>';
html += '<div style="display:flex; gap:0.5rem; flex-wrap:wrap; margin-left:auto;">';
html += '<button type="button" class="btn btn-primary" onclick="showHome(); updatePath(\'/\')" aria-label="Open network dashboard"><i class="fas fa-gauge-high"></i> Network dashboard</button>';
html += '<button type="button" class="btn btn-secondary" onclick="showBridgeMonitoring(); updatePath(\'/bridge\')" aria-label="Open bridge monitoring"><i class="fas fa-bridge"></i> Bridge monitoring</button>';
html += '</div>';
html += '</div>';
html += '<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); gap:0.75rem;">';
html += '<a href="/" onclick="event.preventDefault(); showHome(); updatePath(\'/\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">Gas &amp; Network</div><div style="color:var(--text-light); line-height:1.5;">Open the live home dashboard for gas price, TPS, block time, validator count, and latest-chain activity.</div></a>';
html += '<a href="/blocks" onclick="event.preventDefault(); showBlocks(); updatePath(\'/blocks\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">Block cadence</div><div style="color:var(--text-light); line-height:1.5;">Inspect live block production, miner attribution, gas usage, and exportable block history.</div></a>';
html += '<a href="/transactions" onclick="event.preventDefault(); showTransactions(); updatePath(\'/transactions\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">Transaction flow</div><div style="color:var(--text-light); line-height:1.5;">Review the recent transaction stream and drill into decoded execution details and internal calls.</div></a>';
html += '<a href="/routes" onclick="event.preventDefault(); showRoutes(); updatePath(\'/routes\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">Route coverage</div><div style="color:var(--text-light); line-height:1.5;">Open the dedicated route-decision tree for swap-path coverage, bridge branches, and missing quote-token diagnostics.</div></a>';
html += '</div>';
html += '</div>';
return html;
}
// Analytics view
function renderAnalyticsView() {
if (!hasAccess(3)) {
showToast('Analytics features require Track 3 access. Please connect your wallet and ensure you are approved.', 'error');
return;
}
switchToView('analytics');
showView('analytics');
if ((window.location.pathname || '').replace(/^\//, '').split('/')[0] !== 'analytics') updatePath('/analytics');
var container = document.getElementById('analyticsContent');
if (!container) return;
container.innerHTML = buildAnalyticsViewHtml();
}
window._showAnalytics = renderAnalyticsView;
// Operator view (Track 4)
function buildOperatorViewHtml() {
var html = '';
html += '<div style="display:grid; gap:1rem;">';
html += '<div class="card" style="margin:0; box-shadow:none;">';
html += '<div class="card-header" style="align-items:flex-start; gap:0.75rem;">';
html += '<div>';
html += '<h3 class="card-title" style="margin-bottom:0.25rem;"><i class="fas fa-server"></i> Operator Access Hub</h3>';
html += '<div style="color:var(--text-light); line-height:1.55;">The explorer does not expose raw privileged controls here. Instead, this page collects the live operator-facing observability and execution surfaces that are safe to browse from the public UI.</div>';
html += '</div>';
html += '<div style="display:flex; gap:0.5rem; flex-wrap:wrap; margin-left:auto;">';
html += '<button type="button" class="btn btn-primary" onclick="showBridgeMonitoring(); updatePath(\'/bridge\')" aria-label="Open bridge monitoring"><i class="fas fa-bridge"></i> Bridge status</button>';
html += '<button type="button" class="btn btn-secondary" onclick="showLiquidityAccess(); updatePath(\'/liquidity\')" aria-label="Open liquidity access"><i class="fas fa-wave-square"></i> Liquidity APIs</button>';
html += '</div>';
html += '</div>';
html += '<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); gap:0.75rem;">';
html += '<a href="/bridge" onclick="event.preventDefault(); showBridgeMonitoring(); updatePath(\'/bridge\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">Bridge monitoring</div><div style="color:var(--text-light); line-height:1.5;">Inspect bridge balances, fee-token posture, destination configuration, and live contract references.</div></a>';
html += '<a href="/liquidity" onclick="event.preventDefault(); showLiquidityAccess(); updatePath(\'/liquidity\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">Liquidity access</div><div style="color:var(--text-light); line-height:1.5;">Jump to partner payload routes, ingestion APIs, and public execution-plan endpoints without leaving the explorer.</div></a>';
html += '<a href="/pools" onclick="event.preventDefault(); showPools(); updatePath(\'/pools\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">Pool inventory</div><div style="color:var(--text-light); line-height:1.5;">Review canonical PMM addresses, funding state, registry status, and exportable pool snapshots.</div></a>';
html += '<a href="/weth" onclick="event.preventDefault(); showWETHUtilities(); updatePath(\'/weth\');" style="display:block; text-decoration:none; color:inherit; border:1px solid var(--border); border-radius:14px; padding:1rem; background:var(--muted-surface);"><div style="font-weight:700; margin-bottom:0.3rem;">WETH utilities</div><div style="color:var(--text-light); line-height:1.5;">Open the WETH9/WETH10 utilities, bridge contract references, and balance tools that operators often need during support.</div></a>';
html += '</div>';
html += '</div>';
return html;
}
// Operator view
function renderOperatorView() {
if (!hasAccess(4)) {
showToast('Operator features require Track 4 access. Please connect your wallet and ensure you are approved.', 'error');
return;
}
switchToView('operator');
showView('operator');
if ((window.location.pathname || '').replace(/^\//, '').split('/')[0] !== 'operator') updatePath('/operator');
var container = document.getElementById('operatorContent');
if (!container) return;
container.innerHTML = buildOperatorViewHtml();
}
window._showOperator = renderOperatorView;
function showView(viewName) {
currentView = viewName;
var detailViews = ['blockDetail','transactionDetail','addressDetail','tokenDetail','nftDetail','watchlist','searchResults','tokens','addresses','pools','liquidity','more'];
var detailViews = ['blockDetail','transactionDetail','addressDetail','tokenDetail','nftDetail','watchlist','searchResults','tokens','addresses','pools','routes','liquidity','more','analytics','operator'];
if (detailViews.indexOf(viewName) === -1) currentDetailKey = '';
document.querySelectorAll('.detail-view').forEach(v => v.classList.remove('active'));
const homeView = document.getElementById('homeView');
@@ -3995,9 +4043,9 @@
key: 'explore',
title: 'Explore',
items: [
{ title: 'Gas Tracker', icon: 'fa-gas-pump', status: 'Live', badgeClass: 'badge-success', desc: 'Review live gas, block time, TPS, and chain health from the analytics and home dashboards.', action: 'showAnalytics();', href: '/analytics' },
{ title: 'Gas Tracker', icon: 'fa-gas-pump', status: 'Live', badgeClass: 'badge-success', desc: 'Review live gas, block time, TPS, and chain health from the home network dashboard.', action: 'showHome();', href: '/' },
{ title: 'DEX Tracker', icon: 'fa-chart-line', status: 'Live', badgeClass: 'badge-success', desc: 'Open liquidity discovery, PMM pool status, live route trees, and partner payload access points.', action: 'showRoutes();', href: '/routes' },
{ title: 'Node Tracker', icon: 'fa-server', status: 'Live', badgeClass: 'badge-success', desc: 'Inspect bridge and operator infrastructure surfaces already exposed in the Bridge and Operator panels.', action: 'showOperator();', href: '/operator' },
{ title: 'Node Tracker', icon: 'fa-server', status: 'Live', badgeClass: 'badge-success', desc: 'Inspect bridge balances, destination configuration, and operator-facing chain references from the live bridge monitoring panel.', action: 'showBridgeMonitoring();', href: '/bridge' },
{ title: 'Label Cloud', icon: 'fa-tags', status: 'Live', badgeClass: 'badge-success', desc: 'Browse labeled addresses, contracts, and address activity through the explorer address index.', action: 'showAddresses();', href: '/addresses' },
{ title: 'Domain Name Lookup', icon: 'fa-magnifying-glass', status: 'Live', badgeClass: 'badge-success', desc: 'Use the smart search launcher to resolve ENS-style names, domains, addresses, hashes, and token symbols.', action: 'openSmartSearchModal(\'\');', href: '/more' }
]
@@ -4010,7 +4058,7 @@
{ title: 'Verified Signature', icon: 'fa-signature', status: 'Live', badgeClass: 'badge-success', desc: 'Use wallet sign-in and verified address flows already built into the explorer authentication surfaces.', action: 'showWalletModal();', href: '/more' },
{ title: 'Input Data Messages', icon: 'fa-message', status: 'Live', badgeClass: 'badge-info', desc: 'Transaction detail pages already surface decoded input data, event logs, and contract interaction context.', action: 'showTransactionsList();', href: '/transactions' },
{ title: 'Advanced Filter', icon: 'fa-filter', status: 'Live', badgeClass: 'badge-success', desc: 'Block, transaction, address, token, pool, bridge, and watchlist screens all support focused page-level filtering.', action: 'showTransactionsList();', href: '/transactions' },
{ title: 'Blockscan Chat', icon: 'fa-comments', status: 'Soon', badgeClass: 'badge-muted', desc: 'Messaging and collaborative address discussion are not exposed yet in SolaceScanScout.', disabled: true, href: '/more' }
{ title: 'MetaMask Snap', icon: 'fa-wallet', status: 'Live', badgeClass: 'badge-success', desc: 'Open the Chain 138 MetaMask Snap companion for network setup, token list access, and wallet integration guidance.', action: 'window.location.href=\'/snap/\';', href: '/snap/' }
]
}
];
@@ -4022,6 +4070,7 @@
html += '<div style="display:grid; gap:0.75rem;">';
html += '<div style="padding:0.85rem; border:1px solid var(--border); border-radius:14px; background:var(--muted-surface);"><div style="font-size:0.82rem; text-transform:uppercase; letter-spacing:0.08em; color:var(--text-light); margin-bottom:0.35rem;">Now live</div><div style="font-weight:700;">Route matrix, ingestion APIs, smart search, pool exports, and live Mainnet stable bridge discovery.</div></div>';
html += '<div style="padding:0.85rem; border:1px solid var(--border); border-radius:14px; background:var(--muted-surface);"><div style="font-size:0.82rem; text-transform:uppercase; letter-spacing:0.08em; color:var(--text-light); margin-bottom:0.35rem;">Good entry points</div><div style="display:flex; flex-wrap:wrap; gap:0.5rem;">';
html += '<button type="button" class="btn btn-primary" onclick="showRoutes(); updatePath(\'/routes\');"><i class="fas fa-diagram-project"></i> Routes</button>';
html += '<button type="button" class="btn btn-primary" onclick="showLiquidityAccess(); updatePath(\'/liquidity\');"><i class="fas fa-wave-square"></i> Liquidity</button>';
html += '<button type="button" class="btn btn-secondary" onclick="openSmartSearchModal(\'\');"><i class="fas fa-magnifying-glass"></i> Search</button>';
html += '<button type="button" class="btn btn-secondary" onclick="showAddresses(); updatePath(\'/addresses\');"><i class="fas fa-address-book"></i> Addresses</button>';

View File

@@ -1540,7 +1540,7 @@
<h2 class="card-title"><i class="fas fa-chart-line"></i> Analytics Dashboard</h2>
</div>
<div id="analyticsContent">
<p style="color: var(--text-light);">Analytics dashboard (Track 3). Network stats, flow tracking, and bridge analytics coming soon.</p>
<div class="loading"><i class="fas fa-spinner"></i> Loading analytics access...</div>
</div>
</div>
</div>
@@ -1552,7 +1552,7 @@
<h2 class="card-title"><i class="fas fa-cog"></i> Operator Panel</h2>
</div>
<div id="operatorContent">
<p style="color: var(--text-light);">Operator panel (Track 4). Configuration and operational controls coming soon.</p>
<div class="loading"><i class="fas fa-spinner"></i> Loading operator access...</div>
</div>
</div>
</div>

View File

@@ -1,65 +1,96 @@
const baseUrl = (process.env.BASE_URL || 'http://127.0.0.1:3000').replace(/\/$/, '')
import { chromium } from 'playwright';
const baseUrl = (process.env.BASE_URL || 'https://explorer.d-bis.org').replace(/\/$/, '');
const checks = [
{ path: '/', expect: ['/addresses', '/transactions', '/watchlist', '/pools'] },
{ path: '/blocks', expect: ['Latest Blocks', 'View blockchain blocks', 'Blocks'] },
{ path: '/transactions', expect: ['Latest Transactions', 'Recent Transactions', 'Transactions'] },
{ path: '/addresses', expect: ['Open An Address', 'Recently Active Addresses', 'Saved Watchlist'] },
{ path: '/tokens', expect: ['Find A Token', 'Common token searches', 'Tokens'] },
{ path: '/pools', expect: ['Canonical PMM routes', 'Pool operation shortcuts', 'Pools'] },
{ path: '/routes', expect: ['Live Route Decision Tree', 'Coverage sweep + priority route cards', 'Routes'] },
{ path: '/watchlist', expect: ['Saved Addresses', 'Watchlist', 'Export JSON'] },
{ path: '/search?q=cUSDT', expect: ['Search Results', 'Results for', 'Search'] },
{ path: '/blocks/1', expect: ['Loading block details', 'Block Details'] },
{ path: '/', homeVisible: true, expectTexts: ['Gas & Network', 'Latest Blocks', 'Latest Transactions'] },
{ path: '/blocks', activeView: 'blocksView', expectTexts: ['All Blocks'] },
{ path: '/transactions', activeView: 'transactionsView', expectTexts: ['All Transactions'] },
{ path: '/addresses', activeView: 'addressesView', expectTexts: ['All Addresses'] },
{ path: '/tokens', activeView: 'tokensView', expectTexts: ['Tokens'] },
{ path: '/pools', activeView: 'poolsView', expectTexts: ['Pools', 'Canonical PMM routes'] },
{ path: '/routes', activeView: 'routesView', expectTexts: ['Routes', 'Live Route Decision Tree'] },
{ path: '/watchlist', activeView: 'watchlistView', expectTexts: ['Watchlist'] },
{ path: '/bridge', activeView: 'bridgeView', expectTexts: ['Bridge Monitoring'] },
{ path: '/weth', activeView: 'wethView', expectTexts: ['WETH', 'Wrap ETH to WETH9'] },
{ path: '/liquidity', activeView: 'liquidityView', expectTexts: ['Liquidity Access', 'Public Explorer Access Points'] },
{ path: '/more', activeView: 'moreView', expectTexts: ['More', 'Tools & Services'] },
{ path: '/analytics', activeView: 'analyticsView', expectTexts: ['Analytics Dashboard', 'Live Network Analytics'] },
{ path: '/operator', activeView: 'operatorView', expectTexts: ['Operator Panel', 'Operator Access Hub'] },
{ path: '/block/1', activeView: 'blockDetailView', expectTexts: ['Block Details'] },
{
path: '/transactions/0x0000000000000000000000000000000000000000000000000000000000000000',
expect: ['Loading transaction details', 'Transaction Details', 'Transaction not found'],
path: '/tx/0x0000000000000000000000000000000000000000000000000000000000000000',
activeView: 'transactionDetailView',
expectTexts: ['Transaction Details', 'Transaction not found'],
},
{
path: '/addresses/0x0000000000000000000000000000000000000000',
expect: ['Loading address details', 'Address Details', 'Open An Address'],
path: '/address/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22',
activeView: 'addressDetailView',
expectTexts: ['Address Details', 'Address'],
},
]
];
function hasExpectedBody(text, expectedSnippets) {
return expectedSnippets.some((snippet) => text.includes(snippet))
function hasExpectedText(text, snippets) {
return snippets.some((snippet) => text.includes(snippet));
}
async function run() {
let failures = 0
async function main() {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
let failures = 0;
for (const check of checks) {
const url = `${baseUrl}${check.path}`
const url = `${baseUrl}${check.path}`;
try {
const response = await fetch(url, { redirect: 'follow' })
const body = await response.text()
if (!response.ok) {
console.error(`FAIL ${check.path}: HTTP ${response.status}`)
failures += 1
continue
const response = await page.goto(url, { waitUntil: 'networkidle' });
if (!response || !response.ok()) {
console.error(`FAIL ${check.path}: HTTP ${response ? response.status() : 'no-response'}`);
failures += 1;
continue;
}
if (!hasExpectedBody(body, check.expect)) {
console.error(`FAIL ${check.path}: expected one of ${check.expect.join(' | ')}`)
failures += 1
continue
const bodyText = await page.textContent('body');
if (check.homeVisible) {
const homeVisible = await page.$eval('#homeView', (el) => getComputedStyle(el).display !== 'none').catch(() => false);
if (!homeVisible) {
console.error(`FAIL ${check.path}: home view not visible`);
failures += 1;
continue;
}
} else {
const activeView = await page.$eval('.detail-view.active', (el) => el.id).catch(() => null);
if (activeView !== check.activeView) {
console.error(`FAIL ${check.path}: expected active view ${check.activeView}, got ${activeView}`);
failures += 1;
continue;
}
}
console.log(`OK ${check.path}`)
if (!hasExpectedText(bodyText || '', check.expectTexts)) {
console.error(`FAIL ${check.path}: expected one of ${check.expectTexts.join(' | ')}`);
failures += 1;
continue;
}
console.log(`OK ${check.path}`);
} catch (error) {
console.error(`FAIL ${check.path}: ${error instanceof Error ? error.message : String(error)}`)
failures += 1
console.error(`FAIL ${check.path}: ${error instanceof Error ? error.message : String(error)}`);
failures += 1;
}
}
await browser.close();
if (failures > 0) {
process.exitCode = 1
return
process.exitCode = 1;
return;
}
console.log(`All ${checks.length} route checks passed for ${baseUrl}`)
console.log(`All ${checks.length} route checks passed for ${baseUrl}`);
}
run()
main().catch((error) => {
console.error(error instanceof Error ? error.stack || error.message : String(error));
process.exitCode = 1;
});