Skill: clipxonchain/clipx-skills (version 1.0.20)

--- name: clipx-bnbchain-api-client description: Thin client for the private ClipX BNBChain API, returning text-only JSON metrics and rankings for BNB Chain (no scraping code, no API keys). metadata: { "openclaw": { "emoji": "🟡", "requires": { "bins": ["python"] }, "os": ["win32", "linux", "darwin"] } } --- ## Response rules (read first) **Rule 1 — Menu format:** Always use numbered lines (1. 2. 3. …). See "Interactive menu" section. **Rule 2 — Table format:** Always wrap the table output inside a markdown code block (triple backticks). Start with a line containing only three backticks, then the table lines, then a line containing only three backticks. This is required so the table displays in monospace with aligned columns. The API returns tables formatted at **40 chars per line** (mobile-friendly); do not reformat or truncate — pass through exactly. **Exception:** For option 11 (Binance Announcements), do NOT wrap in code block — pass through the API output as-is (plain markdown with **📢 Binance Announcements** header, 🔸 bullets, and blank line after each item). **Rule 3 — Response ends with the output.** After the table (or Binance list for option 11), your message is complete. Write nothing else. --- ## What this skill does Calls the ClipX BNBChain API via `python "{baseDir}/api_client_cli.py"` to fetch text-only BNB Chain metrics and rankings. The backend handles all scraping. --- ## Interactive menu When the user says "clipx", "bnbchain", "bnbchain analysis", or asks for BNB Chain reports without specifying which one, output this menu exactly: Output this menu inside a code block (triple backticks) so it displays as a formatted box: ``` ======================================== 🟡 ClipX / BNBChain Analysis ======================================== 1. TVL Rank 2. Fees Rank (24h/7d/30d) 3. Revenue Rank (24h/7d/30d) 4. DApps Rank 5. Full Ecosystem 6. Social Hype 7. Meme Rank 8. Network Metrics 9. Market Insight 10. Market Insight (Live) 11. Binance Announcements 12. DEX Volume (24h/7d/30d) ======================================== Reply with a number (1–12) ``` --- ## Commands (number → command) | # | Command | |---|---------| | 1 | `python "{baseDir}/api_client_cli.py" --mode clipx --analysis-type tvl_rank --timezone UTC` | | 2 | `python "{baseDir}/api_client_cli.py" --mode clipx --analysis-type fees_rank --interval 24h --timezone UTC` | | 3 | `python "{baseDir}/api_client_cli.py" --mode clipx --analysis-type revenue_rank --interval 24h --timezone UTC` | | 4 | `python "{baseDir}/api_client_cli.py" --mode clipx --analysis-type dapps_rank --timezone UTC` | | 5 | `python "{baseDir}/api_client_cli.py" --mode clipx --analysis-type fulleco --timezone UTC` | | 6 | `python "{baseDir}/api_client_cli.py" --mode clipx --analysis-type social_hype --interval 24 --timezone UTC` | | 7 | `python "{baseDir}/api_client_cli.py" --mode clipx --analysis-type meme_rank --interval 24 --timezone UTC` | | 8 | `python "{baseDir}/api_client_cli.py" --mode metrics_basic` | | 9 | `python "{baseDir}/api_client_cli.py" --mode clipx --analysis-type market_insight --timezone UTC` | | 10 | `python "{baseDir}/api_client_cli.py" --mode clipx --analysis-type market_insight_live --timezone UTC` | | 11 | `python "{baseDir}/api_client_cli.py" --mode clipx --analysis-type binance_announcements --timezone UTC` | | 12 | `python "{baseDir}/api_client_cli.py" --mode clipx --analysis-type dex_volume --interval 24h --timezone UTC` | For 2 (fees), 3 (revenue), and 12 (DEX volume), default to 24h. If the user specifies 7d or 30d, use `--interval 7d` or `--interval 30d`. --- ## Displaying results The client prints a pre-formatted table. Your job: 1. Run the command. 2. Take the stdout output (the formatted table). 3. **For options 1–10, 12:** Put it inside a markdown code block (three backticks on a line before, three backticks on a line after). Tables are formatted at **40 chars per line** (mobile-friendly); pass through exactly — do not reformat, truncate, or widen. 4. **For option 11 (Binance Announcements):** Output the stdout as-is. Do NOT wrap in code block. Pass through exactly. 5. Send it. Done. Your response is complete. Your response must look exactly like this (API uses 40 chars/line for mobile): ``` ======================================== 🚀 TOP 10 MEME TOKENS BY SCORE ======================================== ---------------------------------------- # | NAME | — | SCORE ---------------------------------------- 1 | TokenA | — | 4.76 2 | TokenB | — | 4.61 ... ======================================== Source: @ClipX0_ ``` Do not reformat. Pass through the 40-char output as-is. --- ## Network metrics (option 8) Returns JSON with `latest_block`, `gas_price_gwei`, `syncing`. Summarize in plain language. --- ## Market Insight (Live) — option 10 Uses API `market_insight_live` — Volume Leaders + Top Gainers + Top Losers in one snapshot. (No `--live` in chat; OpenClaw shows static messages. Use `--live` locally for real-time refresh.) ## Binance Announcements — option 11 Uses API `binance_announcements` — Top 10 newest announcements. **Display rules:** Run the command, take the stdout output, and output it exactly as received. Do NOT wrap in a code block. The API returns plain markdown with: - Bold header: **📢 Binance Announcements** - 🔸 diamond bullet before each announcement - Blank line after each item Pass through the output unchanged. Your response ends after the last announcement. ## DEX Volume — option 12 Uses API `dex_volume` — Top 10 DEXs on BNB Chain by trading volume. Supports intervals: 24h (default), 7d, 30d. Data from DefiLlama. --- ## Other modes - `--mode metrics_block --blocks 100` — average block time and gas over recent blocks. - `--mode metrics_address --address 0x...` — BNB balance and tx count for an address. --- ## Environment The API base URL defaults to `https://skill.clipx.app`. Override with `CLIPX_API_BASE` env var.

