| |
| """ |
| AirMicroDrip Hugging Face Space |
| Unified Flask app serving API and static UI |
| No mocks - real data from all AirMicroDrip systems |
| """ |
|
|
| from flask import Flask, jsonify, request, render_template_string |
| from flask_cors import CORS |
| import sqlite3 |
| import json |
| import hashlib |
| import threading |
| from datetime import datetime |
| import sys |
| import os |
| import requests |
| import logging |
|
|
| logger = logging.getLogger(__name__) |
|
|
| |
| from slippage_collector import SlippageCollector, create_collector |
| from holder_tracker import HolderTracker |
| from llm_liquidity_provider import InferenceRegistry, LiquidityConverter, PerformanceMonitor |
| from merkle_token_launch import build_launch_manifest |
| from hf_account_collateral import scan_hf_account_collateral |
| from token_launcher import autonomously_create_token, get_launch_status, get_keypair_backup, get_existing_mint |
|
|
| app = Flask(__name__) |
| CORS(app) |
| COLLATERAL_SCAN_JOBS = {} |
|
|
| |
| HOLDER_DB = "holder_tracker/holder_registry.db" |
| INFERENCE_DB = "llm_liquidity_provider/inference_registry.db" |
| TRADING_DB = "perp_trading_engine/perp_trading.db" |
| LAUNCH_DB = "token_launch/launch_registry.db" |
| COLLATERAL_DB = "token_launch/collateral_registry.db" |
| SPACE_REPO_ID = os.environ.get("SPACE_ID") or os.environ.get("HF_SPACE_ID") or "josephrw/ce" |
| SPACE_OWNER = SPACE_REPO_ID.split("/")[0] |
| DEFAULT_TOKEN_MINT = "So11111111111111111111111111111111111111112" |
| DEFAULT_SOLANA_RPC_URL = "https://api.mainnet-beta.solana.com" |
|
|
| |
| os.makedirs('holder_tracker', exist_ok=True) |
| os.makedirs('llm_liquidity_provider', exist_ok=True) |
| os.makedirs('perp_trading_engine', exist_ok=True) |
| os.makedirs('token_launch', exist_ok=True) |
|
|
| |
| def init_databases(): |
| """Initialize all databases""" |
| |
| conn = sqlite3.connect(HOLDER_DB) |
| cursor = conn.cursor() |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS holders ( |
| address TEXT PRIMARY KEY, |
| current_balance REAL DEFAULT 0, |
| first_seen TIMESTAMP, |
| eligible BOOLEAN DEFAULT FALSE, |
| eligibility_timestamp TIMESTAMP |
| ) |
| """) |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS transfers ( |
| transfer_id TEXT PRIMARY KEY, |
| from_address TEXT, |
| to_address TEXT, |
| amount REAL, |
| timestamp TIMESTAMP |
| ) |
| """) |
| conn.commit() |
| conn.close() |
|
|
| |
| conn = sqlite3.connect(LAUNCH_DB) |
| cursor = conn.cursor() |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS launch_manifests ( |
| manifest_hash TEXT PRIMARY KEY, |
| merkle_root TEXT NOT NULL, |
| status TEXT NOT NULL, |
| execution_status TEXT NOT NULL, |
| manifest_json TEXT NOT NULL, |
| created_at TIMESTAMP NOT NULL |
| ) |
| """) |
| conn.commit() |
| conn.close() |
|
|
| |
| conn = sqlite3.connect(COLLATERAL_DB) |
| cursor = conn.cursor() |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS account_collateral ( |
| owner TEXT PRIMARY KEY, |
| collateral_root TEXT NOT NULL, |
| collateral_json TEXT NOT NULL, |
| scanned_at TIMESTAMP NOT NULL |
| ) |
| """) |
| conn.commit() |
| conn.close() |
| |
| |
| conn = sqlite3.connect(INFERENCE_DB) |
| cursor = conn.cursor() |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS providers ( |
| provider_id TEXT PRIMARY KEY, |
| wallet_address TEXT, |
| model_type TEXT, |
| registered_at TIMESTAMP, |
| status TEXT DEFAULT 'pending', |
| reputation_score REAL DEFAULT 0.5, |
| total_earnings REAL DEFAULT 0.0 |
| ) |
| """) |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS capacity ( |
| provider_id TEXT, |
| tokens_per_second REAL, |
| latency_ms REAL, |
| uptime_percentage REAL, |
| quality_score REAL, |
| verified_at TIMESTAMP, |
| FOREIGN KEY (provider_id) REFERENCES providers(provider_id) |
| ) |
| """) |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS liquidity_allocations ( |
| provider_id TEXT, |
| synthetic_liquidity_usd REAL, |
| liquidity_tokens REAL, |
| market_allocation TEXT, |
| allocated_at TIMESTAMP, |
| FOREIGN KEY (provider_id) REFERENCES providers(provider_id) |
| ) |
| """) |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS rewards ( |
| reward_id TEXT PRIMARY KEY, |
| provider_id TEXT, |
| amount REAL, |
| source TEXT, |
| multiplier REAL, |
| timestamp TIMESTAMP, |
| FOREIGN KEY (provider_id) REFERENCES providers(provider_id) |
| ) |
| """) |
| conn.commit() |
| conn.close() |
| |
| |
| conn = sqlite3.connect(TRADING_DB) |
| cursor = conn.cursor() |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS orders ( |
| order_id TEXT PRIMARY KEY, |
| trader TEXT, |
| market TEXT, |
| side TEXT, |
| order_type TEXT, |
| size REAL, |
| price REAL, |
| stop_price REAL, |
| leverage INTEGER, |
| status TEXT, |
| filled_size REAL, |
| avg_fill_price REAL, |
| created_at TIMESTAMP, |
| updated_at TIMESTAMP |
| ) |
| """) |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS positions ( |
| position_id TEXT PRIMARY KEY, |
| trader TEXT, |
| market TEXT, |
| side TEXT, |
| size REAL, |
| entry_price REAL, |
| leverage INTEGER, |
| margin REAL, |
| unrealized_pnl REAL, |
| realized_pnl REAL, |
| liquidation_price REAL, |
| opened_at TIMESTAMP, |
| updated_at TIMESTAMP |
| ) |
| """) |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS trades ( |
| trade_id TEXT PRIMARY KEY, |
| order_id TEXT, |
| market TEXT, |
| side TEXT, |
| size REAL, |
| price REAL, |
| fee REAL, |
| timestamp TIMESTAMP |
| ) |
| """) |
| cursor.execute(""" |
| CREATE TABLE IF NOT EXISTS funding_rates ( |
| market TEXT, |
| rate REAL, |
| timestamp TIMESTAMP, |
| PRIMARY KEY (market, timestamp) |
| ) |
| """) |
| conn.commit() |
| conn.close() |
|
|
| init_databases() |
|
|
| def _token_mint(): |
| """Use a public no-key default unless a token mint is explicitly configured.""" |
| return os.environ.get("TOKEN_MINT", DEFAULT_TOKEN_MINT).strip() |
|
|
| def _inference_url(): |
| """Inference endpoints are optional and never require app-level API keys.""" |
| return os.environ.get("INFERENCE_API_URL", "").strip() |
|
|
| def _integration_config(): |
| """Return public-safe integration configuration status.""" |
| token_mint = _token_mint() |
| inference_url = _inference_url() |
| solana_rpc_url = os.environ.get("SOLANA_RPC_URL", DEFAULT_SOLANA_RPC_URL).strip() |
| return { |
| "token_mint": { |
| "configured": True, |
| "defaulted": token_mint == DEFAULT_TOKEN_MINT, |
| "label": "Solana token mint", |
| "env": "optional override", |
| "value_public": token_mint, |
| "status": "wired", |
| }, |
| "inference_endpoint": { |
| "configured": bool(inference_url), |
| "label": "LLM inference endpoint", |
| "env": "optional local endpoint", |
| "status": "wired" if inference_url else "local_only", |
| }, |
| "solana_rpc": { |
| "configured": True, |
| "label": "Solana RPC", |
| "env": "public default", |
| "status": "wired", |
| }, |
| "gateio_market_data": { |
| "configured": True, |
| "label": "Gate.io public market data", |
| "env": None, |
| "status": "wired", |
| }, |
| "api_keys": { |
| "configured": True, |
| "label": "API keys", |
| "env": None, |
| "status": "not_required", |
| }, |
| } |
|
|
| def _status_meta(status, label=None, detail=None): |
| """Consistent UI status payload: real backend, no synthetic success.""" |
| copy = { |
| "active": ("active", "Real backend route responded with usable data."), |
| "pending": ("waiting", "Backend is live; the public source has not returned usable data yet."), |
| "not_wired": ("not wired", "Optional integration is disabled."), |
| "local_only": ("local only", "No API key is required; live external benchmark is optional."), |
| "unsigned_ready": ("unsigned ready", "Merkle manifest is verified locally and waiting for a real wallet signature."), |
| "ready_for_signature": ("ready for signature", "Collateral evidence is scanned and waiting for owner wallet signature."), |
| "not_scanned": ("not scanned", "Collateral scan has not run yet."), |
| "error": ("error", "The backend route returned an error."), |
| } |
| display, default_detail = copy.get(status, (status, "Unknown status.")) |
| return { |
| "status": status, |
| "display": display, |
| "label": label or display, |
| "detail": detail or default_detail, |
| } |
|
|
| |
| def _sync_real_data(): |
| """Sync real data from external APIs into local SQLite DBs""" |
| token_mint = _token_mint() |
| |
| |
| if not token_mint: |
| try: |
| existing = get_existing_mint() |
| if existing: |
| print(f"[startup] Using existing auto-created mint: {existing}") |
| os.environ["TOKEN_MINT"] = existing |
| token_mint = existing |
| else: |
| print("[startup] No TOKEN_MINT set. Auto-creating devnet token...") |
| result = autonomously_create_token() |
| if result["status"] == "ok": |
| mint = result["mint_address"] |
| os.environ["TOKEN_MINT"] = mint |
| token_mint = mint |
| print(f"[startup] Auto-created token: {mint}") |
| print(f"[startup] Wallet: {result['wallet_pubkey']}") |
| print(f"[startup] BACKUP REQUIRED: Visit /api/token/backup to download keypair") |
| else: |
| print(f"[startup] Auto-token creation failed: {result.get('message')}") |
| except Exception as e: |
| print(f"[startup] Token auto-creation error: {e}") |
| |
| |
| if token_mint: |
| try: |
| collector = create_collector(token_mint, "drippage_pool_placeholder") |
| count = collector.process_real_trades() |
| print(f"[startup] Synced {count} slippage collections from DexScreener") |
| except Exception as e: |
| print(f"[startup] Slippage sync skipped: {e}") |
| |
| |
| if token_mint: |
| try: |
| tracker = HolderTracker(token_mint, db_path=HOLDER_DB) |
| count = tracker.sync_holders_from_chain() |
| print(f"[startup] Synced {count} holders from Solana RPC") |
| except Exception as e: |
| print(f"[startup] Holder sync skipped: {e}") |
| |
| |
| inference_url = _inference_url() |
| if inference_url: |
| try: |
| registry = InferenceRegistry(db_path=INFERENCE_DB) |
| converter = LiquidityConverter(registry) |
| monitor = PerformanceMonitor(registry) |
| bench = monitor._benchmark_inference_endpoint(inference_url, "llama2") |
| if bench["status"] == "verified": |
| |
| registry.register_provider("prov_hf_001", "hf_worker", "llama2") |
| registry.verify_capacity( |
| "prov_hf_001", |
| bench["tokens_per_second"], |
| bench["latency_ms"], |
| 99.0, |
| bench["quality_score"], |
| ) |
| print(f"[startup] Inference benchmark: {bench['tokens_per_second']:.1f} t/s, {bench['latency_ms']:.1f}ms") |
| else: |
| print(f"[startup] Inference endpoint unreachable at {inference_url}") |
| except Exception as e: |
| print(f"[startup] Inference benchmark skipped: {e}") |
| else: |
| print("[startup] No inference endpoint configured; LLM liquidity stays no-key local-only") |
|
|
| _sync_real_data() |
|
|
| |
| @app.route('/health', methods=['GET']) |
| def health(): |
| """Health check endpoint""" |
| config = _integration_config() |
| return jsonify({ |
| "status": "healthy", |
| "timestamp": datetime.utcnow().isoformat(), |
| "mode": "no_mock_real_backend", |
| "configured_integrations": config, |
| "systems": { |
| "slippage_collector": "active" if config["token_mint"]["configured"] else "not_wired", |
| "holder_tracker": "active" if config["token_mint"]["configured"] else "not_wired", |
| "llm_liquidity": "active" if config["inference_endpoint"]["configured"] else "local_only", |
| "merkle_token_launch": "unsigned_ready", |
| "hf_account_collateral": _collateral_summary()["status"], |
| "trading_engine": "active", |
| "funding_engine": "active", |
| "liquidation_system": "active", |
| "mining_rewards": "active", |
| } |
| }) |
|
|
| @app.route('/api/config', methods=['GET']) |
| def config_status(): |
| """Public-safe integration status. Does not expose secrets.""" |
| return jsonify({ |
| "mode": "no_mock_real_backend", |
| "timestamp": datetime.utcnow().isoformat(), |
| "integrations": _integration_config(), |
| "principles": [ |
| "No fake holders, liquidity, trades, payouts, or inference benchmarks.", |
| "Optional external sources return local_only, waiting, or error states.", |
| "Dashboard metrics are derived from local DBs or real public APIs.", |
| ], |
| }) |
|
|
| def _launch_source_metrics(): |
| """Use local persisted state as launch-tree inputs without inventing activity.""" |
| owner_profile = _hf_owner_profile() |
| metrics = { |
| "token_mint_source": _token_mint(), |
| "space_repo_id": SPACE_REPO_ID, |
| "space_owner": SPACE_OWNER, |
| "owner_repository_count": owner_profile["total_repositories"], |
| "owner_repository_hash": owner_profile["repository_index_hash"], |
| "generated_from": "airmicrodrip_runtime_ledgers", |
| "holders": 0, |
| "eligible_holders": 0, |
| "active_llm_providers": 0, |
| "synthetic_liquidity_usd": 0, |
| "active_positions": 0, |
| "total_rewards": 0, |
| } |
|
|
| try: |
| conn = sqlite3.connect(HOLDER_DB) |
| cursor = conn.cursor() |
| cursor.execute("SELECT COUNT(*) FROM holders") |
| metrics["holders"] = cursor.fetchone()[0] |
| cursor.execute("SELECT COUNT(*) FROM holders WHERE eligible = TRUE") |
| metrics["eligible_holders"] = cursor.fetchone()[0] |
| conn.close() |
| except Exception as e: |
| metrics["holder_metric_error"] = str(e) |
|
|
| try: |
| conn = sqlite3.connect(INFERENCE_DB) |
| cursor = conn.cursor() |
| cursor.execute("SELECT COUNT(*) FROM providers WHERE status = 'active'") |
| metrics["active_llm_providers"] = cursor.fetchone()[0] |
| cursor.execute("SELECT SUM(synthetic_liquidity_usd) FROM liquidity_allocations") |
| metrics["synthetic_liquidity_usd"] = cursor.fetchone()[0] or 0 |
| cursor.execute("SELECT SUM(amount) FROM rewards") |
| metrics["total_rewards"] = cursor.fetchone()[0] or 0 |
| conn.close() |
| except Exception as e: |
| metrics["liquidity_metric_error"] = str(e) |
|
|
| try: |
| conn = sqlite3.connect(TRADING_DB) |
| cursor = conn.cursor() |
| cursor.execute("SELECT COUNT(*) FROM positions WHERE size > 0") |
| metrics["active_positions"] = cursor.fetchone()[0] |
| conn.close() |
| except Exception as e: |
| metrics["trading_metric_error"] = str(e) |
|
|
| return metrics |
|
|
| def _owner_token_symbol(owner): |
| clean = "".join(ch for ch in owner.upper() if ch.isalnum()) |
| if not clean: |
| clean = "OWNER" |
| return f"{clean[:4]}CE" |
|
|
| def _owner_token_spec(owner_profile): |
| symbol = _owner_token_symbol(owner_profile["owner"]) |
| return { |
| "name": f"{owner_profile['owner']} Compute Exchange", |
| "symbol": symbol, |
| "decimals": 9, |
| "network": "solana-mainnet", |
| "total_supply": 1_000_000_000, |
| "derived_from_space": SPACE_REPO_ID, |
| "derived_from_owner": owner_profile["owner"], |
| "derived_repository_count": owner_profile["total_repositories"], |
| "repository_index_hash": owner_profile["repository_index_hash"], |
| } |
|
|
| def _owner_pool_spec(token_spec): |
| return { |
| "dex": "meteora-or-raydium", |
| "pair": f"{token_spec['symbol']}/SOL", |
| "base_asset": token_spec["symbol"], |
| "quote_asset": "SOL", |
| "initial_ce_liquidity": 100_000_000, |
| "initial_quote_liquidity_required": "external_wallet_signature_required", |
| "lp_lock": "root_committed", |
| "derived_from_owner": token_spec["derived_from_owner"], |
| } |
|
|
| def _hf_api_list(path, owner): |
| try: |
| response = requests.get( |
| f"https://huggingface.co/api/{path}", |
| params={"author": owner, "limit": 100}, |
| timeout=10, |
| ) |
| if response.status_code == 200: |
| return response.json() |
| except Exception as e: |
| logger.warning("HF %s fetch skipped: %s", path, e) |
| return [] |
|
|
| def _hf_owner_profile(owner=None): |
| owner = owner or SPACE_OWNER |
| repo_records = [] |
| for repo_type, path in (("model", "models"), ("dataset", "datasets"), ("space", "spaces")): |
| for item in _hf_api_list(path, owner): |
| repo_id = item.get("id") or item.get("name") |
| if not repo_id: |
| continue |
| repo_records.append({ |
| "repo_type": repo_type, |
| "repo_id": repo_id, |
| "likes": item.get("likes", 0), |
| "downloads": item.get("downloads", 0), |
| "last_modified": item.get("lastModified") or item.get("updatedAt"), |
| "sdk": item.get("sdk"), |
| }) |
|
|
| repo_records.sort(key=lambda item: (item["repo_type"], item["repo_id"])) |
| repo_index_json = json.dumps(repo_records, sort_keys=True, separators=(",", ":")) |
| return { |
| "owner": owner, |
| "space_repo_id": SPACE_REPO_ID, |
| "total_repositories": len(repo_records), |
| "repository_index_hash": __import__("hashlib").sha256(repo_index_json.encode("utf-8")).hexdigest(), |
| "repositories": repo_records, |
| } |
|
|
| def _persist_collateral(collateral): |
| conn = sqlite3.connect(COLLATERAL_DB) |
| cursor = conn.cursor() |
| cursor.execute(""" |
| INSERT OR REPLACE INTO account_collateral |
| (owner, collateral_root, collateral_json, scanned_at) |
| VALUES (?, ?, ?, ?) |
| """, ( |
| collateral["owner"], |
| collateral["collateral_root"], |
| json.dumps(collateral, sort_keys=True), |
| collateral["scanned_at"], |
| )) |
| conn.commit() |
| conn.close() |
|
|
| def _latest_collateral(owner=None): |
| owner = owner or SPACE_OWNER |
| conn = sqlite3.connect(COLLATERAL_DB) |
| cursor = conn.cursor() |
| cursor.execute(""" |
| SELECT collateral_json |
| FROM account_collateral |
| WHERE owner = ? |
| """, (owner,)) |
| row = cursor.fetchone() |
| conn.close() |
| return json.loads(row[0]) if row else None |
|
|
| def _collateral_summary(owner=None): |
| collateral = _latest_collateral(owner) |
| job = COLLATERAL_SCAN_JOBS.get(owner or SPACE_OWNER) |
| if collateral: |
| summary = { |
| "status": collateral["collateral_status"], |
| "owner": collateral["owner"], |
| "collateral_root": collateral["collateral_root"], |
| "repo_count": collateral["repo_count"], |
| "space_count": collateral["space_count"], |
| "total_files": collateral["total_files"], |
| "total_text_files": collateral["total_text_files"], |
| "total_loc": collateral["total_loc"], |
| "collateral_score": collateral["collateral_score"], |
| "scanned_at": collateral["scanned_at"], |
| } |
| if job and job.get("status") == "running": |
| summary["scan_job_status"] = "running" |
| return summary |
| if job: |
| return { |
| "status": job.get("status", "running"), |
| "owner": owner or SPACE_OWNER, |
| "collateral_root": None, |
| "repo_count": 0, |
| "space_count": 0, |
| "total_files": 0, |
| "total_text_files": 0, |
| "total_loc": 0, |
| "collateral_score": 0, |
| "started_at": job.get("started_at"), |
| "message": job.get("message", "Collateral scan is running."), |
| } |
| return { |
| "status": "not_scanned", |
| "owner": owner or SPACE_OWNER, |
| "collateral_root": None, |
| "repo_count": 0, |
| "space_count": 0, |
| "total_files": 0, |
| "total_text_files": 0, |
| "total_loc": 0, |
| "collateral_score": 0, |
| } |
|
|
| def _run_collateral_scan_job(owner): |
| COLLATERAL_SCAN_JOBS[owner] = { |
| "status": "running", |
| "owner": owner, |
| "started_at": datetime.utcnow().isoformat(), |
| "message": "Scanning public HF repos, spaces, files, and readable LOC.", |
| } |
| try: |
| collateral = scan_hf_account_collateral(owner) |
| _persist_collateral(collateral) |
| manifest = _prepare_launch_manifest({"owner": owner}) |
| COLLATERAL_SCAN_JOBS[owner] = { |
| "status": "complete", |
| "owner": owner, |
| "started_at": COLLATERAL_SCAN_JOBS[owner]["started_at"], |
| "completed_at": datetime.utcnow().isoformat(), |
| "message": "Collateral scan completed and launch manifest rebuilt.", |
| "collateral_root": collateral["collateral_root"], |
| "manifest_hash": manifest["manifest_hash"], |
| "merkle_root": manifest["merkle_root"], |
| } |
| except Exception as e: |
| COLLATERAL_SCAN_JOBS[owner] = { |
| "status": "error", |
| "owner": owner, |
| "started_at": COLLATERAL_SCAN_JOBS.get(owner, {}).get("started_at"), |
| "completed_at": datetime.utcnow().isoformat(), |
| "message": str(e), |
| } |
|
|
| def _owner_repo_leaves(owner_profile): |
| return [ |
| { |
| "kind": f"owner_repo:{repo['repo_type']}:{repo['repo_id']}", |
| "payload": repo, |
| } |
| for repo in owner_profile["repositories"] |
| ] |
|
|
| def _collateral_leaves(collateral): |
| if not collateral or collateral.get("status") == "not_scanned": |
| return [] |
| leaves = [{ |
| "kind": f"hf_account_collateral:{collateral['owner']}", |
| "payload": { |
| "owner": collateral["owner"], |
| "collateral_root": collateral["collateral_root"], |
| "repo_count": collateral["repo_count"], |
| "space_count": collateral["space_count"], |
| "total_files": collateral["total_files"], |
| "total_text_files": collateral["total_text_files"], |
| "total_loc": collateral["total_loc"], |
| "collateral_score": collateral["collateral_score"], |
| }, |
| }] |
| for repo in collateral["repositories"]: |
| leaves.append({ |
| "kind": f"hf_repo_collateral:{repo['repo_type']}:{repo['repo_id']}", |
| "payload": { |
| "repo_type": repo["repo_type"], |
| "repo_id": repo["repo_id"], |
| "file_count": repo.get("file_count", 0), |
| "text_file_count": repo.get("text_file_count", 0), |
| "loc": repo.get("loc", 0), |
| "repo_evidence_hash": repo.get("repo_evidence_hash"), |
| }, |
| }) |
| for file_record in repo.get("files", []): |
| leaves.append({ |
| "kind": f"hf_file:{repo['repo_type']}:{repo['repo_id']}:{file_record['path']}", |
| "payload": { |
| "repo_id": repo["repo_id"], |
| "repo_type": repo["repo_type"], |
| "path": file_record["path"], |
| "text": file_record["text"], |
| "loc": file_record["loc"], |
| "sha256": file_record["sha256"], |
| "read_status": file_record["read_status"], |
| }, |
| }) |
| return leaves |
|
|
| def _persist_launch_manifest(manifest): |
| conn = sqlite3.connect(LAUNCH_DB) |
| cursor = conn.cursor() |
| cursor.execute(""" |
| INSERT OR REPLACE INTO launch_manifests |
| (manifest_hash, merkle_root, status, execution_status, manifest_json, created_at) |
| VALUES (?, ?, ?, ?, ?, ?) |
| """, ( |
| manifest["manifest_hash"], |
| manifest["merkle_root"], |
| manifest["status"], |
| manifest["execution_status"], |
| json.dumps(manifest, sort_keys=True), |
| manifest["created_at"], |
| )) |
| conn.commit() |
| conn.close() |
|
|
| def _latest_launch_manifest(): |
| conn = sqlite3.connect(LAUNCH_DB) |
| cursor = conn.cursor() |
| cursor.execute(""" |
| SELECT manifest_json |
| FROM launch_manifests |
| ORDER BY created_at DESC |
| LIMIT 1 |
| """) |
| row = cursor.fetchone() |
| conn.close() |
| return json.loads(row[0]) if row else None |
|
|
| def _prepare_launch_manifest(payload=None): |
| payload = payload or {} |
| owner_profile = _hf_owner_profile(payload.get("owner") or SPACE_OWNER) |
| collateral = _latest_collateral(owner_profile["owner"]) |
| token_spec = payload.get("token_spec") or _owner_token_spec(owner_profile) |
| pool_spec = payload.get("pool_spec") or _owner_pool_spec(token_spec) |
| manifest = build_launch_manifest( |
| token_spec=token_spec, |
| pool_spec=pool_spec, |
| allocations=payload.get("allocations"), |
| source_metrics=_launch_source_metrics(), |
| extra_leaves=_owner_repo_leaves(owner_profile) + _collateral_leaves(collateral), |
| ) |
| manifest["owner_profile"] = owner_profile |
| manifest["account_collateral"] = _collateral_summary(owner_profile["owner"]) |
| _persist_launch_manifest(manifest) |
| return manifest |
|
|
| @app.route('/api/token-launch/status', methods=['GET']) |
| def token_launch_status(): |
| """Return the latest Merkle token-launch manifest, creating one if needed.""" |
| try: |
| manifest = _latest_launch_manifest() or _prepare_launch_manifest() |
| return jsonify(manifest) |
| except Exception as e: |
| return jsonify({"status": "error", "message": str(e)}), 500 |
|
|
| @app.route('/api/token-launch/prepare', methods=['POST']) |
| def token_launch_prepare(): |
| """Prepare a new unsigned Merkle launch manifest from one canonical tree.""" |
| try: |
| payload = request.get_json(silent=True) or {} |
| manifest = _prepare_launch_manifest(payload) |
| return jsonify(manifest), 201 |
| except Exception as e: |
| return jsonify({"status": "error", "message": str(e)}), 500 |
|
|
| @app.route('/api/token-launch/pool-setup', methods=['GET']) |
| def token_launch_pool_setup(): |
| """Return the unsigned token mint and liquidity-pool setup plan.""" |
| try: |
| manifest = _latest_launch_manifest() or _prepare_launch_manifest() |
| return jsonify({ |
| "status": manifest["pool_setup_status"]["status"], |
| "execution_status": manifest["execution_status"], |
| "pool_setup_status": manifest["pool_setup_status"], |
| "merkle_root": manifest["merkle_root"], |
| "manifest_hash": manifest["manifest_hash"], |
| "token_spec": manifest["token_spec"], |
| "pool_spec": manifest["pool_spec"], |
| "unsigned_solana_plan": manifest["unsigned_solana_plan"], |
| "requires_wallet_signature": True, |
| "requires_quote_asset_funding": True, |
| }) |
| except Exception as e: |
| return jsonify({"status": "error", "message": str(e)}), 500 |
|
|
| |
| @app.route('/api/token/create', methods=['POST']) |
| def token_create(): |
| """Autonomously create a new Solana devnet token mint.""" |
| try: |
| payload = request.get_json(silent=True) or {} |
| |
| existing = get_existing_mint() |
| if existing: |
| return jsonify({ |
| "status": "already_exists", |
| "message": "A token mint already exists. Use /api/token/status to view it.", |
| "mint_address": existing, |
| }) |
| result = autonomously_create_token( |
| token_name=payload.get("token_name", "AirMicroDrip"), |
| token_symbol=payload.get("token_symbol", "DRIP"), |
| decimals=payload.get("decimals", 9), |
| ) |
| if result["status"] == "ok": |
| |
| os.environ["TOKEN_MINT"] = result["mint_address"] |
| return jsonify(result) |
| if result["status"] in ("wallet_created_needs_funding", "wallet_ready"): |
| |
| return jsonify(result), 200 |
| return jsonify(result), 400 |
| except Exception as e: |
| return jsonify({"status": "error", "message": str(e)}), 500 |
|
|
| @app.route('/api/token/status', methods=['GET']) |
| def token_status(): |
| """Get the autonomous token launch status.""" |
| try: |
| status = get_launch_status() |
| if not status: |
| return jsonify({ |
| "status": "not_created", |
| "message": "No token mint found. POST to /api/token/create to create one.", |
| }) |
| return jsonify({"status": "ok", "launch": status}) |
| except Exception as e: |
| return jsonify({"status": "error", "message": str(e)}), 500 |
|
|
| @app.route('/api/token/backup', methods=['GET']) |
| def token_backup(): |
| """Download the wallet keypair backup (one-time sensitive operation).""" |
| try: |
| backup = get_keypair_backup() |
| if not backup: |
| return jsonify({ |
| "status": "not_found", |
| "message": "No token launch found. Create one first at /api/token/create", |
| }), 404 |
|
|
| keypair_json = { |
| "pubkey": backup["pubkey"], |
| "secret": backup["secret"], |
| "mint_address": backup["mint_address"], |
| "token_symbol": backup["token_symbol"], |
| "warning": "This is your wallet private key. Store it securely. If lost, this wallet cannot be recovered.", |
| } |
| return jsonify(keypair_json) |
| except Exception as e: |
| return jsonify({"status": "error", "message": str(e)}), 500 |
|
|
| @app.route('/api/collateral/status', methods=['GET']) |
| def collateral_status(): |
| """Return latest HF account collateral scan summary.""" |
| try: |
| owner = request.args.get("owner", SPACE_OWNER) |
| collateral = _latest_collateral(owner) |
| if not collateral: |
| return jsonify(_collateral_summary(owner)) |
| return jsonify(collateral) |
| except Exception as e: |
| return jsonify({"status": "error", "message": str(e)}), 500 |
|
|
| @app.route('/api/collateral/scan', methods=['POST']) |
| def collateral_scan(): |
| """Scan owner public HF repos/spaces/files and rebuild launch manifest.""" |
| try: |
| payload = request.get_json(silent=True) or {} |
| owner = payload.get("owner") or SPACE_OWNER |
| existing = COLLATERAL_SCAN_JOBS.get(owner) |
| if existing and existing.get("status") == "running": |
| return jsonify({ |
| "status": "scan_already_running", |
| "owner": owner, |
| "job": existing, |
| "collateral": _collateral_summary(owner), |
| }), 202 |
|
|
| thread = threading.Thread(target=_run_collateral_scan_job, args=(owner,), daemon=True) |
| thread.start() |
| return jsonify({ |
| "status": "scan_started", |
| "owner": owner, |
| "collateral": _collateral_summary(owner), |
| "requires_wallet_signature": True, |
| "message": "Scanning all public HF repos, spaces, files, and readable LOC in the background.", |
| }), 202 |
| except Exception as e: |
| return jsonify({"status": "error", "message": str(e)}), 500 |
|
|
| def _gateio_tickers(): |
| """Fetch real Gate.io futures tickers""" |
| try: |
| r = requests.get('https://api.gateio.ws/api/v4/futures/usdt/tickers', timeout=10) |
| if r.status_code == 200: |
| return {t['contract']: t for t in r.json()} |
| except Exception as e: |
| logger.warning("Gate.io tickers fetch failed: %s", e) |
| return {} |
|
|
| def _gateio_funding(): |
| """Fetch real Gate.io funding rates""" |
| try: |
| r = requests.get('https://api.gateio.ws/api/v4/futures/usdt/funding_rate', timeout=10) |
| if r.status_code == 200: |
| return {f['contract']: f for f in r.json()} |
| except Exception as e: |
| logger.warning("Gate.io funding fetch failed: %s", e) |
| return {} |
|
|
| @app.route('/api/slippage/stats', methods=['GET']) |
| def slippage_stats(): |
| """Get slippage collection statistics from real DexScreener API""" |
| try: |
| token_mint = _token_mint() |
| |
| collector = create_collector(token_mint, "drippage_pool") |
| collector.process_real_trades() |
| stats = collector.get_collection_stats() |
| |
| |
| from slippage_collector import _fetch_dexscreener_pairs, WhaleDetector |
| pairs = _fetch_dexscreener_pairs(token_mint) |
| detector = WhaleDetector() |
| detector.detect_from_pairs(pairs) |
| whale_stats = detector.get_whale_stats() |
| |
| return jsonify({ |
| "status": stats.get("status", "pending"), |
| "last_fetch": stats.get("last_fetch"), |
| "total_collected_usd": stats.get("total_collected_usd", 0), |
| "total_collections": stats.get("total_collections", 0), |
| "avg_slippage_bps": stats.get("avg_slippage_bps", 0), |
| "recent_collections": stats.get("recent_collections", []), |
| "whale_trades_today": whale_stats.get("total_whale_trades", 0), |
| "total_whale_volume_24h": whale_stats.get("total_volume_24h", 0), |
| }) |
| except Exception as e: |
| return jsonify({"status": "error", "message": str(e)}), 500 |
|
|
| @app.route('/api/holders/stats', methods=['GET']) |
| def holder_stats(): |
| """Get holder statistics from real Solana RPC""" |
| try: |
| token_mint = _token_mint() |
| |
| |
| if token_mint: |
| try: |
| tracker = HolderTracker(token_mint, db_path=HOLDER_DB) |
| tracker.sync_holders_from_chain() |
| except Exception as sync_err: |
| logger.warning("Holder sync warning: %s", sync_err) |
| |
| conn = sqlite3.connect(HOLDER_DB) |
| cursor = conn.cursor() |
| |
| cursor.execute("SELECT COUNT(*) FROM holders") |
| total_holders = cursor.fetchone()[0] |
| |
| cursor.execute("SELECT COUNT(*) FROM holders WHERE eligible = TRUE") |
| eligible_holders = cursor.fetchone()[0] |
| |
| today = datetime.utcnow().date() |
| cursor.execute("SELECT COUNT(*) FROM holders WHERE DATE(first_seen) = ?", (today.isoformat(),)) |
| new_holders_today = cursor.fetchone()[0] |
| |
| cursor.execute("SELECT SUM(current_balance) FROM holders") |
| total_balance = cursor.fetchone()[0] or 0 |
| |
| conn.close() |
| |
| return jsonify({ |
| "status": "active" if token_mint else "pending", |
| "token_mint": token_mint or None, |
| "default_token": token_mint == DEFAULT_TOKEN_MINT, |
| "total_holders": total_holders, |
| "eligible_holders": eligible_holders, |
| "new_holders_today": new_holders_today, |
| "total_balance": total_balance, |
| "eligibility_rate": eligible_holders / total_holders if total_holders > 0 else 0, |
| }) |
| except Exception as e: |
| return jsonify({"status": "error", "message": str(e)}), 500 |
|
|
| @app.route('/api/holders/eligible', methods=['GET']) |
| def eligible_holders(): |
| """Get eligible holders for drippage""" |
| try: |
| conn = sqlite3.connect(HOLDER_DB) |
| cursor = conn.cursor() |
| |
| cursor.execute(""" |
| SELECT address, current_balance, first_seen, eligibility_timestamp |
| FROM holders |
| WHERE eligible = TRUE |
| ORDER BY current_balance DESC |
| LIMIT 100 |
| """) |
| |
| holders = cursor.fetchall() |
| conn.close() |
| |
| return jsonify([ |
| { |
| "address": h[0], |
| "balance": h[1], |
| "first_seen": h[2], |
| "holding_hours": (datetime.utcnow() - datetime.fromisoformat(h[2])).total_seconds() / 3600 if h[2] else 0, |
| } |
| for h in holders |
| ]) |
| except Exception as e: |
| return jsonify({"error": str(e)}), 500 |
|
|
| @app.route('/api/liquidity/stats', methods=['GET']) |
| def liquidity_stats(): |
| """Get LLM liquidity statistics with real inference benchmark""" |
| try: |
| inference_url = _inference_url() |
| |
| |
| if inference_url: |
| try: |
| registry = InferenceRegistry(db_path=INFERENCE_DB) |
| converter = LiquidityConverter(registry) |
| monitor = PerformanceMonitor(registry) |
| bench = monitor._benchmark_inference_endpoint(inference_url, "llama2") |
| if bench["status"] == "verified": |
| registry.register_provider("prov_api_001", "api_worker", "llama2") |
| registry.verify_capacity( |
| "prov_api_001", |
| bench["tokens_per_second"], |
| bench["latency_ms"], |
| 99.0, |
| bench["quality_score"], |
| ) |
| metrics = registry.get_provider_capacity("prov_api_001") |
| if metrics: |
| liquidity = converter.calculate_liquidity(metrics) |
| converter.allocate_liquidity("prov_api_001", liquidity) |
| except Exception as bench_err: |
| print(f"Liquidity benchmark warning: {bench_err}") |
| |
| conn = sqlite3.connect(INFERENCE_DB) |
| cursor = conn.cursor() |
| |
| cursor.execute("SELECT COUNT(*) FROM providers WHERE status = 'active'") |
| total_providers = cursor.fetchone()[0] |
| |
| cursor.execute("SELECT SUM(total_earnings) FROM providers") |
| total_earnings = cursor.fetchone()[0] or 0 |
| |
| cursor.execute(""" |
| SELECT provider_id, synthetic_liquidity_usd, allocated_at |
| FROM liquidity_allocations |
| ORDER BY allocated_at DESC |
| LIMIT 10 |
| """) |
| |
| allocations = cursor.fetchall() |
| conn.close() |
| |
| total_liquidity = sum(a[1] for a in allocations) if allocations else 0 |
| |
| return jsonify({ |
| "status": "active" if total_providers > 0 else "local_only", |
| "message": None if total_providers > 0 else "No API key required. Connect a no-key local Ollama/OpenAI-compatible endpoint to benchmark live LLM liquidity.", |
| "inference_endpoint": inference_url or None, |
| "total_providers": total_providers, |
| "total_liquidity_usd": round(total_liquidity, 2), |
| "total_earnings": round(total_earnings, 2), |
| "avg_capacity": round(total_liquidity / total_providers, 2) if total_providers > 0 else 0, |
| "recent_allocations": [ |
| {"provider_id": a[0], "liquidity_usd": a[1], "allocated_at": a[2]} |
| for a in allocations |
| ], |
| }) |
| except Exception as e: |
| return jsonify({"status": "error", "message": str(e)}), 500 |
|
|
| @app.route('/api/liquidity/providers', methods=['GET']) |
| def liquidity_providers(): |
| """Get all LLM liquidity providers""" |
| try: |
| conn = sqlite3.connect(INFERENCE_DB) |
| cursor = conn.cursor() |
| |
| cursor.execute(""" |
| SELECT provider_id, wallet_address, model_type, status, reputation_score, total_earnings |
| FROM providers |
| WHERE status = 'active' |
| ORDER BY total_earnings DESC |
| """) |
| |
| providers = cursor.fetchall() |
| conn.close() |
| |
| return jsonify([ |
| { |
| "provider_id": p[0], |
| "wallet_address": p[1], |
| "model_type": p[2], |
| "status": p[3], |
| "reputation_score": p[4], |
| "total_earnings": p[5], |
| } |
| for p in providers |
| ]) |
| except Exception as e: |
| return jsonify({"error": str(e)}), 500 |
|
|
| @app.route('/api/trading/stats', methods=['GET']) |
| def trading_stats(): |
| """Get trading statistics""" |
| try: |
| conn = sqlite3.connect(TRADING_DB) |
| cursor = conn.cursor() |
| |
| cursor.execute("SELECT COUNT(*) FROM positions WHERE size > 0") |
| active_positions = cursor.fetchone()[0] |
| |
| cursor.execute("SELECT COUNT(*) FROM trades") |
| total_trades = cursor.fetchone()[0] |
| |
| cursor.execute(""" |
| SELECT SUM(size * price) |
| FROM trades |
| WHERE timestamp > datetime('now', '-1 day') |
| """) |
| volume_24h = cursor.fetchone()[0] or 0 |
| |
| cursor.execute("SELECT SUM(size) FROM positions WHERE size > 0") |
| total_size = cursor.fetchone()[0] or 0 |
| |
| conn.close() |
| |
| |
| tickers = _gateio_tickers() |
| btc_price = float(tickers.get('BTC_USDT', {}).get('last', 50000)) |
| return jsonify({ |
| "total_volume": volume_24h, |
| "open_interest": total_size * btc_price, |
| "active_positions": active_positions, |
| "total_trades": total_trades, |
| }) |
| except Exception as e: |
| return jsonify({"error": str(e)}), 500 |
|
|
| @app.route('/api/trading/markets', methods=['GET']) |
| def trading_markets(): |
| """Get market overview from Gate.io real data""" |
| tickers = _gateio_tickers() |
| funding = _gateio_funding() |
| markets = [] |
| for contract, t in tickers.items(): |
| markets.append({ |
| "market": contract.replace('_', '/'), |
| "mark_price": float(t.get('last', 0)), |
| "index_price": float(t.get('index_price', t.get('last', 0))), |
| "funding_rate": float(funding.get(contract, {}).get('funding_rate', 0)), |
| "volume_24h": float(t.get('volume_24h', 0)), |
| "open_interest": float(t.get('total_size', 0)), |
| "change_24h": float(t.get('change_percentage', 0)), |
| }) |
| if not markets: |
| return jsonify({"error": "Gate.io API unreachable"}), 503 |
| return jsonify(markets[:20]) |
|
|
| @app.route('/api/funding/stats', methods=['GET']) |
| def funding_stats(): |
| """Get funding rate statistics from Gate.io""" |
| try: |
| funding = _gateio_funding() |
| rates = list(funding.values()) |
| if rates: |
| current_rate = sum(float(r.get('funding_rate', 0)) for r in rates) / len(rates) |
| avg_rate = current_rate |
| else: |
| current_rate = 0 |
| avg_rate = 0 |
|
|
| return jsonify({ |
| "current_rate": current_rate, |
| "current_rate_percent": current_rate * 100, |
| "avg_rate_24h": avg_rate, |
| "oi_imbalance": 0, |
| "recent_rates": [ |
| {"market": r.get('contract', ''), "rate": float(r.get('funding_rate', 0)), "timestamp": r.get('funding_time', '')} |
| for r in rates[:24] |
| ], |
| }) |
| except Exception as e: |
| return jsonify({"error": str(e)}), 500 |
|
|
| def _safe_json(response): |
| """Extract JSON from a Flask Response or (Response, status) tuple.""" |
| if isinstance(response, tuple): |
| response = response[0] |
| if hasattr(response, 'get_json'): |
| return response.get_json() or {} |
| return {} |
|
|
| @app.route('/api/liquidation/stats', methods=['GET']) |
| def liquidation_stats(): |
| """Get liquidation statistics from real DB""" |
| try: |
| conn = sqlite3.connect(TRADING_DB) |
| cursor = conn.cursor() |
| cursor.execute("SELECT COUNT(*) FROM positions WHERE size = 0") |
| total_liquidations = cursor.fetchone()[0] |
| cursor.execute("SELECT SUM(margin) FROM positions WHERE size > 0") |
| insurance_fund = cursor.fetchone()[0] or 0 |
|
|
| |
| tickers = _gateio_tickers() |
| cursor.execute(""" |
| SELECT position_id, trader, market, side, size, entry_price, margin, liquidation_price |
| FROM positions |
| WHERE size > 0 |
| """) |
| positions = cursor.fetchall() |
| at_risk_count = 0 |
| for pos in positions: |
| market = pos[2] |
| contract = market.replace('/', '_').upper() |
| mark_price = float(tickers.get(contract, {}).get('last', 50000)) |
| notional = pos[4] * mark_price |
| margin_ratio = pos[6] / notional if notional > 0 else 1 |
| if margin_ratio < 0.10: |
| at_risk_count += 1 |
| conn.close() |
| return jsonify({ |
| "total_liquidations": total_liquidations, |
| "insurance_fund": insurance_fund, |
| "at_risk": at_risk_count, |
| "recent_liquidations": [], |
| }) |
| except Exception as e: |
| return jsonify({"error": str(e)}), 500 |
|
|
| @app.route('/api/liquidation/at-risk', methods=['GET']) |
| def at_risk_positions(): |
| """Get at-risk positions""" |
| try: |
| conn = sqlite3.connect(TRADING_DB) |
| cursor = conn.cursor() |
| |
| cursor.execute(""" |
| SELECT position_id, trader, market, side, size, entry_price, margin, liquidation_price |
| FROM positions |
| WHERE size > 0 |
| """) |
| |
| positions = cursor.fetchall() |
| conn.close() |
| |
| tickers = _gateio_tickers() |
| at_risk = [] |
| for pos in positions: |
| market = pos[2] |
| contract = market.replace('/', '_').upper() |
| mark_price = float(tickers.get(contract, {}).get('last', 50000)) |
| notional = pos[4] * mark_price |
| margin_ratio = pos[6] / notional if notional > 0 else 1 |
|
|
| if margin_ratio < 0.10: |
| at_risk.append({ |
| "position_id": pos[0], |
| "trader": pos[1], |
| "market": pos[2], |
| "side": pos[3], |
| "margin_ratio": margin_ratio, |
| "liquidation_price": pos[7], |
| }) |
| |
| return jsonify(at_risk[:10]) |
| except Exception as e: |
| return jsonify({"error": str(e)}), 500 |
|
|
| @app.route('/api/mining/stats', methods=['GET']) |
| def mining_stats(): |
| """Get mining rewards statistics""" |
| try: |
| conn = sqlite3.connect(INFERENCE_DB) |
| cursor = conn.cursor() |
| |
| cursor.execute("SELECT COUNT(*) FROM providers WHERE status = 'active'") |
| active_providers = cursor.fetchone()[0] |
| |
| cursor.execute("SELECT SUM(amount) FROM rewards") |
| total_rewards = cursor.fetchone()[0] or 0 |
| |
| cursor.execute("SELECT COUNT(*) FROM rewards") |
| total_reward_count = cursor.fetchone()[0] |
| |
| conn.close() |
| |
| return jsonify({ |
| "active_providers": active_providers, |
| "total_rewards": total_rewards, |
| "total_reward_count": total_reward_count, |
| "avg_reward_per_provider": total_rewards / active_providers if active_providers > 0 else 0, |
| }) |
| except Exception as e: |
| return jsonify({"error": str(e)}), 500 |
|
|
| @app.route('/api/mining/leaderboard', methods=['GET']) |
| def mining_leaderboard(): |
| """Get mining rewards leaderboard""" |
| try: |
| conn = sqlite3.connect(INFERENCE_DB) |
| cursor = conn.cursor() |
| |
| cursor.execute(""" |
| SELECT provider_id, wallet_address, model_type, total_earnings, reputation_score |
| FROM providers |
| WHERE status = 'active' |
| ORDER BY total_earnings DESC |
| LIMIT 10 |
| """) |
| |
| providers = cursor.fetchall() |
| conn.close() |
| |
| return jsonify([ |
| { |
| "rank": i + 1, |
| "provider_id": p[0], |
| "wallet_address": p[1], |
| "model_type": p[2], |
| "total_earnings": p[3], |
| "reputation_score": p[4], |
| } |
| for i, p in enumerate(providers) |
| ]) |
| except Exception as e: |
| return jsonify({"error": str(e)}), 500 |
|
|
| @app.route('/api/overview', methods=['GET']) |
| def overview(): |
| """Get overview statistics from all systems""" |
| try: |
| config = _integration_config() |
| slippage = _safe_json(slippage_stats()) |
| holders = _safe_json(holder_stats()) |
| liquidity = _safe_json(liquidity_stats()) |
| trading = _safe_json(trading_stats()) |
| funding = _safe_json(funding_stats()) |
| liquidation = _safe_json(liquidation_stats()) |
| mining = _safe_json(mining_stats()) |
| token_launch = _safe_json(token_launch_status()) |
| collateral = _collateral_summary() |
| |
| |
| systems = { |
| "slippage_collector": slippage.get("status", "pending") if config["token_mint"]["configured"] else "not_wired", |
| "holder_tracker": holders.get("status", "pending") if config["token_mint"]["configured"] else "not_wired", |
| "llm_liquidity": liquidity.get("status", "pending") if config["inference_endpoint"]["configured"] else "local_only", |
| "merkle_token_launch": token_launch.get("status", "pending"), |
| "hf_account_collateral": collateral.get("status", "not_scanned"), |
| "trading_engine": "active" if trading.get("total_volume") is not None else "pending", |
| "funding_engine": "active" if funding.get("current_rate") is not None else "pending", |
| "liquidation_system": "active" if liquidation.get("total_liquidations") is not None else "pending", |
| "mining_rewards": "active" if mining.get("total_rewards") is not None else "pending", |
| } |
| status_meta = { |
| "slippage_collector": _status_meta(systems["slippage_collector"], "Slippage collector", "Uses a public default Solana token mint unless another token is configured."), |
| "holder_tracker": _status_meta(systems["holder_tracker"], "Holder tracker", "Uses public Solana RPC with a default token mint. No API key required."), |
| "llm_liquidity": _status_meta(systems["llm_liquidity"], "LLM liquidity", "Local-only until an optional no-key local inference endpoint is connected."), |
| "merkle_token_launch": _status_meta(systems["merkle_token_launch"], "Merkle token launch", "One Merkle root commits token spec, pool spec, allocation vector, gates, and live source metrics."), |
| "hf_account_collateral": _status_meta(systems["hf_account_collateral"], "HF account collateral", "Public HF repos, spaces, files, and readable LOC are scanned into collateral evidence."), |
| "trading_engine": _status_meta(systems["trading_engine"], "Trading engine", "Local perpetual futures DB plus public Gate.io market data."), |
| "funding_engine": _status_meta(systems["funding_engine"], "Funding engine", "Public Gate.io funding-rate feed."), |
| "liquidation_system": _status_meta(systems["liquidation_system"], "Liquidation system", "Local position-risk engine."), |
| "mining_rewards": _status_meta(systems["mining_rewards"], "Mining rewards", "Local provider rewards ledger."), |
| } |
| |
| return jsonify({ |
| "mode": "no_mock_real_backend", |
| "timestamp": datetime.utcnow().isoformat(), |
| "config": config, |
| "slippage": slippage, |
| "holders": holders, |
| "liquidity": liquidity, |
| "trading": trading, |
| "funding": funding, |
| "liquidation": liquidation, |
| "mining": mining, |
| "token_launch": token_launch, |
| "collateral": collateral, |
| "systems": systems, |
| "status_meta": status_meta, |
| }) |
| except Exception as e: |
| return jsonify({"error": str(e)}), 500 |
|
|
| |
| @app.route('/') |
| def index(): |
| """Serve the dashboard UI""" |
| return render_template_string(""" |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>AirMicroDrip Control Plane</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script> |
| tailwind.config = { |
| theme: { |
| extend: { |
| colors: { |
| ink: '#08111f', |
| panel: '#101b2e', |
| panel2: '#14233a', |
| line: '#26364f', |
| cyan: '#67e8f9', |
| mint: '#7dd3a8', |
| amber: '#f5c76b' |
| } |
| } |
| } |
| } |
| </script> |
| <style> |
| body { |
| background: |
| radial-gradient(circle at top left, rgba(103,232,249,0.16), transparent 34rem), |
| radial-gradient(circle at 80% 15%, rgba(125,211,168,0.10), transparent 30rem), |
| linear-gradient(135deg, #07111f 0%, #0b1322 55%, #060a12 100%); |
| } |
| .glass { background: rgba(16, 27, 46, .82); backdrop-filter: blur(18px); } |
| .grid-bg { |
| background-image: |
| linear-gradient(rgba(255,255,255,.035) 1px, transparent 1px), |
| linear-gradient(90deg, rgba(255,255,255,.035) 1px, transparent 1px); |
| background-size: 28px 28px; |
| } |
| .mono { font-variant-numeric: tabular-nums; } |
| </style> |
| </head> |
| <body class="grid-bg min-h-screen text-slate-100"> |
| <div class="mx-auto flex min-h-screen w-full max-w-7xl flex-col px-4 py-5 sm:px-6 lg:px-8"> |
| <header class="mb-5 flex flex-col gap-4 rounded-3xl border border-white/10 bg-white/[0.035] p-5 shadow-2xl shadow-black/30 md:flex-row md:items-center md:justify-between"> |
| <div> |
| <div class="mb-3 flex flex-wrap items-center gap-2"> |
| <span class="rounded-full border border-cyan/30 bg-cyan/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.24em] text-cyan">No-key backend</span> |
| <span id="last-updated" class="rounded-full border border-white/10 px-3 py-1 text-xs text-slate-400">syncing</span> |
| </div> |
| <h1 class="text-3xl font-black tracking-tight text-white sm:text-5xl">AirMicroDrip Control Plane</h1> |
| <p class="mt-3 max-w-3xl text-sm leading-6 text-slate-300 sm:text-base"> |
| Real Flask backend, public market data, local SQLite ledgers, and optional no-key inference wiring. No fabricated holders, liquidity, payouts, or model benchmarks. |
| </p> |
| </div> |
| <div class="grid min-w-[250px] grid-cols-2 gap-2 text-xs"> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-3"> |
| <div class="text-slate-500">Mode</div> |
| <div id="runtime-mode" class="mt-1 font-semibold text-mint">no_mock_real_backend</div> |
| </div> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-3"> |
| <div class="text-slate-500">API keys</div> |
| <div class="mt-1 font-semibold text-cyan">not required</div> |
| </div> |
| </div> |
| </header> |
| |
| <main class="grid flex-1 gap-5 lg:grid-cols-[1.35fr_.65fr]"> |
| <section class="space-y-5"> |
| <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4"> |
| <article class="glass rounded-3xl border border-white/10 p-5 shadow-xl shadow-black/20"> |
| <p class="text-xs uppercase tracking-[0.2em] text-slate-500">Total holders</p> |
| <p id="total-holders" class="mono mt-3 text-4xl font-black">--</p> |
| <p id="holder-subtitle" class="mt-2 text-xs text-slate-400">public Solana RPC</p> |
| </article> |
| <article class="glass rounded-3xl border border-white/10 p-5 shadow-xl shadow-black/20"> |
| <p class="text-xs uppercase tracking-[0.2em] text-slate-500">LLM providers</p> |
| <p id="total-providers" class="mono mt-3 text-4xl font-black">--</p> |
| <p class="mt-2 text-xs text-slate-400">optional local inference endpoint</p> |
| </article> |
| <article class="glass rounded-3xl border border-white/10 p-5 shadow-xl shadow-black/20"> |
| <p class="text-xs uppercase tracking-[0.2em] text-slate-500">Synthetic liquidity</p> |
| <p id="total-liquidity" class="mono mt-3 text-4xl font-black">--</p> |
| <p class="mt-2 text-xs text-slate-400">verified benchmark only</p> |
| </article> |
| <article class="glass rounded-3xl border border-white/10 p-5 shadow-xl shadow-black/20"> |
| <p class="text-xs uppercase tracking-[0.2em] text-slate-500">Active positions</p> |
| <p id="active-positions" class="mono mt-3 text-4xl font-black">--</p> |
| <p class="mt-2 text-xs text-slate-400">local perp engine</p> |
| </article> |
| </div> |
| |
| <div class="grid gap-5 xl:grid-cols-[.9fr_1.1fr]"> |
| <section class="glass rounded-3xl border border-white/10 p-5"> |
| <div class="mb-4 flex items-center justify-between"> |
| <h2 class="text-lg font-bold">System fabric</h2> |
| <span class="rounded-full bg-mint/10 px-3 py-1 text-xs font-semibold text-mint">backend online</span> |
| </div> |
| <div id="system-status" class="space-y-3"> |
| <div class="h-14 animate-pulse rounded-2xl bg-white/5"></div> |
| <div class="h-14 animate-pulse rounded-2xl bg-white/5"></div> |
| <div class="h-14 animate-pulse rounded-2xl bg-white/5"></div> |
| </div> |
| </section> |
| |
| <section class="glass rounded-3xl border border-white/10 p-5"> |
| <div class="mb-4 flex items-center justify-between"> |
| <h2 class="text-lg font-bold">No-key integration map</h2> |
| <span class="rounded-full border border-cyan/25 bg-cyan/10 px-3 py-1 text-xs text-cyan">public + local</span> |
| </div> |
| <div id="integration-map" class="grid gap-3 sm:grid-cols-2"></div> |
| </section> |
| </div> |
| |
| <section class="glass rounded-3xl border border-white/10 p-5"> |
| <div class="mb-4 flex items-center justify-between"> |
| <h2 class="text-lg font-bold">Market and protocol telemetry</h2> |
| <span class="text-xs text-slate-500">values are persisted or fetched live</span> |
| </div> |
| <div class="grid gap-3 md:grid-cols-3"> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-4"> |
| <p class="text-xs uppercase tracking-[0.18em] text-slate-500">24h volume</p> |
| <p id="volume-24h" class="mono mt-2 text-2xl font-bold">$0</p> |
| </div> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-4"> |
| <p class="text-xs uppercase tracking-[0.18em] text-slate-500">Funding avg</p> |
| <p id="funding-rate" class="mono mt-2 text-2xl font-bold">0%</p> |
| </div> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-4"> |
| <p class="text-xs uppercase tracking-[0.18em] text-slate-500">Insurance fund</p> |
| <p id="insurance-fund" class="mono mt-2 text-2xl font-bold">$0</p> |
| </div> |
| </div> |
| </section> |
| |
| <section class="glass rounded-3xl border border-cyan/20 p-5"> |
| <div class="mb-4 flex items-center justify-between gap-3"> |
| <div> |
| <h2 class="text-lg font-bold">One Merkle Tree Token Launch</h2> |
| <p class="mt-1 text-xs text-slate-400">A single root commits token spec, pool spec, allocation vector, gates, and live source metrics.</p> |
| </div> |
| <span id="launch-status" class="rounded-full border border-cyan/30 bg-cyan/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.18em] text-cyan">loading</span> |
| </div> |
| <div class="grid gap-3 md:grid-cols-3"> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-4"> |
| <p class="text-xs uppercase tracking-[0.18em] text-slate-500">Token</p> |
| <p id="launch-token" class="mt-2 text-2xl font-black">--</p> |
| <p id="launch-supply" class="mt-1 text-xs text-slate-400">supply pending</p> |
| </div> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-4"> |
| <p class="text-xs uppercase tracking-[0.18em] text-slate-500">Pool</p> |
| <p id="launch-pool" class="mt-2 text-2xl font-black">--</p> |
| <p id="launch-pool-status" class="mt-1 text-xs text-slate-400">quote asset requires signer funding</p> |
| </div> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-4"> |
| <p class="text-xs uppercase tracking-[0.18em] text-slate-500">Setup plan</p> |
| <p id="launch-proofs" class="mt-2 text-2xl font-black">--</p> |
| <p id="launch-leaves" class="mt-1 text-xs text-slate-400">leaves pending</p> |
| </div> |
| </div> |
| <div class="mt-3 grid gap-3 lg:grid-cols-2"> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-4"> |
| <p class="text-xs uppercase tracking-[0.18em] text-slate-500">Merkle root</p> |
| <p id="launch-root" class="mono mt-2 break-all text-xs text-cyan">--</p> |
| </div> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-4"> |
| <p class="text-xs uppercase tracking-[0.18em] text-slate-500">Manifest hash</p> |
| <p id="launch-hash" class="mono mt-2 break-all text-xs text-mint">--</p> |
| </div> |
| </div> |
| <div class="mt-3 rounded-2xl border border-amber/20 bg-amber/10 p-4 text-xs leading-5 text-amber"> |
| This prepares the launch tree. It does not claim a live SPL mint or live liquidity pool until real wallet-signed transaction signatures are published. |
| </div> |
| </section> |
| |
| <section class="glass rounded-3xl border border-mint/20 p-5"> |
| <div class="mb-4 flex items-center justify-between gap-3"> |
| <div> |
| <h2 class="text-lg font-bold">HF Account Collateral</h2> |
| <p class="mt-1 text-xs text-slate-400">Owner repos, Spaces, files, and readable LOC become collateral leaves in the same launch tree.</p> |
| </div> |
| <div class="flex flex-col items-end gap-2"> |
| <span id="collateral-status" class="rounded-full border border-mint/30 bg-mint/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.18em] text-mint">not scanned</span> |
| <button id="collateral-scan-button" class="rounded-full border border-cyan/30 bg-cyan/10 px-3 py-1 text-xs font-semibold text-cyan hover:bg-cyan/20">scan account</button> |
| </div> |
| </div> |
| <div class="grid gap-3 md:grid-cols-4"> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-4"> |
| <p class="text-xs uppercase tracking-[0.18em] text-slate-500">Owner</p> |
| <p id="collateral-owner" class="mt-2 text-2xl font-black">--</p> |
| </div> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-4"> |
| <p class="text-xs uppercase tracking-[0.18em] text-slate-500">Repos</p> |
| <p id="collateral-repos" class="mono mt-2 text-2xl font-black">0</p> |
| </div> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-4"> |
| <p class="text-xs uppercase tracking-[0.18em] text-slate-500">Files read</p> |
| <p id="collateral-files" class="mono mt-2 text-2xl font-black">0</p> |
| </div> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-4"> |
| <p class="text-xs uppercase tracking-[0.18em] text-slate-500">LOC</p> |
| <p id="collateral-loc" class="mono mt-2 text-2xl font-black">0</p> |
| </div> |
| </div> |
| <div class="mt-3 rounded-2xl border border-white/10 bg-black/20 p-4"> |
| <p class="text-xs uppercase tracking-[0.18em] text-slate-500">Collateral root</p> |
| <p id="collateral-root" class="mono mt-2 break-all text-xs text-mint">scan required</p> |
| </div> |
| </section> |
| </div> |
| |
| <aside class="space-y-5"> |
| <section class="glass rounded-3xl border border-white/10 p-5"> |
| <h2 class="text-lg font-bold">Backend contract</h2> |
| <div class="mt-4 space-y-3 text-sm text-slate-300"> |
| <div class="rounded-2xl border border-mint/20 bg-mint/10 p-4"> |
| <div class="font-semibold text-mint">No API keys required</div> |
| <p class="mt-1 text-xs text-slate-300">Public feeds and local ledgers are used by default. Optional endpoints are clearly marked local-only until connected.</p> |
| </div> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-4"> |
| <div class="font-semibold text-white">No mock success states</div> |
| <p class="mt-1 text-xs text-slate-400">If a source is unavailable, the app reports waiting, local-only, or error states instead of inventing values.</p> |
| </div> |
| </div> |
| </section> |
| |
| <section class="glass rounded-3xl border border-white/10 p-5"> |
| <h2 class="text-lg font-bold">Data provenance</h2> |
| <div class="mt-4 space-y-3 text-sm" id="provenance-list"> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-3 text-slate-400">Loading provenance...</div> |
| </div> |
| </section> |
| |
| <section class="glass rounded-3xl border border-white/10 p-5"> |
| <h2 class="text-lg font-bold">Operator notes</h2> |
| <ul class="mt-4 space-y-2 text-sm text-slate-300"> |
| <li>• Holder and slippage routes use a public default token mint.</li> |
| <li>• LLM liquidity activates only after a real endpoint responds.</li> |
| <li>• Existing local ledgers remain empty until real events occur.</li> |
| </ul> |
| </section> |
| </aside> |
| </main> |
| </div> |
| |
| <script> |
| function formatNumber(value, prefix = '') { |
| if (value === undefined || value === null || Number.isNaN(Number(value))) return prefix + '0'; |
| return prefix + Number(value).toLocaleString(undefined, { maximumFractionDigits: 2 }); |
| } |
| |
| function formatValue(value, prefix = '') { |
| if (value === undefined || value === null) return prefix + '0'; |
| if (typeof value === 'number') return formatNumber(value, prefix); |
| return String(value); |
| } |
| |
| function statusClasses(status) { |
| if (status === 'active') return 'border-mint/30 bg-mint/10 text-mint'; |
| if (status === 'local_only') return 'border-cyan/30 bg-cyan/10 text-cyan'; |
| if (status === 'unsigned_ready') return 'border-cyan/30 bg-cyan/10 text-cyan'; |
| if (status === 'ready_for_signature') return 'border-mint/30 bg-mint/10 text-mint'; |
| if (status === 'pending' || status === 'not_wired') return 'border-amber/30 bg-amber/10 text-amber'; |
| if (status === 'not_scanned') return 'border-amber/30 bg-amber/10 text-amber'; |
| return 'border-red-400/30 bg-red-400/10 text-red-300'; |
| } |
| |
| function statusDot(status) { |
| if (status === 'active') return 'bg-mint'; |
| if (status === 'local_only') return 'bg-cyan'; |
| if (status === 'unsigned_ready') return 'bg-cyan'; |
| if (status === 'ready_for_signature') return 'bg-mint'; |
| if (status === 'pending' || status === 'not_wired') return 'bg-amber'; |
| if (status === 'not_scanned') return 'bg-amber'; |
| return 'bg-red-300'; |
| } |
| |
| async function startCollateralScan() { |
| const button = document.getElementById('collateral-scan-button'); |
| button.disabled = true; |
| button.textContent = 'scanning...'; |
| try { |
| await fetch('/api/collateral/scan', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({}) |
| }); |
| await loadData(); |
| } catch (error) { |
| console.error('Failed to start collateral scan:', error); |
| } finally { |
| setTimeout(() => { |
| button.disabled = false; |
| button.textContent = 'scan account'; |
| }, 3000); |
| } |
| } |
| |
| async function loadData() { |
| try { |
| const response = await fetch('/api/overview'); |
| if (!response.ok) throw new Error('overview failed: ' + response.status); |
| const data = await response.json(); |
| |
| const holders = data.holders || {}; |
| const liquidity = data.liquidity || {}; |
| const trading = data.trading || {}; |
| const funding = data.funding || {}; |
| const liquidation = data.liquidation || {}; |
| const tokenLaunch = data.token_launch || {}; |
| const collateral = data.collateral || {}; |
| const statusMeta = data.status_meta || {}; |
| const config = data.config || {}; |
| const integrations = config || {}; |
| |
| document.getElementById('total-holders').textContent = formatValue(holders.total_holders); |
| document.getElementById('total-providers').textContent = formatValue(liquidity.total_providers); |
| document.getElementById('total-liquidity').textContent = formatNumber(liquidity.total_liquidity_usd, '$'); |
| document.getElementById('active-positions').textContent = formatValue(trading.active_positions); |
| document.getElementById('volume-24h').textContent = formatNumber(trading.total_volume, '$'); |
| document.getElementById('funding-rate').textContent = ((funding.current_rate_percent || 0).toFixed(4)) + '%'; |
| document.getElementById('insurance-fund').textContent = formatNumber(liquidation.insurance_fund, '$'); |
| document.getElementById('runtime-mode').textContent = data.mode || 'no_mock_real_backend'; |
| document.getElementById('last-updated').textContent = data.timestamp ? new Date(data.timestamp).toLocaleTimeString() : 'live'; |
| document.getElementById('holder-subtitle').textContent = holders.default_token ? 'default public token mint' : 'configured token mint'; |
| document.getElementById('launch-status').textContent = (tokenLaunch.status || 'waiting').replace('_', ' '); |
| document.getElementById('launch-token').textContent = `${tokenLaunch.token_spec?.symbol || '--'}`; |
| document.getElementById('launch-supply').textContent = formatNumber(tokenLaunch.token_spec?.total_supply) + ' committed supply'; |
| document.getElementById('launch-pool').textContent = tokenLaunch.pool_spec?.pair || '--'; |
| document.getElementById('launch-pool-status').textContent = tokenLaunch.pool_setup_status?.status?.replaceAll('_', ' ') || 'ready for signature'; |
| document.getElementById('launch-proofs').textContent = `${tokenLaunch.unsigned_solana_plan?.length || 0} steps`; |
| document.getElementById('launch-leaves').textContent = `${tokenLaunch.leaf_count || 0} leaves / proofs ${Object.values(tokenLaunch.proof_checks || {}).every(Boolean) ? 'verified' : 'waiting'}`; |
| document.getElementById('launch-root').textContent = tokenLaunch.merkle_root || '--'; |
| document.getElementById('launch-hash').textContent = tokenLaunch.manifest_hash || '--'; |
| document.getElementById('collateral-status').textContent = (collateral.status || 'not_scanned').replaceAll('_', ' '); |
| document.getElementById('collateral-owner').textContent = collateral.owner || '--'; |
| document.getElementById('collateral-repos').textContent = formatValue(collateral.repo_count); |
| document.getElementById('collateral-files').textContent = formatValue(collateral.total_text_files || collateral.total_files); |
| document.getElementById('collateral-loc').textContent = formatValue(collateral.total_loc); |
| document.getElementById('collateral-root').textContent = collateral.collateral_root || 'scan required'; |
| const scanButton = document.getElementById('collateral-scan-button'); |
| if (collateral.status === 'running') { |
| scanButton.disabled = true; |
| scanButton.textContent = 'scanning...'; |
| } else { |
| scanButton.disabled = false; |
| scanButton.textContent = collateral.collateral_root ? 'rescan account' : 'scan account'; |
| } |
| |
| const statusHtml = Object.entries(statusMeta).map(([key, meta]) => { |
| const status = meta.status || 'pending'; |
| return ` |
| <div class="rounded-2xl border ${statusClasses(status)} p-4"> |
| <div class="flex items-start justify-between gap-3"> |
| <div> |
| <div class="flex items-center gap-2 font-semibold"> |
| <span class="h-2 w-2 rounded-full ${statusDot(status)}"></span> |
| ${meta.label || key} |
| </div> |
| <p class="mt-1 text-xs leading-5 text-slate-300">${meta.detail || ''}</p> |
| </div> |
| <span class="rounded-full bg-black/30 px-2.5 py-1 text-[10px] uppercase tracking-[0.18em]">${meta.display || status}</span> |
| </div> |
| </div>`; |
| }).join(''); |
| document.getElementById('system-status').innerHTML = statusHtml || '<p class="text-slate-400">No system status returned.</p>'; |
| |
| const integrationHtml = Object.entries(integrations).map(([key, item]) => { |
| const status = item.status || 'pending'; |
| return ` |
| <div class="rounded-2xl border ${statusClasses(status)} p-4"> |
| <div class="text-xs uppercase tracking-[0.18em] opacity-80">${item.label || key}</div> |
| <div class="mt-2 text-lg font-bold">${(item.status || '').replace('_', ' ')}</div> |
| <div class="mt-2 text-xs text-slate-300">${item.env || 'no key needed'}</div> |
| ${item.value_public ? `<div class="mono mt-2 truncate text-[11px] text-slate-400">${item.value_public}</div>` : ''} |
| </div>`; |
| }).join(''); |
| document.getElementById('integration-map').innerHTML = integrationHtml; |
| |
| document.getElementById('provenance-list').innerHTML = ` |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-3"> |
| <div class="font-semibold text-white">Market data</div> |
| <div class="mt-1 text-xs text-slate-400">Gate.io public futures endpoints; no API key.</div> |
| </div> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-3"> |
| <div class="font-semibold text-white">Token graph</div> |
| <div class="mt-1 text-xs text-slate-400">Solana RPC and DexScreener public routes using the displayed token mint.</div> |
| </div> |
| <div class="rounded-2xl border border-white/10 bg-black/20 p-3"> |
| <div class="font-semibold text-white">Local ledgers</div> |
| <div class="mt-1 text-xs text-slate-400">SQLite stores inside the running Space container.</div> |
| </div>`; |
| } catch (error) { |
| console.error('Failed to load data:', error); |
| document.getElementById('system-status').innerHTML = '<div class="rounded-2xl border border-red-400/30 bg-red-400/10 p-4 text-red-200">Backend API did not respond. This is a real error, not a simulated state.</div>'; |
| } |
| } |
| |
| loadData(); |
| setInterval(loadData, 5000); |
| document.getElementById('collateral-scan-button').addEventListener('click', startCollateralScan); |
| </script> |
| </body> |
| </html> |
| """) |
|
|
| if __name__ == '__main__': |
| app.run(host='0.0.0.0', port=7860) |
|
|