Skip to content

Commit 94575f6

Browse files
TokenBriceclaude
andcommitted
feat(frontend): add curated reserves for 94 stablecoins + coin notice system
Research and populate reserve composition data for all remaining 94 tracked stablecoins (33 high, 46 medium, 15 low confidence). Total curated reserves now cover 129 of 143 tracked coins. Add CoinNotice component for important alerts on detail pages, rendered between AI summary and report card. Three notices added: - AEUR: winding down (FlowBank bankruptcy) - sUSD: prolonged depeg (~$0.83-0.85) - USDU Finance: undercollateralized by design (~43% CR) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ec178a2 commit 94575f6

File tree

5 files changed

+2562
-1
lines changed

5 files changed

+2562
-1
lines changed

src/app/stablecoin/[id]/client.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { KeyInfoCard } from "@/components/key-info-card";
2222
import { ContractAddresses } from "@/components/contract-addresses";
2323
import { AiSummary } from "@/components/ai-summary";
2424
import { ReportCardDetail } from "@/components/report-card";
25+
import { CoinNotices } from "@/components/coin-notice";
2526
import { DetailSectionNav } from "@/components/detail-section-nav";
2627
import { PegGauge } from "@/components/peg-gauge";
2728
import { ReserveTreemap } from "@/components/reserve-treemap";
@@ -376,6 +377,10 @@ export default function StablecoinDetailClient({ id, summary, coin, logoSrc }: S
376377
{summary && <AiSummary {...summary} />}
377378
</section>
378379

380+
{coin.notices && coin.notices.length > 0 && (
381+
<CoinNotices notices={coin.notices} />
382+
)}
383+
379384
<section id="report-card">
380385
{reportCard && <ReportCardDetail card={reportCard} />}
381386
</section>

src/components/coin-notice.tsx

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import React from "react";
2+
import type { CoinNotice as CoinNoticeType } from "@/lib/types";
3+
4+
const STYLES: Record<CoinNoticeType["type"], { border: string; bg: string; icon: string; title: string }> = {
5+
danger: {
6+
border: "border-red-500/40",
7+
bg: "bg-red-500/5",
8+
icon: "text-red-500",
9+
title: "text-red-600 dark:text-red-400",
10+
},
11+
warning: {
12+
border: "border-amber-500/40",
13+
bg: "bg-amber-500/5",
14+
icon: "text-amber-500",
15+
title: "text-amber-600 dark:text-amber-400",
16+
},
17+
info: {
18+
border: "border-blue-500/40",
19+
bg: "bg-blue-500/5",
20+
icon: "text-blue-500",
21+
title: "text-blue-600 dark:text-blue-400",
22+
},
23+
};
24+
25+
const ICONS: Record<CoinNoticeType["type"], React.ReactNode> = {
26+
danger: (
27+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="size-5 shrink-0">
28+
<path fillRule="evenodd" d="M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0Zm-8-5a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-1.5 0v-4.5A.75.75 0 0 1 10 5Zm0 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" clipRule="evenodd" />
29+
</svg>
30+
),
31+
warning: (
32+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="size-5 shrink-0">
33+
<path fillRule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495ZM10 6a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 10 6Zm0 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" clipRule="evenodd" />
34+
</svg>
35+
),
36+
info: (
37+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="size-5 shrink-0">
38+
<path fillRule="evenodd" d="M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0Zm-7-4a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM9 9a.75.75 0 0 0 0 1.5h.253a.25.25 0 0 1 .244.304l-.459 2.066A1.75 1.75 0 0 0 10.747 15H11a.75.75 0 0 0 0-1.5h-.253a.25.25 0 0 1-.244-.304l.459-2.066A1.75 1.75 0 0 0 9.253 9H9Z" clipRule="evenodd" />
39+
</svg>
40+
),
41+
};
42+
43+
interface CoinNoticesProps {
44+
notices: CoinNoticeType[];
45+
}
46+
47+
export function CoinNotices({ notices }: CoinNoticesProps) {
48+
if (notices.length === 0) return null;
49+
50+
return (
51+
<div className="space-y-3">
52+
{notices.map((notice, i) => {
53+
const s = STYLES[notice.type];
54+
return (
55+
<div
56+
key={i}
57+
className={`flex items-start gap-3 rounded-lg border-l-4 ${s.border} ${s.bg} px-4 py-3`}
58+
>
59+
<span className={s.icon}>{ICONS[notice.type]}</span>
60+
<div className="min-w-0">
61+
<p className={`text-sm font-semibold ${s.title}`}>{notice.title}</p>
62+
<p className="mt-0.5 text-sm text-muted-foreground">{notice.message}</p>
63+
</div>
64+
</div>
65+
);
66+
})}
67+
</div>
68+
);
69+
}

0 commit comments

Comments
 (0)