path: api_client_cli.py
content: """
Thin public client for the private ClipX BNBChain API.

This file is safe to publish on ClawHub. It contains:
- No scraping logic
- No Playwright usage
- No DefiLlama / DappBay / Binance page structure knowledge

It only calls your hosted API and prints the JSON response.
"""

import argparse
import json
import os
import sys
import time
from typing import Any, Dict

import requests


def _clear_screen() -> None:
    os.system("cls" if os.name == "nt" else "clear")


def get_api_base() -> str:
    # Prefer env var so you can change base URL without republishing the skill.
    env_base = os.getenv("CLIPX_API_BASE")
    if env_base:
        return env_base.rstrip("/")
    # Fallback: hard-code your public API URL here
    return "https://skill.clipx.app"


def call_api(path: str, params: Dict[str, Any]) -> Dict[str, Any]:
    base = get_api_base()
    url = f"{base}{path}"
    try:
        resp = requests.get(url, params=params, timeout=180)
    except requests.RequestException as exc:  # noqa: BLE001
        return {"ok": False, "error": f"Network error: {exc}"}

    if resp.status_code != 200:
        return {"ok": False, "error": f"HTTP {resp.status_code}: {resp.text}"}

    try:
        data = resp.json()
    except Exception as exc:  # noqa: BLE001
        return {"ok": False, "error": f"Invalid JSON from API: {exc}"}

    return data


