Skill: lxcong/web3-data (version 1.0.0)

--- name: web3-data description: > Explore Web3 on-chain data using Chainbase APIs. Use this skill when the user asks about blockchain data, token holders, wallet addresses, token prices, NFTs, ENS domains, transactions, DeFi portfolios, or any on-chain analytics. Triggers include: "top holders of", "who holds", "wallet address", "address labels", "token price", "token transfers", "NFT owners", "ENS domain", "on-chain data", "blockchain query", "SQL query on-chain", or any request to look up, analyze, or explore Web3/blockchain data across Ethereum, BSC, Polygon, Arbitrum, Optimism, Base, Avalanche, zkSync, and other EVM chains. --- # Web3 Data Explorer (Chainbase) Query on-chain data via Chainbase Web3 API and SQL API. ## Quick Reference **API Key**: Use env `CHAINBASE_API_KEY`, falls back to `demo`. If rate-limited, direct user to https://console.chainbase.com to upgrade. **Script**: `scripts/chainbase.sh <endpoint> [params...]` ```bash # Top token holders scripts/chainbase.sh /v1/token/top-holders chain_id=1 contract_address=0xdAC17F958D2ee523a2206206994597C13D831ec7 limit=10 # Address labels scripts/chainbase.sh /v1/address/labels chain_id=1 address=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 # SQL query scripts/chainbase.sh /query/execute --sql="SELECT * FROM ethereum.blocks ORDER BY number DESC LIMIT 5" ``` ## Chain IDs | Chain | ID | Chain | ID | |---|---|---|---| | Ethereum | 1 | Optimism | 10 | | BSC | 56 | Base | 8453 | | Polygon | 137 | zkSync | 324 | | Avalanche | 43114 | Merlin | 4200 | | Arbitrum | 42161 | | | Default to Ethereum (chain_id=1) unless user specifies otherwise. ## Routing Logic Match user intent to the right endpoint: | User wants | Endpoint | |---|---| | Top token holders / who holds a token | `GET /v1/token/top-holders` | | List of holder addresses | `GET /v1/token/holders` | | Token price | `GET /v1/token/price` | | Historical token price | `GET /v1/token/price/history` | | Token info (name, symbol, supply) | `GET /v1/token/metadata` | | Token transfer history | `GET /v1/token/transfers` | | Address labels/tags | `GET /v1/address/labels` | | Wallet transaction history | `GET /v1/account/txs` | | Single transaction detail | `GET /v1/tx/detail` | | Native token balance (ETH/BNB) | `GET /v1/account/balance` | | ERC20 token balances of wallet | `GET /v1/account/tokens` | | NFTs owned by wallet | `GET /v1/account/nfts` | | DeFi portfolio positions | `GET /v1/account/portfolios` | | ENS domain lookup | `GET /v1/ens/records` or `/v1/ens/reverse` | | NFT metadata/owner/rarity | `GET /v1/nft/metadata`, `/owner`, `/rarity` | | Trending NFT collections | `GET /v1/nft/collection/trending` | | **Anything not covered above** | **SQL API** (`POST /query/execute`) | ## Workflow 1. **Identify intent** — Determine what data the user needs 2. **Resolve identifiers** — If user gives token name (e.g. "USDT"), look up the contract address. Common tokens: - USDT: [REDACTED_SECRET] (ETH) - USDC: [REDACTED_SECRET] (ETH) - WETH: [REDACTED_SECRET] (ETH) - DAI: [REDACTED_SECRET] (ETH) - WBTC: [REDACTED_SECRET] (ETH) - If unknown, use `GET /v1/token/metadata` or ask the user for the contract address 3. **Select endpoint** — Use the routing table above; fall back to SQL API for complex/custom queries 4. **Execute** — Run via `scripts/chainbase.sh` or direct `curl` 5. **Present results** — Format data clearly with tables for lists, highlight key insights ## SQL API Fallback When fixed endpoints don't cover the query, translate user intent to SQL: ```bash scripts/chainbase.sh /query/execute --sql="SELECT from_address, SUM(value) as total FROM ethereum.token_transfers WHERE contract_address = '0x...' GROUP BY from_address ORDER BY total DESC LIMIT 20" ``` Common table patterns (replace `ethereum` with chain name): - `{chain}.blocks` — Block data - `{chain}.transactions` — Transactions - `{chain}.token_transfers` — ERC20 transfers - `{chain}.token_metas` — Token metadata - `{chain}.logs` — Event logs SQL constraints: max 100,000 results per query. For complete endpoint parameters and response schemas, read [references/api-endpoints.md](references/api-endpoints.md).

