I Built a Fake Market Detector Using DEX Trade Fees

Published: (February 27, 2026 at 12:27 PM EST)
12 min read
Source: Dev.to

Source: Dev.to

Source: Dev.to – I built a fake market detector using DEX trade fees (1LOC)

🚀 How I Discovered a Wash‑Traded Solana Token (and Built a Scanner)

Last month I almost bought a Solana token that showed $2 M in daily volume.
Everything looked legit – an active chart, thousands of transactions, and a growing holder count.

When I dug into the fees, I saw the token had generated $12 in total platform fees on $2 M of volume – a 0.0006 % fee ratio. In a real market you’d expect something between 0.5 % – 2 %.

It turned out to be a wash‑traded token: bots were trading back‑and‑forth with themselves. I would have lost everything.

That experience inspired me to build a market‑legitimacy scanner powered by fee data. Below is a cleaned‑up guide on how it works and how you can build one too.


Table of Contents

  1. What is Wash Trading?
  2. Why Fees Reveal the Truth
  3. Data Sources & Tools
  4. Step‑by‑Step Scanner Build
  5. Interpreting the Results
  6. Further Enhancements

What is Wash Trading?

  • Definition – The practice of repeatedly buying and selling the same asset to create the illusion of activity and liquidity.
  • Motivation – Inflate perceived demand, attract unsuspecting investors, or manipulate price charts.
  • Typical pattern – Very high transaction count, high volume, but extremely low or zero fees collected by the underlying protocol.

Why Fees Reveal the Truth

MetricTypical MarketWash‑Traded Scenario
Volume‑to‑Fee Ratio0.5 % – 2 % (e.g., $1 M volume → $5 k–$20 k fees)< 0.01 % (e.g., $2 M volume → $12 fees)
Fee per TransactionConsistent with protocol fee scheduleNear‑zero, often below the minimum fee unit
Holder Growth vs. Fee GrowthCorrelatedDivergent

When the fee ratio falls far below the expected range, it’s a strong red flag that the volume is artificial.


Data Sources & Tools

ComponentRecommended ProviderAPI Example
On‑chain token dataSolana RPC, Helius, The GraphGET https://api.helius.xyz/v0/tokens/{mint}
Historical feesSerum DEX, Raydium, Orca subgraphquery { token(id: "...") { totalFees } }
Transaction countSolscan, Solana BeachGET https://public-api.solscan.io/token/transactions?tokenAddress=...
Programming languagePython (requests, pandas) or JavaScript (node‑fetch, ethers)

Tip: Cache responses for 5‑10 minutes to avoid rate‑limit issues while scanning many tokens.


Step‑by‑Step Scanner Build

Below is a minimal Python implementation. Adjust the endpoints and keys to match your provider.

import requests
import pandas as pd
from datetime import datetime, timedelta

# -------------------------------------------------
# CONFIGURATION
# -------------------------------------------------
API_KEY = "YOUR_HELIUS_API_KEY"
BASE_URL = "https://api.helius.xyz/v0"
MIN_FEE_RATIO = 0.005   # 0.5 %
MAX_FEE_RATIO = 0.02    # 2 %

# -------------------------------------------------
# HELPER FUNCTIONS
# -------------------------------------------------
def get_token_info(mint: str) -> dict:
    """Fetch basic token metadata."""
    url = f"{BASE_URL}/tokens/{mint}"
    resp = requests.get(url, headers={"Authorization": f"Bearer {API_KEY}"})
    resp.raise_for_status()
    return resp.json()

def get_daily_volume(mint: str, days: int = 1) -> float:
    """Sum of trade volume for the last `days` days."""
    end = datetime.utcnow()
    start = end - timedelta(days=days)
    url = f"{BASE_URL}/tokens/{mint}/volume?start={int(start.timestamp())}&end={int(end.timestamp())}"
    resp = requests.get(url, headers={"Authorization": f"Bearer {API_KEY}"})
    resp.raise_for_status()
    data = resp.json()
    return sum(item["volumeUsd"] for item in data["records"])

def get_total_fees(mint: str) -> float:
    """Total fees collected by the protocol for this token."""
    url = f"{BASE_URL}/tokens/{mint}/fees"
    resp = requests.get(url, headers={"Authorization": f"Bearer {API_KEY}"})
    resp.raise_for_status()
    return resp.json()["totalFeesUsd"]

