From 5408e6bc55452fb4f1e86b667c849cbf44e5837e Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Wed, 19 Aug 2026 11:32:29 +0200 Subject: [PATCH 1/5] fix: trading-apps data accuracy (p50, Swap Tx label, Fomo+Flap volume, Bloom, remove broken exec link) --- src/app/trading-apps/page.tsx | 85 ++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/src/app/trading-apps/page.tsx b/src/app/trading-apps/page.tsx index f71ab7ce..80d240d9 100644 --- a/src/app/trading-apps/page.tsx +++ b/src/app/trading-apps/page.tsx @@ -7,7 +7,7 @@ import { safeJsonLd, buildBreadcrumbJsonLd } from "@/lib/jsonld"; import { SITE } from "@/data/site"; const DESCRIPTION = - "Live benchmarks for Solana trading platforms and Telegram bots — volume, fees, execution quality, unique traders, and app store ratings."; + "Live benchmarks for Solana trading platforms and Telegram bots — volume, fees, unique swap transactions, and app store ratings."; export const metadata: import("next").Metadata = pageMetadata({ path: "/trading-apps", @@ -24,7 +24,6 @@ const BENCH_SLUGS = [ "solana-avg-trade-size", "solana-launchpad-wars", "memecoin-platforms", - "trading-app-execution", "app-store-ratings", ] as const; @@ -35,24 +34,32 @@ const PLATFORMS = [ { slug: "fomo", name: "FOMO" }, { slug: "trojan", name: "Trojan" }, { slug: "photon", name: "Photon" }, + { slug: "bloom", name: "Bloom" }, { slug: "maestro", name: "Maestro" }, ] as const; +// Some platforms own a launchpad that runs under a different slug. +// Add the launchpad's volume to the platform's terminal volume so the +// "24h Volume" column reflects the full ecosystem (e.g. FOMO = terminal + Flap). +const LAUNCHPAD_COMPANION: Partial> = { + fomo: "flap", +}; + const COLUMNS = [ { key: "volume" as const, label: "24h Volume", bench: "solana-trading-platform-wars", fmt: fmtUSD, - tip: "Attributed 24h trading volume", + tip: "Terminal routing volume + owned launchpad volume (Mobula attribution)", higherBetter: true, }, { key: "traders" as const, - label: "Unique Traders", + label: "Swap Tx", bench: "solana-unique-traders", fmt: fmtCount, - tip: "Unique swap transactions in 24h", + tip: "Unique swap transactions in 24h (not unique wallets — active traders submit multiple tx/day)", higherBetter: true, }, { @@ -68,7 +75,7 @@ const COLUMNS = [ label: "Fee Rate", bench: "memecoin-platforms", fmt: fmtPct, - tip: "Protocol fee revenue ÷ volume (take rate)", + tip: "Observed take rate: on-chain fee revenue ÷ total attributed volume (lower bound — platforms with fee-exempt volume show lower rates)", higherBetter: false, }, { @@ -86,7 +93,7 @@ type ColKey = (typeof COLUMNS)[number]["key"]; function indexBySlug(results: ProviderResult[] | undefined): Record { const out: Record = {}; for (const r of results ?? []) { - out[r.slug] = r.ms.mean; + out[r.slug] = r.ms.p50; } return out; } @@ -131,10 +138,9 @@ const GROUPS = [ items: [{ slug: "solana-launchpad-wars", title: "Launchpad volume" }], }, { - label: "Fees & execution", + label: "Fees", items: [ { slug: "memecoin-platforms", title: "Platform fee rates" }, - { slug: "trading-app-execution", title: "Execution quality" }, ], }, { @@ -144,9 +150,10 @@ const GROUPS = [ ] as const; export default async function TradingAppsHubPage() { - const [volBench, tradersBench, tradeSizeBench, feeBench, ratingsBench] = + const [volBench, launchpadBench, tradersBench, tradeSizeBench, feeBench, ratingsBench] = await Promise.all([ getBenchmark("solana-trading-platform-wars"), + getBenchmark("solana-launchpad-wars"), getBenchmark("solana-unique-traders"), getBenchmark("solana-avg-trade-size"), getBenchmark("memecoin-platforms"), @@ -154,6 +161,7 @@ export default async function TradingAppsHubPage() { ]); const volIdx = indexBySlug(volBench?.results); + const launchpadIdx = indexBySlug(launchpadBench?.results); const tradersIdx = indexBySlug(tradersBench?.results); const tradeSizeIdx = indexBySlug(tradeSizeBench?.results); const feeIdx = indexBySlug(feeBench?.results); @@ -169,17 +177,23 @@ export default async function TradingAppsHubPage() { 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)); + const matrix: Row[] = PLATFORMS.map((p) => { + const termVol = volIdx[p.slug] ?? null; + const companionSlug = LAUNCHPAD_COMPANION[p.slug]; + const lpVol = companionSlug ? (launchpadIdx[companionSlug] ?? null) : null; + const volume = + termVol !== null || lpVol !== null ? (termVol ?? 0) + (lpVol ?? 0) : null; + return { + slug: p.slug, + name: p.name, + volume, + 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; @@ -191,9 +205,12 @@ export default async function TradingAppsHubPage() { bests[col.key] = best(col.key, col.higherBetter); } - const topVolume = volBench?.results[0]; + const topVolumeRow = matrix.reduce( + (best, row) => ((row.volume ?? -1) > (best.volume ?? -1) ? row : best), + matrix[0], + ); const topRating = ratingsBench?.results.find((r) => - PLATFORMS.some((p) => p.slug === r.slug) + PLATFORMS.some((p) => p.slug === r.slug), ); const breadcrumbLd = { @@ -237,7 +254,6 @@ export default async function TradingAppsHubPage() { dangerouslySetInnerHTML={{ __html: safeJsonLd(itemListLd) }} /> - {/* Header */}

{PLATFORMS.length} platforms measured across {BENCH_SLUGS.length}{" "} - independent benchmarks: volume, unique traders, average trade size, fee + independent benchmarks: volume, swap transactions, average trade size, fee rates, and app store ratings. Live data, no marketing claims.

- {/* KPI strip */}
- {/* Comparison table */}

- {/* Benchmarks grouped list */}

- 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{" "} + Volume combines terminal routing volume (Mobula lighthouse byPlatform) + with owned launchpad volume where applicable (e.g. FOMO includes Flap). + Swap transaction counts are from Dune Analytics on-chain data. + Fee rates compare fee-wallet inflows to attributed volume. + App store ratings are fetched from the Apple App Store API. + All harnesses are open source on{" "} Date: Wed, 19 Aug 2026 12:27:03 +0200 Subject: [PATCH 2/5] fix: remove fee rate from table (stale/inconsistent), honest tooltips per column, drop launchpad companion --- src/app/trading-apps/page.tsx | 108 +++++++++++++--------------------- 1 file changed, 42 insertions(+), 66 deletions(-) diff --git a/src/app/trading-apps/page.tsx b/src/app/trading-apps/page.tsx index 80d240d9..ed7426fc 100644 --- a/src/app/trading-apps/page.tsx +++ b/src/app/trading-apps/page.tsx @@ -7,7 +7,7 @@ import { safeJsonLd, buildBreadcrumbJsonLd } from "@/lib/jsonld"; import { SITE } from "@/data/site"; const DESCRIPTION = - "Live benchmarks for Solana trading platforms and Telegram bots — volume, fees, unique swap transactions, and app store ratings."; + "Live benchmarks for Solana trading platforms and Telegram bots — volume, swap transactions, average trade size, and app store ratings."; export const metadata: import("next").Metadata = pageMetadata({ path: "/trading-apps", @@ -27,6 +27,9 @@ const BENCH_SLUGS = [ "app-store-ratings", ] as const; +// pump.fun is tracked via byLaunchpad (bonding curve, no referral tag system). +// All other platforms are tracked via byPlatform (referral tags). Volume is +// not directly comparable across the two groups — tooltip discloses this. const PLATFORMS = [ { slug: "pump-fun", name: "pump.fun" }, { slug: "gmgn", name: "GMGN" }, @@ -34,24 +37,20 @@ const PLATFORMS = [ { slug: "fomo", name: "FOMO" }, { slug: "trojan", name: "Trojan" }, { slug: "photon", name: "Photon" }, - { slug: "bloom", name: "Bloom" }, { slug: "maestro", name: "Maestro" }, ] as const; -// Some platforms own a launchpad that runs under a different slug. -// Add the launchpad's volume to the platform's terminal volume so the -// "24h Volume" column reflects the full ecosystem (e.g. FOMO = terminal + Flap). -const LAUNCHPAD_COMPANION: Partial> = { - fomo: "flap", -}; - +// Fee rate (memecoin-platforms) removed from the comparison table: the bench +// uses different data sources per platform (Dune on-chain vs DeFiLlama off-chain +// for FOMO), and the denominator (Mobula attributed volume) is inconsistent +// across platforms. Full detail available at /benchmarks/memecoin-platforms. const COLUMNS = [ { key: "volume" as const, label: "24h Volume", bench: "solana-trading-platform-wars", fmt: fmtUSD, - tip: "Terminal routing volume + owned launchpad volume (Mobula attribution)", + tip: "Mobula attribution. pump.fun = bonding-curve launchpad volume. All others = terminal routing volume via referral tag. Not directly comparable across the two groups.", higherBetter: true, }, { @@ -59,7 +58,7 @@ const COLUMNS = [ label: "Swap Tx", bench: "solana-unique-traders", fmt: fmtCount, - tip: "Unique swap transactions in 24h (not unique wallets — active traders submit multiple tx/day)", + tip: "Unique swap transactions in 24h via Dune. pump.fun uses dex_solana.trades (all swaps incl. 0-fee). Terminals use fee-wallet detection (fee-generating swaps only). Methods differ.", higherBetter: true, }, { @@ -67,15 +66,7 @@ const COLUMNS = [ label: "Avg Trade", bench: "solana-avg-trade-size", fmt: fmtUSD, - tip: "Average swap size in USD", - higherBetter: false, - }, - { - key: "feeRate" as const, - label: "Fee Rate", - bench: "memecoin-platforms", - fmt: fmtPct, - tip: "Observed take rate: on-chain fee revenue ÷ total attributed volume (lower bound — platforms with fee-exempt volume show lower rates)", + tip: "24h volume ÷ trade count via Mobula. Includes bots and MEV — platforms with heavy bot sniping (notably pump.fun) show lower averages than human-only baselines.", higherBetter: false, }, { @@ -83,7 +74,7 @@ const COLUMNS = [ label: "App Rating", bench: "app-store-ratings", fmt: fmtRating, - tip: "Apple App Store rating (out of 5)", + tip: "Apple App Store all-time average rating. Axiom, Trojan, Photon and Maestro have no iOS app — they show —.", higherBetter: true, }, ] as const; @@ -113,11 +104,6 @@ function fmtCount(v: number | null): string { 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`; @@ -129,7 +115,7 @@ const GROUPS = [ items: [ { 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-unique-traders", title: "Swap transactions" }, { slug: "solana-avg-trade-size", title: "Average trade size" }, ], }, @@ -139,9 +125,7 @@ const GROUPS = [ }, { label: "Fees", - items: [ - { slug: "memecoin-platforms", title: "Platform fee rates" }, - ], + items: [{ slug: "memecoin-platforms", title: "Platform fee rates" }], }, { label: "App store", @@ -150,21 +134,17 @@ const GROUPS = [ ] as const; export default async function TradingAppsHubPage() { - const [volBench, launchpadBench, tradersBench, tradeSizeBench, feeBench, ratingsBench] = + const [volBench, tradersBench, tradeSizeBench, ratingsBench] = await Promise.all([ getBenchmark("solana-trading-platform-wars"), - getBenchmark("solana-launchpad-wars"), getBenchmark("solana-unique-traders"), getBenchmark("solana-avg-trade-size"), - getBenchmark("memecoin-platforms"), getBenchmark("app-store-ratings"), ]); const volIdx = indexBySlug(volBench?.results); - const launchpadIdx = indexBySlug(launchpadBench?.results); const tradersIdx = indexBySlug(tradersBench?.results); const tradeSizeIdx = indexBySlug(tradeSizeBench?.results); - const feeIdx = indexBySlug(feeBench?.results); const ratingIdx = indexBySlug(ratingsBench?.results); type Row = { @@ -173,26 +153,17 @@ export default async function TradingAppsHubPage() { volume: number | null; traders: number | null; tradeSize: number | null; - feeRate: number | null; rating: number | null; }; - const matrix: Row[] = PLATFORMS.map((p) => { - const termVol = volIdx[p.slug] ?? null; - const companionSlug = LAUNCHPAD_COMPANION[p.slug]; - const lpVol = companionSlug ? (launchpadIdx[companionSlug] ?? null) : null; - const volume = - termVol !== null || lpVol !== null ? (termVol ?? 0) + (lpVol ?? 0) : null; - return { - slug: p.slug, - name: p.name, - volume, - 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)); + 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, + rating: ratingIdx[p.slug] ?? null, + })).sort((a, b) => (b.volume ?? -1) - (a.volume ?? -1)); function best(key: ColKey, higherBetter: boolean): number | null { const vals = matrix.map((r) => r[key]).filter((v): v is number => v !== null); @@ -206,10 +177,10 @@ export default async function TradingAppsHubPage() { } const topVolumeRow = matrix.reduce( - (best, row) => ((row.volume ?? -1) > (best.volume ?? -1) ? row : best), + (b, row) => ((row.volume ?? -1) > (b.volume ?? -1) ? row : b), matrix[0], ); - const topRating = ratingsBench?.results.find((r) => + const topRating = ratingsBench?.results.find((r: ProviderResult) => PLATFORMS.some((p) => p.slug === r.slug), ); @@ -266,8 +237,8 @@ export default async function TradingAppsHubPage() {

{PLATFORMS.length} platforms measured across {BENCH_SLUGS.length}{" "} - independent benchmarks: volume, swap transactions, average trade size, fee - rates, and app store ratings. Live data, no marketing claims. + independent benchmarks: volume, swap transactions, average trade size, + fee rates, and app store ratings. Live data, no marketing claims.

@@ -302,7 +273,7 @@ export default async function TradingAppsHubPage() { Platform comparison

- +
@@ -375,7 +346,7 @@ export default async function TradingAppsHubPage() {

Best value per column highlighted in green. Sorted by 24h volume. - Data refreshes every 60 s. + Hover column headers for methodology notes. Data refreshes every 60 s.

@@ -393,7 +364,7 @@ export default async function TradingAppsHubPage() { {group.label}

- {group.items.map((item, i) => ( + {group.items.map((item: { slug: string; title: string }, i: number) => (
{i > 0 &&
}

- Volume combines terminal routing volume (Mobula lighthouse byPlatform) - with owned launchpad volume where applicable (e.g. FOMO includes Flap). - Swap transaction counts are from Dune Analytics on-chain data. - Fee rates compare fee-wallet inflows to attributed volume. - App store ratings are fetched from the Apple App Store API. - All harnesses are open source on{" "} + Volume via Mobula lighthouse: pump.fun uses bonding-curve launchpad + attribution; terminals use referral-tag attribution — not directly + comparable across the two groups. Swap transaction counts from Dune + Analytics (pump.fun: dex-level; terminals: fee-wallet detection). + Average trade size = volume ÷ trade count, includes bots and MEV. + Fee rates available in the dedicated{" "} + + fee rates bench + + . App store ratings from the Apple iTunes lookup API. All harnesses + open source on{" "} GitHub - . Data released under{" "} + . Data under{" "} Date: Wed, 19 Aug 2026 12:45:57 +0200 Subject: [PATCH 3/5] fix: restore fee rate column with correct metric + fix prometheus scrape for memecoin-platforms --- src/app/trading-apps/page.tsx | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/app/trading-apps/page.tsx b/src/app/trading-apps/page.tsx index ed7426fc..95fdf542 100644 --- a/src/app/trading-apps/page.tsx +++ b/src/app/trading-apps/page.tsx @@ -40,10 +40,6 @@ const PLATFORMS = [ { slug: "maestro", name: "Maestro" }, ] as const; -// Fee rate (memecoin-platforms) removed from the comparison table: the bench -// uses different data sources per platform (Dune on-chain vs DeFiLlama off-chain -// for FOMO), and the denominator (Mobula attributed volume) is inconsistent -// across platforms. Full detail available at /benchmarks/memecoin-platforms. const COLUMNS = [ { key: "volume" as const, @@ -69,6 +65,14 @@ const COLUMNS = [ tip: "24h volume ÷ trade count via Mobula. Includes bots and MEV — platforms with heavy bot sniping (notably pump.fun) show lower averages than human-only baselines.", higherBetter: false, }, + { + key: "feeRate" as const, + label: "Fee Rate", + bench: "memecoin-platforms", + fmt: fmtPct, + tip: "Observed take rate: fee revenue ÷ fee-paying volume (Dune tx join). Comparable across platforms. FOMO uses DeFiLlama (includes off-chain relay fees). pump.fun cut trading fees to 0% in Aug 2026.", + higherBetter: false, + }, { key: "rating" as const, label: "App Rating", @@ -104,6 +108,11 @@ function fmtCount(v: number | null): string { 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`; @@ -134,17 +143,19 @@ const GROUPS = [ ] as const; export default async function TradingAppsHubPage() { - const [volBench, tradersBench, tradeSizeBench, ratingsBench] = + const [volBench, tradersBench, tradeSizeBench, feeBench, ratingsBench] = await Promise.all([ getBenchmark("solana-trading-platform-wars"), getBenchmark("solana-unique-traders"), getBenchmark("solana-avg-trade-size"), + getBenchmark("memecoin-platforms"), getBenchmark("app-store-ratings"), ]); const volIdx = indexBySlug(volBench?.results); const tradersIdx = indexBySlug(tradersBench?.results); const tradeSizeIdx = indexBySlug(tradeSizeBench?.results); + const feeIdx = indexBySlug(feeBench?.results); const ratingIdx = indexBySlug(ratingsBench?.results); type Row = { @@ -153,6 +164,7 @@ export default async function TradingAppsHubPage() { volume: number | null; traders: number | null; tradeSize: number | null; + feeRate: number | null; rating: number | null; }; @@ -162,6 +174,7 @@ export default async function TradingAppsHubPage() { 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)); From fe138ca7209c7e26e15b1c7f3a39a3cf7b058577 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Wed, 19 Aug 2026 12:48:02 +0200 Subject: [PATCH 4/5] fix: update methodology footer for fee rate --- src/app/trading-apps/page.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/app/trading-apps/page.tsx b/src/app/trading-apps/page.tsx index 95fdf542..a8bd3716 100644 --- a/src/app/trading-apps/page.tsx +++ b/src/app/trading-apps/page.tsx @@ -425,12 +425,9 @@ export default async function TradingAppsHubPage() { comparable across the two groups. Swap transaction counts from Dune Analytics (pump.fun: dex-level; terminals: fee-wallet detection). Average trade size = volume ÷ trade count, includes bots and MEV. - Fee rates available in the dedicated{" "} - - fee rates bench - - . App store ratings from the Apple iTunes lookup API. All harnesses - open source on{" "} + Fee rate = on-chain fee revenue ÷ fee-paying volume (Dune tx join); + FOMO via DeFiLlama. App store ratings from the Apple iTunes lookup API. + All harnesses open source on{" "} Date: Wed, 19 Aug 2026 13:23:44 +0200 Subject: [PATCH 5/5] fix: higherBetter true for avg trade size column --- src/app/trading-apps/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/trading-apps/page.tsx b/src/app/trading-apps/page.tsx index a8bd3716..ea76cd87 100644 --- a/src/app/trading-apps/page.tsx +++ b/src/app/trading-apps/page.tsx @@ -63,7 +63,7 @@ const COLUMNS = [ bench: "solana-avg-trade-size", fmt: fmtUSD, tip: "24h volume ÷ trade count via Mobula. Includes bots and MEV — platforms with heavy bot sniping (notably pump.fun) show lower averages than human-only baselines.", - higherBetter: false, + higherBetter: true, }, { key: "feeRate" as const,