#!/usr/bin/env python3 """Arkham Intelligence connector — whale tracking, token flows, address intelligence. Requires API key for most endpoints. Set ARKHAM_API_KEY env var. Sign up at https://platform.arkhamintelligence.com """ import argparse import json import os import sys from typing import Any import requests BASE = "https://api.arkhamintelligence.com" TIMEOUT = 30 NOTABLE_ADDRESSES = { "vitalik": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "justin-sun": "0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296", "binance-hot": "0x28C6c06298d514Db089934071355E5743bf21d60", "coinbase-prime": "0xA9D1e08C7793af67e9d92fe308d5697FB81d3E43", "aave-treasury": "0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c", "uniswap-deployer": "0x41653c7d61609D856f29355E404F310Ec4142Cfb", } def _get(path: str, params: dict | None = None) -> Any: key = os.environ.get("ARKHAM_API_KEY") headers = {"User-Agent": "Mozilla/5.0"} if key: headers["API-Key"] = key r = requests.get(f"{BASE}/{path}", params=params, headers=headers, timeout=TIMEOUT) if r.status_code in (401, 403) or "api key" in r.text.lower(): raise EnvironmentError( "Arkham API key required. Set ARKHAM_API_KEY env var.\n" "Sign up at https://platform.arkhamintelligence.com" ) r.raise_for_status() return r.json() def resolve_address(name_or_addr: str) -> str: return NOTABLE_ADDRESSES.get(name_or_addr.lower(), name_or_addr) # ── Data fetchers ─────────────────────────────────────────────────────────── def get_address_info(address: str) -> dict: return _get(f"intelligence/address/{resolve_address(address)}") def get_transfers(address: str, limit: int = 20) -> dict: return _get("token/transfers", {"address": resolve_address(address), "limit": limit}) def search_entity(query: str) -> dict: return _get("intelligence/search", {"query": query}) # ── Summary helpers ───────────────────────────────────────────────────────── def summary_address(data: dict) -> str: lines = ["═══ Address Intelligence ═══", ""] if isinstance(data, dict): entity = data.get("entity", {}) or {} if entity: lines.append(f" Entity: {entity.get('name', 'Unknown')}") lines.append(f" Type: {entity.get('type', 'Unknown')}") lines.append(f" Address: {data.get('address', '?')}") labels = data.get("labels", []) if labels: lines.append(f" Labels: {', '.join(str(l) for l in labels)}") else: lines.append(f" {data}") return "\n".join(lines) def summary_transfers(data) -> str: lines = ["═══ Recent Transfers ═══", ""] transfers = data if isinstance(data, list) else (data.get("transfers", data.get("data", [])) if isinstance(data, dict) else []) if not transfers: lines.append(" No transfers found.") return "\n".join(lines) for t in transfers[:15]: token = t.get("token", {}).get("symbol", "?") if isinstance(t.get("token"), dict) else "?" amount = t.get("amount", t.get("value", "?")) fr = t.get("from", {}) to = t.get("to", {}) fl = (fr.get("label") or fr.get("address", "?")[:12]) if isinstance(fr, dict) else str(fr)[:12] tl = (to.get("label") or to.get("address", "?")[:12]) if isinstance(to, dict) else str(to)[:12] lines.append(f" {token:<8} {str(amount):>15} {fl} → {tl}") return "\n".join(lines) def summary_notable() -> str: lines = ["═══ Notable/Whale Addresses ═══", ""] for name, addr in NOTABLE_ADDRESSES.items(): lines.append(f" {name:<20} {addr}") lines.append("") lines.append(" Use these as shortcuts: arkham.py address vitalik") return "\n".join(lines) # ── CLI ───────────────────────────────────────────────────────────────────── def main(): common = argparse.ArgumentParser(add_help=False) common.add_argument("--pretty", action="store_true", help="Pretty-print JSON output") common.add_argument("--summary", action="store_true", help="Human-readable summary") parser = argparse.ArgumentParser(description="Arkham Intelligence connector", parents=[common]) sub = parser.add_subparsers(dest="command", required=True) p_addr = sub.add_parser("address", help="Address intelligence", parents=[common]) p_addr.add_argument("address", help="Ethereum address or notable name") p_tx = sub.add_parser("transfers", help="Recent token transfers", parents=[common]) p_tx.add_argument("address") p_tx.add_argument("--limit", type=int, default=20) p_search = sub.add_parser("search", help="Search entities", parents=[common]) p_search.add_argument("query") sub.add_parser("notable", help="List notable/whale addresses", parents=[common]) args = parser.parse_args() try: if args.command == "notable": if args.summary: print(summary_notable()) else: json.dump(NOTABLE_ADDRESSES, sys.stdout, indent=2 if args.pretty else None) print() return if args.command == "address": data = get_address_info(args.address) if args.summary: print(summary_address(data)); return result = data elif args.command == "transfers": data = get_transfers(args.address, args.limit) if args.summary: print(summary_transfers(data)); return result = data elif args.command == "search": result = search_entity(args.query) else: parser.print_help(); return json.dump(result, sys.stdout, indent=2 if args.pretty else None) print() except EnvironmentError as e: print(str(e), file=sys.stderr); sys.exit(1) except requests.HTTPError as e: detail = e.response.text[:200] if e.response is not None else "" print(json.dumps({"error": str(e), "detail": detail}), file=sys.stderr); sys.exit(1) except Exception as e: print(json.dumps({"error": f"{type(e).__name__}: {e}"}), file=sys.stderr); sys.exit(1) if __name__ == "__main__": main()