def fee_ratio(volume_usd: float, fees_usd: float) -> float:
    """Calculate fee‑to‑volume ratio (as a decimal)."""
    if volume_usd == 0:
        return 0.0
    return fees_usd / volume_usd

# -------------------------------------------------
# MAIN LOGIC
# -------------------------------------------------
def scan_token(mint: str) -> dict:
    """Run the legitimacy check for a single token."""
    meta = get_token_info(mint)
    volume = get_daily_volume(mint, days=1)
    fees = get_total_fees(mint)

    ratio = fee_ratio(volume, fees)

    result = {
        "symbol": meta.get("symbol", "—"),
        "name": meta.get("name", "—"),
        "daily_volume_usd": volume,
        "total_fees_usd": fees,
        "fee_ratio": ratio,
        "status": "✅ Legitimate" if MIN_FEE_RATIO <= ratio <= MAX_FEE_RATIO else "⚠️ Suspicious",
    }
    return result

# -------------------------------------------------
# EXAMPLE USAGE
# -------------------------------------------------
if __name__ == "__main__":
    # Example list – replace with your own token list
    token_mints = [
        "So11111111111111111111111111111111111111112",  # SOL (native)
        "9n4nbM75f5Ui33ZbPYXn59EwSgE8CGsHtAeTH5YFeJ9E",  # Example SPL token
    ]

    reports = [scan_token(mint) for mint in token_mints]
    df = pd.DataFrame(reports)
    print(df.to_markdown(index=False))

What the script does

  1. Fetches token metadata, daily volume, and total fees from Helius (or any RPC provider).
  2. Computes the fee‑to‑volume ratio.
  3. Flags the token as Suspicious when the ratio falls outside the typical 0.5 % – 2 % window.

You can expand the scanner by:

  • Adding a moving‑average of fee ratios over 7‑day windows.
  • Pulling transaction‑count data and checking for abnormal spikes.
  • Integrating with a Discord/Telegram bot for real‑time alerts.

Interpreting the Results

StatusInterpretation
✅ LegitimateFee ratio sits comfortably within market norms → volume likely genuine.
⚠️ SuspiciousRatio is far too low (or zero). Investigate further: check token holder distribution, look for repeated address pairs, or examine the token’s smart‑contract code.
❓ No DataAPI returned 0 volume or fees. Could be a brand‑new token or an API limitation – treat with caution.

A single flag isn’t proof of fraud, but it’s a strong indicator that you should dig deeper before committing capital.


Further Enhancements

IdeaBenefit
Multi‑chain support (Ethereum, BSC, Avalanche)Detect wash‑trading across ecosystems.
Machine‑learning classifier using features like fee ratio, holder churn, and transaction graph patternsReduce false positives/negatives.
On‑chain alert contract that emits an event when a token’s fee ratio drops below a thresholdEnables decentralized monitoring services.
UI Dashboard (React + Chart.js) to visualize fee ratios over time for dozens of tokensMakes the data instantly actionable for traders.

TL;DR

  • Wash‑traded tokens often have massive volume but tiny fees.
  • A fee‑to‑volume ratio between 0.5 % – 2 % is typical for healthy Solana markets.
  • Anything dramatically lower should raise a red flag and trigger deeper due‑diligence.

By automating this check, you can protect yourself (and others) from costly scams. Happy scanning!

🧩 Every DEX Trade Has Three Cost Components (Most Platforms Hide)

ComponentWhat it isTypical cost (Solana)
Gas FeeNetwork cost to validators~ $0.35
Platform FeeAggregator cut (Axiom, GMGN, etc.)~ 1 % of trade
MEV FeePriority tips, Jito bundles~ $2 – $5

Key insight: On a $2,500 trade, the platform fee ($25) accounts for 90 % of the total cost, yet most interfaces only display the gas fee.

Real traders pay real fees.
Wash traders control the entire flow and often trade directly through DEX contracts to avoid platform fees entirely. This asymmetry is the detection signal.

📊 Market‑Manipulation Stats

  • Chainalysis 2025 report: More than 42 % of tokens launched in 2024 were listed on a DEX, and wash‑trading remains one of the most prevalent manipulation tactics.
  • Solidus Labs: Self‑trading on DEX pools is far more common than expected.

📈 Fee‑to‑Volume Ratio

[ \text{Fee‑to‑Volume Ratio}= \frac{\text{Fees Paid (24 h)}}{\text{Volume (24 h)}} \times 100 ]

RatioSignal
0.5 % – 2.5 %✅ Healthy, organic trading
0.1 % – 0.5 %⚠️ Investigate further
5 %⚠️ Unusual fee structure

