refactor: rename SolaceScanScout to Solace and update related configurations

- Updated branding from "SolaceScanScout" to "Solace" across various files including deployment scripts, API responses, and documentation.
- Changed default base URL for Playwright tests and updated security headers to reflect the new branding.
- Enhanced README and API documentation to include new authentication endpoints and product access details.

This refactor aligns the project branding and improves clarity in the API documentation.
This commit is contained in:
defiQUG
2026-04-10 12:52:17 -07:00
parent 6eef6b07f6
commit 0972178cc5
160 changed files with 13274 additions and 1061 deletions

View File

@@ -1,5 +1,6 @@
import { useEffect, useMemo, useState } from 'react'
import { Card } from '@/libs/frontend-ui-primitives'
import Link from 'next/link'
import { explorerFeaturePages } from '@/data/explorerOperations'
import { blocksApi, type Block } from '@/services/api/blocks'
import {
@@ -7,8 +8,14 @@ import {
type MissionControlBridgeStatusResponse,
type MissionControlChainStatus,
} from '@/services/api/missionControl'
import { statsApi, type ExplorerStats } from '@/services/api/stats'
import {
statsApi,
type ExplorerRecentActivitySnapshot,
type ExplorerStats,
type ExplorerTransactionTrendPoint,
} from '@/services/api/stats'
import { transactionsApi, type Transaction } from '@/services/api/transactions'
import { formatWeiAsEth } from '@/utils/format'
import OperationsPageShell, {
MetricCard,
StatusBadge,
@@ -17,6 +24,15 @@ import OperationsPageShell, {
truncateMiddle,
} from './OperationsPageShell'
interface AnalyticsOperationsPageProps {
initialStats?: ExplorerStats | null
initialTransactionTrend?: ExplorerTransactionTrendPoint[]
initialActivitySnapshot?: ExplorerRecentActivitySnapshot | null
initialBlocks?: Block[]
initialTransactions?: Transaction[]
initialBridgeStatus?: MissionControlBridgeStatusResponse | null
}
function getChainStatus(bridgeStatus: MissionControlBridgeStatusResponse | null): MissionControlChainStatus | null {
const chains = bridgeStatus?.data?.chains
if (!chains) return null
@@ -24,11 +40,20 @@ function getChainStatus(bridgeStatus: MissionControlBridgeStatusResponse | null)
return firstChain || null
}
export default function AnalyticsOperationsPage() {
const [stats, setStats] = useState<ExplorerStats | null>(null)
const [blocks, setBlocks] = useState<Block[]>([])
const [transactions, setTransactions] = useState<Transaction[]>([])
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(null)
export default function AnalyticsOperationsPage({
initialStats = null,
initialTransactionTrend = [],
initialActivitySnapshot = null,
initialBlocks = [],
initialTransactions = [],
initialBridgeStatus = null,
}: AnalyticsOperationsPageProps) {
const [stats, setStats] = useState<ExplorerStats | null>(initialStats)
const [transactionTrend, setTransactionTrend] = useState<ExplorerTransactionTrendPoint[]>(initialTransactionTrend)
const [activitySnapshot, setActivitySnapshot] = useState<ExplorerRecentActivitySnapshot | null>(initialActivitySnapshot)
const [blocks, setBlocks] = useState<Block[]>(initialBlocks)
const [transactions, setTransactions] = useState<Transaction[]>(initialTransactions)
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(initialBridgeStatus)
const [loadingError, setLoadingError] = useState<string | null>(null)
const page = explorerFeaturePages.analytics
@@ -36,8 +61,10 @@ export default function AnalyticsOperationsPage() {
let cancelled = false
const load = async () => {
const [statsResult, blocksResult, transactionsResult, bridgeResult] = await Promise.allSettled([
const [statsResult, trendResult, snapshotResult, blocksResult, transactionsResult, bridgeResult] = await Promise.allSettled([
statsApi.get(),
statsApi.getTransactionTrend(),
statsApi.getRecentActivitySnapshot(),
blocksApi.list({ chain_id: 138, page: 1, page_size: 5 }),
transactionsApi.list(138, 1, 5),
missionControlApi.getBridgeStatus(),
@@ -46,15 +73,17 @@ export default function AnalyticsOperationsPage() {
if (cancelled) return
if (statsResult.status === 'fulfilled') setStats(statsResult.value)
if (trendResult.status === 'fulfilled') setTransactionTrend(trendResult.value)
if (snapshotResult.status === 'fulfilled') setActivitySnapshot(snapshotResult.value)
if (blocksResult.status === 'fulfilled') setBlocks(blocksResult.value.data)
if (transactionsResult.status === 'fulfilled') setTransactions(transactionsResult.value.data)
if (bridgeResult.status === 'fulfilled') setBridgeStatus(bridgeResult.value)
const failedCount = [statsResult, blocksResult, transactionsResult, bridgeResult].filter(
const failedCount = [statsResult, trendResult, snapshotResult, blocksResult, transactionsResult, bridgeResult].filter(
(result) => result.status === 'rejected'
).length
if (failedCount === 4) {
if (failedCount === 6) {
setLoadingError('Analytics data is temporarily unavailable from the public explorer APIs.')
}
}
@@ -71,6 +100,27 @@ export default function AnalyticsOperationsPage() {
}, [])
const chainStatus = useMemo(() => getChainStatus(bridgeStatus), [bridgeStatus])
const trailingWindow = useMemo(() => transactionTrend.slice(0, 7), [transactionTrend])
const sevenDayAverage = useMemo(() => {
if (trailingWindow.length === 0) return 0
const total = trailingWindow.reduce((sum, point) => sum + point.transaction_count, 0)
return total / trailingWindow.length
}, [trailingWindow])
const topDay = useMemo(() => {
if (trailingWindow.length === 0) return null
return trailingWindow.reduce((best, point) => (point.transaction_count > best.transaction_count ? point : best))
}, [trailingWindow])
const averageGasUtilization = useMemo(() => {
if (blocks.length === 0) return 0
return blocks.reduce((sum, block) => {
const ratio = block.gas_limit > 0 ? block.gas_used / block.gas_limit : 0
return sum + ratio
}, 0) / blocks.length
}, [blocks])
const trendPeak = useMemo(
() => trailingWindow.reduce((max, point) => Math.max(max, point.transaction_count), 0),
[trailingWindow],
)
return (
<OperationsPageShell page={page}>
@@ -107,9 +157,111 @@ export default function AnalyticsOperationsPage() {
: 'Latest public RPC head age from mission control.'
}
/>
<MetricCard
title="7d Avg Tx"
value={formatNumber(Math.round(sevenDayAverage))}
description="Average daily transactions over the latest seven charted days."
className="border border-violet-200 bg-violet-50/70 dark:border-violet-900/50 dark:bg-violet-950/20"
/>
<MetricCard
title="Recent Success Rate"
value={activitySnapshot ? `${Math.round(activitySnapshot.success_rate * 100)}%` : 'Unknown'}
description="Success rate across the public main-page transaction sample."
/>
<MetricCard
title="Failure Rate"
value={activitySnapshot ? `${Math.round(activitySnapshot.failure_rate * 100)}%` : 'Unknown'}
description="The complement to the recent success rate in the visible sample."
className="border border-rose-200 bg-rose-50/70 dark:border-rose-900/50 dark:bg-rose-950/20"
/>
<MetricCard
title="Avg Gas Used"
value={activitySnapshot ? formatNumber(Math.round(activitySnapshot.average_gas_used)) : 'Unknown'}
description="Average gas used in the recent sampled transactions."
/>
<MetricCard
title="Avg Block Gas"
value={`${Math.round(averageGasUtilization * 100)}%`}
description="Average gas utilization across the latest visible blocks."
className="border border-amber-200 bg-amber-50/70 dark:border-amber-900/50 dark:bg-amber-950/20"
/>
</div>
<div className="mb-8 grid gap-6 lg:grid-cols-[1fr_1fr]">
<Card title="Activity Trend">
<div className="space-y-4">
<div className="grid gap-4 sm:grid-cols-3">
<div className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40">
<div className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Peak Day</div>
<div className="mt-2 text-xl font-semibold text-gray-900 dark:text-white">
{topDay ? formatNumber(topDay.transaction_count) : 'Unknown'}
</div>
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400">{topDay?.date || 'No trend data yet'}</div>
</div>
<div className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40">
<div className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Contract Creations</div>
<div className="mt-2 text-xl font-semibold text-gray-900 dark:text-white">
{formatNumber(activitySnapshot?.contract_creations)}
</div>
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400">Within the sampled recent transaction feed.</div>
</div>
<div className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40">
<div className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Avg Sample Fee</div>
<div className="mt-2 text-xl font-semibold text-gray-900 dark:text-white">
{activitySnapshot ? formatWeiAsEth(Math.round(activitySnapshot.average_fee_wei).toString(), 6) : 'Unknown'}
</div>
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400">Average fee from the recent public transaction sample.</div>
</div>
</div>
{activitySnapshot ? (
<div className="grid gap-3 sm:grid-cols-3">
<div className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40">
<div className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Token Transfer Share</div>
<div className="mt-2 text-xl font-semibold text-gray-900 dark:text-white">
{Math.round(activitySnapshot.token_transfer_share * 100)}%
</div>
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400">Sampled transactions involving token transfers.</div>
</div>
<div className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40">
<div className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Contract Call Share</div>
<div className="mt-2 text-xl font-semibold text-gray-900 dark:text-white">
{Math.round(activitySnapshot.contract_call_share * 100)}%
</div>
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400">Sampled transactions calling contracts.</div>
</div>
<div className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40">
<div className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Creation Share</div>
<div className="mt-2 text-xl font-semibold text-gray-900 dark:text-white">
{Math.round(activitySnapshot.contract_creation_share * 100)}%
</div>
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400">Sampled transactions deploying contracts.</div>
</div>
</div>
) : null}
<div className="space-y-3">
{trailingWindow.map((point) => {
const width = trendPeak > 0 ? Math.max(8, Math.round((point.transaction_count / trendPeak) * 100)) : 0
return (
<div key={point.date}>
<div className="mb-1 flex items-center justify-between text-sm text-gray-600 dark:text-gray-400">
<span>{point.date}</span>
<span>{formatNumber(point.transaction_count)} tx</span>
</div>
<div className="h-2 rounded-full bg-gray-200 dark:bg-gray-800">
<div className="h-2 rounded-full bg-primary-600" style={{ width: `${width}%` }} />
</div>
</div>
)
})}
{trailingWindow.length === 0 ? (
<p className="text-sm text-gray-600 dark:text-gray-400">Trend data is temporarily unavailable.</p>
) : null}
</div>
</div>
</Card>
<Card title="Recent Blocks">
<div className="space-y-4">
{blocks.map((block) => (
@@ -119,15 +271,18 @@ export default function AnalyticsOperationsPage() {
>
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
<div>
<div className="text-base font-semibold text-gray-900 dark:text-white">
<Link href={`/blocks/${block.number}`} className="text-base font-semibold text-primary-600 hover:underline">
Block {formatNumber(block.number)}
</div>
</Link>
<div className="mt-1 text-xs text-gray-500 dark:text-gray-400">
{truncateMiddle(block.hash)} · miner {truncateMiddle(block.miner)}
{truncateMiddle(block.hash)} · miner{' '}
<Link href={`/addresses/${block.miner}`} className="text-primary-600 hover:underline">
{truncateMiddle(block.miner)}
</Link>
</div>
</div>
<div className="text-sm text-gray-600 dark:text-gray-400">
{formatNumber(block.transaction_count)} tx · {relativeAge(block.timestamp)}
{formatNumber(block.transaction_count)} tx · {Math.round((block.gas_limit > 0 ? block.gas_used / block.gas_limit : 0) * 100)}% gas · {relativeAge(block.timestamp)}
</div>
</div>
</div>
@@ -147,11 +302,26 @@ export default function AnalyticsOperationsPage() {
>
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
<div>
<div className="text-base font-semibold text-gray-900 dark:text-white">
<Link href={`/transactions/${transaction.hash}`} className="text-base font-semibold text-primary-600 hover:underline">
{truncateMiddle(transaction.hash, 12, 10)}
</div>
</Link>
<div className="mt-1 text-xs text-gray-500 dark:text-gray-400">
Block {formatNumber(transaction.block_number)} · from {truncateMiddle(transaction.from_address)}
Block{' '}
<Link href={`/blocks/${transaction.block_number}`} className="text-primary-600 hover:underline">
{formatNumber(transaction.block_number)}
</Link>
{' '}· from{' '}
<Link href={`/addresses/${transaction.from_address}`} className="text-primary-600 hover:underline">
{truncateMiddle(transaction.from_address)}
</Link>
{transaction.to_address ? (
<>
{' '}· to{' '}
<Link href={`/addresses/${transaction.to_address}`} className="text-primary-600 hover:underline">
{truncateMiddle(transaction.to_address)}
</Link>
</>
) : null}
</div>
</div>
<div className="flex items-center gap-3">
@@ -164,6 +334,13 @@ export default function AnalyticsOperationsPage() {
</div>
</div>
</div>
<div className="mt-3 flex flex-wrap gap-2 text-xs">
{transaction.method ? <StatusBadge status={transaction.method} tone="warning" /> : null}
{transaction.contract_address ? <StatusBadge status="contract creation" tone="warning" /> : null}
{transaction.token_transfers && transaction.token_transfers.length > 0 ? (
<StatusBadge status={`${transaction.token_transfers.length} token transfer${transaction.token_transfers.length === 1 ? '' : 's'}`} />
) : null}
</div>
</div>
))}
{transactions.length === 0 ? (

View File

@@ -100,9 +100,13 @@ function ActionLink({
)
}
export default function BridgeMonitoringPage() {
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(null)
const [feedState, setFeedState] = useState<FeedState>('connecting')
export default function BridgeMonitoringPage({
initialBridgeStatus = null,
}: {
initialBridgeStatus?: MissionControlBridgeStatusResponse | null
}) {
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(initialBridgeStatus)
const [feedState, setFeedState] = useState<FeedState>(initialBridgeStatus ? 'fallback' : 'connecting')
const page = explorerFeaturePages.bridge
useEffect(() => {

View File

@@ -30,21 +30,55 @@ interface TokenPoolRecord {
pools: MissionControlLiquidityPool[]
}
interface EndpointCard {
name: string
method: string
href: string
notes: string
}
interface LiquidityOperationsPageProps {
initialTokenList?: TokenListResponse | null
initialRouteMatrix?: RouteMatrixResponse | null
initialPlannerCapabilities?: PlannerCapabilitiesResponse | null
initialInternalPlan?: InternalExecutionPlanResponse | null
initialTokenPoolRecords?: TokenPoolRecord[]
}
function routePairLabel(routeId: string, routeLabel: string, tokenIn?: string, tokenOut?: string): string {
return [tokenIn, tokenOut].filter(Boolean).join(' / ') || routeLabel || routeId
}
export default function LiquidityOperationsPage() {
const [tokenList, setTokenList] = useState<TokenListResponse | null>(null)
const [routeMatrix, setRouteMatrix] = useState<RouteMatrixResponse | null>(null)
const [plannerCapabilities, setPlannerCapabilities] = useState<PlannerCapabilitiesResponse | null>(null)
const [internalPlan, setInternalPlan] = useState<InternalExecutionPlanResponse | null>(null)
const [tokenPoolRecords, setTokenPoolRecords] = useState<TokenPoolRecord[]>([])
export default function LiquidityOperationsPage({
initialTokenList = null,
initialRouteMatrix = null,
initialPlannerCapabilities = null,
initialInternalPlan = null,
initialTokenPoolRecords = [],
}: LiquidityOperationsPageProps) {
const [tokenList, setTokenList] = useState<TokenListResponse | null>(initialTokenList)
const [routeMatrix, setRouteMatrix] = useState<RouteMatrixResponse | null>(initialRouteMatrix)
const [plannerCapabilities, setPlannerCapabilities] = useState<PlannerCapabilitiesResponse | null>(initialPlannerCapabilities)
const [internalPlan, setInternalPlan] = useState<InternalExecutionPlanResponse | null>(initialInternalPlan)
const [tokenPoolRecords, setTokenPoolRecords] = useState<TokenPoolRecord[]>(initialTokenPoolRecords)
const [loadingError, setLoadingError] = useState<string | null>(null)
const [copiedEndpoint, setCopiedEndpoint] = useState<string | null>(null)
useEffect(() => {
let cancelled = false
if (
initialTokenList &&
initialRouteMatrix &&
initialPlannerCapabilities &&
initialInternalPlan &&
initialTokenPoolRecords.length > 0
) {
return () => {
cancelled = true
}
}
const load = async () => {
const [tokenListResult, routeMatrixResult, plannerCapabilitiesResult, planResult] =
await Promise.allSettled([
@@ -102,7 +136,13 @@ export default function LiquidityOperationsPage() {
return () => {
cancelled = true
}
}, [])
}, [
initialInternalPlan,
initialPlannerCapabilities,
initialRouteMatrix,
initialTokenList,
initialTokenPoolRecords,
])
const featuredTokens = useMemo(
() => selectFeaturedLiquidityTokens(tokenList?.tokens || []),
@@ -139,7 +179,7 @@ export default function LiquidityOperationsPage() {
[routeMatrix, aggregatedPools.length, featuredTokens.length, livePlannerProviders.length, internalPlan?.plannerResponse?.decision, routeBackedPoolAddresses.length]
)
const endpointCards = [
const endpointCards: EndpointCard[] = [
{
name: 'Canonical route matrix',
method: 'GET',
@@ -166,6 +206,18 @@ export default function LiquidityOperationsPage() {
},
]
const copyEndpoint = async (endpoint: EndpointCard) => {
try {
await navigator.clipboard.writeText(endpoint.href)
setCopiedEndpoint(endpoint.name)
window.setTimeout(() => {
setCopiedEndpoint((current) => (current === endpoint.name ? null : current))
}, 1500)
} catch {
setCopiedEndpoint(null)
}
}
return (
<main className="container mx-auto px-4 py-6 sm:py-8">
<div className="mb-8 max-w-4xl">
@@ -258,9 +310,16 @@ export default function LiquidityOperationsPage() {
</div>
))}
{aggregatedPools.length === 0 ? (
<p className="text-sm text-gray-600 dark:text-gray-400">
No live pool inventory is available right now.
</p>
<div className="rounded-2xl border border-amber-200 bg-amber-50/70 p-4 dark:border-amber-900/50 dark:bg-amber-950/20">
<p className="text-sm leading-6 text-amber-900 dark:text-amber-100">
Mission-control pool inventory is currently empty, but the live route matrix still references{' '}
{formatNumber(routeBackedPoolAddresses.length)} pool-backed legs across{' '}
{formatNumber(routeMatrix?.counts?.filteredLiveRoutes)} published live routes.
</p>
<p className="mt-2 text-sm leading-6 text-amber-900/80 dark:text-amber-100/80">
Use the highlighted route-backed paths below and the public route matrix endpoint while pool inventory catches up.
</p>
</div>
) : null}
</div>
</Card>
@@ -339,12 +398,9 @@ export default function LiquidityOperationsPage() {
<Card title="Explorer Access Points">
<div className="grid gap-4 md:grid-cols-2">
{endpointCards.map((endpoint) => (
<a
<div
key={endpoint.href}
href={endpoint.href}
target="_blank"
rel="noopener noreferrer"
className="rounded-2xl border border-gray-200 bg-white p-5 transition hover:border-primary-400 hover:shadow-md dark:border-gray-700 dark:bg-gray-800"
className="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-700 dark:bg-gray-800"
>
<div className="flex flex-col items-start gap-3 sm:flex-row sm:items-center sm:justify-between">
<div className="text-base font-semibold text-gray-900 dark:text-white">{endpoint.name}</div>
@@ -356,7 +412,24 @@ export default function LiquidityOperationsPage() {
{endpoint.href}
</div>
<div className="mt-3 text-sm leading-6 text-gray-600 dark:text-gray-400">{endpoint.notes}</div>
</a>
<div className="mt-4 flex flex-wrap gap-3">
<button
type="button"
onClick={() => void copyEndpoint(endpoint)}
className="rounded-full border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition hover:border-primary-400 hover:text-primary-700 dark:border-gray-600 dark:text-gray-300 dark:hover:text-primary-300"
>
{copiedEndpoint === endpoint.name ? 'Copied' : 'Copy endpoint'}
</button>
{endpoint.name === 'Mission-control token pools' ? (
<Link
href="/pools"
className="rounded-full border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition hover:border-primary-400 hover:text-primary-700 dark:border-gray-600 dark:text-gray-300 dark:hover:text-primary-300"
>
Open pools page
</Link>
) : null}
</div>
</div>
))}
</div>
</Card>
@@ -404,12 +477,12 @@ export default function LiquidityOperationsPage() {
>
Open wallet tools
</Link>
<a
href="/docs.html"
<Link
href="/docs"
className="rounded-full border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition hover:border-primary-400 hover:text-primary-700 dark:border-gray-600 dark:text-gray-300 dark:hover:text-primary-300"
>
Explorer docs
</a>
</Link>
</div>
</div>
</Card>

View File

@@ -45,14 +45,28 @@ function ActionLink({
)
}
export default function MoreOperationsPage() {
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(null)
const [routeMatrix, setRouteMatrix] = useState<RouteMatrixResponse | null>(null)
const [networksConfig, setNetworksConfig] = useState<NetworksConfigResponse | null>(null)
const [tokenList, setTokenList] = useState<TokenListResponse | null>(null)
const [capabilities, setCapabilities] = useState<CapabilitiesResponse | null>(null)
interface OperationsHubPageProps {
initialBridgeStatus?: MissionControlBridgeStatusResponse | null
initialRouteMatrix?: RouteMatrixResponse | null
initialNetworksConfig?: NetworksConfigResponse | null
initialTokenList?: TokenListResponse | null
initialCapabilities?: CapabilitiesResponse | null
}
export default function OperationsHubPage({
initialBridgeStatus = null,
initialRouteMatrix = null,
initialNetworksConfig = null,
initialTokenList = null,
initialCapabilities = null,
}: OperationsHubPageProps) {
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(initialBridgeStatus)
const [routeMatrix, setRouteMatrix] = useState<RouteMatrixResponse | null>(initialRouteMatrix)
const [networksConfig, setNetworksConfig] = useState<NetworksConfigResponse | null>(initialNetworksConfig)
const [tokenList, setTokenList] = useState<TokenListResponse | null>(initialTokenList)
const [capabilities, setCapabilities] = useState<CapabilitiesResponse | null>(initialCapabilities)
const [loadingError, setLoadingError] = useState<string | null>(null)
const page = explorerFeaturePages.more
const page = explorerFeaturePages.operations
useEffect(() => {
let cancelled = false

View File

@@ -17,6 +17,13 @@ import OperationsPageShell, {
truncateMiddle,
} from './OperationsPageShell'
interface OperatorOperationsPageProps {
initialBridgeStatus?: MissionControlBridgeStatusResponse | null
initialRouteMatrix?: RouteMatrixResponse | null
initialPlannerCapabilities?: PlannerCapabilitiesResponse | null
initialInternalPlan?: InternalExecutionPlanResponse | null
}
function relayTone(status?: string): 'normal' | 'warning' | 'danger' {
const normalized = String(status || 'unknown').toLowerCase()
if (['degraded', 'stale', 'stopped', 'down'].includes(normalized)) return 'danger'
@@ -24,11 +31,16 @@ function relayTone(status?: string): 'normal' | 'warning' | 'danger' {
return 'normal'
}
export default function OperatorOperationsPage() {
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(null)
const [routeMatrix, setRouteMatrix] = useState<RouteMatrixResponse | null>(null)
const [plannerCapabilities, setPlannerCapabilities] = useState<PlannerCapabilitiesResponse | null>(null)
const [internalPlan, setInternalPlan] = useState<InternalExecutionPlanResponse | null>(null)
export default function OperatorOperationsPage({
initialBridgeStatus = null,
initialRouteMatrix = null,
initialPlannerCapabilities = null,
initialInternalPlan = null,
}: OperatorOperationsPageProps) {
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(initialBridgeStatus)
const [routeMatrix, setRouteMatrix] = useState<RouteMatrixResponse | null>(initialRouteMatrix)
const [plannerCapabilities, setPlannerCapabilities] = useState<PlannerCapabilitiesResponse | null>(initialPlannerCapabilities)
const [internalPlan, setInternalPlan] = useState<InternalExecutionPlanResponse | null>(initialInternalPlan)
const [loadingError, setLoadingError] = useState<string | null>(null)
const page = explorerFeaturePages.operator

View File

@@ -200,7 +200,7 @@ export default function PoolsOperationsPage() {
</div>
</Card>
<Card title="Liquidity Shortcuts">
<Card title="Pool operation shortcuts">
<div className="space-y-4 text-sm leading-6 text-gray-600 dark:text-gray-400">
<p>
The broader liquidity page now shows live route, planner, and pool access together.

View File

@@ -10,6 +10,12 @@ import {
type RouteMatrixResponse,
} from '@/services/api/routes'
interface RoutesMonitoringPageProps {
initialRouteMatrix?: RouteMatrixResponse | null
initialNetworks?: ExplorerNetwork[]
initialPools?: MissionControlLiquidityPool[]
}
const canonicalLiquidityToken = '0x93E66202A11B1772E55407B32B44e5Cd8eda7f22'
function relativeAge(isoString?: string): string {
@@ -80,10 +86,14 @@ function ActionLink({
)
}
export default function RoutesMonitoringPage() {
const [routeMatrix, setRouteMatrix] = useState<RouteMatrixResponse | null>(null)
const [networks, setNetworks] = useState<ExplorerNetwork[]>([])
const [pools, setPools] = useState<MissionControlLiquidityPool[]>([])
export default function RoutesMonitoringPage({
initialRouteMatrix = null,
initialNetworks = [],
initialPools = [],
}: RoutesMonitoringPageProps) {
const [routeMatrix, setRouteMatrix] = useState<RouteMatrixResponse | null>(initialRouteMatrix)
const [networks, setNetworks] = useState<ExplorerNetwork[]>(initialNetworks)
const [pools, setPools] = useState<MissionControlLiquidityPool[]>(initialPools)
const [loadingError, setLoadingError] = useState<string | null>(null)
const page = explorerFeaturePages.routes
@@ -389,7 +399,7 @@ export default function RoutesMonitoringPage() {
<ActionLink
href={action.href}
label={action.label}
external={'external' in action ? action.external : undefined}
external={Boolean((action as { external?: boolean }).external)}
/>
</div>
</div>

View File

@@ -12,13 +12,29 @@ import OperationsPageShell, {
relativeAge,
} from './OperationsPageShell'
export default function SystemOperationsPage() {
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(null)
const [networksConfig, setNetworksConfig] = useState<NetworksConfigResponse | null>(null)
const [tokenList, setTokenList] = useState<TokenListResponse | null>(null)
const [capabilities, setCapabilities] = useState<CapabilitiesResponse | null>(null)
const [routeMatrix, setRouteMatrix] = useState<RouteMatrixResponse | null>(null)
const [stats, setStats] = useState<ExplorerStats | null>(null)
interface SystemOperationsPageProps {
initialBridgeStatus?: MissionControlBridgeStatusResponse | null
initialNetworksConfig?: NetworksConfigResponse | null
initialTokenList?: TokenListResponse | null
initialCapabilities?: CapabilitiesResponse | null
initialRouteMatrix?: RouteMatrixResponse | null
initialStats?: ExplorerStats | null
}
export default function SystemOperationsPage({
initialBridgeStatus = null,
initialNetworksConfig = null,
initialTokenList = null,
initialCapabilities = null,
initialRouteMatrix = null,
initialStats = null,
}: SystemOperationsPageProps) {
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(initialBridgeStatus)
const [networksConfig, setNetworksConfig] = useState<NetworksConfigResponse | null>(initialNetworksConfig)
const [tokenList, setTokenList] = useState<TokenListResponse | null>(initialTokenList)
const [capabilities, setCapabilities] = useState<CapabilitiesResponse | null>(initialCapabilities)
const [routeMatrix, setRouteMatrix] = useState<RouteMatrixResponse | null>(initialRouteMatrix)
const [stats, setStats] = useState<ExplorerStats | null>(initialStats)
const [loadingError, setLoadingError] = useState<string | null>(null)
const page = explorerFeaturePages.system

View File

@@ -16,6 +16,12 @@ import OperationsPageShell, {
truncateMiddle,
} from './OperationsPageShell'
interface WethOperationsPageProps {
initialBridgeStatus?: MissionControlBridgeStatusResponse | null
initialPlannerCapabilities?: PlannerCapabilitiesResponse | null
initialInternalPlan?: InternalExecutionPlanResponse | null
}
function relayTone(status?: string): 'normal' | 'warning' | 'danger' {
const normalized = String(status || 'unknown').toLowerCase()
if (['degraded', 'stale', 'stopped', 'down'].includes(normalized)) return 'danger'
@@ -27,10 +33,14 @@ function relaySnapshot(relay: MissionControlRelayPayload | undefined) {
return relay?.url_probe?.body || relay?.file_snapshot
}
export default function WethOperationsPage() {
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(null)
const [plannerCapabilities, setPlannerCapabilities] = useState<PlannerCapabilitiesResponse | null>(null)
const [internalPlan, setInternalPlan] = useState<InternalExecutionPlanResponse | null>(null)
export default function WethOperationsPage({
initialBridgeStatus = null,
initialPlannerCapabilities = null,
initialInternalPlan = null,
}: WethOperationsPageProps) {
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(initialBridgeStatus)
const [plannerCapabilities, setPlannerCapabilities] = useState<PlannerCapabilitiesResponse | null>(initialPlannerCapabilities)
const [internalPlan, setInternalPlan] = useState<InternalExecutionPlanResponse | null>(initialInternalPlan)
const [loadingError, setLoadingError] = useState<string | null>(null)
const page = explorerFeaturePages.weth
@@ -85,6 +95,13 @@ export default function WethOperationsPage() {
return (
<OperationsPageShell page={page}>
<Card className="mb-6 border border-amber-200 bg-amber-50/70 dark:border-amber-900/50 dark:bg-amber-950/20">
<p className="text-sm leading-6 text-amber-950 dark:text-amber-100">
These WETH references are bridge and transport surfaces, not a claim that Ethereum mainnet WETH contracts are native Chain 138 assets.
Use this page to review wrapped-asset lane posture, counterpart contracts, and operational dependencies.
</p>
</Card>
{loadingError ? (
<Card className="mb-6 border border-red-200 bg-red-50/70 dark:border-red-900/50 dark:bg-red-950/20">
<p className="text-sm leading-6 text-red-900 dark:text-red-100">{loadingError}</p>