import { cn } from "~/lib/utils"; import { Users, AlertTriangle, Crown } from "lucide-react"; import { ChainIcon } from "../components/shared/ChainIcon"; export interface Holder { rank: number; address: string; label?: string; balance: number; percentage: number; isContract?: boolean; } export interface HolderAnalysisData { tokenSymbol: string; chain: string; totalHolders: number; top10Percentage: number; top50Percentage?: number; holders: Holder[]; concentrationRisk?: "low" | "medium" | "high" | "critical"; } export interface HolderAnalysisWidgetProps { /** Holder analysis data */ data: HolderAnalysisData; /** Callback when holder is clicked */ onHolderClick?: (holder: Holder) => void; /** Additional class names */ className?: string; } const shortenAddress = (address: string): string => { return `${address.slice(0, 6)}...${address.slice(-4)}`; }; const formatBalance = (balance: number): string => { if (balance >= 1e9) return `${(balance / 1e9).toFixed(2)}B`; if (balance >= 1e6) return `${(balance / 1e6).toFixed(2)}M`; if (balance >= 1e3) return `${(balance / 1e3).toFixed(2)}K`; return balance.toFixed(2); }; const getRiskColor = (risk: string) => { switch (risk) { case "low": return "text-green-500 bg-green-500/10"; case "medium": return "text-yellow-500 bg-yellow-500/10"; case "high": return "text-orange-500 bg-orange-500/10"; case "critical": return "text-red-500 bg-red-500/10"; default: return "text-muted-foreground bg-muted"; } }; /** * HolderAnalysisWidget - Displays holder distribution inline in chat * Used when AI responds to "who holds BULLA?" or "analyze holders" */ export function HolderAnalysisWidget({ data, onHolderClick, className, }: HolderAnalysisWidgetProps) { const risk = data.concentrationRisk || "medium"; return (
{/* Header */}
Holder Analysis - {data.tokenSymbol}
{/* Summary Stats */}

Total Holders

{data.totalHolders.toLocaleString()}

50 ? "bg-red-500/10" : "bg-muted/50")}>

Top 10 Hold

50 && "text-red-500")}> {data.top10Percentage.toFixed(1)}%

{data.top50Percentage && (

Top 50 Hold

{data.top50Percentage.toFixed(1)}%

)}

Concentration Risk

{risk}

{/* Risk Warning */} {(risk === "high" || risk === "critical") && (
High holder concentration. Top wallets could impact price.
)} {/* Top Holders List */}

Top Holders

{data.holders.slice(0, 10).map((holder) => (
onHolderClick?.(holder)} >
#{holder.rank} {holder.rank <= 3 && ( )}

{holder.label || shortenAddress(holder.address)}

{holder.isContract && ( Contract )}

{holder.percentage.toFixed(2)}%

{formatBalance(holder.balance)}

))}
); }