"use client"; import { makeAssistantToolUI } from "@assistant-ui/react"; import { z } from "zod"; import { cn } from "@/lib/utils"; import { Users, AlertTriangle, Shield, Crown } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { ChainIcon } from "@/components/crypto/ChainIcon"; // Schema for holder const HolderSchema = z.object({ rank: z.number(), address: z.string(), label: z.string().optional(), balance: z.number(), percentage: z.number(), isContract: z.boolean().optional(), }); // Schema for holder analysis tool arguments export const HolderAnalysisArgsSchema = z.object({ tokenSymbol: z.string(), chain: z.string(), totalHolders: z.number(), top10Percentage: z.number(), top50Percentage: z.number().optional(), holders: z.array(HolderSchema), concentrationRisk: z.enum(["low", "medium", "high", "critical"]).optional(), }); export type HolderAnalysisArgs = z.infer; // Schema for holder analysis result export const HolderAnalysisResultSchema = z.object({ success: z.boolean(), message: z.string().optional(), }); export type HolderAnalysisResult = z.infer; 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"; } }; /** * HolderAnalysisToolUI - Displays holder distribution inline in chat * Used when AI responds to "who holds BULLA?" or "analyze holders" */ export const HolderAnalysisToolUI = makeAssistantToolUI({ toolName: "analyze_holders", render: ({ args, status }) => { const isLoading = status.type === "running"; const holders = args.holders || []; const risk = args.concentrationRisk || "medium"; return (
Holder Analysis - {args.tokenSymbol} {isLoading && Loading...}
{/* Summary Stats */}

Total Holders

{args.totalHolders.toLocaleString()}

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

Top 10 Hold

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

{args.top50Percentage && (

Top 50 Hold

{args.top50Percentage.toFixed(1)}%

)}

Concentration Risk

{risk}

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

Top Holders

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

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

{holder.isContract && Contract}

{holder.percentage.toFixed(2)}%

{formatBalance(holder.balance)}

))}
); }, });