Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion benchmarks/memecoin-platforms.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@ metric: Median fee
unit: pct

disclaimer: |
Measures explicit, separately charged platform fees on sampled trades above $5 USD. Costs embedded in pool pricing (bonding-curve spread, AMM LP fees, price impact) are not captured. Effective round-trip cost may differ see methodology for sampling scope.
Measures explicit, separately charged platform fees on sampled trades above $5 USD. Costs embedded in pool pricing (bonding-curve spread, AMM LP fees, price impact) are not captured. Effective round-trip cost may differ; see methodology for sampling scope.

source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/memecoin-platforms

Expand Down
10 changes: 5 additions & 5 deletions benchmarks/trading-app-execution.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,8 +4,8 @@ slug: trading-app-execution
number: "204"
title: "Trading app execution quality: Axiom vs GMGN vs Trojan vs Maestro, Solana + EVM"
seo_title: "Solana trading bot execution quality 2026: Axiom, GMGN, Trojan, Maestro, pump.fun"
seo_description: "Compare on-chain execution quality for Solana trading apps: avg platform fee, priority fee, Jito bundle rate, CU price. Axiom vs GMGN vs Trojan vs Maestro vs pump.fun vs Photon measured passively from fee accounts, updated hourly."
subtitle: Passive on-chain execution metrics for the top Solana trading apps and Telegram bots. Measured directly from fee-account transactions no synthetic trades. Priority fee, Jito rate, CU price, and platform fee per transaction. EVM chain revenue (Ethereum, BSC, Base) on the chain tabs.
seo_description: "Compare on-chain execution quality for Solana trading apps: avg platform fee, priority fee, Jito bundle rate, CU price. Axiom vs GMGN vs Trojan vs Maestro vs pump.fun vs Photon, measured passively from fee accounts, updated hourly."
subtitle: Passive on-chain execution metrics for the top Solana trading apps and Telegram bots. Measured directly from fee-account transactions, no synthetic trades. Priority fee, Jito rate, CU price, and platform fee per transaction. EVM chain revenue (Ethereum, BSC, Base) on the chain tabs.

category: Trading
status: live
Expand All@@ -23,7 +23,7 @@ abstract: |
PostgreSQL and materialised every 5 minutes. No trades are simulated; the collector
reads only existing on-chain activity. For EVM chains (Ethereum, BSC, Base), a separate
collector tracks USDC and native ETH/BNB transfers to each platform's fee address using
eth_getLogs on public RPCs no trace API required.
eth_getLogs on public RPCs; no trace API required.

methodology:
- "Solana source: Helius RPC getSignaturesForAddress on each platform's fee wallet. 100 transactions sampled per poll (every ~5 min)."
Expand All@@ -34,7 +34,7 @@ methodology:
- "Tx count: total transactions at the fee account over the window, from raw getSignaturesForAddress counts (not just the 100-tx sample)."
- "EVM source: eth_getLogs for USDC (ERC-20) transfers + native ETH/BNB transfers to each platform's fee address. Base chain: USDC only (no free trace API for native)."
- "EVM cadence: block-by-block scanning, bootstrapped 30 days back on first run, then continuous."
- "All measurements are passive no test transactions, no funded wallets, no on-chain footprint."
- "All measurements are passive; no test transactions, no funded wallets, no on-chain footprint."

findings:
- "Axiom leads Solana trading apps by transaction volume, processing over 400K fee transactions per 24h across its 20 fee wallets."
Expand All@@ -45,7 +45,7 @@ findings:

faq:
- q: "What is platform fee vs priority fee?"
a: "Priority fee is paid to Solana validators to compete for block space it does not go to the trading app. Platform fee is the SOL transferred to the trading app's own fee wallet per transaction, which is the actual revenue the platform captures from each trade."
a: "Priority fee is paid to Solana validators to compete for block space; it does not go to the trading app. Platform fee is the SOL transferred to the trading app's own fee wallet per transaction, which is the actual revenue the platform captures from each trade."
- q: "Why does Trojan show a near-zero Jito rate?"
a: "Trojan does not appear to use Jito MEV bundles for its standard execution path. This is consistent with its design as a Telegram bot that prioritises simplicity over MEV protection."
- q: "How is transaction count measured?"
Expand Down
23 changes: 20 additions & 3 deletions src/app/apps/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,13 +7,14 @@ import { fetchExecLeaderboard } from "@/lib/solana-exec";
import { fetchSolPrice } from "@/lib/sol-price";
import { fetchFOMORelayFees } from "@/lib/dune";
import { TRADING_APPS } from "@/lib/trading-apps-config";
import { fetchDeFiLlamaData, type DLPlatformData } from "@/lib/defillama";
import { TradingAppsLeaderboard, type UnifiedAppRow } from "@/components/trading-apps-leaderboard";
import { RevenueSummary } from "@/components/revenue-summary";
import { ExecChainTabs } from "@/components/exec-chain-tabs";
import Link from "next/link";

