Operations pages get collapsible surface navigation on small screens and a shared action-card accordion; the footer surfaces read-only JSON endpoints with e2e coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
570 lines
25 KiB
TypeScript
570 lines
25 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useMemo, useState } from 'react'
|
|
import Link from 'next/link'
|
|
import { Card } from '@/libs/frontend-ui-primitives'
|
|
import { type TokenListResponse } from '@/services/api/config'
|
|
import { tokensApi } from '@/services/api/tokens'
|
|
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 MarketEvidenceNote from '@/components/common/MarketEvidenceNote'
|
|
import SubsystemPosturePanel from '@/components/common/SubsystemPosturePanel'
|
|
import TokenListSurfaceNote from '@/components/common/TokenListSurfaceNote'
|
|
import OperationsSurfaceNav from './OperationsSurfaceNav'
|
|
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([
|
|
tokensApi.listForSurface('extended', 138).then(({ ok, data }) => ({ tokens: ok ? data : [] })),
|
|
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 liquidityInventoryUpdatedAt =
|
|
stats?.sampling?.stats_generated_at ||
|
|
stats?.freshness?.chain_head?.timestamp ||
|
|
routeMatrix?.generatedAt ||
|
|
routeMatrix?.updated
|
|
|
|
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.',
|
|
},
|
|
{
|
|
name: 'External indexer readiness',
|
|
method: 'GET',
|
|
href: `/api/v1/report/external-indexer-readiness?chainId=138`,
|
|
notes: 'One JSON posture for DefiLlama, CoinGecko, CoinMarketCap, and Dexscreener readiness.',
|
|
},
|
|
]
|
|
|
|
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>
|
|
<TokenListSurfaceNote className="mt-3 text-sm text-gray-600 dark:text-gray-400" />
|
|
</div>
|
|
|
|
<OperationsSurfaceNav />
|
|
|
|
{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"
|
|
/>
|
|
<SubsystemPosturePanel
|
|
className="mt-3"
|
|
subsystems={bridgeStatus?.data?.subsystems}
|
|
title="Liquidity Subsystem Posture"
|
|
preferredKeys={['rpc_head', 'tx_index', 'stats_summary', 'freshness_queries']}
|
|
scopeLabel="Use these subsystem signals to tell whether sparse liquidity visibility is consistent with quiet-chain conditions, partial transaction indexing, or stale public summary data."
|
|
/>
|
|
</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>
|
|
<MarketEvidenceNote
|
|
source="mission-control"
|
|
lastUpdated={liquidityInventoryUpdatedAt}
|
|
method="Route matrix, provider capabilities, and mission-control pool inventory are reconciled for visible public liquidity only."
|
|
compact
|
|
/>
|
|
</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>
|
|
<MarketEvidenceNote
|
|
source="mission-control"
|
|
lastUpdated={liquidityInventoryUpdatedAt}
|
|
method="Pool TVL is the visible mission-control value for discovered route-backed liquidity."
|
|
compact
|
|
/>
|
|
</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>
|
|
)
|
|
}
|