Example: A token with $500 K daily volume but only $50 in fees → 0.01 % ratio → Something is very wrong.

📡 Pulling Fee Data from Mobula

Most crypto APIs only return volume and price. Mobula provides a full fee breakdown (gas, platform, MEV) for each trade and aggregates it per token.


Token Details (REST)

curl -X GET "https://api.mobula.io/api/2/token/details?blockchain=solana&address=TOKEN_ADDRESS" \
     -H "Authorization: YOUR_API_KEY"

Sample response

{
  "data": {
    "symbol": "EXAMPLE",
    "totalFeesPaidUSD": 125000.5,
    "feesPaid24hUSD": 4450.0,
    "feesPaid1hUSD": 185.0,
    "feesPaid5minUSD": 15.5,
    "volume24hUSD": 890000.0
  }
}
FieldDescription
symbolToken ticker symbol.
totalFeesPaidUSDCumulative fees paid (USD) since token launch.
feesPaid24hUSDFees paid in the last 24 hours (USD).
feesPaid1hUSDFees paid in the last hour (USD).
feesPaid5minUSDFees paid in the last 5 minutes (USD).
volume24hUSDTrading volume in the last 24 hours (USD).

Recent Trades (REST)

curl -X GET "https://api.mobula.io/api/2/token/trades?blockchain=solana&address=TOKEN_ADDRESS&limit=50" \
     -H "Authorization: YOUR_API_KEY"

Each trade object includes the following fee fields:

FieldDescription
totalFeesUSDSum of all fee components (USD).
gasFeesUSDNetwork transaction (gas) cost (USD).
platformFeesUSDAggregator / frontend fee (USD).
mevFeesUSDPriority / MEV‑related cost (USD).

💻 TypeScript Scanner – Flag Suspicious Fee Patterns

The script below queries Mobula for a token’s 24‑hour volume and fees, computes the fee‑to‑volume ratio and classifies the token as:

VerdictMeaning
ORGANICFee distribution looks normal.
SUSPICIOUSRatio is unusually low for a high‑volume token (possible wash‑trading).
WARNINGRatio is either zero with high volume (potential honeypot) or abnormally high (needs investigation).

📂 Full Source (cleaned & type‑safe)

import { MobulaClient } from "@mobula_labs/sdk";

interface FeeAnalysis {
  symbol: string;
  volume24h: number;
  fees24h: number;
  feeRatio: number; // % of fees over volume
  verdict: "ORGANIC" | "SUSPICIOUS" | "WARNING";
  reason: string;
}

/**
 * Scan a single token on a given blockchain.
 *
 * @param blockchain – e.g. "solana", "ethereum", …
 * @param address    – token contract address (or mint on Solana)
 * @returns          FeeAnalysis describing the token’s fee pattern
 */
async function scanToken(
  blockchain: string,
  address: string
): Promise<FeeAnalysis> {
  const client = new MobulaClient({ apiKey: process.env.MOBULA_API_KEY! });

  const details = await client.fetchTokenDetails({ blockchain, address });

  const volume = details.volume24hUSD ?? 0;
  const fees = details.feesPaid24hUSD ?? 0;
  const symbol = details.symbol ?? "UNKNOWN";
  const ratio = volume > 0 ? (fees / volume) * 100 : 0; // % value

  // -----------------------------------------------------------------
  // 1️⃣ Zero fees + active volume → possible honeypot
  // -----------------------------------------------------------------
  if (fees === 0 && volume > 10_000) {
    return {
      symbol,
      volume24h: volume,
      fees24h: fees,
      feeRatio: 0,
      verdict: "WARNING",
      reason: "Zero fees with active volume — possible honeypot",
    };
  }

  // -----------------------------------------------------------------
  // 2️⃣ Very low ratio + high volume → wash‑trading signal
  // -----------------------------------------------------------------
  // Expected fee ratio for most DEXs is roughly 0.5 %–2 %
  if (ratio < 0.5 && volume > 100_000) {
    return {
      symbol,
      volume24h: volume,
      fees24h: fees,
      feeRatio: ratio,
      verdict: "SUSPICIOUS",
      reason: `Fee ratio ${ratio.toFixed(4)}% is far below expected 0.5‑2%`,
    };
  }

  // -----------------------------------------------------------------
  // 3️⃣ Abnormally high fees → investigate
  // -----------------------------------------------------------------
  if (ratio > 5) {
    return {
      symbol,
      volume24h: volume,
      fees24h: fees,
      feeRatio: ratio,
      verdict: "WARNING",
      reason: `Fee ratio ${ratio.toFixed(2)}% is unusually high`,
    };
  }

  // -----------------------------------------------------------------
  // 4️⃣ Default – looks normal
  // -----------------------------------------------------------------
  return {
    symbol,
    volume24h: volume,
    fees24h: fees,
    feeRatio: ratio,
    verdict: "ORGANIC",
    reason: "Fee distribution looks normal",
  };
}