def parse_args(argv: Any = None) -> argparse.Namespace:
    p = argparse.ArgumentParser(
        description="ClipX BNBChain API client (text-only JSON)."
    )
    p.add_argument(
        "--mode",
        required=True,
        choices=["metrics_basic", "metrics_block", "metrics_address", "clipx"],
        help="Which API group to call.",
    )
    p.add_argument("--blocks", type=int, help="Recent blocks for metrics_block.")
    p.add_argument("--address", type=str, help="Address for metrics_address.")
    p.add_argument(
        "--analysis-type",
        type=str,
        help="ClipX analysis_type (tvl_rank, fees_rank, revenue_rank, dapps_rank, fulleco, social_hype, meme_rank, market_insight, market_insight_live, binance_announcements, dex_volume).",
    )
    p.add_argument(
        "--interval",
        type=str,
        default="24h",
        help="Interval for some ClipX analyses (24h, 7d, 30d, 24, etc.).",
    )
    p.add_argument(
        "--timezone",
        type=str,
        default="UTC",
        help="Timezone name for timestamps (e.g. UTC, Europe/London).",
    )
    p.add_argument(
        "--formatted",
        action="store_true",
        default=True,
        help="For clipx mode: print server-formatted table (default). Same as VPS output.",
    )
    p.add_argument(
        "--no-formatted",
        dest="formatted",
        action="store_false",
        help="For clipx mode: print raw JSON instead of formatted table.",
    )
    p.add_argument(
        "--live",
        action="store_true",
        help="For clipx mode: keep refreshing in real time (Ctrl+C to stop).",
    )
    p.add_argument(
        "--refresh-interval",
        type=int,
        default=1,
        help="Seconds between refreshes when --live (default: 1 for full real time).",
    )
    return p.parse_args(argv)


def main(argv: Any = None) -> int:
    args = parse_args(argv)

    if args.mode == "metrics_basic":
        result = call_api("/api/bnb/metrics/basic", {})
    elif args.mode == "metrics_block":
        if not args.blocks or args.blocks <= 1:
            print(json.dumps({"ok": False, "error": "blocks>1 required"}))
            return 1
        result = call_api("/api/bnb/metrics/block-stats", {"blocks": args.blocks})
    elif args.mode == "metrics_address":
        if not args.address:
            print(json.dumps({"ok": False, "error": "address required"}))
            return 1
        result = call_api("/api/bnb/metrics/address", {"address": args.address})
    elif args.mode == "clipx":
        if not args.analysis_type:
            print(json.dumps({"ok": False, "error": "analysis-type required"}))
            return 1
        params = {"t": args.analysis_type, "interval": args.interval, "tz": args.timezone}
        if args.live:
            try:
                while True:
                    result = call_api("/api/clipx/analysis", params)
                    _clear_screen()
                    if result.get("ok") and result.get("formatted_table") and args.formatted:
                        print(result["formatted_table"], end="")
                    else:
                        print(json.dumps(result, separators=(",", ":")))
                    print(f"\nRefreshing in {args.refresh_interval}s... (Ctrl+C to stop)")
                    time.sleep(args.refresh_interval)
            except KeyboardInterrupt:
                print("\nStopped.")
                return 0
        result = call_api("/api/clipx/analysis", params)
        if result.get("ok") and result.get("formatted_table") and args.formatted:
            print(result["formatted_table"], end="")
            return 0
    else:
        print(json.dumps({"ok": False, "error": "unknown mode"}))
        return 1

    print(json.dumps(result, separators=(",", ":")))
    return 0 if result.get("ok") else 1


if __name__ == "__main__":
    raise SystemExit(main())



path: format_box.py
content: """
Read ClipX JSON from stdin and print box-style table to stdout.
Optional: pass --analysis-type (and --interval, --timezone) to fetch and format in one step.

Examples:
  # Fetch + format in one command
  python format_box.py --analysis-type tvl_rank
  python format_box.py --analysis-type meme_rank --interval 24 --timezone UTC
  python format_box.py --analysis-type fees_rank --interval 7d

  # Or pipe from api_client_cli
  python api_client_cli.py --mode clipx --analysis-type tvl_rank --timezone UTC | python format_box.py
"""
import argparse
import json
import os
import subprocess
import sys

ANALYSIS_TYPES = (
    "tvl_rank",
    "fees_rank",
    "revenue_rank",
    "dapps_rank",
    "fulleco",
    "social_hype",
    "meme_rank",
    "market_insight",
    "market_insight_live",
    "binance_announcements",
    "dex_volume",
)


def get_json_from_stdin() -> str:
    # Use utf-8-sig to strip BOM when piping on Windows
    return sys.stdin.buffer.read().decode("utf-8-sig").strip()