const DESCRIPTION =
"Protocol fees collected by trading apps: meme bots and telegram bots. On-chain data, updated hourly.";
"Protocol fees collected by trading apps and meme trading terminals — Solana, Ethereum, BSC. On-chain data, updated every 5 min.";

export const metadata: Metadata = pageMetadata({
path: "/apps",
Expand All@@ -26,13 +27,22 @@ export const revalidate = 300;
const WINDOWS = ["24h", "7d", "30d"] as const;

export default async function AppsHubPage() {
const [evmData, solanaData, solPrice, fomoRelay] = await Promise.all([
const dlSlugMap = Object.fromEntries(
TRADING_APPS.filter((a) => a.defillamaSlug).map((a) => [a.id, a.defillamaSlug!])
);

const [evmData, solanaData, solPrice, fomoRelay, dlData] = await Promise.all([
fetchEVMRevenue(),
fetchExecLeaderboard(),
fetchSolPrice(),
fetchFOMORelayFees(),
fetchDeFiLlamaData(dlSlugMap),
]);

// Market share = each platform's DL 24h fees / cohort total (active only)
const activeDlTotal = TRADING_APPS.filter((a) => !a.inactive && a.defillamaSlug)
.reduce((sum, a) => sum + (dlData.get(a.id)?.total24h ?? 0), 0);

const evmByPlatform = new Map(
(evmData?.platforms ?? []).map((p) => [p.platform, p])
);
Expand DownExpand Up@@ -97,6 +107,11 @@ export default async function AppsHubPage() {
};
}

const dl = dlData.get(meta.id) ?? null;
const marketSharePct = dl && activeDlTotal > 0 && !meta.inactive
? (dl.total24h / activeDlTotal) * 100
: null;

return {
meta,
windows,
Expand All@@ -106,6 +121,8 @@ export default async function AppsHubPage() {
base: baseChain?.coverage === "stable-only",
robinhood: rhChain?.coverage === "stable-only",
},
dl,
marketSharePct,
};
});

Expand DownExpand Up@@ -135,7 +152,7 @@ export default async function AppsHubPage() {
</p>

<div className="mt-10">
<TradingAppsLeaderboard rows={rows} updatedAt={updatedAt} fomoLatestDate={fomoRelay?.latestDate ?? null} fomoRelayAvailable={fomoRelay !== null} />
<TradingAppsLeaderboard rows={rows} updatedAt={updatedAt} fomoLatestDate={fomoRelay?.latestDate ?? null} fomoRelayAvailable={fomoRelay !== null} activeDlTotal={activeDlTotal} />
</div>

<p className="mt-6 text-xs text-ink-muted leading-relaxed max-w-2xl">
Expand Down
108 changes: 93 additions & 15 deletions src/components/trading-apps-leaderboard.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ import Link from "next/link";
import { useState } from "react";
import { logoPath } from "@/lib/logo-manifest";
import type { AppMeta } from "@/lib/trading-apps-config";
import type { DLPlatformData } from "@/lib/defillama";

type FeeWindow = {
solana: number | null;
Expand All@@ -19,6 +20,8 @@ export type UnifiedAppRow = {
meta: AppMeta;
windows: Record<string, FeeWindow>;
stableOnly: { ethereum: boolean; bsc: boolean; base: boolean; robinhood: boolean };
dl: DLPlatformData | null;
marketSharePct: number | null;
};

type TabKey = "all" | "trading-terminal" | "telegram-bot";
Expand All@@ -41,9 +44,15 @@ const CATEGORY_BADGE: Record<AppMeta["category"], string> = {
"telegram-bot": "bg-blue-500/10 text-blue-400 border border-blue-500/20",
};

const FORM_FACTOR_ICON: Record<AppMeta["formFactor"], string> = {
web: "🌐",
mobile: "📱",
telegram: "✈️",
};

const CATEGORY_LABEL: Record<AppMeta["category"], string> = {
"trading-terminal": "Trading Terminal",
"telegram-bot": "Telegram Bot",
"trading-terminal": "Terminal",
"telegram-bot": "Bot",
};

function fmtUSD(n: number | null): string {
Expand DownExpand Up@@ -99,6 +108,7 @@ export function TradingAppsLeaderboard({
updatedAt: string | null;
fomoLatestDate: string | null;
fomoRelayAvailable: boolean;
activeDlTotal: number;
}) {
const [tab, setTab] = useState<TabKey>("all");
const [window, setWindow] = useState<WindowKey>("24h");
Expand All@@ -114,6 +124,11 @@ export function TradingAppsLeaderboard({
(r) => r.stableOnly.ethereum || r.stableOnly.bsc || r.stableOnly.base || r.stableOnly.robinhood
);

// Any active platform with >50% daily swing makes Share numbers unreliable for the whole cohort
const hasExtremeMove = sorted.some(
(r) => !r.meta.inactive && r.dl && Math.abs(r.dl.change_1d ?? 0) > 50
);

return (
<div className="card rounded-xl overflow-hidden">
<div className="px-4 sm:px-6 pt-4 pb-0 flex items-end justify-between gap-4 flex-wrap border-b border-rule">
Expand DownExpand Up@@ -164,26 +179,31 @@ export function TradingAppsLeaderboard({
<tr className="border-b border-rule">
<th className="text-left py-3 pl-4 sm:pl-6 pr-3 font-medium text-ink-muted text-xs w-8">#</th>
<th className="text-left py-3 pr-4 font-medium text-ink-muted text-xs">App</th>
<th className="text-left py-3 pr-4 font-medium text-ink-muted text-xs hidden sm:table-cell">Category</th>
<th className="text-left py-3 pr-4 font-medium text-ink-muted text-xs hidden sm:table-cell">Type</th>
<th className="text-right py-3 pr-4 sm:pr-6 font-medium text-ink-muted text-xs">
<Link href="/benchmarks/memecoin-platforms" className="hover:text-ink-soft transition-colors underline decoration-dotted">
Solana
</Link>
</th>
<th className="text-right py-3 pr-4 sm:pr-6 font-medium text-ink-muted text-xs hidden md:table-cell">Ethereum</th>
<th className="text-right py-3 pr-4 sm:pr-6 font-medium text-ink-muted text-xs hidden md:table-cell">BSC</th>
<th className="text-right py-3 pr-4 sm:pr-6 font-medium text-ink-muted text-xs hidden md:table-cell">Base</th>
<th className="text-right py-3 pr-4 sm:pr-6 font-medium text-ink-muted text-xs hidden lg:table-cell">Robinhood</th>
<th className="text-right py-3 pr-4 sm:pr-6 font-medium text-ink-muted text-xs hidden md:table-cell">ETH/BSC/Base</th>
<th className="text-right py-3 pr-4 sm:pr-6 font-medium text-ink-muted text-xs hidden lg:table-cell" title="Net revenue per DeFiLlama (fees minus referral/cashback)">DL Rev</th>
<th
className="text-right py-3 pr-4 sm:pr-6 font-medium text-ink-muted text-xs hidden lg:table-cell"
title={hasExtremeMove ? "Share distorted — at least one platform has a >50% daily swing (possible data gap)" : undefined}
>
Share{hasExtremeMove ? " ⚠" : ""}
</th>
<th className="text-right py-3 pr-4 sm:pr-6 font-medium text-ink-muted text-xs">
Total {window}
</th>
</tr>
</thead>
<tbody>
{sorted.map((row, i) => {
const { meta, stableOnly } = row;
const { meta, dl, marketSharePct } = row;
const fees = row.windows[window] ?? { solana: null, ethereum: null, bsc: null, base: null, robinhood: null, total: 0 };
const logo = meta.logoKey ? logoPath(meta.logoKey) : null;
const evmTotal = (fees.ethereum ?? 0) + (fees.bsc ?? 0) + (fees.base ?? 0) + (fees.robinhood ?? 0);

return (
<tr
Expand DownExpand Up@@ -216,6 +236,25 @@ export function TradingAppsLeaderboard({
Suspended
</span>
)}
{dl?.change_1d !== null && dl?.change_1d !== undefined && !meta.inactive && dl.total24h >= 5000 && (
(() => {
const extreme = Math.abs(dl.change_1d) > 50;
return (
<span
className={`inline-flex px-1.5 py-0.5 rounded text-[10px] font-mono tabular-nums shrink-0 ${
extreme
? "text-amber-400/80 bg-amber-500/10"
: dl.change_1d >= 0
? "text-green-400 bg-green-500/10"
: "text-red-400 bg-red-500/10"
}`}
title={extreme ? "Large move — possible data ingestion gap, not verified" : "DeFiLlama revenue 24h vs prior 24h"}
>
{extreme ? "⚠ " : dl.change_1d >= 0 ? "+" : ""}{dl.change_1d.toFixed(1)}%
</span>
);
})()
)}
{meta.benchUrl && (
<a
href={meta.productUrl}
Expand All@@ -232,15 +271,51 @@ export function TradingAppsLeaderboard({
</div>
</td>
<td className="py-4 pr-4 align-middle hidden sm:table-cell">
<span className={`inline-flex px-2 py-0.5 rounded text-[11px] font-medium ${CATEGORY_BADGE[meta.category]}`}>
{CATEGORY_LABEL[meta.category]}
</span>
<div className="flex items-center gap-1.5">
<span className="text-sm" title={meta.formFactor}>{FORM_FACTOR_ICON[meta.formFactor]}</span>
<span className={`inline-flex px-2 py-0.5 rounded text-[11px] font-medium ${CATEGORY_BADGE[meta.category]}`}>
{CATEGORY_LABEL[meta.category]}
</span>
</div>
</td>
<ChainCell value={fees.solana} />
<ChainCell value={fees.ethereum} stableOnly={stableOnly.ethereum} />
<ChainCell value={fees.bsc} stableOnly={stableOnly.bsc} />
<ChainCell value={fees.base} stableOnly={stableOnly.base} />
<ChainCell value={fees.robinhood} stableOnly={stableOnly.robinhood} hideBelow="lg" />
<td className="py-4 pr-4 sm:pr-6 text-right font-mono tabular-nums align-middle hidden md:table-cell">
{evmTotal > 0 ? (
<span className="text-ink">{fmtUSD(evmTotal)}</span>
) : <span className="text-ink-faint">—</span>}
</td>
<td className="py-4 pr-4 sm:pr-6 text-right font-mono tabular-nums align-middle hidden lg:table-cell">
{dl && !meta.inactive ? (
<span className="text-ink-soft">
{fmtUSD(dl.total24h)}
{meta.defillamaScope === "venue" && (
<sup className="ml-0.5 text-[10px] text-ink-muted font-normal" title="Venue-level: includes creator/cashback fees not retained by the platform">²</sup>
)}
</span>
) : <span className="text-ink-faint">—</span>}
</td>
<td className="py-4 pr-4 sm:pr-6 text-right align-middle hidden lg:table-cell">
{marketSharePct !== null ? (
(() => {
const rowExtreme = !meta.inactive && dl && Math.abs(dl.change_1d ?? 0) > 50;
return rowExtreme ? (
<span className="font-mono text-xs text-amber-400/60 tabular-nums" title="Share unreliable — data gap suspected">⚠</span>
) : (
<div className="flex items-center justify-end gap-1.5">
<div className="w-12 h-1.5 rounded-full bg-paper-soft overflow-hidden">
<div
className="h-full rounded-full bg-accent/60"
style={{ width: `${Math.min(marketSharePct, 100)}%` }}
/>
</div>
<span className={`font-mono text-xs tabular-nums w-8 text-right ${hasExtremeMove ? "text-amber-400/60" : "text-ink-muted"}`}>
{marketSharePct.toFixed(1)}%
</span>
</div>
);
})()
) : <span className="text-ink-faint">—</span>}
</td>
<td className="py-4 pr-4 sm:pr-6 text-right font-mono font-semibold text-ink tabular-nums align-middle">
{fees.total > 0 ? fmtUSD(fees.total) : <Dash />}
</td>
Expand DownExpand Up@@ -273,6 +348,9 @@ export function TradingAppsLeaderboard({
</p>
)}
<FOMODataNotice latestDate={fomoLatestDate} />
<p className="px-4 sm:px-6 py-2.5 text-[11px] text-ink-faint border-t border-rule">
DL Rev = net revenue per DeFiLlama (fees minus referral/cashback). <sup>²</sup> Venue-level: bonding curve + creator slice, not frontend-only.
</p>
</div>
);
}
Expand Down
Loading
Loading