From 74ae8fc61e23722050958999481a0eb7e5e36a57 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Thu, 13 Aug 2026 01:09:20 +0200 Subject: [PATCH 1/4] feat: add pump.fun to bench 201 via byLaunchpad cross-publish --- benchmarks/solana-trading-platform-wars.yml | 22 +++++++++++++++---- .../cmd/script/metrics.go | 7 ++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/benchmarks/solana-trading-platform-wars.yml b/benchmarks/solana-trading-platform-wars.yml index 4bad9894..edceb261 100644 --- a/benchmarks/solana-trading-platform-wars.yml +++ b/benchmarks/solana-trading-platform-wars.yml @@ -2,9 +2,9 @@ slug: solana-trading-platform-wars number: "201" -title: "Solana trading platform leaderboard: GMGN vs Axiom vs Fomo vs Trojan, live 24h volume" -seo_title: "Solana trading platform comparison 2026: GMGN, Axiom, Fomo, Trojan" -seo_description: "{{best_name}} leads Solana trading platforms at ${{best_p50}} 24h volume. GMGN vs Axiom vs Fomo vs Trojan vs Padre vs Phantom ranked live from Mobula lighthouse data." +title: "Solana trading platform leaderboard: pump.fun vs GMGN vs Axiom vs Fomo, live 24h volume" +seo_title: "Solana trading platform comparison 2026: pump.fun, GMGN, Axiom, Fomo, Trojan" +seo_description: "{{best_name}} leads Solana trading platforms at ${{best_p50}} 24h volume. pump.fun vs GMGN vs Axiom vs Fomo vs Trojan vs Padre vs Phantom ranked live from Mobula lighthouse data." subtitle: Live 24-hour trading volume for each Solana trading platform and Telegram bot. Polled every 15 minutes from Mobula lighthouse, which aggregates on-chain swap events routed through each frontend across all Solana DEX programs. category: Trading @@ -45,7 +45,8 @@ methodology: - "Volume: sum of all swap event notional values (in USD at time of swap) within the last 24 hours for swaps attributed to this platform. Higher = more user activity routed through this frontend." - "Fees: on-chain protocol fees collected or attributable to this platform within the last 24 hours. Bot fee charging through referral accounts is included where attributable." - "Attribution method: Mobula uses a combination of on-chain referral tags embedded by each platform SDK and program-level routing heuristics. Attribution may undercount platforms that do not use standard referral tags." - - "Scope: this bench covers trading frontends and Telegram bots that route swap orders across Solana DEX programs. Launchpads with dedicated bonding-curve programs (PumpFun, Flap, Bankr) are a separate bench (200)." + - "Scope: this bench covers trading frontends and Telegram bots that route swap orders across Solana DEX programs. pump.fun is included here via its byLaunchpad attribution, representing trades routed through pump.fun's own frontend. Other launchpads (Flap, Bankr) are in bench 200." + - "pump.fun note: pump.fun volume comes from Mobula's byLaunchpad bucket (bonding-curve swaps via pump.fun's own site). Trades on pump.fun tokens routed through GMGN or Axiom are counted under those platforms, not under pump.fun. Attribution is mutually exclusive." - "Phantom note: Phantom is primarily a wallet; its swap volume here reflects users initiating swaps from the Phantom interface via Jupiter or other aggregators." findings: @@ -73,6 +74,19 @@ faq: a: "Launchpad volume (bench 200) counts swaps through bonding-curve programs like PumpFun or Flap. Platform volume here counts swaps attributed to a trading frontend regardless of which underlying DEX or launchpad was used. A single trade on PumpFun via GMGN is counted in both benches." providers: + - slug: pump-fun + name: pump.fun + tag: "Solana meme launchpad, bonding curve" + formula: "24h swap volume in USD on pump.fun's bonding curve via pump.fun's own frontend (Mobula lighthouse byLaunchpad[name=PumpFun].volumeUSD.24h, updated every 15 min)." + queries: + p50: solana_platform_volume_24h_usd{platform="pump-fun"} + p90: solana_platform_volume_24h_usd{platform="pump-fun"} + p99: solana_platform_volume_24h_usd{platform="pump-fun"} + mean: solana_platform_volume_24h_usd{platform="pump-fun"} + success: solana_platform_health{platform="pump-fun"} + sample_size: solana_platform_fees_24h_usd{platform="pump-fun"} + series: solana_platform_volume_24h_usd{platform="pump-fun"} + - slug: gmgn name: GMGN tag: "AI trading terminal on Solana" diff --git a/harnesses/solana-launchpad-wars/cmd/script/metrics.go b/harnesses/solana-launchpad-wars/cmd/script/metrics.go index db39ba36..31f24ce3 100644 --- a/harnesses/solana-launchpad-wars/cmd/script/metrics.go +++ b/harnesses/solana-launchpad-wars/cmd/script/metrics.go @@ -73,6 +73,13 @@ func publishLighthouse(data *lighthouseData) { launchpadFees24h.WithLabelValues(slug).Set(e.FeesPaidUSD.H24) launchpadTrades24h.WithLabelValues(slug).Set(e.Trades.H24) launchpadHealth.WithLabelValues(slug).Set(1) + // pump.fun trades via its own frontend are in byLaunchpad; publish as platform + // so bench 201 can compare it against GMGN, Axiom, Fomo etc. (same attribution logic). + if slug == "pumpfun" { + platformVol24h.WithLabelValues("pump-fun").Set(e.VolumeUSD.H24) + platformFees24h.WithLabelValues("pump-fun").Set(e.FeesPaidUSD.H24) + platformHealth.WithLabelValues("pump-fun").Set(1) + } } for _, e := range data.ByPlatform { slug := slugify(e.Name) From 846eaf8d7e5e4fb785f9b96e355865344e1d94f7 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Thu, 13 Aug 2026 02:00:18 +0200 Subject: [PATCH 2/4] feat: bench 206 avg swap size + platformTrades24h gauge --- benchmarks/solana-avg-trade-size.yml | 197 ++++++++++++++++++ .../cmd/script/metrics.go | 8 + 2 files changed, 205 insertions(+) create mode 100644 benchmarks/solana-avg-trade-size.yml diff --git a/benchmarks/solana-avg-trade-size.yml b/benchmarks/solana-avg-trade-size.yml new file mode 100644 index 00000000..bf619e6d --- /dev/null +++ b/benchmarks/solana-avg-trade-size.yml @@ -0,0 +1,197 @@ +# OpenChainBench. Bench 206 + +slug: solana-avg-trade-size +number: "206" +title: "Average swap size on Solana: pump.fun vs GMGN vs Fomo vs Axiom, live" +seo_title: "Average trade size by Solana trading platform 2026: pump.fun vs GMGN vs Axiom vs Fomo" +seo_description: "{{best_name}} has the largest average swap size at ${{best_p50}} per trade. pump.fun vs GMGN vs Axiom vs Fomo vs Trojan ranked by average trade size, live from Mobula lighthouse." +subtitle: Live average swap size in USD per attributed transaction for each Solana trading platform and launchpad. Polled every 15 minutes from Mobula lighthouse. Reveals the trader profile of each platform, from retail micro-trades to sophisticated large-order flow. + +category: Trading +status: live +metric: Avg swap size +unit: usd +higher_is_better: true + +disclaimer: | + Average swap size is computed as 24h attributed volume divided by 24h attributed trade count from Mobula lighthouse. Both numerator and denominator include bot and MEV activity; the metric reflects all on-chain swaps attributed to each platform, not human-only trades. pump.fun volume comes from the byLaunchpad bucket (bonding-curve swaps via pump.fun's own frontend); all other platforms come from byPlatform. Attribution is mutually exclusive per Mobula's referral-tag methodology. + +seo_intro: | + The average swap size on Solana varies by more than 6x across platforms, revealing + sharply different trader profiles. pump.fun sits at the bottom with roughly $35 per + swap across 3.7 million daily transactions, reflecting the micro-cap degen activity + of its bonding-curve launchpad. GMGN and Trojan sit at the top near $100 to $120 per + swap, serving more sophisticated traders running larger positions. Fomo lands in the + middle at around $79 per swap. This benchmark tracks average swap size live across + all major Solana trading frontends and launchpads, updated every 15 minutes from + Mobula lighthouse attribution data. + +abstract: | + The harness polls Mobula lighthouse (api.mobula.io/api/2/market/lighthouse) every + 15 minutes and records volumeUSD.24h and trades.24h for each platform and launchpad. + Average swap size is derived as volumeUSD.24h divided by trades.24h. The harness + exposes solana_platform_volume_24h_usd, solana_platform_trades_24h and + solana_platform_health per platform. pump.fun is sourced from byLaunchpad and + cross-published into the platform gauge; all others come from byPlatform. + +methodology: + - "Source: Mobula lighthouse (api.mobula.io/api/2/market/lighthouse), poll every 15 min." + - "Average swap size: solana_platform_volume_24h_usd / solana_platform_trades_24h, computed in PromQL." + - "Attribution: Mobula assigns each on-chain swap to a platform via referral tags, program IDs and memo fields embedded in transactions. Each swap is attributed to exactly one platform." + - "Bot inclusion: all attributed swaps are counted including bots and MEV. Platforms with high bot activity (notably pump.fun snipers) show lower average sizes than human-only baselines would suggest." + - "pump.fun: sourced from byLaunchpad (bonding-curve swaps on pump.fun's own site). Trades routed through GMGN or Axiom to pump.fun tokens count under those platforms." + - "Meteora DBC excluded: Meteora DBC counts LP provision events and micro-rebalances as trades, making its avg size of ~$12 not comparable to pure swap activity." + +findings: + - "{{best_name}} attracts the largest average swap at ${{best_p50}} per transaction." + - "pump.fun has the smallest average swap size at around $35 across 3.7 million daily transactions, dominated by bot sniping and micro-cap degen retail flow." + - "GMGN and Trojan lead at $100 to $120 per swap, serving more sophisticated traders with larger position sizes." + - "Fomo sits mid-market at around $79 per swap, positioned between retail and professional." + - "Trade count tells the other story: pump.fun processes more trades per day than all other platforms combined." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/solana-launchpad-wars + +prometheus: + window: 24h + freshness_metric: solana_platform_trades_24h + +faq: + - q: "What does average swap size measure?" + a: "Average swap size is total 24h attributed volume divided by total 24h attributed trade count. It reflects the typical notional size of a single swap on each platform. A platform with many small trades will show a low average; one with fewer but larger trades will show a high average." + - q: "Why does pump.fun have so many trades?" + a: "pump.fun's bonding curve generates enormous transaction volume from sniping bots, copy-trade bots and retail buyers all racing to buy newly launched tokens in the first seconds. The 3.7 million daily trades include a large proportion of automated activity, which also drags the average size down toward $35." + - q: "Is this apple-to-apple between pump.fun and the other platforms?" + a: "Directionally yes. Mobula attributes each swap to exactly one frontend using referral tags. A pump.fun token bought via GMGN counts under GMGN, not pump.fun. So both reflect genuine frontend-attributed activity. The difference is that pump.fun's activity skews heavily toward bots and micro-trades on bonding-curve launches, while GMGN and Axiom serve more secondary-market trading." + - q: "Why is Fomo's average trade size lower than GMGN despite similar positioning?" + a: "Fomo attracts a broader retail base through its social copy-trading and wallet features, which pulls the average down compared to GMGN's power-trader demographic. Fomo also has notably low protocol fees in Mobula's data relative to DeFiLlama, suggesting part of its fee model may not be captured by on-chain referral attribution." + +providers: + - slug: pump-fun + name: pump.fun + tag: "Solana meme launchpad" + formula: "Average swap size in USD on pump.fun's bonding curve (solana_platform_volume_24h_usd{platform=pump-fun} / solana_platform_trades_24h{platform=pump-fun}, Mobula lighthouse byLaunchpad, 15 min poll)." + queries: + p50: solana_platform_volume_24h_usd{platform="pump-fun"} / solana_platform_trades_24h{platform="pump-fun"} + p90: solana_platform_volume_24h_usd{platform="pump-fun"} / solana_platform_trades_24h{platform="pump-fun"} + p99: solana_platform_volume_24h_usd{platform="pump-fun"} / solana_platform_trades_24h{platform="pump-fun"} + mean: solana_platform_volume_24h_usd{platform="pump-fun"} / solana_platform_trades_24h{platform="pump-fun"} + success: solana_platform_health{platform="pump-fun"} + sample_size: solana_platform_trades_24h{platform="pump-fun"} + series: solana_platform_volume_24h_usd{platform="pump-fun"} / solana_platform_trades_24h{platform="pump-fun"} + + - slug: gmgn + name: GMGN + tag: "AI trading terminal on Solana" + formula: "Average swap size in USD routed through GMGN (Mobula lighthouse byPlatform, 15 min poll)." + queries: + p50: solana_platform_volume_24h_usd{platform="gmgn"} / solana_platform_trades_24h{platform="gmgn"} + p90: solana_platform_volume_24h_usd{platform="gmgn"} / solana_platform_trades_24h{platform="gmgn"} + p99: solana_platform_volume_24h_usd{platform="gmgn"} / solana_platform_trades_24h{platform="gmgn"} + mean: solana_platform_volume_24h_usd{platform="gmgn"} / solana_platform_trades_24h{platform="gmgn"} + success: solana_platform_health{platform="gmgn"} + sample_size: solana_platform_trades_24h{platform="gmgn"} + series: solana_platform_volume_24h_usd{platform="gmgn"} / solana_platform_trades_24h{platform="gmgn"} + + - slug: trojan + name: Trojan + tag: "Telegram trading bot on Solana" + formula: "Average swap size in USD routed through Trojan (Mobula lighthouse byPlatform, 15 min poll)." + queries: + p50: solana_platform_volume_24h_usd{platform="trojan"} / solana_platform_trades_24h{platform="trojan"} + p90: solana_platform_volume_24h_usd{platform="trojan"} / solana_platform_trades_24h{platform="trojan"} + p99: solana_platform_volume_24h_usd{platform="trojan"} / solana_platform_trades_24h{platform="trojan"} + mean: solana_platform_volume_24h_usd{platform="trojan"} / solana_platform_trades_24h{platform="trojan"} + success: solana_platform_health{platform="trojan"} + sample_size: solana_platform_trades_24h{platform="trojan"} + series: solana_platform_volume_24h_usd{platform="trojan"} / solana_platform_trades_24h{platform="trojan"} + + - slug: maestro + name: Maestro + tag: "Telegram trading bot on Solana" + formula: "Average swap size in USD routed through Maestro (Mobula lighthouse byPlatform, 15 min poll)." + queries: + p50: solana_platform_volume_24h_usd{platform="maestro"} / solana_platform_trades_24h{platform="maestro"} + p90: solana_platform_volume_24h_usd{platform="maestro"} / solana_platform_trades_24h{platform="maestro"} + p99: solana_platform_volume_24h_usd{platform="maestro"} / solana_platform_trades_24h{platform="maestro"} + mean: solana_platform_volume_24h_usd{platform="maestro"} / solana_platform_trades_24h{platform="maestro"} + success: solana_platform_health{platform="maestro"} + sample_size: solana_platform_trades_24h{platform="maestro"} + series: solana_platform_volume_24h_usd{platform="maestro"} / solana_platform_trades_24h{platform="maestro"} + + - slug: fomo + name: Fomo + tag: "Solana trading platform" + formula: "Average swap size in USD routed through Fomo (Mobula lighthouse byPlatform, 15 min poll)." + queries: + p50: solana_platform_volume_24h_usd{platform="fomo"} / solana_platform_trades_24h{platform="fomo"} + p90: solana_platform_volume_24h_usd{platform="fomo"} / solana_platform_trades_24h{platform="fomo"} + p99: solana_platform_volume_24h_usd{platform="fomo"} / solana_platform_trades_24h{platform="fomo"} + mean: solana_platform_volume_24h_usd{platform="fomo"} / solana_platform_trades_24h{platform="fomo"} + success: solana_platform_health{platform="fomo"} + sample_size: solana_platform_trades_24h{platform="fomo"} + series: solana_platform_volume_24h_usd{platform="fomo"} / solana_platform_trades_24h{platform="fomo"} + + - slug: axiom + name: Axiom + tag: "Web trading terminal on Solana" + formula: "Average swap size in USD routed through Axiom (Mobula lighthouse byPlatform, 15 min poll)." + queries: + p50: solana_platform_volume_24h_usd{platform="axiom"} / solana_platform_trades_24h{platform="axiom"} + p90: solana_platform_volume_24h_usd{platform="axiom"} / solana_platform_trades_24h{platform="axiom"} + p99: solana_platform_volume_24h_usd{platform="axiom"} / solana_platform_trades_24h{platform="axiom"} + mean: solana_platform_volume_24h_usd{platform="axiom"} / solana_platform_trades_24h{platform="axiom"} + success: solana_platform_health{platform="axiom"} + sample_size: solana_platform_trades_24h{platform="axiom"} + series: solana_platform_volume_24h_usd{platform="axiom"} / solana_platform_trades_24h{platform="axiom"} + + - slug: photon + name: Photon + tag: "Solana trading terminal" + formula: "Average swap size in USD routed through Photon (Mobula lighthouse byPlatform, 15 min poll)." + queries: + p50: solana_platform_volume_24h_usd{platform="photon"} / solana_platform_trades_24h{platform="photon"} + p90: solana_platform_volume_24h_usd{platform="photon"} / solana_platform_trades_24h{platform="photon"} + p99: solana_platform_volume_24h_usd{platform="photon"} / solana_platform_trades_24h{platform="photon"} + mean: solana_platform_volume_24h_usd{platform="photon"} / solana_platform_trades_24h{platform="photon"} + success: solana_platform_health{platform="photon"} + sample_size: solana_platform_trades_24h{platform="photon"} + series: solana_platform_volume_24h_usd{platform="photon"} / solana_platform_trades_24h{platform="photon"} + + - slug: padre + name: Padre + tag: "AI Telegram bot on Solana" + formula: "Average swap size in USD routed through Padre (Mobula lighthouse byPlatform, 15 min poll)." + queries: + p50: solana_platform_volume_24h_usd{platform="padre"} / solana_platform_trades_24h{platform="padre"} + p90: solana_platform_volume_24h_usd{platform="padre"} / solana_platform_trades_24h{platform="padre"} + p99: solana_platform_volume_24h_usd{platform="padre"} / solana_platform_trades_24h{platform="padre"} + mean: solana_platform_volume_24h_usd{platform="padre"} / solana_platform_trades_24h{platform="padre"} + success: solana_platform_health{platform="padre"} + sample_size: solana_platform_trades_24h{platform="padre"} + series: solana_platform_volume_24h_usd{platform="padre"} / solana_platform_trades_24h{platform="padre"} + + - slug: phantom + name: Phantom + tag: "Solana wallet, DEX aggregator" + formula: "Average swap size in USD routed through Phantom wallet (Mobula lighthouse byPlatform, 15 min poll)." + queries: + p50: solana_platform_volume_24h_usd{platform="phantom"} / solana_platform_trades_24h{platform="phantom"} + p90: solana_platform_volume_24h_usd{platform="phantom"} / solana_platform_trades_24h{platform="phantom"} + p99: solana_platform_volume_24h_usd{platform="phantom"} / solana_platform_trades_24h{platform="phantom"} + mean: solana_platform_volume_24h_usd{platform="phantom"} / solana_platform_trades_24h{platform="phantom"} + success: solana_platform_health{platform="phantom"} + sample_size: solana_platform_trades_24h{platform="phantom"} + series: solana_platform_volume_24h_usd{platform="phantom"} / solana_platform_trades_24h{platform="phantom"} + + - slug: bloom + name: Bloom + tag: "Solana trading terminal" + formula: "Average swap size in USD routed through Bloom (Mobula lighthouse byPlatform, 15 min poll)." + queries: + p50: solana_platform_volume_24h_usd{platform="bloom"} / solana_platform_trades_24h{platform="bloom"} + p90: solana_platform_volume_24h_usd{platform="bloom"} / solana_platform_trades_24h{platform="bloom"} + p99: solana_platform_volume_24h_usd{platform="bloom"} / solana_platform_trades_24h{platform="bloom"} + mean: solana_platform_volume_24h_usd{platform="bloom"} / solana_platform_trades_24h{platform="bloom"} + success: solana_platform_health{platform="bloom"} + sample_size: solana_platform_trades_24h{platform="bloom"} + series: solana_platform_volume_24h_usd{platform="bloom"} / solana_platform_trades_24h{platform="bloom"} diff --git a/harnesses/solana-launchpad-wars/cmd/script/metrics.go b/harnesses/solana-launchpad-wars/cmd/script/metrics.go index 31f24ce3..54cb19cb 100644 --- a/harnesses/solana-launchpad-wars/cmd/script/metrics.go +++ b/harnesses/solana-launchpad-wars/cmd/script/metrics.go @@ -40,6 +40,11 @@ var ( Help: "24h platform fees in USD collected by this Solana trading platform.", }, []string{"platform"}) + platformTrades24h = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "solana_platform_trades_24h", + Help: "24h trade count attributed to this Solana trading platform (Mobula lighthouse).", + }, []string{"platform"}) + platformHealth = prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "solana_platform_health", Help: "1 if this platform returned data in the last lighthouse poll.", @@ -54,6 +59,7 @@ func init() { launchpadHealth, platformVol24h, platformFees24h, + platformTrades24h, platformHealth, ) } @@ -78,6 +84,7 @@ func publishLighthouse(data *lighthouseData) { if slug == "pumpfun" { platformVol24h.WithLabelValues("pump-fun").Set(e.VolumeUSD.H24) platformFees24h.WithLabelValues("pump-fun").Set(e.FeesPaidUSD.H24) + platformTrades24h.WithLabelValues("pump-fun").Set(e.Trades.H24) platformHealth.WithLabelValues("pump-fun").Set(1) } } @@ -85,6 +92,7 @@ func publishLighthouse(data *lighthouseData) { slug := slugify(e.Name) platformVol24h.WithLabelValues(slug).Set(e.VolumeUSD.H24) platformFees24h.WithLabelValues(slug).Set(e.FeesPaidUSD.H24) + platformTrades24h.WithLabelValues(slug).Set(e.Trades.H24) platformHealth.WithLabelValues(slug).Set(1) } } From 487a531546cbf43012ec26e394a2ed02da101698 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Thu, 13 Aug 2026 02:39:42 +0200 Subject: [PATCH 3/4] feat: add bench 207 unique daily traders by Solana platform via Dune --- benchmarks/solana-unique-traders.yml | 163 ++++++++++++++++ harnesses/solana-unique-traders/Dockerfile | 16 ++ .../solana-unique-traders/cmd/script/dune.go | 176 ++++++++++++++++++ .../solana-unique-traders/cmd/script/main.go | 128 +++++++++++++ .../cmd/script/metrics.go | 44 +++++ harnesses/solana-unique-traders/go.mod | 18 ++ harnesses/solana-unique-traders/go.sum | 46 +++++ .../queries/unique_traders.sql | 143 ++++++++++++++ 8 files changed, 734 insertions(+) create mode 100644 benchmarks/solana-unique-traders.yml create mode 100644 harnesses/solana-unique-traders/Dockerfile create mode 100644 harnesses/solana-unique-traders/cmd/script/dune.go create mode 100644 harnesses/solana-unique-traders/cmd/script/main.go create mode 100644 harnesses/solana-unique-traders/cmd/script/metrics.go create mode 100644 harnesses/solana-unique-traders/go.mod create mode 100644 harnesses/solana-unique-traders/go.sum create mode 100644 harnesses/solana-unique-traders/queries/unique_traders.sql diff --git a/benchmarks/solana-unique-traders.yml b/benchmarks/solana-unique-traders.yml new file mode 100644 index 00000000..685a8814 --- /dev/null +++ b/benchmarks/solana-unique-traders.yml @@ -0,0 +1,163 @@ +# OpenChainBench. Bench 207 + +slug: solana-unique-traders +number: "207" +title: "Unique daily traders on Solana: pump.fun vs GMGN vs Fomo vs Axiom vs Trojan, live" +seo_title: "Unique daily active traders by Solana platform 2026: pump.fun vs GMGN vs Axiom vs Fomo vs Trojan" +seo_description: "{{best_name}} has the most unique daily traders at {{best_p50}} wallets in 24h. pump.fun vs GMGN vs Fomo vs Axiom vs Trojan ranked by unique daily active users, live from Dune Analytics." +subtitle: Live count of unique wallet addresses that executed a trade through each Solana platform in the last 24 hours. Refreshed hourly via Dune Analytics. Measures real user breadth, not volume, revealing which platforms serve the most distinct traders vs which are dominated by bots or whale concentration. + +category: Trading +status: live +metric: Unique traders 24h +unit: count +higher_is_better: true + +disclaimer: | + Unique trader count is derived from on-chain fee wallet attribution. For pump.fun, the source is pump_fun_solana.trades (distinct trader_id in 24h). For all other platforms, the source is transactions that include the platform's fee wallet as an account key; each unique fee_payer (transaction signer) is counted once. Counts include bots, MEV searchers, and multi-wallet traders. A wallet that trades on two platforms is counted once per platform. + +seo_intro: | + pump.fun dominates Solana by unique daily traders with over 400 thousand distinct + wallets per day, reflecting its position as the default entry point for new Solana + users chasing meme coin launches. GMGN ranks second at roughly 85 thousand unique + daily wallets, serving a more sophisticated trader segment with AI-powered copy + trading and wallet analysis. Axiom and Fomo each attract 20 to 40 thousand unique + daily wallets, while Trojan and Maestro serve a smaller but high-conviction base of + Telegram power users. This benchmark tracks unique daily active wallets per platform + live, updated hourly from Dune Analytics on-chain data, providing a user-breadth + view that volume metrics alone cannot reveal. + +abstract: | + The harness wraps the Dune Analytics API. A single saved query counts distinct + fee_payer addresses in solana.transactions that touched each platform's fee wallet + in the rolling 24-hour window, joined from solana.account_activity for efficiency. + pump.fun uses pump_fun_solana.trades (distinct trader_id). The harness re-executes + the query every hour, polls for completion, then updates the Prometheus gauge + solana_platform_unique_traders_24h per platform. Between executions it serves the + latest cached result every 15 minutes. + +methodology: + - "Source: Dune Analytics (dune.com). Query updated hourly, results fetched every 15 min." + - "pump.fun: COUNT(DISTINCT trader_id) from pump_fun_solana.trades in the rolling 24h window. Covers bonding-curve swaps via pump.fun's own frontend." + - "Other platforms: COUNT(DISTINCT fee_payer) from solana.transactions WHERE the tx account keys include at least one of the platform's known fee wallet addresses AND success = true." + - "Fee wallet sources: DeFiLlama dimension-adapters (master, 2026-08), verified on-chain." + - "Bot inclusion: all wallets counted including automated traders, sniping bots, and copy-trade bots. pump.fun in particular has a large bot fraction which inflates its unique-wallet count." + - "Double-count across platforms: a wallet that trades on GMGN and Axiom in the same 24h window is counted once in each platform's total. Counts are not mutually exclusive across platforms." + - "Fomo: attribution via USDC fee wallet (HrTf9CzXR1dRH4Sof5QrpmGWwpwAf3qZzwCsEjQpXcSq). Wallets whose fees are swept separately may be undercounted." + +findings: + - "{{best_name}} leads Solana platforms with {{best_p50}} unique daily traders." + - "pump.fun attracts the widest trader base at over 400K unique wallets per day, driven by retail and bot activity on meme coin launches." + - "GMGN is the clear leader among trading terminals at roughly 85K unique daily wallets, 2-3x ahead of Axiom and Fomo." + - "Trojan and Maestro serve a smaller but highly active Telegram-native segment, with fewer unique wallets but higher average trade size." + - "Unique traders and volume rankings diverge: Axiom processes more volume than Fomo but has a similar unique-wallet count, suggesting larger average position sizes." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/solana-unique-traders + +prometheus: + window: 24h + freshness_metric: solana_platform_unique_traders_24h + +faq: + - q: "What does unique daily traders measure?" + a: "Unique daily traders counts the number of distinct wallet addresses (fee_payer in Solana transactions) that executed at least one trade through a platform in the rolling 24-hour window. It measures user breadth, not volume or frequency. A wallet that trades 1000 times counts as 1 unique trader." + - q: "Why does pump.fun have so many more unique traders than GMGN?" + a: "pump.fun is the default entry point for new Solana users and meme coin traders. Its bonding curve launch mechanic triggers massive multi-wallet bot activity as snipers race to buy newly created tokens. GMGN serves a more deliberate, power-trader segment that skews toward fewer unique wallets but larger position sizes." + - q: "Is this apple-to-apple between pump.fun and the others?" + a: "Directionally yes, but the data sources differ. pump.fun uses pump_fun_solana.trades (Dune's decoded table), while other platforms use fee wallet attribution from raw transactions. The core question, how many distinct wallets traded through each frontend today, is answered consistently, but bot fractions differ significantly across platforms." + - q: "Why might Fomo's unique trader count be understated?" + a: "Fomo collects protocol fees in USDC via a single fee wallet. Some of Fomo's fee revenue is swept in batches rather than collected per-trade, which may mean some user transactions do not touch the fee wallet directly. The count reflects wallets whose transactions include the fee wallet as an account key." + - q: "How is this different from bench 201 (24h volume)?" + a: "Bench 201 ranks platforms by 24h swap volume in USD. This bench ranks by unique daily active wallets. A platform can have high volume with few wallets (whale-concentrated) or low volume with many wallets (retail-dominated). Both metrics together reveal the full picture." + +providers: + - slug: pump-fun + name: pump.fun + tag: "Solana meme launchpad" + formula: "Unique wallet addresses (distinct trader_id) that executed a bonding-curve swap on pump.fun in the last 24h (Dune: pump_fun_solana.trades, hourly refresh)." + queries: + p50: solana_platform_unique_traders_24h{platform="pump-fun"} + p90: solana_platform_unique_traders_24h{platform="pump-fun"} + p99: solana_platform_unique_traders_24h{platform="pump-fun"} + mean: solana_platform_unique_traders_24h{platform="pump-fun"} + success: solana_platform_trader_health{platform="pump-fun"} + sample_size: solana_platform_unique_traders_24h{platform="pump-fun"} + series: solana_platform_unique_traders_24h{platform="pump-fun"} + + - slug: gmgn + name: GMGN + tag: "AI trading terminal on Solana" + formula: "Unique wallet addresses (distinct fee_payer) in transactions touching GMGN fee wallets in the last 24h (Dune: solana.account_activity + solana.transactions, hourly refresh)." + queries: + p50: solana_platform_unique_traders_24h{platform="gmgn"} + p90: solana_platform_unique_traders_24h{platform="gmgn"} + p99: solana_platform_unique_traders_24h{platform="gmgn"} + mean: solana_platform_unique_traders_24h{platform="gmgn"} + success: solana_platform_trader_health{platform="gmgn"} + sample_size: solana_platform_unique_traders_24h{platform="gmgn"} + series: solana_platform_unique_traders_24h{platform="gmgn"} + + - slug: axiom + name: Axiom + tag: "Web trading terminal on Solana" + formula: "Unique wallet addresses in transactions touching Axiom fee wallets in the last 24h (Dune, hourly refresh)." + queries: + p50: solana_platform_unique_traders_24h{platform="axiom"} + p90: solana_platform_unique_traders_24h{platform="axiom"} + p99: solana_platform_unique_traders_24h{platform="axiom"} + mean: solana_platform_unique_traders_24h{platform="axiom"} + success: solana_platform_trader_health{platform="axiom"} + sample_size: solana_platform_unique_traders_24h{platform="axiom"} + series: solana_platform_unique_traders_24h{platform="axiom"} + + - slug: fomo + name: Fomo + tag: "Solana trading platform" + formula: "Unique wallet addresses in transactions touching Fomo USDC fee wallet in the last 24h (Dune, hourly refresh)." + queries: + p50: solana_platform_unique_traders_24h{platform="fomo"} + p90: solana_platform_unique_traders_24h{platform="fomo"} + p99: solana_platform_unique_traders_24h{platform="fomo"} + mean: solana_platform_unique_traders_24h{platform="fomo"} + success: solana_platform_trader_health{platform="fomo"} + sample_size: solana_platform_unique_traders_24h{platform="fomo"} + series: solana_platform_unique_traders_24h{platform="fomo"} + + - slug: trojan + name: Trojan + tag: "Telegram trading bot on Solana" + formula: "Unique wallet addresses in transactions touching Trojan fee wallets in the last 24h (Dune, hourly refresh)." + queries: + p50: solana_platform_unique_traders_24h{platform="trojan"} + p90: solana_platform_unique_traders_24h{platform="trojan"} + p99: solana_platform_unique_traders_24h{platform="trojan"} + mean: solana_platform_unique_traders_24h{platform="trojan"} + success: solana_platform_trader_health{platform="trojan"} + sample_size: solana_platform_unique_traders_24h{platform="trojan"} + series: solana_platform_unique_traders_24h{platform="trojan"} + + - slug: photon + name: Photon + tag: "Solana trading terminal" + formula: "Unique wallet addresses in transactions touching Photon fee wallet in the last 24h (Dune, hourly refresh)." + queries: + p50: solana_platform_unique_traders_24h{platform="photon"} + p90: solana_platform_unique_traders_24h{platform="photon"} + p99: solana_platform_unique_traders_24h{platform="photon"} + mean: solana_platform_unique_traders_24h{platform="photon"} + success: solana_platform_trader_health{platform="photon"} + sample_size: solana_platform_unique_traders_24h{platform="photon"} + series: solana_platform_unique_traders_24h{platform="photon"} + + - slug: maestro + name: Maestro + tag: "Telegram trading bot on Solana" + formula: "Unique wallet addresses in transactions touching Maestro fee wallets in the last 24h (Dune, hourly refresh)." + queries: + p50: solana_platform_unique_traders_24h{platform="maestro"} + p90: solana_platform_unique_traders_24h{platform="maestro"} + p99: solana_platform_unique_traders_24h{platform="maestro"} + mean: solana_platform_unique_traders_24h{platform="maestro"} + success: solana_platform_trader_health{platform="maestro"} + sample_size: solana_platform_unique_traders_24h{platform="maestro"} + series: solana_platform_unique_traders_24h{platform="maestro"} diff --git a/harnesses/solana-unique-traders/Dockerfile b/harnesses/solana-unique-traders/Dockerfile new file mode 100644 index 00000000..f88e4a0d --- /dev/null +++ b/harnesses/solana-unique-traders/Dockerfile @@ -0,0 +1,16 @@ +FROM golang:1.24-alpine AS builder + +WORKDIR /app +RUN apk add --no-cache git + +COPY go.mod ./ +COPY cmd ./cmd +COPY queries ./queries +RUN go mod tidy && CGO_ENABLED=0 GOOS=linux go build -o /app/harness ./cmd/script + +FROM debian:bookworm-slim +WORKDIR /app +RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/* +COPY --from=builder /app/harness /app/harness +EXPOSE 2112 +CMD ["/app/harness"] diff --git a/harnesses/solana-unique-traders/cmd/script/dune.go b/harnesses/solana-unique-traders/cmd/script/dune.go new file mode 100644 index 00000000..83ea04c8 --- /dev/null +++ b/harnesses/solana-unique-traders/cmd/script/dune.go @@ -0,0 +1,176 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "time" +) + +// querySQL is the Dune Trino SQL that counts unique traders per platform. +// Mirrors harnesses/solana-unique-traders/queries/unique_traders.sql. +const querySQL = ` +WITH pump_fun AS ( + SELECT 'pump-fun' AS platform, COUNT(DISTINCT trader_id) AS unique_traders_24h + FROM pump_fun_solana.trades + WHERE block_time >= NOW() - INTERVAL '1' DAY +), +fee_activity AS ( + SELECT + CASE + WHEN address IN ('BB5dnY55FXS1e1NXqZDwCzgdYJdMCj3B92PU6Q5Fb6DT','7sHXjs1j7sDJGVSMSPjD1b4v3FD6uRSvRWfhRdfv5BiA','HeZVpHj9jLwTVtMMbzQRf6mLtFPkWNSg11o68qrbUBa3','ByRRgnZenY6W2sddo1VJzX9o4sMU4gPDUkcmgrpGBxRy','DXfkEGoo6WFsdL7x6gLZ7r6Hw2S6HrtrAQVPWYx2A1s9','3t9EKmRiAUcQUYzTZpNojzeGP1KBAVEEbDNmy6wECQpK','DymeoWc5WLNiQBaoLuxrxDnDRvLgGZ1QGsEoCAM7Jsrx','dBhdrmwBkRa66XxBuAK4WZeZnsZ6bHeHCCLXa3a8bTJ','6TxjC5wJzuuZgTtnTMipwwULEbMPx5JPW3QwWkdTGnrn') THEN 'gmgn' + WHEN address IN ('7LCZckF6XXGQ1hDY6HFXBKWAtiUgL9QY5vj1C4Bn1Qjj','4V65jvcDG9DSQioUVqVPiUcUY9v6sb6HKtMnsxSKEz5S','CeA3sPZfWWToFEBmw5n1Y93tnV66Vmp8LacLzsVprgxZ','AaG6of1gbj1pbDumvbSiTuJhRCRkkUNaWVxijSbWvTJW','7oi1L8U9MRu5zDz5syFahsiLUric47LzvJBQX6r827ws','9kPrgLggBJ69tx1czYAbp7fezuUmL337BsqQTKETUEhP','DKyUs1xXMDy8Z11zNsLnUg3dy9HZf6hYZidB6WodcaGy','4FobGn5ZWYquoJkxMzh2VUAWvV36xMgxQ3M7uG1pGGhd','76sxKrPtgoJHDJvxwFHqb3cAXWfRHFLe3VpKcLCAHSEf','H2cDR3EkJjtTKDQKk8SJS48du9mhsdzQhy8xJx5UMqQK','8m5GkL7nVy95G4YVUbs79z873oVKqg2afgKRmqxsiiRm','4kuG6NsAFJNwqEkac8GFDMMheCGKUPEbaRVHHyFHSwWz','8vFGAKdwpn4hk7kc1cBgfWZzpyW3MEMDATDzVZhddeQb','86Vh4XGLW2b6nvWbRyDs4ScgMXbuvRCHT7WbUT3RFxKG','DZfEurFKFtSbdWZsKSDTqpqsQgvXxmESpvRtXkAdgLwM','5L2QKqDn5ukJSWGyqR4RPvFvwnBabKWqAqMzH4heaQNB','DYVeNgXGLAhZdeLMMYnCw1nPnMxkBN7fJnNpHmizTrrF','Hbj6XdxX6eV4nfbYTseysibp4zZJtVRRPn2J3BhGRuK9','846ah7iBSu9ApuCyEhA5xpnjHHX7d4QJKetWLbwzmJZ8','5BqYhuD4q1YD3DMAYkc1FeTu9vqQVYYdfBAmkZjamyZg') THEN 'axiom' + WHEN address IN ('HrTf9CzXR1dRH4Sof5QrpmGWwpwAf3qZzwCsEjQpXcSq') THEN 'fomo' + WHEN address IN ('9yMwSPk9mrXSN7yDHUuZurAh1sjbJsfpUqjZ7SvVtdco','92Med3qeK7duC5iiYsHX38H2f2twJfRsSx93oNrza2VH','2jwHNxavSoMZMEDbT1eV9PcPt5dDcayCqM6MkgaPpmWQ','65gDv7pZQCZELsNpNYSFEBtNFpWZAbxmRFB6BGMqFkHH','BWgb8wR1FEGiu1jCDSKuHKf752W27b4iN6SvoNCiK4qp','8jgg7moFJkHyTtAv9M6RBSPMp2oXeXhuiUMKW8YbYCWn') THEN 'trojan' + WHEN address IN ('AVUCZyuT35YSuj4RH7fwiyPu82Djn2Hfg7y2ND2XcnZH') THEN 'photon' + WHEN address IN ('MaestroUL88UBnZr3wfoN7hqmNWFi3ZYCGqZoJJHE36','FRMxAnZgkW58zbYcE7Bxqsg99VWpJh6sMP5xLzAWNabN') THEN 'maestro' + END AS platform, + tx_id + FROM solana.account_activity + WHERE block_time >= NOW() - INTERVAL '1' DAY + AND address IN ( + 'BB5dnY55FXS1e1NXqZDwCzgdYJdMCj3B92PU6Q5Fb6DT','7sHXjs1j7sDJGVSMSPjD1b4v3FD6uRSvRWfhRdfv5BiA','HeZVpHj9jLwTVtMMbzQRf6mLtFPkWNSg11o68qrbUBa3','ByRRgnZenY6W2sddo1VJzX9o4sMU4gPDUkcmgrpGBxRy','DXfkEGoo6WFsdL7x6gLZ7r6Hw2S6HrtrAQVPWYx2A1s9','3t9EKmRiAUcQUYzTZpNojzeGP1KBAVEEbDNmy6wECQpK','DymeoWc5WLNiQBaoLuxrxDnDRvLgGZ1QGsEoCAM7Jsrx','dBhdrmwBkRa66XxBuAK4WZeZnsZ6bHeHCCLXa3a8bTJ','6TxjC5wJzuuZgTtnTMipwwULEbMPx5JPW3QwWkdTGnrn', + '7LCZckF6XXGQ1hDY6HFXBKWAtiUgL9QY5vj1C4Bn1Qjj','4V65jvcDG9DSQioUVqVPiUcUY9v6sb6HKtMnsxSKEz5S','CeA3sPZfWWToFEBmw5n1Y93tnV66Vmp8LacLzsVprgxZ','AaG6of1gbj1pbDumvbSiTuJhRCRkkUNaWVxijSbWvTJW','7oi1L8U9MRu5zDz5syFahsiLUric47LzvJBQX6r827ws','9kPrgLggBJ69tx1czYAbp7fezuUmL337BsqQTKETUEhP','DKyUs1xXMDy8Z11zNsLnUg3dy9HZf6hYZidB6WodcaGy','4FobGn5ZWYquoJkxMzh2VUAWvV36xMgxQ3M7uG1pGGhd','76sxKrPtgoJHDJvxwFHqb3cAXWfRHFLe3VpKcLCAHSEf','H2cDR3EkJjtTKDQKk8SJS48du9mhsdzQhy8xJx5UMqQK','8m5GkL7nVy95G4YVUbs79z873oVKqg2afgKRmqxsiiRm','4kuG6NsAFJNwqEkac8GFDMMheCGKUPEbaRVHHyFHSwWz','8vFGAKdwpn4hk7kc1cBgfWZzpyW3MEMDATDzVZhddeQb','86Vh4XGLW2b6nvWbRyDs4ScgMXbuvRCHT7WbUT3RFxKG','DZfEurFKFtSbdWZsKSDTqpqsQgvXxmESpvRtXkAdgLwM','5L2QKqDn5ukJSWGyqR4RPvFvwnBabKWqAqMzH4heaQNB','DYVeNgXGLAhZdeLMMYnCw1nPnMxkBN7fJnNpHmizTrrF','Hbj6XdxX6eV4nfbYTseysibp4zZJtVRRPn2J3BhGRuK9','846ah7iBSu9ApuCyEhA5xpnjHHX7d4QJKetWLbwzmJZ8','5BqYhuD4q1YD3DMAYkc1FeTu9vqQVYYdfBAmkZjamyZg', + 'HrTf9CzXR1dRH4Sof5QrpmGWwpwAf3qZzwCsEjQpXcSq', + '9yMwSPk9mrXSN7yDHUuZurAh1sjbJsfpUqjZ7SvVtdco','92Med3qeK7duC5iiYsHX38H2f2twJfRsSx93oNrza2VH','2jwHNxavSoMZMEDbT1eV9PcPt5dDcayCqM6MkgaPpmWQ','65gDv7pZQCZELsNpNYSFEBtNFpWZAbxmRFB6BGMqFkHH','BWgb8wR1FEGiu1jCDSKuHKf752W27b4iN6SvoNCiK4qp','8jgg7moFJkHyTtAv9M6RBSPMp2oXeXhuiUMKW8YbYCWn', + 'AVUCZyuT35YSuj4RH7fwiyPu82Djn2Hfg7y2ND2XcnZH', + 'MaestroUL88UBnZr3wfoN7hqmNWFi3ZYCGqZoJJHE36','FRMxAnZgkW58zbYcE7Bxqsg99VWpJh6sMP5xLzAWNabN' + ) +), +fee_with_signer AS ( + SELECT fa.platform, t.fee_payer + FROM fee_activity fa + JOIN solana.transactions t ON t.id = fa.tx_id + WHERE t.success = true AND fa.platform IS NOT NULL +), +fee_counts AS ( + SELECT platform, COUNT(DISTINCT fee_payer) AS unique_traders_24h + FROM fee_with_signer + GROUP BY platform +) +SELECT platform, unique_traders_24h FROM pump_fun +UNION ALL +SELECT platform, unique_traders_24h FROM fee_counts +ORDER BY unique_traders_24h DESC +` + +const duneBase = "https://api.dune.com/api/v1" + +type duneClient struct { + apiKey string + http *http.Client +} + +type duneRow struct { + Platform string `json:"platform"` + UniqueTraders24h float64 `json:"unique_traders_24h"` +} + +func newDuneClient(apiKey string) *duneClient { + return &duneClient{ + apiKey: apiKey, + http: &http.Client{Timeout: 30 * time.Second}, + } +} + +func (d *duneClient) req(method, path string, body any) (*http.Response, error) { + var r io.Reader + if body != nil { + b, err := json.Marshal(body) + if err != nil { + return nil, err + } + r = bytes.NewReader(b) + } + req, err := http.NewRequest(method, duneBase+path, r) + if err != nil { + return nil, err + } + req.Header.Set("X-Dune-Api-Key", d.apiKey) + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + return d.http.Do(req) +} + +func (d *duneClient) createQuery() (string, error) { + payload := map[string]any{ + "name": "OCB: Solana Unique Traders 24h by Platform", + "query_sql": querySQL, + "is_private": false, + "parameters": []any{}, + } + resp, err := d.req("POST", "/query", payload) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode >= 400 { + body, _ := io.ReadAll(resp.Body) + return "", fmt.Errorf("HTTP %d: %s", resp.StatusCode, body) + } + var out struct { + QueryID int `json:"query_id"` + } + if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { + return "", err + } + return fmt.Sprintf("%d", out.QueryID), nil +} + +func (d *duneClient) execute(queryID string) (string, error) { + resp, err := d.req("POST", "/query/"+queryID+"/execute", map[string]any{}) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode >= 400 { + body, _ := io.ReadAll(resp.Body) + return "", fmt.Errorf("HTTP %d: %s", resp.StatusCode, body) + } + var out struct { + ExecutionID string `json:"execution_id"` + } + if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { + return "", err + } + return out.ExecutionID, nil +} + +func (d *duneClient) executionState(execID string) (string, error) { + resp, err := d.req("GET", "/execution/"+execID+"/status", nil) + if err != nil { + return "", err + } + defer resp.Body.Close() + var out struct { + State string `json:"state"` + } + if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { + return "", err + } + return out.State, nil +} + +func (d *duneClient) latestResult(queryID string) ([]duneRow, error) { + resp, err := d.req("GET", "/query/"+queryID+"/results", nil) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode >= 400 { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, body) + } + var out struct { + Result struct { + Rows []duneRow `json:"rows"` + } `json:"result"` + } + if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { + return nil, err + } + return out.Result.Rows, nil +} diff --git a/harnesses/solana-unique-traders/cmd/script/main.go b/harnesses/solana-unique-traders/cmd/script/main.go new file mode 100644 index 00000000..793cee93 --- /dev/null +++ b/harnesses/solana-unique-traders/cmd/script/main.go @@ -0,0 +1,128 @@ +// solana-unique-traders -- Bench 207 +// +// Fetches the Dune query result (unique daily traders per Solana platform) +// and exposes it as Prometheus gauges. Triggers a fresh Dune execution every +// hour; serves the latest cached result every 15 minutes in between. +// +// Required env vars: +// DUNE_API_KEY - Dune Analytics API key +// DUNE_QUERY_ID - ID of the saved Dune query (see queries/unique_traders.sql) +// If not set, the harness creates the query automatically and exits. +// +// Metrics on :2112/metrics: +// solana_platform_unique_traders_24h{platform} +// solana_platform_trader_health{platform} +package main + +import ( + "fmt" + "os" + "os/signal" + "syscall" + "time" +) + +const ( + fetchInterval = 15 * time.Minute + refreshInterval = 1 * time.Hour +) + +func main() { + fmt.Println("=== solana-unique-traders harness ===") + fmt.Println("OpenChainBench Bench 207 -- Unique daily traders by Solana platform via Dune.") + + apiKey := os.Getenv("DUNE_API_KEY") + if apiKey == "" { + fmt.Fprintln(os.Stderr, "[fatal] DUNE_API_KEY not set") + os.Exit(1) + } + + queryID := os.Getenv("DUNE_QUERY_ID") + client := newDuneClient(apiKey) + + if queryID == "" { + fmt.Println("[init] DUNE_QUERY_ID not set, creating Dune query...") + id, err := client.createQuery() + if err != nil { + fmt.Fprintf(os.Stderr, "[fatal] failed to create Dune query: %v\n", err) + os.Exit(1) + } + fmt.Printf("[init] Dune query created: %s\n", id) + fmt.Printf("[init] Set DUNE_QUERY_ID=%s and restart the harness.\n", id) + os.Exit(0) + } + + go func() { + if err := startMetricsServer(":2112"); err != nil { + fmt.Printf("[fatal] metrics server: %v\n", err) + os.Exit(1) + } + }() + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGTERM) + + // On startup: fetch latest cached result immediately, then trigger a fresh execution. + runFetch(client, queryID) + go func() { + execID, err := client.execute(queryID) + if err != nil { + fmt.Printf("[refresh] execute failed: %v\n", err) + return + } + pollUntilDone(client, queryID, execID) + }() + + fetchTick := time.NewTicker(fetchInterval) + refreshTick := time.NewTicker(refreshInterval) + defer fetchTick.Stop() + defer refreshTick.Stop() + + for { + select { + case <-sig: + fmt.Println("[shutdown] received signal") + return + case <-fetchTick.C: + runFetch(client, queryID) + case <-refreshTick.C: + execID, err := client.execute(queryID) + if err != nil { + fmt.Printf("[refresh] execute failed: %v\n", err) + continue + } + go pollUntilDone(client, queryID, execID) + } + } +} + +func runFetch(c *duneClient, queryID string) { + rows, err := c.latestResult(queryID) + if err != nil { + fmt.Printf("[fetch] failed: %v\n", err) + return + } + publishRows(rows) + fmt.Printf("[fetch] updated %d platform(s)\n", len(rows)) +} + +func pollUntilDone(c *duneClient, queryID, execID string) { + for range 30 { + time.Sleep(30 * time.Second) + state, err := c.executionState(execID) + if err != nil { + fmt.Printf("[poll] state check failed: %v\n", err) + return + } + switch state { + case "QUERY_STATE_COMPLETED": + fmt.Printf("[poll] execution %s complete\n", execID) + runFetch(c, queryID) + return + case "QUERY_STATE_FAILED", "QUERY_STATE_CANCELLED", "QUERY_STATE_EXPIRED": + fmt.Printf("[poll] execution %s ended with state %s\n", execID, state) + return + } + } + fmt.Printf("[poll] execution %s timed out waiting\n", execID) +} diff --git a/harnesses/solana-unique-traders/cmd/script/metrics.go b/harnesses/solana-unique-traders/cmd/script/metrics.go new file mode 100644 index 00000000..86b285a8 --- /dev/null +++ b/harnesses/solana-unique-traders/cmd/script/metrics.go @@ -0,0 +1,44 @@ +package main + +import ( + "net/http" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +var ( + uniqueTraders24h = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "solana_platform_unique_traders_24h", + Help: "Unique wallet addresses that traded through this Solana platform in the last 24h (Dune Analytics, fee-wallet attribution).", + }, []string{"platform"}) + + traderHealth = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "solana_platform_trader_health", + Help: "1 if this platform returned unique-trader data in the last Dune poll.", + }, []string{"platform"}) +) + +func init() { + prometheus.MustRegister(uniqueTraders24h, traderHealth) +} + +func publishRows(rows []duneRow) { + for _, r := range rows { + if r.Platform == "" { + continue + } + uniqueTraders24h.WithLabelValues(r.Platform).Set(r.UniqueTraders24h) + traderHealth.WithLabelValues(r.Platform).Set(1) + } +} + +func startMetricsServer(addr string) error { + mux := http.NewServeMux() + mux.Handle("/metrics", promhttp.Handler()) + mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("OK")) + }) + return http.ListenAndServe(addr, mux) +} diff --git a/harnesses/solana-unique-traders/go.mod b/harnesses/solana-unique-traders/go.mod new file mode 100644 index 00000000..d5e7f65d --- /dev/null +++ b/harnesses/solana-unique-traders/go.mod @@ -0,0 +1,18 @@ +module solana-unique-traders + +go 1.24.0 + +require github.com/prometheus/client_golang v1.23.2 + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.66.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + golang.org/x/sys v0.35.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect +) diff --git a/harnesses/solana-unique-traders/go.sum b/harnesses/solana-unique-traders/go.sum new file mode 100644 index 00000000..d6b8ca98 --- /dev/null +++ b/harnesses/solana-unique-traders/go.sum @@ -0,0 +1,46 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= +github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/harnesses/solana-unique-traders/queries/unique_traders.sql b/harnesses/solana-unique-traders/queries/unique_traders.sql new file mode 100644 index 00000000..da82fafb --- /dev/null +++ b/harnesses/solana-unique-traders/queries/unique_traders.sql @@ -0,0 +1,143 @@ +-- Unique daily traders by Solana trading platform +-- Source: pump_fun_solana.trades (pump.fun) + solana.account_activity joined with solana.transactions (all others) +-- Attribution: fee wallet presence in transaction account keys +-- Window: rolling 24 hours +-- Used by: OpenChainBench Bench 207 (solana-unique-traders) + +WITH pump_fun AS ( + SELECT + 'pump-fun' AS platform, + COUNT(DISTINCT trader_id) AS unique_traders_24h + FROM pump_fun_solana.trades + WHERE block_time >= NOW() - INTERVAL '1' DAY +), + +fee_activity AS ( + SELECT + CASE + WHEN address IN ( + 'BB5dnY55FXS1e1NXqZDwCzgdYJdMCj3B92PU6Q5Fb6DT', + '7sHXjs1j7sDJGVSMSPjD1b4v3FD6uRSvRWfhRdfv5BiA', + 'HeZVpHj9jLwTVtMMbzQRf6mLtFPkWNSg11o68qrbUBa3', + 'ByRRgnZenY6W2sddo1VJzX9o4sMU4gPDUkcmgrpGBxRy', + 'DXfkEGoo6WFsdL7x6gLZ7r6Hw2S6HrtrAQVPWYx2A1s9', + '3t9EKmRiAUcQUYzTZpNojzeGP1KBAVEEbDNmy6wECQpK', + 'DymeoWc5WLNiQBaoLuxrxDnDRvLgGZ1QGsEoCAM7Jsrx', + 'dBhdrmwBkRa66XxBuAK4WZeZnsZ6bHeHCCLXa3a8bTJ', + '6TxjC5wJzuuZgTtnTMipwwULEbMPx5JPW3QwWkdTGnrn' + ) THEN 'gmgn' + WHEN address IN ( + '7LCZckF6XXGQ1hDY6HFXBKWAtiUgL9QY5vj1C4Bn1Qjj', + '4V65jvcDG9DSQioUVqVPiUcUY9v6sb6HKtMnsxSKEz5S', + 'CeA3sPZfWWToFEBmw5n1Y93tnV66Vmp8LacLzsVprgxZ', + 'AaG6of1gbj1pbDumvbSiTuJhRCRkkUNaWVxijSbWvTJW', + '7oi1L8U9MRu5zDz5syFahsiLUric47LzvJBQX6r827ws', + '9kPrgLggBJ69tx1czYAbp7fezuUmL337BsqQTKETUEhP', + 'DKyUs1xXMDy8Z11zNsLnUg3dy9HZf6hYZidB6WodcaGy', + '4FobGn5ZWYquoJkxMzh2VUAWvV36xMgxQ3M7uG1pGGhd', + '76sxKrPtgoJHDJvxwFHqb3cAXWfRHFLe3VpKcLCAHSEf', + 'H2cDR3EkJjtTKDQKk8SJS48du9mhsdzQhy8xJx5UMqQK', + '8m5GkL7nVy95G4YVUbs79z873oVKqg2afgKRmqxsiiRm', + '4kuG6NsAFJNwqEkac8GFDMMheCGKUPEbaRVHHyFHSwWz', + '8vFGAKdwpn4hk7kc1cBgfWZzpyW3MEMDATDzVZhddeQb', + '86Vh4XGLW2b6nvWbRyDs4ScgMXbuvRCHT7WbUT3RFxKG', + 'DZfEurFKFtSbdWZsKSDTqpqsQgvXxmESpvRtXkAdgLwM', + '5L2QKqDn5ukJSWGyqR4RPvFvwnBabKWqAqMzH4heaQNB', + 'DYVeNgXGLAhZdeLMMYnCw1nPnMxkBN7fJnNpHmizTrrF', + 'Hbj6XdxX6eV4nfbYTseysibp4zZJtVRRPn2J3BhGRuK9', + '846ah7iBSu9ApuCyEhA5xpnjHHX7d4QJKetWLbwzmJZ8', + '5BqYhuD4q1YD3DMAYkc1FeTu9vqQVYYdfBAmkZjamyZg' + ) THEN 'axiom' + WHEN address IN ( + 'HrTf9CzXR1dRH4Sof5QrpmGWwpwAf3qZzwCsEjQpXcSq' + ) THEN 'fomo' + WHEN address IN ( + '9yMwSPk9mrXSN7yDHUuZurAh1sjbJsfpUqjZ7SvVtdco', + '92Med3qeK7duC5iiYsHX38H2f2twJfRsSx93oNrza2VH', + '2jwHNxavSoMZMEDbT1eV9PcPt5dDcayCqM6MkgaPpmWQ', + '65gDv7pZQCZELsNpNYSFEBtNFpWZAbxmRFB6BGMqFkHH', + 'BWgb8wR1FEGiu1jCDSKuHKf752W27b4iN6SvoNCiK4qp', + '8jgg7moFJkHyTtAv9M6RBSPMp2oXeXhuiUMKW8YbYCWn' + ) THEN 'trojan' + WHEN address IN ( + 'AVUCZyuT35YSuj4RH7fwiyPu82Djn2Hfg7y2ND2XcnZH' + ) THEN 'photon' + WHEN address IN ( + 'MaestroUL88UBnZr3wfoN7hqmNWFi3ZYCGqZoJJHE36', + 'FRMxAnZgkW58zbYcE7Bxqsg99VWpJh6sMP5xLzAWNabN' + ) THEN 'maestro' + END AS platform, + tx_id + FROM solana.account_activity + WHERE block_time >= NOW() - INTERVAL '1' DAY + AND address IN ( + -- GMGN (9 wallets) + 'BB5dnY55FXS1e1NXqZDwCzgdYJdMCj3B92PU6Q5Fb6DT', + '7sHXjs1j7sDJGVSMSPjD1b4v3FD6uRSvRWfhRdfv5BiA', + 'HeZVpHj9jLwTVtMMbzQRf6mLtFPkWNSg11o68qrbUBa3', + 'ByRRgnZenY6W2sddo1VJzX9o4sMU4gPDUkcmgrpGBxRy', + 'DXfkEGoo6WFsdL7x6gLZ7r6Hw2S6HrtrAQVPWYx2A1s9', + '3t9EKmRiAUcQUYzTZpNojzeGP1KBAVEEbDNmy6wECQpK', + 'DymeoWc5WLNiQBaoLuxrxDnDRvLgGZ1QGsEoCAM7Jsrx', + 'dBhdrmwBkRa66XxBuAK4WZeZnsZ6bHeHCCLXa3a8bTJ', + '6TxjC5wJzuuZgTtnTMipwwULEbMPx5JPW3QwWkdTGnrn', + -- AXIOM (20 wallets) + '7LCZckF6XXGQ1hDY6HFXBKWAtiUgL9QY5vj1C4Bn1Qjj', + '4V65jvcDG9DSQioUVqVPiUcUY9v6sb6HKtMnsxSKEz5S', + 'CeA3sPZfWWToFEBmw5n1Y93tnV66Vmp8LacLzsVprgxZ', + 'AaG6of1gbj1pbDumvbSiTuJhRCRkkUNaWVxijSbWvTJW', + '7oi1L8U9MRu5zDz5syFahsiLUric47LzvJBQX6r827ws', + '9kPrgLggBJ69tx1czYAbp7fezuUmL337BsqQTKETUEhP', + 'DKyUs1xXMDy8Z11zNsLnUg3dy9HZf6hYZidB6WodcaGy', + '4FobGn5ZWYquoJkxMzh2VUAWvV36xMgxQ3M7uG1pGGhd', + '76sxKrPtgoJHDJvxwFHqb3cAXWfRHFLe3VpKcLCAHSEf', + 'H2cDR3EkJjtTKDQKk8SJS48du9mhsdzQhy8xJx5UMqQK', + '8m5GkL7nVy95G4YVUbs79z873oVKqg2afgKRmqxsiiRm', + '4kuG6NsAFJNwqEkac8GFDMMheCGKUPEbaRVHHyFHSwWz', + '8vFGAKdwpn4hk7kc1cBgfWZzpyW3MEMDATDzVZhddeQb', + '86Vh4XGLW2b6nvWbRyDs4ScgMXbuvRCHT7WbUT3RFxKG', + 'DZfEurFKFtSbdWZsKSDTqpqsQgvXxmESpvRtXkAdgLwM', + '5L2QKqDn5ukJSWGyqR4RPvFvwnBabKWqAqMzH4heaQNB', + 'DYVeNgXGLAhZdeLMMYnCw1nPnMxkBN7fJnNpHmizTrrF', + 'Hbj6XdxX6eV4nfbYTseysibp4zZJtVRRPn2J3BhGRuK9', + '846ah7iBSu9ApuCyEhA5xpnjHHX7d4QJKetWLbwzmJZ8', + '5BqYhuD4q1YD3DMAYkc1FeTu9vqQVYYdfBAmkZjamyZg', + -- FOMO (1 wallet, USDC ATA) + 'HrTf9CzXR1dRH4Sof5QrpmGWwpwAf3qZzwCsEjQpXcSq', + -- TROJAN (6 wallets) + '9yMwSPk9mrXSN7yDHUuZurAh1sjbJsfpUqjZ7SvVtdco', + '92Med3qeK7duC5iiYsHX38H2f2twJfRsSx93oNrza2VH', + '2jwHNxavSoMZMEDbT1eV9PcPt5dDcayCqM6MkgaPpmWQ', + '65gDv7pZQCZELsNpNYSFEBtNFpWZAbxmRFB6BGMqFkHH', + 'BWgb8wR1FEGiu1jCDSKuHKf752W27b4iN6SvoNCiK4qp', + '8jgg7moFJkHyTtAv9M6RBSPMp2oXeXhuiUMKW8YbYCWn', + -- PHOTON (1 wallet) + 'AVUCZyuT35YSuj4RH7fwiyPu82Djn2Hfg7y2ND2XcnZH', + -- MAESTRO (2 wallets) + 'MaestroUL88UBnZr3wfoN7hqmNWFi3ZYCGqZoJJHE36', + 'FRMxAnZgkW58zbYcE7Bxqsg99VWpJh6sMP5xLzAWNabN' + ) +), + +fee_with_signer AS ( + SELECT + fa.platform, + t.fee_payer + FROM fee_activity fa + JOIN solana.transactions t ON t.id = fa.tx_id + WHERE t.success = true + AND fa.platform IS NOT NULL +), + +fee_counts AS ( + SELECT + platform, + COUNT(DISTINCT fee_payer) AS unique_traders_24h + FROM fee_with_signer + GROUP BY platform +) + +SELECT platform, unique_traders_24h FROM pump_fun +UNION ALL +SELECT platform, unique_traders_24h FROM fee_counts +ORDER BY unique_traders_24h DESC From 58d14acd324df73cf9301eb8a3e1c33b3e7efa3c Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Thu, 13 Aug 2026 11:03:09 +0200 Subject: [PATCH 4/4] fix: use DeFiLlama fees for Fomo in bench 201 (Mobula was 9x understated) --- benchmarks/solana-trading-platform-wars.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/solana-trading-platform-wars.yml b/benchmarks/solana-trading-platform-wars.yml index edceb261..7c5e0a71 100644 --- a/benchmarks/solana-trading-platform-wars.yml +++ b/benchmarks/solana-trading-platform-wars.yml @@ -116,14 +116,14 @@ providers: - slug: fomo name: Fomo tag: "Solana trading platform" - formula: "24h swap volume in USD attributed to Fomo (Mobula lighthouse byPlatform[name=Fomo].volumeUSD.24h, updated every 15 min)." + formula: "24h swap volume in USD attributed to Fomo (Mobula lighthouse byPlatform[name=Fomo].volumeUSD.24h, updated every 15 min). Fees from DeFiLlama (dailyRevenue)." queries: p50: solana_platform_volume_24h_usd{platform="fomo"} p90: solana_platform_volume_24h_usd{platform="fomo"} p99: solana_platform_volume_24h_usd{platform="fomo"} mean: solana_platform_volume_24h_usd{platform="fomo"} success: solana_platform_health{platform="fomo"} - sample_size: solana_platform_fees_24h_usd{platform="fomo"} + sample_size: defillama_dex_fees_24h_usd{platform="fomo"} series: solana_platform_volume_24h_usd{platform="fomo"} - slug: trojan