diff --git a/src/app/profile/[address]/page.tsx b/src/app/profile/[address]/page.tsx index aefcc593..fff5452d 100644 --- a/src/app/profile/[address]/page.tsx +++ b/src/app/profile/[address]/page.tsx @@ -192,6 +192,12 @@ export default function ProfilePage() { ); } +function formatCompact(n: number): string { + if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`; + if (n >= 1_000) return `${(n / 1_000).toFixed(0)}K`; + return n.toLocaleString(); +} + // --------------------------------------------------------------------------- // Profile Header — Social Credibility Trust Dashboard // --------------------------------------------------------------------------- @@ -1231,6 +1237,7 @@ interface PortfolioHolding { value: bigint; entryPrice: number | null; lastTraded: string | null; + firstTraded: string | null; priceChange: number | null; reserveDecimals: number; } @@ -1294,6 +1301,7 @@ function PortfolioTab({ address, isOwnProfile }: { address: string; isOwnProfile // Derive entry price from first mint in trade_history let entryPrice: number | null = null; let lastTraded: string | null = null; + let firstTraded: string | null = null; if (supabase) { const { data: firstMint } = await supabase .from("trade_history") @@ -1307,6 +1315,18 @@ function PortfolioTab({ address, isOwnProfile }: { address: string; isOwnProfile if (firstMint && firstMint.length > 0) { entryPrice = firstMint[0].price_per_token; } + // First trade of any type (mint or transfer-in) + const { data: firstTrade } = await supabase + .from("trade_history") + .select("block_timestamp") + .eq("user_address", address) + .eq("storyline_id", sl.storyline_id) + .eq("contract_address", MCV2_BOND.toLowerCase()) + .order("block_timestamp", { ascending: true }) + .limit(1); + if (firstTrade && firstTrade.length > 0) { + firstTraded = firstTrade[0].block_timestamp; + } const { data: lastTrade } = await supabase .from("trade_history") .select("block_timestamp") @@ -1322,7 +1342,7 @@ function PortfolioTab({ address, isOwnProfile }: { address: string; isOwnProfile return { storyline: sl, balance, price: priceBI, value, - entryPrice, lastTraded, + entryPrice, lastTraded, firstTraded, priceChange: priceChangeResult?.changePercent ?? null, reserveDecimals, }; @@ -1495,32 +1515,52 @@ function PortfolioTab({ address, isOwnProfile }: { address: string; isOwnProfile -
-
- {formatPrice(formatUnits(h.value, h.reserveDecimals))} {RESERVE_LABEL} - {plotUsd && ({formatUsdValue(Number(formatUnits(h.value, h.reserveDecimals)) * plotUsd)})} - {h.priceChange !== null && ( - = 0 ? "text-accent" : "text-error"}`}> - {h.priceChange >= 0 ? "+" : ""}{h.priceChange.toFixed(1)}% - - )} -
-
Balance: {formatPrice(formatUnits(h.balance, 18))} tokens
-
- Price:{" "} - {formatPrice(formatUnits(h.price, 18))} {RESERVE_LABEL} - {plotUsd != null && ({formatUsdValue(Number(formatUnits(h.price, 18)) * plotUsd)})} -
- {h.entryPrice !== null && h.entryPrice > 0 && ( -
- Entry:{" "} - {formatPrice(h.entryPrice)} {RESERVE_LABEL} - {plotUsd != null && ({formatUsdValue(h.entryPrice * plotUsd)})} +
+
+ {/* Value */} +
+
+ {plotUsd ? formatUsdValue(Number(formatUnits(h.value, h.reserveDecimals)) * plotUsd) : "—"} + {h.priceChange !== null && ( + = 0 ? "text-accent" : "text-error"}`}> + {h.priceChange >= 0 ? "+" : ""}{h.priceChange.toFixed(1)}% + + )} +
+
Value
- )} - {h.lastTraded && ( -
Traded: {new Date(h.lastTraded).toLocaleDateString("en-US", { month: "short", day: "numeric" })}
- )} + {/* Balance */} +
+
{formatCompact(Number(formatUnits(h.balance, 18)))}
+
Balance
+
+ {/* PnL */} +
+ {h.entryPrice !== null && h.entryPrice > 0 && plotUsd != null ? (() => { + const currentPrice = Number(formatUnits(h.price, 18)); + const balanceNum = Number(formatUnits(h.balance, 18)); + const pnlUsd = (currentPrice - h.entryPrice) * balanceNum * plotUsd; + const pnlPct = ((currentPrice - h.entryPrice) / h.entryPrice) * 100; + const isPositive = pnlUsd >= 0; + return ( +
+ {isPositive ? "+" : "-"}{formatUsdValue(Math.abs(pnlUsd))} + {isPositive ? "+" : ""}{pnlPct.toFixed(1)}% +
+ ); + })() : ( +
+ )} +
PnL
+
+ {/* First Traded */} +
+
+ {h.firstTraded ? new Date(h.firstTraded).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }) : "—"} +
+
First Traded
+
+