Probe LINK balances on CCIP bridge contracts, expose proof-transfer metadata on bridge status, and render funded/unfunded lane health on /bridge with extended smoke coverage. Co-authored-by: Cursor <[email protected]>
108 lines
4.7 KiB
TypeScript
108 lines
4.7 KiB
TypeScript
'use client'
|
|
|
|
import Link from 'next/link'
|
|
import { Card } from '@/libs/frontend-ui-primitives'
|
|
import EntityBadge from '@/components/common/EntityBadge'
|
|
import type { MissionControlBridgeLane, MissionControlBridgeLaneHealth } from '@/services/api/missionControl'
|
|
|
|
function laneTone(status?: string | null): 'success' | 'warning' | 'info' | 'neutral' {
|
|
switch (String(status || '').toLowerCase()) {
|
|
case 'funded':
|
|
case 'proof-recorded':
|
|
return 'success'
|
|
case 'degraded':
|
|
case 'proof-pending':
|
|
return 'warning'
|
|
case 'unfunded':
|
|
return 'warning'
|
|
default:
|
|
return 'neutral'
|
|
}
|
|
}
|
|
|
|
function formatLinkBalance(wei?: string | null): string {
|
|
if (!wei) return 'Unknown'
|
|
try {
|
|
const value = BigInt(wei)
|
|
const whole = value / 10n ** 18n
|
|
const fractional = (value % 10n ** 18n).toString().padStart(18, '0').slice(0, 4).replace(/0+$/, '')
|
|
return fractional ? `${whole}.${fractional} LINK` : `${whole} LINK`
|
|
} catch {
|
|
return wei
|
|
}
|
|
}
|
|
|
|
export default function BridgeLaneHealthPanel({
|
|
laneHealth,
|
|
className = '',
|
|
}: {
|
|
laneHealth?: MissionControlBridgeLaneHealth | null
|
|
className?: string
|
|
}) {
|
|
const lanes = laneHealth?.lanes || []
|
|
if (lanes.length === 0) return null
|
|
|
|
const normalizedClassName = className ? ` ${className}` : ''
|
|
|
|
return (
|
|
<Card title="Config-ready lane health" className={`mb-8${normalizedClassName}`}>
|
|
<p className="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
|
LINK balances are read from each remote CCIP bridge contract. Proof-transfer status comes from operator-recorded CCIP message hashes when available.
|
|
</p>
|
|
<p className="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
|
Operator runbook: fund LINK with{' '}
|
|
<code className="rounded bg-gray-100 px-1.5 py-0.5 text-xs dark:bg-gray-900">fund-ccip-bridges-with-link.sh</code>
|
|
{' '}· lane probe{' '}
|
|
<code className="rounded bg-gray-100 px-1.5 py-0.5 text-xs dark:bg-gray-900">probe-bridge-lane-link-balances.sh</code>
|
|
{' '}· routing reference{' '}
|
|
<Link href="/docs/public-api-access" className="text-primary-600 hover:underline">
|
|
public API access
|
|
</Link>
|
|
</p>
|
|
<div className="overflow-x-auto">
|
|
<table className="min-w-full text-sm">
|
|
<thead>
|
|
<tr className="border-b border-gray-200 text-left text-xs uppercase tracking-wide text-gray-500 dark:border-gray-700 dark:text-gray-400">
|
|
<th className="py-2 pr-4">Lane</th>
|
|
<th className="py-2 pr-4">Overall</th>
|
|
<th className="py-2 pr-4">Proof</th>
|
|
<th className="py-2 pr-4">WETH9 bridge</th>
|
|
<th className="py-2 pr-4">WETH10 bridge</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{lanes.map((lane: MissionControlBridgeLane) => (
|
|
<tr key={lane.key} className="border-b border-gray-100 align-top last:border-0 dark:border-gray-800">
|
|
<td className="py-3 pr-4">
|
|
<div className="font-medium text-gray-900 dark:text-white">{lane.chain_name || lane.key}</div>
|
|
<div className="mt-1 text-xs text-gray-500 dark:text-gray-400">chain {lane.chain_id ?? '?'}</div>
|
|
</td>
|
|
<td className="py-3 pr-4">
|
|
<EntityBadge label={lane.status || 'unknown'} tone={laneTone(lane.status)} />
|
|
</td>
|
|
<td className="py-3 pr-4">
|
|
<EntityBadge label={lane.proof_status || 'proof-pending'} tone={laneTone(lane.proof_status)} />
|
|
</td>
|
|
<td className="py-3 pr-4">
|
|
<div className="font-mono text-xs text-gray-700 dark:text-gray-300">{lane.weth9?.bridge || '—'}</div>
|
|
<div className="mt-1 flex flex-wrap items-center gap-2">
|
|
<EntityBadge label={lane.weth9?.status || 'unknown'} tone={laneTone(lane.weth9?.status)} className="px-2 py-0.5 text-[11px]" />
|
|
<span className="text-xs text-gray-600 dark:text-gray-400">{formatLinkBalance(lane.weth9?.link_balance_wei)}</span>
|
|
</div>
|
|
</td>
|
|
<td className="py-3 pr-4">
|
|
<div className="font-mono text-xs text-gray-700 dark:text-gray-300">{lane.weth10?.bridge || '—'}</div>
|
|
<div className="mt-1 flex flex-wrap items-center gap-2">
|
|
<EntityBadge label={lane.weth10?.status || 'unknown'} tone={laneTone(lane.weth10?.status)} className="px-2 py-0.5 text-[11px]" />
|
|
<span className="text-xs text-gray-600 dark:text-gray-400">{formatLinkBalance(lane.weth10?.link_balance_wei)}</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</Card>
|
|
)
|
|
}
|