feat(explorer): show Ethereum mainnet Etherscan links on attested transactions.
Some checks failed
Deploy Explorer Live / deploy (push) Failing after 15s
Validate Explorer / frontend (push) Failing after 21s
Validate Explorer / smoke-e2e (push) Has been skipped

Render checkpoint batch attestation with per-layer mainnet tx links on the transaction detail page.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
defiQUG
2026-06-08 08:13:59 -07:00
parent 4d6c2891cd
commit 0f02e6e54f
4 changed files with 126 additions and 3 deletions

View File

@@ -0,0 +1,65 @@
import Link from 'next/link'
import { Card } from '@/libs/frontend-ui-primitives'
import type { CheckpointMainnetAttestationLink } from '@/services/api/tokenAggregation'
function shortHash(hash: string): string {
if (hash.length < 20) return hash
return `${hash.slice(0, 10)}${hash.slice(-8)}`
}
export interface MainnetAttestationPanelProps {
batchId: string
batchTotalUsd?: string
links: CheckpointMainnetAttestationLink[]
}
export default function MainnetAttestationPanel({
batchId,
batchTotalUsd,
links,
}: MainnetAttestationPanelProps) {
return (
<Card title="Ethereum mainnet attestation">
<p className="text-sm text-gray-600 dark:text-gray-400">
This Chain 138 transaction is included in checkpoint batch #{batchId}
{batchTotalUsd ? ` · USD ref ${batchTotalUsd}` : ''}.
</p>
{links.length === 0 ? (
<p className="mt-3 text-sm text-gray-500 dark:text-gray-400">
Attested on mainnet; Etherscan transaction links are indexing. Refresh in a minute or open the
checkpoint contracts on Etherscan.
</p>
) : (
<ul className="mt-4 space-y-3">
{links.map((link) => (
<li
key={`${link.layer}:${link.mainnetTxHash}`}
className="rounded-2xl border border-primary-200/60 bg-primary-50/40 p-4 dark:border-primary-500/20 dark:bg-primary-950/20"
>
<div className="flex flex-wrap items-center gap-x-3 gap-y-2">
<span className="text-sm font-medium text-gray-900 dark:text-white">
{link.label || link.layer}
</span>
<Link
href={link.mainnetExplorerUrl}
target="_blank"
rel="noopener noreferrer"
className="font-mono text-sm text-primary-600 hover:underline dark:text-primary-400"
>
{shortHash(link.mainnetTxHash)}
</Link>
</div>
{(link.meta?.role || link.meta?.uetr) && (
<div className="mt-2 flex flex-wrap gap-3 text-xs text-gray-500 dark:text-gray-400">
{link.meta.role ? <span>role: {link.meta.role}</span> : null}
{link.meta.uetr ? <span>UETR: {link.meta.uetr}</span> : null}
</div>
)}
</li>
))}
</ul>
)}
</Card>
)
}

View File

@@ -20,6 +20,7 @@ import PaginationControls from '@/components/common/PaginationControls'
import SectionTabs, { type SectionTab } from '@/components/common/SectionTabs'
import { getGruCatalogPosture } from '@/services/api/gruCatalog'
import { assessTransactionCompliance } from '@/utils/transactionCompliance'
import MainnetAttestationPanel from '@/components/checkpoint/MainnetAttestationPanel'
import {
tokenAggregationApi,
type CheckpointTxAttestationSnapshot,
@@ -504,6 +505,14 @@ export default function TransactionDetailPage() {
</div>
</Card>
{checkpointAttestation ? (
<MainnetAttestationPanel
batchId={checkpointAttestation.batchId}
batchTotalUsd={checkpointAttestation.batchTotalUsd}
links={checkpointAttestation.etherscanLinks ?? []}
/>
) : null}
<SectionTabs tabs={tabs} activeTab={activeTab} onChange={setActiveTab} className="mb-6" />
{activeTab === 'evidence' && complianceAssessment ? (

View File

@@ -37,6 +37,16 @@ export interface CheckpointTransferUsdLine {
priceSource?: string
}
export interface CheckpointMainnetAttestationLink {
layer: string
label: string
mainnetTxHash: string
mainnetExplorerUrl: string
contractAddress?: string
logIndex?: number
meta?: Record<string, string>
}
export interface CheckpointTxAttestationSnapshot {
txHash: string
included: boolean
@@ -50,6 +60,11 @@ export interface CheckpointTxAttestationSnapshot {
priceSource?: string
transfers?: CheckpointTransferUsdLine[]
} | null
etherscanLinks?: CheckpointMainnetAttestationLink[]
etherscan?: {
base?: string
txUrlTemplate?: string
}
source?: string
}