path: README.md
content: # web3-data-skill

A [Claude Code](https://docs.anthropic.com/en/docs/claude-code) skill for exploring Web3 on-chain data using [Chainbase](https://chainbase.com) APIs.

## Features

- **Token Holders** — Query top holders of any ERC20 token with balance and USD value
- **Address Labels** — Look up wallet address tags (exchange, KOL, contract deployer, etc.)
- **Token Data** — Price, metadata, transfer history
- **NFT Data** — Metadata, owners, floor price, trending collections, rarity
- **Wallet Analytics** — Balances, transaction history, DeFi portfolios, ENS domains
- **SQL Queries** — When fixed APIs don't cover your needs, write custom SQL to query any on-chain data

Supports Ethereum, BSC, Polygon, Arbitrum, Optimism, Base, Avalanche, zkSync, and more.

## Install

```bash
claude install-skill /path/to/web3-data-skill
```

Or install from GitHub:

```bash
claude install-skill https://github.com/lxcong/web3-data-skill
```

## Usage

Once installed, just ask Claude Code about any Web3 data in natural language:

```
> USDT 的 top holders 是谁？
> 查一下 vitalik 的地址标签
> ETH 上 USDC 最近 24 小时的转账记录
> 查询 Bored Ape 的地板价
> 用 SQL 查一下以太坊最近 10 个区块
```

## API Key

The skill uses Chainbase's `demo` API key by default. If you hit rate limits, register at [console.chainbase.com](https://console.chainbase.com) and set your key:

```bash
export CHAINBASE_API_KEY=your_api_key
```

## Structure

```
├── SKILL.md                    # Skill definition and routing logic
├── scripts/
│   └── chainbase.sh            # API wrapper script (auto-handles SQL async polling)
└── references/
    └── api-endpoints.md        # Complete API endpoint reference (38 endpoints)
```

## License

MIT


path: clawhub.json
content: {
  "name": "web3-data",
  "tagline": "Explore on-chain data across EVM chains via Chainbase APIs",
  "description": "A Claude Code skill for exploring Web3 on-chain data using Chainbase APIs. Supports querying token holders, address labels, token prices, NFT data, wallet analytics, DeFi portfolios, ENS domains, and custom SQL queries across Ethereum, BSC, Polygon, Arbitrum, Optimism, Base, Avalanche, zkSync, and more. Uses natural language to route queries to the right API endpoint, with SQL fallback for custom analytics.",
  "category": "data",
  "tags": ["web3", "blockchain", "chainbase", "ethereum", "onchain-data", "token-holders", "wallet-analytics", "nft", "defi", "sql"],
  "version": "1.0.0",
  "license": "MIT",
  "pricing": "free",
  "support_url": "https://github.com/lxcong/web3-data-skill/issues",
  "homepage": "https://github.com/lxcong/web3-data-skill"
}


path: CLAUDE.md
content: # CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project

web3-data-skill — A Claude Code skill for exploring Web3 on-chain data using Chainbase APIs.

## Structure

```
├── SKILL.md                    # Skill 定义：路由逻辑、工作流、Chain ID 映射
├── scripts/
│   └── chainbase.sh            # Chainbase API 调用封装脚本（支持 SQL 异步轮询）
└── references/
    └── api-endpoints.md        # 完整 API 端点参考文档（38 个端点）
```

## Key Conventions

- API 调用统一通过 `scripts/chainbase.sh` 封装脚本执行
- 默认使用 `demo` API Key，可通过环境变量 `CHAINBASE_API_KEY` 覆盖
- 默认链为 Ethereum (chain_id=1)，除非用户指定其他链
- SQL API 为异步模式，脚本会自动轮询结果

## Testing

```bash
# 测试 Web3 API
scripts/chainbase.sh /v1/token/top-holders chain_id=1 contract_address=0xdAC17F958D2ee523a2206206994597C13D831ec7 limit=3

# 测试地址标签
scripts/chainbase.sh /v1/address/labels chain_id=1 address=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045

# 测试 SQL API
scripts/chainbase.sh /query/execute --sql="SELECT * FROM ethereum.blocks LIMIT 1"
```


path: scripts/chainbase.sh
content: #!/usr/bin/env bash
# Chainbase API wrapper script
# Usage: chainbase.sh <endpoint> [params...]
#
# Web3 API:
#   chainbase.sh /v1/token/top-holders chain_id=1 contract_address=0x...
#
# SQL API (async - auto-polls for results):
#   chainbase.sh /query/execute --sql="SELECT * FROM ethereum.blocks LIMIT 5"
#
# SQL API (fetch existing results):
#   chainbase.sh /execution/<id>/results

set -euo pipefail

[REDACTED_SECRET]"
BASE_URL="https://api.chainbase.online"
SQL_BASE_URL="https://api.chainbase.com/api/v1"
SQL_POLL_INTERVAL=2
SQL_POLL_MAX=30

endpoint="$1"
shift || true

# Build query string from remaining args
query=""
body=""
method="GET"
is_sql_execute=false

for arg in "$@"; do
  case "$arg" in
    --method=*)
      method="${arg#--method=}"
      ;;
    --sql=*)
      body="{\"sql\": \"${arg#--sql=}\"}"
      method="POST"
      is_sql_execute=true
      ;;
    --body=*)
      body="${arg#--body=}"
      method="POST"
      ;;
    *)
      if [ -z "$query" ]; then
        query="?${arg}"
      else
        query="${query}&${arg}"
      fi
      ;;
  esac
