Files
explorer-monorepo/frontend/src/components/explorer/LiquidityOperationsPage.tsx
defiQUG b5a2e0c0a4 feat(freshness): enhance diagnostics and update snapshot structure
- Introduced a new Diagnostics struct to capture transaction visibility state and activity state.
- Updated BuildSnapshot function to return diagnostics alongside snapshot, completeness, and sampling.
- Enhanced test cases to validate the new diagnostics data.
- Updated frontend components to utilize the new diagnostics information for improved user feedback on freshness context.

This change improves the observability of transaction activity and enhances the user experience by providing clearer insights into the freshness of data.
2026-04-12 18:22:08 -07:00

532 lines
23 KiB
TypeScript

'use client'
import { useEffect, useMemo, useState } from 'react'
import Link from 'next/link'
import { Card } from '@/libs/frontend-ui-primitives'
import { configApi, type TokenListResponse } from '@/services/api/config'
import {
aggregateLiquidityPools,
featuredLiquiditySymbols,
getLivePlannerProviders,
getRouteBackedPoolAddresses,
getTopLiquidityRoutes,
selectFeaturedLiquidityTokens,
type AggregatedLiquidityPool,
} from '@/services/api/liquidity'
import { plannerApi, type InternalExecutionPlanResponse, type PlannerCapabilitiesResponse } from '@/services/api/planner'
import { routesApi, type MissionControlLiquidityPool, type RouteMatrixResponse } from '@/services/api/routes'
import { missionControlApi, type MissionControlBridgeStatusResponse } from '@/services/api/missionControl'
import { statsApi, type ExplorerStats } from '@/services/api/stats'
import { summarizeChainActivity } from '@/utils/activityContext'
import ActivityContextPanel from '@/components/common/ActivityContextPanel'
import FreshnessTrustNote from '@/components/common/FreshnessTrustNote'
import { resolveEffectiveFreshness } from '@/utils/explorerFreshness'
import {
formatCurrency,
formatNumber,
relativeAge,
truncateMiddle,
} from './OperationsPageShell'
const tokenAggregationV1Base = '/token-aggregation/api/v1'
const tokenAggregationV2Base = '/token-aggregation/api/v2'
interface TokenPoolRecord {
symbol: string
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[]
initialStats?: ExplorerStats | null
initialBridgeStatus?: MissionControlBridgeStatusResponse | null
}
function routePairLabel(routeId: string, routeLabel: string, tokenIn?: string, tokenOut?: string): string {
return [tokenIn, tokenOut].filter(Boolean).join(' / ') || routeLabel || routeId
}
export default function LiquidityOperationsPage({
initialTokenList = null,
initialRouteMatrix = null,
initialPlannerCapabilities = null,
initialInternalPlan = null,
initialTokenPoolRecords = [],
initialStats = null,
initialBridgeStatus = null,
}: 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 [stats, setStats] = useState<ExplorerStats | null>(initialStats)
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(initialBridgeStatus)
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 &&
initialStats &&
initialBridgeStatus
) {
return () => {
cancelled = true
}
}
const load = async () => {
const [tokenListResult, routeMatrixResult, plannerCapabilitiesResult, planResult, statsResult, bridgeResult] =
await Promise.allSettled([
configApi.getTokenList(),
routesApi.getRouteMatrix(),
plannerApi.getCapabilities(),
plannerApi.getInternalExecutionPlan(),
statsApi.get(),
missionControlApi.getBridgeStatus(),
])
if (cancelled) return
if (tokenListResult.status === 'fulfilled') setTokenList(tokenListResult.value)
if (routeMatrixResult.status === 'fulfilled') setRouteMatrix(routeMatrixResult.value)
if (plannerCapabilitiesResult.status === 'fulfilled') setPlannerCapabilities(plannerCapabilitiesResult.value)
if (planResult.status === 'fulfilled') setInternalPlan(planResult.value)
if (statsResult.status === 'fulfilled') setStats(statsResult.value)
if (bridgeResult.status === 'fulfilled') setBridgeStatus(bridgeResult.value)
if (tokenListResult.status === 'fulfilled') {
const featuredTokens = selectFeaturedLiquidityTokens(tokenListResult.value.tokens || [])
const poolResults = await Promise.allSettled(
featuredTokens.map(async (token) => ({
symbol: token.symbol,
pools: (await routesApi.getTokenPools(token.address)).pools || [],
}))
)
if (!cancelled) {
setTokenPoolRecords(
poolResults
.filter((result): result is PromiseFulfilledResult<TokenPoolRecord> => result.status === 'fulfilled')
.map((result) => result.value)
)
}
}
const results = [tokenListResult, routeMatrixResult, plannerCapabilitiesResult, planResult, statsResult, bridgeResult] as const
const failedCount = results.filter((result) => result.status === 'rejected').length
if (failedCount === results.length) {
setLoadingError('Live liquidity data is temporarily unavailable from the public explorer APIs.')
}
}
load().catch((error) => {
if (!cancelled) {
setLoadingError(
error instanceof Error ? error.message : 'Live liquidity data is temporarily unavailable from the public explorer APIs.'
)
}
})
return () => {
cancelled = true
}
}, [
initialBridgeStatus,
initialInternalPlan,
initialPlannerCapabilities,
initialRouteMatrix,
initialStats,
initialTokenList,
initialTokenPoolRecords,
])
const featuredTokens = useMemo(
() => selectFeaturedLiquidityTokens(tokenList?.tokens || []),
[tokenList?.tokens]
)
const aggregatedPools = useMemo(
() => aggregateLiquidityPools(tokenPoolRecords),
[tokenPoolRecords]
)
const livePlannerProviders = useMemo(
() => getLivePlannerProviders(plannerCapabilities),
[plannerCapabilities]
)
const routeBackedPoolAddresses = useMemo(
() => getRouteBackedPoolAddresses(routeMatrix),
[routeMatrix]
)
const highlightedRoutes = useMemo(
() => getTopLiquidityRoutes(routeMatrix, 6),
[routeMatrix]
)
const dexCount = useMemo(
() => new Set(aggregatedPools.map((pool) => pool.dex).filter(Boolean)).size,
[aggregatedPools]
)
const activityContext = useMemo(
() =>
summarizeChainActivity({
blocks: [],
transactions: [],
latestBlockNumber: stats?.latest_block,
latestBlockTimestamp: null,
freshness: resolveEffectiveFreshness(stats, bridgeStatus),
diagnostics: stats?.diagnostics ?? bridgeStatus?.data?.diagnostics ?? null,
}),
[bridgeStatus, stats],
)
const insightLines = useMemo(
() => [
`${formatNumber(routeMatrix?.counts?.liveSwapRoutes)} live swap routes and ${formatNumber(routeMatrix?.counts?.liveBridgeRoutes)} bridge routes are currently published in the public route matrix.`,
`${formatNumber(aggregatedPools.length)} unique pools were discovered across ${formatNumber(featuredTokens.length)} featured Chain 138 liquidity tokens.`,
`${formatNumber(livePlannerProviders.length)} planner providers are live, and the current internal fallback decision is ${internalPlan?.plannerResponse?.decision || 'unknown'}.`,
`${formatNumber(routeBackedPoolAddresses.length)} unique pool addresses are referenced directly by the current live route legs.`,
],
[routeMatrix, aggregatedPools.length, featuredTokens.length, livePlannerProviders.length, internalPlan?.plannerResponse?.decision, routeBackedPoolAddresses.length]
)
const endpointCards: EndpointCard[] = [
{
name: 'Canonical route matrix',
method: 'GET',
href: `${tokenAggregationV1Base}/routes/matrix?includeNonLive=true`,
notes: 'All live and non-live route inventory with counts and pool-backed legs.',
},
{
name: 'Planner capabilities',
method: 'GET',
href: `${tokenAggregationV2Base}/providers/capabilities?chainId=138`,
notes: 'Live provider inventory, published pair coverage, and execution modes.',
},
{
name: 'Internal execution plan',
method: 'POST',
href: `${tokenAggregationV2Base}/routes/internal-execution-plan`,
notes: 'Returns the direct-pool fallback posture that the operator surfaces already verify.',
},
{
name: 'Mission-control token pools',
method: 'GET',
href: `/explorer-api/v1/mission-control/liquidity/token/${featuredTokens[0]?.address || '0x93E66202A11B1772E55407B32B44e5Cd8eda7f22'}/pools`,
notes: 'Cached public pool inventory for a specific Chain 138 token.',
},
]
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">
<div className="mb-3 inline-flex rounded-full border border-amber-200 bg-amber-50 px-3 py-1 text-xs font-semibold uppercase tracking-[0.2em] text-amber-700">
Chain 138 Liquidity Access
</div>
<h1 className="mb-3 text-3xl font-bold text-gray-900 dark:text-white sm:text-4xl">
Public liquidity, route discovery, and execution access points
</h1>
<p className="text-base leading-7 text-gray-600 dark:text-gray-400 sm:text-lg sm:leading-8">
This page now reads the live explorer APIs instead of hardcoded pool snapshots. It pulls the
public route matrix, planner capabilities, and mission-control token pool inventory together
so integrators can inspect what Chain 138 is actually serving right now.
</p>
</div>
{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>
</Card>
) : null}
<div className="mb-6">
<ActivityContextPanel context={activityContext} title="Liquidity Freshness Context" />
<FreshnessTrustNote
className="mt-3"
context={activityContext}
stats={stats}
bridgeStatus={bridgeStatus}
scopeLabel="Liquidity inventory and planner posture are shown alongside the same explorer freshness model used on the homepage and core explorer routes"
/>
</div>
<div className="mb-8 grid gap-4 md:grid-cols-2 xl:grid-cols-4">
<Card className="border border-emerald-200 bg-emerald-50/70 dark:border-emerald-900/50 dark:bg-emerald-950/20">
<div className="text-sm font-semibold uppercase tracking-wide text-emerald-800 dark:text-emerald-100">
Live Pools
</div>
<div className="mt-2 text-3xl font-bold text-gray-900 dark:text-white">
{formatNumber(aggregatedPools.length)}
</div>
<div className="mt-2 text-sm text-gray-700 dark:text-gray-300">
Dedupe of mission-control pool inventory across featured Chain 138 tokens.
</div>
</Card>
<Card className="border border-sky-200 bg-sky-50/70 dark:border-sky-900/50 dark:bg-sky-950/20">
<div className="text-sm font-semibold uppercase tracking-wide text-sky-800 dark:text-sky-100">
Route Coverage
</div>
<div className="mt-2 text-3xl font-bold text-gray-900 dark:text-white">
{formatNumber(routeMatrix?.counts?.filteredLiveRoutes)}
</div>
<div className="mt-2 text-sm text-gray-700 dark:text-gray-300">
{formatNumber(routeMatrix?.counts?.liveSwapRoutes)} swaps and {formatNumber(routeMatrix?.counts?.liveBridgeRoutes)} bridges.
</div>
</Card>
<Card>
<div className="text-sm text-gray-500 dark:text-gray-400">Planner providers</div>
<div className="mt-2 text-3xl font-bold text-gray-900 dark:text-white">
{formatNumber(livePlannerProviders.length)}
</div>
<div className="mt-2 text-sm text-gray-600 dark:text-gray-400">
{formatNumber(dexCount)} DEX families in the current discovered pools.
</div>
</Card>
<Card>
<div className="text-sm text-gray-500 dark:text-gray-400">Fallback posture</div>
<div className="mt-2 text-3xl font-bold text-gray-900 dark:text-white">
{internalPlan?.plannerResponse?.decision || 'unknown'}
</div>
<div className="mt-2 text-sm text-gray-600 dark:text-gray-400">
Execution contract {truncateMiddle(internalPlan?.execution?.contractAddress)}
</div>
</Card>
</div>
<div className="mb-8 grid gap-6 lg:grid-cols-[1.2fr_0.8fr]">
<Card title="Live Pool Snapshot">
<div className="space-y-4">
{aggregatedPools.slice(0, 8).map((pool) => (
<div
key={pool.address}
className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40"
>
<div className="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
<div>
<div className="text-base font-semibold text-gray-900 dark:text-white">
{(pool.token0?.symbol || '?') + ' / ' + (pool.token1?.symbol || '?')}
</div>
<div className="mt-1 break-all text-xs text-gray-500 dark:text-gray-400">
Pool: {pool.address}
</div>
</div>
<div className="text-sm font-medium text-gray-700 dark:text-gray-300">
{pool.dex || 'Unknown DEX'} · TVL {formatCurrency(pool.tvl)}
</div>
</div>
<div className="mt-2 text-sm text-gray-600 dark:text-gray-400">
Seen from {pool.sourceSymbols.join(', ')}
</div>
</div>
))}
{aggregatedPools.length === 0 ? (
<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>
<Card title="What Integrators Need To Know">
<div className="space-y-3 text-sm leading-6 text-gray-600 dark:text-gray-400">
{insightLines.map((item) => (
<p key={item}>{item}</p>
))}
<p>
Featured symbols in this view: {featuredLiquiditySymbols.join(', ')}.
</p>
</div>
</Card>
</div>
<div className="mb-8 grid gap-6 lg:grid-cols-[1fr_1fr]">
<Card title="Top Route-Backed Liquidity Paths">
<div className="space-y-4">
{highlightedRoutes.map((route) => (
<div
key={route.routeId}
className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40"
>
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<div>
<div className="text-base font-semibold text-gray-900 dark:text-white">
{routePairLabel(route.routeId, route.label || '', route.tokenInSymbol, route.tokenOutSymbol)}
</div>
<div className="mt-1 text-xs text-gray-500 dark:text-gray-400">
{formatNumber((route.legs || []).length)} legs · {formatNumber((route.legs || []).filter((leg) => leg.poolAddress).length)} pool-backed
</div>
</div>
<div className="text-sm text-gray-600 dark:text-gray-400">
{route.aggregatorFamilies?.join(', ') || 'No provider families listed'}
</div>
</div>
<div className="mt-2 text-sm text-gray-600 dark:text-gray-400">
{(route.legs || [])
.map((leg) => leg.poolAddress)
.filter(Boolean)
.map((address) => truncateMiddle(address))
.join(' · ') || 'No pool addresses published'}
</div>
</div>
))}
</div>
</Card>
<Card title="Featured Token Coverage">
<div className="space-y-4">
{featuredTokens.map((token) => {
const matchingRecord = tokenPoolRecords.find((record) => record.symbol === token.symbol)
return (
<div
key={token.address}
className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40"
>
<div className="text-base font-semibold text-gray-900 dark:text-white">
{token.symbol}
</div>
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400">
{token.name || 'Unnamed token'} · {formatNumber(matchingRecord?.pools.length || 0)} mission-control pools
</div>
<div className="mt-1 text-xs text-gray-500 dark:text-gray-400">
{truncateMiddle(token.address, 10, 8)}
</div>
</div>
)
})}
</div>
</Card>
</div>
<div className="mb-8">
<Card title="Explorer Access Points">
<div className="grid gap-4 md:grid-cols-2">
{endpointCards.map((endpoint) => (
<div
key={endpoint.href}
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>
<span className="rounded-full bg-primary-50 px-2.5 py-1 text-xs font-semibold uppercase tracking-wide text-primary-700 dark:bg-primary-900/30 dark:text-primary-300">
{endpoint.method}
</span>
</div>
<div className="mt-3 break-all rounded-xl bg-gray-50 p-3 text-xs text-gray-700 dark:bg-gray-900 dark:text-gray-300">
{endpoint.href}
</div>
<div className="mt-3 text-sm leading-6 text-gray-600 dark:text-gray-400">{endpoint.notes}</div>
<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>
</div>
<div className="mb-8 grid gap-6 lg:grid-cols-[1fr_1fr]">
<Card title="Quick Request Examples">
<div className="space-y-4">
{[
`GET ${tokenAggregationV1Base}/routes/matrix?includeNonLive=true`,
`GET ${tokenAggregationV2Base}/providers/capabilities?chainId=138`,
`POST ${tokenAggregationV2Base}/routes/internal-execution-plan`,
`GET /explorer-api/v1/mission-control/liquidity/token/${featuredTokens[0]?.address || '0x93E66202A11B1772E55407B32B44e5Cd8eda7f22'}/pools`,
].map((example) => (
<div key={example} className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40">
<code className="block break-all text-xs leading-6 text-gray-700 dark:text-gray-300">
{example}
</code>
</div>
))}
</div>
</Card>
<Card title="Related Explorer Tools">
<div className="space-y-4 text-sm leading-6 text-gray-600 dark:text-gray-400">
<p>
Use the wallet page for network onboarding, the pools page for a tighter live inventory
view, and this page for the broader route and execution surfaces.
</p>
<p>
The live route matrix was updated {relativeAge(routeMatrix?.updated)}, and the current
route-backed pool set references {formatNumber(routeBackedPoolAddresses.length)} unique
pool addresses.
</p>
<div className="flex flex-wrap gap-3">
<Link
href="/pools"
className="rounded-full bg-primary-600 px-4 py-2 text-sm font-medium text-white transition hover:bg-primary-700"
>
Open pools page
</Link>
<Link
href="/wallet"
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 wallet tools
</Link>
<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
</Link>
</div>
</div>
</Card>
</div>
</main>
)
}