def get_json_from_api(analysis_type: str, interval: str, timezone: str) -> str:
    script_dir = os.path.dirname(os.path.abspath(__file__))
    cli_path = os.path.join(script_dir, "api_client_cli.py")
    cmd = [
        sys.executable,
        cli_path,
        "--mode", "clipx",
        "--analysis-type", analysis_type,
        "--interval", interval,
        "--timezone", timezone,
        "--no-formatted",
    ]
    result = subprocess.run(
        cmd,
        capture_output=True,
        text=True,
        cwd=script_dir,
        timeout=200,
    )
    if result.returncode != 0 and not result.stdout.strip():
        err = result.stderr.strip() or result.stdout.strip() or "Unknown error"
        raise SystemExit(f"API client failed: {err}")
    return result.stdout.strip()


def main():
    p = argparse.ArgumentParser(
        description="Format ClipX API JSON as a box-style table. Read from stdin or fetch by analysis type."
    )
    p.add_argument(
        "--analysis-type",
        type=str,
        choices=ANALYSIS_TYPES,
        help="Fetch this analysis from the API and format (skips stdin).",
    )
    p.add_argument(
        "--interval",
        type=str,
        default="24h",
        help="Interval for API (24h, 7d, 30d, 24, etc.). Default: 24h",
    )
    p.add_argument(
        "--timezone",
        type=str,
        default="UTC",
        help="Timezone for API. Default: UTC",
    )
    args = p.parse_args()

    if args.analysis_type:
        try:
            raw = get_json_from_api(args.analysis_type, args.interval, args.timezone)
        except subprocess.TimeoutExpired:
            print("Error: API request timed out", file=sys.stderr)
            sys.exit(1)
        except FileNotFoundError:
            print("Error: api_client_cli.py not found next to format_box.py", file=sys.stderr)
            sys.exit(1)
    else:
        raw = get_json_from_stdin()
        if not raw:
            print("No input. Pipe JSON from api_client_cli.py or use --analysis-type.", file=sys.stderr)
            sys.exit(1)

    try:
        data = json.loads(raw)
    except json.JSONDecodeError as e:
        print(f"Invalid JSON: {e}", file=sys.stderr)
        sys.exit(1)

    if not data.get("ok"):
        print("Error:", data.get("error", "unknown"), file=sys.stderr)
        sys.exit(1)

    analysis_type = data.get("analysis_type", "")
    items = data.get("items", [])
    if not items:
        print(data.get("caption", ""))
        return

    # binance_announcements: plain markdown, 40 chars/line, 🔸 bullets, blank line after each
    if analysis_type == "binance_announcements" and items:
        def _wrap(prefix: str, text: str, w: int = 40) -> list:
            avail = w - len(prefix)
            words = text.split()
            out, cur, cur_len = [], [], 0
            for word in words:
                add = len(word) + (1 if cur else 0)
                if cur_len + add <= avail:
                    cur.append(word)
                    cur_len += add
                else:
                    if cur:
                        out.append(prefix + " ".join(cur))
                    cur, cur_len = [word], len(word)
                    prefix, avail = " " * len(prefix), w - len(prefix)
            if cur:
                out.append(prefix + " ".join(cur))
            return out
        print()
        print("**📢 Binance Announcements**")
        print()
        for it in items:
            name = (it.get("name") or "").strip()
            if name:
                for line in _wrap("🔸 ", name, 40):
                    print(line)
                print()
        print()
        return

    # market_insight_live: 3 sections (Volume Leaders, Gainers, Losers)
    if analysis_type == "market_insight_live" and items:
        vol_items = [it for it in items if it.get("section") == "volume"]
        gain_items = [it for it in items if it.get("section") == "gainer"]
        lose_items = [it for it in items if it.get("section") == "loser"]
        sep = "=" * 72
        sep_s = "-" * 72
        print()
        print(sep)
        print("🟢 LIVE BINANCE DATA")
        print(sep)
        if vol_items:
            print("\n💰 24H VOLUME LEADERS:")
            print(sep_s)
            print(f"{'#':<3} | {'NAME':<10} | {'PRICE':<14} | {'24H VOLUME':<14}")
            print(sep_s)
            for it in vol_items:
                print(f"{it.get('rank',''):<3} | {(it.get('name') or '')[:8]:<10} | {it.get('price',''):<14} | {it.get('metric_value',''):<14}")
        if gain_items:
            print("\n📈 TOP GAINERS (24H):")
            print(sep_s)
            print(f"{'#':<3} | {'NAME':<10} | {'PRICE':<14} | {'24H CHANGE':<12}")
            print(sep_s)
            for it in gain_items:
                print(f"{it.get('rank',''):<3} | {(it.get('name') or '')[:8]:<10} | {it.get('price',''):<14} | {it.get('metric_value',''):<12}")
        if lose_items:
            print("\n📉 TOP LOSERS (24H):")
            print(sep_s)
            print(f"{'#':<3} | {'NAME':<10} | {'PRICE':<14} | {'24H CHANGE':<12}")
            print(sep_s)
            for it in lose_items:
                print(f"{it.get('rank',''):<3} | {(it.get('name') or '')[:8]:<10} | {it.get('price',''):<14} | {it.get('metric_value',''):<12}")
        print("\n" + sep)
        if data.get("source"):
            print(f"Source: {data['source']}")
        print()
        return

    # fulleco: Name | Category | Users (no rank column)
    if analysis_type == "fulleco" and items:
        w2, w3 = 12, 8
        sep_double = "=" * 40
        sep_single = "-" * 40
        print()
        print(sep_double)
        print("🚀 FULL ECOSYSTEM LEADERS")
        print(sep_double)
        print(sep_single)
        print(f"{'NAME':<{w2}} | {'CATEGORY':<{w3}} | {'USERS'}")
        print(sep_single)
        for it in items:
            name = (it.get("name") or "")[:w2]
            cat = (it.get("category") or "—")[:w3]
            users = it.get("metric_value", "")
            print(f"{name:<{w2}} | {cat:<{w3}} | {users}")
        print(sep_double)
        if data.get("source"):
            print(f"Source: {data['source']}")
        print()
        return

    # meme_rank: match VPS "FETCHED MEME TOKENS (FOR VERIFICATION)" (RANK|NAME|SCORE|MCAP|B.HOLDERS)
    if analysis_type == "meme_rank" and items and (items[0].get("mcap") is not None or items[0].get("bHolders") is not None):
        sep = "=" * 62
        sep_s = "-" * 62
        print()
        print(sep)
        print("🚀 FETCHED MEME TOKENS (FOR VERIFICATION)")
        print(sep)
        print(f"{'RANK':<5} | {'NAME':<20} | {'SCORE':<8} | {'MCAP':<12} | {'B.HOLDERS':<10}")
        print(sep_s)
        for it in items:
            rank = f"#{it.get('rank', '')}"
            name = (it.get("name") or "")[:18]
            score = str(it.get("metric_value", it.get("score", "")))
            mcap = str(it.get("mcap", ""))
            holders = str(it.get("bHolders", ""))
            print(f"{rank:<5} | {name:<20} | {score:<8} | {mcap:<12} | {holders:<10}")
        print(sep)
        if data.get("source"):
            print(f"Source: {data['source']}")
        print()
        return

    # Standard 4-column format for other analysis types
    titles = {
        "tvl_rank": ("TOP 10 TVL PROTOCOLS ON BSC", "#", "NAME", "CATEGORY", "TVL"),
        "fees_rank": ("TOP 10 FEES PAID PROTOCOLS ON BSC", "#", "NAME", "CATEGORY", "FEES"),
        "revenue_rank": ("TOP 10 REVENUE PROTOCOLS ON BSC", "#", "NAME", "CATEGORY", "REVENUE"),
        "dapps_rank": ("TOP 10 DAPPS BY USERS (7D)", "#", "NAME", "CATEGORY", "USERS"),
        "fulleco": ("FULL ECOSYSTEM LEADERS", "#", "NAME", "CATEGORY", "METRIC"),
        "social_hype": ("TOP 10 SOCIAL HYPE TOKENS", "#", "NAME", "SENTIMENT", "HYPE SCORE"),
        "meme_rank": ("TOP 10 MEME TOKENS BY SCORE", "#", "NAME", "—", "SCORE"),
        "market_insight": ("BINANCE 24H VOLUME LEADERS", "#", "NAME", "CATEGORY", "24H VOLUME"),
        "market_insight_live": ("LIVE BINANCE DATA", "#", "NAME", "CATEGORY", "VALUE"),
        "dex_volume": ("TOP 10 DEX VOLUME ON BNB CHAIN", "#", "NAME", "CATEGORY", "VOLUME"),
    }
    title_line, col1, col2, col3, col4 = titles.get(
        analysis_type, ("RANKING", "#", "NAME", "CATEGORY", "VALUE")
    )
    meta = data.get("meta") or {}
    interval = (meta.get("interval") or data.get("interval") or "").strip().upper()
    if interval and analysis_type in ("fees_rank", "revenue_rank", "dex_volume"):
        title_line = f"{title_line} ({interval})"

    # Mobile-friendly width (~40 chars) so table doesn't wrap on small screens
    w2, w3 = 12, 8
    sep_double = "=" * 40
    sep_single = "-" * 40

    print()
    print(sep_double)
    print(f"🚀 {title_line}")
    print(sep_double)
    print(sep_single)
    print(f"{col1:<3} | {col2:<{w2}} | {col3:<{w3}} | {col4}")
    print(sep_single)
    for it in items:
        rank = it.get("rank", "")
        name = (it.get("name") or "")[:w2]
        cat = (it.get("category") or "—")[:w3]
        val = it.get("metric_value", "")
        print(f"{rank:<3} | {name:<{w2}} | {cat:<{w3}} | {val}")
    print(sep_double)
    if data.get("source"):
        print(f"Source: {data['source']}")
    print()

