diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..71990f9 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @mefai-dev diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ea86a66 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributing to BNB Chain Skills + +Thank you for your interest in contributing. This project provides on-chain analysis tools for BNB Smart Chain and we welcome contributions from the community. + +## How to Contribute + +1. **Fork** this repository +2. **Create a feature branch** from `main` +3. **Make your changes** following the guidelines below +4. **Submit a pull request** with a clear description of your changes + +## Adding a New Skill + +Each skill lives in its own directory under `skills///` and must include a `SKILL.md` file following this structure: + +- YAML frontmatter with `name`, `description`, and `metadata` (author, version) +- Overview table listing API endpoints, functions, and use cases +- Use cases section +- Supported chains table +- API documentation with method, URL, parameters, example request, response example, and field descriptions +- Notes section + +See any existing `SKILL.md` for reference. + +## Adding a New API Endpoint + +1. Add the route in `api/routes/bnbchain.py` +2. Create the corresponding `SKILL.md` in the appropriate category +3. Add the frontend panel in `frontend/js/panels.js` if applicable +4. Register the panel in `frontend/js/app.js` +5. Update the skills table in `README.md` + +## Code Style + +- **Python**: Follow PEP 8. Use `async/await` for all HTTP calls. Use type hints. +- **JavaScript**: Vanilla JS, no frameworks. Web Components for panels. +- **Documentation**: English only. Clear, concise descriptions. + +## Testing + +Before submitting a PR: + +1. Verify the API endpoint returns correct data +2. Test the frontend panel renders properly +3. Check that caching works (stale-while-revalidate pattern) +4. Confirm no broken imports or missing dependencies + +## Reporting Issues + +Open a GitHub issue with: + +- Clear description of the problem +- Steps to reproduce +- Expected vs actual behavior +- BSC transaction/block/address examples if applicable + +## License + +By contributing, you agree that your contributions will be licensed under the MIT License. diff --git a/README.md b/README.md index 74df2bf..c69f391 100644 --- a/README.md +++ b/README.md @@ -1,101 +1,173 @@ # BNB Chain Skills -> A collection of AI agent skills for the [BNB Chain MCP](https://github.com/bnb-chain/bnbchain-mcp) (Model Context Protocol) server. +> 28 professional on-chain analysis tools for BNB Smart Chain. Built by [Mefai](https://mefai.io). + +**Live Demo:** [mefai.io/bnbchain](https://mefai.io/bnbchain) + +## Overview + +BNB Chain Skills is an open-source collection of real-time blockchain analysis tools built on BSC JSON-RPC and DexScreener APIs. Each skill provides a focused, production-ready analytical capability — from transaction decoding to honeypot detection, from validator monitoring to cross-DEX arbitrage scanning. + +## Skills + +### Security & Auditing +| Skill | Description | API Endpoint | +|-------|-------------|-------------| +| **Honeypot Check** | Combined honeypot detection: bytecode analysis, buy/sell ratio, owner concentration, dangerous selectors | `/mefai/honeypot-check` | +| **Contract X-Ray** | Deep bytecode analysis: proxy detection, function scanning, mint/pause/blacklist pattern detection | `/mefai/contract-xray` | +| **Approval Scanner** | Token approval checker across 9 major DEX routers — finds unlimited allowances | `/mefai/approval-scanner` | +| **Risk Radar** | Risk scoring (0-100, A-F grade) combining on-chain and market signals | `/mefai/risk-radar` | +| **Upgrade Monitor** | EIP-1967 proxy contract detection with storage slot inspection, admin identification | `/mefai/upgrade-monitor` | +| **Contract Audit** | Smart contract security analysis: ownership, proxy, token standards compliance | `/mefai/contract-audit` | + +### Alpha & Intelligence +| Skill | Description | API Endpoint | +|-------|-------------|-------------| +| **Sniper Detector** | Detect bot sniping on any token — early buyer analysis, hold/dump tracking, snipe score | `/mefai/sniper-detector` | +| **Copy Trade** | Alpha wallet activity monitor — tracks known wallets for token interactions | `/mefai/copy-trade` | +| **Smart Money** | Whale transaction tracker — identifies large-value transfers in real-time | `/mefai/smart-money` | +| **Wallet Cluster** | On-chain forensics: discover connected wallets via transfer patterns and shared holdings | `/mefai/wallet-cluster` | + +### Market Analysis +| Skill | Description | API Endpoint | +|-------|-------------|-------------| +| **DEX Arbitrage** | Cross-DEX price discrepancy finder with gas-adjusted profit estimates | `/mefai/dex-arb` | +| **Token Battle** | Side-by-side comparison of up to 4 tokens: price, volume, liquidity, burns | `/mefai/token-battle` | +| **Token Birth** | Token genesis analysis: creator profile, supply distribution, age, liquidity history | `/mefai/token-birth` | +| **Pair Analytics** | Deep DEX pair analysis with aggregate stats across multiple pairs | `/mefai/pair-analytics` | +| **PancakeSwap Arena** | PancakeSwap top pairs, volume leaders, trending tokens | `/mefai/pancakeswap-arena` | + +### On-Chain Analytics +| Skill | Description | API Endpoint | +|-------|-------------|-------------| +| **TX Decoder** | Decode any BSC transaction: function calls, events, token transfers, gas breakdown | `/mefai/tx-decoder` | +| **Block Autopsy** | Block gas distribution, transaction type breakdown, top gas consumers | `/mefai/block-autopsy` | +| **Token Flow** | Track token transfer movements from recent blocks | `/mefai/token-flow` | +| **Burn Engine** | Real-time BNB and token burn tracking with USD valuations | `/mefai/burn-tracker` | +| **Wallet Scanner** | Complete wallet portfolio analysis: BNB + token balances with USD values | `/mefai/wallet-scanner` | +| **Portfolio Heatmap** | Bloomberg-style portfolio view with 24h performance heatmap | `/mefai/portfolio-heatmap` | + +### Network & Infrastructure +| Skill | Description | API Endpoint | +|-------|-------------|-------------| +| **Validator Map** | Live BSC validator monitoring: 21 validators, gas utilization, block production, MEV detection | `/mefai/validator-map` | +| **Network Pulse** | Network congestion gauge: pressure score, TPS, block timing, gas optimization | `/mefai/network-pulse` | +| **BSC Supremacy** | BSC vs Ethereum comparison: speed, cost, TPS — live data | `/mefai/bsc-supremacy` | +| **Gas Calculator** | Operation cost estimator: 10 operations with BSC vs ETH cost comparison | `/mefai/gas-calculator` | +| **Chain Vitals** | Core network health metrics: block height, gas price, peer count | `/mefai/chain-vitals` | + +### DeFi +| Skill | Description | API Endpoint | +|-------|-------------|-------------| +| **Yield Finder** | APY estimation from trading fee revenue, sorted by opportunity | `/mefai/yield-finder` | +| **DeFi Leaderboard** | Top DeFi protocols ranked by TVL, volume, and user count | `/mefai/defi-leaderboard` | +| **Liquidity Pulse** | Real-time liquidity depth analysis across major BSC pairs | `/mefai/liquidity-pulse` | + +## Architecture -## Introduction +``` ++----------------------------------------------+ +| Frontend (Web Components) | +| 50 panels - _MX premium CSS - BasePanel | +| Stale-while-revalidate cache - Auto-refresh | ++----------------------------------------------+ +| API Layer (FastAPI) | +| 28 /mefai/* endpoints - async - TTL cache | ++----------------------------------------------+ +| Data Sources | +| BSC JSON-RPC - DexScreener - BscScan | ++----------------------------------------------+ +``` -BNB Chain Skills helps AI agents (e.g. Cursor, Claude) install and use the BNB Chain MCP server effectively. It provides structured knowledge on how to connect the MCP server, configure credentials, and use each available tool for blocks, transactions, contracts, tokens, NFTs, wallet operations, ERC-8004 agent registration, and Greenfield storage. +### Tech Stack +- **Frontend:** Vanilla JS Web Components, zero dependencies +- **Backend:** Python FastAPI with async/await, stale-while-revalidate caching +- **Data:** BSC JSON-RPC (bsc-dataseed1.binance.org), DexScreener API, BscScan API +- **Deployment:** Nginx reverse proxy, systemd services -## Claude/Cursor skills vs OpenClaw skills +## API Reference -| | **This repo (bnbchain-skills)** | **OpenClaw skills** | -|---|--------------------------------|---------------------| -| **Who installs** | The **user** installs the skill (e.g. `npx skills add bnb-chain/bnbchain-skills` or copy into `~/.cursor/skills/`). | The **OpenClaw bot** fetches the skill page itself (e.g. `curl` the [OpenClaw Skills](https://docs.bnbchain.org/showcase/mcp/skills) URL) and learns from it. | -| **Who acts** | The **agent** (Cursor/Claude) reads the skill and then **sets up MCP for the user**—adds the bnbchain-mcp server to the user’s MCP config and uses the tools. | The **OpenClaw bot** autonomously knows the `npx @bnb-chain/mcp@latest` command and installs/uses the MCP based on that page. | -| **Purpose** | Teach the in-IDE agent to configure bnbchain-mcp in the user’s environment and use every MCP tool. | Give OpenClaw (and similar agents) a single fetchable page so they can discover and use BNB Chain MCP on their own. | +All endpoints are served under `/superbsc/api/bnbchain/mefai/`. -So: **Claude/Cursor skills** = user installs skill → agent uses it to **set MCP for the user** and call tools. **OpenClaw skills** = bot fetches the skill page → bot **installs and uses** MCP autonomously. +### Authentication +No authentication required. All endpoints are public and rate-limited via caching. -## What are Skills? +### Common Response Pattern +```json +{ + "field1": "value", + "field2": 123, + "error": null +} +``` -Skills are structured knowledge files that give AI coding agents domain-specific expertise. They follow a portable format that works across different AI tools. When you install a skill, the agent learns how to install bnbchain-mcp and how to use each MCP tool without needing to search external docs. +Error responses: +```json +{ + "error": "Description of what went wrong" +} +``` -## Available Skills +### Example Requests -| Skill | Description | -|-------|-------------| -| **bnbchain-mcp-skill** | Install and use BNB Chain MCP — blocks, transactions, contracts, tokens, NFTs, wallet, ERC-8004 agents, Greenfield. Covers connection, credentials, and every MCP tool. | +```bash +# Honeypot check +curl https://mefai.io/superbsc/api/bnbchain/mefai/honeypot-check?address=0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82 -## Installation +# Network pulse +curl https://mefai.io/superbsc/api/bnbchain/mefai/network-pulse -### Quick Install (Recommended) +# Validator map +curl https://mefai.io/superbsc/api/bnbchain/mefai/validator-map -```bash -npx skills add bnb-chain/bnbchain-skills +# Gas calculator +curl https://mefai.io/superbsc/api/bnbchain/mefai/gas-calculator ``` -Install globally (available across all projects): +## Self-Hosting -```bash -npx skills add bnb-chain/bnbchain-skills -g -``` +### Prerequisites +- Python 3.11+ +- Node.js 18+ (for frontend dev only) +- Nginx (production) -### Manual Install (Cursor / Claude) - -**Personal skill** (available across all projects): +### Setup +1. Clone the repository: ```bash -git clone https://github.com/bnb-chain/bnbchain-skills.git -cp -r bnbchain-skills/skills/* ~/.cursor/skills/ +git clone https://github.com/mefai-dev/bnbchain-skills.git +cd bnbchain-skills ``` -**Project skill** (current project only): - +2. Install Python dependencies: ```bash -git clone https://github.com/bnb-chain/bnbchain-skills.git -cp -r bnbchain-skills/skills/* .cursor/skills/ +pip install fastapi uvicorn httpx ``` -### Using the skill - -Once installed, the agent will use the skill when you ask to: - -- Install or connect BNB Chain MCP -- Query blocks, transactions, balances, or contracts on BNB Chain or other EVM networks -- Transfer tokens or NFTs, or interact with smart contracts -- Register or resolve ERC-8004 agents -- Use Greenfield storage (buckets, objects, payments) +3. Start the backend: +```bash +uvicorn api.main:app --host 127.0.0.1 --port 8202 +``` -Example prompts: +4. Serve the frontend: +```bash +# Development +python -m http.server 8080 --directory frontend -- "How do I install bnbchain-mcp in Cursor?" -- "Get the latest block on BSC" -- "Check the ERC-20 balance of 0x... on opBNB" -- "Register this MCP as an ERC-8004 agent" -- "List my Greenfield buckets" +# Production: configure nginx to serve frontend/ as static and proxy /api to port 8202 +``` -## Skill Structure +## Contributing -``` -bnbchain-skills/ -├── skills/ -│ └── bnbchain-mcp-skill/ -│ ├── SKILL.md # Main skill: install + tool usage -│ └── references/ -│ ├── evm-tools-reference.md # Blocks, transactions, contracts, tokens, NFT, wallet, network -│ ├── erc8004-tools-reference.md # ERC-8004 agent tools -│ ├── greenfield-tools-reference.md # Greenfield storage & payment tools -│ └── prompts-reference.md # MCP prompts -├── LICENSE -└── README.md -``` +Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. -## References +## Related Projects -- **BNB Chain MCP:** https://github.com/bnb-chain/bnbchain-mcp -- **npm package:** `@bnb-chain/mcp` — run with `npx @bnb-chain/mcp@latest` -- **ERC-8004** (Identity Registry); **Agent Metadata Profile** for agentURI format. +- [BNB Chain MCP](https://github.com/bnb-chain/bnbchain-mcp) — Model Context Protocol server for BNB Chain +- [Binance Skills Hub](https://github.com/binance/binance-skills-hub) — Official Binance skills collection ## License MIT License — see [LICENSE](LICENSE) for details. + +Built by [Mefai](https://mefai.io) for the BNB Chain community. diff --git a/api/__init__.py b/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/cache.py b/api/cache.py new file mode 100644 index 0000000..5a616d0 --- /dev/null +++ b/api/cache.py @@ -0,0 +1,228 @@ +""" +BNB Chain Skills — In-memory cache with stale-while-revalidate. + +Strategy: +- Fresh TTL (configurable per call): serve from cache instantly +- Stale TTL (300s): serve stale data instantly, refresh in background +- Beyond stale: fetch new data (blocking) +""" + +import asyncio +import time +import hashlib +import json +import logging +from collections import OrderedDict +from typing import Any + +import httpx + +logger = logging.getLogger("bnbchain.cache") + +_cache: OrderedDict[str, tuple[float, Any]] = OrderedDict() +_refreshing: set[str] = set() +_client: httpx.AsyncClient | None = None + +FRESH_TTL = 60 +STALE_TTL = 300 +MAX_CACHE_ENTRIES = 5000 + + +def _key(url: str, params: dict | None = None, body: dict | None = None) -> str: + raw = ( + url + + json.dumps(params or {}, sort_keys=True) + + json.dumps(body or {}, sort_keys=True) + ) + return hashlib.md5(raw.encode()).hexdigest() + + +def get_cached( + url: str, params: dict | None = None, body: dict | None = None, ttl: int = FRESH_TTL +) -> tuple[Any | None, bool]: + """Returns (data, is_fresh). data=None if nothing cached.""" + if ttl <= 0: + return None, False + k = _key(url, params, body) + if k in _cache: + ts, data = _cache[k] + age = time.time() - ts + if age < ttl: + _cache.move_to_end(k) + return data, True + if age < STALE_TTL: + return data, False + del _cache[k] + return None, False + + +def set_cached( + url: str, data: Any, params: dict | None = None, body: dict | None = None +) -> None: + k = _key(url, params, body) + _cache[k] = (time.time(), data) + _cache.move_to_end(k) + while len(_cache) > MAX_CACHE_ENTRIES: + _cache.popitem(last=False) + + +async def get_client() -> httpx.AsyncClient: + global _client + if _client is None or _client.is_closed: + pool_limits = httpx.Limits(max_connections=200, max_keepalive_connections=60) + _client = httpx.AsyncClient( + timeout=httpx.Timeout(15.0, connect=5.0), + follow_redirects=True, + limits=pool_limits, + http2=True, + ) + return _client + + +_RETRYABLE_STATUSES = {429, 503} + + +async def _do_fetch(url: str, params: dict | None, headers: dict) -> Any: + client = await get_client() + resp = None + for attempt in range(3): + try: + resp = await client.get(url, params=params, headers=headers) + except (httpx.ConnectTimeout, httpx.ReadTimeout, httpx.ConnectError) as e: + if attempt < 2: + await asyncio.sleep(0.5 * (attempt + 1)) + continue + return {"error": True, "status": 0, "detail": str(e), "_no_cache": True} + if resp.status_code in _RETRYABLE_STATUSES: + if attempt < 2: + delay = float(resp.headers.get("Retry-After", 1 + attempt)) + await asyncio.sleep(delay) + continue + break + if resp is None: + return {"error": True, "status": 0, "detail": "No response"} + if resp.status_code >= 400: + try: + err = resp.json() + except Exception: + err = {"error": resp.text, "status": resp.status_code} + result = {"error": True, "status": resp.status_code, "detail": err} + if resp.status_code in _RETRYABLE_STATUSES: + result["_no_cache"] = True + return result + return resp.json() + + +async def _do_post( + url: str, body: dict | None, params: dict | None, headers: dict +) -> Any: + client = await get_client() + resp = None + for attempt in range(3): + try: + resp = await client.post(url, json=body, params=params, headers=headers) + except (httpx.ConnectTimeout, httpx.ReadTimeout, httpx.ConnectError) as e: + if attempt < 2: + await asyncio.sleep(0.5 * (attempt + 1)) + continue + return {"error": True, "status": 0, "detail": str(e), "_no_cache": True} + if resp.status_code in _RETRYABLE_STATUSES: + if attempt < 2: + delay = float(resp.headers.get("Retry-After", 1 + attempt)) + await asyncio.sleep(delay) + continue + break + if resp is None: + return {"error": True, "status": 0, "detail": "No response"} + if resp.status_code >= 400: + try: + err = resp.json() + except Exception: + err = {"error": resp.text, "status": resp.status_code} + result = {"error": True, "status": resp.status_code, "detail": err} + if resp.status_code in _RETRYABLE_STATUSES: + result["_no_cache"] = True + return result + return resp.json() + + +def _bg_refresh_get(url: str, params: dict | None, headers: dict): + k = _key(url, params) + if k in _refreshing: + return + _refreshing.add(k) + + async def _refresh(): + try: + data = await _do_fetch(url, params, headers) + if not (isinstance(data, dict) and data.get("error")): + set_cached(url, data, params=params) + except Exception as e: + logger.debug(f"Background refresh failed for {url}: {e}") + finally: + _refreshing.discard(k) + + asyncio.get_event_loop().create_task(_refresh()) + + +def _bg_refresh_post(url: str, body: dict | None, params: dict | None, headers: dict): + k = _key(url, params, body) + if k in _refreshing: + return + _refreshing.add(k) + + async def _refresh(): + try: + data = await _do_post(url, body, params, headers) + if not (isinstance(data, dict) and data.get("error")): + set_cached(url, data, params=params, body=body) + except Exception as e: + logger.debug(f"Background refresh failed for {url}: {e}") + finally: + _refreshing.discard(k) + + asyncio.get_event_loop().create_task(_refresh()) + + +async def fetch_json( + url: str, params: dict | None = None, ttl: int = FRESH_TTL, headers: dict | None = None +) -> Any: + """GET with stale-while-revalidate cache.""" + cached, is_fresh = get_cached(url, params=params, ttl=ttl) + h = {} + if headers: + h.update(headers) + + if cached is not None: + if is_fresh: + return cached + _bg_refresh_get(url, params, h) + return cached + + data = await _do_fetch(url, params, h) + if isinstance(data, dict) and data.get("_no_cache"): + return data + if not (isinstance(data, dict) and data.get("error")): + set_cached(url, data, params=params) + return data + + +async def post_json( + url: str, body: dict | None = None, params: dict | None = None, ttl: int = FRESH_TTL +) -> Any: + """POST with stale-while-revalidate cache.""" + cached, is_fresh = get_cached(url, params=params, body=body, ttl=ttl) + h: dict[str, str] = {} + + if cached is not None: + if is_fresh: + return cached + _bg_refresh_post(url, body, params, h) + return cached + + data = await _do_post(url, body, params, h) + if isinstance(data, dict) and data.get("_no_cache"): + return data + if not (isinstance(data, dict) and data.get("error")): + set_cached(url, data, params=params, body=body) + return data diff --git a/api/main.py b/api/main.py new file mode 100644 index 0000000..3041e66 --- /dev/null +++ b/api/main.py @@ -0,0 +1,21 @@ +"""BNB Chain Skills API — FastAPI application.""" + +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware + +from api.routes.bnbchain import router + +app = FastAPI( + title="BNB Chain Skills API", + description="28 on-chain analysis tools for BNB Smart Chain", + version="1.0.0", +) + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_methods=["*"], + allow_headers=["*"], +) + +app.include_router(router, prefix="/mefai") diff --git a/api/routes/__init__.py b/api/routes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/routes/bnbchain.py b/api/routes/bnbchain.py new file mode 100644 index 0000000..939d740 --- /dev/null +++ b/api/routes/bnbchain.py @@ -0,0 +1,3773 @@ +"""BNB Chain on-chain data — TX Explorer, NFT Portfolio, Greenfield Storage.""" + +from fastapi import APIRouter, Query +from api.cache import fetch_json, post_json + +router = APIRouter() + +BSC_RPC = "https://bsc-dataseed1.binance.org" +GREENFIELD_API = "https://greenfield-sp.bnbchain.org" +BSC_SCAN = "https://api.bscscan.com/api" + + +async def _rpc_call(method: str, params: list, ttl: int = 30) -> dict: + """Execute a BSC JSON-RPC call via the cache layer.""" + return await post_json( + BSC_RPC, + body={"jsonrpc": "2.0", "method": method, "params": params, "id": 1}, + ttl=ttl, + ) + + +# ── TX Explorer ────────────────────────────────────────────────────── + + +@router.get("/block") +async def get_block( + number: str = Query("latest", description="Block number (hex) or 'latest'"), + full: bool = Query(False, description="Include full TX objects"), +): + """Get block by number. Use hex like '0x1234' or 'latest'.""" + return await _rpc_call("eth_getBlockByNumber", [number, full], ttl=15) + + +@router.get("/tx") +async def get_transaction( + hash: str = Query(..., min_length=66, max_length=66), +): + """Get transaction by hash.""" + return await _rpc_call("eth_getTransactionByHash", [hash], ttl=60) + + +@router.get("/receipt") +async def get_receipt( + hash: str = Query(..., min_length=66, max_length=66), +): + """Get transaction receipt (status, gas used, logs).""" + return await _rpc_call("eth_getTransactionReceipt", [hash], ttl=60) + + +@router.get("/balance") +async def get_balance( + address: str = Query(..., min_length=42, max_length=42), +): + """Get BNB balance for address.""" + return await _rpc_call("eth_getBalance", [address.lower(), "latest"], ttl=15) + + +@router.get("/block-number") +async def block_number(): + """Get latest block number.""" + return await _rpc_call("eth_blockNumber", [], ttl=5) + + +@router.get("/gas-price") +async def gas_price(): + """Get current gas price.""" + return await _rpc_call("eth_gasPrice", [], ttl=10) + + +# ── NFT Portfolio ──────────────────────────────────────────────────── + +# ERC721 balanceOf(address) selector +_ERC721_BALANCE = "0x70a08231" +# ERC721 tokenOfOwnerByIndex(address,index) selector +_ERC721_TOKEN_BY_INDEX = "0x2f745c59" + + +@router.get("/nft-balance") +async def nft_balance( + owner: str = Query(..., min_length=42, max_length=42), + contract: str = Query(..., min_length=42, max_length=42), +): + """Get NFT balance (ERC721) for owner at contract.""" + data = owner.lower().replace("0x", "").zfill(64) + return await _rpc_call( + "eth_call", + [{"to": contract.lower(), "data": f"{_ERC721_BALANCE}{data}"}, "latest"], + ttl=30, + ) + + +@router.get("/nft-tokens") +async def nft_tokens( + owner: str = Query(..., min_length=42, max_length=42), + contract: str = Query(..., min_length=42, max_length=42), + count: int = Query(10, ge=1, le=50), +): + """Get NFT token IDs owned by address (ERC721Enumerable).""" + addr_padded = owner.lower().replace("0x", "").zfill(64) + results = [] + for i in range(count): + idx = hex(i)[2:].zfill(64) + r = await _rpc_call( + "eth_call", + [ + { + "to": contract.lower(), + "data": f"{_ERC721_TOKEN_BY_INDEX}{addr_padded}{idx}", + }, + "latest", + ], + ttl=60, + ) + if r and r.get("result") and r["result"] != "0x": + results.append({"index": i, "tokenId": r["result"]}) + else: + break + return {"tokens": results, "contract": contract, "owner": owner} + + +# ── Greenfield Storage ─────────────────────────────────────────────── + + +@router.get("/greenfield/status") +async def greenfield_status(): + """Greenfield SP (Storage Provider) status.""" + return await fetch_json(f"{GREENFIELD_API}/status", ttl=30) + + +@router.get("/greenfield/buckets") +async def greenfield_buckets( + address: str = Query(..., min_length=42, max_length=42), +): + """List buckets for a Greenfield account address.""" + import httpx + import xml.etree.ElementTree as ET + + try: + async with httpx.AsyncClient(timeout=10) as client: + resp = await client.get( + f"{GREENFIELD_API}/", + headers={"X-Gnfd-User-Address": address}, + ) + if resp.status_code != 200: + return {"buckets": [], "error": resp.text} + # Parse XML response + root = ET.fromstring(resp.text) + ns = root.tag.split("}")[0] + "}" if "}" in root.tag else "" + buckets = [] + for bi_elem in root.iter(f"{ns}BucketInfo"): + bucket = {} + for child in bi_elem: + tag = child.tag.replace(ns, "") + bucket[tag] = child.text + if bucket: + buckets.append({"bucket_info": bucket}) + # Fallback: if no BucketInfo found, try simpler structure + if not buckets: + for elem in root: + tag = elem.tag.replace(ns, "") + if "bucket" in tag.lower(): + bucket = {} + for child in elem: + ctag = child.tag.replace(ns, "") + bucket[ctag] = child.text + if bucket: + buckets.append({"bucket_info": bucket}) + return {"buckets": buckets} + except Exception as e: + return {"buckets": [], "error": str(e)} + + +# ── Contract Reader ───────────────────────────────────────────────── + +# ERC20 selectors +_ERC20_NAME = "0x06fdde03" +_ERC20_SYMBOL = "0x95d89b41" +_ERC20_DECIMALS = "0x313ce567" +_ERC20_TOTAL_SUPPLY = "0x18160ddd" +_ERC20_BALANCE_OF = "0x70a08231" + + +@router.get("/token-info") +async def token_info( + contract: str = Query(..., min_length=42, max_length=42), +): + """Get ERC20 token info (name, symbol, decimals, totalSupply).""" + addr = contract.lower() + calls = [ + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_NAME}, "latest"], ttl=300), + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_SYMBOL}, "latest"], ttl=300), + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_DECIMALS}, "latest"], ttl=300), + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_TOTAL_SUPPLY}, "latest"], ttl=60), + ] + import asyncio + results = await asyncio.gather(*calls, return_exceptions=True) + return { + "contract": addr, + "name": results[0] if not isinstance(results[0], Exception) else None, + "symbol": results[1] if not isinstance(results[1], Exception) else None, + "decimals": results[2] if not isinstance(results[2], Exception) else None, + "totalSupply": results[3] if not isinstance(results[3], Exception) else None, + } + + +@router.get("/token-balance") +async def token_balance( + contract: str = Query(..., min_length=42, max_length=42), + address: str = Query(..., min_length=42, max_length=42), +): + """Get ERC20 token balance for address.""" + addr_padded = address.lower().replace("0x", "").zfill(64) + return await _rpc_call( + "eth_call", + [{"to": contract.lower(), "data": f"{_ERC20_BALANCE_OF}{addr_padded}"}, "latest"], + ttl=15, + ) + + +@router.get("/read-contract") +async def read_contract( + contract: str = Query(..., min_length=42, max_length=42), + data: str = Query(..., description="ABI-encoded call data (hex)"), +): + """Read from a smart contract (view/pure function call).""" + return await _rpc_call( + "eth_call", + [{"to": contract.lower(), "data": data}, "latest"], + ttl=15, + ) + + +@router.get("/estimate-gas") +async def estimate_gas( + to: str = Query(..., min_length=42, max_length=42), + data: str = Query("0x", description="Call data (hex)"), + value: str = Query("0x0", description="Value in wei (hex)"), +): + """Estimate gas for a transaction.""" + return await _rpc_call( + "eth_estimateGas", + [{"to": to.lower(), "data": data, "value": value}], + ttl=10, + ) + + +@router.get("/code") +async def get_code( + address: str = Query(..., min_length=42, max_length=42), +): + """Get contract bytecode at address (returns '0x' if EOA).""" + return await _rpc_call("eth_getCode", [address.lower(), "latest"], ttl=300) + + +# ── Network Info ──────────────────────────────────────────────────── + + +@router.get("/chain-id") +async def chain_id(): + """Get BSC chain ID.""" + return await _rpc_call("eth_chainId", [], ttl=3600) + + +@router.get("/peer-count") +async def peer_count(): + """Get connected peer count.""" + return await _rpc_call("net_peerCount", [], ttl=30) + + +@router.get("/syncing") +async def syncing(): + """Check if node is syncing.""" + return await _rpc_call("eth_syncing", [], ttl=10) + + +@router.get("/supported-networks") +async def supported_networks(): + """List supported EVM networks for BNB Chain MCP.""" + return { + "networks": [ + {"name": "BNB Smart Chain", "chainId": 56, "rpc": "https://bsc-dataseed1.binance.org", "symbol": "BNB", "explorer": "https://bscscan.com"}, + {"name": "BNB Smart Chain Testnet", "chainId": 97, "rpc": "https://data-seed-prebsc-1-s1.binance.org:8545", "symbol": "tBNB", "explorer": "https://testnet.bscscan.com"}, + {"name": "opBNB", "chainId": 204, "rpc": "https://opbnb-mainnet-rpc.bnbchain.org", "symbol": "BNB", "explorer": "https://opbnbscan.com"}, + {"name": "opBNB Testnet", "chainId": 5611, "rpc": "https://opbnb-testnet-rpc.bnbchain.org", "symbol": "tBNB", "explorer": "https://testnet.opbnbscan.com"}, + {"name": "BNB Greenfield", "chainId": 1017, "rpc": "https://greenfield-chain.bnbchain.org", "symbol": "BNB", "explorer": "https://greenfieldscan.com"}, + ] + } + + +# ── ERC-8004 Agent Registry ──────────────────────────────────────── + +# ERC-8004 Identity Registry on BSC +_ERC8004_REGISTRY = "0x5901DeB30816E210B2C4cFeDC21De5f288Ec4C73" +# Function selectors +_ERC8004_GET_AGENT = "0xc12c21c0" # getAgent(uint256) +_ERC8004_AGENT_URI = "0xc87b56dd" # tokenURI(uint256) +_ERC8004_BALANCE = "0x70a08231" # balanceOf(address) + + +@router.get("/erc8004/agent") +async def get_erc8004_agent( + token_id: str = Query(..., description="Agent token ID (decimal or hex)"), +): + """Get ERC-8004 agent info by token ID.""" + tid = token_id if token_id.startswith("0x") else hex(int(token_id)) + tid_padded = tid.replace("0x", "").zfill(64) + + import asyncio + uri_res, agent_res = await asyncio.gather( + _rpc_call("eth_call", [{"to": _ERC8004_REGISTRY, "data": f"{_ERC8004_AGENT_URI}{tid_padded}"}, "latest"], ttl=60), + _rpc_call("eth_call", [{"to": _ERC8004_REGISTRY, "data": f"{_ERC8004_GET_AGENT}{tid_padded}"}, "latest"], ttl=60), + ) + return {"tokenId": token_id, "tokenURI": uri_res, "agentData": agent_res} + + +@router.get("/erc8004/balance") +async def get_erc8004_balance( + address: str = Query(..., min_length=42, max_length=42), +): + """Get number of ERC-8004 agent NFTs owned by address.""" + addr_padded = address.lower().replace("0x", "").zfill(64) + return await _rpc_call( + "eth_call", + [{"to": _ERC8004_REGISTRY, "data": f"{_ERC8004_BALANCE}{addr_padded}"}, "latest"], + ttl=30, + ) + + +# ── Address Profile ───────────────────────────────────────────────── + + +# ── Market Data (DexScreener + CoinGecko) ───────────────────────── + +DEXSCREENER = "https://api.dexscreener.com" +COINGECKO = "https://api.coingecko.com/api/v3" + +# Well-known BSC token contracts +_BSC_TOKENS = [ + "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", # WBNB + "0x55d398326f99059fF775485246999027B3197955", # USDT + "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", # BUSD + "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", # CAKE + "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", # ETH + "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c", # BTCB +] + + +@router.get("/market/top-tokens") +async def market_top_tokens(): + """Top BSC tokens by volume from DexScreener.""" + return await fetch_json( + f"{DEXSCREENER}/latest/dex/tokens/" + ",".join(_BSC_TOKENS[:6]), + ttl=60, + ) + + +@router.get("/market/search") +async def market_search( + q: str = Query(..., min_length=1, max_length=100, description="Token name or address"), +): + """Search BSC tokens on DexScreener.""" + return await fetch_json( + f"{DEXSCREENER}/latest/dex/search", + params={"q": f"{q} bsc"}, + ttl=30, + ) + + +@router.get("/market/token") +async def market_token( + address: str = Query(..., min_length=42, max_length=42, description="Token contract"), +): + """Get token market data from DexScreener.""" + return await fetch_json( + f"{DEXSCREENER}/latest/dex/tokens/{address}", + ttl=30, + ) + + +@router.get("/market/new-pairs") +async def market_new_pairs(): + """Latest BSC token pairs from DexScreener.""" + return await fetch_json( + f"{DEXSCREENER}/token-profiles/latest/v1", + ttl=60, + ) + + +@router.get("/address-profile") +async def address_profile( + address: str = Query(..., min_length=42, max_length=42), +): + """Comprehensive address profile: balance, tx count, contract check.""" + addr = address.lower() + import asyncio + bal, nonce, code = await asyncio.gather( + _rpc_call("eth_getBalance", [addr, "latest"], ttl=15), + _rpc_call("eth_getTransactionCount", [addr, "latest"], ttl=15), + _rpc_call("eth_getCode", [addr, "latest"], ttl=300), + ) + is_contract = code and code.get("result") and code["result"] != "0x" + return { + "address": addr, + "balance": bal, + "transactionCount": nonce, + "isContract": is_contract, + "codeSize": len(code.get("result", "0x")) // 2 - 1 if is_contract else 0, + } + + +# ── Mefai Skills: Advanced combined on-chain + market ───────────── + +_TOP_BSC_PORTFOLIO = { + "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c": "WBNB", + "0x55d398326f99059fF775485246999027B3197955": "USDT", + "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56": "BUSD", + "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82": "CAKE", + "0x2170Ed0880ac9A755fd29B2688956BD959F933F8": "ETH", + "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c": "BTCB", + "0x1D2F0da169ceB9fC7B3144628dB156f3F6c60dBE": "XRP", + "0x3EE2200Efb3400fAbB9AacF31297cBdD1d435D47": "ADA", +} + + +@router.get("/mefai/wallet-scanner") +async def mefai_wallet_scanner( + address: str = Query(..., min_length=42, max_length=42), +): + """Mefai Wallet Scanner: BNB balance + top token holdings with USD prices.""" + import asyncio + + addr = address.lower() + addr_padded = addr.replace("0x", "").zfill(64) + + # Fetch BNB balance + all token balances in parallel + tasks = [_rpc_call("eth_getBalance", [addr, "latest"], ttl=15)] + token_list = list(_TOP_BSC_PORTFOLIO.items()) + for contract, _ in token_list: + tasks.append( + _rpc_call( + "eth_call", + [{"to": contract.lower(), "data": f"{_ERC20_BALANCE_OF}{addr_padded}"}, "latest"], + ttl=15, + ) + ) + results = await asyncio.gather(*tasks, return_exceptions=True) + + # Parse BNB + bnb_raw = results[0] + bnb_wei = int(bnb_raw.get("result", "0x0"), 16) if isinstance(bnb_raw, dict) else 0 + bnb_bal = bnb_wei / 1e18 + + # Parse tokens + holdings = [] + for i, (contract, symbol) in enumerate(token_list): + r = results[i + 1] + if isinstance(r, Exception) or not isinstance(r, dict): + continue + raw = r.get("result", "0x0") + if raw and raw != "0x": + val = int(raw, 16) + if val > 0: + # Use 18 decimals for all (standard for BSC top tokens) + bal = val / 1e18 + holdings.append({"contract": contract, "symbol": symbol, "balance": bal}) + + # Fetch market prices for tokens with balances + price_contracts = [h["contract"] for h in holdings] + if price_contracts: + addrs = ",".join(price_contracts[:6]) + try: + market = await fetch_json(f"{DEXSCREENER}/latest/dex/tokens/{addrs}", ttl=60) + pairs = market.get("pairs", []) if isinstance(market, dict) else [] + price_map = {} + for p in pairs: + addr_key = (p.get("baseToken", {}).get("address") or "").lower() + if addr_key and addr_key not in price_map and p.get("priceUsd"): + price_map[addr_key] = { + "price": float(p["priceUsd"]), + "change24h": p.get("priceChange", {}).get("h24"), + } + for h in holdings: + pm = price_map.get(h["contract"].lower(), {}) + h["priceUsd"] = pm.get("price") + h["change24h"] = pm.get("change24h") + if h["priceUsd"]: + h["valueUsd"] = h["balance"] * h["priceUsd"] + except Exception: + pass + + # Get BNB price + try: + bnb_market = await fetch_json( + f"{DEXSCREENER}/latest/dex/tokens/0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + ttl=60, + ) + bnb_pairs = bnb_market.get("pairs", []) if isinstance(bnb_market, dict) else [] + bnb_price = float(bnb_pairs[0]["priceUsd"]) if bnb_pairs else None + except Exception: + bnb_price = None + + return { + "address": addr, + "bnb": {"balance": bnb_bal, "priceUsd": bnb_price, "valueUsd": bnb_bal * bnb_price if bnb_price else None}, + "tokens": holdings, + } + + +@router.get("/mefai/contract-audit") +async def mefai_contract_audit( + address: str = Query(..., min_length=42, max_length=42), +): + """Mefai Contract Audit: on-chain verification + market data + safety score.""" + import asyncio + + addr = address.lower() + dead = "000000000000000000000000000000000000dead".zfill(64) + + # On-chain checks in parallel + code_call, name_call, sym_call, dec_call, supply_call, dead_bal = await asyncio.gather( + _rpc_call("eth_getCode", [addr, "latest"], ttl=300), + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_NAME}, "latest"], ttl=300), + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_SYMBOL}, "latest"], ttl=300), + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_DECIMALS}, "latest"], ttl=300), + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_TOTAL_SUPPLY}, "latest"], ttl=60), + _rpc_call("eth_call", [{"to": addr, "data": f"{_ERC20_BALANCE_OF}{dead}"}, "latest"], ttl=60), + ) + + has_code = code_call.get("result", "0x") not in ("0x", "0x0", None, "") + code_size = (len(code_call.get("result", "0x")) - 2) // 2 if has_code else 0 + + # Decode token info + def _dec_str(hex_val): + if not hex_val or hex_val == "0x" or len(hex_val) < 66: + return "" + try: + raw = hex_val.replace("0x", "") + offset = int(raw[:64], 16) * 2 + length = int(raw[offset:offset + 64], 16) + data = raw[offset + 64:offset + 64 + length * 2] + return bytes.fromhex(data).decode("utf-8", errors="ignore").strip("\x00") + except Exception: + return "" + + name = _dec_str(name_call.get("result")) + symbol = _dec_str(sym_call.get("result")) + decimals = int(dec_call.get("result", "0x0"), 16) if dec_call.get("result") else None + total_supply_raw = int(supply_call.get("result", "0x0"), 16) if supply_call.get("result") else 0 + dead_bal_raw = int(dead_bal.get("result", "0x0"), 16) if dead_bal.get("result") else 0 + + dec = decimals if decimals and decimals <= 18 else 18 + total_supply = total_supply_raw / (10 ** dec) if total_supply_raw else 0 + burned = dead_bal_raw / (10 ** dec) if dead_bal_raw else 0 + burn_pct = (burned / total_supply * 100) if total_supply > 0 else 0 + + # Market data + try: + market = await fetch_json(f"{DEXSCREENER}/latest/dex/tokens/{addr}", ttl=30) + pairs = [p for p in (market.get("pairs") or []) if p.get("chainId") == "bsc"] + except Exception: + pairs = [] + + top = pairs[0] if pairs else {} + price = float(top.get("priceUsd", 0)) if top.get("priceUsd") else None + volume = top.get("volume", {}).get("h24") + liquidity = top.get("liquidity", {}).get("usd") + pair_count = len(pairs) + dex = top.get("dexId") + change24h = top.get("priceChange", {}).get("h24") + + # Safety score (0-100) + score = 0 + checks = [] + if has_code: + score += 15 + checks.append({"name": "Contract Code", "pass": True, "detail": f"{code_size} bytes"}) + else: + checks.append({"name": "Contract Code", "pass": False, "detail": "No code (EOA)"}) + if name: + score += 10 + checks.append({"name": "ERC20 name()", "pass": True, "detail": name}) + else: + checks.append({"name": "ERC20 name()", "pass": False, "detail": "Not readable"}) + if symbol: + score += 10 + checks.append({"name": "ERC20 symbol()", "pass": True, "detail": symbol}) + else: + checks.append({"name": "ERC20 symbol()", "pass": False, "detail": "Not readable"}) + if decimals is not None and 0 <= decimals <= 18: + score += 10 + checks.append({"name": "Decimals", "pass": True, "detail": str(decimals)}) + else: + checks.append({"name": "Decimals", "pass": False, "detail": "Invalid"}) + if total_supply > 0: + score += 10 + checks.append({"name": "Total Supply", "pass": True, "detail": f"{total_supply:,.0f}"}) + else: + checks.append({"name": "Total Supply", "pass": False, "detail": "Zero or unreadable"}) + if liquidity and liquidity > 1000: + score += 15 + checks.append({"name": "DEX Liquidity", "pass": True, "detail": f"${liquidity:,.0f}"}) + elif liquidity: + score += 5 + checks.append({"name": "DEX Liquidity", "pass": False, "detail": f"${liquidity:,.0f} (low)"}) + else: + checks.append({"name": "DEX Liquidity", "pass": False, "detail": "Not found"}) + if pair_count >= 2: + score += 10 + checks.append({"name": "DEX Pairs", "pass": True, "detail": f"{pair_count} pairs"}) + elif pair_count == 1: + score += 5 + checks.append({"name": "DEX Pairs", "pass": True, "detail": "1 pair"}) + else: + checks.append({"name": "DEX Pairs", "pass": False, "detail": "No pairs"}) + if volume and volume > 1000: + score += 10 + checks.append({"name": "24h Volume", "pass": True, "detail": f"${volume:,.0f}"}) + else: + checks.append({"name": "24h Volume", "pass": volume is not None and volume > 0, "detail": f"${volume:,.0f}" if volume else "No volume"}) + if burn_pct > 1: + score += 10 + checks.append({"name": "Burn Address", "pass": True, "detail": f"{burn_pct:.1f}% burned"}) + else: + checks.append({"name": "Burn Address", "pass": False, "detail": f"{burn_pct:.2f}% burned"}) + + grade = "A" if score >= 80 else "B" if score >= 60 else "C" if score >= 40 else "D" if score >= 20 else "F" + + return { + "address": addr, + "name": name or (top.get("baseToken", {}).get("name")), + "symbol": symbol or (top.get("baseToken", {}).get("symbol")), + "decimals": decimals, + "totalSupply": total_supply, + "burnedPct": burn_pct, + "codeSize": code_size, + "price": price, + "change24h": change24h, + "volume24h": volume, + "liquidity": liquidity, + "pairCount": pair_count, + "dex": dex, + "score": score, + "grade": grade, + "checks": checks, + } + + +@router.get("/mefai/liquidity-pulse") +async def mefai_liquidity_pulse(): + """Mefai Liquidity Pulse: BSC tokens ranked by volume/liquidity ratio.""" + data = await fetch_json( + f"{DEXSCREENER}/latest/dex/tokens/" + ",".join(_BSC_TOKENS), + ttl=60, + ) + pairs = data.get("pairs", []) if isinstance(data, dict) else [] + bsc_pairs = [p for p in pairs if p.get("chainId") == "bsc"] + + # Deduplicate by base token, keep highest volume + seen = {} + for p in bsc_pairs: + sym = (p.get("baseToken", {}).get("symbol") or "").upper() + if not sym: + continue + vol = p.get("volume", {}).get("h24") or 0 + if sym not in seen or vol > (seen[sym].get("volume", {}).get("h24") or 0): + seen[sym] = p + + results = [] + for sym, p in seen.items(): + vol = p.get("volume", {}).get("h24") or 0 + liq = p.get("liquidity", {}).get("usd") or 0 + ratio = vol / liq if liq > 0 else 0 + results.append({ + "symbol": sym, + "name": p.get("baseToken", {}).get("name", ""), + "address": p.get("baseToken", {}).get("address", ""), + "price": p.get("priceUsd"), + "change24h": p.get("priceChange", {}).get("h24"), + "change1h": p.get("priceChange", {}).get("h1"), + "volume24h": vol, + "liquidity": liq, + "vlRatio": round(ratio, 2), + "dex": p.get("dexId"), + "txns24h": (p.get("txns", {}).get("h24", {}).get("buys", 0) or 0) + (p.get("txns", {}).get("h24", {}).get("sells", 0) or 0), + "buys24h": p.get("txns", {}).get("h24", {}).get("buys", 0), + "sells24h": p.get("txns", {}).get("h24", {}).get("sells", 0), + }) + + results.sort(key=lambda x: x["vlRatio"], reverse=True) + return {"tokens": results} + + +# ── Mefai Skills Batch 2: CZ-approved BNB ecosystem showcase ───── + +_DEAD_ADDR = "0x000000000000000000000000000000000000dEaD" +_ZERO_ADDR = "0x0000000000000000000000000000000000000000" + +# Top burn-tracked tokens +_BURN_TOKENS = { + "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c": ("WBNB", 18), + "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82": ("CAKE", 18), + "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56": ("BUSD", 18), + "0x55d398326f99059fF775485246999027B3197955": ("USDT", 18), + "0x2170Ed0880ac9A755fd29B2688956BD959F933F8": ("ETH", 18), + "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c": ("BTCB", 18), + "0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3": ("DAI", 18), + "0xba2ae424d960c26247dd6c32edc70b295c744c43": ("DOGE", 8), +} + +# Top DeFi protocols on BSC +_DEFI_TOKENS = [ + "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", # CAKE (PancakeSwap) + "0xcF6BB5389c92Bdda8a3747Ddb454cB7a64626C63", # XVS (Venus) + "0xa184088a740c695E156F91f5cC086a06bb78b827", # AUTO (AutoFarm) + "0xfb6115445bff7b52feb98650c87f44907e58f802", # AAVE on BSC + "0x4338665CBB7B2485A8855A139b75D5e34AB0DB94", # LTC (Litecoin) + "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", # USDC + "0x1D2F0da169ceB9fC7B3144628dB156f3F6c60dBE", # XRP + "0x7083609fCE4d1d8Dc0C979AAb8c869Ea2C873402", # DOT + "0xBf5140A22578168FD562DCcF235E5D43A02ce9B1", # UNI + "0x3EE2200Efb3400fAbB9AacF31297cBdD1d435D47", # ADA +] + + +@router.get("/mefai/burn-tracker") +async def mefai_burn_tracker(): + """BNB Burn Tracker: burned tokens at dead/zero addresses with USD values.""" + import asyncio + + dead_padded = _DEAD_ADDR.replace("0x", "").zfill(64) + zero_padded = _ZERO_ADDR.replace("0x", "").zfill(64) + + # Query BNB balance at dead+zero, plus all token balances at dead addr + tasks = [ + _rpc_call("eth_getBalance", [_DEAD_ADDR, "latest"], ttl=60), + _rpc_call("eth_getBalance", [_ZERO_ADDR, "latest"], ttl=60), + ] + token_list = list(_BURN_TOKENS.items()) + for contract, _ in token_list: + # balanceOf(dead) + tasks.append(_rpc_call("eth_call", [ + {"to": contract, "data": f"{_ERC20_BALANCE_OF}{dead_padded}"}, + "latest", + ], ttl=60)) + # totalSupply + tasks.append(_rpc_call("eth_call", [ + {"to": contract, "data": _ERC20_TOTAL_SUPPLY}, + "latest", + ], ttl=60)) + + results = await asyncio.gather(*tasks, return_exceptions=True) + + # BNB burned + bnb_dead = 0 + bnb_zero = 0 + try: + bnb_dead = int(results[0].get("result", "0x0"), 16) / 1e18 + except Exception: + pass + try: + bnb_zero = int(results[1].get("result", "0x0"), 16) / 1e18 + except Exception: + pass + + # Get all prices in a single batch DexScreener call (max 30 addresses) + all_addrs = ["0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"] + [c for c in _BURN_TOKENS.keys()] + price_map = {} + try: + px = await fetch_json( + f"{DEXSCREENER}/latest/dex/tokens/" + ",".join(all_addrs), + ttl=120, + ) + for p in (px.get("pairs", []) if isinstance(px, dict) else []): + if p.get("chainId") != "bsc" or not p.get("priceUsd"): + continue + # Track base token price + addr = (p.get("baseToken", {}).get("address") or "").lower() + vol = p.get("volume", {}).get("h24") or 0 + if addr and (addr not in price_map or vol > price_map[addr][1]): + price_map[addr] = (float(p["priceUsd"]), vol) + # Also track quote token price via priceNative inverse + qaddr = (p.get("quoteToken", {}).get("address") or "").lower() + if qaddr and qaddr not in price_map: + try: + base_price = float(p["priceUsd"]) + native = float(p.get("priceNative") or 0) + if native > 0: + price_map[qaddr] = (base_price / native, 0) + except Exception: + pass + except Exception: + pass + # Hardcode stablecoin prices as fallback + for sa in ["0x55d398326f99059ff775485246999027b3197955", + "0xe9e7cea3dedca5984780bafc599bd69add087d56", + "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d"]: + if sa not in price_map: + price_map[sa] = (1.0, 0) + + bnb_price = price_map.get("0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c", (0, 0))[0] + + tokens = [] + for i, (contract, (sym, dec)) in enumerate(token_list): + idx = 2 + i * 2 + burned = 0 + supply = 0 + try: + r = results[idx] + if isinstance(r, dict) and r.get("result"): + burned = int(r["result"], 16) / (10 ** dec) + except Exception: + pass + try: + r = results[idx + 1] + if isinstance(r, dict) and r.get("result"): + supply = int(r["result"], 16) / (10 ** dec) + except Exception: + pass + pct = (burned / supply * 100) if supply > 0 else 0 + price = price_map.get(contract.lower(), (0, 0))[0] + + tokens.append({ + "symbol": sym, + "address": contract, + "burned": round(burned, 4), + "totalSupply": round(supply, 2), + "burnedPct": round(pct, 4), + "priceUsd": price, + "burnedValueUsd": round(burned * price, 2), + }) + + total_bnb_burned = bnb_dead + bnb_zero + return { + "bnb": { + "deadBalance": round(bnb_dead, 4), + "zeroBalance": round(bnb_zero, 4), + "totalBurned": round(total_bnb_burned, 4), + "priceUsd": bnb_price, + "burnedValueUsd": round(total_bnb_burned * bnb_price, 2), + }, + "tokens": tokens, + "totalBurnedUsd": round( + total_bnb_burned * bnb_price + sum(t["burnedValueUsd"] for t in tokens), 2 + ), + } + + +@router.get("/mefai/pancakeswap-arena") +async def mefai_pancakeswap_arena(): + """PancakeSwap Arena: top trading pairs with volume and price data.""" + data = await fetch_json( + f"{DEXSCREENER}/latest/dex/tokens/" + ",".join(_BSC_TOKENS[:6]), + ttl=30, + ) + pairs = data.get("pairs", []) if isinstance(data, dict) else [] + # Only PancakeSwap pairs + pcs_pairs = [p for p in pairs if p.get("chainId") == "bsc" and "pancake" in (p.get("dexId") or "").lower()] + + # Sort by volume desc + pcs_pairs.sort(key=lambda p: p.get("volume", {}).get("h24") or 0, reverse=True) + + results = [] + seen = set() + for p in pcs_pairs[:20]: + pair_key = p.get("pairAddress", "") + if pair_key in seen: + continue + seen.add(pair_key) + base = p.get("baseToken", {}) + quote = p.get("quoteToken", {}) + vol = p.get("volume", {}) + chg = p.get("priceChange", {}) + txns = p.get("txns", {}).get("h24", {}) + liq = p.get("liquidity", {}) + results.append({ + "pair": f"{base.get('symbol', '?')}/{quote.get('symbol', '?')}", + "baseSymbol": base.get("symbol", ""), + "baseAddress": base.get("address", ""), + "quoteSymbol": quote.get("symbol", ""), + "price": p.get("priceUsd"), + "priceNative": p.get("priceNative"), + "change5m": chg.get("m5"), + "change1h": chg.get("h1"), + "change6h": chg.get("h6"), + "change24h": chg.get("h24"), + "volume24h": vol.get("h24") or 0, + "volume6h": vol.get("h6") or 0, + "volume1h": vol.get("h1") or 0, + "liquidity": liq.get("usd") or 0, + "buys24h": txns.get("buys") or 0, + "sells24h": txns.get("sells") or 0, + "pairAddress": pair_key, + "pairCreated": p.get("pairCreatedAt"), + }) + + return {"pairs": results} + + +@router.get("/mefai/chain-vitals") +async def mefai_chain_vitals(): + """BSC Chain Vitals: real-time TPS, block time, gas efficiency from last N blocks.""" + import asyncio + + # Get latest block number + bn = await _rpc_call("eth_blockNumber", [], ttl=5) + latest = 0 + try: + latest = int(bn.get("result", "0x0"), 16) + except Exception: + pass + if latest == 0: + return {"error": "Cannot fetch block number"} + + # Fetch last 10 blocks in parallel + n_blocks = 10 + tasks = [] + for i in range(n_blocks): + num = latest - i + tasks.append(_rpc_call("eth_getBlockByNumber", [hex(num), False], ttl=15)) + + # Also get gas price + tasks.append(_rpc_call("eth_gasPrice", [], ttl=10)) + results = await asyncio.gather(*tasks, return_exceptions=True) + + blocks = [] + for i in range(n_blocks): + r = results[i] + if isinstance(r, dict) and r.get("result"): + b = r["result"] + blocks.append({ + "number": int(b.get("number", "0x0"), 16), + "timestamp": int(b.get("timestamp", "0x0"), 16), + "txCount": len(b.get("transactions", [])), + "gasUsed": int(b.get("gasUsed", "0x0"), 16), + "gasLimit": int(b.get("gasLimit", "0x0"), 16), + "size": int(b.get("size", "0x0"), 16), + "miner": b.get("miner", ""), + }) + + gas_price = 0 + try: + gas_price = int(results[n_blocks].get("result", "0x0"), 16) / 1e9 + except Exception: + pass + + if len(blocks) < 2: + return {"error": "Insufficient block data"} + + # Calculate metrics + blocks.sort(key=lambda b: b["number"]) + block_times = [] + for i in range(1, len(blocks)): + dt = blocks[i]["timestamp"] - blocks[i - 1]["timestamp"] + if dt > 0: + block_times.append(dt) + + avg_block_time = sum(block_times) / len(block_times) if block_times else 3 + total_tx = sum(b["txCount"] for b in blocks) + time_span = blocks[-1]["timestamp"] - blocks[0]["timestamp"] + tps = total_tx / time_span if time_span > 0 else 0 + avg_gas_used = sum(b["gasUsed"] for b in blocks) / len(blocks) + avg_gas_limit = sum(b["gasLimit"] for b in blocks) / len(blocks) + gas_utilization = (avg_gas_used / avg_gas_limit * 100) if avg_gas_limit > 0 else 0 + avg_tx_per_block = total_tx / len(blocks) + avg_block_size = sum(b["size"] for b in blocks) / len(blocks) + + # Validator distribution + validators = {} + for b in blocks: + v = b["miner"] + validators[v] = validators.get(v, 0) + 1 + + return { + "latestBlock": blocks[-1]["number"], + "blocksAnalyzed": len(blocks), + "avgBlockTime": round(avg_block_time, 2), + "tps": round(tps, 1), + "avgTxPerBlock": round(avg_tx_per_block, 1), + "gasPrice": round(gas_price, 2), + "gasUtilization": round(gas_utilization, 1), + "avgGasUsed": int(avg_gas_used), + "avgGasLimit": int(avg_gas_limit), + "avgBlockSize": int(avg_block_size), + "totalTx": total_tx, + "timeSpan": time_span, + "validators": [ + {"address": addr, "blocks": count} + for addr, count in sorted(validators.items(), key=lambda x: -x[1]) + ], + "blockDetails": [ + { + "number": b["number"], + "txCount": b["txCount"], + "gasUsed": b["gasUsed"], + "gasLimit": b["gasLimit"], + "gasPct": round(b["gasUsed"] / b["gasLimit"] * 100, 1) if b["gasLimit"] > 0 else 0, + "size": b["size"], + "time": b["timestamp"], + } + for b in blocks + ], + } + + +@router.get("/mefai/token-trends") +async def mefai_token_trends(): + """Token Trends: trending and boosted tokens on BSC from DexScreener.""" + import asyncio + + profiles_task = fetch_json(f"{DEXSCREENER}/token-profiles/latest/v1", ttl=60) + boosts_task = fetch_json(f"{DEXSCREENER}/token-boosts/latest/v1", ttl=60) + + profiles_raw, boosts_raw = await asyncio.gather(profiles_task, boosts_task, return_exceptions=True) + + # Filter BSC profiles + profiles = [] + if isinstance(profiles_raw, list): + for p in profiles_raw: + if p.get("chainId") == "bsc" and p.get("tokenAddress"): + profiles.append({ + "address": p["tokenAddress"], + "description": p.get("description", ""), + "icon": p.get("icon", ""), + "url": p.get("url", ""), + }) + # Max 15 + profiles = profiles[:15] + + # Filter BSC boosts + boosts = [] + if isinstance(boosts_raw, list): + for b in boosts_raw: + if b.get("chainId") == "bsc" and b.get("tokenAddress"): + boosts.append({ + "address": b["tokenAddress"], + "amount": b.get("amount", 0), + "totalAmount": b.get("totalAmount", 0), + "icon": b.get("icon", ""), + "description": b.get("description", ""), + "url": b.get("url", ""), + }) + boosts = boosts[:15] + + # Enrich with market data: gather all unique addresses + all_addrs = list({p["address"] for p in profiles} | {b["address"] for b in boosts}) + if not all_addrs: + return {"profiles": [], "boosts": [], "enriched": []} + + # Batch fetch from DexScreener (max 30 addresses at once) + enriched = [] + batch = all_addrs[:30] + try: + market = await fetch_json( + f"{DEXSCREENER}/latest/dex/tokens/" + ",".join(batch), + ttl=60, + ) + pairs = market.get("pairs", []) if isinstance(market, dict) else [] + # Group by token address, take best pair + token_data = {} + for p in pairs: + if p.get("chainId") != "bsc": + continue + addr = (p.get("baseToken", {}).get("address") or "").lower() + vol = p.get("volume", {}).get("h24") or 0 + if addr not in token_data or vol > (token_data[addr].get("volume", {}).get("h24") or 0): + token_data[addr] = p + + for addr in batch: + p = token_data.get(addr.lower()) + if not p: + continue + base = p.get("baseToken", {}) + chg = p.get("priceChange", {}) + enriched.append({ + "address": addr, + "symbol": base.get("symbol", ""), + "name": base.get("name", ""), + "price": p.get("priceUsd"), + "change1h": chg.get("h1"), + "change24h": chg.get("h24"), + "volume24h": p.get("volume", {}).get("h24") or 0, + "liquidity": p.get("liquidity", {}).get("usd") or 0, + "dex": p.get("dexId", ""), + "buys24h": p.get("txns", {}).get("h24", {}).get("buys") or 0, + "sells24h": p.get("txns", {}).get("h24", {}).get("sells") or 0, + "isBoosted": any(b["address"].lower() == addr.lower() for b in boosts), + "boostAmount": next((b.get("totalAmount", 0) for b in boosts if b["address"].lower() == addr.lower()), 0), + }) + except Exception: + pass + + enriched.sort(key=lambda x: x.get("volume24h", 0), reverse=True) + return {"profiles": profiles, "boosts": boosts, "enriched": enriched} + + +@router.get("/mefai/defi-leaderboard") +async def mefai_defi_leaderboard(): + """DeFi Leaderboard: top BSC DeFi protocols by volume and liquidity.""" + data = await fetch_json( + f"{DEXSCREENER}/latest/dex/tokens/" + ",".join(_DEFI_TOKENS), + ttl=60, + ) + pairs = data.get("pairs", []) if isinstance(data, dict) else [] + bsc_pairs = [p for p in pairs if p.get("chainId") == "bsc"] + + # Group by base token, aggregate metrics + tokens = {} + for p in bsc_pairs: + base = p.get("baseToken", {}) + sym = base.get("symbol", "").upper() + addr = base.get("address", "") + if not sym: + continue + + vol = p.get("volume", {}).get("h24") or 0 + liq = p.get("liquidity", {}).get("usd") or 0 + + if sym not in tokens: + tokens[sym] = { + "symbol": sym, + "name": base.get("name", ""), + "address": addr, + "price": p.get("priceUsd"), + "change24h": p.get("priceChange", {}).get("h24"), + "change1h": p.get("priceChange", {}).get("h1"), + "totalVolume24h": 0, + "totalLiquidity": 0, + "pairCount": 0, + "topDex": "", + "topDexVolume": 0, + "totalBuys": 0, + "totalSells": 0, + } + t = tokens[sym] + t["totalVolume24h"] += vol + t["totalLiquidity"] += liq + t["pairCount"] += 1 + txns = p.get("txns", {}).get("h24", {}) + t["totalBuys"] += txns.get("buys") or 0 + t["totalSells"] += txns.get("sells") or 0 + if vol > t["topDexVolume"]: + t["topDex"] = p.get("dexId", "") + t["topDexVolume"] = vol + t["price"] = p.get("priceUsd") + t["change24h"] = p.get("priceChange", {}).get("h24") + + results = list(tokens.values()) + for t in results: + t["totalVolume24h"] = round(t["totalVolume24h"], 2) + t["totalLiquidity"] = round(t["totalLiquidity"], 2) + results.sort(key=lambda x: x["totalVolume24h"], reverse=True) + return {"tokens": results} + + +@router.get("/mefai/bsc-supremacy") +async def mefai_bsc_supremacy(): + """BSC vs ETH: real-time comparison proving BSC superiority.""" + import asyncio + + # BSC real data + bn_task = _rpc_call("eth_blockNumber", [], ttl=5) + gas_task = _rpc_call("eth_gasPrice", [], ttl=10) + bn_res, gas_res = await asyncio.gather(bn_task, gas_task, return_exceptions=True) + + bsc_block = 0 + try: + bsc_block = int(bn_res.get("result", "0x0"), 16) + except Exception: + pass + bsc_gas = 0 + try: + bsc_gas = int(gas_res.get("result", "0x0"), 16) / 1e9 + except Exception: + pass + + # Fetch last 5 BSC blocks for real TPS + block time + tasks = [] + for i in range(5): + tasks.append(_rpc_call("eth_getBlockByNumber", [hex(bsc_block - i), False], ttl=15)) + blocks_res = await asyncio.gather(*tasks, return_exceptions=True) + + bsc_blocks = [] + for r in blocks_res: + if isinstance(r, dict) and r.get("result"): + b = r["result"] + bsc_blocks.append({ + "number": int(b.get("number", "0x0"), 16), + "timestamp": int(b.get("timestamp", "0x0"), 16), + "txCount": len(b.get("transactions", [])), + "gasUsed": int(b.get("gasUsed", "0x0"), 16), + "gasLimit": int(b.get("gasLimit", "0x0"), 16), + }) + bsc_blocks.sort(key=lambda b: b["number"]) + + bsc_block_time = 3.0 + bsc_tps = 0 + if len(bsc_blocks) >= 2: + times = [bsc_blocks[i]["timestamp"] - bsc_blocks[i - 1]["timestamp"] for i in range(1, len(bsc_blocks))] + times = [t for t in times if t > 0] + if times: + bsc_block_time = sum(times) / len(times) + total_tx = sum(b["txCount"] for b in bsc_blocks) + span = bsc_blocks[-1]["timestamp"] - bsc_blocks[0]["timestamp"] + bsc_tps = total_tx / span if span > 0 else 0 + + avg_gas_used = sum(b["gasUsed"] for b in bsc_blocks) / len(bsc_blocks) if bsc_blocks else 0 + avg_gas_limit = sum(b["gasLimit"] for b in bsc_blocks) / len(bsc_blocks) if bsc_blocks else 1 + bsc_gas_util = avg_gas_used / avg_gas_limit * 100 if avg_gas_limit > 0 else 0 + + # BNB price + bnb_price = 0 + try: + px = await fetch_json( + f"{DEXSCREENER}/latest/dex/tokens/0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + ttl=60, + ) + for p in px.get("pairs", []): + if p.get("chainId") == "bsc" and p.get("priceUsd"): + bnb_price = float(p["priceUsd"]) + break + except Exception: + pass + + # BSC transfer cost in USD + bsc_transfer_cost = bsc_gas * 21000 / 1e9 * bnb_price + bsc_swap_cost = bsc_gas * 200000 / 1e9 * bnb_price + + # ETH known averages (updated periodically) + eth_block_time = 12.1 + eth_tps = 15 + eth_gas_gwei = 8.0 + eth_price = 1950.0 + eth_transfer_cost = eth_gas_gwei * 21000 / 1e9 * eth_price + eth_swap_cost = eth_gas_gwei * 200000 / 1e9 * eth_price + + # Multipliers + speed_mult = round(eth_block_time / bsc_block_time, 1) if bsc_block_time > 0 else 0 + tps_mult = round(bsc_tps / eth_tps, 1) if eth_tps > 0 else 0 + cost_mult = round(eth_transfer_cost / bsc_transfer_cost, 0) if bsc_transfer_cost > 0 else 0 + + return { + "bsc": { + "blockTime": round(bsc_block_time, 1), + "tps": round(bsc_tps, 1), + "gasGwei": round(bsc_gas, 2), + "gasUtilization": round(bsc_gas_util, 1), + "transferCost": round(bsc_transfer_cost, 4), + "swapCost": round(bsc_swap_cost, 4), + "latestBlock": bsc_block, + "validators": 40, + "finality": "1 block (~1s)", + "nativePrice": bnb_price, + }, + "eth": { + "blockTime": eth_block_time, + "tps": eth_tps, + "gasGwei": eth_gas_gwei, + "transferCost": round(eth_transfer_cost, 4), + "swapCost": round(eth_swap_cost, 4), + "validators": 1000000, + "finality": "2 epochs (~12.8min)", + "nativePrice": eth_price, + }, + "advantage": { + "speedMultiplier": speed_mult, + "tpsMultiplier": tps_mult, + "costMultiplier": int(cost_mult), + "transferSavings": round(eth_transfer_cost - bsc_transfer_cost, 4), + "swapSavings": round(eth_swap_cost - bsc_swap_cost, 4), + }, + } + + +# Well-known BSC wallets for labeling +_KNOWN_WALLETS = { + "0x8894e0a0c962cb723c1ef8a1b6737b4e1e2ae02b": "Binance Hot Wallet", + "0xe2fc31f816a9b94326492132018c3aecc4a93ae1": "Binance Hot 2", + "0x3c783c21a0383057d128bae431894a5c19f9cf06": "Binance Hot 3", + "0xf977814e90da44bfa03b6295a0616a897441acec": "Binance Cold", + "0x4b16c5de96eb2117bbe5fd171e4d203624b014aa": "CZ Wallet", + "0x631fc1ea2270e98fbd9d92658ece0f5a269aa161": "Binance Staking", + "0xb1256d6b31e4ae87da1d56e5890c66be7f1c038e": "PancakeSwap", + "0x10ed43c718714eb63d5aa57b78b54917c3e6fe49": "PancakeSwap Router", + "0x000000000000000000000000000000000000dead": "Burn Address", + "0x0000000000000000000000000000000000000000": "Zero Address", +} + + +@router.get("/mefai/smart-money") +async def mefai_smart_money(): + """Smart Money Radar: whale transactions and flow analysis from recent blocks.""" + import asyncio + + # Get latest block + bn = await _rpc_call("eth_blockNumber", [], ttl=5) + latest = 0 + try: + latest = int(bn.get("result", "0x0"), 16) + except Exception: + pass + if latest == 0: + return {"error": "Cannot fetch block number"} + + # Fetch last 10 blocks with full transactions + n_scan = 10 + tasks = [] + for i in range(n_scan): + tasks.append(_rpc_call("eth_getBlockByNumber", [hex(latest - i), True], ttl=10)) + + # Get BNB price + tasks.append(fetch_json( + f"{DEXSCREENER}/latest/dex/tokens/0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + ttl=60, + )) + + results = await asyncio.gather(*tasks, return_exceptions=True) + + bnb_price = 0 + try: + px = results[n_scan] + for p in px.get("pairs", []): + if p.get("chainId") == "bsc" and p.get("priceUsd"): + bnb_price = float(p["priceUsd"]) + break + except Exception: + pass + + whale_txs = [] + total_volume = 0 + total_txs = 0 + inflow = 0 # to known wallets + outflow = 0 # from known wallets + + for i in range(n_scan): + r = results[i] + if not isinstance(r, dict) or not r.get("result"): + continue + block = r["result"] + block_num = int(block.get("number", "0x0"), 16) + block_time = int(block.get("timestamp", "0x0"), 16) + txs = block.get("transactions", []) + total_txs += len(txs) + + for tx in txs: + val = int(tx.get("value", "0x0"), 16) / 1e18 + if val < 0.0001: + continue + total_volume += val + from_addr = (tx.get("from") or "").lower() + to_addr = (tx.get("to") or "").lower() + + from_label = _KNOWN_WALLETS.get(from_addr, "") + to_label = _KNOWN_WALLETS.get(to_addr, "") + + if from_label: + outflow += val + if to_label: + inflow += val + + if val >= 0.0001: # Track any meaningful BNB transfer + whale_txs.append({ + "hash": tx.get("hash", ""), + "from": from_addr, + "fromLabel": from_label, + "to": to_addr, + "toLabel": to_label, + "value": round(val, 4), + "valueUsd": round(val * bnb_price, 2), + "block": block_num, + "time": block_time, + "gasPrice": round(int(tx.get("gasPrice", "0x0"), 16) / 1e9, 2), + }) + + # Sort by value desc + whale_txs.sort(key=lambda x: x["value"], reverse=True) + whale_txs = whale_txs[:25] + + net_flow = inflow - outflow + flow_direction = "accumulation" if net_flow > 0 else "distribution" if net_flow < 0 else "neutral" + + return { + "bnbPrice": bnb_price, + "blocksScanned": n_scan, + "latestBlock": latest, + "totalTxs": total_txs, + "totalVolume": round(total_volume, 2), + "totalVolumeUsd": round(total_volume * bnb_price, 2), + "whaleCount": len(whale_txs), + "inflow": round(inflow, 2), + "outflow": round(outflow, 2), + "netFlow": round(net_flow, 2), + "netFlowUsd": round(net_flow * bnb_price, 2), + "flowDirection": flow_direction, + "whales": whale_txs, + } + + +# ── Mefai Skills Batch 3: Deep Chain Analytics ────────────────────── + +_FUNC_SIGS = { + "0xa9059cbb": "transfer(address,uint256)", + "0x095ea7b3": "approve(address,uint256)", + "0x23b872dd": "transferFrom(address,address,uint256)", + "0x38ed1739": "swapExactTokensForTokens", + "0x7ff36ab5": "swapExactETHForTokens", + "0x18cbafe5": "swapExactTokensForETH", + "0x5c11d795": "swapExactTokensForTokensSupportingFeeOnTransferTokens", + "0xb6f9de95": "swapExactETHForTokensSupportingFeeOnTransferTokens", + "0x791ac947": "swapExactTokensForETHSupportingFeeOnTransferTokens", + "0xe8e33700": "addLiquidity", + "0xf305d719": "addLiquidityETH", + "0xbaa2abde": "removeLiquidity", + "0x02751cec": "removeLiquidityETH", + "0x40c10f19": "mint(address,uint256)", + "0x42966c68": "burn(uint256)", + "0x70a08231": "balanceOf(address)", + "0xdd62ed3e": "allowance(address,address)", +} + +_EVENT_TOPICS = { + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef": "Transfer", + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925": "Approval", + "0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822": "Swap", + "0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1": "Sync", +} + +_PANCAKE_ROUTER = "0x10ED43C718714eb63d5aA57B78B54917c3e6fE49" +_1INCH_ROUTER = "0x1111111254EEB25477B68fb85Ed929f73A960582" + + +@router.get("/mefai/tx-decoder") +async def mefai_tx_decoder( + hash: str = Query(..., min_length=66, max_length=66, description="Transaction hash"), +): + """Decode any BSC transaction: function, events, token transfers, gas.""" + import asyncio + + tx_task = _rpc_call("eth_getTransactionByHash", [hash], ttl=120) + receipt_task = _rpc_call("eth_getTransactionReceipt", [hash], ttl=120) + tx_raw, receipt_raw = await asyncio.gather(tx_task, receipt_task, return_exceptions=True) + + tx = tx_raw.get("result") if isinstance(tx_raw, dict) else None + receipt = receipt_raw.get("result") if isinstance(receipt_raw, dict) else None + + if not tx: + return {"error": "Transaction not found"} + + # Decode function selector + input_data = tx.get("input", "0x") + func_selector = input_data[:10] if len(input_data) >= 10 else "" + func_name = _FUNC_SIGS.get(func_selector, "unknown") + + # Parse value + value_wei = int(tx.get("value", "0x0"), 16) + value_bnb = value_wei / 1e18 + + # Gas info from receipt + gas_used = int(receipt.get("gasUsed", "0x0"), 16) if receipt else 0 + gas_price = int(tx.get("gasPrice", "0x0"), 16) + gas_cost_bnb = gas_used * gas_price / 1e18 + status = int(receipt.get("status", "0x1"), 16) if receipt else None + + # Parse event logs + events = [] + token_transfers = [] + logs = receipt.get("logs", []) if receipt else [] + for log in logs: + topics = log.get("topics", []) + if not topics: + continue + event_name = _EVENT_TOPICS.get(topics[0], "Unknown") + event = { + "event": event_name, + "address": log.get("address", ""), + "topic0": topics[0] if len(topics) > 0 else "", + "data": log.get("data", "0x"), + } + events.append(event) + + # Extract Transfer events for token transfers + if event_name == "Transfer" and len(topics) >= 3: + try: + from_addr = "0x" + topics[1][-40:] + to_addr = "0x" + topics[2][-40:] + raw_amount = int(log.get("data", "0x0"), 16) if log.get("data") and log["data"] != "0x" else 0 + token_transfers.append({ + "token": log.get("address", ""), + "from": from_addr, + "fromLabel": _KNOWN_WALLETS.get(from_addr.lower(), ""), + "to": to_addr, + "toLabel": _KNOWN_WALLETS.get(to_addr.lower(), ""), + "rawAmount": str(raw_amount), + }) + except Exception: + pass + + return { + "hash": hash, + "from": tx.get("from", ""), + "fromLabel": _KNOWN_WALLETS.get((tx.get("from") or "").lower(), ""), + "to": tx.get("to", ""), + "toLabel": _KNOWN_WALLETS.get((tx.get("to") or "").lower(), ""), + "value": value_bnb, + "function": func_name, + "functionSelector": func_selector, + "status": "success" if status == 1 else "failed" if status == 0 else "unknown", + "gasUsed": gas_used, + "gasPrice": round(gas_price / 1e9, 2), + "gasCostBnb": round(gas_cost_bnb, 6), + "blockNumber": int(tx.get("blockNumber", "0x0"), 16) if tx.get("blockNumber") else None, + "nonce": int(tx.get("nonce", "0x0"), 16), + "events": events, + "tokenTransfers": token_transfers, + } + + +@router.get("/mefai/contract-xray") +async def mefai_contract_xray( + address: str = Query(..., min_length=42, max_length=42, description="Contract address"), +): + """Deep contract analysis: bytecode selectors, proxy check, ownership, patterns.""" + import asyncio + + addr = address.lower() + + # Parallel: code, owner, name, symbol, decimals, proxy impl slot + code_task = _rpc_call("eth_getCode", [addr, "latest"], ttl=300) + owner_task = _rpc_call("eth_call", [{"to": addr, "data": "0x8da5cb5b"}, "latest"], ttl=300) + name_task = _rpc_call("eth_call", [{"to": addr, "data": _ERC20_NAME}, "latest"], ttl=300) + sym_task = _rpc_call("eth_call", [{"to": addr, "data": _ERC20_SYMBOL}, "latest"], ttl=300) + dec_task = _rpc_call("eth_call", [{"to": addr, "data": _ERC20_DECIMALS}, "latest"], ttl=300) + proxy_task = _rpc_call( + "eth_getStorageAt", + [addr, "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", "latest"], + ttl=300, + ) + + code_res, owner_res, name_res, sym_res, dec_res, proxy_res = await asyncio.gather( + code_task, owner_task, name_task, sym_task, dec_task, proxy_task, + return_exceptions=True, + ) + + # Parse code + bytecode = "" + if isinstance(code_res, dict): + bytecode = code_res.get("result", "0x") + code_size = (len(bytecode) - 2) // 2 if bytecode and bytecode != "0x" else 0 + + if code_size == 0: + return {"address": addr, "isContract": False, "codeSize": 0, "error": "Not a contract (EOA)"} + + # Scan for PUSH4 opcodes (0x63) to extract function selectors + detected_funcs = [] + all_sigs = {**_FUNC_SIGS, _ERC20_NAME: "name()", _ERC20_SYMBOL: "symbol()", + _ERC20_DECIMALS: "decimals()", _ERC20_TOTAL_SUPPLY: "totalSupply()", + _ERC20_BALANCE_OF: "balanceOf(address)"} + try: + code_hex = bytecode[2:] # strip 0x + i = 0 + found_selectors = set() + while i < len(code_hex) - 10: + # PUSH4 = 0x63 + if code_hex[i:i + 2] == "63": + selector = "0x" + code_hex[i + 2:i + 10] + found_selectors.add(selector) + i += 10 + else: + i += 2 + for sel in found_selectors: + name = all_sigs.get(sel, "") + detected_funcs.append({"selector": sel, "name": name}) + except Exception: + pass + + # Proxy check + is_proxy = False + impl_addr = "" + if isinstance(proxy_res, dict): + slot_val = proxy_res.get("result", "0x" + "0" * 64) + if slot_val and slot_val != "0x" + "0" * 64 and int(slot_val, 16) != 0: + is_proxy = True + impl_addr = "0x" + slot_val[-40:] + + # Owner + owner = "" + if isinstance(owner_res, dict) and owner_res.get("result") and len(owner_res["result"]) >= 42: + raw = owner_res["result"] + if int(raw, 16) != 0: + owner = "0x" + raw[-40:] + + # Patterns + has_mint = any(f["selector"] == "0x40c10f19" for f in detected_funcs) + has_pause = any(f["selector"] == "0x8456cb59" for f in detected_funcs) + has_burn = any(f["selector"] == "0x42966c68" for f in detected_funcs) + has_blacklist = any(f["selector"] in ("0x44337ea1", "0xe47d6060") for f in detected_funcs) + + # ERC20 info + def _dec_str(res): + if not isinstance(res, dict): + return "" + hex_val = res.get("result", "0x") + if not hex_val or hex_val == "0x" or len(hex_val) < 66: + return "" + try: + raw = hex_val.replace("0x", "") + offset = int(raw[:64], 16) * 2 + length = int(raw[offset:offset + 64], 16) + data = raw[offset + 64:offset + 64 + length * 2] + return bytes.fromhex(data).decode("utf-8", errors="ignore").strip("\x00") + except Exception: + return "" + + name = _dec_str(name_res) + symbol = _dec_str(sym_res) + decimals = None + if isinstance(dec_res, dict) and dec_res.get("result"): + try: + decimals = int(dec_res["result"], 16) + except Exception: + pass + + erc20_info = None + if name or symbol or decimals is not None: + erc20_info = {"name": name, "symbol": symbol, "decimals": decimals} + + return { + "address": addr, + "isContract": True, + "codeSize": code_size, + "isProxy": is_proxy, + "implementationAddress": impl_addr if is_proxy else None, + "owner": owner or None, + "ownerLabel": _KNOWN_WALLETS.get(owner.lower(), "") if owner else None, + "detectedFunctions": sorted(detected_funcs, key=lambda x: x["name"], reverse=True)[:50], + "hasPatterns": { + "mintable": has_mint, + "pausable": has_pause, + "burnable": has_burn, + "blacklistable": has_blacklist, + }, + "erc20Info": erc20_info, + } + + +@router.get("/mefai/approval-scanner") +async def mefai_approval_scanner( + address: str = Query(..., min_length=42, max_length=42, description="Wallet address"), +): + """Check token approvals for a wallet against major BSC DEX routers.""" + import asyncio + + addr = address.lower() + addr_padded = addr.replace("0x", "").zfill(64) + + # Major BSC DEX routers and protocols + spenders = { + "0x10ed43c718714eb63d5aa57b78b54704e256024e": "PancakeSwap V2", + "0x13f4ea83d0bd40e75c8222255bc855a974568dd4": "PancakeSwap V3", + "0x1b81d678ffb9c0263b24a97847620c99d213eb14": "PancakeSwap Smart", + "0x05ff2b0db69458a0750badebc4f9e13add608c7f": "PancakeSwap V1", + _1INCH_ROUTER.lower(): "1inch Router", + "0xeaf1ac8e89ea0ae13e0cee2bda2c84ab5b1d8e09": "Biswap", + "0xcf0febd3f17cef5b47b0cd257acf6025c5bff3b7": "ApeSwap", + "0x1111111254eeb25477b68fb85ed929f73a960582": "1inch v5", + "0xdef171fe48cf0115b1d80b88dc8eab59176fee57": "ParaSwap", + } + + tasks = [] + task_keys = [] + token_list = list(_TOP_BSC_PORTFOLIO.items()) + + for contract, symbol in token_list: + for spender, label in spenders.items(): + spender_padded = spender.replace("0x", "").zfill(64) + data = f"0xdd62ed3e{addr_padded}{spender_padded}" + tasks.append( + _rpc_call("eth_call", [{"to": contract.lower(), "data": data}, "latest"], ttl=30) + ) + task_keys.append((contract, symbol, spender, label)) + + results = await asyncio.gather(*tasks, return_exceptions=True) + + approvals = [] + for i, (contract, symbol, spender, label) in enumerate(task_keys): + r = results[i] + if isinstance(r, Exception) or not isinstance(r, dict): + continue + raw = r.get("result", "0x0") + if raw and raw != "0x": + try: + allowance = int(raw, 16) + except Exception: + allowance = 0 + if allowance > 0: + is_unlimited = allowance > 10 ** 50 + approvals.append({ + "token": contract, + "symbol": symbol, + "spender": spender, + "spenderLabel": label, + "allowance": str(allowance), + "isUnlimited": is_unlimited, + }) + + # Sort: unlimited first, then by symbol + approvals.sort(key=lambda a: (0 if a["isUnlimited"] else 1, a["symbol"])) + + return { + "address": addr, + "tokensChecked": len(token_list), + "spendersChecked": len(spenders), + "totalApprovals": len(approvals), + "approvals": approvals, + } + + +@router.get("/mefai/block-autopsy") +async def mefai_block_autopsy( + number: str = Query("latest", description="Block number in hex or 'latest'"), +): + """Drill into a block: TX types, gas consumers, value transferred.""" + import asyncio + + # If 'latest', resolve to actual number + if number == "latest": + bn = await _rpc_call("eth_blockNumber", [], ttl=5) + try: + number = bn.get("result", "0x0") + except Exception: + return {"error": "Cannot fetch block number"} + + block_res = await _rpc_call("eth_getBlockByNumber", [number, True], ttl=15) + block = block_res.get("result") if isinstance(block_res, dict) else None + if not block: + return {"error": "Block not found"} + + block_num = int(block.get("number", "0x0"), 16) + timestamp = int(block.get("timestamp", "0x0"), 16) + gas_used = int(block.get("gasUsed", "0x0"), 16) + gas_limit = int(block.get("gasLimit", "0x0"), 16) + miner = block.get("miner", "") + txs = block.get("transactions", []) + + tx_types = {"transfer": 0, "swap": 0, "approve": 0, "other": 0} + total_value = 0 + gas_by_contract = {} + + swap_selectors = {"0x38ed1739", "0x7ff36ab5", "0x18cbafe5", "0x5c11d795", "0xb6f9de95", "0x791ac947"} + approve_selectors = {"0x095ea7b3"} + transfer_selectors = {"0xa9059cbb", "0x23b872dd"} + + for tx in txs: + val = int(tx.get("value", "0x0"), 16) / 1e18 + total_value += val + to_addr = (tx.get("to") or "").lower() + input_data = tx.get("input", "0x") + selector = input_data[:10] if len(input_data) >= 10 else "" + tx_gas = int(tx.get("gas", "0x0"), 16) + + if selector in swap_selectors: + tx_types["swap"] += 1 + elif selector in approve_selectors: + tx_types["approve"] += 1 + elif selector in transfer_selectors or (input_data == "0x" and val > 0): + tx_types["transfer"] += 1 + else: + tx_types["other"] += 1 + + if to_addr: + if to_addr not in gas_by_contract: + gas_by_contract[to_addr] = {"gasUsed": 0, "txCount": 0} + gas_by_contract[to_addr]["gasUsed"] += tx_gas + gas_by_contract[to_addr]["txCount"] += 1 + + # Top 5 gas consumers + top_gas = sorted(gas_by_contract.items(), key=lambda x: x[1]["gasUsed"], reverse=True)[:5] + top_consumers = [ + { + "address": addr, + "label": _KNOWN_WALLETS.get(addr, ""), + "gasUsed": info["gasUsed"], + "txCount": info["txCount"], + } + for addr, info in top_gas + ] + + return { + "blockNumber": block_num, + "timestamp": timestamp, + "txCount": len(txs), + "gasUsed": gas_used, + "gasLimit": gas_limit, + "gasPct": round(gas_used / gas_limit * 100, 1) if gas_limit > 0 else 0, + "totalValue": round(total_value, 4), + "txTypes": tx_types, + "topGasConsumers": top_consumers, + "miner": miner, + "minerLabel": _KNOWN_WALLETS.get(miner.lower(), ""), + } + + +@router.get("/mefai/gas-calculator") +async def mefai_gas_calculator(): + """Operation cost estimator: gas costs for common BSC operations vs ETH.""" + import asyncio + + gas_task = _rpc_call("eth_gasPrice", [], ttl=15) + bnb_task = fetch_json( + f"{DEXSCREENER}/latest/dex/tokens/0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + ttl=60, + ) + gas_res, bnb_market = await asyncio.gather(gas_task, bnb_task, return_exceptions=True) + + bsc_gas_gwei = 0 + try: + bsc_gas_gwei = int(gas_res.get("result", "0x0"), 16) / 1e9 + except Exception: + pass + + bnb_price = 0 + try: + for p in bnb_market.get("pairs", []): + if p.get("chainId") == "bsc" and p.get("priceUsd"): + bnb_price = float(p["priceUsd"]) + break + except Exception: + pass + + operations = [ + ("BNB Transfer", 21000), + ("ERC20 Transfer", 65000), + ("ERC20 Approve", 46000), + ("PancakeSwap Swap", 200000), + ("Add Liquidity", 250000), + ("Remove Liquidity", 220000), + ("Contract Deploy (small)", 500000), + ("Contract Deploy (large)", 3000000), + ("NFT Mint", 150000), + ("NFT Transfer", 85000), + ] + + eth_gas_gwei = 8.0 + eth_price = 1950.0 + + costs = [] + for name, gas_units in operations: + bsc_cost_bnb = bsc_gas_gwei * gas_units / 1e9 + bsc_cost_usd = bsc_cost_bnb * bnb_price + eth_cost_eth = eth_gas_gwei * gas_units / 1e9 + eth_cost_usd = eth_cost_eth * eth_price + savings = eth_cost_usd - bsc_cost_usd + + costs.append({ + "operation": name, + "gasUnits": gas_units, + "bsc": { + "costBnb": round(bsc_cost_bnb, 8), + "costUsd": round(bsc_cost_usd, 6), + }, + "eth": { + "costEth": round(eth_cost_eth, 8), + "costUsd": round(eth_cost_usd, 4), + }, + "savingsUsd": round(savings, 4), + }) + + return { + "bscGasGwei": round(bsc_gas_gwei, 2), + "bnbPrice": bnb_price, + "ethGasGwei": eth_gas_gwei, + "ethPrice": eth_price, + "operations": costs, + } + + +@router.get("/mefai/token-battle") +async def mefai_token_battle( + tokens: str = Query(..., description="Comma separated token addresses, max 4"), +): + """Compare tokens side-by-side: on-chain + market data.""" + import asyncio + + addresses = [a.strip().lower() for a in tokens.split(",") if a.strip()][:4] + if not addresses: + return {"error": "Provide at least one token address"} + + dead_padded = _DEAD_ADDR.replace("0x", "").zfill(64) + + # Build all tasks + tasks = [] + task_map = [] # (token_idx, field) + for idx, addr in enumerate(addresses): + tasks.append(_rpc_call("eth_call", [{"to": addr, "data": _ERC20_NAME}, "latest"], ttl=300)) + task_map.append((idx, "name")) + tasks.append(_rpc_call("eth_call", [{"to": addr, "data": _ERC20_SYMBOL}, "latest"], ttl=300)) + task_map.append((idx, "symbol")) + tasks.append(_rpc_call("eth_call", [{"to": addr, "data": _ERC20_DECIMALS}, "latest"], ttl=300)) + task_map.append((idx, "decimals")) + tasks.append(_rpc_call("eth_call", [{"to": addr, "data": _ERC20_TOTAL_SUPPLY}, "latest"], ttl=60)) + task_map.append((idx, "totalSupply")) + tasks.append(_rpc_call("eth_getCode", [addr, "latest"], ttl=300)) + task_map.append((idx, "code")) + tasks.append(_rpc_call("eth_call", [{"to": addr, "data": f"{_ERC20_BALANCE_OF}{dead_padded}"}, "latest"], ttl=60)) + task_map.append((idx, "burned")) + + # DexScreener batch + dex_addr_str = ",".join(addresses) + tasks.append(fetch_json(f"{DEXSCREENER}/latest/dex/tokens/{dex_addr_str}", ttl=60)) + task_map.append((-1, "dex")) + + results = await asyncio.gather(*tasks, return_exceptions=True) + + def _dec_str(res): + if not isinstance(res, dict): + return "" + hex_val = res.get("result", "0x") + if not hex_val or hex_val == "0x" or len(hex_val) < 66: + return "" + try: + raw = hex_val.replace("0x", "") + offset = int(raw[:64], 16) * 2 + length = int(raw[offset:offset + 64], 16) + data = raw[offset + 64:offset + 64 + length * 2] + return bytes.fromhex(data).decode("utf-8", errors="ignore").strip("\x00") + except Exception: + return "" + + # Parse on-chain results + token_data = [{} for _ in addresses] + for i, (tidx, field) in enumerate(task_map): + if tidx < 0: + continue + r = results[i] + if isinstance(r, Exception): + continue + if field == "name": + token_data[tidx]["name"] = _dec_str(r) + elif field == "symbol": + token_data[tidx]["symbol"] = _dec_str(r) + elif field == "decimals": + try: + token_data[tidx]["decimals"] = int(r.get("result", "0x0"), 16) if isinstance(r, dict) and r.get("result") else None + except Exception: + token_data[tidx]["decimals"] = None + elif field == "totalSupply": + try: + token_data[tidx]["totalSupplyRaw"] = int(r.get("result", "0x0"), 16) if isinstance(r, dict) and r.get("result") else 0 + except Exception: + token_data[tidx]["totalSupplyRaw"] = 0 + elif field == "code": + code = r.get("result", "0x") if isinstance(r, dict) else "0x" + token_data[tidx]["codeSize"] = (len(code) - 2) // 2 if code and code != "0x" else 0 + elif field == "burned": + try: + token_data[tidx]["burnedRaw"] = int(r.get("result", "0x0"), 16) if isinstance(r, dict) and r.get("result") else 0 + except Exception: + token_data[tidx]["burnedRaw"] = 0 + + # Parse DexScreener + dex_res = results[-1] + dex_pairs = (dex_res.get("pairs", []) if isinstance(dex_res, dict) else []) + price_map = {} + for p in dex_pairs: + if p.get("chainId") != "bsc": + continue + base_addr = (p.get("baseToken", {}).get("address") or "").lower() + vol = p.get("volume", {}).get("h24") or 0 + if base_addr and (base_addr not in price_map or vol > (price_map[base_addr].get("volume24h") or 0)): + price_map[base_addr] = { + "price": p.get("priceUsd"), + "volume24h": p.get("volume", {}).get("h24"), + "liquidity": p.get("liquidity", {}).get("usd"), + "change24h": p.get("priceChange", {}).get("h24"), + "change1h": p.get("priceChange", {}).get("h1"), + "pairs": sum(1 for pp in dex_pairs if (pp.get("baseToken", {}).get("address") or "").lower() == base_addr), + "dex": p.get("dexId", ""), + "buys24h": p.get("txns", {}).get("h24", {}).get("buys") or 0, + "sells24h": p.get("txns", {}).get("h24", {}).get("sells") or 0, + } + + # Combine + output = [] + for idx, addr in enumerate(addresses): + td = token_data[idx] + dec = td.get("decimals") or 18 + total_supply = td.get("totalSupplyRaw", 0) / (10 ** dec) if td.get("totalSupplyRaw") else 0 + burned = td.get("burnedRaw", 0) / (10 ** dec) if td.get("burnedRaw") else 0 + market = price_map.get(addr, {}) + + output.append({ + "address": addr, + "name": td.get("name", ""), + "symbol": td.get("symbol", ""), + "decimals": td.get("decimals"), + "totalSupply": total_supply, + "codeSize": td.get("codeSize", 0), + "burned": burned, + "burnPct": round(burned / total_supply * 100, 2) if total_supply > 0 else 0, + "price": market.get("price"), + "volume24h": market.get("volume24h"), + "liquidity": market.get("liquidity"), + "change24h": market.get("change24h"), + "change1h": market.get("change1h"), + "pairs": market.get("pairs", 0), + "dex": market.get("dex", ""), + "buys24h": market.get("buys24h", 0), + "sells24h": market.get("sells24h", 0), + }) + + return {"tokens": output} + + +@router.get("/mefai/pair-analytics") +async def mefai_pair_analytics( + address: str = Query(..., min_length=42, max_length=42, description="Token address"), +): + """Deep pair analysis from DexScreener for a token.""" + data = await fetch_json(f"{DEXSCREENER}/latest/dex/tokens/{address.lower()}", ttl=30) + all_pairs = data.get("pairs", []) if isinstance(data, dict) else [] + bsc_pairs = [p for p in all_pairs if p.get("chainId") == "bsc"] + + if not bsc_pairs: + return {"address": address.lower(), "pairCount": 0, "pairs": [], "aggregate": {}} + + pairs_out = [] + total_volume = 0 + total_liquidity = 0 + total_txns = 0 + + for p in bsc_pairs: + base = p.get("baseToken", {}) + quote = p.get("quoteToken", {}) + vol = p.get("volume", {}) + chg = p.get("priceChange", {}) + txns_h24 = p.get("txns", {}).get("h24", {}) + liq = p.get("liquidity", {}).get("usd") or 0 + v24 = vol.get("h24") or 0 + buys = txns_h24.get("buys") or 0 + sells = txns_h24.get("sells") or 0 + + total_volume += v24 + total_liquidity += liq + total_txns += buys + sells + + pairs_out.append({ + "pairAddress": p.get("pairAddress", ""), + "dex": p.get("dexId", ""), + "baseSymbol": base.get("symbol", ""), + "quoteSymbol": quote.get("symbol", ""), + "price": p.get("priceUsd"), + "priceNative": p.get("priceNative"), + "volume5m": vol.get("m5"), + "volume1h": vol.get("h1"), + "volume6h": vol.get("h6"), + "volume24h": v24, + "liquidity": liq, + "buys24h": buys, + "sells24h": sells, + "change5m": chg.get("m5"), + "change1h": chg.get("h1"), + "change6h": chg.get("h6"), + "change24h": chg.get("h24"), + "pairCreated": p.get("pairCreatedAt"), + }) + + pairs_out.sort(key=lambda x: x["volume24h"] or 0, reverse=True) + avg_trade = total_volume / total_txns if total_txns > 0 else 0 + + return { + "address": address.lower(), + "pairCount": len(pairs_out), + "aggregate": { + "totalVolume24h": round(total_volume, 2), + "totalLiquidity": round(total_liquidity, 2), + "totalTxns24h": total_txns, + "avgTradeSize": round(avg_trade, 2), + }, + "pairs": pairs_out, + } + + +@router.get("/mefai/token-flow") +async def mefai_token_flow( + contract: str = Query(..., min_length=42, max_length=42, description="Token contract address"), +): + """Track token movements in recent blocks by scanning block receipts.""" + import asyncio + + token_addr = contract.lower() + + # Get latest block number + bn = await _rpc_call("eth_blockNumber", [], ttl=5) + latest = 0 + try: + latest = int(bn.get("result", "0x0"), 16) + except Exception: + pass + if latest == 0: + return {"error": "Cannot fetch block number"} + + # Fetch last 10 blocks with full transactions + n_blocks = 10 + block_tasks = [ + _rpc_call("eth_getBlockByNumber", [hex(latest - i), True], ttl=15) + for i in range(n_blocks) + ] + block_results = await asyncio.gather(*block_tasks, return_exceptions=True) + + # Find TXs that interact with the token contract OR have input data calling it + matching_hashes = [] + for r in block_results: + if not isinstance(r, dict) or not r.get("result"): + continue + txs = r["result"].get("transactions", []) + for tx in txs: + to_addr = (tx.get("to") or "").lower() + input_data = tx.get("input", "0x") or "0x" + # Match direct calls to token contract OR any contract call that might transfer this token + if to_addr == token_addr: + matching_hashes.append(tx.get("hash", "")) + elif len(input_data) >= 10 and token_addr[2:] in input_data.lower(): + matching_hashes.append(tx.get("hash", "")) + + # If no direct matches, grab all contract interactions and check receipts + if len(matching_hashes) < 5: + for r in block_results: + if not isinstance(r, dict) or not r.get("result"): + continue + txs = r["result"].get("transactions", []) + for tx in txs: + h = tx.get("hash", "") + if h not in matching_hashes and len(tx.get("input", "0x")) > 10: + matching_hashes.append(h) + if len(matching_hashes) >= 50: + break + if len(matching_hashes) >= 50: + break + + if not matching_hashes: + return { + "contract": token_addr, + "blocksScanned": n_blocks, + "latestBlock": latest, + "totalTransfers": 0, + "uniqueAddresses": 0, + "transfers": [], + } + + # Fetch receipts in parallel + receipt_tasks = [ + _rpc_call("eth_getTransactionReceipt", [h], ttl=15) + for h in matching_hashes[:50] + ] + receipt_results = await asyncio.gather(*receipt_tasks, return_exceptions=True) + + transfer_topic = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + transfers = [] + unique_addrs = set() + + for r in receipt_results: + if not isinstance(r, dict) or not r.get("result"): + continue + receipt = r["result"] + tx_hash = receipt.get("transactionHash", "") + block_num = int(receipt.get("blockNumber", "0x0"), 16) + for log in receipt.get("logs", []): + if (log.get("address") or "").lower() != token_addr: + continue + topics = log.get("topics", []) + if len(topics) >= 3 and topics[0] == transfer_topic: + try: + from_addr = "0x" + topics[1][-40:] + to_addr = "0x" + topics[2][-40:] + raw_amount = int(log.get("data", "0x0"), 16) if log.get("data") and log["data"] != "0x" else 0 + unique_addrs.add(from_addr) + unique_addrs.add(to_addr) + transfers.append({ + "txHash": tx_hash, + "block": block_num, + "from": from_addr, + "fromLabel": _KNOWN_WALLETS.get(from_addr.lower(), ""), + "to": to_addr, + "toLabel": _KNOWN_WALLETS.get(to_addr.lower(), ""), + "rawAmount": str(raw_amount), + }) + except Exception: + pass + + # Sort by amount desc + transfers.sort(key=lambda x: int(x["rawAmount"]), reverse=True) + largest = transfers[0]["rawAmount"] if transfers else "0" + + return { + "contract": token_addr, + "blocksScanned": n_blocks, + "latestBlock": latest, + "totalTransfers": len(transfers), + "uniqueAddresses": len(unique_addrs), + "largestTransfer": largest, + "transfers": transfers[:20], + } + + +@router.get("/mefai/yield-finder") +async def mefai_yield_finder(): + """Find best yield opportunities on BSC by trading fee proxy.""" + all_addrs = list(_TOP_BSC_PORTFOLIO.keys()) + _DEFI_TOKENS[:6] + data = await fetch_json( + f"{DEXSCREENER}/latest/dex/tokens/" + ",".join(all_addrs), + ttl=60, + ) + pairs = data.get("pairs", []) if isinstance(data, dict) else [] + bsc_pairs = [p for p in pairs if p.get("chainId") == "bsc"] + + # Deduplicate by pair address + seen = {} + for p in bsc_pairs: + pa = p.get("pairAddress", "") + if pa not in seen: + seen[pa] = p + + results = [] + for p in seen.values(): + vol = p.get("volume", {}).get("h24") or 0 + liq = p.get("liquidity", {}).get("usd") or 0 + if liq < 10000: + continue + + # Annualized fee yield estimate: (volume * 0.3% fee * 365) / liquidity + fee_yield = (vol * 0.003 * 365) / liq * 100 + + txns = p.get("txns", {}).get("h24", {}) + buys = txns.get("buys") or 0 + sells = txns.get("sells") or 0 + total = buys + sells + buy_pressure = (buys / total * 100) if total > 0 else 50 + + base = p.get("baseToken", {}) + quote = p.get("quoteToken", {}) + + results.append({ + "pair": f"{base.get('symbol', '?')}/{quote.get('symbol', '?')}", + "pairAddress": p.get("pairAddress", ""), + "dex": p.get("dexId", ""), + "baseAddress": base.get("address", ""), + "price": p.get("priceUsd"), + "volume24h": vol, + "liquidity": liq, + "vlRatio": round(vol / liq, 2) if liq > 0 else 0, + "estimatedApy": round(fee_yield, 2), + "buyPressure": round(buy_pressure, 1), + "buys24h": buys, + "sells24h": sells, + "change24h": p.get("priceChange", {}).get("h24"), + }) + + results.sort(key=lambda x: x["estimatedApy"], reverse=True) + return {"opportunities": results[:15]} + + +@router.get("/mefai/risk-radar") +async def mefai_risk_radar( + address: str = Query(..., min_length=42, max_length=42, description="Address or contract"), +): + """Comprehensive risk assessment for any BSC address/contract.""" + import asyncio + + addr = address.lower() + + # Parallel checks + code_task = _rpc_call("eth_getCode", [addr, "latest"], ttl=60) + bal_task = _rpc_call("eth_getBalance", [addr, "latest"], ttl=15) + nonce_task = _rpc_call("eth_getTransactionCount", [addr, "latest"], ttl=15) + owner_task = _rpc_call("eth_call", [{"to": addr, "data": "0x8da5cb5b"}, "latest"], ttl=60) + name_task = _rpc_call("eth_call", [{"to": addr, "data": _ERC20_NAME}, "latest"], ttl=60) + sym_task = _rpc_call("eth_call", [{"to": addr, "data": _ERC20_SYMBOL}, "latest"], ttl=60) + dead_padded = _DEAD_ADDR.replace("0x", "").zfill(64) + burn_task = _rpc_call("eth_call", [{"to": addr, "data": f"{_ERC20_BALANCE_OF}{dead_padded}"}, "latest"], ttl=60) + dex_task = fetch_json(f"{DEXSCREENER}/latest/dex/tokens/{addr}", ttl=60) + + code_res, bal_res, nonce_res, owner_res, name_res, sym_res, burn_res, dex_res = await asyncio.gather( + code_task, bal_task, nonce_task, owner_task, name_task, sym_task, burn_task, dex_task, + return_exceptions=True, + ) + + # Parse code + bytecode = code_res.get("result", "0x") if isinstance(code_res, dict) else "0x" + is_contract = bytecode not in ("0x", "0x0", None, "") + code_size = (len(bytecode) - 2) // 2 if is_contract else 0 + + # Balance + balance = 0 + try: + balance = int(bal_res.get("result", "0x0"), 16) / 1e18 + except Exception: + pass + + # TX count + tx_count = 0 + try: + tx_count = int(nonce_res.get("result", "0x0"), 16) + except Exception: + pass + + # Owner + owner = "" + if isinstance(owner_res, dict) and owner_res.get("result") and len(owner_res["result"]) >= 42: + raw = owner_res["result"] + try: + if int(raw, 16) != 0: + owner = "0x" + raw[-40:] + except Exception: + pass + + # Name/Symbol + def _dec_str(res): + if not isinstance(res, dict): + return "" + hex_val = res.get("result", "0x") + if not hex_val or hex_val == "0x" or len(hex_val) < 66: + return "" + try: + raw = hex_val.replace("0x", "") + offset = int(raw[:64], 16) * 2 + length = int(raw[offset:offset + 64], 16) + data = raw[offset + 64:offset + 64 + length * 2] + return bytes.fromhex(data).decode("utf-8", errors="ignore").strip("\x00") + except Exception: + return "" + + name = _dec_str(name_res) + symbol = _dec_str(sym_res) + + # Burn balance + has_burn_tokens = False + try: + burn_val = int(burn_res.get("result", "0x0"), 16) if isinstance(burn_res, dict) and burn_res.get("result") else 0 + has_burn_tokens = burn_val > 0 + except Exception: + pass + + # Dangerous selectors in bytecode + has_mint = False + has_pause = False + has_blacklist = False + has_selfdestruct = False + if is_contract: + code_hex = bytecode[2:] if bytecode.startswith("0x") else bytecode + # Check for PUSH4 selectors + has_mint = "40c10f19" in code_hex + has_pause = "8456cb59" in code_hex + has_blacklist = "44337ea1" in code_hex or "e47d6060" in code_hex + has_selfdestruct = "ff" in code_hex # SELFDESTRUCT opcode + + # DexScreener market data + market_data = None + liquidity = 0 + volume = 0 + pair_count = 0 + try: + pairs = [p for p in (dex_res.get("pairs", []) if isinstance(dex_res, dict) else []) if p.get("chainId") == "bsc"] + pair_count = len(pairs) + if pairs: + top = pairs[0] + liquidity = top.get("liquidity", {}).get("usd") or 0 + volume = top.get("volume", {}).get("h24") or 0 + market_data = { + "price": top.get("priceUsd"), + "volume24h": volume, + "liquidity": liquidity, + "change24h": top.get("priceChange", {}).get("h24"), + "pairs": pair_count, + "dex": top.get("dexId", ""), + } + except Exception: + pass + + # Calculate risk score (0-100, higher=safer) + score = 0 + risks = [] + positives = [] + + if is_contract: + score += 20 + positives.append("Is a contract") + if name and symbol: + score += 15 + positives.append(f"Has name ({name}) and symbol ({symbol})") + if liquidity > 10000: + score += 15 + positives.append(f"Liquidity ${liquidity:,.0f}") + if volume > 1000: + score += 10 + positives.append(f"24h volume ${volume:,.0f}") + if pair_count > 1: + score += 10 + positives.append(f"{pair_count} trading pairs") + if not has_mint: + score += 10 + positives.append("No mint function detected") + else: + score -= 20 + risks.append("Owner can mint new tokens") + if not has_pause: + score += 10 + positives.append("No pause function detected") + else: + score -= 15 + risks.append("Contract is pausable") + if not has_blacklist: + score += 5 + positives.append("No blacklist function detected") + else: + score -= 10 + risks.append("Contract has blacklist capability") + if has_burn_tokens: + score += 5 + positives.append("Tokens burned at dead address") + if has_selfdestruct and is_contract: + risks.append("Contains SELFDESTRUCT opcode") + + # Clamp score + score = max(0, min(100, score)) + grade = "A" if score >= 80 else "B" if score >= 60 else "C" if score >= 40 else "D" if score >= 20 else "F" + + return { + "address": addr, + "isContract": is_contract, + "codeSize": code_size, + "balance": round(balance, 6), + "txCount": tx_count, + "owner": owner or None, + "ownerLabel": _KNOWN_WALLETS.get(owner.lower(), "") if owner else None, + "name": name, + "symbol": symbol, + "riskScore": score, + "grade": grade, + "risks": risks, + "positives": positives, + "marketData": market_data, + } + + +# ── Mefai Advanced Endpoints (10 new) ──────────────────────────────── + +_BSC_VALIDATORS = { + "0x72b61c6014342d914470ec7ac2975be345796c2b": "Ankr", + "0x2465176c461afb316ebc773c61faee85a6515daa": "BNB48 Club", + "0x295e26495cef6f69dfa69911d9d8e4f3bbadb89b": "Legend", + "0xe2d3a739effcd3a99387d015e260eefac72ebea1": "Fuji", + "0xb218c5d6af1f979ac42bc68d98a5a0d796c6ab01": "Namelix", + "0xa6f79b60359f141df90a0c745125b131caaffd12": "Certik", + "0xee226379db83cffc681495730c11fdde79ba4c0c": "Avengers", + "0x2d4c407bbe49438ed859fe965b140dcf1aab71a9": "TW Staking", + "0x685b1ded8013785d6623cc18d214320b6bb64759": "NodeReal", + "0xe9ae3261a475a27bb1028f140bc2a7c843318afd": "Defibit", + "0x70f657164e5b75689b64b7fd1fa275f334f28e18": "InfStones", + "0xbe807dddb074639cd9fa61b47676c064fc50d62c": "Tranchess", + "0x4396e28197653d0c244d95f8c1e57da902a72b4e": "MathWallet", + "0xef0274e31810c9df02f98fafde0f841f4e66a1cd": "Celer Network", + "0x9bb832254baf4e8b4cc26bd2b52b31389b56e98b": "Legend II", + "0x980a75ecd1309ea12fa2ed87a8744fbfc9b863d5": "HashQuark", + "0x3f349bbafec1551819b8be1efea2fc46ca749aa1": "Pexmons", + "0x35ead5bca32d21bead5c6b603e8de8ad4a35e24c": "Stakely", + "0x4430b3230294d12c6ab2aac5c2cd68e80b16b581": "BSCValidator", + "0xcc8e6d00c17eb431350c6c50d8b8f4d23c4c3d23": "Validator 20", + "0xbbb2d18aced2fa6a426448a4bea99d1e7b7ef2cc": "Validator 21", +} + +_EXTENDED_TOKENS = { + "0xbA2aE424d960c26247Dd6c32edC70B295c744C43": "DOGE", + "0x7083609fce4d1d8Dc0C979AAb8c869Ea2C873402": "DOT", + "0xF8A0BF9cF54Bb92F17374d9e9A321E6a111a51bD": "LINK", + "0x570A5D26f7765Ecb712C0924E4De545B89fD43dF": "SOL", + "0xCC42724C6683B7E57334c4E856f4c9965ED682bD": "MATIC", + "0xBf5140A22578168FD562DCcF235E5D43A02ce9B1": "UNI", + "0xfb6115445Bff7b52FeB98650C87f44907E58f802": "AAVE", +} + +_TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + + +def _dec_str_util(hex_val): + """Decode ABI-encoded string from hex result.""" + if not hex_val or hex_val == "0x" or len(hex_val) < 66: + return "" + try: + raw = hex_val.replace("0x", "") + offset = int(raw[:64], 16) * 2 + length = int(raw[offset:offset + 64], 16) + data = raw[offset + 64:offset + 64 + length * 2] + return bytes.fromhex(data).decode("utf-8", errors="ignore").strip("\x00") + except Exception: + return "" + + +def _safe_int(hex_val, default=0): + """Safely convert hex string to int.""" + try: + if hex_val and hex_val not in ("0x", "0x0", None, ""): + return int(hex_val, 16) + except Exception: + pass + return default + + +# ── 1. Sniper Detector ────────────────────────────────────────────── + +@router.get("/mefai/sniper-detector") +async def mefai_sniper_detector( + address: str = Query(..., min_length=42, max_length=42), +): + """Detect sniper bot activity on a token by analyzing early block buys.""" + import asyncio + + addr = address.lower() + try: + # Get token pairs from DexScreener + dex_data = await fetch_json(f"{DEXSCREENER}/latest/dex/tokens/{addr}", ttl=30) + pairs = [p for p in (dex_data.get("pairs") or []) if p.get("chainId") == "bsc"] + if not pairs: + return {"error": "No BSC pairs found for this token", "address": addr} + + pair_addr = pairs[0].get("pairAddress", "").lower() + token_symbol = pairs[0].get("baseToken", {}).get("symbol", "UNKNOWN") + + # Get latest block number + bn = await _rpc_call("eth_blockNumber", [], ttl=5) + latest = _safe_int(bn.get("result")) + if latest == 0: + return {"error": "Cannot fetch block number"} + + # Fetch last 20 blocks with full transactions + block_tasks = [ + _rpc_call("eth_getBlockByNumber", [hex(latest - i), True], ttl=10) + for i in range(20) + ] + block_results = await asyncio.gather(*block_tasks, return_exceptions=True) + + # Find transactions involving the token or pair contracts + candidate_txs = [] + for idx, br in enumerate(block_results): + if isinstance(br, Exception) or not br.get("result"): + continue + block = br["result"] + block_num = _safe_int(block.get("number")) + for tx in (block.get("transactions") or []): + to_addr = (tx.get("to") or "").lower() + if to_addr in (addr, pair_addr, _PANCAKE_ROUTER.lower()): + candidate_txs.append({ + "hash": tx["hash"], + "from": tx.get("from", "").lower(), + "to": to_addr, + "block": block_num, + "blockIndex": idx, + "value": _safe_int(tx.get("value")), + }) + + # Fetch receipts for candidate TXs to find Transfer events + receipt_tasks = [ + _rpc_call("eth_getTransactionReceipt", [tx["hash"]], ttl=30) + for tx in candidate_txs[:30] # limit to 30 receipts + ] + receipts = await asyncio.gather(*receipt_tasks, return_exceptions=True) + + early_buyers = {} + for i, rcpt in enumerate(receipts): + if isinstance(rcpt, Exception) or not rcpt.get("result"): + continue + logs = rcpt["result"].get("logs") or [] + for log in logs: + topics = log.get("topics") or [] + if len(topics) >= 3 and topics[0] == _TRANSFER_TOPIC: + log_addr = (log.get("address") or "").lower() + if log_addr == addr: + buyer = "0x" + topics[2][-40:] + amount_raw = _safe_int(log.get("data")) + if buyer not in early_buyers: + early_buyers[buyer] = { + "address": buyer, + "block": candidate_txs[i]["block"], + "blockIndex": candidate_txs[i]["blockIndex"], + "amount": amount_raw, + "label": _KNOWN_WALLETS.get(buyer, ""), + } + + # Check if early buyers still hold + buyer_list = list(early_buyers.values())[:15] + hold_tasks = [] + for b in buyer_list: + padded = b["address"].replace("0x", "").zfill(64) + hold_tasks.append( + _rpc_call("eth_call", + [{"to": addr, "data": f"{_ERC20_BALANCE_OF}{padded}"}, "latest"], + ttl=15) + ) + hold_results = await asyncio.gather(*hold_tasks, return_exceptions=True) + + for i, hr in enumerate(hold_results): + if isinstance(hr, Exception): + buyer_list[i]["stillHolds"] = False + else: + bal = _safe_int(hr.get("result")) + buyer_list[i]["stillHolds"] = bal > 0 + + # Calculate sniper score + # High score if many buys in first 2-3 blocks + first_block_buys = sum(1 for b in buyer_list if b["blockIndex"] >= 17) # earliest blocks + first_3_block_buys = sum(1 for b in buyer_list if b["blockIndex"] >= 15) + total_buys = len(buyer_list) + + sniper_score = 0 + if total_buys > 0: + concentration = first_3_block_buys / max(total_buys, 1) + sniper_score = min(100, int(concentration * 60 + first_block_buys * 15)) + + return { + "address": addr, + "symbol": token_symbol, + "pairAddress": pair_addr, + "sniperScore": sniper_score, + "totalEarlyBuyers": total_buys, + "earlyBuyers": [ + { + "address": b["address"], + "block": b["block"], + "amount": b["amount"], + "stillHolds": b.get("stillHolds", False), + "label": b["label"], + } + for b in buyer_list + ], + "blocksScanned": 20, + "candidateTxCount": len(candidate_txs), + } + except Exception as e: + return {"error": str(e), "address": addr} + + +# ── 2. Wallet Cluster ─────────────────────────────────────────────── + +@router.get("/mefai/wallet-cluster") +async def mefai_wallet_cluster( + address: str = Query(..., min_length=42, max_length=42), +): + """Find connected wallets by analyzing BNB transfers and shared token holdings.""" + import asyncio + + seed = address.lower() + try: + # Get latest block + bn = await _rpc_call("eth_blockNumber", [], ttl=5) + latest = _safe_int(bn.get("result")) + if latest == 0: + return {"error": "Cannot fetch block number"} + + # Scan last 30 blocks for direct transfers to/from seed + block_tasks = [ + _rpc_call("eth_getBlockByNumber", [hex(latest - i), True], ttl=10) + for i in range(30) + ] + block_results = await asyncio.gather(*block_tasks, return_exceptions=True) + + counterparties = {} + for br in block_results: + if isinstance(br, Exception) or not br.get("result"): + continue + for tx in (br["result"].get("transactions") or []): + from_addr = (tx.get("from") or "").lower() + to_addr = (tx.get("to") or "").lower() + value = _safe_int(tx.get("value")) + if value == 0: + continue + if from_addr == seed and to_addr: + cp = counterparties.setdefault(to_addr, {"txCount": 0}) + cp["txCount"] += 1 + elif to_addr == seed and from_addr: + cp = counterparties.setdefault(from_addr, {"txCount": 0}) + cp["txCount"] += 1 + + # Remove known contracts/routers + skip = {_PANCAKE_ROUTER.lower(), _1INCH_ROUTER.lower(), + "0x000000000000000000000000000000000000dead", + "0x0000000000000000000000000000000000000000", seed} + counterparties = {k: v for k, v in counterparties.items() if k not in skip} + + # Get BNB balances + top token balances for seed + counterparties (max 10) + wallets = [seed] + list(counterparties.keys())[:10] + token_list = list(_TOP_BSC_PORTFOLIO.items())[:6] + + bal_tasks = [] + for w in wallets: + bal_tasks.append(_rpc_call("eth_getBalance", [w, "latest"], ttl=15)) + padded = w.replace("0x", "").zfill(64) + for contract, _ in token_list: + bal_tasks.append( + _rpc_call("eth_call", + [{"to": contract.lower(), "data": f"{_ERC20_BALANCE_OF}{padded}"}, "latest"], + ttl=30) + ) + + bal_results = await asyncio.gather(*bal_tasks, return_exceptions=True) + + stride = 1 + len(token_list) + wallet_holdings = {} + for wi, w in enumerate(wallets): + offset = wi * stride + bnb_raw = _safe_int(bal_results[offset].get("result") if not isinstance(bal_results[offset], Exception) else "0x0") + bnb = bnb_raw / 1e18 + + held_tokens = [] + for ti, (contract, symbol) in enumerate(token_list): + r = bal_results[offset + 1 + ti] + if isinstance(r, Exception): + continue + bal = _safe_int(r.get("result")) + if bal > 0: + held_tokens.append(symbol) + + wallet_holdings[w] = {"bnb": bnb, "tokens": held_tokens} + + seed_tokens = set(wallet_holdings.get(seed, {}).get("tokens", [])) + + cluster = [] + total_value = wallet_holdings.get(seed, {}).get("bnb", 0) + for w in wallets[1:]: + info = wallet_holdings.get(w, {}) + shared = list(set(info.get("tokens", [])) & seed_tokens) + is_cluster = len(shared) >= 2 + entry = { + "address": w, + "bnbBalance": round(info.get("bnb", 0), 6), + "sharedTokens": shared, + "txCount": counterparties.get(w, {}).get("txCount", 0), + "label": _KNOWN_WALLETS.get(w, ""), + "isCluster": is_cluster, + } + if is_cluster: + cluster.append(entry) + total_value += info.get("bnb", 0) + + return { + "seedWallet": { + "address": seed, + "bnbBalance": round(wallet_holdings.get(seed, {}).get("bnb", 0), 6), + "tokens": wallet_holdings.get(seed, {}).get("tokens", []), + "label": _KNOWN_WALLETS.get(seed, ""), + }, + "clusterWallets": cluster, + "totalClusterValue": round(total_value, 6), + "counterpartiesScanned": len(counterparties), + "blocksScanned": 30, + } + except Exception as e: + return {"error": str(e), "address": seed} + + +# ── 3. Honeypot Check ─────────────────────────────────────────────── + +@router.get("/mefai/honeypot-check") +async def mefai_honeypot_check( + address: str = Query(..., min_length=42, max_length=42), +): + """Combined honeypot detection: bytecode analysis, owner concentration, DEX data.""" + import asyncio + + addr = address.lower() + try: + # Dangerous function selectors + dangerous_selectors = { + "44337ea1": "blacklist", + "8456cb59": "pause", + "49bd5a5e": "maxTxAmount", + "5342acb4": "excludeFromFee", + "715018a6": "renounceOwnership", + "a9059cbb": "transfer", + "dd62ed3e": "allowance", + } + trap_selectors = {"44337ea1", "8456cb59", "49bd5a5e", "5342acb4"} + + dead_padded = "000000000000000000000000000000000000000000000000000000000000dead" + + # Parallel: code, owner, totalSupply, name, symbol, decimals, DexScreener + tasks = [ + _rpc_call("eth_getCode", [addr, "latest"], ttl=300), # 0 + _rpc_call("eth_call", [{"to": addr, "data": "0x8da5cb5b"}, "latest"], ttl=60), # 1 owner + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_TOTAL_SUPPLY}, "latest"], ttl=60), # 2 + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_NAME}, "latest"], ttl=300), # 3 + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_SYMBOL}, "latest"], ttl=300), # 4 + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_DECIMALS}, "latest"], ttl=300), # 5 + fetch_json(f"{DEXSCREENER}/latest/dex/tokens/{addr}", ttl=30), # 6 + ] + results = await asyncio.gather(*tasks, return_exceptions=True) + + code_hex = results[0].get("result", "0x") if not isinstance(results[0], Exception) else "0x" + has_code = code_hex not in ("0x", "0x0", None, "") + + # Decode owner + owner = "" + if not isinstance(results[1], Exception): + owner_raw = results[1].get("result", "0x") + if owner_raw and len(owner_raw) >= 42: + owner = "0x" + owner_raw[-40:] + if owner == "0x" + "0" * 40: + owner = "" + + # Decode token info + name = _dec_str_util(results[3].get("result") if not isinstance(results[3], Exception) else None) + symbol = _dec_str_util(results[4].get("result") if not isinstance(results[4], Exception) else None) + decimals = 18 + if not isinstance(results[5], Exception): + d = _safe_int(results[5].get("result")) + if 0 < d <= 18: + decimals = d + total_supply_raw = _safe_int(results[2].get("result") if not isinstance(results[2], Exception) else "0x0") + total_supply = total_supply_raw / (10 ** decimals) if total_supply_raw else 0 + + # Owner balance + owner_pct = 0 + if owner: + owner_padded = owner.replace("0x", "").zfill(64) + ob = await _rpc_call("eth_call", + [{"to": addr, "data": f"{_ERC20_BALANCE_OF}{owner_padded}"}, "latest"], + ttl=30) + owner_bal = _safe_int(ob.get("result")) + owner_supply = owner_bal / (10 ** decimals) if owner_bal else 0 + owner_pct = (owner_supply / total_supply * 100) if total_supply > 0 else 0 + + # Scan bytecode for dangerous selectors + code_lower = code_hex.lower() if code_hex else "" + found_dangerous = [] + for sel, fname in dangerous_selectors.items(): + if sel in code_lower: + found_dangerous.append({"selector": sel, "function": fname}) + trap_count = sum(1 for sel in trap_selectors if sel in code_lower) + + # DexScreener data + dex_data = results[6] if not isinstance(results[6], Exception) else {} + pairs = [p for p in (dex_data.get("pairs") or []) if p.get("chainId") == "bsc"] + top_pair = pairs[0] if pairs else {} + + buy_count = 0 + sell_count = 0 + volume_buy = 0 + volume_sell = 0 + pair_age_hours = 0 + if top_pair: + txns = top_pair.get("txns", {}).get("h24", {}) + buy_count = txns.get("buys", 0) + sell_count = txns.get("sells", 0) + volume = top_pair.get("volume", {}) + volume_buy = volume.get("h24", 0) or 0 + created = top_pair.get("pairCreatedAt") + if created: + import time + pair_age_hours = (time.time() * 1000 - created) / 3600000 + + # Calculate trapScore + risk_factors = [] + trap_score = 50 # start neutral + + # Buy/sell asymmetry + if buy_count > 0 and sell_count == 0: + trap_score += 30 + risk_factors.append({"name": "No sells detected", "value": f"{buy_count} buys, 0 sells", "severity": "HIGH"}) + elif sell_count > 0: + ratio = buy_count / sell_count + if ratio > 5: + trap_score += 20 + risk_factors.append({"name": "Buy/sell asymmetry", "value": f"{ratio:.1f}x", "severity": "HIGH"}) + elif ratio > 2: + trap_score += 10 + risk_factors.append({"name": "Buy/sell asymmetry", "value": f"{ratio:.1f}x", "severity": "MEDIUM"}) + else: + trap_score -= 10 + + # Dangerous selectors + if trap_count >= 3: + trap_score += 25 + risk_factors.append({"name": "Dangerous functions", "value": f"{trap_count} found", "severity": "HIGH"}) + elif trap_count >= 1: + trap_score += 10 * trap_count + risk_factors.append({"name": "Dangerous functions", "value": f"{trap_count} found", "severity": "MEDIUM"}) + else: + trap_score -= 10 + + # Owner concentration + if owner_pct > 50: + trap_score += 20 + risk_factors.append({"name": "Owner concentration", "value": f"{owner_pct:.1f}%", "severity": "HIGH"}) + elif owner_pct > 20: + trap_score += 10 + risk_factors.append({"name": "Owner concentration", "value": f"{owner_pct:.1f}%", "severity": "MEDIUM"}) + elif owner_pct > 0: + risk_factors.append({"name": "Owner concentration", "value": f"{owner_pct:.1f}%", "severity": "LOW"}) + + # Pair age + if pair_age_hours < 1: + trap_score += 15 + risk_factors.append({"name": "Very new pair", "value": f"{pair_age_hours:.1f}h", "severity": "HIGH"}) + elif pair_age_hours < 24: + trap_score += 5 + risk_factors.append({"name": "New pair", "value": f"{pair_age_hours:.1f}h", "severity": "MEDIUM"}) + else: + trap_score -= 5 + + trap_score = max(0, min(100, trap_score)) + if trap_score >= 70: + verdict = "DANGER" + elif trap_score >= 40: + verdict = "CAUTION" + else: + verdict = "SAFE" + + return { + "address": addr, + "trapScore": trap_score, + "verdict": verdict, + "riskFactors": risk_factors, + "tokenInfo": { + "name": name, + "symbol": symbol, + "decimals": decimals, + "totalSupply": total_supply, + "ownerAddress": owner or None, + "ownerLabel": _KNOWN_WALLETS.get(owner, "") if owner else None, + "ownerConcentration": round(owner_pct, 2), + }, + "dangerousFunctions": found_dangerous, + "dexData": { + "pairCount": len(pairs), + "buys24h": buy_count, + "sells24h": sell_count, + "volume24h": volume_buy, + "price": float(top_pair.get("priceUsd", 0)) if top_pair.get("priceUsd") else None, + "liquidity": top_pair.get("liquidity", {}).get("usd"), + "pairAgeHours": round(pair_age_hours, 1), + }, + } + except Exception as e: + return {"error": str(e), "address": addr} + + +# ── 4. Validator Map ──────────────────────────────────────────────── + +@router.get("/mefai/validator-map") +async def mefai_validator_map(): + """BSC validator analysis: block production, gas utilization, MEV detection.""" + import asyncio + + try: + bn = await _rpc_call("eth_blockNumber", [], ttl=5) + latest = _safe_int(bn.get("result")) + if latest == 0: + return {"error": "Cannot fetch block number"} + + # Fetch 63 blocks (3 rounds of 21 validators) + block_tasks = [ + _rpc_call("eth_getBlockByNumber", [hex(latest - i), False], ttl=10) + for i in range(63) + ] + block_results = await asyncio.gather(*block_tasks, return_exceptions=True) + + validators = {} + rotation = [] + timestamps = [] + total_gas_used = 0 + total_gas_limit = 0 + total_txs = 0 + gas_prices = [] + + for idx, br in enumerate(block_results): + if isinstance(br, Exception) or not br.get("result"): + continue + block = br["result"] + miner = (block.get("miner") or "").lower() + gas_used = _safe_int(block.get("gasUsed")) + gas_limit = _safe_int(block.get("gasLimit")) + timestamp = _safe_int(block.get("timestamp")) + tx_count = len(block.get("transactions") or []) + block_num = _safe_int(block.get("number")) + + rotation.append(miner) + timestamps.append(timestamp) + total_gas_used += gas_used + total_gas_limit += gas_limit + total_txs += tx_count + + gas_util = (gas_used / gas_limit * 100) if gas_limit > 0 else 0 + + if miner not in validators: + validators[miner] = { + "blocks": [], + "gasUtils": [], + "txCounts": [], + "timestamps": [], + } + v = validators[miner] + v["blocks"].append(block_num) + v["gasUtils"].append(gas_util) + v["txCounts"].append(tx_count) + v["timestamps"].append(timestamp) + + # Calculate block times + block_times = [] + for i in range(len(timestamps) - 1): + dt = abs(timestamps[i] - timestamps[i + 1]) + if 0 < dt < 30: + block_times.append(dt) + + avg_block_time = sum(block_times) / len(block_times) if block_times else 3.0 + + # Detect MEV: gas utilization spikes + all_gas_utils = [] + for v in validators.values(): + all_gas_utils.extend(v["gasUtils"]) + median_util = sorted(all_gas_utils)[len(all_gas_utils) // 2] if all_gas_utils else 50 + + mev_alerts = [] + validator_list = [] + for miner, data in validators.items(): + label = _BSC_VALIDATORS.get(miner, _KNOWN_WALLETS.get(miner, "Unknown")) + blocks_produced = len(data["blocks"]) + avg_gas = sum(data["gasUtils"]) / len(data["gasUtils"]) if data["gasUtils"] else 0 + avg_tps = sum(data["txCounts"]) / (blocks_produced * avg_block_time) if blocks_produced > 0 else 0 + + # Timing consistency + if len(data["timestamps"]) >= 2: + intervals = [abs(data["timestamps"][i] - data["timestamps"][i + 1]) + for i in range(len(data["timestamps"]) - 1)] + timing_var = max(intervals) - min(intervals) if intervals else 0 + else: + timing_var = 0 + + validator_list.append({ + "address": miner, + "label": label, + "blocksProduced": blocks_produced, + "avgGasUtil": round(avg_gas, 2), + "avgTps": round(avg_tps, 2), + "timing": timing_var, + }) + + # MEV detection: blocks with gas util significantly above median + for gu in data["gasUtils"]: + if gu > median_util * 1.5 and gu > 80: + mev_alerts.append({ + "validator": label, + "gasUtil": round(gu, 2), + "medianUtil": round(median_util, 2), + }) + + validator_list.sort(key=lambda x: x["blocksProduced"], reverse=True) + + overall_tps = total_txs / (len(timestamps) * avg_block_time) if timestamps else 0 + overall_gas_util = (total_gas_used / total_gas_limit * 100) if total_gas_limit > 0 else 0 + + return { + "validators": validator_list, + "rotationPattern": rotation, + "mevAlerts": mev_alerts[:10], + "networkStats": { + "blocksAnalyzed": 63, + "uniqueValidators": len(validators), + "avgBlockTime": round(avg_block_time, 3), + "avgTps": round(overall_tps, 2), + "avgGasUtilization": round(overall_gas_util, 2), + "latestBlock": latest, + }, + } + except Exception as e: + return {"error": str(e)} + + +# ── 5. DEX Arb ────────────────────────────────────────────────────── + +@router.get("/mefai/dex-arb") +async def mefai_dex_arb( + address: str = Query(None, min_length=42, max_length=42), +): + """Find cross-DEX price discrepancies for BSC tokens.""" + import asyncio + + try: + # Determine tokens to scan + if address: + tokens_to_check = {address.lower(): "CUSTOM"} + else: + tokens_to_check = {k.lower(): v for k, v in _TOP_BSC_PORTFOLIO.items()} + + # Fetch DexScreener data for all tokens + dex_tasks = [ + fetch_json(f"{DEXSCREENER}/latest/dex/tokens/{addr}", ttl=30) + for addr in tokens_to_check + ] + dex_results = await asyncio.gather(*dex_tasks, return_exceptions=True) + + # Estimate gas cost in USD + gas_price_resp = await _rpc_call("eth_gasPrice", [], ttl=15) + gas_gwei = _safe_int(gas_price_resp.get("result")) / 1e9 + # Typical swap ~150k gas, arb needs 2 swaps = 300k gas + gas_cost_bnb = (gas_gwei * 300000) / 1e9 + # Get BNB price + bnb_resp = await fetch_json( + f"{DEXSCREENER}/latest/dex/tokens/0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + ttl=60) + bnb_price = 0 + for p in (bnb_resp.get("pairs") or []): + if p.get("chainId") == "bsc" and p.get("priceUsd"): + bnb_price = float(p["priceUsd"]) + break + gas_cost_usd = gas_cost_bnb * bnb_price + + opportunities = [] + for (addr, symbol), dex_data in zip(tokens_to_check.items(), dex_results): + if isinstance(dex_data, Exception) or not dex_data: + continue + pairs = [p for p in (dex_data.get("pairs") or []) if p.get("chainId") == "bsc" and p.get("priceUsd")] + if len(pairs) < 2: + continue + + # Resolve symbol + if symbol == "CUSTOM": + symbol = pairs[0].get("baseToken", {}).get("symbol", "UNKNOWN") + + # Group by DEX + by_dex = {} + for p in pairs: + dex_id = p.get("dexId", "unknown") + price = float(p["priceUsd"]) + liq = p.get("liquidity", {}).get("usd", 0) or 0 + if liq < 1000: + continue # skip illiquid pairs + if dex_id not in by_dex or price < by_dex[dex_id]["price"]: + by_dex[dex_id] = {"price": price, "pair": p.get("pairAddress"), "liquidity": liq} + + if len(by_dex) < 2: + continue + + # Find cheapest and most expensive + sorted_dexes = sorted(by_dex.items(), key=lambda x: x[1]["price"]) + cheapest_dex, cheapest_info = sorted_dexes[0] + expensive_dex, expensive_info = sorted_dexes[-1] + + spread_pct = ((expensive_info["price"] - cheapest_info["price"]) / cheapest_info["price"]) * 100 + # Estimate profit on $1000 trade + profit_on_1k = 1000 * (spread_pct / 100) - gas_cost_usd + + if spread_pct > 0.1: # minimum 0.1% spread + opportunities.append({ + "token": addr, + "symbol": symbol, + "cheapestDex": cheapest_dex, + "cheapestPrice": cheapest_info["price"], + "expensiveDex": expensive_dex, + "expensivePrice": expensive_info["price"], + "spreadPct": round(spread_pct, 4), + "estimatedProfitUsd": round(profit_on_1k, 2), + "gasEstimate": round(gas_cost_usd, 4), + }) + + opportunities.sort(key=lambda x: x["spreadPct"], reverse=True) + + return { + "opportunities": opportunities[:20], + "tokensScanned": len(tokens_to_check), + "gasPrice": round(gas_gwei, 2), + "gasCostUsd": round(gas_cost_usd, 4), + "bnbPrice": bnb_price, + } + except Exception as e: + return {"error": str(e)} + + +# ── 6. Token Birth ────────────────────────────────────────────────── + +@router.get("/mefai/token-birth") +async def mefai_token_birth( + address: str = Query(..., min_length=42, max_length=42), +): + """Token genesis analysis: creator profile, supply distribution, market data.""" + import asyncio + import time + + addr = address.lower() + dead_padded = "000000000000000000000000000000000000000000000000000000000000dead" + zero_padded = "0" * 64 + + try: + # Parallel: name, symbol, decimals, totalSupply, owner, balanceOf(dead), balanceOf(zero), DexScreener + tasks = [ + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_NAME}, "latest"], ttl=300), # 0 + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_SYMBOL}, "latest"], ttl=300), # 1 + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_DECIMALS}, "latest"], ttl=300), # 2 + _rpc_call("eth_call", [{"to": addr, "data": _ERC20_TOTAL_SUPPLY}, "latest"], ttl=60), # 3 + _rpc_call("eth_call", [{"to": addr, "data": "0x8da5cb5b"}, "latest"], ttl=60), # 4 owner + _rpc_call("eth_call", [{"to": addr, "data": f"{_ERC20_BALANCE_OF}{dead_padded}"}, "latest"], ttl=60), # 5 + _rpc_call("eth_call", [{"to": addr, "data": f"{_ERC20_BALANCE_OF}{zero_padded}"}, "latest"], ttl=60), # 6 + fetch_json(f"{DEXSCREENER}/latest/dex/tokens/{addr}", ttl=30), # 7 + ] + results = await asyncio.gather(*tasks, return_exceptions=True) + + name = _dec_str_util(results[0].get("result") if not isinstance(results[0], Exception) else None) + symbol = _dec_str_util(results[1].get("result") if not isinstance(results[1], Exception) else None) + decimals = 18 + if not isinstance(results[2], Exception): + d = _safe_int(results[2].get("result")) + if 0 < d <= 18: + decimals = d + total_supply_raw = _safe_int(results[3].get("result") if not isinstance(results[3], Exception) else "0x0") + total_supply = total_supply_raw / (10 ** decimals) if total_supply_raw else 0 + + # Owner + owner = "" + if not isinstance(results[4], Exception): + owner_raw = results[4].get("result", "0x") + if owner_raw and len(owner_raw) >= 42: + owner = "0x" + owner_raw[-40:] + if owner == "0x" + "0" * 40: + owner = "" + + dead_bal = _safe_int(results[5].get("result") if not isinstance(results[5], Exception) else "0x0") + zero_bal = _safe_int(results[6].get("result") if not isinstance(results[6], Exception) else "0x0") + burned = (dead_bal + zero_bal) / (10 ** decimals) if (dead_bal + zero_bal) else 0 + + # DexScreener + dex_data = results[7] if not isinstance(results[7], Exception) else {} + pairs = [p for p in (dex_data.get("pairs") or []) if p.get("chainId") == "bsc"] + top_pair = pairs[0] if pairs else {} + + created_at = top_pair.get("pairCreatedAt") + age_hours = 0 + if created_at: + age_hours = (time.time() * 1000 - created_at) / 3600000 + + price = float(top_pair.get("priceUsd", 0)) if top_pair.get("priceUsd") else None + volume24h = top_pair.get("volume", {}).get("h24") + liquidity = top_pair.get("liquidity", {}).get("usd") + change24h = top_pair.get("priceChange", {}).get("h24") + + # Owner profile + owner_profile = None + owner_hold_pct = 0 + if owner: + owner_padded = owner.replace("0x", "").zfill(64) + owner_tasks = [ + _rpc_call("eth_getBalance", [owner, "latest"], ttl=15), + _rpc_call("eth_getTransactionCount", [owner, "latest"], ttl=15), + _rpc_call("eth_call", [{"to": addr, "data": f"{_ERC20_BALANCE_OF}{owner_padded}"}, "latest"], ttl=30), + ] + oresults = await asyncio.gather(*owner_tasks, return_exceptions=True) + owner_bnb = _safe_int(oresults[0].get("result") if not isinstance(oresults[0], Exception) else "0x0") / 1e18 + owner_txs = _safe_int(oresults[1].get("result") if not isinstance(oresults[1], Exception) else "0x0") + owner_token_bal = _safe_int(oresults[2].get("result") if not isinstance(oresults[2], Exception) else "0x0") / (10 ** decimals) + owner_hold_pct = (owner_token_bal / total_supply * 100) if total_supply > 0 else 0 + + owner_profile = { + "address": owner, + "label": _KNOWN_WALLETS.get(owner, ""), + "bnbBalance": round(owner_bnb, 6), + "txCount": owner_txs, + "tokenHoldPct": round(owner_hold_pct, 2), + } + + burned_pct = (burned / total_supply * 100) if total_supply > 0 else 0 + circulating = total_supply - burned - (total_supply * owner_hold_pct / 100 if total_supply > 0 else 0) + + return { + "address": addr, + "name": name, + "symbol": symbol, + "decimals": decimals, + "creatorProfile": owner_profile, + "supplyBreakdown": { + "total": total_supply, + "burned": burned, + "burnedPct": round(burned_pct, 2), + "owner": round(total_supply * owner_hold_pct / 100, 2) if total_supply else 0, + "ownerPct": round(owner_hold_pct, 2), + "circulating": max(0, circulating), + }, + "marketData": { + "price": price, + "volume24h": volume24h, + "liquidity": liquidity, + "change24h": change24h, + }, + "pairCount": len(pairs), + "ageHours": round(age_hours, 1), + "ageDays": round(age_hours / 24, 1) if age_hours else 0, + } + except Exception as e: + return {"error": str(e), "address": addr} + + +# ── 7. Network Pulse ──────────────────────────────────────────────── + +@router.get("/mefai/network-pulse") +async def mefai_network_pulse(): + """Network congestion gauge: gas utilization, block timing, TPS trend.""" + import asyncio + + try: + bn = await _rpc_call("eth_blockNumber", [], ttl=5) + latest = _safe_int(bn.get("result")) + if latest == 0: + return {"error": "Cannot fetch block number"} + + # Fetch 20 blocks + gas price + tasks = [ + _rpc_call("eth_gasPrice", [], ttl=10), + ] + for i in range(20): + tasks.append(_rpc_call("eth_getBlockByNumber", [hex(latest - i), False], ttl=10)) + + results = await asyncio.gather(*tasks, return_exceptions=True) + + gas_price_wei = _safe_int(results[0].get("result") if not isinstance(results[0], Exception) else "0x0") + gas_price_gwei = gas_price_wei / 1e9 + + block_stats = [] + timestamps = [] + gas_utils = [] + tx_counts = [] + + for i in range(1, 21): + r = results[i] + if isinstance(r, Exception) or not r.get("result"): + continue + block = r["result"] + gas_used = _safe_int(block.get("gasUsed")) + gas_limit = _safe_int(block.get("gasLimit")) + ts = _safe_int(block.get("timestamp")) + tx_count = len(block.get("transactions") or []) + block_num = _safe_int(block.get("number")) + gas_util = (gas_used / gas_limit * 100) if gas_limit > 0 else 0 + + timestamps.append(ts) + gas_utils.append(gas_util) + tx_counts.append(tx_count) + + block_stats.append({ + "blockNum": block_num, + "gasUtil": round(gas_util, 2), + "txCount": tx_count, + }) + + # Calculate block times + block_times = [] + for i in range(len(timestamps) - 1): + dt = abs(timestamps[i] - timestamps[i + 1]) + if 0 < dt < 30: + block_times.append(dt) + block_stats[i]["blockTime"] = dt + + avg_block_time = sum(block_times) / len(block_times) if block_times else 3.0 + block_time_var = 0 + if block_times: + mean_bt = avg_block_time + block_time_var = sum((bt - mean_bt) ** 2 for bt in block_times) / len(block_times) + + avg_gas_util = sum(gas_utils) / len(gas_utils) if gas_utils else 0 + avg_tps = sum(tx_counts) / (len(tx_counts) * avg_block_time) if tx_counts else 0 + + # Pressure score 0-100 + pressure = 0 + pressure += min(40, avg_gas_util * 0.4) # gas util contributes up to 40 + pressure += min(20, gas_price_gwei * 2) # gas price + pressure += min(20, block_time_var * 10) # block time variance + pressure += min(20, avg_tps / 50 * 20) # TPS load + pressure = max(0, min(100, int(pressure))) + + if pressure >= 70: + recommendation = "CONGESTED" + elif pressure >= 40: + recommendation = "BUSY" + else: + recommendation = "OPTIMAL" + + return { + "pressureScore": pressure, + "recommendation": recommendation, + "gasUtilization": round(avg_gas_util, 2), + "avgBlockTime": round(avg_block_time, 3), + "currentTps": round(avg_tps, 2), + "gasPriceGwei": round(gas_price_gwei, 2), + "blockTimeVariance": round(block_time_var, 4), + "blockStats": block_stats, + "latestBlock": latest, + } + except Exception as e: + return {"error": str(e)} + + +# ── 8. Copy Trade ─────────────────────────────────────────────────── + +@router.get("/mefai/copy-trade") +async def mefai_copy_trade(): + """Monitor alpha wallets for recent trading activity.""" + import asyncio + + try: + # Use first 10 known wallets as alpha wallets + alpha_wallets = dict(list(_KNOWN_WALLETS.items())[:10]) + + bn = await _rpc_call("eth_blockNumber", [], ttl=5) + latest = _safe_int(bn.get("result")) + if latest == 0: + return {"error": "Cannot fetch block number"} + + # Scan last 15 blocks with full TXs + block_tasks = [ + _rpc_call("eth_getBlockByNumber", [hex(latest - i), True], ttl=10) + for i in range(15) + ] + block_results = await asyncio.gather(*block_tasks, return_exceptions=True) + + # Find transactions from/to alpha wallets + matching_txs = [] + for br in block_results: + if isinstance(br, Exception) or not br.get("result"): + continue + block = br["result"] + block_ts = _safe_int(block.get("timestamp")) + for tx in (block.get("transactions") or []): + from_addr = (tx.get("from") or "").lower() + to_addr = (tx.get("to") or "").lower() + if from_addr in alpha_wallets or to_addr in alpha_wallets: + wallet = from_addr if from_addr in alpha_wallets else to_addr + matching_txs.append({ + "hash": tx["hash"], + "wallet": wallet, + "walletLabel": alpha_wallets[wallet], + "from": from_addr, + "to": to_addr, + "value": _safe_int(tx.get("value")), + "timestamp": block_ts, + "input": tx.get("input", "0x")[:10], + }) + + # Fetch receipts for matching TXs to find Transfer events + receipt_tasks = [ + _rpc_call("eth_getTransactionReceipt", [tx["hash"]], ttl=30) + for tx in matching_txs[:20] + ] + receipts = await asyncio.gather(*receipt_tasks, return_exceptions=True) + + # Collect token addresses involved + token_addrs = set() + signals = [] + import time as _time + now = int(_time.time()) + + for i, rcpt in enumerate(receipts): + if isinstance(rcpt, Exception) or not rcpt.get("result"): + continue + tx = matching_txs[i] + logs = rcpt["result"].get("logs") or [] + + for log in logs: + topics = log.get("topics") or [] + if len(topics) >= 3 and topics[0] == _TRANSFER_TOPIC: + token_addr = (log.get("address") or "").lower() + from_t = "0x" + topics[1][-40:] + to_t = "0x" + topics[2][-40:] + amount_raw = _safe_int(log.get("data")) + token_addrs.add(token_addr) + + # Determine action + if from_t.lower() == tx["wallet"]: + action = "sell" + elif to_t.lower() == tx["wallet"]: + action = "buy" + else: + action = "transfer" + + signals.append({ + "wallet": tx["wallet"], + "walletLabel": tx["walletLabel"], + "action": action, + "token": token_addr, + "amount": amount_raw, + "timeAgo": now - tx["timestamp"], + "txHash": tx["hash"], + }) + + # Enrich with DexScreener data (batch up to 30 tokens) + token_list = list(token_addrs)[:30] + if token_list: + batch_url = f"{DEXSCREENER}/latest/dex/tokens/" + ",".join(token_list[:30]) + try: + token_data = await fetch_json(batch_url, ttl=30) + pair_map = {} + for p in (token_data.get("pairs") or []): + if p.get("chainId") == "bsc": + base = p.get("baseToken", {}).get("address", "").lower() + if base not in pair_map: + pair_map[base] = p + except Exception: + pair_map = {} + else: + pair_map = {} + + for sig in signals: + pair = pair_map.get(sig["token"], {}) + sig["tokenSymbol"] = pair.get("baseToken", {}).get("symbol", "UNKNOWN") + sig["tokenPrice"] = float(pair["priceUsd"]) if pair.get("priceUsd") else None + sig["tokenChange24h"] = pair.get("priceChange", {}).get("h24") + + active_wallets = len(set(s["wallet"] for s in signals)) + + return { + "signals": signals[:30], + "activeWallets": active_wallets, + "totalSignals": len(signals), + "blocksScanned": 15, + "alphaWalletsMonitored": len(alpha_wallets), + } + except Exception as e: + return {"error": str(e)} + + +# ── 9. Upgrade Monitor ───────────────────────────────────────────── + +@router.get("/mefai/upgrade-monitor") +async def mefai_upgrade_monitor( + address: str = Query(..., min_length=42, max_length=42), +): + """Proxy contract detection and upgrade risk assessment.""" + import asyncio + + addr = address.lower() + # EIP-1967 slots + impl_slot = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc" + admin_slot = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103" + + try: + tasks = [ + _rpc_call("eth_getCode", [addr, "latest"], ttl=300), # 0 + _rpc_call("eth_getStorageAt", [addr, impl_slot, "latest"], ttl=30), # 1 + _rpc_call("eth_getStorageAt", [addr, admin_slot, "latest"], ttl=30), # 2 + _rpc_call("eth_call", [{"to": addr, "data": "0x8da5cb5b"}, "latest"], ttl=60),# 3 owner + fetch_json(f"{DEXSCREENER}/latest/dex/tokens/{addr}", ttl=30), # 4 + ] + results = await asyncio.gather(*tasks, return_exceptions=True) + + # Check code + code_hex = results[0].get("result", "0x") if not isinstance(results[0], Exception) else "0x" + has_code = code_hex not in ("0x", "0x0", None, "") + code_size = (len(code_hex) - 2) // 2 if has_code else 0 + + if not has_code: + return {"error": "Not a contract", "address": addr, "isProxy": False, "codeSize": 0} + + # Implementation address + impl_raw = results[1].get("result", "0x" + "0" * 64) if not isinstance(results[1], Exception) else "0x" + "0" * 64 + impl_addr = "0x" + impl_raw[-40:] if impl_raw and len(impl_raw) >= 42 else "" + is_proxy = impl_addr != "" and impl_addr != "0x" + "0" * 40 + + # Admin address + admin_raw = results[2].get("result", "0x" + "0" * 64) if not isinstance(results[2], Exception) else "0x" + "0" * 64 + admin_addr = "0x" + admin_raw[-40:] if admin_raw and len(admin_raw) >= 42 else "" + if admin_addr == "0x" + "0" * 40: + admin_addr = "" + + # Owner + owner = "" + if not isinstance(results[3], Exception): + owner_raw = results[3].get("result", "0x") + if owner_raw and len(owner_raw) >= 42: + owner = "0x" + owner_raw[-40:] + if owner == "0x" + "0" * 40: + owner = "" + + # If proxy, get implementation code size + impl_code_size = 0 + if is_proxy: + impl_code = await _rpc_call("eth_getCode", [impl_addr, "latest"], ttl=300) + impl_hex = impl_code.get("result", "0x") + if impl_hex and impl_hex not in ("0x", "0x0"): + impl_code_size = (len(impl_hex) - 2) // 2 + + # Market context + dex_data = results[4] if not isinstance(results[4], Exception) else {} + pairs = [p for p in (dex_data.get("pairs") or []) if p.get("chainId") == "bsc"] + top_pair = pairs[0] if pairs else {} + + market_context = None + if top_pair: + market_context = { + "symbol": top_pair.get("baseToken", {}).get("symbol"), + "price": float(top_pair["priceUsd"]) if top_pair.get("priceUsd") else None, + "liquidity": top_pair.get("liquidity", {}).get("usd"), + "volume24h": top_pair.get("volume", {}).get("h24"), + } + + # Upgrade risk assessment + risk = "LOW" + if is_proxy: + if admin_addr and admin_addr != "0x" + "0" * 40: + risk = "HIGH" # has admin that can upgrade + else: + risk = "MEDIUM" # proxy but no admin slot (might use different pattern) + # If market context with significant liquidity, upgrade is critical + if market_context and (market_context.get("liquidity") or 0) > 100000: + risk = "CRITICAL" + elif owner: + risk = "MEDIUM" # has owner but not proxy + + return { + "address": addr, + "isProxy": is_proxy, + "implementationAddress": impl_addr if is_proxy else None, + "adminAddress": admin_addr if admin_addr else None, + "adminLabel": _KNOWN_WALLETS.get(admin_addr.lower(), "") if admin_addr else None, + "ownerAddress": owner or None, + "ownerLabel": _KNOWN_WALLETS.get(owner.lower(), "") if owner else None, + "codeSize": code_size, + "implCodeSize": impl_code_size if is_proxy else None, + "upgradeRisk": risk, + "marketContext": market_context, + } + except Exception as e: + return {"error": str(e), "address": addr} + + +# ── 10. Portfolio Heatmap ─────────────────────────────────────────── + +@router.get("/mefai/portfolio-heatmap") +async def mefai_portfolio_heatmap( + address: str = Query(..., min_length=42, max_length=42), +): + """Portfolio heatmap with performance: balances, USD values, 24h changes.""" + import asyncio + + addr = address.lower() + addr_padded = addr.replace("0x", "").zfill(64) + + # Merge token lists: _TOP_BSC_PORTFOLIO + _EXTENDED_TOKENS (15 total) + all_tokens = {**_TOP_BSC_PORTFOLIO, **_EXTENDED_TOKENS} + + try: + # BNB balance + all token balances in parallel + tasks = [_rpc_call("eth_getBalance", [addr, "latest"], ttl=15)] + token_list = list(all_tokens.items()) + for contract, _ in token_list: + tasks.append( + _rpc_call("eth_call", + [{"to": contract.lower(), "data": f"{_ERC20_BALANCE_OF}{addr_padded}"}, "latest"], + ttl=30) + ) + + # Also get BNB price + tasks.append(fetch_json( + f"{DEXSCREENER}/latest/dex/tokens/0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + ttl=60)) + + results = await asyncio.gather(*tasks, return_exceptions=True) + + # Parse BNB balance + bnb_raw = _safe_int(results[0].get("result") if not isinstance(results[0], Exception) else "0x0") + bnb_balance = bnb_raw / 1e18 + + # BNB price + bnb_price = 0 + bnb_change = 0 + bnb_dex = results[-1] + if not isinstance(bnb_dex, Exception): + for p in (bnb_dex.get("pairs") or []): + if p.get("chainId") == "bsc" and p.get("priceUsd"): + bnb_price = float(p["priceUsd"]) + bnb_change = p.get("priceChange", {}).get("h24", 0) or 0 + break + + # Parse token balances + held_tokens = [] + token_addrs = [] + for idx, (contract, symbol) in enumerate(token_list): + r = results[1 + idx] + if isinstance(r, Exception): + continue + bal = _safe_int(r.get("result")) + if bal > 0: + held_tokens.append({"contract": contract.lower(), "symbol": symbol, "rawBalance": bal}) + token_addrs.append(contract.lower()) + + # Fetch DexScreener data for held tokens + price_map = {} + if token_addrs: + batch_url = f"{DEXSCREENER}/latest/dex/tokens/" + ",".join(token_addrs[:30]) + try: + dex_data = await fetch_json(batch_url, ttl=30) + for p in (dex_data.get("pairs") or []): + if p.get("chainId") == "bsc": + base = p.get("baseToken", {}).get("address", "").lower() + if base not in price_map: + price_map[base] = { + "price": float(p["priceUsd"]) if p.get("priceUsd") else 0, + "change24h": p.get("priceChange", {}).get("h24", 0) or 0, + "logo": p.get("info", {}).get("imageUrl", ""), + } + except Exception: + pass + + # Build holdings list + holdings = [] + # BNB first + bnb_value = bnb_balance * bnb_price + holdings.append({ + "symbol": "BNB", + "balance": round(bnb_balance, 6), + "valueUsd": round(bnb_value, 2), + "change24h": bnb_change, + "pctOfPortfolio": 0, # will calculate after total + "logo": "", + }) + + total_value = bnb_value + for tok in held_tokens: + pinfo = price_map.get(tok["contract"], {}) + price = pinfo.get("price", 0) + # Assume 18 decimals for simplicity (most BSC tokens) + balance = tok["rawBalance"] / 1e18 + value = balance * price + total_value += value + holdings.append({ + "symbol": tok["symbol"], + "balance": round(balance, 6), + "valueUsd": round(value, 2), + "change24h": pinfo.get("change24h", 0), + "pctOfPortfolio": 0, + "logo": pinfo.get("logo", ""), + }) + + # Calculate percentages and sort + for h in holdings: + h["pctOfPortfolio"] = round((h["valueUsd"] / total_value * 100) if total_value > 0 else 0, 2) + holdings.sort(key=lambda x: x["valueUsd"], reverse=True) + + # 24h weighted P&L + weighted_change = 0 + for h in holdings: + weight = h["pctOfPortfolio"] / 100 + weighted_change += (h.get("change24h") or 0) * weight + + # Concentration risk + top_pct = holdings[0]["pctOfPortfolio"] if holdings else 0 + if top_pct > 80: + concentration_risk = "HIGH" + elif top_pct > 50: + concentration_risk = "MEDIUM" + else: + concentration_risk = "LOW" + + # Health score: penalize high concentration + negative change + health = 70 + health -= max(0, top_pct - 50) # penalize over-concentration + health += min(15, max(-15, weighted_change)) # boost/penalize by performance + health += min(15, len([h for h in holdings if h["valueUsd"] > 1]) * 3) # diversity bonus + health = max(0, min(100, int(health))) + + return { + "address": addr, + "totalValueUsd": round(total_value, 2), + "change24hPct": round(weighted_change, 2), + "holdings": [h for h in holdings if h["valueUsd"] > 0.01], + "healthScore": health, + "concentrationRisk": concentration_risk, + "topHolding": holdings[0]["symbol"] if holdings else None, + "tokenCount": len([h for h in holdings if h["valueUsd"] > 0.01]), + } + except Exception as e: + return {"error": str(e), "address": addr} diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..7d7ee5d --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,177 @@ + + + + + +BNB Chain Skills + + + + + +
+
+ + +
+ +
+ +
+ +
+ Not an official Binance product. Open-source community project built on BNB Chain MCP. + Powered by Mefai +
+ + + + + + + diff --git a/frontend/js/api.js b/frontend/js/api.js new file mode 100644 index 0000000..4abea7a --- /dev/null +++ b/frontend/js/api.js @@ -0,0 +1,158 @@ +// BNB Chain Skills — API layer with persistent cache + stale-while-revalidate + +const API_BASE = '/superbsc/api/bnbchain'; +const CACHE_KEY = 'bnbchain-cache'; +const CACHE_MAX_AGE = 300000; // 5 min + +const _c = new Map(); + +// Restore from localStorage +try { + const stored = localStorage.getItem(CACHE_KEY); + if (stored) { + const entries = JSON.parse(stored); + const now = Date.now(); + for (const [k, v] of entries) { + if (now - v.t < CACHE_MAX_AGE) _c.set(k, v); + } + } +} catch {} + +let _saveTimer = null; +function _persist() { + if (_saveTimer) return; + _saveTimer = setTimeout(() => { + _saveTimer = null; + try { + const entries = []; + const now = Date.now(); + for (const [k, v] of _c) { + if (now - v.t < CACHE_MAX_AGE) entries.push([k, v]); + } + localStorage.setItem(CACHE_KEY, JSON.stringify(entries.slice(-100))); + } catch {} + }, 1000); +} + +function _cg(k, ttl, stale) { + const e = _c.get(k); + if (!e) return null; + if (Date.now() - e.t < ttl) return e.d; + return stale ? e.d : null; +} + +function _cs(k, d) { + _c.set(k, { d, t: Date.now() }); + if (_c.size > 200) { + const now = Date.now(); + for (const [ek, ev] of _c) { if (now - ev.t > 60000) { _c.delete(ek); if (_c.size <= 150) break; } } + } + _persist(); +} + +const _inflight = new Map(); + +function _url(path, params = {}) { + const u = new URL(API_BASE + path, window.location.origin); + for (const [k, v] of Object.entries(params)) + if (v !== undefined && v !== null && v !== '') u.searchParams.set(k, v); + return u.href; +} + +async function _fetchGet(url) { + const ac = new AbortController(); + const tid = setTimeout(() => ac.abort(), 15000); + try { + const r = await fetch(url, { signal: ac.signal }); + clearTimeout(tid); + let d; + try { d = await r.json(); } catch { return { error: true, status: r.status }; } + if (r.ok && !d?.error) _cs(url, d); + return d; + } catch (e) { + clearTimeout(tid); + if (e.name === 'AbortError') return { error: true, status: 408, detail: 'timeout' }; + throw e; + } finally { + _inflight.delete(url); + } +} + +async function get(path, params = {}, ttl = 30000) { + const url = _url(path, params); + const fresh = _cg(url, ttl, false); + if (fresh) return fresh; + const stale = _cg(url, CACHE_MAX_AGE, true); + if (stale) { + if (!_inflight.has(url)) { + const p = _fetchGet(url); + _inflight.set(url, p); + p.catch(() => {}); + } + return stale; + } + if (_inflight.has(url)) return _inflight.get(url); + const p = _fetchGet(url); + _inflight.set(url, p); + return p; +} + +// Public API +window.bnbApi = { + blockNumber: () => get('/block-number', {}, 5000), + block: (n) => get('/block', { number: n || 'latest' }, 15000), + blockFull: (n) => get('/block', { number: n || 'latest', full: true }, 15000), + tx: (hash) => get('/tx', { hash }, 60000), + receipt: (hash) => get('/receipt', { hash }, 60000), + balance: (addr) => get('/balance', { address: addr }, 15000), + gasPrice: () => get('/gas-price', {}, 10000), + chainId: () => get('/chain-id', {}, 3600000), + peerCount: () => get('/peer-count', {}, 30000), + syncing: () => get('/syncing', {}, 10000), + supportedNetworks: () => get('/supported-networks', {}, 3600000), + estimateGas: (to, data, value) => get('/estimate-gas', { to, data, value }, 10000), + code: (addr) => get('/code', { address: addr }, 60000), + tokenInfo: (contract) => get('/token-info', { contract }, 60000), + tokenBalance: (contract, addr) => get('/token-balance', { contract, address: addr }, 15000), + readContract: (contract, data) => get('/read-contract', { contract, data }, 15000), + nftBalance: (owner, contract) => get('/nft-balance', { owner, contract }, 30000), + nftTokens: (owner, contract, count) => get('/nft-tokens', { owner, contract, count: count || 10 }, 60000), + greenfieldStatus: () => get('/greenfield/status', {}, 30000), + greenfieldBuckets: (addr) => get('/greenfield/buckets', { address: addr }, 60000), + erc8004Agent: (tokenId) => get('/erc8004/agent', { token_id: tokenId }, 60000), + erc8004Balance: (addr) => get('/erc8004/balance', { address: addr }, 30000), + addressProfile: (addr) => get('/address-profile', { address: addr }, 15000), + marketTopTokens: () => get('/market/top-tokens', {}, 60000), + marketSearch: (q) => get('/market/search', { q }, 30000), + marketToken: (addr) => get('/market/token', { address: addr }, 30000), + mefaiWalletScanner: (addr) => get('/mefai/wallet-scanner', { address: addr }, 30000), + mefaiContractAudit: (addr) => get('/mefai/contract-audit', { address: addr }, 30000), + mefaiLiquidityPulse: () => get('/mefai/liquidity-pulse', {}, 60000), + mefaiBurnTracker: () => get('/mefai/burn-tracker', {}, 120000), + mefaiPancakeswapArena: () => get('/mefai/pancakeswap-arena', {}, 30000), + mefaiChainVitals: () => get('/mefai/chain-vitals', {}, 10000), + mefaiTokenTrends: () => get('/mefai/token-trends', {}, 60000), + mefaiDefiLeaderboard: () => get('/mefai/defi-leaderboard', {}, 60000), + mefaiBscSupremacy: () => get('/mefai/bsc-supremacy', {}, 15000), + mefaiSmartMoney: () => get('/mefai/smart-money', {}, 15000), + mefaiTxDecoder: (hash) => get('/mefai/tx-decoder', { hash }, 120000), + mefaiContractXray: (addr) => get('/mefai/contract-xray', { address: addr }, 300000), + mefaiApprovalScanner: (addr) => get('/mefai/approval-scanner', { address: addr }, 30000), + mefaiBlockAutopsy: (n) => get('/mefai/block-autopsy', n ? { number: n } : {}, 15000), + mefaiGasCalculator: () => get('/mefai/gas-calculator', {}, 15000), + mefaiTokenBattle: (tokens) => get('/mefai/token-battle', { tokens }, 60000), + mefaiPairAnalytics: (addr) => get('/mefai/pair-analytics', { address: addr }, 30000), + mefaiTokenFlow: (addr) => get('/mefai/token-flow', { contract: addr }, 15000), + mefaiYieldFinder: () => get('/mefai/yield-finder', {}, 60000), + mefaiRiskRadar: (addr) => get('/mefai/risk-radar', { address: addr }, 60000), + mefaiSniperDetector: (addr) => get('/mefai/sniper-detector', { address: addr }, 30000), + mefaiWalletCluster: (addr) => get('/mefai/wallet-cluster', { address: addr }, 30000), + mefaiHoneypotCheck: (addr) => get('/mefai/honeypot-check', { address: addr }, 30000), + mefaiValidatorMap: () => get('/mefai/validator-map', {}, 15000), + mefaiDexArb: (addr) => get('/mefai/dex-arb', addr ? { address: addr } : {}, 30000), + mefaiTokenBirth: (addr) => get('/mefai/token-birth', { address: addr }, 60000), + mefaiNetworkPulse: () => get('/mefai/network-pulse', {}, 10000), + mefaiCopyTrade: () => get('/mefai/copy-trade', {}, 15000), + mefaiUpgradeMonitor: (addr) => get('/mefai/upgrade-monitor', { address: addr }, 30000), + mefaiPortfolioHeatmap: (addr) => get('/mefai/portfolio-heatmap', { address: addr }, 30000), +}; diff --git a/frontend/js/app.js b/frontend/js/app.js new file mode 100644 index 0000000..0decefd --- /dev/null +++ b/frontend/js/app.js @@ -0,0 +1,169 @@ +// BNB Chain Skills — Layout engine & panel registry + +const panelRegistry = { + 'network-status': 'network-status-panel', + 'block-explorer': 'block-explorer-panel', + 'tx-explorer': 'tx-explorer-panel', + 'gas-estimator': 'gas-estimator-panel', + 'address-profiler': 'address-profiler-panel', + 'token-inspector': 'token-inspector-panel', + 'contract-reader': 'contract-reader-panel', + 'nft-explorer': 'nft-explorer-panel', + 'transfer-ops': 'transfer-ops-panel', + 'contract-writing': 'contract-writing-panel', + 'erc8004': 'erc8004-panel', + 'greenfield-overview':'greenfield-overview-panel', + 'greenfield-buckets': 'greenfield-buckets-panel', + 'greenfield-objects': 'greenfield-objects-panel', + 'greenfield-payments':'greenfield-payments-panel', + 'mcp-prompts': 'mcp-prompts-panel', + 'market-pack': 'market-pack-panel', + 'syncx': 'syncx-panel', + 'whale-radar': 'whale-radar-panel', + 'token-xray': 'token-xray-panel', + 'bsc-live-feed': 'bsc-live-feed-panel', + 'wallet-scanner': 'wallet-scanner-panel', + 'contract-audit': 'contract-audit-panel', + 'liquidity-pulse': 'liquidity-pulse-panel', + 'burn-tracker': 'burn-tracker-panel', + 'pancakeswap-arena': 'pancakeswap-arena-panel', + 'chain-vitals': 'chain-vitals-panel', + 'token-trends': 'token-trends-panel', + 'defi-leaderboard': 'defi-leaderboard-panel', + 'bsc-supremacy': 'bsc-supremacy-panel', + 'smart-money': 'smart-money-panel', + 'mefai-hub': 'mefai-hub-panel', + 'tx-decoder': 'tx-decoder-panel', + 'contract-xray': 'contract-xray-panel', + 'approval-scanner': 'approval-scanner-panel', + 'block-autopsy': 'block-autopsy-panel', + 'gas-calculator': 'gas-calculator-panel', + 'token-battle': 'token-battle-panel', + 'pair-analytics': 'pair-analytics-panel', + 'token-flow': 'token-flow-panel', + 'yield-finder': 'yield-finder-panel', + 'risk-radar': 'risk-radar-panel', + 'sniper-detector': 'sniper-detector-panel', + 'wallet-cluster': 'wallet-cluster-panel', + 'honeypot-check': 'honeypot-check-panel', + 'validator-map': 'validator-map-panel', + 'dex-arb': 'dex-arb-panel', + 'token-birth': 'token-birth-panel', + 'network-pulse': 'network-pulse-panel', + 'copy-trade': 'copy-trade-panel', + 'upgrade-monitor': 'upgrade-monitor-panel', + 'portfolio-heatmap': 'portfolio-heatmap-panel', +}; + +const layouts = { + 'all': { + name: 'All', + grid: 'grid-2x4', + panels: [ + 'network-status', 'block-explorer', 'tx-explorer', 'gas-estimator', + 'address-profiler', 'token-inspector', 'contract-reader', 'nft-explorer', + ], + }, + 'evm': { + name: 'EVM', + grid: 'grid-2x3', + panels: [ + 'network-status', 'block-explorer', 'tx-explorer', + 'gas-estimator', 'transfer-ops', 'contract-writing', + ], + }, + 'tokens': { + name: 'Tokens', + grid: 'grid-2x2', + panels: ['token-inspector', 'nft-explorer', 'contract-reader', 'address-profiler'], + }, + 'greenfield': { + name: 'Greenfield', + grid: 'grid-2x2', + panels: ['greenfield-overview', 'greenfield-buckets', 'greenfield-objects', 'greenfield-payments'], + }, + 'erc8004': { + name: 'ERC-8004', + grid: 'grid-1x2', + panels: ['erc8004', 'mcp-prompts'], + }, + 'live': { + name: 'Live', + grid: 'grid-2x3', + panels: ['bsc-live-feed', 'smart-money', 'bsc-supremacy', 'burn-tracker', 'token-xray', 'market-pack'], + }, + 'community': { + name: 'Community', + grid: 'grid-1x3', + panels: ['market-pack', 'syncx', 'token-xray'], + }, + 'mefai': { + name: 'Mefai', + grid: 'grid-1x1', + panels: ['mefai-hub'], + isMefaiHub: true, + }, +}; + +const grid = document.getElementById('grid'); +const PREF_KEY = 'bnbchain-layout'; + +function setLayout(key) { + const layout = layouts[key]; + if (!layout) return; + + grid.innerHTML = ''; + grid.className = 'grid ' + layout.grid; + + layout.panels.forEach(name => { + const tag = panelRegistry[name]; + if (tag && customElements.get(tag)) { + grid.appendChild(document.createElement(tag)); + } else { + const div = document.createElement('div'); + div.className = 'panel'; + div.innerHTML = ` +
${name}
+
Panel not loaded
`; + grid.appendChild(div); + } + }); + + document.querySelectorAll('.layout-btn').forEach(btn => { + btn.classList.toggle('active', btn.dataset.layout === key); + }); + + try { localStorage.setItem(PREF_KEY, key); } catch {} +} + +// Nav clicks +document.querySelectorAll('.layout-btn[data-layout]').forEach(btn => { + btn.addEventListener('click', () => setLayout(btn.dataset.layout)); +}); + +// Keyboard: r = refresh all, 1-5 = switch layout +document.addEventListener('keydown', e => { + if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return; + if (e.key === 'r') { + document.querySelectorAll('.panel').forEach(p => { if (p.refresh) p.refresh(); }); + return; + } + const n = parseInt(e.key); + if (n >= 1 && n <= 9) { + const keys = Object.keys(layouts); + if (keys[n - 1]) setLayout(keys[n - 1]); + } +}); + +// Init +function init() { + const urlLayout = new URLSearchParams(window.location.search).get('layout'); + let saved = null; + try { saved = localStorage.getItem(PREF_KEY); } catch {} + const key = (urlLayout && layouts[urlLayout]) ? urlLayout + : (saved && layouts[saved]) ? saved + : 'all'; + setLayout(key); +} + +document.addEventListener('DOMContentLoaded', init); diff --git a/frontend/js/base-panel.js b/frontend/js/base-panel.js new file mode 100644 index 0000000..a7001bc --- /dev/null +++ b/frontend/js/base-panel.js @@ -0,0 +1,101 @@ +// BNB Chain Skills — BasePanel Web Component + +class BasePanel extends HTMLElement { + static skill = ''; + static defaultTitle = 'Panel'; + + constructor() { + super(); + this._interval = null; + this._refreshRate = 10000; + this._loading = false; + this._data = null; + this._error = null; + this._firstLoad = true; + } + + connectedCallback() { + this.classList.add('panel'); + this.render(); + this.startAutoRefresh(); + this._visCb = () => { + if (document.hidden) this.stopAutoRefresh(); + else this.startAutoRefresh(); + }; + document.addEventListener('visibilitychange', this._visCb); + } + + disconnectedCallback() { + this.stopAutoRefresh(); + if (this._visCb) document.removeEventListener('visibilitychange', this._visCb); + } + + render() { + const title = this.getAttribute('title') || this.constructor.defaultTitle; + const skill = this.constructor.skill; + this.innerHTML = ` +
+
+ ${title} + ${skill ? `${skill}` : ''} +
+
+ +
+
+
Loading...
+ `; + this.querySelector('.panel-refresh')?.addEventListener('click', () => this.refresh()); + this.refresh(); + } + + async refresh() { + if (this._loading) return; + this._loading = true; + const body = this.querySelector('.panel-body'); + try { + const data = await this.fetchData(); + this._data = data; + this._error = null; + this._firstLoad = false; + if (body) { + body.innerHTML = this.renderContent(this._data); + this.afterRender(body); + } + } catch (e) { + this._error = e; + if (!this._data || this._firstLoad) { + if (body) body.innerHTML = `
Error: ${_esc(e.message || 'Unknown')}
`; + } + } + this._loading = false; + } + + async fetchData() { return null; } + renderContent(data) { return '
No data
'; } + afterRender(body) {} + + startAutoRefresh() { + this.stopAutoRefresh(); + if (this._refreshRate > 0) this._interval = setInterval(() => this.refresh(), this._refreshRate); + } + + stopAutoRefresh() { + if (this._interval) { clearInterval(this._interval); this._interval = null; } + } +} + +function _esc(s) { + const d = document.createElement('div'); + d.textContent = s; + return d.innerHTML; +} + +function _fmt(n) { + if (n === null || n === undefined) return '—'; + return Number(n).toLocaleString('en-US'); +} + +window.BasePanel = BasePanel; +window._esc = _esc; +window._fmt = _fmt; diff --git a/frontend/js/panels.js b/frontend/js/panels.js new file mode 100644 index 0000000..4b53400 --- /dev/null +++ b/frontend/js/panels.js @@ -0,0 +1,3393 @@ +// BNB Chain Skills — All 16 Panels +// Each panel shows skill number(s) it demonstrates + +// ── Well-known BSC addresses for examples ────────────────────────── +const EX = { + WBNB: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c', + BUSD: '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56', + USDT: '0x55d398326f99059fF775485246999027B3197955', + CAKE: '0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82', + PANCAKE_ROUTER: '0x10ED43C718714eb63d5aA57B78B54917c3e6fE49', + DEAD: '0x000000000000000000000000000000000000dEaD', + BINANCE_HOT: '0x8894E0a0c962CB723c1ef8a1B6737B4E1e2AE02b', + CZ_WALLET: '0x4B16c5dE96EB2117bBE5fd171E4d203624B014aa', + NFT_PANCAKE: '0xDf7952B35f24aCF7fC0487D01c8d5690a60DBa07', + ERC8004_REG: '0x5901DeB30816E210B2C4cFeDC21De5f288Ec4C73', +}; + +// Token logos from TrustWallet assets CDN +function tokenLogo(addr) { + if (!addr) return ''; + return 'https://assets-cdn.trustwallet.com/blockchains/smartchain/assets/' + addr + '/logo.png'; +} + +// ── Shared CSS fragments ──────────────────────────────────────────── +const S = { + tabs: '.p-tabs{display:flex;gap:4px;margin-bottom:8px}.p-tab{padding:3px 10px;font-size:10px;font-weight:600;border:1px solid var(--border);border-radius:4px;background:transparent;color:var(--text-muted);cursor:pointer;text-transform:uppercase;transition:all .15s;box-shadow:0 2px 0 var(--border)}.p-tab:hover{color:var(--text);transform:translateY(-1px);box-shadow:0 3px 0 var(--border)}.p-tab:active{transform:translateY(1px);box-shadow:none}.p-tab.active{background:var(--accent);color:#0b0e11;border-color:var(--accent);box-shadow:0 2px 0 #c99700}', + cards: '.p-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;margin-bottom:10px}.p-cards-2{grid-template-columns:repeat(2,1fr)}.p-card{background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:8px;text-align:center}.p-label{font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px}.p-val{font-size:14px;font-weight:700;color:var(--text);margin-top:2px}.p-sub{font-size:9px;margin-top:1px;color:var(--text-muted)}', + search: '.p-search{display:flex;gap:4px;margin-bottom:10px}.p-input{flex:1;background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:6px 8px;font-size:11px;color:var(--text);font-family:inherit}.p-input:focus{border-color:var(--accent);outline:none}.p-btn{padding:6px 12px;background:var(--accent);color:#0b0e11;border:none;border-radius:4px;font-size:10px;font-weight:700;cursor:pointer;text-transform:uppercase;transition:all .15s;box-shadow:0 3px 0 #c99700,0 4px 8px rgba(0,0,0,.3)}.p-btn:hover{transform:translateY(-1px);box-shadow:0 4px 0 #c99700,0 6px 12px rgba(0,0,0,.4)}.p-btn:active{transform:translateY(2px);box-shadow:0 1px 0 #c99700}.p-btn-sm{padding:4px 8px;font-size:9px}', + detail: '.p-detail{background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:10px;font-size:11px;overflow-wrap:break-word;word-wrap:break-word}.p-row{display:flex;justify-content:space-between;padding:4px 0;border-bottom:1px solid var(--border)}.p-row:last-child{border-bottom:none}.p-key{color:var(--text-muted);font-size:10px;min-width:80px}.p-value{color:var(--text);font-size:10px;text-align:right;max-width:65%;overflow:hidden;text-overflow:ellipsis;overflow-wrap:break-word;word-wrap:break-word}.p-mono{font-family:monospace;font-size:9px;color:var(--accent)}', + info: '.p-info{background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:12px;font-size:11px;line-height:1.6;overflow-wrap:break-word;word-wrap:break-word}.p-feat{padding:6px 0;border-bottom:1px solid var(--border)}.p-feat:last-child{border-bottom:none}.p-feat-t{font-weight:700;font-size:11px;color:var(--text)}.p-feat-d{font-size:9px;color:var(--text-muted);margin-top:1px}.p-badge{display:inline-block;padding:2px 6px;border-radius:3px;font-size:8px;font-weight:700;text-transform:uppercase}.p-badge-ok{background:rgba(14,203,129,.15);color:#0ecb81}.p-badge-warn{background:rgba(240,185,11,.15);color:#f0b90b}.p-badge-write{background:rgba(246,70,93,.12);color:#f6465d}.p-param{font-family:monospace;font-size:10px;color:var(--accent);background:var(--bg);padding:2px 5px;border-radius:3px;display:inline-block;margin:2px 0}', + examples: '.p-examples{display:flex;gap:4px;flex-wrap:wrap;margin-bottom:8px}.p-ex{padding:3px 8px;font-size:9px;background:var(--bg);border:1px solid var(--border);border-radius:4px;color:var(--text-muted);cursor:pointer;transition:all .15s;box-shadow:0 2px 0 var(--border);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.p-ex:hover{border-color:var(--accent);color:var(--accent);transform:translateY(-1px);box-shadow:0 3px 0 var(--border)}.p-ex:active{transform:translateY(1px);box-shadow:none}', + table: '.p-table{width:100%;border-collapse:collapse;font-size:10px}.p-table th{text-align:left;padding:4px 6px;font-weight:600;font-size:9px;text-transform:uppercase;letter-spacing:.5px;color:var(--text-muted);border-bottom:1px solid var(--border);position:sticky;top:0;background:var(--bg2);white-space:nowrap}.p-table td{padding:4px 6px;border-bottom:1px solid var(--border);white-space:nowrap}.p-table tr:hover td{background:rgba(240,185,11,.04)}.p-table .t-right{text-align:right}.p-table .t-green{color:#0ecb81}.p-table .t-red{color:#f6465d}.p-table .t-mono{font-family:monospace;font-size:9px}.p-table .t-name{font-weight:600;color:var(--text)}.p-table .t-sym{color:var(--accent);font-weight:700;font-size:10px}.p-table .t-muted{color:var(--text-muted);font-size:9px}', +}; + +function css(...keys) { return ''; } + +function tabsHTML(tabs, active) { + return '
' + tabs.map(t => + `` + ).join('') + '
'; +} + +function bindTabs(body, panel) { + body.querySelectorAll('.p-tab').forEach(t => t.addEventListener('click', () => { + panel._tab = t.dataset.tab; + body.innerHTML = panel.renderContent(panel._data); + panel.afterRender(body); + })); +} + +function examplesHTML(items, inputId) { + return '
' + items.map(e => + `` + ).join('') + '
'; +} + +function bindExamples(body, panel, searchFn) { + body.querySelectorAll('.p-ex').forEach(btn => btn.addEventListener('click', () => { + const inputId = btn.dataset.input; + const val = btn.dataset.val; + const inp = body.querySelector('#' + inputId); + if (inp) inp.value = val; + // Store value so it persists after re-render + if (!panel._inputVals) panel._inputVals = {}; + panel._inputVals[inputId] = val; + if (searchFn) searchFn(val); + })); +} + +// Restore stored input values after re-render +function restoreInputs(body, panel) { + if (!panel._inputVals) return; + for (const [id, val] of Object.entries(panel._inputVals)) { + const inp = body.querySelector('#' + id); + if (inp && !inp.value) inp.value = val; + } +} + +function decodeString(hex) { + if (!hex || hex === '0x' || hex.length < 66) return ''; + try { + const raw = hex.replace('0x', ''); + const offset = parseInt(raw.slice(0, 64), 16) * 2; + const len = parseInt(raw.slice(offset, offset + 64), 16); + const data = raw.slice(offset + 64, offset + 64 + len * 2); + let str = ''; + for (let i = 0; i < data.length; i += 2) { + const c = parseInt(data.slice(i, i + 2), 16); + if (c > 0) str += String.fromCharCode(c); + } + return str; + } catch { return ''; } +} + +function shortAddr(a) { return a ? a.slice(0, 8) + '...' + a.slice(-6) : ''; } + +// ═══════════════════════════════════════════════════════════════════ +// 1. Network Status — Skills #1-#5 +// ═══════════════════════════════════════════════════════════════════ +class NetworkStatusPanel extends BasePanel { + static skill = 'Skill #1-#5'; + static defaultTitle = 'Network Status'; + constructor() { super(); this._refreshRate = 15000; this._tab = 'status'; } + + async fetchData() { + const [blk, gas, chain, sync, nets] = await Promise.allSettled([ + bnbApi.blockNumber(), bnbApi.gasPrice(), bnbApi.chainId(), bnbApi.syncing(), bnbApi.supportedNetworks(), + ]); + return { + blockNum: blk.status === 'fulfilled' && blk.value?.result ? parseInt(blk.value.result, 16) : 0, + gasPrice: gas.status === 'fulfilled' && gas.value?.result ? parseInt(gas.value.result, 16) / 1e9 : 0, + chainId: chain.status === 'fulfilled' && chain.value?.result ? parseInt(chain.value.result, 16) : 0, + syncing: sync.status === 'fulfilled' ? sync.value?.result : false, + networks: nets.status === 'fulfilled' && nets.value?.networks ? nets.value.networks : [], + }; + } + + renderContent(d) { + if (!d) return '
Loading...
'; + let h = css('tabs', 'cards', 'detail'); + h += '
'; + h += `
Block
${_fmt(d.blockNum)}
`; + h += `
Gas
${d.gasPrice.toFixed(1)}
Gwei
`; + h += `
Status
${d.syncing === false ? 'Synced' : 'Syncing'}
`; + h += '
'; + h += tabsHTML([{id:'status',label:'Chain'},{id:'networks',label:'Networks'}], this._tab); + if (this._tab === 'status') { + h += '
'; + h += `
Chain ID
${d.chainId}
BNB Smart Chain
`; + h += `
Consensus
PoSA
21 Validators
`; + h += '
'; + } else { + h += '
'; + for (const n of d.networks) { + h += `
${_esc(n.name)}Chain ${n.chainId} / ${_esc(n.symbol)}
`; + } + h += '
'; + } + return h; + } + afterRender(b) { bindTabs(b, this); } +} +customElements.define('network-status-panel', NetworkStatusPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 2. Block Explorer — Skills #6-#8 +// ═══════════════════════════════════════════════════════════════════ +class BlockExplorerPanel extends BasePanel { + static skill = 'Skill #6-#8'; + static defaultTitle = 'Block Explorer'; + constructor() { super(); this._refreshRate = 30000; this._searchResult = null; } + + async fetchData() { + const blkNum = await bnbApi.blockNumber(); + const num = blkNum?.result ? parseInt(blkNum.result, 16) : 0; + let block = null; + if (num > 0) { + const res = await bnbApi.block('0x' + num.toString(16)); + if (res?.result) { + const b = res.result; + block = { number: parseInt(b.number, 16), timestamp: new Date(parseInt(b.timestamp, 16) * 1000), txCount: b.transactions?.length || 0, gasUsed: parseInt(b.gasUsed, 16), gasLimit: parseInt(b.gasLimit, 16), miner: b.miner, hash: b.hash }; + } + } + return { blockNum: num, block, searchResult: this._searchResult }; + } + + renderContent(d) { + if (!d) return '
Loading...
'; + let h = css('cards', 'search', 'detail', 'examples'); + h += '
'; + h += `
Latest Block
${_fmt(d.blockNum)}
`; + h += `
TXs in Block
${d.block ? d.block.txCount : '—'}
`; + h += `
Gas Used
${d.block ? _fmt(d.block.gasUsed) : '—'}
`; + h += '
'; + h += ''; + // Example buttons + const exBlocks = d.blockNum > 0 ? [ + {label: 'Latest', val: String(d.blockNum)}, + {label: 'Latest -10', val: String(d.blockNum - 10)}, + {label: 'Latest -100', val: String(d.blockNum - 100)}, + {label: 'Block 1', val: '1'}, + ] : [{label: 'Block 1', val: '1'}]; + h += examplesHTML(exBlocks, 'blk-input'); + if (d.block && !d.searchResult) { + const b = d.block; + const pct = b.gasLimit > 0 ? (b.gasUsed / b.gasLimit * 100).toFixed(1) : 0; + h += '
'; + h += `
Block#${_fmt(b.number)}
`; + h += `
Time${b.timestamp.toLocaleTimeString()}
`; + h += `
Gas${_fmt(b.gasUsed)} (${pct}%)
`; + h += `
Validator${b.miner}
`; + h += `
Hash${b.hash}
`; + h += '
'; + } + if (d.searchResult) { + const b = d.searchResult; + h += '
'; + h += `
Block#${_fmt(b.number)}
`; + h += `
TXs${b.txCount}
`; + h += `
Time${b.timestamp.toLocaleTimeString()}
`; + h += `
Hash${b.hash}
`; + h += '
'; + } + return h; + } + + async _search(num) { + if (!num) return; + if (!this._inputVals) this._inputVals = {}; + this._inputVals['blk-input'] = num; + const hex = '0x' + parseInt(num).toString(16); + const res = await bnbApi.block(hex); + if (res?.result) { + const b = res.result; + this._searchResult = { number: parseInt(b.number, 16), txCount: b.transactions?.length || 0, timestamp: new Date(parseInt(b.timestamp, 16) * 1000), hash: b.hash }; + } + const body = this.querySelector('.panel-body'); + if (body) { body.innerHTML = this.renderContent(this._data); this.afterRender(body); } + } + + afterRender(b) { + const btn = b.querySelector('#blk-btn'), inp = b.querySelector('#blk-input'); + if (btn && inp) { + btn.addEventListener('click', () => this._search(inp.value.trim())); + inp.addEventListener('keydown', e => { if (e.key === 'Enter') this._search(inp.value.trim()); }); + } + bindExamples(b, this, v => this._search(v)); + restoreInputs(b, this); + } +} +customElements.define('block-explorer-panel', BlockExplorerPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 3. TX Explorer — Skills #9-#11 +// ═══════════════════════════════════════════════════════════════════ +class TxExplorerPanel extends BasePanel { + static skill = 'Skill #9-#11'; + static defaultTitle = 'TX Explorer'; + constructor() { super(); this._refreshRate = 0; this._result = null; this._autoLoaded = false; } + + async fetchData() { + // Auto-load a recent TX from the latest block on first load + if (!this._autoLoaded) { + this._autoLoaded = true; + try { + const blkNum = await bnbApi.blockNumber(); + if (blkNum?.result) { + const blk = await bnbApi.block(blkNum.result); + if (blk?.result?.transactions?.length > 0) { + const txHash = typeof blk.result.transactions[0] === 'string' ? blk.result.transactions[0] : blk.result.transactions[0].hash; + if (txHash) { + const [tx, receipt] = await Promise.allSettled([bnbApi.tx(txHash), bnbApi.receipt(txHash)]); + const txData = tx.status === 'fulfilled' && tx.value?.result ? tx.value.result : null; + if (txData) { + if (receipt.status === 'fulfilled' && receipt.value?.result) txData._receipt = receipt.value.result; + this._result = txData; + } + } + } + } + } catch {} + } + return { result: this._result }; + } + + renderContent(d) { + let h = css('search', 'detail', 'examples'); + h += ''; + // Show current TX hash as example if available + const txExamples = []; + if (d?.result?.hash) txExamples.push({label: 'Current TX', val: d.result.hash}); + if (txExamples.length > 0) h += examplesHTML(txExamples, 'tx-input'); + if (d?.result && !d.result.error) { + const r = d.result; + const val = r.value ? (parseInt(r.value, 16) / 1e18).toFixed(6) : '0'; + const gasP = r.gasPrice ? (parseInt(r.gasPrice, 16) / 1e9).toFixed(2) : '—'; + const status = r._receipt?.status === '0x1' ? 'Success' : r._receipt?.status === '0x0' ? 'Failed' : 'Pending'; + h += '
'; + h += `
Status${status}
`; + h += `
From${r.from || '—'}
`; + h += `
To${r.to || 'Contract Creation'}
`; + h += `
Value${val} BNB
`; + h += `
Gas Price${gasP} Gwei
`; + h += `
Block${r.blockNumber ? _fmt(parseInt(r.blockNumber, 16)) : 'Pending'}
`; + h += `
Hash${r.hash || '—'}
`; + h += '
'; + } else if (d?.result?.error) { + h += '
Transaction not found
'; + } else { + h += '
Loading latest transaction...
'; + } + return h; + } + + async _search(hash) { + if (!hash || hash.length !== 66) return; + if (!this._inputVals) this._inputVals = {}; + this._inputVals['tx-input'] = hash; + try { + const [tx, receipt] = await Promise.allSettled([bnbApi.tx(hash), bnbApi.receipt(hash)]); + const txData = tx.status === 'fulfilled' && tx.value?.result ? tx.value.result : null; + if (txData) { + if (receipt.status === 'fulfilled' && receipt.value?.result) txData._receipt = receipt.value.result; + this._result = txData; + } else { this._result = { error: true }; } + } catch { this._result = { error: true }; } + const body = this.querySelector('.panel-body'); + if (body) { body.innerHTML = this.renderContent(this._data); this.afterRender(body); } + } + + afterRender(b) { + const btn = b.querySelector('#tx-btn'), inp = b.querySelector('#tx-input'); + if (btn && inp) { + btn.addEventListener('click', () => this._search(inp.value.trim())); + inp.addEventListener('keydown', e => { if (e.key === 'Enter') this._search(inp.value.trim()); }); + } + bindExamples(b, this, v => this._search(v)); + restoreInputs(b, this); + } +} +customElements.define('tx-explorer-panel', TxExplorerPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 4. Gas Estimator — Skills #12-#13 +// ═══════════════════════════════════════════════════════════════════ +class GasEstimatorPanel extends BasePanel { + static skill = 'Skill #12-#13'; + static defaultTitle = 'Gas Estimator'; + constructor() { super(); this._refreshRate = 10000; this._estResult = null; this._autoEstimated = false; } + + async fetchData() { + const gas = await bnbApi.gasPrice(); + const price = gas?.result ? parseInt(gas.result, 16) / 1e9 : 0; + // Auto-estimate for WBNB on first load + if (!this._autoEstimated && price > 0) { + this._autoEstimated = true; + try { this._estResult = await bnbApi.estimateGas(EX.WBNB, '0x', '0x0'); } catch {} + } + return { gasPrice: price, estResult: this._estResult }; + } + + renderContent(d) { + if (!d) return '
Loading...
'; + let h = css('cards', 'search', 'detail', 'examples'); + h += '
'; + h += `
Current Gas Price
${d.gasPrice.toFixed(2)}
Gwei
`; + const txCost = (d.gasPrice * 21000 / 1e9).toFixed(6); + h += `
Simple Transfer
${txCost}
BNB (21,000 gas)
`; + h += '
'; + h += ''; + h += examplesHTML([ + {label: 'WBNB (BNB)', val: EX.WBNB}, + {label: 'CZ Wallet', val: EX.CZ_WALLET}, + {label: 'USDT', val: EX.USDT}, + {label: 'PancakeRouter', val: EX.PANCAKE_ROUTER}, + ], 'gas-to'); + if (d.estResult) { + const est = d.estResult; + if (est.result) { + const gasUnits = parseInt(est.result, 16); + const cost = (d.gasPrice * gasUnits / 1e9).toFixed(6); + h += '
'; + h += `
Gas Units${_fmt(gasUnits)}
`; + h += `
Estimated Cost${cost} BNB
`; + h += '
'; + } else { + h += '
Could not estimate gas
'; + } + } + return h; + } + + async _estimate(to) { + if (!to || to.length !== 42) return; + if (!this._inputVals) this._inputVals = {}; + this._inputVals['gas-to'] = to; + try { this._estResult = await bnbApi.estimateGas(to, '0x', '0x0'); } catch { this._estResult = null; } + const body = this.querySelector('.panel-body'); + if (body) { body.innerHTML = this.renderContent(this._data); this.afterRender(body); } + } + + afterRender(b) { + const btn = b.querySelector('#gas-btn'), inp = b.querySelector('#gas-to'); + if (btn && inp) { + btn.addEventListener('click', () => this._estimate(inp.value.trim())); + inp.addEventListener('keydown', e => { if (e.key === 'Enter') this._estimate(inp.value.trim()); }); + } + bindExamples(b, this, v => this._estimate(v)); + restoreInputs(b, this); + } +} +customElements.define('gas-estimator-panel', GasEstimatorPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 5. Address Profiler — Skills #14-#16 +// ═══════════════════════════════════════════════════════════════════ +class AddressProfilerPanel extends BasePanel { + static skill = 'Skill #14-#16'; + static defaultTitle = 'Address Profiler'; + constructor() { super(); this._refreshRate = 0; this._profile = null; this._autoLoaded = false; } + + async fetchData() { + // Auto-load CZ wallet on first load + if (!this._autoLoaded) { + this._autoLoaded = true; + try { this._profile = await bnbApi.addressProfile(EX.CZ_WALLET); } catch {} + } + return { profile: this._profile }; + } + + renderContent(d) { + let h = css('search', 'cards', 'detail', 'info', 'examples'); + h += ''; + h += examplesHTML([ + {label: 'CZ Wallet', val: EX.CZ_WALLET}, + {label: 'Binance Hot', val: EX.BINANCE_HOT}, + {label: 'WBNB Contract', val: EX.WBNB}, + {label: 'Burn Address', val: EX.DEAD}, + ], 'ap-input'); + if (d?.profile) { + const p = d.profile; + const bal = p.balance?.result ? (parseInt(p.balance.result, 16) / 1e18).toFixed(6) : '0'; + const txCount = p.transactionCount?.result ? parseInt(p.transactionCount.result, 16) : 0; + h += '
'; + h += `
BNB Balance
${bal}
`; + h += `
TX Count
${_fmt(txCount)}
`; + h += '
'; + h += '
'; + h += `
Address${_esc(p.address)}
`; + h += `
Type${p.isContract ? 'Contract' : 'EOA'}
`; + if (p.isContract && p.codeSize > 0) h += `
Code Size${_fmt(p.codeSize)} bytes
`; + h += '
'; + } + return h; + } + + async _search(addr) { + if (!addr || addr.length !== 42) return; + if (!this._inputVals) this._inputVals = {}; + this._inputVals['ap-input'] = addr; + try { this._profile = await bnbApi.addressProfile(addr); } catch { this._profile = null; } + const body = this.querySelector('.panel-body'); + if (body) { body.innerHTML = this.renderContent(this._data); this.afterRender(body); } + } + + afterRender(b) { + const btn = b.querySelector('#ap-btn'), inp = b.querySelector('#ap-input'); + if (btn && inp) { + btn.addEventListener('click', () => this._search(inp.value.trim())); + inp.addEventListener('keydown', e => { if (e.key === 'Enter') this._search(inp.value.trim()); }); + } + bindExamples(b, this, v => this._search(v)); + restoreInputs(b, this); + } +} +customElements.define('address-profiler-panel', AddressProfilerPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 6. Token Inspector — Skills #17-#18 +// ═══════════════════════════════════════════════════════════════════ +class TokenInspectorPanel extends BasePanel { + static skill = 'Skill #17-#18'; + static defaultTitle = 'Token Inspector'; + constructor() { super(); this._refreshRate = 0; this._tab = 'info'; this._tokenData = null; this._balData = null; this._autoLoaded = false; } + + async fetchData() { + // Auto-load WBNB (BNB) token info + CZ balance on first load + if (!this._autoLoaded) { + this._autoLoaded = true; + try { + this._tokenData = await bnbApi.tokenInfo(EX.WBNB); + const balRes = await bnbApi.tokenBalance(EX.WBNB, EX.CZ_WALLET); + if (balRes?.result) this._balData = BigInt(balRes.result).toString(); + this._autoContract = EX.WBNB; + this._autoAddr = EX.CZ_WALLET; + } catch {} + } + return { tokenData: this._tokenData, balData: this._balData, autoContract: this._autoContract, autoAddr: this._autoAddr }; + } + + renderContent(d) { + let h = css('tabs', 'search', 'cards', 'detail', 'examples'); + h += tabsHTML([{id:'info',label:'Token Info'},{id:'balance',label:'Balance'}], this._tab); + if (this._tab === 'info') { + h += ''; + h += examplesHTML([ + {label: 'WBNB (BNB)', val: EX.WBNB}, + {label: 'USDT', val: EX.USDT}, + {label: 'BUSD', val: EX.BUSD}, + {label: 'CAKE', val: EX.CAKE}, + ], 'ti-addr'); + if (d?.tokenData) { + const t = d.tokenData; + const name = decodeString(t.name?.result); + const symbol = decodeString(t.symbol?.result); + const decimals = t.decimals?.result ? parseInt(t.decimals.result, 16) : '?'; + h += '
'; + h += `
Name
${_esc(name || 'Unknown')}
`; + h += `
Symbol
${_esc(symbol || '?')}
`; + h += '
'; + h += '
'; + h += `
Decimals${decimals}
`; + h += `
Contract${_esc(t.contract)}
`; + h += '
'; + } + } else { + h += ''; + h += ''; + h += examplesHTML([ + {label: 'WBNB (BNB)', val: EX.WBNB}, + {label: 'USDT', val: EX.USDT}, + ], 'tb-contract'); + if (d?.balData) { + h += '
'; + h += `
Balance (raw)${d.balData}
`; + if (d.autoContract) h += `
Token${_esc(d.autoContract)}
`; + if (d.autoAddr) h += `
Wallet${_esc(d.autoAddr)}
`; + h += '
'; + } + } + return h; + } + + async _lookupToken(addr) { + if (!addr || addr.length !== 42) return; + if (!this._inputVals) this._inputVals = {}; + this._inputVals['ti-addr'] = addr; + try { this._tokenData = await bnbApi.tokenInfo(addr); } catch {} + const body = this.querySelector('.panel-body'); + if (body) { body.innerHTML = this.renderContent(this._data); this.afterRender(body); } + } + + async _checkBalance(contract, addr) { + if (!contract || !addr) return; + if (!this._inputVals) this._inputVals = {}; + this._inputVals['tb-contract'] = contract; + this._inputVals['tb-addr'] = addr; + try { + const res = await bnbApi.tokenBalance(contract, addr); + this._balData = res?.result ? BigInt(res.result).toString() : '0'; + } catch { this._balData = 'Error'; } + const body = this.querySelector('.panel-body'); + if (body) { body.innerHTML = this.renderContent(this._data); this.afterRender(body); } + } + + afterRender(b) { + bindTabs(b, this); + const tiBtn = b.querySelector('#ti-btn'), tiInp = b.querySelector('#ti-addr'); + if (tiBtn && tiInp) { tiBtn.addEventListener('click', () => this._lookupToken(tiInp.value.trim())); tiInp.addEventListener('keydown', e => { if (e.key === 'Enter') this._lookupToken(tiInp.value.trim()); }); } + const tbBtn = b.querySelector('#tb-btn'), tbC = b.querySelector('#tb-contract'), tbA = b.querySelector('#tb-addr'); + if (tbBtn && tbC && tbA) { tbBtn.addEventListener('click', () => this._checkBalance(tbC.value.trim(), tbA.value.trim())); } + bindExamples(b, this, v => { + if (this._tab === 'info') this._lookupToken(v); + else if (this._tab === 'balance') { + if (!this._inputVals) this._inputVals = {}; + this._inputVals['tb-contract'] = v; + const c = b.querySelector('#tb-contract'); + if (c) c.value = v; + } + }); + restoreInputs(b, this); + } +} +customElements.define('token-inspector-panel', TokenInspectorPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 7. Contract Reader — Skills #19-#20 +// ═══════════════════════════════════════════════════════════════════ +class ContractReaderPanel extends BasePanel { + static skill = 'Skill #19-#20'; + static defaultTitle = 'Contract Reader'; + constructor() { super(); this._refreshRate = 0; this._result = null; this._codeResult = null; this._autoLoaded = false; } + + async fetchData() { + // Auto-check WBNB contract + call name() on first load + if (!this._autoLoaded) { + this._autoLoaded = true; + try { + const res = await bnbApi.code(EX.WBNB); + this._codeResult = res?.result && res.result !== '0x'; + this._checkedAddr = EX.WBNB; + // Call name() = 0x06fdde03 + this._result = await bnbApi.readContract(EX.WBNB, '0x06fdde03'); + } catch { this._codeResult = false; } + } + return { result: this._result, codeResult: this._codeResult, checkedAddr: this._checkedAddr }; + } + + renderContent(d) { + let h = css('search', 'detail', 'examples'); + h += ''; + h += examplesHTML([ + {label: 'WBNB (BNB)', val: EX.WBNB}, + {label: 'PancakeSwap', val: EX.PANCAKE_ROUTER}, + {label: 'USDT', val: EX.USDT}, + {label: 'CZ Wallet (EOA)', val: EX.CZ_WALLET}, + ], 'cr-addr'); + if (d?.codeResult !== null && d?.codeResult !== undefined) { + const isC = d.codeResult; + h += `
Type${isC ? 'Smart Contract' : 'EOA (Wallet)'}
`; + if (d.checkedAddr) h += `
Address${_esc(d.checkedAddr)}
`; + h += '
'; + } + h += '
'; + h += ''; + if (d?.result) { + h += '
'; + h += `
Result${_esc(d.result.result || 'Error')}
`; + h += '
'; + } else { + h += '
Call any view/pure function using ABI-encoded data
'; + } + return h; + } + + async _checkCode(addr) { + if (!addr || addr.length !== 42) return; + if (!this._inputVals) this._inputVals = {}; + this._inputVals['cr-addr'] = addr; + try { + const res = await bnbApi.code(addr); + this._codeResult = res?.result && res.result !== '0x'; + this._checkedAddr = addr; + } catch { this._codeResult = false; this._checkedAddr = addr; } + const body = this.querySelector('.panel-body'); + if (body) { body.innerHTML = this.renderContent(this._data); this.afterRender(body); } + } + + async _call(addr, data) { + if (!addr || !data) return; + if (!this._inputVals) this._inputVals = {}; + this._inputVals['cr-addr'] = addr; + this._inputVals['cr-data'] = data; + try { this._result = await bnbApi.readContract(addr, data); } catch { this._result = { result: 'Error' }; } + const body = this.querySelector('.panel-body'); + if (body) { body.innerHTML = this.renderContent(this._data); this.afterRender(body); } + } + + afterRender(b) { + const codeBtn = b.querySelector('#cr-code-btn'), codeInp = b.querySelector('#cr-addr'); + if (codeBtn && codeInp) { codeBtn.addEventListener('click', () => this._checkCode(codeInp.value.trim())); codeInp.addEventListener('keydown', e => { if (e.key === 'Enter') this._checkCode(codeInp.value.trim()); }); } + const callBtn = b.querySelector('#cr-call-btn'), dataInp = b.querySelector('#cr-data'); + if (callBtn && dataInp && codeInp) { callBtn.addEventListener('click', () => this._call(codeInp.value.trim(), dataInp.value.trim())); } + bindExamples(b, this, v => this._checkCode(v)); + restoreInputs(b, this); + } +} +customElements.define('contract-reader-panel', ContractReaderPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 8. NFT Explorer — Skills #21-#24 +// ═══════════════════════════════════════════════════════════════════ +class NftExplorerPanel extends BasePanel { + static skill = 'Skill #21-#24'; + static defaultTitle = 'NFT Explorer'; + constructor() { super(); this._refreshRate = 0; this._balResult = null; this._autoLoaded = false; } + + async fetchData() { + // Auto-check PancakeSwap NFT for CZ wallet + if (!this._autoLoaded) { + this._autoLoaded = true; + try { + const [balRes, tokRes] = await Promise.allSettled([bnbApi.nftBalance(EX.CZ_WALLET, EX.NFT_PANCAKE), bnbApi.nftTokens(EX.CZ_WALLET, EX.NFT_PANCAKE, 5)]); + const balance = balRes.status === 'fulfilled' && balRes.value?.result ? parseInt(balRes.value.result, 16) : 0; + const tokens = tokRes.status === 'fulfilled' && tokRes.value?.tokens ? tokRes.value.tokens.map(t => parseInt(t.tokenId, 16)) : []; + this._balResult = { balance, tokens, contract: EX.NFT_PANCAKE, owner: EX.CZ_WALLET }; + } catch {} + } + return { balResult: this._balResult }; + } + + renderContent(d) { + let h = css('search', 'cards', 'detail', 'examples'); + h += ''; + h += ''; + h += examplesHTML([ + {label: 'PancakeSquad', val: EX.NFT_PANCAKE}, + ], 'nft-contract'); + if (d?.balResult) { + const r = d.balResult; + h += '
'; + h += `
NFT Balance
${r.balance}
`; + h += `
Token IDs
${r.tokens.length > 0 ? r.tokens.slice(0, 5).join(', ') : 'None'}
`; + h += '
'; + h += '
'; + h += `
Contract${_esc(r.contract)}
`; + h += `
Owner${_esc(r.owner)}
`; + h += '
'; + } + return h; + } + + async _check(contract, owner) { + if (!contract || !owner || contract.length !== 42 || owner.length !== 42) return; + if (!this._inputVals) this._inputVals = {}; + this._inputVals['nft-contract'] = contract; + this._inputVals['nft-owner'] = owner; + try { + const [balRes, tokRes] = await Promise.allSettled([bnbApi.nftBalance(owner, contract), bnbApi.nftTokens(owner, contract, 5)]); + const balance = balRes.status === 'fulfilled' && balRes.value?.result ? parseInt(balRes.value.result, 16) : 0; + const tokens = tokRes.status === 'fulfilled' && tokRes.value?.tokens ? tokRes.value.tokens.map(t => parseInt(t.tokenId, 16)) : []; + this._balResult = { balance, tokens, contract, owner }; + } catch { this._balResult = { balance: 0, tokens: [], contract, owner }; } + const body = this.querySelector('.panel-body'); + if (body) { body.innerHTML = this.renderContent(this._data); this.afterRender(body); } + } + + afterRender(b) { + const btn = b.querySelector('#nft-btn'), c = b.querySelector('#nft-contract'), o = b.querySelector('#nft-owner'); + if (btn && c && o) { btn.addEventListener('click', () => this._check(c.value.trim(), o.value.trim())); } + bindExamples(b, this, v => { + if (!this._inputVals) this._inputVals = {}; + this._inputVals['nft-contract'] = v; + const c2 = b.querySelector('#nft-contract'); + if (c2) c2.value = v; + }); + restoreInputs(b, this); + } +} +customElements.define('nft-explorer-panel', NftExplorerPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 9. Transfer Operations — Skills #25-#28 +// ═══════════════════════════════════════════════════════════════════ +class TransferOpsPanel extends BasePanel { + static skill = 'Skill #25-#28'; + static defaultTitle = 'Transfer Operations'; + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return {}; } + + renderContent() { + let h = css('info'); + const ops = [ + { name: 'transfer_native_token', desc: 'Send BNB to another address', params: 'to, amount, network' }, + { name: 'transfer_erc20', desc: 'Transfer ERC20 tokens', params: 'contractAddress, to, amount, network' }, + { name: 'transfer_nft', desc: 'Send an ERC721 NFT', params: 'contractAddress, to, tokenId, network' }, + { name: 'transfer_erc1155', desc: 'Transfer ERC1155 multi-tokens', params: 'contractAddress, to, tokenId, amount, network' }, + ]; + h += '
'; + h += '
State-changing operations — require PRIVATE_KEY in MCP server environment
'; + for (const o of ops) { + h += '
'; + h += `
${o.name} Write
`; + h += `
${o.desc}
`; + h += `
${o.params.split(', ').map(p => `${p}`).join(' ')}
`; + h += '
'; + } + h += '
'; + return h; + } +} +customElements.define('transfer-ops-panel', TransferOpsPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 10. Contract Writing — Skills #29-#30 +// ═══════════════════════════════════════════════════════════════════ +class ContractWritingPanel extends BasePanel { + static skill = 'Skill #29-#30'; + static defaultTitle = 'Contract Writing'; + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return {}; } + + renderContent() { + let h = css('info'); + const ops = [ + { name: 'write_contract', desc: 'Execute any state-changing smart contract function', params: 'contractAddress, functionName, args, abi, value, network' }, + { name: 'approve_token_spending', desc: 'Grant a spender contract allowance for your tokens', params: 'contractAddress, spender, amount, network' }, + ]; + h += '
'; + h += '
State-changing operations — require explicit network confirmation
'; + for (const o of ops) { + h += '
'; + h += `
${o.name} Write
`; + h += `
${o.desc}
`; + h += `
${o.params.split(', ').map(p => `${p}`).join(' ')}
`; + h += '
'; + } + h += '
'; + return h; + } +} +customElements.define('contract-writing-panel', ContractWritingPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 11. ERC-8004 Agents — Skills #31-#34 +// ═══════════════════════════════════════════════════════════════════ +class Erc8004Panel extends BasePanel { + static skill = 'Skill #31-#34'; + static defaultTitle = 'ERC-8004 Agents'; + constructor() { super(); this._refreshRate = 0; this._tab = 'lookup'; this._agentResult = null; this._balResult = null; this._autoLoaded = false; } + + async fetchData() { + // Auto-lookup agent #1 on first load + if (!this._autoLoaded) { + this._autoLoaded = true; + try { this._agentResult = await bnbApi.erc8004Agent('1'); } catch { this._agentResult = { tokenId: '1' }; } + } + return { agentResult: this._agentResult, balResult: this._balResult }; + } + + renderContent(d) { + let h = css('tabs', 'search', 'detail', 'info', 'examples'); + h += tabsHTML([{id:'lookup',label:'Lookup'},{id:'owner',label:'By Owner'},{id:'about',label:'About'}], this._tab); + if (this._tab === 'lookup') { + h += ''; + h += examplesHTML([ + {label: 'Agent #1', val: '1'}, + {label: 'Agent #2', val: '2'}, + {label: 'Agent #5', val: '5'}, + {label: 'Agent #10', val: '10'}, + ], 'ag-id'); + if (d?.agentResult) { + const r = d.agentResult; + h += '
'; + h += `
Token ID${_esc(r.tokenId)}
`; + const hasData = r.tokenURI?.result && r.tokenURI.result !== '0x'; + h += `
Status${hasData ? 'Registered' : 'Not Found'}
`; + if (hasData) h += `
URI Data${_esc(r.tokenURI.result.slice(0, 80))}...
`; + h += '
'; + } + } else if (this._tab === 'owner') { + h += ''; + h += examplesHTML([ + {label: 'CZ Wallet', val: EX.CZ_WALLET}, + {label: 'Binance Hot', val: EX.BINANCE_HOT}, + ], 'ag-owner'); + if (d?.balResult !== null && d?.balResult !== undefined) { + h += `
Registered Agents${d.balResult}
`; + } + } else { + h += '
'; + h += '
ERC-8004 Identity Registry
'; + const feats = [ + { t: 'On-Chain Agent Identity', d: 'NFT-based identity tokens for AI agents on BSC, Ethereum, Base, Polygon' }, + { t: 'Agent Metadata Profile', d: 'Name, description, image, service endpoints via agentURI format' }, + { t: 'Payment Wallet (x402)', d: 'Verified payment wallets for autonomous agent-to-agent payments' }, + { t: 'register_erc8004_agent', d: 'Register your MCP server on-chain. Requires PRIVATE_KEY.' }, + { t: 'set_erc8004_agent_uri', d: 'Update metadata URI. Owner only. Requires PRIVATE_KEY.' }, + ]; + for (const f of feats) { + h += `
${f.t}
${f.d}
`; + } + h += '
'; + } + return h; + } + + async _lookup(id) { + if (!id) return; + if (!this._inputVals) this._inputVals = {}; + this._inputVals['ag-id'] = id; + try { this._agentResult = await bnbApi.erc8004Agent(id); } catch { this._agentResult = { tokenId: id }; } + const body = this.querySelector('.panel-body'); + if (body) { body.innerHTML = this.renderContent(this._data); this.afterRender(body); } + } + + async _checkOwner(addr) { + if (!addr || addr.length !== 42) return; + if (!this._inputVals) this._inputVals = {}; + this._inputVals['ag-owner'] = addr; + try { + const res = await bnbApi.erc8004Balance(addr); + this._balResult = res?.result ? parseInt(res.result, 16) : 0; + } catch { this._balResult = 0; } + const body = this.querySelector('.panel-body'); + if (body) { body.innerHTML = this.renderContent(this._data); this.afterRender(body); } + } + + afterRender(b) { + bindTabs(b, this); + const btn = b.querySelector('#ag-btn'), inp = b.querySelector('#ag-id'); + if (btn && inp) { btn.addEventListener('click', () => this._lookup(inp.value.trim())); inp.addEventListener('keydown', e => { if (e.key === 'Enter') this._lookup(inp.value.trim()); }); } + const obtn = b.querySelector('#ag-own-btn'), oinp = b.querySelector('#ag-owner'); + if (obtn && oinp) { obtn.addEventListener('click', () => this._checkOwner(oinp.value.trim())); oinp.addEventListener('keydown', e => { if (e.key === 'Enter') this._checkOwner(oinp.value.trim()); }); } + bindExamples(b, this, v => { + if (this._tab === 'lookup') this._lookup(v); + else if (this._tab === 'owner') this._checkOwner(v); + }); + restoreInputs(b, this); + } +} +customElements.define('erc8004-panel', Erc8004Panel); + +// ═══════════════════════════════════════════════════════════════════ +// 12. Greenfield Overview — Skill #35 +// ═══════════════════════════════════════════════════════════════════ +class GreenfieldOverviewPanel extends BasePanel { + static skill = 'Skill #35'; + static defaultTitle = 'Greenfield Overview'; + constructor() { super(); this._refreshRate = 60000; } + + async fetchData() { + let status = null; + try { const res = await bnbApi.greenfieldStatus(); if (res && !res.error) status = res; } catch {} + return { status }; + } + + renderContent(d) { + if (!d) return '
Loading...
'; + let h = css('cards', 'info'); + h += '
'; + h += `
Network
BNB Greenfield
Decentralized Storage
`; + if (d.status) { + const ver = d.status.node_info?.version || d.status.version || 'Active'; + h += `
Status
Online
v${_esc(String(ver))}
`; + } else { + h += `
Status
Checking
`; + } + h += '
'; + h += '
'; + h += '
Decentralized data storage for the BNB ecosystem. Reed-Solomon erasure coding, Tendermint consensus, cross-chain programmability with BSC.
'; + h += '
'; + h += '
Encoding
Reed-Solomon EC
'; + h += '
Consensus
Tendermint PoS
'; + h += '
'; + return h; + } +} +customElements.define('greenfield-overview-panel', GreenfieldOverviewPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 13. Greenfield Buckets — Skills #36-#40 +// ═══════════════════════════════════════════════════════════════════ +class GreenfieldBucketsPanel extends BasePanel { + static skill = 'Skill #36-#40'; + static defaultTitle = 'Greenfield Buckets'; + constructor() { super(); this._refreshRate = 0; this._buckets = null; this._autoLoaded = false; } + + async fetchData() { + if (!this._autoLoaded) { + this._autoLoaded = true; + try { + const res = await bnbApi.greenfieldBuckets(EX.CZ_WALLET); + if (res && !res.error) { this._buckets = Array.isArray(res) ? res : (res.buckets || res.GfSpGetUserBucketsResponse?.Buckets || []); } + else { this._buckets = { error: true }; } + } catch { this._buckets = { error: true }; } + } + return { buckets: this._buckets }; + } + + renderContent(d) { + let h = css('search', 'detail', 'info', 'examples'); + h += ''; + h += examplesHTML([ + {label: 'CZ Wallet', val: EX.CZ_WALLET}, + {label: 'Binance Hot', val: EX.BINANCE_HOT}, + ], 'gf-addr'); + if (d?.buckets) { + if (d.buckets.error) { + h += '
No buckets found or SP not reachable
'; + } else if (Array.isArray(d.buckets) && d.buckets.length > 0) { + h += '
'; + h += `
${d.buckets.length} Bucket(s)
`; + for (const b of d.buckets) { + const name = b.bucket_info?.bucket_name || b.BucketName || b.name || 'Unknown'; + h += `
${_esc(String(name))}
`; + } + h += '
'; + } else { + h += '
No buckets found for this address
'; + } + } else { + h += '
Enter a Greenfield address to list storage buckets
Write operations (gnfd_create_bucket, gnfd_delete_bucket) require PRIVATE_KEY in MCP server
'; + } + return h; + } + + async _browse(addr) { + if (!addr || addr.length !== 42) return; + if (!this._inputVals) this._inputVals = {}; + this._inputVals['gf-addr'] = addr; + try { + const res = await bnbApi.greenfieldBuckets(addr); + if (res && !res.error) { this._buckets = Array.isArray(res) ? res : (res.buckets || res.GfSpGetUserBucketsResponse?.Buckets || []); } + else { this._buckets = { error: true }; } + } catch { this._buckets = { error: true }; } + const body = this.querySelector('.panel-body'); + if (body) { body.innerHTML = this.renderContent(this._data); this.afterRender(body); } + } + + afterRender(b) { + const btn = b.querySelector('#gf-btn'), inp = b.querySelector('#gf-addr'); + if (btn && inp) { + btn.addEventListener('click', () => this._browse(inp.value.trim())); + inp.addEventListener('keydown', e => { if (e.key === 'Enter') this._browse(inp.value.trim()); }); + } + bindExamples(b, this, v => this._browse(v)); + restoreInputs(b, this); + } +} +customElements.define('greenfield-buckets-panel', GreenfieldBucketsPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 14. Greenfield Objects — Skills #41-#47 +// ═══════════════════════════════════════════════════════════════════ +class GreenfieldObjectsPanel extends BasePanel { + static skill = 'Skill #41-#47'; + static defaultTitle = 'Greenfield Objects'; + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return {}; } + + renderContent() { + let h = css('info'); + const ops = [ + { name: 'gnfd_list_objects', desc: 'List all objects in a bucket', type: 'Read' }, + { name: 'gnfd_get_object_info', desc: 'Get object metadata and details', type: 'Read' }, + { name: 'gnfd_upload_object', desc: 'Upload a file to a bucket', type: 'Write' }, + { name: 'gnfd_create_file', desc: 'Create and upload a file (alternate)', type: 'Write' }, + { name: 'gnfd_download_object', desc: 'Download object to local disk', type: 'Read' }, + { name: 'gnfd_delete_object', desc: 'Remove an object from bucket', type: 'Write' }, + { name: 'gnfd_create_folder', desc: 'Create a folder in a bucket', type: 'Write' }, + ]; + h += '
'; + for (const o of ops) { + h += '
'; + h += `
${o.name} ${o.type}
`; + h += `
${o.desc}
`; + h += '
'; + } + h += '
'; + return h; + } +} +customElements.define('greenfield-objects-panel', GreenfieldObjectsPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 15. Greenfield Payments — Skills #48-#54 +// ═══════════════════════════════════════════════════════════════════ +class GreenfieldPaymentsPanel extends BasePanel { + static skill = 'Skill #48-#54'; + static defaultTitle = 'Greenfield Payments'; + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return {}; } + + renderContent() { + let h = css('info'); + const ops = [ + { name: 'gnfd_get_payment_accounts', desc: 'List payment accounts for an address', type: 'Read' }, + { name: 'gnfd_get_payment_account_info', desc: 'Payment account details', type: 'Read' }, + { name: 'gnfd_get_payment_balance', desc: 'Check payment account balance', type: 'Read' }, + { name: 'gnfd_create_payment', desc: 'Create new payment account', type: 'Write' }, + { name: 'gnfd_deposit_to_payment', desc: 'Deposit BNB for storage fees', type: 'Write' }, + { name: 'gnfd_withdraw_from_payment', desc: 'Withdraw BNB from payment', type: 'Write' }, + { name: 'gnfd_disable_refund', desc: 'Permanently disable refunds (irreversible)', type: 'Write' }, + ]; + h += '
'; + h += '
Greenfield storage billing — payment accounts fund bucket storage costs
'; + for (const o of ops) { + h += '
'; + h += `
${o.name} ${o.type}
`; + h += `
${o.desc}
`; + h += '
'; + } + h += '
'; + return h; + } +} +customElements.define('greenfield-payments-panel', GreenfieldPaymentsPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 16. MCP Prompts — Skills #55-#62 +// ═══════════════════════════════════════════════════════════════════ +class McpPromptsPanel extends BasePanel { + static skill = 'Skill #55-#62'; + static defaultTitle = 'MCP Prompts'; + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return {}; } + + renderContent() { + let h = css('info'); + const prompts = [ + { name: 'analyze_block', desc: 'Detailed block analysis — transactions, gas, validator' }, + { name: 'analyze_transaction', desc: 'Transaction breakdown — status, value, gas, events' }, + { name: 'analyze_address', desc: 'Address investigation — balance, activity, type' }, + { name: 'interact_with_contract', desc: 'Smart contract interaction guidance with ABI' }, + { name: 'explain_evm_concept', desc: 'EVM concepts — gas, opcodes, ABI encoding' }, + { name: 'compare_networks', desc: 'Compare BSC, opBNB, Ethereum, other chains' }, + { name: 'analyze_token', desc: 'ERC20 or NFT analysis — supply, metadata, standards' }, + { name: 'how_to_register_mcp_as_erc8004_agent', desc: 'Guide to register MCP as ERC-8004 agent' }, + ]; + h += '
'; + h += '
Guided analysis prompts — contextual AI workflows for BNB Chain
'; + for (const p of prompts) { + h += '
'; + h += `
${p.name}
`; + h += `
${p.desc}
`; + h += '
'; + } + h += '
'; + return h; + } +} +customElements.define('mcp-prompts-panel', McpPromptsPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 17. Market Pack (Community PR #2) — Skills #63-#80 +// ═══════════════════════════════════════════════════════════════════ + +function _fmtPrice(p) { + if (!p) return '—'; + const n = parseFloat(p); + if (n >= 1000) return '$' + n.toLocaleString('en-US', {maximumFractionDigits:2}); + if (n >= 1) return '$' + n.toFixed(2); + if (n >= 0.01) return '$' + n.toFixed(4); + return '$' + n.toPrecision(3); +} + +function _fmtVol(v) { + if (!v) return '—'; + if (v >= 1e9) return '$' + (v/1e9).toFixed(1) + 'B'; + if (v >= 1e6) return '$' + (v/1e6).toFixed(1) + 'M'; + if (v >= 1e3) return '$' + (v/1e3).toFixed(1) + 'K'; + return '$' + v.toFixed(0); +} + +function _fmtChg(v) { + if (v === null || v === undefined) return ''; + const n = parseFloat(v); + const cls = n >= 0 ? 't-green' : 't-red'; + const sign = n >= 0 ? '+' : ''; + return `${sign}${n.toFixed(2)}%`; +} + +class MarketPackPanel extends BasePanel { + static skill = 'Skill #63-#80'; + static defaultTitle = 'Market Pack'; + constructor() { super(); this._refreshRate = 60000; this._tab = 'market'; this._searchResult = null; this._fmSearchResult = null; this._fmLoaded = false; } + + async fetchData() { + const data = await bnbApi.marketTopTokens(); + // Auto-load meme tokens for Four.meme tab + if (!this._fmLoaded) { + this._fmLoaded = true; + try { + const res = await bnbApi.marketSearch('meme bsc'); + const bscPairs = (res?.pairs || []).filter(p => p.chainId === 'bsc'); + if (bscPairs.length > 0) this._fmSearchResult = bscPairs; + } catch {} + } + return { pairs: data?.pairs || [] }; + } + + renderContent(d) { + let h = css('tabs', 'search', 'cards', 'table', 'detail', 'info', 'examples'); + h += tabsHTML([{id:'market',label:'Live Market'},{id:'fourmeme',label:'Four.meme'},{id:'search',label:'Search'}], this._tab); + + if (this._tab === 'market') { + // Deduplicate pairs by base token symbol, keep highest volume + const seen = new Map(); + for (const p of (d?.pairs || [])) { + const sym = p.baseToken?.symbol; + if (!sym) continue; + if (!seen.has(sym) || (p.volume?.h24 || 0) > (seen.get(sym).volume?.h24 || 0)) seen.set(sym, p); + } + const top = [...seen.values()].sort((a,b) => (b.volume?.h24 || 0) - (a.volume?.h24 || 0)).slice(0, 12); + + if (top.length > 0) { + h += '
BSC DEX — Live prices from DexScreener (auto-refresh 60s)
'; + h += ''; + for (const p of top) { + const name = p.baseToken?.name || ''; + const sym = p.baseToken?.symbol || '?'; + const dex = p.dexId || '?'; + const chg = p.priceChange?.h24; + const addr = p.baseToken?.address || ''; + const logo = p.info?.imageUrl || (addr ? tokenLogo(addr) : ''); + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + } + h += '
TokenDEXPrice24hVolumeLiquidity
`; + if (logo) h += ``; + h += `${_esc(sym)} ${_esc(name.length > 12 ? name.slice(0,12)+'...' : name)}${_esc(dex)}${_fmtPrice(p.priceUsd)}${_fmtChg(chg)}${_fmtVol(p.volume?.h24)}${_fmtVol(p.liquidity?.usd)}
'; + } else { + h += '
Loading BSC market data...
'; + } + + h += '
'; + h += '
'; + h += '
Market Skills (Community PR #2 by brief-onchain)
'; + const skills = [ + 'price_snapshot', 'top_movers', 'kline_brief', 'symbol_status', + 'funding_watch', 'open_interest_scan', 'liquidation_heatmap', + 'crowding_risk_scan', 'funding_basis_carry_scan', 'bsc_honeypot_check', 'bsc_rpc_fanout_check', + ]; + h += '
'; + for (const s of skills) h += `${s}`; + h += '
'; + + } else if (this._tab === 'fourmeme') { + // Show BSC meme tokens from the market data + const memeKeywords = ['PEPE','DOGE','SHIB','FLOKI','BONK','MEME','BOME','WIF','BRETT','NEIRO','BABYDOGE','TURBO','MOG','WOJAK']; + const memePairs = (d?.pairs || []).filter(p => { + const sym = (p.baseToken?.symbol || '').toUpperCase(); + return memeKeywords.some(k => sym.includes(k)); + }); + // Also search for Four.meme tokens if we have search results + const fmResult = this._fmSearchResult; + + h += '
Four.meme — BSC meme token launch platform. Agentic workflows for token creation, trading, and graduation.
'; + + // Show Four.meme skill cards + const fmSkills = [ + { name: 'Trade Playbook', desc: 'Quote-first buy/sell with slippage limits', badge: 'Agentic' }, + { name: 'Create Pipeline', desc: 'Two-step token creation with fee checks', badge: 'Agentic' }, + { name: 'Graduation Radar', desc: 'Bonding curve to Pancake migration', badge: 'Agentic' }, + { name: 'Mempool Sentinel', desc: 'Pending TX watchlists with events', badge: 'Agentic' }, + { name: 'One-Stop BSC', desc: 'Launch + buy + tweet + ERC-8004', badge: 'Agentic' }, + { name: 'Tax Token Guard', desc: 'Anti-sniping and fee-split checks', badge: 'Agentic' }, + { name: 'Agentic Ops', desc: 'Discover, score, confirm, execute', badge: 'Agentic' }, + ]; + h += '
'; + for (const s of fmSkills) { + h += `
${s.name}
${s.desc}
${s.badge}
`; + } + h += '
'; + + // Show meme tokens from market data if available + if (fmResult && fmResult.length > 0) { + h += '
BSC Meme Tokens
'; + h += ''; + for (const p of fmResult.slice(0, 8)) { + const chg = p.priceChange?.h24; + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + } + h += '
TokenPrice24hVolumeLiq
${_esc(p.baseToken?.symbol || '?')}${_fmtPrice(p.priceUsd)}${_fmtChg(chg)}${_fmtVol(p.volume?.h24)}${_fmtVol(p.liquidity?.usd)}
'; + } else if (memePairs.length > 0) { + h += '
BSC Meme Tokens (from market data)
'; + h += ''; + for (const p of memePairs.slice(0, 6)) { + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + } + h += '
TokenPrice24hVolume
${_esc(p.baseToken?.symbol || '?')}${_fmtPrice(p.priceUsd)}${_fmtChg(p.priceChange?.h24)}${_fmtVol(p.volume?.h24)}
'; + } + + } else { + // Search tab + h += ''; + h += examplesHTML([ + {label: 'CAKE', val: EX.CAKE}, + {label: 'WBNB', val: EX.WBNB}, + {label: 'USDT', val: EX.USDT}, + {label: 'FLOKI', val: 'FLOKI'}, + {label: 'PEPE', val: 'PEPE'}, + ], 'mp-search'); + + if (this._searchResult && this._searchResult.length > 0) { + h += ''; + for (const p of this._searchResult.slice(0, 15)) { + const base = p.baseToken?.symbol || '?'; + const quote = p.quoteToken?.symbol || '?'; + const chain = p.chainId || ''; + if (chain !== 'bsc') continue; + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + } + h += '
PairDEXPrice24hVolumeLiq
${_esc(base)}/${_esc(quote)}${_esc(p.dexId || '?')}${_fmtPrice(p.priceUsd)}${_fmtChg(p.priceChange?.h24)}${_fmtVol(p.volume?.h24)}${_fmtVol(p.liquidity?.usd)}
'; + } else if (this._searchResult !== null) { + h += '
No BSC pairs found
'; + } + } + return h; + } + + async _searchToken(q) { + if (!q) return; + if (!this._inputVals) this._inputVals = {}; + this._inputVals['mp-search'] = q; + try { + // If it looks like an address, use token endpoint; otherwise search + const isAddr = q.startsWith('0x') && q.length === 42; + const res = isAddr ? await bnbApi.marketToken(q) : await bnbApi.marketSearch(q); + this._searchResult = res?.pairs || []; + } catch { this._searchResult = []; } + const body = this.querySelector('.panel-body'); + if (body) { body.innerHTML = this.renderContent(this._data); this.afterRender(body); } + } + + afterRender(b) { + bindTabs(b, this); + const btn = b.querySelector('#mp-btn'), inp = b.querySelector('#mp-search'); + if (btn && inp) { + btn.addEventListener('click', () => this._searchToken(inp.value.trim())); + inp.addEventListener('keydown', e => { if (e.key === 'Enter') this._searchToken(inp.value.trim()); }); + } + bindExamples(b, this, v => this._searchToken(v)); + restoreInputs(b, this); + } +} +customElements.define('market-pack-panel', MarketPackPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 18. SyncX Cross-Posting (Community PR #1) — Skill #81 +// ═══════════════════════════════════════════════════════════════════ +class SyncxPanel extends BasePanel { + static skill = 'Skill #81'; + static defaultTitle = 'SyncX Cross-Post'; + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return {}; } + + renderContent() { + let h = css('info'); + h += '
'; + h += '
SyncX — Cross-Platform Content Posting
'; + h += '
Community PR #1 — One-stop crypto creator autopost workflow by SyncX2026
'; + const feats = [ + { t: 'Binance Square', d: 'Auto-post content to Binance Square with proper formatting' }, + { t: 'X / Twitter', d: 'Cross-post to X with thread support and media handling' }, + { t: 'Telegram', d: 'Optional channel broadcast with markdown formatting' }, + { t: 'Threads', d: 'Meta Threads cross-posting with image support' }, + { t: 'Farcaster', d: 'Decentralized social via Neynar API integration' }, + { t: 'Single Command', d: 'Sync the same post across all platforms in one action' }, + ]; + for (const f of feats) { + h += `
${f.t}
${f.d}
`; + } + h += '
'; + return h; + } +} +customElements.define('syncx-panel', SyncxPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 19. Whale Radar — Skill #82 +// Scans latest blocks for high-value BNB transfers +// ═══════════════════════════════════════════════════════════════════ +class WhaleRadarPanel extends BasePanel { + static skill = 'Skill #82'; + static defaultTitle = 'Whale Radar'; + constructor() { super(); this._refreshRate = 15000; this._whales = []; } + + async fetchData() { + const blkNum = await bnbApi.blockNumber(); + const num = blkNum?.result ? parseInt(blkNum.result, 16) : 0; + if (num === 0) return { whales: [], blockNum: 0 }; + // Scan last 3 blocks for high-value TXs (full TX objects) + const blocks = []; + for (let i = 0; i < 3; i++) { + const hex = '0x' + (num - i).toString(16); + try { blocks.push(bnbApi.blockFull(hex)); } catch {} + } + const results = await Promise.allSettled(blocks); + const whales = []; + for (const r of results) { + if (r.status !== 'fulfilled' || !r.value?.result) continue; + const b = r.value.result; + const blockNum = parseInt(b.number, 16); + const blockTime = new Date(parseInt(b.timestamp, 16) * 1000); + const txs = b.transactions || []; + for (const tx of txs) { + if (typeof tx === 'string') continue; // need full TX objects + const val = tx.value ? parseInt(tx.value, 16) / 1e18 : 0; + if (val >= 0.5) { + whales.push({ + hash: tx.hash, + from: tx.from, + to: tx.to, + value: val, + block: blockNum, + time: blockTime, + isContract: tx.input && tx.input !== '0x' && tx.input.length > 2, + }); + } + } + } + // Sort by value descending + whales.sort((a, b) => b.value - a.value); + this._whales = whales.slice(0, 20); + return { whales: this._whales, blockNum: num }; + } + + renderContent(d) { + if (!d) return '
Scanning blocks...
'; + let h = css('cards', 'table'); + h += '
'; + h += `
Latest Block
${_fmt(d.blockNum)}
`; + h += `
Whales Found
${d.whales.length}
`; + const total = d.whales.reduce((s, w) => s + w.value, 0); + h += `
Total Value
${total.toFixed(2)}
BNB
`; + h += '
'; + h += '
Transfers over 0.5 BNB in last 3 blocks (auto-refresh 15s)
'; + if (d.whales.length > 0) { + h += ''; + for (const w of d.whales) { + const cls = w.value >= 10 ? 't-green' : w.value >= 2 ? 'style="color:var(--accent)"' : ''; + const valStr = w.value >= 1000 ? _fmt(Math.round(w.value)) : w.value.toFixed(4); + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + } + h += '
ValueFromToTypeBlock
${valStr} BNB${shortAddr(w.from)}${w.to ? shortAddr(w.to) : 'Create'}${w.isContract ? 'Contract' : 'Transfer'}${_fmt(w.block)}
'; + } else { + h += '
No whale transfers in recent blocks. Waiting for next scan...
'; + } + return h; + } +} +customElements.define('whale-radar-panel', WhaleRadarPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 20. Token X-Ray — Skill #83 +// Deep token analysis: on-chain + market data + logo +// ═══════════════════════════════════════════════════════════════════ +class TokenXrayPanel extends BasePanel { + static skill = 'Skill #83'; + static defaultTitle = 'Token X-Ray'; + constructor() { super(); this._refreshRate = 0; this._result = null; this._autoLoaded = false; } + + async fetchData() { + if (!this._autoLoaded) { + this._autoLoaded = true; + await this._analyze(EX.CAKE); + } + return { result: this._result }; + } + + renderContent(d) { + let h = css('search', 'cards', 'detail', 'table', 'examples'); + h += ''; + h += examplesHTML([ + {label: 'CAKE', val: EX.CAKE}, + {label: 'WBNB', val: EX.WBNB}, + {label: 'USDT', val: EX.USDT}, + {label: 'BUSD', val: EX.BUSD}, + ], 'xr-addr'); + + if (d?.result) { + const r = d.result; + // Header with logo + const logoUrl = r.logo; + h += '
'; + if (logoUrl) h += ``; + h += `
${_esc(r.name || 'Unknown')}
`; + h += `
${_esc(r.symbol || '?')} ${r.priceUsd ? '— ' + _fmtPrice(r.priceUsd) : ''}
`; + h += '
'; + + // Stats cards + h += '
'; + h += `
Price
${r.priceUsd ? _fmtPrice(r.priceUsd) : '—'}
`; + h += `
24h Change
${r.change24h !== null ? _fmtChg(r.change24h) : '—'}
`; + h += `
Volume 24h
${r.volume24h ? _fmtVol(r.volume24h) : '—'}
`; + h += '
'; + + // On-chain details + h += '
'; + h += `
Contract${_esc(r.contract)}
`; + h += `
Decimals${r.decimals ?? '—'}
`; + if (r.totalSupply) h += `
Total Supply${r.totalSupply}
`; + h += `
Contract Code${r.isContract ? 'Verified' : 'No Code'}
`; + if (r.liquidity) h += `
Liquidity${_fmtVol(r.liquidity)}
`; + if (r.pairCount) h += `
DEX Pairs${r.pairCount}
`; + if (r.dex) h += `
Main DEX${_esc(r.dex)}
`; + h += '
'; + + // DEX pairs table if available + if (r.pairs && r.pairs.length > 1) { + h += '
DEX Pairs
'; + h += ''; + for (const p of r.pairs.slice(0, 5)) { + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + } + h += '
PairDEXPriceLiq
${_esc(p.base)}/${_esc(p.quote)}${_esc(p.dex)}${_fmtPrice(p.price)}${_fmtVol(p.liq)}
'; + } + } else { + h += '
Loading token analysis...
'; + } + return h; + } + + async _analyze(addr) { + if (!addr || addr.length !== 42) return; + if (!this._inputVals) this._inputVals = {}; + this._inputVals['xr-addr'] = addr; + try { + // Fetch on-chain + market data in parallel + const [tokenInfo, codeRes, marketRes] = await Promise.allSettled([ + bnbApi.tokenInfo(addr), + bnbApi.code(addr), + bnbApi.marketToken(addr), + ]); + + const token = tokenInfo.status === 'fulfilled' ? tokenInfo.value : null; + const code = codeRes.status === 'fulfilled' ? codeRes.value : null; + const market = marketRes.status === 'fulfilled' ? marketRes.value : null; + + const name = token ? decodeString(token.name?.result) : ''; + const symbol = token ? decodeString(token.symbol?.result) : ''; + const decimals = token?.decimals?.result ? parseInt(token.decimals.result, 16) : null; + let totalSupply = ''; + if (token?.totalSupply?.result) { + try { + const raw = BigInt(token.totalSupply.result); + const dec = decimals || 18; + const whole = raw / BigInt(10 ** dec); + totalSupply = whole.toLocaleString('en-US'); + } catch { totalSupply = ''; } + } + + const isContract = code?.result && code.result !== '0x'; + const bscPairs = (market?.pairs || []).filter(p => p.chainId === 'bsc'); + const topPair = bscPairs[0]; + + this._result = { + contract: addr, + name: topPair?.baseToken?.name || name || 'Unknown', + symbol: topPair?.baseToken?.symbol || symbol || '?', + decimals, + totalSupply, + isContract, + priceUsd: topPair?.priceUsd || null, + change24h: topPair?.priceChange?.h24 ?? null, + volume24h: topPair?.volume?.h24 || null, + liquidity: topPair?.liquidity?.usd || null, + dex: topPair?.dexId || null, + pairCount: bscPairs.length, + logo: topPair?.info?.imageUrl || tokenLogo(addr), + pairs: bscPairs.map(p => ({ + base: p.baseToken?.symbol || '?', + quote: p.quoteToken?.symbol || '?', + dex: p.dexId || '?', + price: p.priceUsd, + liq: p.liquidity?.usd, + })), + }; + } catch { this._result = null; } + const body = this.querySelector('.panel-body'); + if (body) { body.innerHTML = this.renderContent({ result: this._result }); this.afterRender(body); } + } + + afterRender(b) { + const btn = b.querySelector('#xr-btn'), inp = b.querySelector('#xr-addr'); + if (btn && inp) { + btn.addEventListener('click', () => this._analyze(inp.value.trim())); + inp.addEventListener('keydown', e => { if (e.key === 'Enter') this._analyze(inp.value.trim()); }); + } + bindExamples(b, this, v => this._analyze(v)); + restoreInputs(b, this); + } +} +customElements.define('token-xray-panel', TokenXrayPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 21. BSC Live Feed — Skill #84 +// Real-time block feed with visual gas bars +// ═══════════════════════════════════════════════════════════════════ +class BscLiveFeedPanel extends BasePanel { + static skill = 'Skill #84'; + static defaultTitle = 'BSC Live Feed'; + constructor() { super(); this._refreshRate = 3000; this._blocks = []; this._maxBlocks = 15; } + + async fetchData() { + const blkNum = await bnbApi.blockNumber(); + const num = blkNum?.result ? parseInt(blkNum.result, 16) : 0; + if (num === 0) return { blocks: [], latestBlock: 0 }; + + // Fetch latest block if we don't have it + if (this._blocks.length === 0 || this._blocks[0].number < num) { + const hex = '0x' + num.toString(16); + try { + const res = await bnbApi.block(hex); + if (res?.result) { + const b = res.result; + const blockData = { + number: parseInt(b.number, 16), + txCount: b.transactions?.length || 0, + gasUsed: parseInt(b.gasUsed, 16), + gasLimit: parseInt(b.gasLimit, 16), + time: new Date(parseInt(b.timestamp, 16) * 1000), + miner: b.miner, + hash: b.hash, + }; + // Don't add duplicates + if (!this._blocks.find(x => x.number === blockData.number)) { + this._blocks.unshift(blockData); + if (this._blocks.length > this._maxBlocks) this._blocks.pop(); + } + } + } catch {} + } + const gas = await bnbApi.gasPrice(); + const gasPrice = gas?.result ? parseInt(gas.result, 16) / 1e9 : 0; + return { blocks: this._blocks, latestBlock: num, gasPrice }; + } + + renderContent(d) { + if (!d || !d.blocks) return '
Connecting to BSC...
'; + let h = css('cards', 'table'); + h += '
'; + h += `
Latest Block
${_fmt(d.latestBlock)}
`; + h += `
Gas Price
${d.gasPrice.toFixed(1)}
Gwei
`; + const avgTx = d.blocks.length > 0 ? Math.round(d.blocks.reduce((s, b) => s + b.txCount, 0) / d.blocks.length) : 0; + h += `
Avg TXs/Block
${avgTx}
`; + h += '
'; + h += '
Live block stream (auto-refresh 3s)
'; + if (d.blocks.length > 0) { + h += ''; + for (const b of d.blocks) { + const pct = b.gasLimit > 0 ? (b.gasUsed / b.gasLimit * 100) : 0; + const barColor = pct > 80 ? '#f6465d' : pct > 50 ? '#f0b90b' : '#0ecb81'; + const barW = Math.min(pct, 100); + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + } + h += '
BlockTimeTXsGas UsageValidator
${_fmt(b.number)}${b.time.toLocaleTimeString()}${b.txCount}
${pct.toFixed(0)}%
${shortAddr(b.miner)}
'; + } + return h; + } +} +customElements.define('bsc-live-feed-panel', BscLiveFeedPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 22. Wallet Scanner — Mefai Skill: Multi-token portfolio scanner +// ═══════════════════════════════════════════════════════════════════ +class WalletScannerPanel extends BasePanel { + get panelTitle() { return 'Wallet Scanner'; } + get skillLabel() { return 'MEFAI · PORTFOLIO'; } + + constructor() { super(); this._refreshRate = 0; } + + async fetchData() { + if (!this._addr) return null; + return bnbApi.mefaiWalletScanner(this._addr); + } + + renderContent(d) { + const addr = this._addr || ''; + let h = css('search', 'examples', 'cards', 'table', 'detail'); + h += ''; + h += examplesHTML([ + { label: 'Binance Hot', val: EX.BINANCE_HOT }, + { label: 'CZ Wallet', val: EX.CZ_WALLET }, + ], 'ws-addr'); + + if (!d) return h + '
Enter a BSC wallet address to scan portfolio
'; + + // BNB balance card + h += '
'; + h += `
BNB Balance
${(d.bnb?.balance || 0).toFixed(4)}
$${(d.bnb?.valueUsd || 0).toLocaleString(undefined, {maximumFractionDigits:2})}
`; + const totalUsd = (d.bnb?.valueUsd || 0) + (d.tokens || []).reduce((s, t) => s + (t.valueUsd || 0), 0); + h += `
Total Value
$${totalUsd.toLocaleString(undefined, {maximumFractionDigits:2})}
BNB @ $${(d.bnb?.priceUsd || 0).toFixed(2)}
`; + h += '
'; + + if (d.tokens && d.tokens.length > 0) { + h += ''; + for (const t of d.tokens) { + const logo = tokenLogo(t.address); + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + } + h += '
TokenBalancePriceValue
${t.symbol || '?'}
${Number(t.balance || 0).toLocaleString(undefined, {maximumFractionDigits:4})}$${Number(t.priceUsd || 0).toFixed(4)}$${Number(t.valueUsd || 0).toLocaleString(undefined, {maximumFractionDigits:2})}
'; + } else { + h += '
No top tokens found in this wallet
'; + } + return h; + } + + afterRender(body) { + restoreInputs(body, this); + const go = () => { + const v = body.querySelector('#ws-addr')?.value?.trim(); + if (v && v.length === 42) { + if (!this._inputVals) this._inputVals = {}; + this._inputVals['ws-addr'] = v; + this._addr = v; + this.refresh(); + } + }; + body.querySelector('#ws-go')?.addEventListener('click', go); + body.querySelector('#ws-addr')?.addEventListener('keydown', e => { if (e.key === 'Enter') go(); }); + bindExamples(body, this, v => { this._addr = v; this.refresh(); }); + } +} +customElements.define('wallet-scanner-panel', WalletScannerPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 23. Contract Audit — Mefai Skill: On-chain + market safety scoring +// ═══════════════════════════════════════════════════════════════════ +class ContractAuditPanel extends BasePanel { + get panelTitle() { return 'Contract Audit'; } + get skillLabel() { return 'MEFAI · SAFETY'; } + + constructor() { super(); this._refreshRate = 0; } + + async fetchData() { + if (!this._addr) return null; + return bnbApi.mefaiContractAudit(this._addr); + } + + renderContent(d) { + const addr = this._addr || ''; + let h = css('search', 'examples', 'cards', 'detail'); + h += ''; + h += examplesHTML([ + { label: 'CAKE', val: EX.CAKE }, + { label: 'WBNB', val: EX.WBNB }, + { label: 'USDT', val: EX.USDT }, + ], 'ca-addr'); + + if (!d) return h + '
Enter a BSC token contract to run safety audit
'; + + const gradeColors = { A: '#0ecb81', B: '#3cc68a', C: '#f0b90b', D: '#e8961e', F: '#f6465d' }; + const gc = gradeColors[d.grade] || '#848e9c'; + const logo = tokenLogo(d.address); + + // Header with score + h += '
'; + h += `
${d.grade}
`; + h += '
'; + h += `
${d.name || 'Unknown'}${d.symbol || ''}
`; + h += `
Score: ${d.score}/100 · ${d.pairCount || 0} pairs on ${d.dex || 'DEX'}
`; + if (d.price) h += `
Price: $${d.price} · Vol: $${Number(d.volume24h || 0).toLocaleString()} · Liq: $${Number(d.liquidity || 0).toLocaleString()}
`; + h += '
'; + + // Score bar + h += `
`; + + // Checks + if (d.checks && d.checks.length > 0) { + h += '
'; + for (const c of d.checks) { + const icon = c.pass ? '✓' : '✗'; + const color = c.pass ? '#0ecb81' : '#f6465d'; + h += `
${icon} ${c.name}${c.detail || ''}
`; + } + h += '
'; + } + + // Burn info + if (d.burnedPct > 0) { + h += `
Burned: ${d.burnedPct.toFixed(2)}% of total supply · Supply: ${Number(d.totalSupply || 0).toLocaleString()}
`; + } + + return h; + } + + afterRender(body) { + restoreInputs(body, this); + const go = () => { + const v = body.querySelector('#ca-addr')?.value?.trim(); + if (v && v.length === 42) { + if (!this._inputVals) this._inputVals = {}; + this._inputVals['ca-addr'] = v; + this._addr = v; + this.refresh(); + } + }; + body.querySelector('#ca-go')?.addEventListener('click', go); + body.querySelector('#ca-addr')?.addEventListener('keydown', e => { if (e.key === 'Enter') go(); }); + bindExamples(body, this, v => { this._addr = v; this.refresh(); }); + } +} +customElements.define('contract-audit-panel', ContractAuditPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 24. Liquidity Pulse — Mefai Skill: BSC token liquidity heatmap +// ═══════════════════════════════════════════════════════════════════ +class LiquidityPulsePanel extends BasePanel { + get panelTitle() { return 'Liquidity Pulse'; } + get skillLabel() { return 'MEFAI · LIQUIDITY'; } + + constructor() { super(); this._refreshRate = 60000; } + + async fetchData() { + return bnbApi.mefaiLiquidityPulse(); + } + + renderContent(d) { + if (!d || !d.tokens) return '
Loading liquidity data...
'; + let h = css('table', 'cards'); + const tokens = d.tokens; + if (tokens.length === 0) return h + '
No token data available
'; + + // Summary cards + const avgVL = tokens.reduce((s, t) => s + (t.vlRatio || 0), 0) / tokens.length; + const totalVol = tokens.reduce((s, t) => s + (t.volume24h || 0), 0); + const totalLiq = tokens.reduce((s, t) => s + (t.liquidity || 0), 0); + h += '
'; + h += `
Tokens Tracked
${tokens.length}
`; + h += `
Total Volume 24h
$${(totalVol/1e6).toFixed(1)}M
`; + h += `
Avg V/L Ratio
${avgVL.toFixed(2)}
`; + h += '
'; + + h += '
BSC tokens ranked by volume/liquidity ratio (higher = more active trading)
'; + h += ''; + for (const t of tokens) { + const logo = tokenLogo(t.address); + const chgClass = (t.change24h || 0) >= 0 ? 't-green' : 't-red'; + const chgSign = (t.change24h || 0) >= 0 ? '+' : ''; + const vlColor = (t.vlRatio || 0) > 1 ? '#0ecb81' : (t.vlRatio || 0) > 0.3 ? '#f0b90b' : '#f6465d'; + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + } + h += '
TokenPrice24h %VolumeLiquidityV/LBuy/Sell
${t.symbol}
$${t.price}${chgSign}${(t.change24h || 0).toFixed(2)}%$${(t.volume24h/1e3).toFixed(0)}K$${(t.liquidity/1e3).toFixed(0)}K${(t.vlRatio || 0).toFixed(2)}${t.buys24h || 0}/${t.sells24h || 0}
'; + return h; + } +} +customElements.define('liquidity-pulse-panel', LiquidityPulsePanel); + +// ═══════════════════════════════════════════════════════════════════ +// MEFAI EXCLUSIVE STYLES — Premium visual system +// ═══════════════════════════════════════════════════════════════════ +const _MX = ` +`; + +function _gauge(pct, color, val, sub, size = 64) { + const r = (size - 6) / 2; + const circ = 2 * Math.PI * r; + const dash = circ * Math.min(pct, 100) / 100; + return `
+ + +
${val}
${sub}
`; +} + +// ═══════════════════════════════════════════════════════════════════ +// 25. BNB Burn Engine — $12B+ burned, visual fire dashboard +// ═══════════════════════════════════════════════════════════════════ +class BurnTrackerPanel extends BasePanel { + get panelTitle() { return 'BNB Burn Engine'; } + get skillLabel() { return 'MEFAI · DEFLATIONARY'; } + constructor() { super(); this._refreshRate = 120000; } + async fetchData() { return bnbApi.mefaiBurnTracker(); } + renderContent(d) { + if (!d || d.error) return '
Loading burn data...
'; + let h = _MX + css('table'); + const bnb = d.bnb || {}; + const totalUsd = d.totalBurnedUsd || 0; + + // Hero: massive burn counter with gauge + h += '
'; + h += '
'; + const burnPctOfMax = Math.min((bnb.totalBurned || 0) / 200000000 * 100, 100); + h += _gauge(burnPctOfMax, '#f0b90b', Math.round(burnPctOfMax) + '%', 'of 200M', 80); + h += '
'; + h += '
Total Value Burned
'; + h += `
$${_fmtM(totalUsd)}
`; + h += `
${_fmt(Math.round(bnb.totalBurned||0))} BNB permanently removed from circulation
`; + h += `
BNB Price: $${(bnb.priceUsd||0).toFixed(2)}
`; + h += '
'; + + // Burn sources + h += '
'; + h += `
Dead Address
${_fmtShort(bnb.deadBalance||0)}
BNB
`; + h += `
Zero Address
${_fmtShort(bnb.zeroBalance||0)}
BNB
`; + h += `
BNB Burned USD
$${_fmtM(bnb.burnedValueUsd||0)}
`; + h += '
'; + + // Token burns with visual burn bars + const tokens = (d.tokens || []).filter(t => t.burned > 0); + if (tokens.length > 0) { + h += '
'; + for (const t of tokens) { + const logo = tokenLogo(t.address); + const pct = Math.min(t.burnedPct, 100); + const barColor = pct > 50 ? '#f6465d' : pct > 10 ? '#f0b90b' : '#0ecb81'; + h += '
'; + h += `
${t.symbol}
`; + h += `
`; + h += `
${pct.toFixed(1)}%
`; + h += `
$${_fmtM(t.burnedValueUsd)}
`; + h += '
'; + } + h += '
'; + } + return h; + } +} +customElements.define('burn-tracker-panel', BurnTrackerPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 26. BSC vs ETH Showdown — Real-time superiority proof +// ═══════════════════════════════════════════════════════════════════ +class BscSupremacyPanel extends BasePanel { + get panelTitle() { return 'BSC vs ETH Showdown'; } + get skillLabel() { return 'MEFAI · SUPREMACY'; } + constructor() { super(); this._refreshRate = 15000; } + async fetchData() { return bnbApi.mefaiBscSupremacy(); } + renderContent(d) { + if (!d || d.error) return '
Comparing chains...
'; + let h = _MX; + const bsc = d.bsc || {}; + const eth = d.eth || {}; + const adv = d.advantage || {}; + + // Advantage hero + h += '
'; + h += '
BSC Advantage
'; + h += '
'; + h += `
${adv.speedMultiplier}x
FASTER
`; + h += `
${adv.tpsMultiplier}x
MORE TPS
`; + h += `
${adv.costMultiplier}x
CHEAPER
`; + h += '
'; + + // Side by side comparison + h += '
'; + // BSC side + h += '
'; + h += '
BNB Smart Chain
'; + h += `
Block Time${bsc.blockTime}s
`; + h += `
TPS${bsc.tps}
`; + h += `
Gas${bsc.gasGwei} Gwei
`; + h += `
Transfer$${bsc.transferCost}
`; + h += `
Swap$${bsc.swapCost}
`; + h += `
Finality${bsc.finality}
`; + h += `
BNB$${bsc.nativePrice?.toFixed(2)}
`; + h += '
'; + // VS + h += '
VS
'; + // ETH side + h += '
'; + h += '
Ethereum
'; + h += `
Block Time${eth.blockTime}s
`; + h += `
TPS${eth.tps}
`; + h += `
Gas${eth.gasGwei} Gwei
`; + h += `
Transfer$${eth.transferCost}
`; + h += `
Swap$${eth.swapCost}
`; + h += `
Finality${eth.finality}
`; + h += `
ETH$${eth.nativePrice?.toFixed(2)}
`; + h += '
'; + h += '
'; + + // Savings callout + h += '
'; + h += `You save $${adv.swapSavings?.toFixed(4)} per swap on BSC vs Ethereum`; + h += '
'; + return h; + } +} +customElements.define('bsc-supremacy-panel', BscSupremacyPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 27. Smart Money Radar — Live whale tracking with flow analysis +// ═══════════════════════════════════════════════════════════════════ +class SmartMoneyPanel extends BasePanel { + get panelTitle() { return 'Smart Money Radar'; } + get skillLabel() { return 'MEFAI · WHALE INTEL'; } + constructor() { super(); this._refreshRate = 15000; } + async fetchData() { return bnbApi.mefaiSmartMoney(); } + renderContent(d) { + if (!d || d.error) return '
Scanning whale activity...
'; + let h = _MX; + + // Hero: flow direction + const flowColor = d.flowDirection === 'accumulation' ? '#0ecb81' : d.flowDirection === 'distribution' ? '#f6465d' : '#f0b90b'; + const flowText = d.flowDirection === 'accumulation' ? 'ACCUMULATION' : d.flowDirection === 'distribution' ? 'DISTRIBUTION' : 'NEUTRAL'; + h += '
'; + h += '
'; + h += '
'; + h += `
Live Flow Direction
`; + h += `
${flowText}
`; + h += `
Net: ${d.netFlow >= 0 ? '+' : ''}${_fmt(d.netFlow)} BNB ($${_fmtM(Math.abs(d.netFlowUsd))})
`; + h += '
'; + h += _gauge(d.whaleCount > 0 ? Math.min(d.whaleCount * 4, 100) : 5, flowColor, d.whaleCount, 'WHALES', 56); + h += '
'; + + // Flow cards + h += '
'; + h += `
Inflow
${_fmtShort(d.inflow)}
BNB to known wallets
`; + h += `
Outflow
${_fmtShort(d.outflow)}
BNB from known wallets
`; + h += `
Total Volume
${_fmtShort(d.totalVolume)}
$${_fmtM(d.totalVolumeUsd)}
`; + h += '
'; + + // Whale transactions + const whales = d.whales || []; + if (whales.length > 0) { + h += `
${whales.length} BNB transfers in last ${d.blocksScanned} blocks
`; + for (const w of whales.slice(0, 12)) { + const valColor = w.value >= 100 ? '#f6465d' : w.value >= 50 ? '#f0b90b' : '#0ecb81'; + h += '
'; + h += `
${w.value.toLocaleString()} BNB
`; + h += '
'; + if (w.fromLabel) h += `${w.fromLabel} `; + else h += `${shortAddr(w.from)} `; + h += `-> `; + if (w.toLabel) h += `${w.toLabel}`; + else h += `${shortAddr(w.to)}`; + h += '
'; + h += `
$${_fmtM(w.valueUsd)}
`; + h += '
'; + } + } else { + h += '
No whale transactions in recent blocks
'; + } + return h; + } +} +customElements.define('smart-money-panel', SmartMoneyPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 28. PancakeSwap Arena — Enhanced with visual volume bars +// ═══════════════════════════════════════════════════════════════════ +class PancakeswapArenaPanel extends BasePanel { + get panelTitle() { return 'PancakeSwap Arena'; } + get skillLabel() { return 'MEFAI · PANCAKESWAP'; } + constructor() { super(); this._refreshRate = 30000; } + async fetchData() { return bnbApi.mefaiPancakeswapArena(); } + renderContent(d) { + if (!d || !d.pairs) return '
Loading PancakeSwap data...
'; + let h = _MX + css('table'); + const pairs = d.pairs || []; + if (pairs.length === 0) return h + '
No pairs found
'; + + const totalVol = pairs.reduce((s, p) => s + (p.volume24h || 0), 0); + const totalLiq = pairs.reduce((s, p) => s + (p.liquidity || 0), 0); + const maxVol = Math.max(...pairs.map(p => p.volume24h || 0)); + + // Hero + h += '
'; + h += `
Active Pairs
${pairs.length}
`; + h += `
24h Volume
$${_fmtM(totalVol)}
`; + h += `
Liquidity
$${_fmtM(totalLiq)}
`; + h += '
'; + + h += ''; + for (const p of pairs) { + const logo = tokenLogo(p.baseAddress); + const c5 = _chgClass(p.change5m); + const c1 = _chgClass(p.change1h); + const c24 = _chgClass(p.change24h); + const volPct = maxVol > 0 ? (p.volume24h / maxVol * 100) : 0; + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + } + h += '
PairPrice5m1h24hVolumeTxns
${p.pair}
$${p.price || '?'}${_chgFmt(p.change5m)}${_chgFmt(p.change1h)}${_chgFmt(p.change24h)}
$${_fmtM(p.volume24h)}
${p.buys24h}/${p.sells24h}
'; + return h; + } +} +customElements.define('pancakeswap-arena-panel', PancakeswapArenaPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 29. DeFi Leaderboard — With medals and visual dominance bars +// ═══════════════════════════════════════════════════════════════════ +class DefiLeaderboardPanel extends BasePanel { + get panelTitle() { return 'DeFi Leaderboard'; } + get skillLabel() { return 'MEFAI · DEFI'; } + constructor() { super(); this._refreshRate = 60000; } + async fetchData() { return bnbApi.mefaiDefiLeaderboard(); } + renderContent(d) { + if (!d || !d.tokens) return '
Loading DeFi data...
'; + let h = _MX; + const tokens = d.tokens || []; + if (tokens.length === 0) return h + '
No DeFi data
'; + + const totalVol = tokens.reduce((s, t) => s + (t.totalVolume24h || 0), 0); + const totalLiq = tokens.reduce((s, t) => s + (t.totalLiquidity || 0), 0); + const maxVol = Math.max(...tokens.map(t => t.totalVolume24h || 0)); + + h += '
'; + h += `
Protocols
${tokens.length}
`; + h += `
24h Volume
$${_fmtM(totalVol)}
`; + h += `
Liquidity
$${_fmtM(totalLiq)}
`; + h += '
'; + + for (const [i, t] of tokens.entries()) { + const logo = tokenLogo(t.address); + const volPct = maxVol > 0 ? (t.totalVolume24h / maxVol * 100) : 0; + const medal = i === 0 ? '1' : i === 1 ? '2' : i === 2 ? '3' : `${i+1}`; + const c24 = _chgClass(t.change24h); + + h += '
'; + h += medal; + h += ``; + h += '
'; + h += `
${t.symbol}${_chgFmt(t.change24h)}
`; + h += `
$${_fmtM(t.totalVolume24h)}
`; + h += `
${t.pairCount} pairs · $${_fmtM(t.totalLiquidity)} liq · ${t.totalBuys}/${t.totalSells}
`; + h += '
'; + } + return h; + } +} +customElements.define('defi-leaderboard-panel', DefiLeaderboardPanel); + +// ═══════════════════════════════════════════════════════════════════ +// 30. Mefai Hub — Tab-based skill selector (SuperBSC style) +// ═══════════════════════════════════════════════════════════════════ +const _MEFAI_SKILLS = [ + { id: 'burn', label: 'Burn Engine', tag: 'burn-tracker-panel' }, + { id: 'supremacy', label: 'BSC vs ETH', tag: 'bsc-supremacy-panel' }, + { id: 'smart', label: 'Smart Money', tag: 'smart-money-panel' }, + { id: 'pancake', label: 'PancakeSwap', tag: 'pancakeswap-arena-panel' }, + { id: 'defi', label: 'DeFi Board', tag: 'defi-leaderboard-panel' }, + { id: 'wallet', label: 'Wallet Scanner', tag: 'wallet-scanner-panel' }, + { id: 'audit', label: 'Contract Audit', tag: 'contract-audit-panel' }, + { id: 'pulse', label: 'Liquidity Pulse',tag: 'liquidity-pulse-panel' }, + { id: 'txdecode', label: 'TX Decoder', tag: 'tx-decoder-panel' }, + { id: 'xray', label: 'Contract X-Ray', tag: 'contract-xray-panel' }, + { id: 'approvals', label: 'Approvals', tag: 'approval-scanner-panel' }, + { id: 'autopsy', label: 'Block Autopsy', tag: 'block-autopsy-panel' }, + { id: 'gascalc', label: 'Gas Calculator', tag: 'gas-calculator-panel' }, + { id: 'battle', label: 'Token Battle', tag: 'token-battle-panel' }, + { id: 'pairdeep', label: 'Pair Analytics', tag: 'pair-analytics-panel' }, + { id: 'tokenflow', label: 'Token Flow', tag: 'token-flow-panel' }, + { id: 'yield', label: 'Yield Finder', tag: 'yield-finder-panel' }, + { id: 'risk', label: 'Risk Radar', tag: 'risk-radar-panel' }, + { id: 'sniper', label: 'Sniper Detector', tag: 'sniper-detector-panel' }, + { id: 'cluster', label: 'Wallet Cluster', tag: 'wallet-cluster-panel' }, + { id: 'honeypot', label: 'Honeypot Check', tag: 'honeypot-check-panel' }, + { id: 'validators', label: 'Validators', tag: 'validator-map-panel' }, + { id: 'arb', label: 'DEX Arbitrage', tag: 'dex-arb-panel' }, + { id: 'birth', label: 'Token Birth', tag: 'token-birth-panel' }, + { id: 'netpulse', label: 'Network Pulse', tag: 'network-pulse-panel' }, + { id: 'copytrade', label: 'Copy Trade', tag: 'copy-trade-panel' }, + { id: 'upgrade', label: 'Upgrade Monitor', tag: 'upgrade-monitor-panel' }, + { id: 'heatmap', label: 'Portfolio Heatmap', tag: 'portfolio-heatmap-panel' }, +]; + +class MefaiHubPanel extends BasePanel { + get panelTitle() { return 'Mefai Skills'; } + get skillLabel() { return 'MEFAI · HUB'; } + constructor() { + super(); + this._refreshRate = 0; + this._activeSkill = 'burn'; + this._childPanel = null; + } + + async fetchData() { return {}; } + + renderContent() { + let h = ``; + h += '
'; + for (const s of _MEFAI_SKILLS) { + h += ``; + } + h += '
'; + h += '
'; + return h; + } + + afterRender(body) { + // Bind tabs + body.querySelectorAll('.mh-tab').forEach(btn => { + btn.addEventListener('click', () => { + this._activeSkill = btn.dataset.skill; + // Update tab visual + body.querySelectorAll('.mh-tab').forEach(b => b.classList.toggle('active', b.dataset.skill === this._activeSkill)); + this._mountChild(body); + }); + }); + this._mountChild(body); + } + + _mountChild(body) { + const slot = body.querySelector('#mh-slot'); + if (!slot) return; + // Remove old child + if (this._childPanel && this._childPanel.parentNode) { + if (this._childPanel._timer) clearInterval(this._childPanel._timer); + this._childPanel.remove(); + } + const skill = _MEFAI_SKILLS.find(s => s.id === this._activeSkill); + if (!skill) return; + slot.innerHTML = ''; + const el = document.createElement(skill.tag); + // Override panel's own border/header styling to fit inside hub + el.style.border = 'none'; + el.style.background = 'transparent'; + slot.appendChild(el); + this._childPanel = el; + } + + disconnectedCallback() { + if (this._childPanel) { + if (this._childPanel._timer) clearInterval(this._childPanel._timer); + } + super.disconnectedCallback && super.disconnectedCallback(); + } +} +customElements.define('mefai-hub-panel', MefaiHubPanel); + +// ── Shared formatting helpers ────────────────────────────────── +function _fmt(n) { return n ? n.toLocaleString() : '0'; } +function _fmtM(n) { if (!n) return '0'; if (n >= 1e9) return (n/1e9).toFixed(2)+'B'; if (n >= 1e6) return (n/1e6).toFixed(2)+'M'; if (n >= 1e3) return (n/1e3).toFixed(1)+'K'; return n.toFixed(2); } +function _fmtShort(n) { if (!n) return '0'; if (n >= 1e9) return (n/1e9).toFixed(1)+'B'; if (n >= 1e6) return (n/1e6).toFixed(1)+'M'; if (n >= 1e3) return (n/1e3).toFixed(0)+'K'; return String(Math.round(n)); } +function _chgClass(v) { return v == null ? 't-muted' : v >= 0 ? 't-green' : 't-red'; } +function _chgFmt(v) { if (v == null) return '-'; return (v >= 0 ? '+' : '') + v.toFixed(2) + '%'; } + +// ═══════════════════════════════════════════════════════════════════ +// BATCH 3: 10 Professional Developer Tools +// ═══════════════════════════════════════════════════════════════════ + +// Helper: search panel template (reusable) +function _searchPanel(id, placeholder, btnText, exArr, exInputId) { + let h = css('search', 'examples'); + h += ``; + if (exArr && exArr.length) h += examplesHTML(exArr, id); + return h; +} +function _bindSearch(body, panel, inputId, fn) { + const go = () => { const v = body.querySelector('#'+inputId)?.value?.trim(); if (v) { if(!panel._inputVals) panel._inputVals={}; panel._inputVals[inputId]=v; panel._q=v; fn(v); }}; + body.querySelector('#'+inputId+'-go')?.addEventListener('click', go); + body.querySelector('#'+inputId)?.addEventListener('keydown', e => { if(e.key==='Enter') go(); }); + restoreInputs(body, panel); + bindExamples(body, panel, v => { panel._q=v; fn(v); }); +} + +// 31. TX Decoder +class TxDecoderPanel extends BasePanel { + get panelTitle() { return 'TX Decoder'; } + get skillLabel() { return 'MEFAI · DECODE'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return this._q ? bnbApi.mefaiTxDecoder(this._q) : null; } + renderContent(d) { + let h = _MX + _searchPanel('txd-hash', 'Transaction hash (0x...)', 'Decode', [ + { label: 'Latest Block TX', val: '__latest_tx__' }, + ], 'txd-hash'); + if (!d) return h + '
Paste any BSC transaction hash to decode function calls, events, and transfers
'; + if (d.error) return h + `
${d.detail || d.error}
`; + + // Status + Gas + const ok = d.status === 'success' || d.status === '0x1' || d.status === true; + h += '
'; + h += `
${ok?'SUCCESS':'FAILED'}`; + h += `Block ${_fmt(d.blockNumber || 0)}
`; + h += `
Function: ${d.function || d.functionName || d.functionSelector || 'Unknown'}
`; + h += `
Gas: ${_fmt(d.gasUsed||0)} · Price: ${d.gasPrice||0} Gwei · Cost: ${d.gasCostBnb||0} BNB
`; + if (d.value > 0) h += `
Value: ${d.value} BNB
`; + h += '
'; + + // From/To + h += '
'; + h += `
From${d.from||''}
`; + h += `
To${d.to||''}
`; + h += '
'; + + // Events + const events = d.events || []; + if (events.length > 0) { + h += `
${events.length} EVENTS
`; + for (const e of events.slice(0, 10)) { + const ename = e.event || e.name || 'Event'; + const ec = ename === 'Transfer' ? '#0ecb81' : ename === 'Approval' ? '#f0b90b' : ename === 'Swap' ? '#e040fb' : 'var(--text)'; + h += `
`; + h += `${ename}`; + h += ` ${shortAddr(e.address||'')}`; + if (e.decoded) h += `
${e.decoded}
`; + h += '
'; + } + } + + // Token Transfers + const transfers = d.tokenTransfers || []; + if (transfers.length > 0) { + h += `
${transfers.length} TOKEN TRANSFERS
`; + for (const t of transfers) { + h += `
${shortAddr(t.from)}->${shortAddr(t.to)}${t.amount || '?'}
`; + } + } + return h; + } + afterRender(body) { + _bindSearch(body, this, 'txd-hash', () => this.refresh()); + // Handle dynamic example + body.querySelectorAll('.ex-btn').forEach(btn => { + if (btn.dataset.val === '__latest_tx__') { + btn.addEventListener('click', async (e) => { + e.stopImmediatePropagation(); + btn.textContent = 'Loading...'; + try { + const block = await bnbApi.block(); + const txs = block?.result?.transactions || []; + if (txs.length > 0) { + const hash = typeof txs[0] === 'string' ? txs[0] : txs[0].hash; + this._q = hash; + const input = body.querySelector('#txd-hash'); + if (input) input.value = hash; + this.refresh(); + } + } catch(e) { btn.textContent = 'Error'; } + }, { once: true }); + } + }); + } +} +customElements.define('tx-decoder-panel', TxDecoderPanel); + +// 32. Contract X-Ray +class ContractXrayPanel extends BasePanel { + get panelTitle() { return 'Contract X-Ray'; } + get skillLabel() { return 'MEFAI · FORENSICS'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return this._q ? bnbApi.mefaiContractXray(this._q) : null; } + renderContent(d) { + let h = _MX + _searchPanel('cx-addr', 'Contract address (0x...)', 'X-Ray', [ + { label: 'CAKE', val: EX.CAKE }, { label: 'PancakeRouter', val: EX.PANCAKE_ROUTER }, { label: 'WBNB', val: EX.WBNB }, + ], 'cx-addr'); + if (!d) return h + '
Deep bytecode analysis: proxy detection, function scanning, ownership, dangerous patterns
'; + + const logo = tokenLogo(d.address); + h += '
'; + h += '
'; + h += ``; + h += '
'; + h += `
${d.erc20Info?.name || 'Contract'} ${d.erc20Info?.symbol || ''}
`; + h += `
${_fmt(d.codeSize||0)} bytes · ${(d.detectedFunctions||[]).length} functions detected
`; + h += '
'; + if (d.isProxy) h += `
PROXY Impl: ${shortAddr(d.implementationAddress||'')}
`; + if (d.owner && d.owner !== '0x0000000000000000000000000000000000000000') h += `
Owner: ${shortAddr(d.owner)}
`; + h += '
'; + + // Patterns + const p = d.hasPatterns || {}; + h += '
'; + h += `${p.mintable?'MINTABLE':'NO MINT'}`; + h += `${p.pausable?'PAUSABLE':'NO PAUSE'}`; + h += `${p.blacklistable?'BLACKLIST':'NO BLACKLIST'}`; + h += `${p.burnable?'BURNABLE':'NO BURN'}`; + h += '
'; + + // Functions + const funcs = d.detectedFunctions || []; + if (funcs.length > 0) { + h += '
DETECTED FUNCTIONS
'; + h += '
'; + for (const f of funcs) { + const isKnown = f.name && f.name !== f.selector; + h += `${f.name || f.selector}`; + } + h += '
'; + } + return h; + } + afterRender(body) { _bindSearch(body, this, 'cx-addr', () => this.refresh()); } +} +customElements.define('contract-xray-panel', ContractXrayPanel); + +// 33. Approval Scanner +class ApprovalScannerPanel extends BasePanel { + get panelTitle() { return 'Approval Scanner'; } + get skillLabel() { return 'MEFAI · SECURITY'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return this._q ? bnbApi.mefaiApprovalScanner(this._q) : null; } + renderContent(d) { + let h = _MX + _searchPanel('as-addr', 'Wallet address (0x...)', 'Scan', [ + { label: 'Paste Your Wallet', val: '' }, + ], 'as-addr'); + if (!d) return h + '
Paste your BSC wallet to check token approvals against 9 major DEX routers (PancakeSwap V1/V2/V3, 1inch, Biswap, ApeSwap, ParaSwap)
'; + + const approvals = d.approvals || []; + const unlimited = approvals.filter(a => a.isUnlimited).length; + const active = approvals.length; + + h += '
'; + h += `
Tokens Checked
${d.tokensChecked||approvals.length}
`; + h += `
Active Approvals
${active}
`; + h += `
Unlimited
${unlimited}
`; + h += '
'; + + for (const a of approvals) { + const logo = tokenLogo(a.token || a.tokenAddress); + const danger = a.isUnlimited; + h += `
`; + h += ``; + h += `
`; + h += `
${a.symbol||'?'}${danger?'UNLIMITED':_fmtShort(a.allowance)}
`; + h += `
Spender: ${a.spenderLabel||shortAddr(a.spender||'')}
`; + h += '
'; + } + if (active === 0) h += '
No active approvals — wallet is clean
'; + return h; + } + afterRender(body) { _bindSearch(body, this, 'as-addr', () => this.refresh()); } +} +customElements.define('approval-scanner-panel', ApprovalScannerPanel); + +// 34. Block Autopsy +class BlockAutopsyPanel extends BasePanel { + get panelTitle() { return 'Block Autopsy'; } + get skillLabel() { return 'MEFAI · BLOCK'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return bnbApi.mefaiBlockAutopsy(this._q || ''); } + renderContent(d) { + let h = _MX + _searchPanel('ba-num', 'Block number (leave empty for latest)', 'Analyze', [], 'ba-num'); + if (!d || d.error) return h + '
Analyzing block...
'; + + h += '
'; + h += `
Block #${_fmt(d.blockNumber)}
`; + h += `
`; + h += `
${d.txCount}
TXs
`; + h += `
${_fmtShort(d.gasUsed)}
Gas Used
`; + h += `
${d.totalValue?.toFixed(2)||0}
BNB Value
`; + h += '
'; + + // TX type breakdown + const types = d.txTypes || {}; + const total = d.txCount || 1; + h += '
TRANSACTION TYPES
'; + const typeColors = { swap:'#e040fb', transfer:'#0ecb81', approve:'#f0b90b', other:'var(--text-muted)' }; + for (const [type, count] of Object.entries(types)) { + const pct = (count / total * 100); + h += '
'; + h += `${type}`; + h += `
`; + h += `${count} (${pct.toFixed(0)}%)`; + h += '
'; + } + + // Top gas consumers + const top = d.topGasConsumers || []; + if (top.length > 0) { + h += '
TOP GAS CONSUMERS
'; + for (const c of top) { + const gasPct = d.gasUsed > 0 ? (c.gasUsed / d.gasUsed * 100) : 0; + h += '
'; + h += `${shortAddr(c.address)}`; + h += `
`; + h += `${_fmtShort(c.gasUsed)} ${c.txCount}tx`; + h += '
'; + } + } + return h; + } + afterRender(body) { _bindSearch(body, this, 'ba-num', v => { this._q = v; this.refresh(); }); } +} +customElements.define('block-autopsy-panel', BlockAutopsyPanel); + +// 35. Gas Calculator +class GasCalculatorPanel extends BasePanel { + get panelTitle() { return 'Gas Calculator'; } + get skillLabel() { return 'MEFAI · GAS'; } + constructor() { super(); this._refreshRate = 15000; } + async fetchData() { return bnbApi.mefaiGasCalculator(); } + renderContent(d) { + if (!d || d.error) return '
Loading gas data...
'; + let h = _MX; + + h += '
'; + h += `
BSC Gas
${d.bscGasGwei||d.gasPriceGwei||'?'} Gwei
`; + h += `
BNB Price
$${(d.bnbPrice||0).toFixed(0)}
`; + h += `
ETH Gas
${d.ethGasGwei||8} Gwei
`; + h += '
'; + + const ops = d.operations || []; + if (ops.length > 0) { + h += '
OPERATION COSTS
'; + h += ``; + for (const op of ops) { + const bscUsd = op.bsc?.costUsd ?? op.bscCostUsd ?? 0; + const ethUsd = op.eth?.costUsd ?? op.ethCostUsd ?? 0; + const save = ethUsd - bscUsd; + const savePct = ethUsd > 0 ? (save / ethUsd * 100) : 0; + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + } + h += '
OperationGasBSC CostETH CostSavings
${op.operation||op.name||'?'}${_fmtShort(op.gasUnits)}$${bscUsd.toFixed(4)}$${ethUsd.toFixed(4)}${savePct > 0 ? '-' : ''}${savePct.toFixed(0)}%
'; + } + return h; + } +} +customElements.define('gas-calculator-panel', GasCalculatorPanel); + +// 36. Multi-Token Battle +class TokenBattlePanel extends BasePanel { + get panelTitle() { return 'Token Battle'; } + get skillLabel() { return 'MEFAI · COMPARE'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return this._q ? bnbApi.mefaiTokenBattle(this._q) : null; } + renderContent(d) { + let h = _MX + _searchPanel('tb-addrs', 'Token addresses (comma-separated, max 4)', 'Battle', [ + { label: 'CAKE vs WBNB', val: EX.CAKE+','+EX.WBNB }, + { label: 'USDT vs BUSD', val: EX.USDT+','+EX.BUSD }, + ], 'tb-addrs'); + if (!d) return h + '
Compare up to 4 tokens side-by-side: price, volume, liquidity, burn rate
'; + const tokens = d.tokens || []; + if (tokens.length === 0) return h + '
No token data found
'; + + // Cards grid + h += `
`; + for (const t of tokens) { + const logo = tokenLogo(t.address); + const c24 = _chgClass(t.change24h); + h += '
'; + h += `
${t.symbol||'?'}
${t.name||''}
`; + h += `
$${t.price||'?'}
`; + h += `
${_chgFmt(t.change24h)}
`; + h += `
`; + h += `Vol: $${_fmtM(t.volume24h||0)}
`; + h += `Liq: $${_fmtM(t.liquidity||0)}
`; + h += `Pairs: ${t.pairCount||0}
`; + if (t.burnedPct > 0) h += `Burned: ${t.burnedPct?.toFixed(1)}%
`; + h += `Code: ${_fmtShort(t.codeSize||0)} B`; + h += '
'; + } + h += '
'; + + // Comparison bars + const metrics = [ + { key: 'volume24h', label: 'Volume 24h', fmt: v => '$'+_fmtM(v) }, + { key: 'liquidity', label: 'Liquidity', fmt: v => '$'+_fmtM(v) }, + { key: 'pairCount', label: 'Pairs', fmt: v => String(v||0) }, + ]; + const colors = ['#f0b90b','#0ecb81','#e040fb','#1e88e5']; + for (const m of metrics) { + const max = Math.max(...tokens.map(t => t[m.key]||0), 1); + h += `
${m.label}
`; + tokens.forEach((t, i) => { + const pct = ((t[m.key]||0) / max * 100); + h += `
${t.symbol}
${m.fmt(t[m.key]||0)}
`; + }); + } + return h; + } + afterRender(body) { _bindSearch(body, this, 'tb-addrs', () => this.refresh()); } +} +customElements.define('token-battle-panel', TokenBattlePanel); + +// 37. Pair Analytics +class PairAnalyticsPanel extends BasePanel { + get panelTitle() { return 'Pair Analytics'; } + get skillLabel() { return 'MEFAI · PAIRS'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return this._q ? bnbApi.mefaiPairAnalytics(this._q) : null; } + renderContent(d) { + let h = _MX + css('table') + _searchPanel('pa-addr', 'Token contract address (0x...)', 'Analyze', [ + { label: 'CAKE', val: EX.CAKE }, { label: 'WBNB', val: EX.WBNB }, + ], 'pa-addr'); + if (!d) return h + '
Deep DEX pair analysis: volume distribution, liquidity depth, trade metrics
'; + + const pairs = d.pairs || []; + const agg = d.aggregate || {}; + h += '
'; + h += `
Pairs
${agg.pairCount||0}
`; + h += `
Total Volume
$${_fmtM(agg.totalVolume||0)}
`; + h += `
Total Liquidity
$${_fmtM(agg.totalLiquidity||0)}
`; + h += '
'; + + if (pairs.length > 0) { + h += ''; + for (const p of pairs.slice(0, 15)) { + const c24 = _chgClass(p.change24h); + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + } + h += '
PairDEXPrice24hVolumeLiqTxns
${p.pair||'?'}${p.dex||''}$${p.price||'?'}${_chgFmt(p.change24h)}$${_fmtM(p.volume24h||0)}$${_fmtM(p.liquidity||0)}${p.buys24h||0}/${p.sells24h||0}
'; + } + return h; + } + afterRender(body) { _bindSearch(body, this, 'pa-addr', () => this.refresh()); } +} +customElements.define('pair-analytics-panel', PairAnalyticsPanel); + +// 38. Token Flow Radar +class TokenFlowPanel extends BasePanel { + get panelTitle() { return 'Token Flow'; } + get skillLabel() { return 'MEFAI · FLOW'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return this._q ? bnbApi.mefaiTokenFlow(this._q) : null; } + renderContent(d) { + let h = _MX + _searchPanel('tf-addr', 'Token contract address (0x...)', 'Track', [ + { label: 'CAKE', val: EX.CAKE }, { label: 'USDT', val: EX.USDT }, + ], 'tf-addr'); + if (!d) return h + '
Track real-time token transfers from latest BSC blocks
'; + + const transfers = d.transfers || []; + h += '
'; + h += `
Transfers
${d.totalTransfers||0}
`; + h += `
Unique Addrs
${d.uniqueAddresses||0}
`; + h += `
Blocks Scanned
${d.blocksScanned||0}
`; + h += '
'; + + if (d.largestTransfer) { + const lg = typeof d.largestTransfer === 'object' ? (d.largestTransfer.amount || d.largestTransfer.rawAmount || '?') : _fmtShort(Number(d.largestTransfer) / 1e18); + h += `
Largest: ${lg} tokens
`; + } + + for (const t of transfers.slice(0, 12)) { + const amt = t.amount || _fmtShort(Number(t.rawAmount || 0) / 1e18); + h += '
'; + h += `${shortAddr(t.from)}`; + h += '->'; + h += `${shortAddr(t.to)}`; + h += `${amt}`; + h += '
'; + } + if (transfers.length === 0) h += '
No transfers in recent blocks
'; + return h; + } + afterRender(body) { _bindSearch(body, this, 'tf-addr', () => this.refresh()); } +} +customElements.define('token-flow-panel', TokenFlowPanel); + +// 39. Yield Finder +class YieldFinderPanel extends BasePanel { + get panelTitle() { return 'Yield Finder'; } + get skillLabel() { return 'MEFAI · YIELD'; } + constructor() { super(); this._refreshRate = 60000; } + async fetchData() { return bnbApi.mefaiYieldFinder(); } + renderContent(d) { + if (!d || d.error) return '
Scanning yield opportunities...
'; + let h = _MX + css('table'); + const opps = d.opportunities || []; + if (opps.length === 0) return h + '
No opportunities found
'; + + const best = opps[0]; + h += '
'; + h += `
Best Estimated APY
`; + h += `
${(best.estimatedApy||0).toFixed(1)}%
`; + h += `
${best.pair||''} on ${best.dex||''}
`; + h += '
'; + + h += ''; + opps.forEach((o, i) => { + const apyColor = o.estimatedApy > 100 ? '#0ecb81' : o.estimatedApy > 30 ? '#f0b90b' : 'var(--text)'; + const medal = i < 3 ? `${i+1}` : `${i+1}`; + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + }); + h += '
#PairAPY Est.VolumeLiqV/LBuy%
${medal}
${o.pair}
${o.estimatedApy?.toFixed(1)}%$${_fmtM(o.volume24h||0)}$${_fmtM(o.liquidity||0)}${o.vlRatio?.toFixed(2)||0}${(o.buyPressure||0).toFixed(0)}%
'; + h += '
APY estimated from trading fee revenue (0.3% fee). Not financial advice.
'; + return h; + } +} +customElements.define('yield-finder-panel', YieldFinderPanel); + +// 40. Risk Radar +class RiskRadarPanel extends BasePanel { + get panelTitle() { return 'Risk Radar'; } + get skillLabel() { return 'MEFAI · RISK'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return this._q ? bnbApi.mefaiRiskRadar(this._q) : null; } + renderContent(d) { + let h = _MX + _searchPanel('rr-addr', 'Address or contract (0x...)', 'Analyze', [ + { label: 'CAKE', val: EX.CAKE }, { label: 'WBNB', val: EX.WBNB }, { label: 'Dead', val: EX.DEAD }, + ], 'rr-addr'); + if (!d) return h + '
Comprehensive risk assessment: contract analysis, market health, danger patterns
'; + + const gc = { A:'#0ecb81', B:'#3cc68a', C:'#f0b90b', D:'#e8961e', F:'#f6465d' }; + const color = gc[d.grade] || '#848e9c'; + + h += '
'; + h += _gauge(d.riskScore||0, color, d.riskScore||0, d.grade||'?', 72); + h += '
'; + h += `
Grade ${d.grade} — ${d.riskScore}/100
`; + h += `
${d.isContract?'Smart Contract':'EOA Wallet'} · ${_fmtShort(d.codeSize||0)} bytes · ${d.txCount||0} txs
`; + h += `
Balance: ${(d.balance||0).toFixed(4)} BNB
`; + h += '
'; + + // Risks + const risks = d.risks || []; + const positives = d.positives || []; + if (positives.length > 0) { + h += '
SAFE INDICATORS
'; + for (const p of positives) { + h += `
✓ ${p}
`; + } + } + if (risks.length > 0) { + h += '
RISK FACTORS
'; + for (const r of risks) { + h += `
✗ ${r}
`; + } + } + + // Market data + if (d.marketData && d.marketData.price) { + const md = d.marketData; + h += '
'; + h += `
MARKET DATA
`; + h += `
Price$${md.price}
`; + h += `
Volume$${_fmtM(md.volume24h||0)}
`; + h += `
Liquidity$${_fmtM(md.liquidity||0)}
`; + h += '
'; + } + return h; + } + afterRender(body) { _bindSearch(body, this, 'rr-addr', () => this.refresh()); } +} +customElements.define('risk-radar-panel', RiskRadarPanel); + +// ═══════════════════════════════════════════════════════════════════ +// BATCH 4: 10 Advanced On-Chain Intelligence Panels (19-28) +// ═══════════════════════════════════════════════════════════════════ + +// 19. Sniper Detector — Detect early buyers / snipers on token launches +class SniperDetectorPanel extends BasePanel { + get panelTitle() { return 'Sniper Detector'; } + get skillLabel() { return 'MEFAI · SNIPER'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return this._q ? bnbApi.mefaiSniperDetector(this._q) : null; } + renderContent(d) { + let h = _MX + _searchPanel('snp-addr', 'Token address (0x...)', 'Scan', [ + { label: 'CAKE', val: EX.CAKE }, + { label: 'WBNB', val: EX.WBNB }, + ], 'snp-addr'); + if (!d) return h + '
Enter a token address to detect sniper bots and early buyers
'; + if (d.error) return h + `
${d.detail || d.error}
`; + + const score = d.sniperScore || 0; + const gColor = score > 70 ? '#f6465d' : score > 40 ? '#f0b90b' : '#0ecb81'; + const verdict = score > 70 ? 'HIGH RISK' : score > 40 ? 'MODERATE' : 'LOW RISK'; + const vClass = score > 70 ? 'mx-badge-danger' : score > 40 ? 'mx-badge-hot' : 'mx-badge-win'; + + h += '
'; + h += _gauge(score, gColor, score, 'SNIPER', 80); + h += '
'; + h += `
Sniper Score
`; + h += `${verdict}`; + h += `
${d.tokenName || 'Token'} · ${d.totalBuyers || 0} early buyers analyzed
`; + h += '
'; + + // Stats row + h += '
'; + h += `
BLOCK 0 BUYS
${d.block0Buys || 0}
`; + h += `
BOT WALLETS
${d.botWallets || 0}
`; + h += `
DUMPED
${d.dumpedCount || 0}
`; + h += '
'; + + // Early buyers list + const buyers = d.earlyBuyers || []; + if (buyers.length > 0) { + h += '
EARLY BUYERS
'; + for (const b of buyers.slice(0, 12)) { + const statusColor = b.status === 'DUMPED' ? '#f6465d' : b.status === 'HOLDING' ? '#0ecb81' : '#f0b90b'; + const statusBg = b.status === 'DUMPED' ? 'rgba(246,70,93,.12)' : b.status === 'HOLDING' ? 'rgba(14,203,129,.12)' : 'rgba(240,185,11,.12)'; + h += '
'; + h += `${shortAddr(b.address || '')}`; + h += `${b.status || 'UNKNOWN'}`; + h += `Block +${b.blockDelay || 0}`; + h += `${b.amount ? _fmtShort(b.amount) : '?'}`; + h += '
'; + } + } + return h; + } + afterRender(body) { _bindSearch(body, this, 'snp-addr', () => this.refresh()); } +} +customElements.define('sniper-detector-panel', SniperDetectorPanel); + +// 20. Wallet Cluster — Find related wallets via on-chain patterns +class WalletClusterPanel extends BasePanel { + get panelTitle() { return 'Wallet Cluster'; } + get skillLabel() { return 'MEFAI · CLUSTER'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return this._q ? bnbApi.mefaiWalletCluster(this._q) : null; } + renderContent(d) { + let h = _MX + _searchPanel('wc-addr', 'Wallet address (0x...)', 'Cluster', [ + { label: 'Binance Hot', val: EX.BINANCE_HOT }, + { label: 'CZ Wallet', val: EX.CZ_WALLET }, + ], 'wc-addr'); + if (!d) return h + '
Enter a wallet address to discover related wallet clusters
'; + if (d.error) return h + `
${d.detail || d.error}
`; + + // Seed wallet hero + h += '
'; + h += `
SEED WALLET
`; + h += `
${d.seedAddress || this._q}
`; + h += `
`; + h += `
Cluster Size
${d.clusterSize || 0}
`; + h += `
Total Value
$${_fmtM(d.totalValue || 0)}
`; + h += `
Shared Tokens
${d.sharedTokens || 0}
`; + h += '
'; + + // Cluster wallets + const wallets = d.wallets || []; + if (wallets.length > 0) { + h += '
CLUSTER WALLETS
'; + for (const w of wallets.slice(0, 10)) { + h += '
'; + h += `
`; + h += `${shortAddr(w.address || '')}`; + h += `$${_fmtM(w.value || 0)}`; + h += '
'; + h += `
Link: ${w.linkType || 'funding'} · TXs: ${w.txCount || 0} · Similarity: ${w.similarity || 0}%
`; + const tokens = w.sharedTokens || []; + if (tokens.length > 0) { + h += '
'; + for (const t of tokens.slice(0, 5)) { + h += `${t}`; + } + h += '
'; + } + h += '
'; + } + } + return h; + } + afterRender(body) { _bindSearch(body, this, 'wc-addr', () => this.refresh()); } +} +customElements.define('wallet-cluster-panel', WalletClusterPanel); + +// 21. Honeypot Check — Detect honeypot / rug pull tokens +class HoneypotCheckPanel extends BasePanel { + get panelTitle() { return 'Honeypot Check'; } + get skillLabel() { return 'MEFAI · HONEYPOT'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return this._q ? bnbApi.mefaiHoneypotCheck(this._q) : null; } + renderContent(d) { + let h = _MX + _searchPanel('hp-addr', 'Token address (0x...)', 'Check', [ + { label: 'CAKE', val: EX.CAKE }, + { label: 'USDT', val: EX.USDT }, + ], 'hp-addr'); + if (!d) return h + '
Enter a token address to check for honeypot / rug pull risks
'; + if (d.error) return h + `
${d.detail || d.error}
`; + + const score = d.trapScore || 0; + const gColor = score > 70 ? '#f6465d' : score > 40 ? '#f0b90b' : '#0ecb81'; + const verdict = score > 70 ? 'DANGER' : score > 40 ? 'CAUTION' : 'SAFE'; + const vClass = score > 70 ? 'mx-badge-danger' : score > 40 ? 'mx-badge-hot' : 'mx-badge-win'; + + // Big gauge center + h += '
'; + h += `
${_gauge(score, gColor, score, 'TRAP', 100)}
`; + h += `
${verdict}
`; + h += `
${d.tokenName || 'Token'} · ${d.tokenSymbol || '?'}
`; + h += '
'; + + // Risk factors + const risks = d.riskFactors || []; + if (risks.length > 0) { + h += '
RISK FACTORS
'; + for (const r of risks) { + const rPct = r.severity || 0; + const rColor = rPct > 70 ? '#f6465d' : rPct > 40 ? '#f0b90b' : '#0ecb81'; + h += '
'; + h += `${r.name || 'Risk'}`; + h += `
`; + h += `${rPct}%`; + h += '
'; + } + } + + // Checks grid + const checks = d.checks || {}; + h += '
'; + const checkItems = [ + ['Buy Tax', checks.buyTax], + ['Sell Tax', checks.sellTax], + ['Can Sell', checks.canSell], + ['Ownership', checks.ownershipRenounced], + ['Liquidity Lock', checks.liquidityLocked], + ['Verified', checks.verified], + ]; + for (const [lbl, val] of checkItems) { + const isGood = val === true || val === 'Yes' || val === 'Renounced' || val === 'Locked' || val === 'Verified' || (typeof val === 'number' && val < 10); + const ic = isGood ? '#0ecb81' : '#f6465d'; + const display = typeof val === 'number' ? val + '%' : (val === true ? 'Yes' : val === false ? 'No' : (val || '?')); + h += `
`; + h += `${lbl}`; + h += `${display}`; + h += '
'; + } + h += '
'; + return h; + } + afterRender(body) { _bindSearch(body, this, 'hp-addr', () => this.refresh()); } +} +customElements.define('honeypot-check-panel', HoneypotCheckPanel); + +// 22. Validator Map — BSC validator network overview +class ValidatorMapPanel extends BasePanel { + get panelTitle() { return 'Validator Map'; } + get skillLabel() { return 'MEFAI · VALIDATORS'; } + constructor() { super(); this._refreshRate = 30000; } + async fetchData() { return bnbApi.mefaiValidatorMap(); } + renderContent(d) { + if (!d || d.error) return '
Loading validator data...
'; + let h = _MX; + + // Network stats hero + h += '
'; + h += `
Active
${d.activeValidators || 21}
`; + h += `
Gas Util
${(d.avgGasUtil || 0).toFixed(1)}%
`; + h += `
Block Time
${(d.avgBlockTime || 3).toFixed(1)}s
`; + h += '
'; + + // Validator grid + const validators = d.validators || []; + if (validators.length > 0) { + h += '
VALIDATORS ' + validators.length + '
'; + h += '
'; + for (const v of validators.slice(0, 21)) { + const util = v.gasUtil || 0; + const borderColor = util > 80 ? '#f6465d' : util > 50 ? '#f0b90b' : '#0ecb81'; + const pulseColor = v.active !== false ? '#0ecb81' : '#f6465d'; + h += `
`; + h += `
`; + h += ``; + h += `${v.label || v.name || shortAddr(v.address || '')}`; + h += '
'; + h += `
${_fmtShort(v.blocksProduced || 0)} blk
`; + h += `
${util.toFixed(1)}% gas
`; + h += '
'; + } + h += '
'; + } + return h; + } +} +customElements.define('validator-map-panel', ValidatorMapPanel); + +// 23. DEX Arbitrage — Cross-DEX spread opportunities +class DexArbPanel extends BasePanel { + get panelTitle() { return 'DEX Arbitrage'; } + get skillLabel() { return 'MEFAI · ARB'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return bnbApi.mefaiDexArb(this._q); } + renderContent(d) { + let h = _MX + _searchPanel('arb-addr', 'Token address (optional)', 'Scan', [ + { label: 'CAKE', val: EX.CAKE }, + { label: 'All Pairs', val: '' }, + ], 'arb-addr'); + if (!d) return h + '
Scan for cross-DEX arbitrage opportunities on BSC
'; + if (d.error) return h + `
${d.detail || d.error}
`; + + // Best spread hero + const best = d.bestOpportunity || d.opportunities?.[0] || {}; + if (best.spread) { + h += '
'; + h += `
BEST SPREAD
`; + h += `
${(best.spread || 0).toFixed(2)}%
`; + h += `
${best.token || '?'}: ${best.cheapDex || '?'} → ${best.expensiveDex || '?'} · Est. $${_fmtShort(best.estProfit || 0)}
`; + h += '
'; + } + + // Opportunities table + const opps = d.opportunities || []; + if (opps.length > 0) { + h += css('table'); + h += '
'; + h += ''; + h += ''; + for (const o of opps.slice(0, 15)) { + const spreadColor = (o.spread || 0) >= 1 ? '#0ecb81' : (o.spread || 0) >= 0.3 ? '#f0b90b' : 'var(--text-muted)'; + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + } + h += '
TokenBuySellSpreadProfit
${o.token || o.symbol || '?'}${o.cheapDex || '?'}${o.expensiveDex || '?'}${(o.spread || 0).toFixed(2)}%$${_fmtShort(o.estProfit || 0)}
'; + } + + // Summary + if (d.totalOpportunities) { + h += `
${d.totalOpportunities} opportunities found · Avg spread ${(d.avgSpread || 0).toFixed(2)}%
`; + } + return h; + } + afterRender(body) { _bindSearch(body, this, 'arb-addr', () => this.refresh()); } +} +customElements.define('dex-arb-panel', DexArbPanel); + +// 24. Token Birth — Deep origin analysis for any token +class TokenBirthPanel extends BasePanel { + get panelTitle() { return 'Token Birth'; } + get skillLabel() { return 'MEFAI · BIRTH'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return this._q ? bnbApi.mefaiTokenBirth(this._q) : null; } + renderContent(d) { + let h = _MX + _searchPanel('tb-addr', 'Token address (0x...)', 'Analyze', [ + { label: 'CAKE', val: EX.CAKE }, + { label: 'WBNB', val: EX.WBNB }, + ], 'tb-addr'); + if (!d) return h + '
Enter a token address to trace its creation and origin
'; + if (d.error) return h + `
${d.detail || d.error}
`; + + // Token hero with logo + const logo = d.address ? tokenLogo(d.address) : ''; + h += '
'; + if (logo) h += ``; + h += '
'; + h += `
${d.tokenName || 'Token'} ${d.tokenSymbol || ''}
`; + const ageDays = d.ageDays || 0; + const ageLabel = ageDays > 365 ? Math.floor(ageDays / 365) + ' years' : ageDays + ' days'; + h += `Born ${ageLabel} ago`; + h += `
Block #${_fmt(d.creationBlock || 0)} · ${d.creationDate || ''}
`; + h += '
'; + + // Creator wallet card + const creator = d.creator || {}; + h += '
'; + h += `
CREATOR WALLET
`; + h += `
${creator.address || d.creatorAddress || '?'}
`; + h += '
'; + h += `
BNB
${(creator.bnbBalance || 0).toFixed(2)}
`; + h += `
TXs
${_fmtShort(creator.txCount || 0)}
`; + h += `
Holds
${(creator.holdPct || 0).toFixed(1)}%
`; + h += '
'; + + // Supply breakdown bars + const supply = d.supply || {}; + h += '
SUPPLY DISTRIBUTION
'; + const circ = supply.circulatingPct || 0; + const burned = supply.burnedPct || 0; + const owner = supply.ownerPct || 0; + h += '
'; + if (circ > 0) h += `
`; + if (burned > 0) h += `
`; + if (owner > 0) h += `
`; + h += '
'; + h += '
'; + h += `Circ ${circ}%`; + h += `Burned ${burned}%`; + h += `Owner ${owner}%`; + h += '
'; + + // Market stats + const mkt = d.market || {}; + h += '
'; + h += `
Price
$${mkt.price || '?'}
`; + h += `
Volume 24h
$${_fmtM(mkt.volume24h || 0)}
`; + h += `
Liquidity
$${_fmtM(mkt.liquidity || 0)}
`; + h += `
Pairs
${mkt.pairCount || 0}
`; + h += '
'; + return h; + } + afterRender(body) { _bindSearch(body, this, 'tb-addr', () => this.refresh()); } +} +customElements.define('token-birth-panel', TokenBirthPanel); + +// 25. Network Pulse — Real-time BSC network health +class NetworkPulsePanel extends BasePanel { + get panelTitle() { return 'Network Pulse'; } + get skillLabel() { return 'MEFAI · PULSE'; } + constructor() { super(); this._refreshRate = 10000; } + async fetchData() { return bnbApi.mefaiNetworkPulse(); } + renderContent(d) { + if (!d || d.error) return '
Loading network pulse...
'; + let h = _MX; + + const pressure = d.pressure || 0; + const pColor = pressure > 80 ? '#f6465d' : pressure > 50 ? '#f0b90b' : '#0ecb81'; + const rec = pressure > 80 ? 'CONGESTED' : pressure > 50 ? 'BUSY' : 'OPTIMAL'; + const recClass = pressure > 80 ? 'mx-badge-danger' : pressure > 50 ? 'mx-badge-hot' : 'mx-badge-win'; + + // Big pressure gauge + h += '
'; + h += `
${_gauge(pressure, pColor, pressure, 'PRESSURE', 90)}
`; + h += `
${rec}
`; + h += '
'; + + // Mini stats + h += '
'; + h += `
GAS UTIL
${(d.gasUtilization || 0).toFixed(1)}%
`; + h += `
BLOCK
${(d.blockTime || 3).toFixed(1)}s
`; + h += `
TPS
${_fmtShort(d.tps || 0)}
`; + h += `
GAS
${d.gasPrice || '?'} Gwei
`; + h += '
'; + + // Recent blocks as colored cells + const blocks = d.recentBlocks || []; + if (blocks.length > 0) { + h += '
RECENT BLOCKS
'; + h += '
'; + for (const b of blocks.slice(0, 20)) { + const bUtil = b.gasUtil || b.gasUtilization || 0; + const bColor = bUtil > 80 ? '#f6465d' : bUtil > 50 ? '#f0b90b' : '#0ecb81'; + h += `
${bUtil.toFixed(0)}
`; + } + h += '
'; + } + return h; + } +} +customElements.define('network-pulse-panel', NetworkPulsePanel); + +// 26. Copy Trade — Alpha wallet signal feed +class CopyTradePanel extends BasePanel { + get panelTitle() { return 'Copy Trade'; } + get skillLabel() { return 'MEFAI · COPY'; } + constructor() { super(); this._refreshRate = 15000; } + async fetchData() { return bnbApi.mefaiCopyTrade(); } + renderContent(d) { + if (!d || d.error) return '
Loading copy trade signals...
'; + let h = _MX; + + // Hero + const activeCount = d.activeWallets || 0; + h += '
'; + h += `
ALPHA WALLETS
${activeCount}
`; + h += `
Signals 24h
${d.signals24h || 0}
`; + h += '
'; + + // Signal feed + const signals = d.signals || []; + if (signals.length > 0) { + h += '
LIVE SIGNALS
'; + for (const s of signals.slice(0, 12)) { + const isBuy = (s.action || '').toUpperCase() === 'BUY'; + const actionColor = isBuy ? '#0ecb81' : '#f6465d'; + const actionBg = isBuy ? 'rgba(14,203,129,.12)' : 'rgba(246,70,93,.12)'; + const logo = s.tokenAddress ? tokenLogo(s.tokenAddress) : ''; + h += '
'; + h += `${s.walletLabel || shortAddr(s.wallet || '')}`; + h += `${(s.action || 'BUY').toUpperCase()}`; + if (logo) h += ``; + h += `${s.tokenName || s.token || '?'}`; + h += `$${_fmtShort(s.amount || 0)}`; + h += `${s.timeAgo || '?'}`; + h += '
'; + } + } else { + h += '
No recent signals detected
'; + } + return h; + } +} +customElements.define('copy-trade-panel', CopyTradePanel); + +// 27. Upgrade Monitor — Proxy contract upgrade detection +class UpgradeMonitorPanel extends BasePanel { + get panelTitle() { return 'Upgrade Monitor'; } + get skillLabel() { return 'MEFAI · UPGRADE'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return this._q ? bnbApi.mefaiUpgradeMonitor(this._q) : null; } + renderContent(d) { + let h = _MX + _searchPanel('um-addr', 'Contract address (0x...)', 'Check', [ + { label: 'PancakeRouter', val: EX.PANCAKE_ROUTER }, + { label: 'CAKE', val: EX.CAKE }, + ], 'um-addr'); + if (!d) return h + '
Enter a contract address to check proxy / upgrade status
'; + if (d.error) return h + `
${d.detail || d.error}
`; + + const isProxy = d.isProxy || false; + const vClass = isProxy ? 'mx-badge-hot' : 'mx-badge-win'; + const vText = isProxy ? 'PROXY' : 'NOT PROXY'; + + // Verdict + h += '
'; + h += `${vText}`; + h += `
${d.contractName || shortAddr(this._q || '')}
`; + h += '
'; + + if (isProxy) { + // Proxy details + h += '
'; + h += '
'; + h += `
IMPLEMENTATION
${d.implementation || '?'}
`; + h += '
'; + h += `
Admin: ${shortAddr(d.admin || '')}
`; + h += `
Proxy Type: ${d.proxyType || 'Unknown'}
`; + h += '
'; + + // Upgrade risk gauge + const risk = d.upgradeRisk || 0; + const rColor = risk > 70 ? '#f6465d' : risk > 40 ? '#f0b90b' : '#0ecb81'; + h += '
'; + h += _gauge(risk, rColor, risk, 'RISK', 56); + h += '
'; + h += `
UPGRADE RISK
`; + h += `
${d.riskDescription || 'Admin can modify contract logic'}
`; + h += '
'; + + // Upgrade history + const history = d.upgradeHistory || []; + if (history.length > 0) { + h += '
UPGRADE HISTORY
'; + for (const u of history.slice(0, 5)) { + h += `
`; + h += `${u.date || '?'}`; + h += `${shortAddr(u.newImpl || '')}`; + h += '
'; + } + } + } + + // Market context + const mkt = d.market || {}; + if (mkt.price) { + h += '
'; + h += `
MARKET CONTEXT
`; + h += `
Price$${mkt.price}
`; + h += `
Volume$${_fmtM(mkt.volume24h || 0)}
`; + h += '
'; + } + return h; + } + afterRender(body) { _bindSearch(body, this, 'um-addr', () => this.refresh()); } +} +customElements.define('upgrade-monitor-panel', UpgradeMonitorPanel); + +// 28. Portfolio Heatmap — Visual portfolio analysis +class PortfolioHeatmapPanel extends BasePanel { + get panelTitle() { return 'Portfolio Heatmap'; } + get skillLabel() { return 'MEFAI · PORTFOLIO'; } + constructor() { super(); this._refreshRate = 0; } + async fetchData() { return this._q ? bnbApi.mefaiPortfolioHeatmap(this._q) : null; } + renderContent(d) { + let h = _MX + _searchPanel('ph-addr', 'Wallet address (0x...)', 'Analyze', [ + { label: 'Binance Hot', val: EX.BINANCE_HOT }, + { label: 'CZ Wallet', val: EX.CZ_WALLET }, + ], 'ph-addr'); + if (!d) return h + '
Enter a wallet address to visualize portfolio as a heatmap
'; + if (d.error) return h + `
${d.detail || d.error}
`; + + // Hero: total value, P&L, health + const pnl = d.pnl24h || 0; + const pnlColor = pnl >= 0 ? '#0ecb81' : '#f6465d'; + const health = d.healthScore || 0; + const hColor = health > 70 ? '#0ecb81' : health > 40 ? '#f0b90b' : '#f6465d'; + + h += '
'; + h += '
'; + h += `
TOTAL VALUE
`; + h += `
$${_fmtM(d.totalValue || 0)}
`; + h += `
${pnl >= 0 ? '+' : ''}$${_fmtShort(Math.abs(pnl))} (${_chgFmt(d.pnl24hPct)})
`; + h += '
'; + h += _gauge(health, hColor, health, 'HEALTH', 56); + h += '
'; + + // Holdings heatmap grid + const holdings = d.holdings || []; + if (holdings.length > 0) { + h += '
HOLDINGS HEATMAP
'; + const maxVal = Math.max(...holdings.map(x => x.value || 0), 1); + h += '
'; + for (const t of holdings.slice(0, 20)) { + const chg = t.change24h || 0; + const bg = chg >= 5 ? 'rgba(14,203,129,.35)' : chg >= 0 ? 'rgba(14,203,129,.15)' : chg >= -5 ? 'rgba(246,70,93,.15)' : 'rgba(246,70,93,.35)'; + const tc = chg >= 0 ? '#0ecb81' : '#f6465d'; + const sizePct = Math.max(40, Math.min(100, ((t.value || 0) / maxVal) * 100)); + h += `
`; + h += `
${t.symbol || '?'}
`; + h += `
$${_fmtShort(t.value || 0)}
`; + h += `
${chg >= 0 ? '+' : ''}${chg.toFixed(1)}%
`; + h += '
'; + } + h += '
'; + + // Concentration bar + const top3 = holdings.slice(0, 3); + const totalVal = d.totalValue || 1; + h += '
CONCENTRATION
'; + h += '
'; + const concColors = ['#f0b90b', '#0ecb81', '#627eea']; + for (let i = 0; i < top3.length; i++) { + const pct = ((top3[i].value || 0) / totalVal * 100).toFixed(1); + h += `
`; + } + h += '
'; + h += '
'; + for (let i = 0; i < top3.length; i++) { + const pct = ((top3[i].value || 0) / totalVal * 100).toFixed(1); + h += `${top3[i].symbol || '?'} ${pct}%`; + } + h += '
'; + + // Mini table + h += css('table'); + h += '
'; + h += ''; + h += ''; + for (const t of holdings.slice(0, 10)) { + const chg = t.change24h || 0; + const pct = ((t.value || 0) / totalVal * 100).toFixed(1); + h += ''; + h += ``; + h += ``; + h += ``; + h += ``; + h += ''; + } + h += '
TokenValue24h%
${t.symbol || '?'}$${_fmtShort(t.value || 0)}${chg >= 0 ? '+' : ''}${chg.toFixed(1)}%${pct}%
'; + } + return h; + } + afterRender(body) { _bindSearch(body, this, 'ph-addr', () => this.refresh()); } +} +customElements.define('portfolio-heatmap-panel', PortfolioHeatmapPanel); diff --git a/skills/alpha/chain-dominance/SKILL.md b/skills/alpha/chain-dominance/SKILL.md new file mode 100644 index 0000000..2fd6db4 --- /dev/null +++ b/skills/alpha/chain-dominance/SKILL.md @@ -0,0 +1,152 @@ +--- +name: chain-dominance +description: | + Composite BSC dominance score vs other L1/L2 chains. Weighs TVL (40%), + DEX volume (30%), fees (20%), and cost efficiency (10%) to produce a + 0-100 score. Use to benchmark BSC's competitive position in the + multi-chain landscape. +metadata: + author: mefai + version: "1.0" +--- + +# Chain Dominance Index + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Chain Dominance Index | Composite BSC score vs other L1/L2 chains weighted by TVL, volume, fees, and efficiency | Benchmark BSC's competitive position across chains | + +## Use Cases + +1. **BSC Positioning**: Understand where BSC ranks among competing L1/L2 chains +2. **Capital Rotation Signals**: Rising dominance score signals capital inflow; falling score may indicate rotation to other chains +3. **Multi-Chain Comparison**: Compare TVL, DEX volume, and fee generation across Ethereum, BSC, Arbitrum, Solana, and more +4. **Ecosystem Health**: Track whether BSC is gaining or losing market share over time + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Chain Dominance Index + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/chain-dominance +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns BSC dominance score and multi-chain comparison | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/chain-dominance' +``` + +**Response Example**: +```json +{ + "dominanceScore": 72, + "bscRank": 3, + "bscTvl": 5800000000, + "bscDominance": 5.2, + "bscDailyFees": 4250000, + "bscDexVolume": 850000000, + "chains": [ + { + "chain": "Ethereum", + "symbol": "ETH", + "tvl": 62000000000, + "dominance": 55.8, + "dailyFees": 18500000, + "dexVolume": 3200000000 + }, + { + "chain": "BSC", + "symbol": "BNB", + "tvl": 5800000000, + "dominance": 5.2, + "dailyFees": 4250000, + "dexVolume": 850000000 + }, + { + "chain": "Arbitrum", + "symbol": "ARB", + "tvl": 4200000000, + "dominance": 3.8, + "dailyFees": 1800000, + "dexVolume": 620000000 + }, + { + "chain": "Solana", + "symbol": "SOL", + "tvl": 8500000000, + "dominance": 7.6, + "dailyFees": 5200000, + "dexVolume": 2100000000 + } + ], + "scoreBreakdown": { + "tvlScore": 28.8, + "dexVolumeScore": 21.6, + "feesScore": 14.4, + "costEfficiencyScore": 7.2 + } +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| dominanceScore | number | Composite BSC score (0-100) | +| bscRank | number | BSC rank among all chains by composite score | +| bscTvl | number | BSC total value locked (USD) | +| bscDominance | number | BSC TVL as percentage of total DeFi TVL | +| bscDailyFees | number | BSC total daily fees (USD) | +| bscDexVolume | number | BSC total 24h DEX volume (USD) | +| chains | array | Top chains sorted by TVL | +| chains[].chain | string | Chain name | +| chains[].symbol | string | Native token symbol | +| chains[].tvl | number | Chain TVL (USD) | +| chains[].dominance | number | TVL market share percentage | +| chains[].dailyFees | number | 24h fee revenue (USD) | +| chains[].dexVolume | number | 24h DEX trading volume (USD) | +| scoreBreakdown | object | Component scores that sum to dominanceScore | +| scoreBreakdown.tvlScore | number | TVL component (max 40 points) | +| scoreBreakdown.dexVolumeScore | number | DEX volume component (max 30 points) | +| scoreBreakdown.feesScore | number | Fees component (max 20 points) | +| scoreBreakdown.costEfficiencyScore | number | Cost efficiency component (max 10 points) | + +--- + +## Scoring Methodology + +| Component | Weight | Source | Calculation | +|-----------|--------|--------|-------------| +| TVL | 40% | DefiLlama chains | BSC TVL rank percentile among top 20 chains | +| DEX Volume | 30% | DefiLlama DEX volumes | BSC DEX volume rank percentile | +| Fees | 20% | DefiLlama fees overview | BSC fee generation rank percentile | +| Cost Efficiency | 10% | BSC RPC block data | Inverse of avg gas cost relative to peers | + +## Data Sources + +1. DefiLlama Chains: TVL data for all L1/L2 chains +2. DefiLlama Fees Overview: Daily fee generation per chain +3. DefiLlama DEX Volumes: 24h DEX trading volume per chain +4. BSC RPC: Block height and gas price for cost efficiency calculation + +## Notes + +1. Dominance score is relative — it measures BSC's position vs the top 20 chains +2. Responses are cached with 300s fresh TTL +3. No authentication required +4. Score of 50 means average among tracked chains; above 70 is strong positioning diff --git a/skills/alpha/copy-trade/SKILL.md b/skills/alpha/copy-trade/SKILL.md new file mode 100644 index 0000000..0816c37 --- /dev/null +++ b/skills/alpha/copy-trade/SKILL.md @@ -0,0 +1,108 @@ +--- +name: copy-trade +description: | + Alpha wallet activity monitor for BSC. Tracks known high-performance + wallets for recent token interactions, swaps, and position changes. + Use to discover alpha by following successful traders. +metadata: + author: mefai + version: "1.0" +--- + +# Copy Trade + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Copy Trade | Monitor known alpha wallets for recent token interactions and trading activity | Discover trading opportunities by following successful wallets | + +## Use Cases + +1. **Alpha Discovery**: Track what tokens top-performing wallets are buying or selling +2. **Trend Detection**: Spot emerging trends by aggregating activity across multiple alpha wallets +3. **Entry Timing**: Observe wallet activity patterns to time entries on tokens of interest + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Copy Trade + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/copy-trade +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns latest activity from tracked wallets | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/copy-trade' +``` + +**Response Example**: +```json +{ + "tracked_wallets": 12, + "recent_activity": [ + { + "wallet": "0x8894...02b", + "wallet_label": "Binance Hot Wallet", + "action": "BUY", + "token": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "token_symbol": "CAKE", + "amount_usd": 125000.0, + "amount_tokens": 48076.92, + "tx_hash": "0xabc123...", + "block": 46250100, + "age_minutes": 12 + } + ], + "hot_tokens": [ + { + "token": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "symbol": "CAKE", + "wallet_count": 4, + "net_flow_usd": 350000.0, + "sentiment": "BULLISH" + } + ], + "last_updated": "2026-03-08T14:30:00Z" +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| tracked_wallets | number | Number of alpha wallets being monitored | +| recent_activity | array | Recent transactions from tracked wallets | +| recent_activity[].wallet | string | Wallet address (truncated) | +| recent_activity[].wallet_label | string | Known label for the wallet | +| recent_activity[].action | string | BUY, SELL, or TRANSFER | +| recent_activity[].token_symbol | string | Token symbol traded | +| recent_activity[].amount_usd | number | Transaction value in USD | +| recent_activity[].age_minutes | number | Minutes since transaction | +| hot_tokens | array | Tokens with activity from multiple tracked wallets | +| hot_tokens[].wallet_count | number | How many tracked wallets are active in this token | +| hot_tokens[].net_flow_usd | number | Net USD flow (positive = buying) | +| hot_tokens[].sentiment | string | Aggregate sentiment: BULLISH, BEARISH, or NEUTRAL | +| last_updated | string | ISO 8601 timestamp of last data refresh | + +--- + +## Notes + +1. Monitors known BSC wallets via JSON-RPC transaction and log queries +2. Responses are cached with 15s fresh TTL for near-real-time data +3. No authentication required +4. Tracked wallets include known whale wallets, fund wallets, and historically high-performance addresses diff --git a/skills/alpha/smart-money/SKILL.md b/skills/alpha/smart-money/SKILL.md new file mode 100644 index 0000000..c5330cd --- /dev/null +++ b/skills/alpha/smart-money/SKILL.md @@ -0,0 +1,105 @@ +--- +name: smart-money +description: | + Whale transaction tracker for BSC. Identifies large-value transfers in + real-time by monitoring recent blocks for high-value BNB and token movements. + Use to detect whale accumulation or distribution. +metadata: + author: mefai + version: "1.0" +--- + +# Smart Money + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Smart Money | Real-time whale transaction detection — identifies large-value BNB and token transfers from recent blocks | Detect whale accumulation and distribution | + +## Use Cases + +1. **Whale Watching**: Monitor large-value transactions to detect whale accumulation or distribution +2. **Market Sentiment**: Gauge market direction from aggregate whale activity +3. **Early Warning**: Detect large exchange deposits that may precede selling pressure + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Smart Money + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/smart-money +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns recent whale transactions | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/smart-money' +``` + +**Response Example**: +```json +{ + "whale_txs": [ + { + "hash": "0xdef456...", + "from": "0x1234...5678", + "to": "0xabcd...ef01", + "value_bnb": 5200.0, + "value_usd": 1560000.0, + "block": 46250095, + "age_seconds": 45, + "type": "TRANSFER", + "from_label": "Unknown Whale", + "to_label": "Binance Deposit" + } + ], + "summary": { + "total_whale_txs": 15, + "total_volume_usd": 8420000.0, + "net_exchange_flow_usd": -2100000.0, + "largest_tx_usd": 3200000.0, + "blocks_scanned": 10 + }, + "trend": "NET OUTFLOW" +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| whale_txs | array | List of whale transactions | +| whale_txs[].hash | string | Transaction hash | +| whale_txs[].from | string | Sender address | +| whale_txs[].to | string | Recipient address | +| whale_txs[].value_bnb | number | Transaction value in BNB | +| whale_txs[].value_usd | number | Transaction value in USD | +| whale_txs[].age_seconds | number | Seconds since transaction | +| whale_txs[].type | string | TRANSFER, SWAP, or CONTRACT_CALL | +| whale_txs[].from_label | string | Known label for sender | +| whale_txs[].to_label | string | Known label for recipient | +| summary | object | Aggregate whale activity metrics | +| summary.net_exchange_flow_usd | number | Net flow to/from exchanges (negative = outflow) | +| trend | string | NET INFLOW, NET OUTFLOW, or BALANCED | + +--- + +## Notes + +1. Scans recent blocks via BSC JSON-RPC for transactions exceeding value thresholds +2. Responses are cached with 15s fresh TTL for near-real-time data +3. No authentication required +4. Whale threshold: transactions above 100 BNB or equivalent token value diff --git a/skills/alpha/sniper-detector/SKILL.md b/skills/alpha/sniper-detector/SKILL.md new file mode 100644 index 0000000..960ca91 --- /dev/null +++ b/skills/alpha/sniper-detector/SKILL.md @@ -0,0 +1,108 @@ +--- +name: sniper-detector +description: | + Detect bot sniping activity on BSC tokens. Analyzes early buyers in the + first blocks after liquidity is added, tracks hold/dump behavior, and + assigns a snipe score. Use to identify tokens targeted by MEV bots. +metadata: + author: mefai + version: "1.0" +--- + +# Sniper Detector + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Sniper Detector | Analyze early token buyers for bot sniping patterns, hold/dump tracking, snipe scoring | Identify MEV bot activity on BSC tokens | + +## Use Cases + +1. **Bot Activity Detection**: Identify tokens where sniper bots dominated early trading blocks +2. **Fair Launch Verification**: Assess whether a token launch was fair or captured by bots +3. **Token Quality Signal**: High snipe scores suggest sophisticated bot targeting, which may indicate pump-and-dump schemes + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Sniper Detector + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/sniper-detector +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| address | string | Yes | Token contract address (0x...) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/sniper-detector?address=0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82' +``` + +**Response Example**: +```json +{ + "address": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "symbol": "CAKE", + "snipe_score": 12, + "early_buyers": 8, + "total_analyzed": 50, + "snipers": [ + { + "address": "0x1234...abcd", + "block_offset": 0, + "buy_amount_bnb": 5.2, + "still_holding": false, + "sold_within_blocks": 15, + "profit_pct": 340.0, + "is_bot": true + } + ], + "summary": { + "bots_detected": 3, + "avg_hold_blocks": 22, + "pct_dumped_fast": 37.5, + "first_block_buys": 5 + }, + "verdict": "LOW SNIPE ACTIVITY" +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| address | string | Token contract address | +| symbol | string | Token symbol | +| snipe_score | number | Snipe activity score 0-100 | +| early_buyers | number | Number of buyers in first few blocks | +| total_analyzed | number | Total transactions analyzed | +| snipers | array | Detected sniper wallet details | +| snipers[].address | string | Sniper wallet address | +| snipers[].block_offset | number | Blocks after liquidity add when buy occurred | +| snipers[].buy_amount_bnb | number | Buy size in BNB | +| snipers[].still_holding | boolean | Whether wallet still holds the token | +| snipers[].sold_within_blocks | number | Blocks until first sell (null if still holding) | +| snipers[].profit_pct | number | Estimated profit percentage | +| snipers[].is_bot | boolean | Whether wallet shows bot characteristics | +| summary | object | Aggregate snipe activity metrics | +| verdict | string | Overall assessment of snipe activity level | + +--- + +## Notes + +1. Analyzes early transaction logs via BSC JSON-RPC +2. Responses are cached with 30s fresh TTL +3. No authentication required +4. A snipe_score above 70 indicates heavy bot activity at launch diff --git a/skills/alpha/wallet-cluster/SKILL.md b/skills/alpha/wallet-cluster/SKILL.md new file mode 100644 index 0000000..7424a87 --- /dev/null +++ b/skills/alpha/wallet-cluster/SKILL.md @@ -0,0 +1,107 @@ +--- +name: wallet-cluster +description: | + On-chain forensics tool for BSC. Discovers connected wallets via transfer + patterns, shared token holdings, and common funding sources. Use for + wallet attribution and Sybil detection. +metadata: + author: mefai + version: "1.0" +--- + +# Wallet Cluster + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Wallet Cluster | Discover connected wallets via transfer patterns, shared holdings, and common funding sources | On-chain forensics and Sybil detection | + +## Use Cases + +1. **Wallet Attribution**: Discover wallets that are likely controlled by the same entity +2. **Sybil Detection**: Identify airdrop farming clusters or wash trading rings +3. **Whale Profiling**: Map the full wallet constellation of a whale to understand their total exposure + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Wallet Cluster + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/wallet-cluster +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| address | string | Yes | Wallet address to analyze (0x...) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/wallet-cluster?address=0x8894E0a0c962CB723c1ef8a1B6737B4E1e2AE02b' +``` + +**Response Example**: +```json +{ + "address": "0x8894E0a0c962CB723c1ef8a1B6737B4E1e2AE02b", + "cluster_size": 5, + "connections": [ + { + "address": "0x2345...6789", + "relationship": "DIRECT_TRANSFER", + "transfer_count": 12, + "total_value_bnb": 450.0, + "shared_tokens": ["CAKE", "USDT", "BUSD"], + "confidence": 0.92 + }, + { + "address": "0x3456...7890", + "relationship": "COMMON_FUNDING", + "transfer_count": 3, + "total_value_bnb": 120.0, + "shared_tokens": ["USDT"], + "confidence": 0.78 + } + ], + "shared_holdings": [ + {"token": "CAKE", "wallets_holding": 4}, + {"token": "USDT", "wallets_holding": 5} + ], + "funding_sources": [ + {"address": "0xBinance...", "label": "Binance Hot Wallet", "funded_wallets": 3} + ] +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| address | string | Root wallet address analyzed | +| cluster_size | number | Total wallets in the discovered cluster | +| connections | array | Connected wallets with relationship details | +| connections[].relationship | string | DIRECT_TRANSFER, COMMON_FUNDING, or SHARED_HOLDINGS | +| connections[].transfer_count | number | Number of transfers between wallets | +| connections[].total_value_bnb | number | Total value transferred in BNB | +| connections[].shared_tokens | array | Tokens held by both wallets | +| connections[].confidence | number | Confidence score 0-1 that wallets are related | +| shared_holdings | array | Tokens held across multiple cluster wallets | +| funding_sources | array | Common funding source wallets | + +--- + +## Notes + +1. Analyzes transfer history and token holdings via BSC JSON-RPC +2. Responses are cached with 30s fresh TTL +3. No authentication required +4. Cluster discovery is limited to 2 hops from the root address to maintain response times diff --git a/skills/analytics/block-autopsy/SKILL.md b/skills/analytics/block-autopsy/SKILL.md new file mode 100644 index 0000000..a4a1602 --- /dev/null +++ b/skills/analytics/block-autopsy/SKILL.md @@ -0,0 +1,110 @@ +--- +name: block-autopsy +description: | + Block-level analysis for BSC. Breaks down gas distribution, transaction + types, top gas consumers, and validator information for any block. + Use for network analysis and MEV research. +metadata: + author: mefai + version: "1.0" +--- + +# Block Autopsy + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Block Autopsy | Gas distribution, transaction type breakdown, top gas consumers per block | Network analysis and MEV research | + +## Use Cases + +1. **Gas Analysis**: Understand gas distribution within a block to identify congestion patterns +2. **MEV Detection**: Identify sandwiching or front-running patterns by analyzing transaction ordering +3. **Validator Analysis**: Track which validators produce blocks and their gas utilization + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Block Autopsy + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/block-autopsy +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| number | string | No | Block number (decimal). Defaults to latest block. | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/block-autopsy?number=46250100' +``` + +**Response Example**: +```json +{ + "block_number": 46250100, + "timestamp": "2026-03-08T14:28:45Z", + "validator": "0x72b61c6014342d914470eC7aC2975bE345796c2b", + "validator_name": "NodeReal", + "tx_count": 185, + "gas_used": 42500000, + "gas_limit": 140000000, + "gas_utilization_pct": 30.4, + "tx_types": { + "transfers": 45, + "swaps": 78, + "contract_creation": 2, + "other": 60 + }, + "top_gas_consumers": [ + { + "address": "0x10ED43C718714eb63d5aA57B78B54917c3e6fE49", + "label": "PancakeSwap V2 Router", + "gas_used": 8500000, + "gas_share_pct": 20.0, + "tx_count": 42 + } + ], + "gas_price_stats": { + "min_gwei": 1.0, + "max_gwei": 10.0, + "avg_gwei": 3.0, + "median_gwei": 3.0 + } +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| block_number | number | Block number analyzed | +| timestamp | string | Block timestamp | +| validator | string | Validator (miner) address | +| validator_name | string | Known validator name | +| tx_count | number | Number of transactions in the block | +| gas_used | number | Total gas consumed | +| gas_limit | number | Block gas limit | +| gas_utilization_pct | number | Percentage of gas limit used | +| tx_types | object | Transaction type breakdown | +| top_gas_consumers | array | Contracts consuming the most gas | +| gas_price_stats | object | Gas price statistics across transactions | + +--- + +## Notes + +1. Fetches full block data via BSC JSON-RPC `eth_getBlockByNumber` with transaction details +2. Responses are cached with 15s fresh TTL +3. No authentication required +4. Defaults to the latest block if no number is specified diff --git a/skills/analytics/burn-tracker/SKILL.md b/skills/analytics/burn-tracker/SKILL.md new file mode 100644 index 0000000..7a9f436 --- /dev/null +++ b/skills/analytics/burn-tracker/SKILL.md @@ -0,0 +1,111 @@ +--- +name: burn-tracker +description: | + Real-time BNB and token burn tracking on BSC. Monitors the dead address + for incoming transfers and calculates USD valuations of burned tokens. + Use for deflationary token analysis. +metadata: + author: mefai + version: "1.0" +--- + +# Burn Engine + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Burn Tracker | Real-time BNB and token burn tracking with USD valuations | Deflationary token analysis | + +## Use Cases + +1. **Burn Verification**: Verify that a project is actually burning tokens as claimed +2. **Deflation Rate**: Calculate the deflation rate of a token based on burn history +3. **Value Destruction**: Track total USD value destroyed through token burns + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Burn Tracker + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/burn-tracker +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns current burn statistics | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/burn-tracker' +``` + +**Response Example**: +```json +{ + "dead_address": "0x000000000000000000000000000000000000dEaD", + "bnb_burned": 124500.32, + "bnb_burned_usd": 37350096.0, + "tokens": [ + { + "address": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "symbol": "CAKE", + "burned": 45200000, + "burned_usd": 106220000, + "pct_of_supply": 11.55, + "last_burn_block": 46249800 + }, + { + "address": "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", + "symbol": "BUSD", + "burned": 8500000, + "burned_usd": 8500000, + "pct_of_supply": 0.05, + "last_burn_block": 46248200 + } + ], + "total_burned_usd": 152070096, + "recent_burns": [ + { + "token_symbol": "CAKE", + "amount": 50000, + "amount_usd": 117500, + "block": 46249800, + "tx_hash": "0x123..." + } + ] +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| dead_address | string | The burn address monitored | +| bnb_burned | number | Total BNB sent to dead address | +| bnb_burned_usd | number | USD value of burned BNB | +| tokens | array | Top burned tokens with details | +| tokens[].symbol | string | Token symbol | +| tokens[].burned | number | Amount of tokens burned | +| tokens[].burned_usd | number | USD value of burned tokens | +| tokens[].pct_of_supply | number | Burned tokens as percentage of total supply | +| total_burned_usd | number | Total USD value of all burns | +| recent_burns | array | Most recent burn transactions | + +--- + +## Notes + +1. Monitors the standard dead address (0x...dEaD) via BSC JSON-RPC balance queries +2. Responses are cached with 120s fresh TTL +3. No authentication required +4. USD valuations use current token prices from DexScreener diff --git a/skills/analytics/portfolio-heatmap/SKILL.md b/skills/analytics/portfolio-heatmap/SKILL.md new file mode 100644 index 0000000..2c7d9df --- /dev/null +++ b/skills/analytics/portfolio-heatmap/SKILL.md @@ -0,0 +1,115 @@ +--- +name: portfolio-heatmap +description: | + Bloomberg-style portfolio heatmap for BSC wallets. Displays token holdings + with 24h performance coloring, allocation weights, and gain/loss metrics. + Use for visual portfolio performance analysis. +metadata: + author: mefai + version: "1.0" +--- + +# Portfolio Heatmap + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Portfolio Heatmap | Bloomberg-style portfolio view with 24h performance heatmap | Visual portfolio performance analysis | + +## Use Cases + +1. **Performance Overview**: Quickly see which holdings are gaining or losing in the last 24 hours +2. **Allocation Analysis**: Visualize portfolio concentration and diversification +3. **Risk Assessment**: Identify overweight positions in volatile tokens + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Portfolio Heatmap + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/portfolio-heatmap +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| address | string | Yes | Wallet address (0x...) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/portfolio-heatmap?address=0x8894E0a0c962CB723c1ef8a1B6737B4E1e2AE02b' +``` + +**Response Example**: +```json +{ + "address": "0x8894E0a0c962CB723c1ef8a1B6737B4E1e2AE02b", + "total_value_usd": 106904150.0, + "pnl_24h_usd": 1250000.0, + "pnl_24h_pct": 1.18, + "holdings": [ + { + "symbol": "USDT", + "value_usd": 45000000.0, + "weight_pct": 42.1, + "change_24h_pct": 0.01, + "pnl_24h_usd": 4500.0, + "heat": "NEUTRAL" + }, + { + "symbol": "BNB", + "value_usd": 37629150.0, + "weight_pct": 35.2, + "change_24h_pct": 2.1, + "pnl_24h_usd": 790212.0, + "heat": "HOT" + }, + { + "symbol": "CAKE", + "value_usd": 5875000.0, + "weight_pct": 5.5, + "change_24h_pct": -1.2, + "pnl_24h_usd": -70500.0, + "heat": "COLD" + } + ], + "best_performer": {"symbol": "BNB", "change_pct": 2.1}, + "worst_performer": {"symbol": "CAKE", "change_pct": -1.2} +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| address | string | Wallet address | +| total_value_usd | number | Total portfolio value | +| pnl_24h_usd | number | Portfolio P&L in last 24 hours | +| pnl_24h_pct | number | Portfolio P&L percentage | +| holdings | array | Token holdings with heatmap data | +| holdings[].symbol | string | Token symbol | +| holdings[].value_usd | number | Current holding value | +| holdings[].weight_pct | number | Percentage of total portfolio | +| holdings[].change_24h_pct | number | 24h price change | +| holdings[].pnl_24h_usd | number | 24h P&L in USD | +| holdings[].heat | string | Heatmap classification: HOT, WARM, NEUTRAL, COOL, COLD | +| best_performer | object | Best performing token in 24h | +| worst_performer | object | Worst performing token in 24h | + +--- + +## Notes + +1. Combines wallet balances from JSON-RPC with DexScreener price data +2. Responses are cached with 30s fresh TTL +3. No authentication required +4. Heat levels: HOT (>5%), WARM (>2%), NEUTRAL (-2% to 2%), COOL (<-2%), COLD (<-5%) diff --git a/skills/analytics/token-flow/SKILL.md b/skills/analytics/token-flow/SKILL.md new file mode 100644 index 0000000..670dca8 --- /dev/null +++ b/skills/analytics/token-flow/SKILL.md @@ -0,0 +1,114 @@ +--- +name: token-flow +description: | + Track token transfer movements on BSC from recent blocks. Shows transfer + volume, unique senders/receivers, and flow direction for any ERC20 token. + Use for token activity monitoring and flow analysis. +metadata: + author: mefai + version: "1.0" +--- + +# Token Flow + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Token Flow | Track ERC20 token transfer movements from recent blocks | Token activity monitoring and flow analysis | + +## Use Cases + +1. **Transfer Monitoring**: Track recent transfer activity for a specific token +2. **Flow Direction**: Determine net flow direction (accumulation vs distribution) +3. **Whale Transfer Detection**: Identify large transfers in recent blocks + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Token Flow + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/token-flow +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| contract | string | Yes | Token contract address (0x...) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/token-flow?contract=0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82' +``` + +**Response Example**: +```json +{ + "token": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "symbol": "CAKE", + "blocks_scanned": 20, + "transfers": [ + { + "from": "0x1234...5678", + "to": "0xabcd...ef01", + "amount": 12500.0, + "amount_usd": 29375.0, + "block": 46250100, + "tx_hash": "0xdef...", + "from_label": "Unknown", + "to_label": "PancakeSwap V2 Pair" + } + ], + "summary": { + "total_transfers": 145, + "total_volume": 2850000.0, + "total_volume_usd": 6697500.0, + "unique_senders": 112, + "unique_receivers": 98, + "largest_transfer": 125000.0, + "avg_transfer": 19655.17 + }, + "flow": { + "to_exchanges_usd": 1200000.0, + "from_exchanges_usd": 850000.0, + "net_exchange_flow_usd": -350000.0, + "direction": "NET OUTFLOW FROM EXCHANGES" + } +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| token | string | Token contract address | +| symbol | string | Token symbol | +| blocks_scanned | number | Number of recent blocks scanned | +| transfers | array | Recent transfer events | +| transfers[].amount | number | Transfer amount in token units | +| transfers[].amount_usd | number | Transfer value in USD | +| transfers[].from_label | string | Known label for sender | +| transfers[].to_label | string | Known label for recipient | +| summary | object | Aggregate transfer statistics | +| summary.total_transfers | number | Total number of transfers | +| summary.unique_senders | number | Unique sender addresses | +| flow | object | Net flow analysis | +| flow.net_exchange_flow_usd | number | Net flow to/from exchanges | +| flow.direction | string | Human-readable flow direction | + +--- + +## Notes + +1. Scans Transfer event logs via BSC JSON-RPC `eth_getLogs` +2. Responses are cached with 15s fresh TTL +3. No authentication required +4. Scans the most recent 20 blocks for transfer events diff --git a/skills/analytics/tx-decoder/SKILL.md b/skills/analytics/tx-decoder/SKILL.md new file mode 100644 index 0000000..ed1268d --- /dev/null +++ b/skills/analytics/tx-decoder/SKILL.md @@ -0,0 +1,123 @@ +--- +name: tx-decoder +description: | + Decode any BSC transaction into human-readable components. Extracts function + calls, event logs, token transfers, and gas breakdown. Use to understand + what a transaction actually did on-chain. +metadata: + author: mefai + version: "1.0" +--- + +# TX Decoder + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| TX Decoder | Decode BSC transactions: function calls, events, token transfers, gas breakdown | Understand on-chain transaction details | + +## Use Cases + +1. **Transaction Analysis**: Decode a transaction to understand what functions were called and what events were emitted +2. **Token Transfer Tracking**: Extract all token transfers within a transaction, including multi-hop swaps +3. **Gas Breakdown**: Analyze gas usage and cost of a transaction +4. **Debugging**: Understand why a transaction failed by examining the decoded data + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: TX Decoder + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/tx-decoder +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| hash | string | Yes | Transaction hash (0x..., 66 characters) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/tx-decoder?hash=0x8f2d0e779c3fcaa9f67403cf22c0afc3a51a7b1b6e7e5a7c0e3f6d1a2b3c4d5e' +``` + +**Response Example**: +```json +{ + "hash": "0x8f2d0e779c3fcaa9f67403cf22c0afc3a51a7b1b6e7e5a7c0e3f6d1a2b3c4d5e", + "status": "SUCCESS", + "block": 46250100, + "timestamp": "2026-03-08T14:28:45Z", + "from": "0x1234...5678", + "to": "0x10ED43C718714eb63d5aA57B78B54917c3e6fE49", + "value_bnb": 1.5, + "function": { + "selector": "0x7ff36ab5", + "name": "swapExactETHForTokens", + "params": { + "amountOutMin": "450000000000000000000", + "path": ["0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82"], + "to": "0x1234...5678", + "deadline": 1709913000 + } + }, + "token_transfers": [ + { + "token": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "symbol": "CAKE", + "from": "0x0eD7...4fD0", + "to": "0x1234...5678", + "amount": 478.23 + } + ], + "events": [ + { + "name": "Swap", + "contract": "0x0eD7e52944161450477ee417DE9Cd3a859b14fD0", + "data": {"amount0In": "1500000000000000000", "amount1Out": "478230000000000000000"} + } + ], + "gas": { + "gas_used": 152340, + "gas_price_gwei": 3.0, + "gas_cost_bnb": 0.000457, + "gas_cost_usd": 0.137 + } +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| hash | string | Transaction hash | +| status | string | SUCCESS or FAILED | +| block | number | Block number | +| timestamp | string | ISO 8601 timestamp | +| from | string | Sender address | +| to | string | Recipient/contract address | +| value_bnb | number | Native BNB value sent | +| function | object | Decoded function call | +| function.name | string | Function name (if known) | +| function.params | object | Decoded function parameters | +| token_transfers | array | ERC20/ERC721 transfers within the transaction | +| events | array | Decoded event logs | +| gas | object | Gas usage and cost breakdown | + +--- + +## Notes + +1. Decodes transactions via BSC JSON-RPC `eth_getTransactionByHash` and `eth_getTransactionReceipt` +2. Responses are cached with 120s fresh TTL (transactions are immutable) +3. No authentication required +4. Function selectors are matched against a database of known signatures diff --git a/skills/analytics/wallet-pnl/SKILL.md b/skills/analytics/wallet-pnl/SKILL.md new file mode 100644 index 0000000..fcf28b0 --- /dev/null +++ b/skills/analytics/wallet-pnl/SKILL.md @@ -0,0 +1,125 @@ +--- +name: wallet-pnl +description: | + Portfolio snapshot via on-chain RPC — BNB balance plus top 15 BSC token + holdings with live prices. Classifies wallet activity level and calculates + total portfolio value in USD. Use to analyze any BSC wallet's holdings and + trading performance at a glance. +metadata: + author: mefai + version: "1.0" +--- + +# Wallet PnL Tracker + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Wallet PnL Tracker | Portfolio snapshot with BNB balance, top 15 token holdings, live prices, and activity classification | Analyze any BSC wallet's holdings and performance | + +## Use Cases + +1. **Portfolio Valuation**: Get instant USD valuation of any BSC wallet including BNB and token holdings +2. **Wallet Profiling**: Classify wallets by activity level — Whale, Power User, Active, Casual, or New +3. **Token Exposure Analysis**: See the top 15 token holdings with live prices to understand portfolio composition +4. **Gas Spending Audit**: Track total gas spent in BNB and USD to understand transaction costs + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Wallet PnL Tracker + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/wallet-pnl +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| address | string | Yes | BSC wallet address (0x...) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/wallet-pnl?address=0x8894E0a0c962CB723c1ef8580d0459b45a845D5F' +``` + +**Response Example**: +```json +{ + "address": "0x8894E0a0c962CB723c1ef8580d0459b45a845D5F", + "bnbBalance": 42.85, + "bnbPrice": 612.50, + "bnbValueUsd": 26255.63, + "txCount": 1847, + "totalGasSpent": 1.23, + "totalGasUsd": 753.38, + "totalPortfolioUsd": 158420.50, + "uniqueTokens": 12, + "activity": "Power User", + "winRate": 62.5, + "topTokens": [ + { + "symbol": "CAKE", + "balance": 5200.0, + "priceUsd": 2.85, + "valueUsd": 14820.00 + }, + { + "symbol": "USDT", + "balance": 50000.0, + "priceUsd": 1.00, + "valueUsd": 50000.00 + }, + { + "symbol": "ETH", + "balance": 8.5, + "priceUsd": 3450.00, + "valueUsd": 29325.00 + } + ] +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| address | string | Queried wallet address | +| bnbBalance | number | Native BNB balance | +| bnbPrice | number | Current BNB price in USD | +| bnbValueUsd | number | BNB balance value in USD | +| txCount | number | Total transaction count | +| totalGasSpent | number | Cumulative gas spent in BNB | +| totalGasUsd | number | Cumulative gas spent in USD | +| totalPortfolioUsd | number | Total portfolio value (BNB + all tokens) | +| uniqueTokens | number | Number of distinct tokens held | +| activity | string | Wallet classification: Whale, Power User, Active, Casual, New | +| winRate | number | Estimated profitable trade percentage | +| topTokens | array | Top 15 token holdings sorted by value | +| topTokens[].symbol | string | Token ticker symbol | +| topTokens[].balance | number | Token balance | +| topTokens[].priceUsd | number | Current token price in USD | +| topTokens[].valueUsd | number | Token holding value in USD | + +--- + +## Data Sources + +1. BSC RPC: eth_getBalance, eth_getTransactionCount, eth_call (balanceOf for each token contract) +2. Binance data API: Live token prices for portfolio valuation +3. Tracked tokens: WBNB, USDT, BUSD, CAKE, ETH, BTCB, XRP, USDC, slisBNB, ankrBNB, DOGE, MATIC, AVAX, SOL, ADA + +## Notes + +1. Activity classification based on transaction count and portfolio size +2. Responses are cached with 30s fresh TTL +3. No authentication required +4. Win rate is estimated from on-chain token transfer patterns diff --git a/skills/analytics/wallet-scanner/SKILL.md b/skills/analytics/wallet-scanner/SKILL.md new file mode 100644 index 0000000..b3770a3 --- /dev/null +++ b/skills/analytics/wallet-scanner/SKILL.md @@ -0,0 +1,110 @@ +--- +name: wallet-scanner +description: | + Complete wallet portfolio analysis for BSC. Shows BNB balance plus top + token holdings with USD values, total portfolio value, and asset + allocation breakdown. Use for wallet profiling. +metadata: + author: mefai + version: "1.0" +--- + +# Wallet Scanner + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Wallet Scanner | Complete wallet portfolio: BNB + token balances with USD values | Wallet profiling and portfolio analysis | + +## Use Cases + +1. **Portfolio Overview**: Get a complete view of any wallet's holdings with current USD values +2. **Whale Profiling**: Analyze the portfolio composition of whale wallets +3. **Asset Allocation**: Understand how a wallet's value is distributed across different tokens + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Wallet Scanner + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/wallet-scanner +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| address | string | Yes | Wallet address to scan (0x...) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/wallet-scanner?address=0x8894E0a0c962CB723c1ef8a1B6737B4E1e2AE02b' +``` + +**Response Example**: +```json +{ + "address": "0x8894E0a0c962CB723c1ef8a1B6737B4E1e2AE02b", + "bnb_balance": 125430.5, + "bnb_value_usd": 37629150.0, + "tokens": [ + { + "address": "0x55d398326f99059fF775485246999027B3197955", + "symbol": "USDT", + "balance": 45000000.0, + "decimals": 18, + "price_usd": 1.0, + "value_usd": 45000000.0, + "pct_of_portfolio": 42.1 + }, + { + "address": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "symbol": "CAKE", + "balance": 2500000.0, + "decimals": 18, + "price_usd": 2.35, + "value_usd": 5875000.0, + "pct_of_portfolio": 5.5 + } + ], + "total_value_usd": 106904150.0, + "token_count": 28, + "is_contract": false, + "label": "Binance Hot Wallet 6" +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| address | string | Wallet address scanned | +| bnb_balance | number | BNB balance | +| bnb_value_usd | number | BNB balance value in USD | +| tokens | array | Token holdings with values | +| tokens[].symbol | string | Token symbol | +| tokens[].balance | number | Token balance | +| tokens[].price_usd | number | Current token price | +| tokens[].value_usd | number | Holding value in USD | +| tokens[].pct_of_portfolio | number | Percentage of total portfolio value | +| total_value_usd | number | Total portfolio value in USD | +| token_count | number | Number of different tokens held | +| is_contract | boolean | Whether the address is a contract | +| label | string | Known label for the address | + +--- + +## Notes + +1. BNB balance from JSON-RPC, token balances and prices from DexScreener +2. Responses are cached with 30s fresh TTL +3. No authentication required +4. Shows top token holdings sorted by USD value diff --git a/skills/bnbchain-mcp-skill/SKILL.md b/skills/bnbchain-mcp-skill/SKILL.md index 6f05447..90a2efb 100644 --- a/skills/bnbchain-mcp-skill/SKILL.md +++ b/skills/bnbchain-mcp-skill/SKILL.md @@ -22,7 +22,7 @@ How to connect to the BNB Chain MCP server and use its tools: blocks, transactio ## 1. MCP server config -Add the `bnbchain-mcp` server to the MCP client config (e.g. Cursor MCP settings, Claude Desktop `claude_desktop_config.json`). +Add the `bnbchain-mcp` server to your MCP client config (e.g. your IDE's MCP settings or `mcp_config.json`). **Default (stdio):** diff --git a/skills/bnbchain-mcp-skill/references/evm-tools-reference.md b/skills/bnbchain-mcp-skill/references/evm-tools-reference.md index 2f8529a..f588700 100644 --- a/skills/bnbchain-mcp-skill/references/evm-tools-reference.md +++ b/skills/bnbchain-mcp-skill/references/evm-tools-reference.md @@ -24,7 +24,6 @@ Tools that require **PRIVATE_KEY** in the MCP server env are marked with **(writ | Tool | Description | Parameters | |------|-------------|------------| | get_transaction | Get transaction by hash | `txHash`, `network` | -| get_transaction_receipt | Get receipt by hash | `txHash`, `network` | | estimate_gas | Estimate gas for a tx | `to`, `value` (optional, e.g. "0.1"), `data` (optional hex), `network` | --- @@ -84,18 +83,6 @@ Tools that require **PRIVATE_KEY** in the MCP server env are marked with **(writ |------|-------------|------------| | get_nft_info | ERC721 metadata, owner | `tokenAddress`, `tokenId`, `network` | | get_erc1155_token_metadata | ERC1155 token metadata | `tokenAddress`, `tokenId`, `network` | -| check_nft_ownership | Whether address owns NFT | (Check with get_nft_info or contract read if available) | -| get_nft_balance | NFT count for address in collection | (Use read_contract with balanceOf if needed) | -| get_erc1155_balance | Balance of ERC1155 token ID for address | (Use read_contract with balanceOf if needed) | Transfer tools: see **Wallet and balances** above (`transfer_nft`, `transfer_erc1155`). ---- - -## ENS - -| Tool | Description | Parameters | -|------|-------------|------------| -| resolve_ens | Resolve ENS name to address | `ensName`, `network` (typically ethereum) | - -Note: ENS is not supported on BSC; use on Ethereum or other chains where ENS is deployed. diff --git a/skills/bnbchain-mcp-skill/references/prompts-reference.md b/skills/bnbchain-mcp-skill/references/prompts-reference.md index 3cde21c..a87cc51 100644 --- a/skills/bnbchain-mcp-skill/references/prompts-reference.md +++ b/skills/bnbchain-mcp-skill/references/prompts-reference.md @@ -1,6 +1,6 @@ # MCP prompts reference -The BNB Chain MCP server exposes **prompts** that return guided analysis or explanations. Use these prompt names when invoking MCP prompts (e.g. from Cursor or Claude). +The BNB Chain MCP server exposes **prompts** that return guided analysis or explanations. Use these prompt names when invoking MCP prompts from your coding agent or IDE. | Prompt name | When to use | |-------------|-------------| diff --git a/skills/defi/bridge-flow/SKILL.md b/skills/defi/bridge-flow/SKILL.md new file mode 100644 index 0000000..f1d3714 --- /dev/null +++ b/skills/defi/bridge-flow/SKILL.md @@ -0,0 +1,148 @@ +--- +name: bridge-flow +description: | + Cross-chain capital flow analysis via bridge contract balances on BSC. + Monitors TVL across major bridge protocols to detect capital inflows + and outflows. Use to understand cross-chain liquidity movement and + market sentiment on BSC. +metadata: + author: mefai + version: "1.0" +--- + +# Bridge Flow Monitor + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Bridge Flow Monitor | Cross-chain capital flow analysis via bridge contract balances on BSC | Detect capital inflows/outflows and gauge cross-chain sentiment | + +## Use Cases + +1. **Capital Flow Detection**: Monitor whether capital is flowing into or out of BSC via bridge protocols +2. **Bridge TVL Comparison**: Compare total value locked across Stargate, Celer, Multichain, LayerZero, Wormhole, and Axelar +3. **Market Sentiment Gauge**: Inflows signal bullish sentiment on BSC; outflows suggest capital rotation to other chains +4. **Liquidity Depth Analysis**: Assess available bridge liquidity for large cross-chain transfers + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Bridge Flow Monitor + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/bridge-flow +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns bridge flow data for all monitored bridges | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/bridge-flow' +``` + +**Response Example**: +```json +{ + "totalBridgeTvl": 285400000, + "bnbPrice": 612.50, + "bridgeCount": 6, + "bridges": [ + { + "name": "Stargate", + "address": "0x6694340fc020c5E6B96567843da2df01b2cE1eb6", + "balanceBnb": 12500.0, + "stablesUsd": 45000000, + "tvlUsd": 52656250 + }, + { + "name": "Celer cBridge", + "address": "0x5d96d4287D1ff115eE50faC0526CF43eCf79bFc6", + "balanceBnb": 8200.0, + "stablesUsd": 28000000, + "tvlUsd": 33022500 + }, + { + "name": "LayerZero", + "address": "0x3c2269811836af69497E5F486A85D7316753cf62", + "balanceBnb": 15800.0, + "stablesUsd": 62000000, + "tvlUsd": 71677500 + }, + { + "name": "Wormhole", + "address": "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B", + "balanceBnb": 9400.0, + "stablesUsd": 38000000, + "tvlUsd": 43757500 + }, + { + "name": "Multichain", + "address": "0xf8650eD81442dbbC6B3a9D0b5086F1a0B5a17aFe", + "balanceBnb": 5600.0, + "stablesUsd": 18000000, + "tvlUsd": 21430000 + }, + { + "name": "Axelar", + "address": "0x304acf330bbE08d1e512eefaa92F6a57871fD895", + "balanceBnb": 4200.0, + "stablesUsd": 12000000, + "tvlUsd": 14572500 + } + ], + "flowDirection": "net_inflow", + "sentiment": "Bullish — capital flowing into BSC from other chains" +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| totalBridgeTvl | number | Combined TVL across all monitored bridges in USD | +| bnbPrice | number | Current BNB price used for valuation | +| bridgeCount | number | Number of bridges monitored | +| bridges | array | Per-bridge breakdown | +| bridges[].name | string | Bridge protocol name | +| bridges[].address | string | Bridge contract address on BSC | +| bridges[].balanceBnb | number | BNB held in bridge contract | +| bridges[].stablesUsd | number | Stablecoin value (USDT + USDC + BUSD) in bridge | +| bridges[].tvlUsd | number | Total bridge TVL in USD | +| flowDirection | string | net_inflow, net_outflow, or neutral | +| sentiment | string | Human-readable market sentiment based on flow direction | + +--- + +## Bridges Monitored + +| Bridge | Description | +|--------|-------------| +| Stargate | LayerZero-based omnichain bridge | +| Celer cBridge | Celer Network cross-chain bridge | +| Multichain | Multi-chain router protocol | +| LayerZero | Omnichain interoperability protocol | +| Wormhole | Cross-chain messaging and bridge | +| Axelar | Universal cross-chain communication | + +## Data Sources + +1. BSC RPC: eth_getBalance for BNB balances, eth_call balanceOf for USDT/USDC/BUSD on each bridge contract +2. Binance Data API: BNB price for USD conversion + +## Notes + +1. Flow direction is determined by comparing current balances against 24h moving average +2. Responses are cached with 60s fresh TTL +3. No authentication required +4. Multichain bridge may show reduced balances due to the July 2023 exploit — included for historical tracking diff --git a/skills/defi/defi-leaderboard/SKILL.md b/skills/defi/defi-leaderboard/SKILL.md new file mode 100644 index 0000000..bde0382 --- /dev/null +++ b/skills/defi/defi-leaderboard/SKILL.md @@ -0,0 +1,125 @@ +--- +name: defi-leaderboard +description: | + Top DeFi protocols on BSC ranked by TVL, volume, and user activity. + Provides a comprehensive overview of the BSC DeFi ecosystem. + Use for protocol comparison and ecosystem analysis. +metadata: + author: mefai + version: "1.0" +--- + +# DeFi Leaderboard + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| DeFi Leaderboard | Top DeFi protocols ranked by TVL, volume, and user count | BSC DeFi ecosystem overview | + +## Use Cases + +1. **Ecosystem Overview**: See the top DeFi protocols on BSC ranked by key metrics +2. **Protocol Comparison**: Compare TVL, volume, and user activity across BSC DeFi protocols +3. **Trend Tracking**: Monitor changes in protocol rankings over time + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: DeFi Leaderboard + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/defi-leaderboard +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns current DeFi rankings | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/defi-leaderboard' +``` + +**Response Example**: +```json +{ + "protocols": [ + { + "rank": 1, + "name": "PancakeSwap", + "category": "DEX", + "tvl_usd": 1800000000, + "volume_24h": 425000000, + "users_24h": 185000, + "tvl_change_24h_pct": 1.2, + "token": "CAKE", + "token_price": 2.35 + }, + { + "rank": 2, + "name": "Venus", + "category": "Lending", + "tvl_usd": 950000000, + "volume_24h": 52000000, + "users_24h": 12000, + "tvl_change_24h_pct": -0.5, + "token": "XVS", + "token_price": 8.42 + }, + { + "rank": 3, + "name": "Alpaca Finance", + "category": "Yield", + "tvl_usd": 420000000, + "volume_24h": 18000000, + "users_24h": 8500, + "tvl_change_24h_pct": 0.8, + "token": "ALPACA", + "token_price": 0.21 + } + ], + "total_bsc_tvl": 5200000000, + "total_protocols": 85, + "categories": { + "DEX": 12, + "Lending": 8, + "Yield": 15, + "Bridge": 5, + "Other": 45 + } +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| protocols | array | DeFi protocols sorted by TVL | +| protocols[].rank | number | Current ranking position | +| protocols[].name | string | Protocol name | +| protocols[].category | string | Protocol category (DEX, Lending, Yield, etc.) | +| protocols[].tvl_usd | number | Total value locked in USD | +| protocols[].volume_24h | number | 24h trading/lending volume | +| protocols[].users_24h | number | Unique users in last 24h | +| protocols[].tvl_change_24h_pct | number | TVL change in last 24h | +| total_bsc_tvl | number | Total BSC ecosystem TVL | +| total_protocols | number | Number of protocols tracked | +| categories | object | Protocol count by category | + +--- + +## Notes + +1. Protocol data aggregated from DexScreener and on-chain queries +2. Responses are cached with 60s fresh TTL +3. No authentication required +4. TVL figures represent total value deposited in smart contracts diff --git a/skills/defi/liquidity-pulse/SKILL.md b/skills/defi/liquidity-pulse/SKILL.md new file mode 100644 index 0000000..a56e431 --- /dev/null +++ b/skills/defi/liquidity-pulse/SKILL.md @@ -0,0 +1,115 @@ +--- +name: liquidity-pulse +description: | + Real-time liquidity depth analysis for major BSC trading pairs. Shows + pool sizes, liquidity changes, and concentration across DEXs. + Use for liquidity monitoring and trading execution planning. +metadata: + author: mefai + version: "1.0" +--- + +# Liquidity Pulse + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Liquidity Pulse | Real-time liquidity depth analysis across major BSC pairs | Liquidity monitoring and execution planning | + +## Use Cases + +1. **Execution Planning**: Assess liquidity depth before executing large trades +2. **Liquidity Monitoring**: Track liquidity changes across major BSC pools +3. **Pool Health**: Identify pools with declining liquidity that may indicate risk + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Liquidity Pulse + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/liquidity-pulse +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns liquidity data for major BSC pairs | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/liquidity-pulse' +``` + +**Response Example**: +```json +{ + "pairs": [ + { + "pair": "WBNB/USDT", + "dex": "PancakeSwap V3", + "liquidity_usd": 125000000, + "volume_24h": 85000000, + "utilization_pct": 68.0, + "liquidity_change_24h_pct": 2.1, + "depth_2pct_usd": 4500000, + "price_impact_10k": 0.002, + "price_impact_100k": 0.022, + "price_impact_1m": 0.24, + "status": "DEEP" + }, + { + "pair": "CAKE/WBNB", + "dex": "PancakeSwap V2", + "liquidity_usd": 18500000, + "volume_24h": 15200000, + "utilization_pct": 82.2, + "liquidity_change_24h_pct": -0.8, + "depth_2pct_usd": 850000, + "price_impact_10k": 0.012, + "price_impact_100k": 0.12, + "price_impact_1m": 1.35, + "status": "HEALTHY" + } + ], + "total_liquidity_usd": 2100000000, + "liquidity_change_24h_pct": 0.5, + "most_liquid": "WBNB/USDT", + "most_utilized": "CAKE/WBNB" +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| pairs | array | Major trading pairs with liquidity data | +| pairs[].pair | string | Trading pair name | +| pairs[].liquidity_usd | number | Pool liquidity in USD | +| pairs[].volume_24h | number | 24h trading volume | +| pairs[].utilization_pct | number | Volume/liquidity ratio as percentage | +| pairs[].liquidity_change_24h_pct | number | Liquidity change in last 24h | +| pairs[].depth_2pct_usd | number | USD amount to move price 2% | +| pairs[].price_impact_10k | number | Price impact for $10K trade (%) | +| pairs[].price_impact_100k | number | Price impact for $100K trade (%) | +| pairs[].price_impact_1m | number | Price impact for $1M trade (%) | +| pairs[].status | string | DEEP, HEALTHY, THIN, or CRITICAL | +| total_liquidity_usd | number | Total tracked liquidity | + +--- + +## Notes + +1. Liquidity data from DexScreener API +2. Responses are cached with 60s fresh TTL +3. No authentication required +4. Price impact estimates based on constant product formula approximation +5. Status levels: DEEP (>$50M), HEALTHY (>$5M), THIN (>$500K), CRITICAL (<$500K) diff --git a/skills/defi/protocol-revenue/SKILL.md b/skills/defi/protocol-revenue/SKILL.md new file mode 100644 index 0000000..ad8b1ff --- /dev/null +++ b/skills/defi/protocol-revenue/SKILL.md @@ -0,0 +1,129 @@ +--- +name: protocol-revenue +description: | + Real fee revenue vs TVL for BSC DeFi protocols. Ranks protocols by daily + fees, daily revenue, and revenue-to-TVL efficiency ratio. Use to identify + the most productive and capital-efficient protocols on BSC. +metadata: + author: mefai + version: "1.0" +--- + +# Protocol Revenue Leaderboard + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Protocol Revenue Leaderboard | Fee revenue vs TVL ranking for BSC DeFi protocols | Identify capital-efficient and revenue-generating protocols | + +## Use Cases + +1. **Revenue Ranking**: See which BSC protocols generate the most daily fee revenue +2. **Capital Efficiency**: Compare revenue-to-TVL ratios to find the most productive protocols +3. **Protocol Valuation**: Annualized revenue data helps assess whether protocol tokens are over/undervalued +4. **Category Analysis**: Break down revenue generation by DeFi category (DEX, lending, derivatives, etc.) + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Protocol Revenue Leaderboard + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/protocol-revenue +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns revenue data for top BSC DeFi protocols | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/protocol-revenue' +``` + +**Response Example**: +```json +{ + "totalDailyFees": 4250000, + "totalDailyRevenue": 1820000, + "protocolCount": 15, + "protocols": [ + { + "name": "PancakeSwap", + "category": "DEX", + "dailyFees": 2800000, + "dailyRevenue": 1120000, + "annualRevenue": 408800000, + "tvl": 2150000000, + "revTvlRatio": 0.19 + }, + { + "name": "Venus", + "category": "Lending", + "dailyFees": 580000, + "dailyRevenue": 290000, + "annualRevenue": 105850000, + "tvl": 1850000000, + "revTvlRatio": 0.057 + }, + { + "name": "Thena", + "category": "DEX", + "dailyFees": 320000, + "dailyRevenue": 160000, + "annualRevenue": 58400000, + "tvl": 185000000, + "revTvlRatio": 0.316 + }, + { + "name": "Radiant", + "category": "Lending", + "dailyFees": 210000, + "dailyRevenue": 105000, + "annualRevenue": 38325000, + "tvl": 420000000, + "revTvlRatio": 0.091 + } + ] +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| totalDailyFees | number | Sum of all protocol daily fees in USD | +| totalDailyRevenue | number | Sum of all protocol daily revenue in USD | +| protocolCount | number | Number of protocols in the leaderboard | +| protocols | array | Protocols sorted by daily revenue descending | +| protocols[].name | string | Protocol name | +| protocols[].category | string | DeFi category (DEX, Lending, Derivatives, Yield, Bridge) | +| protocols[].dailyFees | number | Total fees generated in last 24h (USD) | +| protocols[].dailyRevenue | number | Protocol revenue after LP/user share (USD) | +| protocols[].annualRevenue | number | Annualized revenue projection (USD) | +| protocols[].tvl | number | Total value locked in the protocol (USD) | +| protocols[].revTvlRatio | number | Annual revenue divided by TVL — higher means more capital-efficient | + +--- + +## Data Sources + +1. DefiLlama Fees Overview: Daily fees and revenue for BSC protocols +2. DefiLlama Protocols: TVL data for revenue-to-TVL ratio calculation + +## Notes + +1. Revenue = fees retained by the protocol (after LP/user share deductions) +2. revTvlRatio above 0.10 generally indicates strong capital efficiency +3. Responses are cached with 300s fresh TTL (fee data updates less frequently) +4. No authentication required +5. Annualized revenue is a simple projection (dailyRevenue x 365) and does not account for seasonal variation diff --git a/skills/defi/staking-intel/SKILL.md b/skills/defi/staking-intel/SKILL.md new file mode 100644 index 0000000..8225be9 --- /dev/null +++ b/skills/defi/staking-intel/SKILL.md @@ -0,0 +1,172 @@ +--- +name: staking-intel +description: | + BNB staking ecosystem analysis — total staked BNB, liquid staking protocol + comparison, and APY benchmarks. Reads on-chain data from TokenHub and + liquid staking token contracts. Use to evaluate staking opportunities + and monitor the BNB staking landscape. +metadata: + author: mefai + version: "1.0" +--- + +# Staking Intelligence + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Staking Intelligence | BNB staking ecosystem analysis with total staked, liquid staking protocols, and APY comparison | Evaluate staking opportunities and monitor the BNB staking landscape | + +## Use Cases + +1. **Staking Overview**: See total BNB staked and the overall staking ratio for the network +2. **Liquid Staking Comparison**: Compare APY, TVL, and exchange rates across slisBNB, ankrBNB, BNBx, stkBNB, and rBNB +3. **Yield Optimization**: Find the best staking yield between native staking and liquid staking protocols +4. **Staking Ratio Tracking**: Monitor what percentage of BNB supply is staked — rising ratios reduce sell pressure + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Staking Intelligence + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/staking-intel +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns BNB staking ecosystem data | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/staking-intel' +``` + +**Response Example**: +```json +{ + "bnbPrice": 612.50, + "totalStakedBnb": 31200000, + "totalStakedUsd": 19110000000, + "stakingRatio": 19.8, + "activeValidators": 45, + "nativeApy": 2.8, + "liquidStaking": [ + { + "protocol": "Lista DAO", + "token": "slisBNB", + "contractAddress": "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B", + "totalSupply": 285000, + "exchangeRate": 1.042, + "tvlBnb": 297070, + "tvlUsd": 181907375, + "apy": 3.2 + }, + { + "protocol": "Ankr", + "token": "ankrBNB", + "contractAddress": "0x52F24a5e03aee032804b19dCf825d83709c5f3fc", + "totalSupply": 198000, + "exchangeRate": 1.068, + "tvlBnb": 211464, + "tvlUsd": 129521700, + "apy": 3.5 + }, + { + "protocol": "Stader", + "token": "BNBx", + "contractAddress": "0x1bdd3Cf7F79cfB8EdbB955f20ad99211551BA275", + "totalSupply": 145000, + "exchangeRate": 1.055, + "tvlBnb": 152975, + "tvlUsd": 93697188, + "apy": 3.1 + }, + { + "protocol": "pSTAKE", + "token": "stkBNB", + "contractAddress": "0xc2E9d07F66A89c44062459A47a0D2Dc038E4fb16", + "totalSupply": 82000, + "exchangeRate": 1.038, + "tvlBnb": 85116, + "tvlUsd": 52133550, + "apy": 2.9 + }, + { + "protocol": "StaFi", + "token": "rBNB", + "contractAddress": "0xF027E525D491ef6ffCC478555FBb3CFabB3406a6", + "totalSupply": 35000, + "exchangeRate": 1.045, + "tvlBnb": 36575, + "tvlUsd": 22402188, + "apy": 3.0 + } + ], + "totalLiquidStaked": 783200, + "stakingYields": [ + { "type": "Native Staking", "apy": 2.8 }, + { "type": "ankrBNB (Ankr)", "apy": 3.5 }, + { "type": "slisBNB (Lista DAO)", "apy": 3.2 }, + { "type": "BNBx (Stader)", "apy": 3.1 }, + { "type": "rBNB (StaFi)", "apy": 3.0 }, + { "type": "stkBNB (pSTAKE)", "apy": 2.9 } + ] +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| bnbPrice | number | Current BNB price in USD | +| totalStakedBnb | number | Total BNB staked on the network | +| totalStakedUsd | number | Total staked value in USD | +| stakingRatio | number | Percentage of circulating BNB that is staked | +| activeValidators | number | Number of active validators on BSC | +| nativeApy | number | Native staking APY percentage | +| liquidStaking | array | Liquid staking protocol details | +| liquidStaking[].protocol | string | Protocol name | +| liquidStaking[].token | string | Liquid staking token symbol | +| liquidStaking[].contractAddress | string | Token contract address on BSC | +| liquidStaking[].totalSupply | number | Total supply of the liquid staking token | +| liquidStaking[].exchangeRate | number | Token to BNB exchange rate (always >= 1.0) | +| liquidStaking[].tvlBnb | number | Total BNB represented (supply x exchangeRate) | +| liquidStaking[].tvlUsd | number | TVL in USD | +| liquidStaking[].apy | number | Current APY percentage | +| totalLiquidStaked | number | Sum of all liquid staking TVL in BNB | +| stakingYields | array | All staking options sorted by APY descending | + +--- + +## Liquid Staking Tokens Tracked + +| Token | Protocol | Description | +|-------|----------|-------------| +| slisBNB | Lista DAO | Lista liquid staking BNB | +| ankrBNB | Ankr | Ankr liquid staking BNB | +| BNBx | Stader | Stader liquid staking BNB | +| stkBNB | pSTAKE | pSTAKE liquid staking BNB | +| rBNB | StaFi | StaFi liquid staking BNB | + +## Data Sources + +1. BSC RPC: TokenHub (0x0000000000000000000000000000000000001004) balance for total staked BNB +2. BSC RPC: totalSupply() and exchangeRate() calls on each liquid staking token contract +3. Binance Data API: BNB price for USD conversion + +## Notes + +1. Exchange rates increase over time as staking rewards accrue — this is how liquid staking tokens appreciate +2. Responses are cached with 120s fresh TTL +3. No authentication required +4. APY figures are trailing estimates and may vary based on network conditions and validator performance diff --git a/skills/defi/yield-finder/SKILL.md b/skills/defi/yield-finder/SKILL.md new file mode 100644 index 0000000..b431547 --- /dev/null +++ b/skills/defi/yield-finder/SKILL.md @@ -0,0 +1,114 @@ +--- +name: yield-finder +description: | + APY estimation engine for BSC DEX pairs. Calculates yield from trading fee + revenue based on volume and liquidity, sorted by opportunity. Use to find + the best yield farming opportunities on BSC DEXs. +metadata: + author: mefai + version: "1.0" +--- + +# Yield Finder + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Yield Finder | APY estimation from trading fee revenue, sorted by opportunity | Find best yield farming opportunities on BSC | + +## Use Cases + +1. **Yield Comparison**: Compare estimated APYs across BSC DEX pools to find the best opportunities +2. **LP Decision Making**: Evaluate whether providing liquidity to a pool is profitable +3. **Fee Revenue Analysis**: Understand how trading volume translates to LP fee income + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Yield Finder + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/yield-finder +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns yield estimates for top BSC pools | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/yield-finder' +``` + +**Response Example**: +```json +{ + "pools": [ + { + "pair": "CAKE/WBNB", + "dex": "PancakeSwap V3", + "pair_address": "0x133B3D95bAD5405d14d53473671200e9342b...", + "liquidity_usd": 18500000, + "volume_24h": 15200000, + "fee_tier": 0.25, + "fees_24h_usd": 38000.0, + "apy_estimate_pct": 74.9, + "apr_estimate_pct": 56.2, + "volume_to_liquidity": 0.82, + "risk_level": "LOW" + }, + { + "pair": "USDT/BUSD", + "dex": "PancakeSwap V3", + "pair_address": "0x4f3126d5DE26413AbDCF6948943FB9D0847d9C15", + "liquidity_usd": 85000000, + "volume_24h": 42000000, + "fee_tier": 0.01, + "fees_24h_usd": 4200.0, + "apy_estimate_pct": 1.8, + "apr_estimate_pct": 1.8, + "volume_to_liquidity": 0.49, + "risk_level": "MINIMAL" + } + ], + "total_pools_analyzed": 50, + "avg_apy_pct": 28.5, + "disclaimer": "APY estimates are based on 24h fee revenue extrapolated annually. Actual returns may vary due to impermanent loss, volume changes, and market conditions." +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| pools | array | Yield opportunities sorted by APY | +| pools[].pair | string | Trading pair name | +| pools[].dex | string | DEX name | +| pools[].liquidity_usd | number | Pool liquidity in USD | +| pools[].volume_24h | number | 24h trading volume | +| pools[].fee_tier | number | Fee percentage (e.g., 0.25 = 0.25%) | +| pools[].fees_24h_usd | number | Fees generated in last 24h | +| pools[].apy_estimate_pct | number | Estimated APY from fees (compounded) | +| pools[].apr_estimate_pct | number | Estimated APR from fees (simple) | +| pools[].volume_to_liquidity | number | Volume/liquidity ratio | +| pools[].risk_level | string | MINIMAL, LOW, MEDIUM, HIGH | +| total_pools_analyzed | number | Number of pools evaluated | +| disclaimer | string | Important notice about estimates | + +--- + +## Notes + +1. Fee revenue data from DexScreener API volume and fee tier information +2. Responses are cached with 60s fresh TTL +3. No authentication required +4. APY estimates do not account for impermanent loss — actual returns may be lower diff --git a/skills/market/dex-arb/SKILL.md b/skills/market/dex-arb/SKILL.md new file mode 100644 index 0000000..d61f38a --- /dev/null +++ b/skills/market/dex-arb/SKILL.md @@ -0,0 +1,100 @@ +--- +name: dex-arb +description: | + Cross-DEX arbitrage scanner for BSC. Finds price discrepancies across + PancakeSwap, BiSwap, ApeSwap, and other DEXs with gas-adjusted profit + estimates. Use to identify arbitrage opportunities. +metadata: + author: mefai + version: "1.0" +--- + +# DEX Arbitrage + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| DEX Arbitrage | Cross-DEX price comparison with gas-adjusted profit calculation | Identify arbitrage opportunities across BSC DEXs | + +## Use Cases + +1. **Arbitrage Detection**: Find tokens with significant price differences across DEXs +2. **Profit Estimation**: Calculate gas-adjusted profit for potential arbitrage trades +3. **Market Efficiency Monitoring**: Track how quickly price discrepancies are closed across DEXs + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: DEX Arbitrage + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/dex-arb +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| address | string | No | Token address to check (0x...). If omitted, scans top tokens. | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/dex-arb?address=0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82' +``` + +**Response Example**: +```json +{ + "opportunities": [ + { + "token": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "symbol": "CAKE", + "buy_dex": "BiSwap", + "buy_price": 2.341, + "sell_dex": "PancakeSwap V3", + "sell_price": 2.358, + "spread_pct": 0.73, + "gas_cost_usd": 0.15, + "net_profit_pct": 0.58, + "trade_size_usd": 1000.0, + "estimated_profit_usd": 5.80 + } + ], + "dexes_checked": ["PancakeSwap V2", "PancakeSwap V3", "BiSwap", "ApeSwap", "BabySwap", "MDEX"], + "total_opportunities": 3, + "timestamp": "2026-03-08T14:30:00Z" +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| opportunities | array | List of arbitrage opportunities found | +| opportunities[].symbol | string | Token symbol | +| opportunities[].buy_dex | string | DEX with the lower price | +| opportunities[].buy_price | number | Price on the buy-side DEX | +| opportunities[].sell_dex | string | DEX with the higher price | +| opportunities[].sell_price | number | Price on the sell-side DEX | +| opportunities[].spread_pct | number | Price spread percentage | +| opportunities[].gas_cost_usd | number | Estimated gas cost in USD | +| opportunities[].net_profit_pct | number | Profit after gas costs | +| opportunities[].estimated_profit_usd | number | Estimated profit for trade_size_usd | +| dexes_checked | array | DEXs included in the scan | +| total_opportunities | number | Count of profitable opportunities | + +--- + +## Notes + +1. Prices sourced from DexScreener API across multiple BSC DEXs +2. Responses are cached with 30s fresh TTL +3. No authentication required +4. Gas costs estimated at current BSC gas prices via JSON-RPC diff --git a/skills/market/pair-analytics/SKILL.md b/skills/market/pair-analytics/SKILL.md new file mode 100644 index 0000000..0a08f31 --- /dev/null +++ b/skills/market/pair-analytics/SKILL.md @@ -0,0 +1,110 @@ +--- +name: pair-analytics +description: | + Deep DEX pair analysis for BSC tokens. Aggregates data across multiple + trading pairs for a token including volume, liquidity, price impact, + and trading activity. Use for comprehensive pair-level market analysis. +metadata: + author: mefai + version: "1.0" +--- + +# Pair Analytics + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Pair Analytics | Deep DEX pair analysis with aggregate stats across multiple pairs | Comprehensive pair-level market intelligence | + +## Use Cases + +1. **Liquidity Assessment**: Evaluate liquidity depth and distribution across all pairs for a token +2. **Best Execution**: Identify the most liquid pair for trade execution +3. **Market Structure**: Understand volume distribution across DEXs and pair types + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Pair Analytics + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/pair-analytics +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| address | string | Yes | Token contract address (0x...) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/pair-analytics?address=0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82' +``` + +**Response Example**: +```json +{ + "address": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "symbol": "CAKE", + "total_pairs": 12, + "total_liquidity_usd": 42300000, + "total_volume_24h": 28500000, + "pairs": [ + { + "pair_address": "0x0eD7e52944161450477ee417DE9Cd3a859b14fD0", + "dex": "PancakeSwap V2", + "base_token": "CAKE", + "quote_token": "WBNB", + "price_usd": 2.35, + "liquidity_usd": 18500000, + "volume_24h": 15200000, + "txns_24h": 12450, + "price_change_24h": -1.2, + "volume_share_pct": 53.3 + } + ], + "aggregate": { + "avg_price_usd": 2.352, + "max_price_usd": 2.358, + "min_price_usd": 2.341, + "price_spread_pct": 0.72, + "dominant_dex": "PancakeSwap V2", + "dominant_dex_share_pct": 53.3 + } +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| address | string | Token contract address | +| symbol | string | Token symbol | +| total_pairs | number | Number of active trading pairs | +| total_liquidity_usd | number | Aggregate liquidity across all pairs | +| total_volume_24h | number | Aggregate 24h volume across all pairs | +| pairs | array | Individual pair details | +| pairs[].dex | string | DEX name | +| pairs[].liquidity_usd | number | Pair liquidity in USD | +| pairs[].volume_24h | number | Pair 24h volume in USD | +| pairs[].volume_share_pct | number | This pair's share of total volume | +| aggregate | object | Cross-pair aggregate statistics | +| aggregate.price_spread_pct | number | Price spread across all pairs | +| aggregate.dominant_dex | string | DEX with the most volume | + +--- + +## Notes + +1. Pair data sourced from DexScreener API +2. Responses are cached with 30s fresh TTL +3. No authentication required +4. Price spread can indicate arbitrage opportunities or thin liquidity diff --git a/skills/market/pancakeswap-arena/SKILL.md b/skills/market/pancakeswap-arena/SKILL.md new file mode 100644 index 0000000..4e1b7ec --- /dev/null +++ b/skills/market/pancakeswap-arena/SKILL.md @@ -0,0 +1,109 @@ +--- +name: pancakeswap-arena +description: | + PancakeSwap market overview for BSC. Shows top trading pairs, volume + leaders, and trending tokens on PancakeSwap V2 and V3. Use for + PancakeSwap-specific market intelligence. +metadata: + author: mefai + version: "1.0" +--- + +# PancakeSwap Arena + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| PancakeSwap Arena | Top pairs, volume leaders, trending tokens on PancakeSwap | PancakeSwap-specific market intelligence | + +## Use Cases + +1. **Market Overview**: See the most active trading pairs on PancakeSwap +2. **Trend Discovery**: Identify trending tokens with surging volume +3. **Liquidity Monitoring**: Track top liquidity pools and their performance + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: PancakeSwap Arena + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/pancakeswap-arena +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns current PancakeSwap market data | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/pancakeswap-arena' +``` + +**Response Example**: +```json +{ + "top_pairs": [ + { + "pair": "CAKE/WBNB", + "pair_address": "0x0eD7e52944161450477ee417DE9Cd3a859b14fD0", + "dex": "PancakeSwap V2", + "price_usd": 2.35, + "volume_24h": 15200000, + "liquidity_usd": 18500000, + "txns_24h": 12450, + "price_change_24h": -1.2 + } + ], + "trending": [ + { + "symbol": "NEWTOKEN", + "address": "0xabc...", + "price_usd": 0.0042, + "volume_24h": 850000, + "volume_change_pct": 420.0, + "price_change_24h": 85.3, + "age_hours": 6 + } + ], + "stats": { + "total_pairs": 3250, + "total_volume_24h": 425000000, + "total_liquidity": 1800000000, + "active_pairs_24h": 1840 + } +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| top_pairs | array | Top trading pairs by volume | +| top_pairs[].pair | string | Trading pair name | +| top_pairs[].volume_24h | number | 24h trading volume in USD | +| top_pairs[].liquidity_usd | number | Pool liquidity in USD | +| trending | array | Tokens with rapidly increasing volume | +| trending[].volume_change_pct | number | Volume change vs previous 24h | +| trending[].age_hours | number | Hours since pair was created | +| stats | object | Aggregate PancakeSwap statistics | +| stats.total_pairs | number | Total number of trading pairs | +| stats.total_volume_24h | number | Total 24h volume across PancakeSwap | + +--- + +## Notes + +1. Data sourced from DexScreener API filtered for PancakeSwap pairs +2. Responses are cached with 30s fresh TTL +3. No authentication required +4. Includes both PancakeSwap V2 and V3 pairs diff --git a/skills/market/token-battle/SKILL.md b/skills/market/token-battle/SKILL.md new file mode 100644 index 0000000..5d2058d --- /dev/null +++ b/skills/market/token-battle/SKILL.md @@ -0,0 +1,113 @@ +--- +name: token-battle +description: | + Side-by-side comparison of up to 4 BSC tokens. Compares price, volume, + liquidity, market cap, holder count, and burn metrics. Use for comparative + token analysis and investment decisions. +metadata: + author: mefai + version: "1.0" +--- + +# Token Battle + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Token Battle | Compare up to 4 tokens side-by-side: price, volume, liquidity, burns | Comparative token analysis | + +## Use Cases + +1. **Investment Comparison**: Compare key metrics across competing tokens to make informed allocation decisions +2. **Sector Analysis**: Compare tokens within a sector (e.g., DEX tokens: CAKE vs BSW vs BANANA) +3. **Burn Rate Comparison**: Evaluate token burn programs across different projects + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Token Battle + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/token-battle +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| tokens | string | Yes | Comma-separated token addresses (2-4 tokens) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/token-battle?tokens=0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82,0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c' +``` + +**Response Example**: +```json +{ + "tokens": [ + { + "address": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "symbol": "CAKE", + "name": "PancakeSwap Token", + "price_usd": 2.35, + "price_change_24h": -1.2, + "volume_24h": 28500000, + "market_cap": 680000000, + "liquidity_usd": 42300000, + "fdv": 920000000, + "total_supply": 391350000, + "burned_pct": 12.8 + }, + { + "address": "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + "symbol": "WBNB", + "name": "Wrapped BNB", + "price_usd": 300.50, + "price_change_24h": 0.8, + "volume_24h": 145000000, + "market_cap": 46200000000, + "liquidity_usd": 890000000, + "fdv": 46200000000, + "total_supply": 153856000, + "burned_pct": 0 + } + ], + "winner": { + "by_volume": "WBNB", + "by_liquidity": "WBNB", + "by_price_change": "WBNB", + "by_mcap_ratio": "CAKE" + } +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| tokens | array | Array of token data objects | +| tokens[].symbol | string | Token symbol | +| tokens[].price_usd | number | Current price in USD | +| tokens[].price_change_24h | number | 24h price change percentage | +| tokens[].volume_24h | number | 24h trading volume in USD | +| tokens[].market_cap | number | Market capitalization in USD | +| tokens[].liquidity_usd | number | Total DEX liquidity in USD | +| tokens[].burned_pct | number | Percentage of supply burned | +| winner | object | Category winners across compared tokens | + +--- + +## Notes + +1. Token data sourced from DexScreener API +2. Responses are cached with 60s fresh TTL +3. No authentication required +4. Supports 2 to 4 tokens per comparison diff --git a/skills/market/token-birth/SKILL.md b/skills/market/token-birth/SKILL.md new file mode 100644 index 0000000..7d15064 --- /dev/null +++ b/skills/market/token-birth/SKILL.md @@ -0,0 +1,108 @@ +--- +name: token-birth +description: | + Token genesis analysis for BSC. Examines token creation details including + creator wallet profile, initial supply distribution, contract age, + and early liquidity history. Use to evaluate a token's origins. +metadata: + author: mefai + version: "1.0" +--- + +# Token Birth + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Token Birth | Token genesis analysis: creator profile, supply distribution, age, liquidity history | Evaluate token origins and creator credibility | + +## Use Cases + +1. **Creator Due Diligence**: Analyze the creator wallet's history and other token deployments +2. **Supply Distribution Review**: Check initial token distribution to assess centralization risk +3. **Liquidity Timeline**: Understand when and how liquidity was added and whether it is locked + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Token Birth + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/token-birth +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| address | string | Yes | Token contract address (0x...) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/token-birth?address=0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82' +``` + +**Response Example**: +```json +{ + "address": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "symbol": "CAKE", + "name": "PancakeSwap Token", + "creator": "0x73feaa1eE314F8c655E354234017bE2193C9E24E", + "creation_block": 628285, + "creation_date": "2020-09-25T09:22:12Z", + "age_days": 1990, + "initial_supply": 15000000, + "current_supply": 391350000, + "supply_growth_pct": 2509.0, + "creator_profile": { + "contracts_deployed": 24, + "bnb_balance": 0.0, + "is_contract": true, + "label": "PancakeSwap: MasterChef" + }, + "initial_distribution": { + "creator_pct": 100.0, + "top_5_holders_pct": 100.0 + }, + "first_liquidity": { + "dex": "PancakeSwap V1", + "pair": "CAKE/BNB", + "block": 628350, + "initial_liquidity_usd": 50000 + } +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| address | string | Token contract address | +| symbol | string | Token symbol | +| creator | string | Creator wallet address | +| creation_block | number | Block number of contract deployment | +| creation_date | string | ISO 8601 deployment timestamp | +| age_days | number | Days since deployment | +| initial_supply | number | Initial token supply | +| current_supply | number | Current token supply | +| supply_growth_pct | number | Supply growth percentage since creation | +| creator_profile | object | Creator wallet analysis | +| initial_distribution | object | Initial supply distribution metrics | +| first_liquidity | object | First liquidity addition details | + +--- + +## Notes + +1. Combines BSC JSON-RPC contract history with DexScreener data +2. Responses are cached with 60s fresh TTL +3. No authentication required +4. Creator profile includes number of contracts deployed and current balance diff --git a/skills/network/bsc-supremacy/SKILL.md b/skills/network/bsc-supremacy/SKILL.md new file mode 100644 index 0000000..df05822 --- /dev/null +++ b/skills/network/bsc-supremacy/SKILL.md @@ -0,0 +1,108 @@ +--- +name: bsc-supremacy +description: | + BSC vs Ethereum live comparison. Compares speed, cost, TPS, block time, + and gas prices between BSC and Ethereum using real-time data from both + chains. Use for cross-chain performance benchmarking. +metadata: + author: mefai + version: "1.0" +--- + +# BSC Supremacy + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| BSC Supremacy | BSC vs Ethereum comparison: speed, cost, TPS — live data | Cross-chain performance benchmarking | + +## Use Cases + +1. **Chain Comparison**: Compare BSC and Ethereum performance metrics in real-time +2. **Cost Analysis**: Show the cost advantage of BSC for common operations +3. **Performance Benchmarking**: Track TPS and block time differences between chains + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | +| Ethereum | 1 | + +--- + +## API: BSC Supremacy + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/bsc-supremacy +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns live comparison data | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/bsc-supremacy' +``` + +**Response Example**: +```json +{ + "bsc": { + "block_height": 46250100, + "block_time_ms": 3008, + "gas_price_gwei": 3.0, + "tps": 58.4, + "transfer_cost_usd": 0.025, + "swap_cost_usd": 0.15, + "consensus": "PoSA" + }, + "ethereum": { + "block_height": 19450000, + "block_time_ms": 12080, + "gas_price_gwei": 25.0, + "tps": 15.2, + "transfer_cost_usd": 1.85, + "swap_cost_usd": 12.50, + "consensus": "PoS" + }, + "advantages": { + "speed_multiplier": 4.01, + "cost_ratio_transfer": 74.0, + "cost_ratio_swap": 83.3, + "tps_multiplier": 3.84 + }, + "summary": "BSC is 4x faster, 74x cheaper for transfers, and 3.8x higher throughput than Ethereum" +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| bsc | object | BSC network metrics | +| ethereum | object | Ethereum network metrics | +| bsc.block_time_ms | number | BSC average block time | +| bsc.gas_price_gwei | number | BSC gas price | +| bsc.tps | number | BSC transactions per second | +| bsc.transfer_cost_usd | number | Cost of a simple transfer on BSC | +| bsc.swap_cost_usd | number | Cost of a DEX swap on BSC | +| advantages | object | BSC advantage ratios | +| advantages.speed_multiplier | number | How many times faster BSC is | +| advantages.cost_ratio_transfer | number | How many times cheaper BSC transfers are | +| advantages.tps_multiplier | number | BSC TPS advantage | +| summary | string | Human-readable comparison summary | + +--- + +## Notes + +1. BSC data from BSC JSON-RPC, Ethereum data from public Ethereum RPC +2. Responses are cached with 15s fresh TTL +3. No authentication required +4. Cost estimates based on current gas prices and standard gas usage for each operation diff --git a/skills/network/chain-vitals/SKILL.md b/skills/network/chain-vitals/SKILL.md new file mode 100644 index 0000000..3cd5162 --- /dev/null +++ b/skills/network/chain-vitals/SKILL.md @@ -0,0 +1,91 @@ +--- +name: chain-vitals +description: | + Core BSC network health metrics. Shows block height, gas price, peer count, + sync status, and chain ID. Use as a quick network health check. +metadata: + author: mefai + version: "1.0" +--- + +# Chain Vitals + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Chain Vitals | Core network health: block height, gas price, peer count | Quick BSC network health check | + +## Use Cases + +1. **Health Check**: Quick verification that BSC network is operational and responsive +2. **Status Dashboard**: Core metrics for network monitoring dashboards +3. **Sync Verification**: Confirm that the RPC node is fully synced + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Chain Vitals + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/chain-vitals +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns current chain vitals | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/chain-vitals' +``` + +**Response Example**: +```json +{ + "chain_id": 56, + "chain_name": "BNB Smart Chain", + "block_height": 46250100, + "gas_price_gwei": 3.0, + "peer_count": 42, + "is_syncing": false, + "latest_block_time": "2026-03-08T14:28:45Z", + "block_age_seconds": 2, + "consensus": "PoSA", + "validators": 21, + "status": "HEALTHY" +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| chain_id | number | BSC chain ID (56) | +| chain_name | string | Chain name | +| block_height | number | Latest block number | +| gas_price_gwei | number | Current gas price in Gwei | +| peer_count | number | Number of connected peers | +| is_syncing | boolean | Whether the node is syncing | +| latest_block_time | string | Timestamp of the latest block | +| block_age_seconds | number | Seconds since the latest block | +| consensus | string | Consensus mechanism (PoSA) | +| validators | number | Number of active validators | +| status | string | HEALTHY, DEGRADED, or DOWN | + +--- + +## Notes + +1. Combines multiple BSC JSON-RPC calls: `eth_blockNumber`, `eth_gasPrice`, `net_peerCount`, `eth_syncing` +2. Responses are cached with 10s fresh TTL +3. No authentication required +4. Status is DEGRADED if block age exceeds 10 seconds, DOWN if over 60 seconds diff --git a/skills/network/gas-calculator/SKILL.md b/skills/network/gas-calculator/SKILL.md new file mode 100644 index 0000000..a563c9d --- /dev/null +++ b/skills/network/gas-calculator/SKILL.md @@ -0,0 +1,177 @@ +--- +name: gas-calculator +description: | + Operation cost estimator for BSC. Calculates gas costs for 10 common + operations (transfer, swap, deploy, etc.) with BSC vs ETH cost comparison. + Use for cost planning and gas budgeting. +metadata: + author: mefai + version: "1.0" +--- + +# Gas Calculator + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Gas Calculator | Cost estimator for 10 operations with BSC vs ETH comparison | Cost planning and gas budgeting | + +## Use Cases + +1. **Cost Estimation**: Calculate the exact USD cost of common BSC operations at current gas prices +2. **Budget Planning**: Estimate deployment and operational costs for BSC-based projects +3. **Cross-Chain Cost Comparison**: Compare operation costs between BSC and Ethereum + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Gas Calculator + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/gas-calculator +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns current gas cost estimates | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/gas-calculator' +``` + +**Response Example**: +```json +{ + "gas_price_gwei": 3.0, + "bnb_price_usd": 300.0, + "eth_gas_price_gwei": 25.0, + "eth_price_usd": 3200.0, + "operations": [ + { + "name": "BNB Transfer", + "gas_units": 21000, + "bsc_cost_bnb": 0.000063, + "bsc_cost_usd": 0.019, + "eth_cost_eth": 0.000525, + "eth_cost_usd": 1.68, + "savings_pct": 98.9 + }, + { + "name": "ERC20 Transfer", + "gas_units": 65000, + "bsc_cost_bnb": 0.000195, + "bsc_cost_usd": 0.059, + "eth_cost_eth": 0.001625, + "eth_cost_usd": 5.20, + "savings_pct": 98.9 + }, + { + "name": "DEX Swap", + "gas_units": 150000, + "bsc_cost_bnb": 0.00045, + "bsc_cost_usd": 0.135, + "eth_cost_eth": 0.00375, + "eth_cost_usd": 12.00, + "savings_pct": 98.9 + }, + { + "name": "ERC20 Approval", + "gas_units": 46000, + "bsc_cost_bnb": 0.000138, + "bsc_cost_usd": 0.041, + "eth_cost_eth": 0.00115, + "eth_cost_usd": 3.68, + "savings_pct": 98.9 + }, + { + "name": "Add Liquidity", + "gas_units": 250000, + "bsc_cost_bnb": 0.00075, + "bsc_cost_usd": 0.225, + "eth_cost_eth": 0.00625, + "eth_cost_usd": 20.00, + "savings_pct": 98.9 + }, + { + "name": "NFT Mint", + "gas_units": 120000, + "bsc_cost_bnb": 0.00036, + "bsc_cost_usd": 0.108, + "eth_cost_eth": 0.003, + "eth_cost_usd": 9.60, + "savings_pct": 98.9 + }, + { + "name": "NFT Transfer", + "gas_units": 85000, + "bsc_cost_bnb": 0.000255, + "bsc_cost_usd": 0.077, + "eth_cost_eth": 0.002125, + "eth_cost_usd": 6.80, + "savings_pct": 98.9 + }, + { + "name": "Contract Deploy (small)", + "gas_units": 500000, + "bsc_cost_bnb": 0.0015, + "bsc_cost_usd": 0.45, + "eth_cost_eth": 0.0125, + "eth_cost_usd": 40.00, + "savings_pct": 98.9 + }, + { + "name": "Contract Deploy (large)", + "gas_units": 3000000, + "bsc_cost_bnb": 0.009, + "bsc_cost_usd": 2.70, + "eth_cost_eth": 0.075, + "eth_cost_usd": 240.00, + "savings_pct": 98.9 + }, + { + "name": "Multi-sig Execution", + "gas_units": 200000, + "bsc_cost_bnb": 0.0006, + "bsc_cost_usd": 0.18, + "eth_cost_eth": 0.005, + "eth_cost_usd": 16.00, + "savings_pct": 98.9 + } + ] +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| gas_price_gwei | number | Current BSC gas price | +| bnb_price_usd | number | Current BNB price | +| eth_gas_price_gwei | number | Current Ethereum gas price | +| eth_price_usd | number | Current ETH price | +| operations | array | Cost estimates for each operation | +| operations[].name | string | Operation name | +| operations[].gas_units | number | Estimated gas consumption | +| operations[].bsc_cost_bnb | number | BSC cost in BNB | +| operations[].bsc_cost_usd | number | BSC cost in USD | +| operations[].eth_cost_usd | number | Equivalent Ethereum cost in USD | +| operations[].savings_pct | number | Percentage savings on BSC vs Ethereum | + +--- + +## Notes + +1. Gas prices from BSC and Ethereum JSON-RPC, token prices from market data +2. Responses are cached with 15s fresh TTL +3. No authentication required +4. Gas unit estimates are based on typical transaction complexity diff --git a/skills/network/network-pulse/SKILL.md b/skills/network/network-pulse/SKILL.md new file mode 100644 index 0000000..3b0a63b --- /dev/null +++ b/skills/network/network-pulse/SKILL.md @@ -0,0 +1,102 @@ +--- +name: network-pulse +description: | + Network congestion gauge for BSC. Calculates a pressure score based on + gas utilization, TPS, block timing, and pending transaction count. + Use for gas optimization and network health monitoring. +metadata: + author: mefai + version: "1.0" +--- + +# Network Pulse + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Network Pulse | Congestion gauge: pressure score, TPS, block timing, gas optimization | Network health monitoring and gas optimization | + +## Use Cases + +1. **Gas Optimization**: Monitor network pressure to time transactions when gas is cheapest +2. **Network Health**: Track BSC health metrics including TPS, block timing, and congestion +3. **Alert Trigger**: Use pressure score to trigger alerts when network congestion is high + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Network Pulse + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/network-pulse +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns current network metrics | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/network-pulse' +``` + +**Response Example**: +```json +{ + "pressure_score": 35, + "pressure_level": "MODERATE", + "block_height": 46250100, + "gas_price_gwei": 3.0, + "tps": 58.4, + "avg_block_time_ms": 3008, + "target_block_time_ms": 3000, + "block_time_deviation_pct": 0.27, + "gas_utilization_pct": 30.4, + "recent_blocks": { + "count": 20, + "min_txs": 120, + "max_txs": 245, + "avg_txs": 168, + "empty_blocks": 0 + }, + "recommendation": "Network load is moderate. Standard gas price sufficient for timely inclusion.", + "optimal_gas_gwei": 3.0, + "peer_count": 42 +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| pressure_score | number | Network congestion score 0-100 | +| pressure_level | string | LOW, MODERATE, HIGH, or CRITICAL | +| block_height | number | Current block height | +| gas_price_gwei | number | Current gas price in Gwei | +| tps | number | Transactions per second | +| avg_block_time_ms | number | Average block time in milliseconds | +| target_block_time_ms | number | Target block time (3000ms for BSC) | +| block_time_deviation_pct | number | How far block time deviates from target | +| gas_utilization_pct | number | Percentage of gas limit being used | +| recent_blocks | object | Statistics from recent block analysis | +| recommendation | string | Gas optimization advice | +| optimal_gas_gwei | number | Recommended gas price | +| peer_count | number | Number of connected peers | + +--- + +## Notes + +1. Aggregates multiple BSC JSON-RPC calls for comprehensive network view +2. Responses are cached with 10s fresh TTL for near-real-time data +3. No authentication required +4. Pressure levels: LOW (0-25), MODERATE (26-50), HIGH (51-75), CRITICAL (76-100) diff --git a/skills/network/validator-map/SKILL.md b/skills/network/validator-map/SKILL.md new file mode 100644 index 0000000..76d34ac --- /dev/null +++ b/skills/network/validator-map/SKILL.md @@ -0,0 +1,114 @@ +--- +name: validator-map +description: | + Live BSC validator monitoring. Tracks all 21 active validators with + gas utilization, block production stats, and MEV detection signals. + Use for network health monitoring and validator analysis. +metadata: + author: mefai + version: "1.0" +--- + +# Validator Map + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Validator Map | Live monitoring of 21 BSC validators: gas utilization, block production, MEV detection | Network health and validator analysis | + +## Use Cases + +1. **Validator Monitoring**: Track block production and gas utilization across all 21 BSC validators +2. **MEV Detection**: Identify validators with unusual transaction ordering patterns +3. **Network Decentralization**: Assess block production distribution among validators + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Validator Map + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/validator-map +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| (none) | — | — | Returns current validator stats | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/validator-map' +``` + +**Response Example**: +```json +{ + "active_validators": 21, + "current_block": 46250100, + "validators": [ + { + "address": "0x72b61c6014342d914470eC7aC2975bE345796c2b", + "name": "NodeReal", + "blocks_produced": 48, + "blocks_share_pct": 4.76, + "avg_gas_utilization_pct": 32.5, + "avg_tx_count": 175, + "last_block": 46250095, + "mev_score": 12, + "status": "ACTIVE" + }, + { + "address": "0x2465176C461AfB316ebc773C61fAee85A6515DAA", + "name": "Binance Node 1", + "blocks_produced": 52, + "blocks_share_pct": 5.14, + "avg_gas_utilization_pct": 28.1, + "avg_tx_count": 162, + "last_block": 46250098, + "mev_score": 5, + "status": "ACTIVE" + } + ], + "network_stats": { + "avg_block_time_ms": 3012, + "avg_gas_utilization_pct": 30.8, + "avg_txs_per_block": 168, + "blocks_analyzed": 1008 + } +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| active_validators | number | Number of active validators | +| current_block | number | Latest block number | +| validators | array | Validator details | +| validators[].address | string | Validator address | +| validators[].name | string | Known validator name | +| validators[].blocks_produced | number | Blocks produced in sample period | +| validators[].blocks_share_pct | number | Share of total blocks | +| validators[].avg_gas_utilization_pct | number | Average gas usage in produced blocks | +| validators[].mev_score | number | MEV activity score (0-100) | +| validators[].status | string | ACTIVE or INACTIVE | +| network_stats | object | Aggregate network statistics | +| network_stats.avg_block_time_ms | number | Average block time in milliseconds | + +--- + +## Notes + +1. Analyzes recent blocks via BSC JSON-RPC to build validator statistics +2. Responses are cached with 15s fresh TTL +3. No authentication required +4. BSC uses Proof of Staked Authority (PoSA) with 21 active validators diff --git a/skills/security/approval-scanner/SKILL.md b/skills/security/approval-scanner/SKILL.md new file mode 100644 index 0000000..77156a0 --- /dev/null +++ b/skills/security/approval-scanner/SKILL.md @@ -0,0 +1,118 @@ +--- +name: approval-scanner +description: | + Token approval checker for BSC wallets. Scans 9 major DEX routers for + unlimited or excessive token allowances that could be exploited. + Use to audit wallet security and revoke risky approvals. +metadata: + author: mefai + version: "1.0" +--- + +# Approval Scanner + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Approval Scanner | Check token approvals across 9 BSC DEX routers, flag unlimited allowances | Wallet security audit and approval management | + +## Use Cases + +1. **Wallet Security Audit**: Scan a wallet for unlimited token approvals to DEX routers that could be exploited if a router contract is compromised +2. **Approval Cleanup**: Identify stale or excessive approvals that should be revoked to reduce attack surface +3. **Risk Assessment**: Quantify the total value at risk through outstanding token approvals + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Approval Scanner + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/approval-scanner +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| address | string | Yes | Wallet address to scan (0x...) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/approval-scanner?address=0x8894E0a0c962CB723c1ef8a1B6737B4E1e2AE02b' +``` + +**Response Example**: +```json +{ + "address": "0x8894E0a0c962CB723c1ef8a1B6737B4E1e2AE02b", + "total_approvals": 5, + "unlimited_approvals": 2, + "routers_checked": 9, + "approvals": [ + { + "token": "0x55d398326f99059fF775485246999027B3197955", + "token_symbol": "USDT", + "spender": "0x10ED43C718714eb63d5aA57B78B54917c3e6fE49", + "spender_name": "PancakeSwap V2 Router", + "allowance": "unlimited", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935", + "risk": "HIGH" + }, + { + "token": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "token_symbol": "CAKE", + "spender": "0x13f4EA83D0bd40E75C8222255bc855a974568Dd4", + "spender_name": "PancakeSwap V3 Router", + "allowance": "1000.00", + "allowance_raw": "1000000000000000000000", + "risk": "LOW" + } + ], + "routers": [ + "PancakeSwap V2 Router", + "PancakeSwap V3 Router", + "BiSwap Router", + "ApeSwap Router", + "BabySwap Router", + "MDEX Router", + "BakerySwap Router", + "Uniswap V3 Router", + "1inch Router" + ] +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| address | string | Wallet address scanned | +| total_approvals | number | Total number of active approvals found | +| unlimited_approvals | number | Count of unlimited (max uint256) approvals | +| routers_checked | number | Number of DEX routers checked | +| approvals | array | List of approval details | +| approvals[].token | string | Token contract address | +| approvals[].token_symbol | string | Token symbol | +| approvals[].spender | string | Spender (router) contract address | +| approvals[].spender_name | string | Human-readable router name | +| approvals[].allowance | string | Allowance amount (or "unlimited") | +| approvals[].allowance_raw | string | Raw allowance in wei | +| approvals[].risk | string | Risk level: HIGH, MEDIUM, or LOW | +| routers | array | List of all routers checked | + +--- + +## Notes + +1. Checks approvals across 9 major BSC DEX routers via `eth_call` to each token's `allowance()` function +2. Responses are cached with 30s fresh TTL +3. No authentication required +4. "Unlimited" means the approval was set to max uint256 (2^256 - 1) diff --git a/skills/security/contract-audit/SKILL.md b/skills/security/contract-audit/SKILL.md new file mode 100644 index 0000000..edef213 --- /dev/null +++ b/skills/security/contract-audit/SKILL.md @@ -0,0 +1,105 @@ +--- +name: contract-audit +description: | + Smart contract security analysis for BSC. Checks ownership status, proxy + patterns, token standard compliance, and common vulnerability patterns. + Use for a quick security overview of any BSC smart contract. +metadata: + author: mefai + version: "1.0" +--- + +# Contract Audit + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Contract Audit | Smart contract security analysis: ownership, proxy detection, token standard compliance, vulnerability patterns | Quick security overview of BSC contracts | + +## Use Cases + +1. **Quick Contract Review**: Get a fast security overview of a contract before interacting with it +2. **Token Standard Verification**: Confirm a token properly implements ERC20/ERC721 standards +3. **Ownership Analysis**: Check whether contract ownership is renounced, multi-sig, or held by a single EOA + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Contract Audit + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/contract-audit +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| address | string | Yes | Contract address to audit (0x...) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/contract-audit?address=0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82' +``` + +**Response Example**: +```json +{ + "address": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "is_contract": true, + "bytecode_size": 12847, + "owner": "0x73feaa1ee314f8c655e354234017bE2193C9E24E", + "owner_type": "contract", + "is_renounced": false, + "is_proxy": false, + "token_standard": "ERC20", + "has_mint": true, + "has_pause": false, + "has_blacklist": false, + "has_fee_on_transfer": false, + "compliant": true, + "findings": [ + {"severity": "INFO", "message": "Contract has mint function — supply is not fixed"}, + {"severity": "INFO", "message": "Owner is a contract (likely governance/timelock)"} + ], + "audit_score": 85, + "verdict": "PASS" +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| address | string | Contract address audited | +| is_contract | boolean | Whether the address is a contract | +| bytecode_size | number | Deployed bytecode size in bytes | +| owner | string | Current owner address | +| owner_type | string | Owner type: EOA, contract, or renounced | +| is_renounced | boolean | Whether ownership is renounced | +| is_proxy | boolean | Whether the contract is a proxy | +| token_standard | string | Detected token standard (ERC20, ERC721, etc.) | +| has_mint | boolean | Whether mint function exists | +| has_pause | boolean | Whether pause function exists | +| has_blacklist | boolean | Whether blacklist function exists | +| has_fee_on_transfer | boolean | Whether fee-on-transfer logic exists | +| compliant | boolean | Whether token standard is properly implemented | +| findings | array | List of audit findings with severity and message | +| audit_score | number | Audit score 0-100 (higher is better) | +| verdict | string | PASS, CAUTION, or FAIL | + +--- + +## Notes + +1. Analyzes bytecode and on-chain state via BSC JSON-RPC +2. Responses are cached with 30s fresh TTL +3. No authentication required +4. Audit score considers ownership, proxy risk, dangerous functions, and standard compliance diff --git a/skills/security/contract-xray/SKILL.md b/skills/security/contract-xray/SKILL.md new file mode 100644 index 0000000..a5c4512 --- /dev/null +++ b/skills/security/contract-xray/SKILL.md @@ -0,0 +1,109 @@ +--- +name: contract-xray +description: | + Deep bytecode analysis for BSC smart contracts. Detects proxy patterns, + scans for mint/pause/blacklist functions, identifies dangerous selectors, + and classifies contract type. Use for security auditing of any BSC contract. +metadata: + author: mefai + version: "1.0" +--- + +# Contract X-Ray + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Contract X-Ray | Deep bytecode inspection: proxy detection, function selector scanning, dangerous pattern identification | Security audit of BSC smart contracts | + +## Use Cases + +1. **Security Audit**: Analyze a contract's bytecode for proxy patterns, minting capabilities, pausability, and blacklist functions before interacting with it +2. **Proxy Detection**: Identify whether a contract is upgradeable via EIP-1967 or other proxy patterns +3. **Function Discovery**: Extract and classify all function selectors present in the bytecode + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Contract X-Ray + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/contract-xray +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| address | string | Yes | Smart contract address (0x...) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/contract-xray?address=0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82' +``` + +**Response Example**: +```json +{ + "address": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "is_contract": true, + "bytecode_size": 12847, + "is_proxy": false, + "proxy_type": null, + "implementation": null, + "has_selfdestruct": false, + "has_delegatecall": false, + "has_mint": true, + "has_pause": false, + "has_blacklist": false, + "has_fee_on_transfer": false, + "function_count": 42, + "selectors": ["0x06fdde03", "0x095ea7b3", "0x18160ddd", "0x23b872dd"], + "known_functions": { + "0x06fdde03": "name()", + "0x095ea7b3": "approve(address,uint256)", + "0x18160ddd": "totalSupply()", + "0x23b872dd": "transferFrom(address,address,uint256)" + }, + "dangerous_patterns": [], + "contract_type": "ERC20" +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| address | string | Contract address analyzed | +| is_contract | boolean | Whether the address contains bytecode | +| bytecode_size | number | Size of the deployed bytecode in bytes | +| is_proxy | boolean | Whether the contract is a proxy | +| proxy_type | string/null | Proxy pattern type (EIP-1967, EIP-1822, etc.) | +| implementation | string/null | Implementation contract address if proxy | +| has_selfdestruct | boolean | Whether bytecode contains SELFDESTRUCT opcode | +| has_delegatecall | boolean | Whether bytecode contains DELEGATECALL opcode | +| has_mint | boolean | Whether a mint function is detected | +| has_pause | boolean | Whether a pause function is detected | +| has_blacklist | boolean | Whether blacklist functionality is detected | +| has_fee_on_transfer | boolean | Whether fee-on-transfer logic is detected | +| function_count | number | Number of public function selectors found | +| selectors | array | List of 4-byte function selectors | +| known_functions | object | Mapping of selectors to known function signatures | +| dangerous_patterns | array | List of dangerous bytecode patterns detected | +| contract_type | string | Classified contract type (ERC20, ERC721, etc.) | + +--- + +## Notes + +1. Bytecode analysis is performed directly via BSC JSON-RPC `eth_getCode` +2. Responses are cached with a 5-minute fresh TTL due to immutable bytecode +3. No authentication required +4. Proxy contracts will show the proxy's bytecode; use `implementation` address for the actual logic diff --git a/skills/security/honeypot-check/SKILL.md b/skills/security/honeypot-check/SKILL.md new file mode 100644 index 0000000..a291ab4 --- /dev/null +++ b/skills/security/honeypot-check/SKILL.md @@ -0,0 +1,101 @@ +--- +name: honeypot-check +description: | + Combined honeypot detection for BSC tokens. Analyzes bytecode for dangerous + selectors, checks buy/sell ratio, owner concentration, and proxy patterns. + Use when evaluating whether a token contract may be a honeypot or scam. +metadata: + author: mefai + version: "1.0" +--- + +# Honeypot Check + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Honeypot Check | Multi-signal honeypot detection combining bytecode analysis, trading ratio, holder concentration, and dangerous function selectors | Evaluate token safety before trading | + +## Use Cases + +1. **Pre-Trade Safety Check**: Before buying a token, verify it is not a honeypot by checking for sell-blocking bytecode patterns and dangerous function selectors +2. **Token Audit**: Assess a token's risk profile by analyzing owner concentration, buy/sell ratios, and proxy contract patterns +3. **Portfolio Risk Review**: Scan tokens in a portfolio for honeypot characteristics that may indicate emerging rug-pull risk + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Honeypot Check + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/honeypot-check +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| address | string | Yes | Token contract address (0x...) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/honeypot-check?address=0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82' +``` + +**Response Example**: +```json +{ + "address": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "is_honeypot": false, + "honeypot_score": 8, + "checks": { + "has_dangerous_selectors": false, + "has_proxy": false, + "has_mint": true, + "has_pause": false, + "has_blacklist": false, + "buy_sell_ratio": 0.97, + "top_holder_pct": 12.4, + "owner_is_renounced": false + }, + "dangerous_selectors": [], + "verdict": "LOW RISK", + "details": "No honeypot indicators detected. Token has mint function but no pause or blacklist." +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| address | string | Token contract address analyzed | +| is_honeypot | boolean | Whether the token is flagged as a honeypot | +| honeypot_score | number | Risk score 0-100 (higher = more dangerous) | +| checks | object | Individual check results | +| checks.has_dangerous_selectors | boolean | Whether bytecode contains known honeypot selectors | +| checks.has_proxy | boolean | Whether contract is a proxy (upgradeable) | +| checks.has_mint | boolean | Whether contract has a mint function | +| checks.has_pause | boolean | Whether contract has a pause function | +| checks.has_blacklist | boolean | Whether contract has blacklist functionality | +| checks.buy_sell_ratio | number | Ratio of buy to sell transactions (near 1.0 is healthy) | +| checks.top_holder_pct | number | Percentage held by the largest holder | +| checks.owner_is_renounced | boolean | Whether contract ownership is renounced | +| dangerous_selectors | array | List of dangerous function selectors found in bytecode | +| verdict | string | Human-readable risk verdict | +| details | string | Detailed explanation of findings | + +--- + +## Notes + +1. All data is sourced from BSC JSON-RPC bytecode analysis and DexScreener APIs +2. Responses are cached with stale-while-revalidate pattern (30s fresh TTL) +3. No authentication required +4. A honeypot_score above 60 warrants extreme caution diff --git a/skills/security/risk-radar/SKILL.md b/skills/security/risk-radar/SKILL.md new file mode 100644 index 0000000..8c9f684 --- /dev/null +++ b/skills/security/risk-radar/SKILL.md @@ -0,0 +1,91 @@ +--- +name: risk-radar +description: | + Token risk scoring engine for BSC. Produces a 0-100 risk score and A-F + letter grade by combining on-chain signals (holder distribution, liquidity, + contract patterns) with market signals (volume, price action). +metadata: + author: mefai + version: "1.0" +--- + +# Risk Radar + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Risk Radar | Multi-signal risk scoring (0-100) with letter grade (A-F) combining on-chain and market data | Quantitative token risk assessment | + +## Use Cases + +1. **Token Due Diligence**: Get a quantitative risk score before investing in a BSC token +2. **Portfolio Risk Monitoring**: Periodically check risk scores of held tokens for deteriorating fundamentals +3. **Comparative Analysis**: Compare risk grades across multiple tokens to allocate capital to lower-risk opportunities + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Risk Radar + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/risk-radar +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| address | string | Yes | Token contract address (0x...) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/risk-radar?address=0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82' +``` + +**Response Example**: +```json +{ + "address": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "symbol": "CAKE", + "risk_score": 18, + "grade": "A", + "signals": { + "holder_concentration": {"score": 15, "detail": "Top holder owns 8.2% — well distributed"}, + "liquidity_depth": {"score": 5, "detail": "$42.3M total liquidity across 12 pairs"}, + "contract_risk": {"score": 20, "detail": "Has mint function, ownership not renounced"}, + "volume_health": {"score": 10, "detail": "$28.5M 24h volume, healthy volume/mcap ratio"}, + "price_stability": {"score": 25, "detail": "7.2% max drawdown in 24h"}, + "age_factor": {"score": 0, "detail": "Contract deployed 1,247 days ago"} + }, + "recommendation": "Low risk. Established token with deep liquidity and diversified holder base." +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| address | string | Token contract address | +| symbol | string | Token symbol | +| risk_score | number | Composite risk score 0-100 (lower is safer) | +| grade | string | Letter grade: A (0-20), B (21-40), C (41-60), D (61-80), F (81-100) | +| signals | object | Individual risk signal breakdowns | +| signals.*.score | number | Individual signal score contribution | +| signals.*.detail | string | Human-readable explanation | +| recommendation | string | Overall risk assessment summary | + +--- + +## Notes + +1. Combines BSC JSON-RPC on-chain data with DexScreener market data +2. Responses are cached with 60s fresh TTL +3. No authentication required +4. Grade thresholds: A (0-20), B (21-40), C (41-60), D (61-80), F (81-100) diff --git a/skills/security/upgrade-monitor/SKILL.md b/skills/security/upgrade-monitor/SKILL.md new file mode 100644 index 0000000..807d7d8 --- /dev/null +++ b/skills/security/upgrade-monitor/SKILL.md @@ -0,0 +1,92 @@ +--- +name: upgrade-monitor +description: | + EIP-1967 proxy contract detector for BSC. Inspects storage slots to identify + proxy patterns, find implementation addresses, and identify admin accounts. + Use when assessing upgrade risk of a smart contract. +metadata: + author: mefai + version: "1.0" +--- + +# Upgrade Monitor + +## Overview + +| API | Function | Use Case | +|-----|----------|----------| +| Upgrade Monitor | Detect proxy contracts via EIP-1967 storage slot inspection, identify admin and implementation addresses | Assess upgrade risk of BSC smart contracts | + +## Use Cases + +1. **Proxy Detection**: Determine whether a contract is upgradeable by checking EIP-1967 storage slots +2. **Admin Identification**: Find the admin address that controls proxy upgrades +3. **Implementation Tracking**: Identify the current implementation contract behind a proxy + +## Supported Chains + +| Chain Name | chainId | +|------------|---------| +| BSC | 56 | + +--- + +## API: Upgrade Monitor + +### Method: GET + +**URL**: +``` +https://mefai.io/superbsc/api/bnbchain/mefai/upgrade-monitor +``` + +**Request Parameters**: +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| address | string | Yes | Contract address to inspect (0x...) | + +**Example Request**: +```bash +curl 'https://mefai.io/superbsc/api/bnbchain/mefai/upgrade-monitor?address=0x55d398326f99059fF775485246999027B3197955' +``` + +**Response Example**: +```json +{ + "address": "0x55d398326f99059fF775485246999027B3197955", + "is_proxy": false, + "proxy_type": null, + "implementation": null, + "admin": null, + "beacon": null, + "storage_slots": { + "eip1967_implementation": "0x0000000000000000000000000000000000000000000000000000000000000000", + "eip1967_admin": "0x0000000000000000000000000000000000000000000000000000000000000000", + "eip1967_beacon": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "bytecode_has_delegatecall": false, + "upgrade_risk": "NONE" +} +``` + +**Response Fields**: +| Field | Type | Description | +|-------|------|-------------| +| address | string | Contract address inspected | +| is_proxy | boolean | Whether the contract is a proxy | +| proxy_type | string/null | Proxy standard detected (EIP-1967, EIP-1822, custom) | +| implementation | string/null | Implementation contract address | +| admin | string/null | Admin address that can trigger upgrades | +| beacon | string/null | Beacon contract address (if beacon proxy) | +| storage_slots | object | Raw EIP-1967 storage slot values | +| bytecode_has_delegatecall | boolean | Whether DELEGATECALL opcode is present | +| upgrade_risk | string | Risk level: NONE, LOW, MEDIUM, HIGH, CRITICAL | + +--- + +## Notes + +1. Inspects EIP-1967 standard storage slots via `eth_getStorageAt` +2. Responses are cached with 30s fresh TTL +3. No authentication required +4. HIGH/CRITICAL upgrade risk means the contract can be changed at any time by the admin