$npx -y skills add agiprolabs/claude-trading-skills --skill sybil-detectionCoordinated wallet cluster detection, wash trading identification, and fake activity analysis for Solana tokens
| 1 | # Sybil Detection — Coordinated Wallet & Fake Activity Analysis |
| 2 | |
| 3 | Sybil attacks in Solana token markets involve a single entity operating many wallets to create the illusion of organic activity. This skill covers detecting coordinated wallet clusters, wash trading, bundled transactions, and fake holder inflation — critical for evaluating whether a token's metrics reflect real demand or manufactured signals. |
| 4 | |
| 5 | ## Why Sybil Detection Matters |
| 6 | |
| 7 | Token markets on Solana are rife with manufactured signals: |
| 8 | |
| 9 | - **Inflated holder counts**: 500 "holders" that are really 10 entities with 50 wallets each |
| 10 | - **Fake volume**: Wash trading between self-controlled wallets to simulate demand |
| 11 | - **Artificial social proof**: Many wallets holding small amounts to appear broadly distributed |
| 12 | - **Rug preparation**: Creator distributes supply across many wallets, then sells coordinated |
| 13 | - **Bundled launches**: PumpFun tokens where creator buys via Jito bundle in first slot |
| 14 | |
| 15 | A token showing 1,000 holders with 80% funded from 3 wallets is fundamentally different from one with 1,000 independently-funded holders. Sybil detection separates real demand from theater. |
| 16 | |
| 17 | ## Detection Categories |
| 18 | |
| 19 | ### 1. Funding Source Analysis |
| 20 | |
| 21 | Trace each holder wallet back 1-2 hops to find who sent them SOL: |
| 22 | |
| 23 | ```python |
| 24 | import httpx |
| 25 | |
| 26 | def trace_funding_source(wallet: str, api_key: str, max_hops: int = 2) -> list[str]: |
| 27 | """Trace SOL funding sources for a wallet via Helius parsed transactions.""" |
| 28 | url = f"https://api.helius.xyz/v0/addresses/{wallet}/transactions" |
| 29 | resp = httpx.get(url, params={"api-key": api_key, "type": "TRANSFER", "limit": 50}) |
| 30 | transfers = resp.json() |
| 31 | |
| 32 | funders = [] |
| 33 | for tx in transfers: |
| 34 | for transfer in tx.get("nativeTransfers", []): |
| 35 | if transfer["toUserAccount"] == wallet and transfer["amount"] > 0.001 * 1e9: |
| 36 | funders.append(transfer["fromUserAccount"]) |
| 37 | return funders |
| 38 | ``` |
| 39 | |
| 40 | **Key signals:** |
| 41 | - 3+ holder wallets funded from the same source = cluster |
| 42 | - Funding within 24h of token creation = high suspicion |
| 43 | - Funding amounts are identical (e.g., 0.05 SOL to each) = automated distribution |
| 44 | |
| 45 | ### 2. Co-Trading Patterns |
| 46 | |
| 47 | Wallets that buy the same token at nearly the same time are likely coordinated: |
| 48 | |
| 49 | ```python |
| 50 | def detect_co_trades(buy_events: list[dict], slot_window: int = 3) -> list[list[str]]: |
| 51 | """Group wallets that bought within the same slot window.""" |
| 52 | buy_events.sort(key=lambda x: x["slot"]) |
| 53 | clusters = [] |
| 54 | current_cluster = [buy_events[0]] |
| 55 | |
| 56 | for i in range(1, len(buy_events)): |
| 57 | if buy_events[i]["slot"] - current_cluster[0]["slot"] <= slot_window: |
| 58 | current_cluster.append(buy_events[i]) |
| 59 | else: |
| 60 | if len(current_cluster) >= 3: |
| 61 | clusters.append([b["wallet"] for b in current_cluster]) |
| 62 | current_cluster = [buy_events[i]] |
| 63 | |
| 64 | if len(current_cluster) >= 3: |
| 65 | clusters.append([b["wallet"] for b in current_cluster]) |
| 66 | return clusters |
| 67 | ``` |
| 68 | |
| 69 | **Interpretation:** |
| 70 | - Same slot, different transactions = coordinated (bot-driven) |
| 71 | - Same transaction = bundled (definite sybil) |
| 72 | - First 3 slots after token creation = launch sniping cluster |
| 73 | |
| 74 | ### 3. Bundled Transactions |
| 75 | |
| 76 | Multiple buys packed into a single Solana transaction or Jito bundle: |
| 77 | |
| 78 | ```python |
| 79 | def check_bundle_ratio(early_buys: list[dict], bundle_window_slots: int = 5) -> dict: |
| 80 | """Calculate the ratio of bundled vs independent early buys.""" |
| 81 | bundled = [b for b in early_buys if b.get("is_bundled", False)] |
| 82 | first_slot = min(b["slot"] for b in early_buys) if early_buys else 0 |
| 83 | early = [b for b in early_buys if b["slot"] - first_slot <= bundle_window_slots] |
| 84 | |
| 85 | return { |
| 86 | "total_early_buys": len(early), |
| 87 | "bundled_buys": len(bundled), |
| 88 | "bundle_ratio": len(bundled) / max(len(early), 1), |
| 89 | "bundled_supply_pct": sum(b["amount"] for b in bundled) / max(sum(b["amount"] for b in early), 1), |
| 90 | } |
| 91 | ``` |
| 92 | |
| 93 | See `references/bundler_detection.md` for PumpFun-specific patterns and Jito bundle mechanics. |
| 94 | |
| 95 | ### 4. Wash Trading Detection |
| 96 | |
| 97 | Same entity buying and selling through multiple wallets to inflate volume: |
| 98 | |
| 99 | **Signals:** |
| 100 | - Wallet A buys token, transfers to Wallet B, Wallet B sells — circular flow |
| 101 | - Multiple wallets trading back and forth with no net position change |
| 102 | - Volume concentrated in wallet pairs with funding links |
| 103 | |
| 104 | ```python |
| 105 | def detect_wash_cycles(transfers: list[dict], holder_set: set[str]) -> list[tuple]: |
| 106 | """Find circular transfer patterns among known holders.""" |
| 107 | # Build directed graph of transfers between holders |
| 108 | edges: dict[tuple, float] = {} |
| 109 | for t in transfers: |
| 110 | if t["from"] in holder_set and t["to"] in holder_set: |
| 111 | key = (t["from"], t["to"]) |
| 112 | edges[key] = edges.get(key, 0) + t["amount"] |
| 113 | |
| 114 | # Find reciprocal pairs (A->B and B->A both exist) |
| 115 | wash_pa |