if __name__ == "__main__":
    main()


path: README.md
content: # ClipX Skills

> BNB Chain analytics for OpenClaw — TVL, fees, revenue, DApps, market insight, and more. Thin client, no API keys, no scraping on the client.

[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![OpenClaw](https://img.shields.io/badge/OpenClaw-Skill-green.svg)](https://openclaw.ai)
[![BNB Chain](https://img.shields.io/badge/BNB%20Chain-BSC-yellow.svg)](https://bnbchain.org)

---

## Table of Contents

- [Overview](#overview)
- [Architecture](#architecture)
- [Menu Options](#menu-options)
- [Quick Start](#quick-start)
- [File Structure](#file-structure)
- [API Reference](#api-reference)
- [Local Testing](#local-testing)
- [Configuration](#configuration)
- [Server-Side API](#server-side-api)
- [Publishing](#publishing)
- [Troubleshooting](#troubleshooting)

---

## Overview

**ClipX Skills** is a thin HTTP client for the ClipX BNBChain API. It lets OpenClaw agents fetch BNB Smart Chain metrics and rankings via a simple CLI. All scraping, Playwright, and heavy logic run on the private API — this skill only calls HTTP endpoints.

| Category | Capabilities |
|----------|--------------|
| **Rankings** | TVL, fees, revenue, DApps, full ecosystem, social hype, meme rank |
| **Market** | Volume leaders, gainers/losers, Binance announcements |
| **Network** | Block, gas, sync state, block stats, address balance |
| **Output** | Text-only JSON, pre-formatted tables, monospace code blocks |

**Design:** Thin client · Text-only · Zero secrets (no API keys required)

---

## Architecture

```
User → OpenClaw Agent → api_client_cli.py → ClipX API (VPS) → DefiLlama / DappBay / Binance
                                                                    ↓
User ← OpenClaw Agent ← formatted table / JSON ← api_client_cli.py ←
```

Flow: User says "clipx" or "bnbchain" → Agent shows menu → User replies with number → Agent runs CLI → CLI calls API → API returns formatted table → Agent displays in code block.

---

## Menu Options

| # | Analysis | Description |
|---|----------|-------------|
| 1 | TVL Rank | Top 10 protocols by Total Value Locked |
| 2 | Fees Rank | Top 10 by fees paid (24h / 7d / 30d) |
| 3 | Revenue Rank | Top 10 by revenue (24h / 7d / 30d) |
| 4 | DApps Rank | Top 10 DApps by users (7d) |
| 5 | Full Ecosystem | DeFi, Games, Social, NFTs, AI, Infra, RWA leaders |
| 6 | Social Hype | Top 10 social hype tokens |
| 7 | Meme Rank | Top 10 meme tokens by score |
| 8 | Network metrics | Latest block, gas price, sync state |
| 9 | Market Insight | Binance 24h volume leaders (snapshot) |
| 10 | Market Insight (Live) | Volume + Top Gainers + Top Losers (snapshot) |
| 11 | Binance Announcements | Top 10 newest announcements |
| 12 | DEX Volume | Top 10 DEXs by volume on BNB Chain (24h / 7d / 30d) |

---

## Quick Start

### 1. Install

```bash
pip install -r requirements.txt
```

Requires only `requests`. No Playwright or heavy dependencies.

### 2. Publish to ClawHub

```bash
cd ClipX_Skills
clawhub publish . \
  --slug clipx-bnbchain-api-client \
  --name "ClipX BNBChain Metrics & Rankings (API Client)" \
  --version 1.0.0 \
  --tags latest,bnbchain,metrics,clipx
```

### 3. Use in OpenClaw

1. Install the skill from ClawHub.
2. Say **"clipx"** or **"bnbchain"**.
3. Agent shows numbered menu (1–12).
4. Reply with a number (e.g. `7` for meme rank, `12` for DEX volume).
5. Agent runs the command and displays the formatted table.

---

## File Structure

| File | Purpose |
|------|---------|
| `SKILL.md` | OpenClaw metadata, menu format, table display rules, command mapping |
| `api_client_cli.py` | HTTP client — calls ClipX API, prints JSON or formatted table |
| `format_box.py` | Local helper — fetches or reads JSON, prints box-style table |
| `requirements.txt` | Dependencies (`requests` only) |

---

## API Reference

### Modes

| Mode | Description |
|------|-------------|
| `metrics_basic` | Network status (block, gas, sync) |
| `metrics_block` | Block stats over N blocks (`--blocks 100`) |
| `metrics_address` | Balance and tx count (`--address 0x...`) |
| `clipx` | ClipX rankings (requires `--analysis-type`) |

### ClipX Analysis Types

| Type | Interval | Description |
|------|----------|-------------|
| `tvl_rank` | — | Top protocols by TVL |
| `fees_rank` | 24h, 7d, 30d | Top by fees paid |
| `revenue_rank` | 24h, 7d, 30d | Top by revenue |
| `dapps_rank` | — | Top DApps by users (7d) |
| `fulleco` | — | Full ecosystem leaders |
| `social_hype` | 24 | Social hype tokens |
| `meme_rank` | 24 | Meme token scores |
| `market_insight` | — | Binance 24h volume leaders |
| `market_insight_live` | — | Volume + gainers + losers |
| `binance_announcements` | — | Top 10 newest announcements |
| `dex_volume` | 24h, 7d, 30d | Top DEXs by volume on BNB Chain |

### Parameters

| Parameter | Default | Description |
|-----------|---------|-------------|
| `--analysis-type` | (required for clipx) | One of the types above |
| `--interval` | 24h | For fees_rank, revenue_rank, social_hype, meme_rank, dex_volume |
| `--timezone` | UTC | Timestamp timezone |
| `--formatted` | default | Print server-formatted table |
| `--no-formatted` | — | Print raw JSON |
| `--live` | — | For market_insight_live: refresh in real time (Ctrl+C to stop) |

### Example Commands

```bash
# Network metrics
python api_client_cli.py --mode metrics_basic
python api_client_cli.py --mode metrics_block --blocks 100
python api_client_cli.py --mode metrics_address --address 0x0000000000000000000000000000000000000000

# ClipX rankings
python api_client_cli.py --mode clipx --analysis-type tvl_rank --timezone UTC
python api_client_cli.py --mode clipx --analysis-type fees_rank --interval 24h --timezone UTC
python api_client_cli.py --mode clipx --analysis-type dex_volume --interval 7d --timezone UTC
python api_client_cli.py --mode clipx --analysis-type market_insight_live --timezone UTC
```

---

## Local Testing

### Direct CLI (recommended)

```bash
python api_client_cli.py --mode clipx --analysis-type tvl_rank --timezone UTC
python api_client_cli.py --mode clipx --analysis-type meme_rank --interval 24 --timezone UTC
python api_client_cli.py --mode clipx --analysis-type dex_volume --interval 24h --timezone UTC
```

The client prints the server-formatted table by default.

### Using format_box.py

Fetch and format in one command:

```bash
python format_box.py --analysis-type tvl_rank
python format_box.py --analysis-type fees_rank --interval 7d
```

Or pipe from the client (useful when using `--no-formatted`):

```bash
python api_client_cli.py --mode clipx --analysis-type tvl_rank --no-formatted | python format_box.py
```

Without `--analysis-type`, `format_box.py` reads JSON from stdin (UTF-8 or UTF-8 BOM).

### Timeouts

Options 1–3, 8–12: typically &lt; 10 seconds. Options 4–7 (DappBay scraping): 30–180 seconds. Default HTTP timeout: 180s.

---

## Configuration

| Variable | Description |
|----------|-------------|
| `CLIPX_API_BASE` | ClipX API base URL. Default: `https://skill.clipx.app` |

**Linux / macOS:** `export CLIPX_API_BASE="https://your-api.com"`  
**Windows (PowerShell):** `$env:CLIPX_API_BASE = "https://your-api.com"`  
**Windows (cmd):** `set CLIPX_API_BASE=https://your-api.com`

---

## Server-Side API

This skill calls a **private ClipX API** hosted separately. The API is not published to ClawHub.

### Endpoints

| Endpoint | Description |
|----------|-------------|
| `GET /api/bnb/metrics/basic` | Network metrics |
| `GET /api/bnb/metrics/block-stats?blocks=N` | Block statistics |
| `GET /api/bnb/metrics/address?address=0x...` | Address balance and tx count |
| `GET /api/clipx/analysis?t=TYPE&interval=24h&tz=UTC` | ClipX rankings |

### ClipX Analysis Response

```json
{
  "ok": true,
  "analysis_type": "tvl_rank",
  "timestamp": "2026-03-03T17:03:08.394Z",
  "caption": "...",
  "source": "@ClipX0_",
  "formatted_table": "...",
  "items": [
    { "rank": 1, "name": "PancakeSwap AMM", "category": "Dexs", "metric_label": "TVL", "metric_value": "$1.92B" }
  ],
  "meta": { "interval": "24h" }
}
```

On error: `{ "ok": false, "error": "Human-readable error" }`

---

## Publishing

From the `ClipX_Skills` folder:

```bash
clawhub publish . \
  --slug clipx-bnbchain-api-client \
  --name "ClipX BNBChain Metrics & Rankings (API Client)" \
  --version 1.0.0 \
  --tags latest,bnbchain,metrics,clipx
```

Only the thin client and `SKILL.md` are published. API logic stays private.

---

## Troubleshooting

| Issue | Solution |
|-------|----------|
| Menu shows bullets instead of numbers | Re-publish the skill |
| Table not in monospace | Agent should wrap output in triple backticks. Ensure SKILL.md is up to date |
| HTTP read timeout | Default 180s. Options 4–7 may take up to 3 minutes |
| `Connection refused` / `Network error` | Check `CLIPX_API_BASE`; ensure API is running and reachable |
| `analysis-type required` | For clipx mode, always pass `--analysis-type` with a valid value |

---

## License

This skill is provided as-is. The ClipX API and backend logic are maintained separately.


path: requirements.txt
content: requests>=2.31.0,<3.0.0




Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)