From f062c7e585102219c13c6a6b6052b0ae26670b72 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Fri, 21 Aug 2026 16:51:39 +0200 Subject: [PATCH] feat: add dune_platform_wallets_24h metric + bench 208 + trading-apps hub column --- benchmarks/trading-platform-wallets.yml | 172 ++++++++++++++++++ .../dune-platform-volume/cmd/script/dune.go | 30 +-- .../cmd/script/metrics.go | 9 + src/app/trading-apps/page.tsx | 17 +- 4 files changed, 213 insertions(+), 15 deletions(-) create mode 100644 benchmarks/trading-platform-wallets.yml diff --git a/benchmarks/trading-platform-wallets.yml b/benchmarks/trading-platform-wallets.yml new file mode 100644 index 00000000..122468ed --- /dev/null +++ b/benchmarks/trading-platform-wallets.yml @@ -0,0 +1,172 @@ +# OpenChainBench. Bench 208 + +slug: trading-platform-wallets +number: "208" +title: "Daily active wallets per trading platform: GMGN vs Axiom vs Fomo vs pump.fun, live" +seo_title: "Daily active wallets by trading platform 2026: GMGN vs Axiom vs Fomo vs pump.fun" +seo_description: "{{best_name}} leads trading platforms with {{best_p50}} unique active wallets in 24h. GMGN vs Axiom vs Fomo vs Trojan vs BasedBot ranked by real user count, live from Dune community datasets." +subtitle: Live count of unique wallets that traded through each platform in the last complete day. Sourced from Dune community datasets, refreshed every 4 hours. Reveals real user base vs bot-inflated transaction counts. + +category: Trading +status: live +metric: Active wallets 24h +unit: count +higher_is_better: true + +disclaimer: | + Wallet counts are unique wallets per platform per day from Dune community datasets (cross-chain). A wallet using two platforms is counted in each; counts are not mutually exclusive. Includes bots, MEV searchers, and multi-wallet traders. pump.fun wallets come from dataset_pumpapp_sol_daily only (not relay); relay wallets would overlap with pumpapp and double-count. Data represents the most recent complete day, refreshed every 4 hours. + +seo_intro: | + Daily active wallet count is the clearest signal of real platform usage, filtering out + the noise from bots that inflate transaction counts. GMGN consistently leads on wallet + count among trading terminals, reflecting its broad AI copy-trading user base across + multiple chains. Axiom and Fomo compete closely behind. pump.fun naturally dominates + in absolute numbers due to its launchpad mechanic attracting thousands of new wallets + daily across sniping bots and retail buyers. Trojan and Photon serve smaller but + more concentrated user bases with higher average positions. This benchmark tracks + unique active wallets per platform live from Dune community datasets, updated every + 4 hours. + +abstract: | + The dune-platform-volume harness queries Dune community datasets + (dune.adam_tehc_co.dataset_*_daily) every 4 hours. The wallets column in each + dataset provides the count of unique wallet addresses that executed at least one + swap through the platform on the given day. For multi-chain platforms (GMGN, + Axiom, BasedBot, Terminal) the count is summed across all blockchains. For Fomo + and Trojan, the count is Solana-only. pump.fun uses dataset_pumpapp_sol_daily + wallets only. The metric is exposed as dune_platform_wallets_24h per platform. + +methodology: + - "Source: Dune community datasets (dataset_*_daily). wallets column = unique wallet addresses per platform per day." + - "Poll cadence: harness queries Dune every 4 hours; Prometheus scrapes :2112/metrics every 60 seconds." + - "Cross-chain: GMGN, Axiom, BasedBot, Terminal sum wallets across all blockchains. Fomo and Trojan are Solana-only datasets." + - "pump.fun: dataset_pumpapp_sol_daily wallets only (pumpapp frontend). Relay dataset wallets not added to avoid double-counting wallets active on both." + - "Includes all wallets: bots, MEV searchers, and automated traders are counted. Not a human-only metric." + - "Not mutually exclusive: a wallet using GMGN and Axiom on the same day is counted in both platforms." + +findings: + - "{{best_name}} leads trading platforms with {{best_p50}} unique active wallets in 24h." + - "GMGN leads terminals by wallet count, driven by its broad AI copy-trading and multi-chain reach." + - "pump.fun's wallet count reflects daily launchpad activity — thousands of new wallets snipe each token launch." + - "Comparing wallet count to txns per platform reveals the bot ratio: platforms with 50x more txns than wallets have heavy automated activity." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/dune-platform-volume + +prometheus: + window: 24h + freshness_metric: dune_platform_wallets_24h + +faq: + - q: "What does active wallet count measure?" + a: "Active wallets is the number of unique blockchain addresses that executed at least one swap through the platform during the day. It is a proxy for daily active users, though one human can control many wallets and bots count as wallets." + - q: "How is this different from swap transaction count (bench 207)?" + a: "Swap tx count (dune_platform_txns_24h) counts every individual swap. Active wallets counts unique addresses. A bot executing 500 swaps in one day contributes 500 to tx count but only 1 to wallet count. The ratio txns/wallets is a proxy for trading frequency per user and bot intensity." + - q: "Why is pump.fun's wallet count from pumpapp only and not relay?" + a: "The relay dataset (dataset_pumpfun_relay_daily) covers cross-chain relay swaps where pump.fun tokens are bought on EVM chains via relay. Wallets on relay likely overlap significantly with pumpapp wallets. Adding them would double-count users active on both. We use pumpapp as the primary dataset for pump.fun wallet counts." + - q: "Are wallet counts comparable across platforms?" + a: "Approximately. Each dataset counts unique addresses per day per platform independently. Cross-chain platforms (GMGN, Axiom) sum wallets across chains, so the same wallet on Solana and BNB would be counted twice. Single-chain platforms like Fomo and Trojan are Solana-only. The cross-chain sum slightly overstates unique humans for multi-chain platforms." + +providers: + - slug: pump-fun + name: pump.fun + tag: "Solana meme launchpad" + formula: "Unique wallets on pump.fun's app (dataset_pumpapp_sol_daily), latest complete day." + queries: + p50: dune_platform_wallets_24h{platform="pump-fun"} + p90: dune_platform_wallets_24h{platform="pump-fun"} + p99: dune_platform_wallets_24h{platform="pump-fun"} + mean: dune_platform_wallets_24h{platform="pump-fun"} + success: dune_platform_volume_health{platform="pump-fun"} + sample_size: dune_platform_wallets_24h{platform="pump-fun"} + series: dune_platform_wallets_24h{platform="pump-fun"} + + - slug: gmgn + name: GMGN + tag: "AI trading terminal, multi-chain" + formula: "Unique wallets across all chains (dataset_gmgn_daily, cross-chain sum), latest complete day." + queries: + p50: dune_platform_wallets_24h{platform="gmgn"} + p90: dune_platform_wallets_24h{platform="gmgn"} + p99: dune_platform_wallets_24h{platform="gmgn"} + mean: dune_platform_wallets_24h{platform="gmgn"} + success: dune_platform_volume_health{platform="gmgn"} + sample_size: dune_platform_wallets_24h{platform="gmgn"} + series: dune_platform_wallets_24h{platform="gmgn"} + + - slug: axiom + name: Axiom + tag: "Web trading terminal, multi-chain" + formula: "Unique wallets across all chains (dataset_axiom_daily, cross-chain sum), latest complete day." + queries: + p50: dune_platform_wallets_24h{platform="axiom"} + p90: dune_platform_wallets_24h{platform="axiom"} + p99: dune_platform_wallets_24h{platform="axiom"} + mean: dune_platform_wallets_24h{platform="axiom"} + success: dune_platform_volume_health{platform="axiom"} + sample_size: dune_platform_wallets_24h{platform="axiom"} + series: dune_platform_wallets_24h{platform="axiom"} + + - slug: fomo + name: Fomo + tag: "Solana trading platform" + formula: "Unique wallets on Solana (dataset_fomo_sol_daily), latest complete day." + queries: + p50: dune_platform_wallets_24h{platform="fomo"} + p90: dune_platform_wallets_24h{platform="fomo"} + p99: dune_platform_wallets_24h{platform="fomo"} + mean: dune_platform_wallets_24h{platform="fomo"} + success: dune_platform_volume_health{platform="fomo"} + sample_size: dune_platform_wallets_24h{platform="fomo"} + series: dune_platform_wallets_24h{platform="fomo"} + + - slug: trojan + name: Trojan + tag: "Telegram trading bot on Solana" + formula: "Unique wallets on Solana (dataset_trojan_daily), latest complete day." + queries: + p50: dune_platform_wallets_24h{platform="trojan"} + p90: dune_platform_wallets_24h{platform="trojan"} + p99: dune_platform_wallets_24h{platform="trojan"} + mean: dune_platform_wallets_24h{platform="trojan"} + success: dune_platform_volume_health{platform="trojan"} + sample_size: dune_platform_wallets_24h{platform="trojan"} + series: dune_platform_wallets_24h{platform="trojan"} + + - slug: photon + name: Photon + tag: "Solana trading terminal" + formula: "Unique wallets on Solana (dataset_photon_daily), latest complete day." + queries: + p50: dune_platform_wallets_24h{platform="photon"} + p90: dune_platform_wallets_24h{platform="photon"} + p99: dune_platform_wallets_24h{platform="photon"} + mean: dune_platform_wallets_24h{platform="photon"} + success: dune_platform_volume_health{platform="photon"} + sample_size: dune_platform_wallets_24h{platform="photon"} + series: dune_platform_wallets_24h{platform="photon"} + + - slug: padre + name: Terminal + tag: "pump.fun's own trading app (formerly Padre)" + formula: "Unique wallets across all chains (dataset_terminal_daily, cross-chain sum), latest complete day." + queries: + p50: dune_platform_wallets_24h{platform="padre"} + p90: dune_platform_wallets_24h{platform="padre"} + p99: dune_platform_wallets_24h{platform="padre"} + mean: dune_platform_wallets_24h{platform="padre"} + success: dune_platform_volume_health{platform="padre"} + sample_size: dune_platform_wallets_24h{platform="padre"} + series: dune_platform_wallets_24h{platform="padre"} + + - slug: basedbot + name: BasedBot + tag: "Multi-chain trading bot" + formula: "Unique wallets across all chains (dataset_basedbot_daily, cross-chain sum), latest complete day." + queries: + p50: dune_platform_wallets_24h{platform="basedbot"} + p90: dune_platform_wallets_24h{platform="basedbot"} + p99: dune_platform_wallets_24h{platform="basedbot"} + mean: dune_platform_wallets_24h{platform="basedbot"} + success: dune_platform_volume_health{platform="basedbot"} + sample_size: dune_platform_wallets_24h{platform="basedbot"} + series: dune_platform_wallets_24h{platform="basedbot"} diff --git a/harnesses/dune-platform-volume/cmd/script/dune.go b/harnesses/dune-platform-volume/cmd/script/dune.go index 7d26a77f..69347533 100644 --- a/harnesses/dune-platform-volume/cmd/script/dune.go +++ b/harnesses/dune-platform-volume/cmd/script/dune.go @@ -18,25 +18,25 @@ import ( // All others sum across all blockchains for latest available day. const querySQL = ` WITH latest AS ( - SELECT 'gmgn' AS platform, SUM(volume_usd) AS volume_usd, SUM(CAST(txns AS BIGINT)) AS txns, SUM(CAST(fees_usd AS DOUBLE)) AS fees_usd + SELECT 'gmgn' AS platform, SUM(volume_usd) AS volume_usd, SUM(CAST(txns AS BIGINT)) AS txns, SUM(CAST(fees_usd AS DOUBLE)) AS fees_usd, SUM(CAST(wallets AS BIGINT)) AS wallets FROM dune.adam_tehc_co.dataset_gmgn_daily WHERE day = (SELECT MAX(day) FROM dune.adam_tehc_co.dataset_gmgn_daily) UNION ALL - SELECT 'axiom', SUM(volume_usd), SUM(CAST(txns AS BIGINT)), SUM(CAST(fees_usd AS DOUBLE)) + SELECT 'axiom', SUM(volume_usd), SUM(CAST(txns AS BIGINT)), SUM(CAST(fees_usd AS DOUBLE)), SUM(CAST(wallets AS BIGINT)) FROM dune.adam_tehc_co.dataset_axiom_daily WHERE day = (SELECT MAX(day) FROM dune.adam_tehc_co.dataset_axiom_daily) UNION ALL - SELECT 'trojan', SUM(volume_usd), SUM(CAST(txns AS BIGINT)), SUM(CAST(fees_usd AS DOUBLE)) + SELECT 'trojan', SUM(volume_usd), SUM(CAST(txns AS BIGINT)), SUM(CAST(fees_usd AS DOUBLE)), SUM(CAST(wallets AS BIGINT)) FROM dune.adam_tehc_co.dataset_trojan_daily WHERE day = (SELECT MAX(day) FROM dune.adam_tehc_co.dataset_trojan_daily) UNION ALL - SELECT 'padre', SUM(volume_usd), SUM(CAST(txns AS BIGINT)), SUM(CAST(fees_usd AS DOUBLE)) + SELECT 'padre', SUM(volume_usd), SUM(CAST(txns AS BIGINT)), SUM(CAST(fees_usd AS DOUBLE)), SUM(CAST(wallets AS BIGINT)) FROM dune.adam_tehc_co.dataset_terminal_daily WHERE day = (SELECT MAX(day) FROM dune.adam_tehc_co.dataset_terminal_daily) UNION ALL - SELECT 'photon', SUM(volume_usd), SUM(CAST(txns AS BIGINT)), SUM(CAST(fees_usd AS DOUBLE)) + SELECT 'photon', SUM(volume_usd), SUM(CAST(txns AS BIGINT)), SUM(CAST(fees_usd AS DOUBLE)), SUM(CAST(wallets AS BIGINT)) FROM dune.adam_tehc_co.dataset_photon_daily WHERE day = (SELECT MAX(day) FROM dune.adam_tehc_co.dataset_photon_daily) UNION ALL - SELECT 'basedbot', SUM(volume_usd), SUM(CAST(txns AS BIGINT)), SUM(CAST(fees_usd AS DOUBLE)) + SELECT 'basedbot', SUM(volume_usd), SUM(CAST(txns AS BIGINT)), SUM(CAST(fees_usd AS DOUBLE)), SUM(CAST(wallets AS BIGINT)) FROM dune.adam_tehc_co.dataset_basedbot_daily WHERE day = (SELECT MAX(day) FROM dune.adam_tehc_co.dataset_basedbot_daily) UNION ALL - SELECT 'fomo', volume_usd, CAST(txns AS BIGINT), CAST(fees_usd AS DOUBLE) + SELECT 'fomo', volume_usd, CAST(txns AS BIGINT), CAST(fees_usd AS DOUBLE), CAST(wallets AS BIGINT) FROM dune.adam_tehc_co.dataset_fomo_sol_daily WHERE day = (SELECT MAX(day) FROM dune.adam_tehc_co.dataset_fomo_sol_daily) UNION ALL SELECT 'pump-fun', @@ -44,9 +44,10 @@ WITH latest AS ( + COALESCE((SELECT SUM(volume_usd) FROM dune.adam_tehc_co.dataset_pumpfun_relay_daily WHERE day=(SELECT MAX(day) FROM dune.adam_tehc_co.dataset_pumpfun_relay_daily) AND req_class='swap'),0), COALESCE((SELECT SUM(CAST(txns AS BIGINT)) FROM dune.adam_tehc_co.dataset_pumpapp_sol_daily WHERE day=(SELECT MAX(day) FROM dune.adam_tehc_co.dataset_pumpapp_sol_daily)),0) + COALESCE((SELECT SUM(CAST(txns AS BIGINT)) FROM dune.adam_tehc_co.dataset_pumpfun_relay_daily WHERE day=(SELECT MAX(day) FROM dune.adam_tehc_co.dataset_pumpfun_relay_daily) AND req_class='swap'),0), - CAST(0 AS DOUBLE) + CAST(0 AS DOUBLE), + COALESCE((SELECT SUM(CAST(wallets AS BIGINT)) FROM dune.adam_tehc_co.dataset_pumpapp_sol_daily WHERE day=(SELECT MAX(day) FROM dune.adam_tehc_co.dataset_pumpapp_sol_daily)),0) ) -SELECT platform, volume_usd, txns, fees_usd, +SELECT platform, volume_usd, txns, fees_usd, wallets, CASE WHEN txns > 0 THEN volume_usd / txns ELSE NULL END AS avg_trade_usd, CASE WHEN volume_usd > 0 THEN fees_usd / volume_usd * 100 ELSE 0.0 END AS fee_rate_pct FROM latest ORDER BY volume_usd DESC @@ -60,12 +61,13 @@ type duneClient struct { } type duneRow struct { - Platform string `json:"platform"` - VolumeUSD float64 `json:"volume_usd"` - Txns float64 `json:"txns"` - FeesUSD float64 `json:"fees_usd"` + Platform string `json:"platform"` + VolumeUSD float64 `json:"volume_usd"` + Txns float64 `json:"txns"` + FeesUSD float64 `json:"fees_usd"` + Wallets float64 `json:"wallets"` AvgTradeUSD float64 `json:"avg_trade_usd"` - FeeRatePct float64 `json:"fee_rate_pct"` + FeeRatePct float64 `json:"fee_rate_pct"` } func newDuneClient(apiKey string) *duneClient { diff --git a/harnesses/dune-platform-volume/cmd/script/metrics.go b/harnesses/dune-platform-volume/cmd/script/metrics.go index fd7107c7..6b75b315 100644 --- a/harnesses/dune-platform-volume/cmd/script/metrics.go +++ b/harnesses/dune-platform-volume/cmd/script/metrics.go @@ -33,6 +33,11 @@ var ( Help: "Observed fee take rate in percent per platform (fees_usd / volume_usd * 100, Dune community datasets).", }, []string{"platform"}) + platformWallets = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "dune_platform_wallets_24h", + Help: "Unique active wallets per platform for the latest complete day (Dune community datasets).", + }, []string{"platform"}) + platformHealth = prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "dune_platform_volume_health", Help: "1 if this platform returned data in the last Dune poll.", @@ -46,6 +51,7 @@ func init() { platformFeesUSD, platformAvgTrade, platformFeeRate, + platformWallets, platformHealth, ) } @@ -62,6 +68,9 @@ func publishRows(rows []duneRow) { platformAvgTrade.WithLabelValues(r.Platform).Set(r.AvgTradeUSD) } platformFeeRate.WithLabelValues(r.Platform).Set(r.FeeRatePct) + if r.Wallets > 0 { + platformWallets.WithLabelValues(r.Platform).Set(r.Wallets) + } platformHealth.WithLabelValues(r.Platform).Set(1) } } diff --git a/src/app/trading-apps/page.tsx b/src/app/trading-apps/page.tsx index e05f158b..9f937fb7 100644 --- a/src/app/trading-apps/page.tsx +++ b/src/app/trading-apps/page.tsx @@ -22,6 +22,7 @@ const BENCH_SLUGS = [ "solana-dex-volume", "solana-unique-traders", "solana-avg-trade-size", + "trading-platform-wallets", "solana-launchpad-wars", "memecoin-platforms", "app-store-ratings", @@ -69,6 +70,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: true, }, + { + key: "wallets" as const, + label: "Active Wallets", + bench: "trading-platform-wallets", + fmt: fmtCount, + tip: "Unique wallets that traded through the platform in the last complete day (Dune community datasets). Cross-chain for GMGN/Axiom/BasedBot/Terminal. Better signal of real user base than raw tx count.", + higherBetter: true, + }, { key: "feeRate" as const, label: "Fee Rate", @@ -130,6 +139,7 @@ const GROUPS = [ { slug: "solana-dex-volume", title: "DEX volume & protocol revenue" }, { slug: "solana-unique-traders", title: "Swap transactions" }, { slug: "solana-avg-trade-size", title: "Average trade size" }, + { slug: "trading-platform-wallets", title: "Daily active wallets" }, ], }, { @@ -147,11 +157,12 @@ const GROUPS = [ ] as const; export default async function TradingAppsHubPage() { - const [volBench, tradersBench, tradeSizeBench, feeBench, ratingsBench] = + const [volBench, tradersBench, tradeSizeBench, walletsBench, feeBench, ratingsBench] = await Promise.all([ getBenchmark("solana-trading-platform-wars"), getBenchmark("solana-unique-traders"), getBenchmark("solana-avg-trade-size"), + getBenchmark("trading-platform-wallets"), getBenchmark("memecoin-platforms"), getBenchmark("app-store-ratings"), ]); @@ -159,6 +170,7 @@ export default async function TradingAppsHubPage() { const volIdx = indexBySlug(volBench?.results); const tradersIdx = indexBySlug(tradersBench?.results); const tradeSizeIdx = indexBySlug(tradeSizeBench?.results); + const walletsIdx = indexBySlug(walletsBench?.results); const feeIdx = indexBySlug(feeBench?.results); const ratingIdx = indexBySlug(ratingsBench?.results); @@ -168,6 +180,7 @@ export default async function TradingAppsHubPage() { volume: number | null; traders: number | null; tradeSize: number | null; + wallets: number | null; feeRate: number | null; rating: number | null; }; @@ -178,6 +191,7 @@ export default async function TradingAppsHubPage() { volume: volIdx[p.slug] ?? null, traders: tradersIdx[p.slug] ?? null, tradeSize: tradeSizeIdx[p.slug] ?? null, + wallets: walletsIdx[p.slug] ?? null, feeRate: feeIdx[p.slug] ?? null, rating: ratingIdx[p.slug] ?? null, })).sort((a, b) => (b.volume ?? -1) - (a.volume ?? -1)); @@ -431,6 +445,7 @@ export default async function TradingAppsHubPage() { acq. Apr 2025). Swap transaction counts from Dune Analytics (pump.fun: dex-level; terminals: fee-wallet detection). Average trade size = volume ÷ trade count, includes bots and MEV. + Active wallets = unique wallet addresses per platform per day (Dune community datasets); cross-chain for GMGN/Axiom/BasedBot/Terminal. 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{" "}