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.
This commit is contained in:
@@ -15,6 +15,12 @@ import {
|
||||
} 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,
|
||||
@@ -43,6 +49,8 @@ interface LiquidityOperationsPageProps {
|
||||
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 {
|
||||
@@ -55,12 +63,16 @@ export default function LiquidityOperationsPage({
|
||||
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)
|
||||
|
||||
@@ -72,7 +84,9 @@ export default function LiquidityOperationsPage({
|
||||
initialRouteMatrix &&
|
||||
initialPlannerCapabilities &&
|
||||
initialInternalPlan &&
|
||||
initialTokenPoolRecords.length > 0
|
||||
initialTokenPoolRecords.length > 0 &&
|
||||
initialStats &&
|
||||
initialBridgeStatus
|
||||
) {
|
||||
return () => {
|
||||
cancelled = true
|
||||
@@ -80,12 +94,14 @@ export default function LiquidityOperationsPage({
|
||||
}
|
||||
|
||||
const load = async () => {
|
||||
const [tokenListResult, routeMatrixResult, plannerCapabilitiesResult, planResult] =
|
||||
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
|
||||
@@ -94,6 +110,8 @@ export default function LiquidityOperationsPage({
|
||||
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 || [])
|
||||
@@ -113,14 +131,10 @@ export default function LiquidityOperationsPage({
|
||||
}
|
||||
}
|
||||
|
||||
const failedCount = [
|
||||
tokenListResult,
|
||||
routeMatrixResult,
|
||||
plannerCapabilitiesResult,
|
||||
planResult,
|
||||
].filter((result) => result.status === 'rejected').length
|
||||
const results = [tokenListResult, routeMatrixResult, plannerCapabilitiesResult, planResult, statsResult, bridgeResult] as const
|
||||
const failedCount = results.filter((result) => result.status === 'rejected').length
|
||||
|
||||
if (failedCount === 4) {
|
||||
if (failedCount === results.length) {
|
||||
setLoadingError('Live liquidity data is temporarily unavailable from the public explorer APIs.')
|
||||
}
|
||||
}
|
||||
@@ -137,9 +151,11 @@ export default function LiquidityOperationsPage({
|
||||
cancelled = true
|
||||
}
|
||||
}, [
|
||||
initialBridgeStatus,
|
||||
initialInternalPlan,
|
||||
initialPlannerCapabilities,
|
||||
initialRouteMatrix,
|
||||
initialStats,
|
||||
initialTokenList,
|
||||
initialTokenPoolRecords,
|
||||
])
|
||||
@@ -168,6 +184,18 @@ export default function LiquidityOperationsPage({
|
||||
() => 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(
|
||||
() => [
|
||||
@@ -240,6 +268,17 @@ export default function LiquidityOperationsPage({
|
||||
</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">
|
||||
|
||||
Reference in New Issue
Block a user