diff --git a/src/app/trading-apps/page.tsx b/src/app/trading-apps/page.tsx index 050557da..ecc9c02d 100644 --- a/src/app/trading-apps/page.tsx +++ b/src/app/trading-apps/page.tsx @@ -1,6 +1,7 @@ import Link from "next/link"; import { ProviderLogo } from "@/components/provider-logo"; import { readSnapshot } from "@/lib/snapshot"; +import type { SnapshotPayload } from "@/lib/snapshot"; import { pageMetadata } from "@/lib/page-metadata"; import { safeJsonLd, buildBreadcrumbJsonLd } from "@/lib/jsonld"; import { SITE } from "@/data/site"; @@ -27,140 +28,175 @@ const BENCH_SLUGS = [ "app-store-ratings", ] as const; -const PRODUCTS = [ - { - slug: "pump-fun", - name: "pump.fun", - description: "Leading Solana memecoin launchpad and AMM", - }, - { - slug: "fomo", - name: "FOMO", - description: "Multi-chain memecoin trading with social copy-trading", - }, - { - slug: "gmgn", - name: "GMGN", - description: "Memecoin terminal with smart money tracking and Telegram bot", - }, - { - slug: "axiom", - name: "Axiom", - description: "Telegram trading bot for Solana memecoins", - }, +const PLATFORMS = [ + { slug: "pump-fun", name: "pump.fun" }, + { slug: "gmgn", name: "GMGN" }, + { slug: "axiom", name: "Axiom" }, + { slug: "fomo", name: "FOMO" }, + { slug: "trojan", name: "Trojan" }, + { slug: "photon", name: "Photon" }, + { slug: "maestro", name: "Maestro" }, + { slug: "banana-gun", name: "Banana Gun" }, + { slug: "bullx", name: "BullX" }, +] as const; + +const COLUMNS = [ { - slug: "trojan", - name: "Trojan", - description: "Fast Solana Telegram bot with sniping and MEV protection", + key: "volume" as const, + label: "24h Volume", + bench: "solana-trading-platform-wars", + fmt: fmtUSD, + tip: "Attributed 24h trading volume", + higherBetter: true, }, { - slug: "photon", - name: "Photon", - description: "Advanced Solana trading terminal with wallet tracking", + key: "traders" as const, + label: "Unique Traders", + bench: "solana-unique-traders", + fmt: fmtCount, + tip: "Unique swap transactions in 24h", + higherBetter: true, }, { - slug: "maestro", - name: "Maestro", - description: "Multi-chain Telegram trading bot with limit orders", + key: "tradeSize" as const, + label: "Avg Trade", + bench: "solana-avg-trade-size", + fmt: fmtUSD, + tip: "Average swap size in USD", + higherBetter: false, }, { - slug: "banana-gun", - name: "Banana Gun", - description: "Multi-chain Telegram bot for memecoins and EVM tokens", + key: "feeRate" as const, + label: "Fee Rate", + bench: "memecoin-platforms", + fmt: fmtPct, + tip: "Protocol fee revenue ÷ volume (take rate)", + higherBetter: false, }, { - slug: "bullx", - name: "BullX", - description: "Multi-chain terminal for memecoins on Solana and EVM", + key: "rating" as const, + label: "App Rating", + bench: "app-store-ratings", + fmt: fmtRating, + tip: "Apple App Store rating (out of 5)", + higherBetter: true, }, ] as const; +type ColKey = (typeof COLUMNS)[number]["key"]; + +function indexBySlug(snap: SnapshotPayload | null): Record { + const out: Record = {}; + for (const r of snap?.results ?? []) { + out[r.slug] = r.ms.mean; + } + return out; +} + +function fmtUSD(v: number | null): string { + if (v === null) return "—"; + if (v >= 1_000_000_000) return `$${(v / 1_000_000_000).toFixed(1)}B`; + if (v >= 1_000_000) return `$${(v / 1_000_000).toFixed(0)}M`; + if (v >= 1_000) return `$${(v / 1_000).toFixed(0)}K`; + return `$${v.toFixed(0)}`; +} + +function fmtCount(v: number | null): string { + if (v === null) return "—"; + if (v >= 1_000_000) return `${(v / 1_000_000).toFixed(1)}M`; + if (v >= 1_000) return `${(v / 1_000).toFixed(0)}K`; + return v.toFixed(0); +} + +function fmtPct(v: number | null): string { + if (v === null) return "—"; + return `${v.toFixed(2)}%`; +} + +function fmtRating(v: number | null): string { + if (v === null) return "—"; + return `${v.toFixed(1)} / 5`; +} + const GROUPS = [ { label: "Volume & activity", items: [ - { - slug: "solana-trading-platform-wars", - title: "Trading platform volume", - description: - "24h volume for pump.fun, GMGN, Axiom, FOMO and Telegram bots", - }, - { - slug: "solana-dex-volume", - title: "DEX volume & protocol revenue", - description: "24h trading volume and protocol fees from DeFiLlama", - }, - { - slug: "solana-unique-traders", - title: "Unique traders", - description: "Unique swap transactions per platform in the last 24h", - }, - { - slug: "solana-avg-trade-size", - title: "Average trade size", - description: "Average swap size in USD per platform", - }, + { slug: "solana-trading-platform-wars", title: "Trading platform volume" }, + { slug: "solana-dex-volume", title: "DEX volume & protocol revenue" }, + { slug: "solana-unique-traders", title: "Unique traders" }, + { slug: "solana-avg-trade-size", title: "Average trade size" }, ], }, { label: "Launchpads", - items: [ - { - slug: "solana-launchpad-wars", - title: "Launchpad volume", - description: - "24h volume for pump.fun, Flap, Bankr and other bonding-curve programs", - }, - ], + items: [{ slug: "solana-launchpad-wars", title: "Launchpad volume" }], }, { label: "Fees & execution", items: [ - { - slug: "memecoin-platforms", - title: "Platform fee rates", - description: - "Protocol fee revenue divided by volume — who earns most per dollar traded", - }, - { - slug: "trading-app-execution", - title: "Execution quality", - description: - "Priority fees, Jito bundle rates, CU price and platform fee per transaction", - }, + { slug: "memecoin-platforms", title: "Platform fee rates" }, + { slug: "trading-app-execution", title: "Execution quality" }, ], }, { label: "App store", - items: [ - { - slug: "app-store-ratings", - title: "iOS app store ratings", - description: - "Live Apple App Store ratings for Coinbase, Robinhood, Crypto.com and more", - }, - ], + items: [{ slug: "app-store-ratings", title: "iOS app store ratings" }], }, ] as const; -function fmtUSD(v: number): string { - if (v >= 1_000_000_000) return `$${(v / 1_000_000_000).toFixed(1)}B`; - if (v >= 1_000_000) return `$${(v / 1_000_000).toFixed(0)}M`; - if (v >= 1_000) return `$${(v / 1_000).toFixed(0)}K`; - return `$${v.toFixed(0)}`; -} - export default async function TradingAppsHubPage() { - const [volumeSnap, ratingsSnap] = await Promise.all([ - readSnapshot("solana-trading-platform-wars"), - readSnapshot("app-store-ratings"), - ]); + const [volumeSnap, tradersSnap, tradeSizeSnap, feeSnap, ratingsSnap] = + await Promise.all([ + readSnapshot("solana-trading-platform-wars"), + readSnapshot("solana-unique-traders"), + readSnapshot("solana-avg-trade-size"), + readSnapshot("memecoin-platforms"), + readSnapshot("app-store-ratings"), + ]); - const topVolumePlatform = volumeSnap?.results[0]; - const topRatedApp = ratingsSnap?.results[0]; - const platformsTracked = new Set( - PRODUCTS.map((p) => p.slug) - ).size; + const volIdx = indexBySlug(volumeSnap); + const tradersIdx = indexBySlug(tradersSnap); + const tradeSizeIdx = indexBySlug(tradeSizeSnap); + const feeIdx = indexBySlug(feeSnap); + const ratingIdx = indexBySlug(ratingsSnap); + + type Row = { + slug: string; + name: string; + volume: number | null; + traders: number | null; + tradeSize: number | null; + feeRate: number | null; + rating: number | null; + }; + + const matrix: Row[] = PLATFORMS.map((p) => ({ + slug: p.slug, + name: p.name, + volume: volIdx[p.slug] ?? null, + traders: tradersIdx[p.slug] ?? null, + tradeSize: tradeSizeIdx[p.slug] ?? null, + feeRate: feeIdx[p.slug] ?? null, + rating: ratingIdx[p.slug] ?? null, + })).sort((a, b) => (b.volume ?? -1) - (a.volume ?? -1)); + + // Find the best value per column (for highlighting) + function best(key: ColKey, higherBetter: boolean): number | null { + const vals = matrix.map((r) => r[key]).filter((v): v is number => v !== null); + if (!vals.length) return null; + return higherBetter ? Math.max(...vals) : Math.min(...vals); + } + + const bests: Partial> = {}; + for (const col of COLUMNS) { + bests[col.key] = best(col.key, col.higherBetter); + } + + const topVolume = volumeSnap?.results[0]; + const topRating = ratingsSnap?.results.find((r) => + PLATFORMS.some((p) => p.slug === r.slug) + ); const breadcrumbLd = { "@context": "https://schema.org", @@ -203,7 +239,8 @@ export default async function TradingAppsHubPage() { dangerouslySetInnerHTML={{ __html: safeJsonLd(itemListLd) }} /> -
+ {/* Header */} +

- Eight independent benchmarks across four categories: 24h platform - volume, launchpad activity, execution fees, and app store ratings. - Every number is measured live from the same harness on the same - schedule. No marketing claims, just on-chain data. + {PLATFORMS.length} platforms measured across {BENCH_SLUGS.length}{" "} + independent benchmarks: volume, unique traders, average trade size, fee + rates, and app store ratings. Live data, no marketing claims.

- -
- {BENCH_SLUGS.slice(0, 5).map((slug) => ( - - - Bench - - {slug} - - ))} - {BENCH_SLUGS.length > 5 && ( - - +{BENCH_SLUGS.length - 5} more - - )} -
{/* KPI strip */} @@ -249,61 +262,109 @@ export default async function TradingAppsHubPage() { - - + + - {/* Product grid */} -
+ {/* Comparison table */} +

- Platforms covered + Platform comparison

-
- {PRODUCTS.map((product) => ( - -
- - - {product.name} - -
-

- {product.description} -

- - ))} +
+ + + + + {COLUMNS.map((col) => ( + + ))} + + + + {matrix.map((row, i) => ( + + + {COLUMNS.map((col) => { + const val = row[col.key]; + const isBest = val !== null && val === bests[col.key]; + return ( + + ); + })} + + ))} + +
+ Platform + + + {col.label} + + +
+ + + + {row.name} + + + + {col.fmt(val)} +
+

+ Best value per column highlighted in green. Sorted by 24h volume. + Data refreshes every 60 s. +

{/* Benchmarks grouped list */} @@ -312,9 +373,9 @@ export default async function TradingAppsHubPage() { className="label-mono text-[10px] text-ink-faint mb-4 uppercase tracking-wide" style={{ fontFamily: "var(--font-mono, monospace)" }} > - Benchmarks + All benchmarks

-
+
{GROUPS.map((group) => (

@@ -326,20 +387,15 @@ export default async function TradingAppsHubPage() { {i > 0 &&

} -
- - {item.title} - - - {item.description} - -
+ + {item.title} +

- Volume figures are pulled from on-chain program activity and - DeFiLlama aggregates. Platform fee rates compare fee revenue to - reported volume over the same 24h window. Execution quality probes - submit a representative transaction per platform and records the - priority fee, Jito bundle cost, and compute unit price. App store - ratings are fetched directly from the Apple App Store API. All - harnesses are open source on{" "} + Volume and unique-trader figures are pulled from on-chain program + activity via Mobula Lighthouse and Dune Analytics. Fee rates compare + fee-wallet inflows to reported volume over the same 24h window. + Execution quality monitors Jito bundle rates, priority fees, and + compute unit price passively from fee accounts. App store ratings are + fetched from the Apple App Store API. All harnesses are open source on{" "} +