Skip to content
This repository was archived by the owner on Jul 15, 2026. It is now read-only.

Commit c9dac4e

Browse files
committed
feat: add route-specific explorer metadata
1 parent e937f82 commit c9dac4e

6 files changed

Lines changed: 146 additions & 2 deletions

File tree

src/app/block/[hash]/layout.tsx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import type { Metadata } from "next";
2+
import { createPublicClient, type Hash, http } from "viem";
3+
import { mainnet } from "viem/chains";
4+
5+
interface BlockLayoutProps {
6+
children: React.ReactNode;
7+
params: Promise<{ hash: string }>;
8+
}
9+
10+
const RPC_URL = process.env.TIPS_UI_RPC_URL || "http://localhost:8545";
11+
12+
const client = createPublicClient({
13+
chain: mainnet,
14+
transport: http(RPC_URL),
15+
});
16+
17+
function blockLabel(value: string): string {
18+
if (/^0x[0-9a-f]+$/i.test(value) && value.length < 66) {
19+
try {
20+
return `#${BigInt(value).toLocaleString()}`;
21+
} catch {
22+
return value;
23+
}
24+
}
25+
26+
if (/^[0-9]+$/.test(value)) {
27+
return `#${Number(value).toLocaleString()}`;
28+
}
29+
30+
return `${value.slice(0, 10)}...${value.slice(-8)}`;
31+
}
32+
33+
async function blockNumberLabel(value: string): Promise<string> {
34+
try {
35+
const block = /^0x[0-9a-f]{64}$/i.test(value)
36+
? await client.getBlock({ blockHash: value as Hash })
37+
: await client.getBlock({ blockNumber: BigInt(value) });
38+
return `#${block.number.toLocaleString()}`;
39+
} catch {
40+
return blockLabel(value);
41+
}
42+
}
43+
44+
export async function generateMetadata({
45+
params,
46+
}: BlockLayoutProps): Promise<Metadata> {
47+
const { hash } = await params;
48+
const label = await blockNumberLabel(hash);
49+
50+
return {
51+
title: `Block ${label}`,
52+
description: `Inspect transactions, execution data, and event history for Base block ${label}.`,
53+
};
54+
}
55+
56+
export default function BlockLayout({ children }: BlockLayoutProps) {
57+
return children;
58+
}

src/app/blocks/layout.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { Metadata } from "next";
2+
3+
export const metadata: Metadata = {
4+
title: "Blocks",
5+
description: "Browse recent Base blocks and their execution data.",
6+
};
7+
8+
export default function BlocksLayout({
9+
children,
10+
}: {
11+
children: React.ReactNode;
12+
}) {
13+
return children;
14+
}

src/app/bundles/[hash]/layout.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { Metadata } from "next";
2+
3+
interface BundleLayoutProps {
4+
children: React.ReactNode;
5+
params: Promise<{ hash: string }>;
6+
}
7+
8+
function shortHash(hash: string): string {
9+
return `${hash.slice(0, 10)}...${hash.slice(-8)}`;
10+
}
11+
12+
export async function generateMetadata({
13+
params,
14+
}: BundleLayoutProps): Promise<Metadata> {
15+
const { hash } = await params;
16+
const label = shortHash(hash);
17+
18+
return {
19+
title: `Bundle ${label}`,
20+
description: `Inspect bundle history and simulation data for Base bundle ${hash}.`,
21+
};
22+
}
23+
24+
export default function BundleLayout({ children }: BundleLayoutProps) {
25+
return children;
26+
}

src/app/layout.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ const geistMono = Geist_Mono({
1313
});
1414

1515
export const metadata: Metadata = {
16-
title: "TIPS",
17-
description: "A beautiful UI for interacting with TIPS",
16+
title: {
17+
default: "TIPS | Base transaction observability",
18+
template: "%s | TIPS",
19+
},
20+
description:
21+
"Explore Base transaction events, blocks, bundles, and on-chain inclusion.",
1822
};
1923

2024
export default function RootLayout({

src/app/txn/[hash]/layout.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { Metadata } from "next";
2+
3+
interface TransactionLayoutProps {
4+
children: React.ReactNode;
5+
params: Promise<{ hash: string }>;
6+
}
7+
8+
function shortHash(hash: string): string {
9+
return `${hash.slice(0, 10)}...${hash.slice(-8)}`;
10+
}
11+
12+
export async function generateMetadata({
13+
params,
14+
}: TransactionLayoutProps): Promise<Metadata> {
15+
const { hash } = await params;
16+
const label = shortHash(hash);
17+
18+
return {
19+
title: `Transaction ${label}`,
20+
description: `Inspect audit events and on-chain status for Base transaction ${hash}.`,
21+
};
22+
}
23+
24+
export default function TransactionLayout({
25+
children,
26+
}: TransactionLayoutProps) {
27+
return children;
28+
}

src/app/txs/layout.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { Metadata } from "next";
2+
3+
export const metadata: Metadata = {
4+
title: "Transactions",
5+
description: "Browse confirmed Base transactions from newest to oldest.",
6+
};
7+
8+
export default function TransactionsLayout({
9+
children,
10+
}: {
11+
children: React.ReactNode;
12+
}) {
13+
return children;
14+
}

0 commit comments

Comments
 (0)