// ---------------------------------------------------------------------
// Scan multiple tokens and pretty‑print the results
// ---------------------------------------------------------------------
async function main() {
  const tokens = [
    "9BB6NFEcjBCtnNLFko2FqVQBq8HHM13kCyYcdQbgpump", // example Solana mint
    "8J69rbLTzWWgUJziFY8jeu5tDQEPBwUz4pKBMr5rpump",
  ];

  for (const addr of tokens) {
    const result = await scanToken("solana", addr);
    const icon =
      result.verdict === "ORGANIC"
        ? "✅"
        : result.verdict === "SUSPICIOUS"
        ? "⚠️"
        : "🚨";

    console.log(`\n${icon} ${result.symbol} [${result.verdict}]`);
    console.log(`   Volume 24h: $${result.volume24h.toLocaleString()}`);
    console.log(`   Fees 24h:   $${result.fees24h.toLocaleString()}`);
    console.log(`   Fee ratio:  ${result.feeRatio.toFixed(4)}%`);
    console.log(`   → ${result.reason}`);
  }
}

main().catch((e) => {
  console.error("❌ Unexpected error:", e);
  process.exit(1);
});

🖥️ Sample Output

✅ FARTCOIN [ORGANIC]
   Volume 24h: $12,450,000
   Fees 24h:   $89,200
   Fee ratio:  0.7166%
   → Fee distribution looks normal

🚨 SCAMTOKEN [WARNING]
   Volume 24h: $2,100,000
   Fees 24h:   $12
   Fee ratio:  0.0006%
   → Zero fees with active volume — possible honeypot

Feel free to extend the tokens array, adjust the thresholds, or integrate the scanner into a larger monitoring pipeline. Happy hunting!

🔔 Live Alerting via Mobula’s Trade Stream

const ws = new WebSocket("wss://general-api-v2.mobula.io");

ws.onopen = () => {
  ws.send(
    JSON.stringify({
      type: "fast-trades",
      authorization: process.env.MOBULA_API_KEY,
      payload: {
        tokens: [{ address: "TOKEN_ADDRESS", chainId: "solana:solana" }],
      },
    })
  );
};

ws.onmessage = (event) => {
  const trade = JSON.parse(event.data);
  const feePercent = (trade.totalFeesUSD / trade.amountUSD) * 100;

  // Apply the same logic as `scanToken` here for real‑time alerts
  // …
};

🎉 TL;DR

  1. Collect fee breakdowns (gas, platform, MEV) from Mobula.
  2. Compute the fee‑to‑volume ratio for the last 24 h.
  3. Flag tokens based on the ratio thresholds (honeypot, wash‑trade, abnormal fees).
  4. Optionally listen to the live trade stream for instant alerts.

With just a few lines of code you can separate genuine liquidity from manipulative wash‑trading and protect yourself (and your community) from costly scams. Happy scanning!

// Calculate fee percentage
const feePercent = (trade.totalFeesUSD / trade.baseTokenAmountUSD) * 100;

// Alert on suspicious zero‑fee large trades
if (feePercent > 0 && trade.baseTokenAmountUSD > 1000) {
  console.log(
    `🚨 SUSPICIOUS: $${trade.baseTokenAmountUSD} trade with $${trade.totalFeesUSD} fees`
  );
}

Fee data flips the script on how we evaluate tokens

MetricCan be faked?Reliability
Volume✅ Yes (wash trading)Low
Transaction count✅ Yes (spam txs)Low
Fees paid❌ No (costs real money)High
Unique traders⚠️ Partially (sybil)Medium

Tokens with rapidly increasing feesPaid1hUSD indicate genuine interest from real traders willing to pay premium costs. That’s a signal you can’t manufacture cheaply.

0 views
Back to Blog

Related posts

Read more »

Dragon Ball Color Correction Process [pdf]

I’m sorry, but the content you provided is a PDF file in binary format, not HTML or plain text. I’m unable to extract and convert the article’s text from this b...