done

pretty_json() {
  python3 -m json.tool 2>/dev/null || cat
}

api_call() {
  local call_method="$1" call_url="$2" call_header="$3" call_body="${4:-}"
  if [ "$call_method" = "POST" ]; then
    curl -s -X POST "$call_url" \
      -H "${call_header}: ${API_KEY}" \
      -H "Content-Type: application/json" \
      -d "$call_body"
  else
    curl -s "$call_url" -H "${call_header}: ${API_KEY}"
  fi
}

# Determine base URL and header
if [[ "$endpoint" == /query/* ]] || [[ "$endpoint" == /execution/* ]] || [[ "$endpoint" == /dw/* ]]; then
  url="${SQL_BASE_URL}${endpoint}${query}"
  api_header="X-API-KEY"
else
  url="${BASE_URL}${endpoint}${query}"
  api_header="x-api-key"
fi

result=$(api_call "$method" "$url" "$api_header" "$body")

# For SQL execute: auto-poll for results
if [ "$is_sql_execute" = true ]; then
  exec_id=$(echo "$result" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['data'][0]['executionId'])" 2>/dev/null || true)

  if [ -n "$exec_id" ]; then
    echo "Execution ID: $exec_id" >&2
    echo "Polling for results..." >&2

    poll_url="${SQL_BASE_URL}/execution/${exec_id}/status"
    for i in $(seq 1 $SQL_POLL_MAX); do
      sleep $SQL_POLL_INTERVAL
      status_result=$(api_call "GET" "$poll_url" "$api_header")
      status=$(echo "$status_result" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['data'][0]['status'])" 2>/dev/null || echo "UNKNOWN")

      if [ "$status" = "FINISHED" ] || [ "$status" = "FAILED" ]; then
        result_url="${SQL_BASE_URL}/execution/${exec_id}/results"
        api_call "GET" "$result_url" "$api_header" | pretty_json
        exit 0
      fi
      echo "  Status: $status (attempt $i/$SQL_POLL_MAX)" >&2
    done

    echo "Timeout waiting for query. Check manually:" >&2
    echo "  chainbase.sh /execution/${exec_id}/results" >&2
    echo "$result" | pretty_json
    exit 1
  fi
fi

echo "$result" | pretty_json


path: references/api-endpoints.md
content: # Chainbase API Endpoint Reference

## Base URLs

- **Web3 API**: `https://api.chainbase.online`
- **SQL API**: `https://api.chainbase.com/api/v1`
- **SQL Classic**: `https://api.chainbase.online/v1`

## Authentication

All endpoints require API key in header:
- Web3 API: `x-api-key: <key>` (default: `demo`)
- SQL API: `X-API-KEY: <key>`

## Chain IDs

| Chain | ID |
|---|---|
| Ethereum | 1 |
| BSC | 56 |
| Polygon | 137 |
| Avalanche | 43114 |
| Arbitrum One | 42161 |
| Optimism | 10 |
| Base | 8453 |
| zkSync | 324 |
| Merlin | 4200 |

## Standard Response

```json
{ "code": 0, "message": "ok", "data": ..., "next_page": int, "count": int }
```

Pagination: `page` (default 1), `limit` (default 20, max 100).

---

## TOKEN API

### GET /v1/token/top-holders
Top token holders with balances and USD value.

| Param | Required | Description |
|---|---|---|
| chain_id | Yes | Chain ID |
| contract_address | Yes | Token contract (0x...) |
| page | No | Page (default 1) |
| limit | No | Page size (default 20, max 100) |

Response `data`: `[{ wallet_address, original_amount, amount, usd_value }]`

### GET /v1/token/holders
Token holder address list.

Same params as top-holders.
Response `data`: `["0x...", ...]`

### GET /v1/token/price
Current token USD price.

| Param | Required |
|---|---|
| chain_id | Yes |
| contract_address | Yes |

Response `data`: `{ price, symbol, decimals, updated_at }`

### GET /v1/token/price/history
Historical token price (max 90-day range).

| Param | Required |
|---|---|
| chain_id | Yes |
| contract_address | Yes |
| from_timestamp | Yes (Unix) |
| end_timestamp | Yes (Unix) |

Response `data`: `[{ price, symbol, decimals, updated_at }]`

### GET /v1/token/metadata
Token metadata (name, symbol, supply, logos, price).

| Param | Required |
|---|---|
| chain_id | Yes |
| contract_address | Yes |

Response `data`: `{ contract_address, decimals, name, symbol, total_supply, logos, urls, current_usd_price }`

### GET /v1/token/transfers
ERC20 transfer history.

| Param | Required |
|---|---|
| chain_id | Yes |
| contract_address | No |
| address | No |
| from_block / to_block | No |
| from_timestamp / end_timestamp | No |
| page, limit | No |

Response `data`: `[{ block_number, block_timestamp, transaction_hash, from_address, to_address, value, ... }]`

---

## BASIC API

### GET /v1/address/labels
Address labels and tags.

| Param | Required |
|---|---|
| chain_id | Yes |
| address | Yes |

Response `data`: `{ "0xAddr": [{ category, tags: [...] }] }`

### GET /v1/account/txs
Account transaction history.

| Param | Required |
|---|---|
| chain_id | Yes |
| address | Yes |
| from_block / to_block | No |
| from_timestamp / end_timestamp | No |
| page, limit | No |

Response `data`: `[{ type, status, block_number, block_timestamp, transaction_hash, from_address, to_address, value, gas_used, tx_fee, ... }]`

### GET /v1/tx/detail
Single transaction by hash or block+index.

| Param | Required |
|---|---|
| chain_id | Yes |
| hash | No (or block_number + tx_index) |

### GET /v1/block/number/latest
Latest block number.

| Param | Required |
|---|---|
| chain_id | Yes |

Response `data`: `{ number, hash }`

### GET /v1/block/detail
Block details by number.

| Param | Required |
|---|---|
| chain_id | Yes |
| number | Yes |

### POST /v1/contract/call
Call smart contract read function.

Body: `{ chain_id, contract_address, function_name, abi (JSON string), params (array), to_block }`

---

## BALANCE API

### GET /v1/account/balance
Native token balance (wei).

| Param | Required |
|---|---|
| chain_id | Yes |
| address | Yes |
| to_block | No |

Response `data`: `"1000000000000000000"`

### GET /v1/account/tokens
ERC20 token balances for address.

| Param | Required |
|---|---|
| chain_id | Yes |
| address | Yes |
| contract_address | No (filter, up to 100) |
| page, limit | No |

Response `data`: `[{ contract_address, decimals, name, symbol, balance, total_supply, logos, urls, current_usd_price }]`

### GET /v1/account/nfts
NFTs owned by address.

| Param | Required |
|---|---|
| chain_id | Yes |
| address | Yes |
| contract_address | No |
| page, limit | No |

### GET /v1/account/portfolios
DeFi portfolio positions.

| Param | Required |
|---|---|
| address | Yes |
| chain_id | No (array, e.g. ["ethereum"]) |

Response `data`: `[{ id, name, blockchain, logo_url, portfolios: [{ pool, type, assets_deposited, assets_rewarded, stats }] }]`

---

## DOMAIN API

### GET /v1/account/ens
ENS domains held by address.

| Param | Required |
|---|---|
| chain_id | Yes |
| address | Yes |

### GET /v1/ens/records
Resolve ENS domain to address.

| Param | Required |
|---|---|
| chain_id | Yes |
| domain | Yes |

### GET /v1/ens/reverse
Reverse resolve address to ENS.

| Param | Required |
|---|---|
| chain_id | Yes |
| address | Yes |

### GET /v1/space-id/records / /v1/space-id/reverse
SPACE ID domain resolution (same params as ENS equivalents).

---

## NFT API

### GET /v1/nft/metadata
NFT asset metadata. Params: chain_id, contract_address, token_id.

### GET /v1/nft/collection
Collection metadata. Params: chain_id, contract_address.

### GET /v1/nft/collection/items
Collection items. Params: chain_id, contract_address, page, limit.

### GET /v1/nft/search
Search collections by name. Params: chain_id, name, contract_address, page, limit.

### GET /v1/nft/owner
NFT owner. Params: chain_id, contract_address, token_id.

### GET /v1/nft/owners
Collection owners. Params: chain_id, contract_address, page, limit.

### GET /v1/nft/owner/history
NFT ownership history. Params: chain_id, contract_address, token_id, from_block, to_block, page, limit.

### GET /v1/nft/transfers
NFT transfers. Params: chain_id, contract_address, token_id, address, from_block, to_block, page, limit.

### GET /v1/nft/floor_price
Collection floor price. Params: chain_id, contract_address.

### GET /v1/nft/price/history
NFT price history. Params: chain_id, contract_address, from_timestamp, end_timestamp.

### GET /v1/nft/collection/trending
Trending collections. Params: chain_id, range (1h/12h/24h/7d/30d/90d), exchange_name (opensea/x2y2/looksrare/all), sort, page, limit.

### GET /v1/nft/rarity
NFT rarity scores. Params: chain_id, contract_address, token_id, rank_min, rank_max, page, limit.

---

## SQL API

### POST /query/execute (Alpha)
Base URL: `https://api.chainbase.com/api/v1`

Body: `{ "sql": "SELECT * FROM ethereum.blocks LIMIT 10" }`
Max 100,000 results.

### GET /execution/{execution_id}/results (Alpha)
Fetch results of async query.

### GET /execution/{execution_id}/status (Alpha)
Check query execution status. Status values: FINISHED, RUNNING, etc.

### POST /dw/query (Classic)
Base URL: `https://api.chainbase.online/v1`

Body: `{ "query": "SQL...", "task_id": "optional", "page": 1 }`
1,000 results per page, task_id valid for 1 hour, max 100,000 records.

### Common SQL Tables
- `ethereum.blocks` - Block data
- `ethereum.transactions` - Transaction data
- `ethereum.token_transfers` - ERC20 transfers
- `ethereum.token_metas` - Token metadata
- `ethereum.logs` - Event logs
- Replace `ethereum` with chain name: `polygon`, `bsc`, `avalanche`, `arbitrum`, `optimism`, `base`, etc.


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