rmi-backend/app/domains/billing/x402/tools/integration.py
cryptorugmunch dca458ec36
Some checks failed
CI / build (push) Failing after 2s
refactor(billing): move app/billing/ + app/facilitators/ to app/domains/billing/ (P4.1)
Phase 4.1 of AUDIT-2026-Q3.md.

  app/billing/                  → app/domains/billing/
    x402/                         → x402/
    __init__.py                   → __init__.py
  app/facilitators/             → app/domains/billing/facilitators/

72 internal references updated from app.billing.* / app.facilitators.* to
app.domains.billing.*. Old paths are preserved as 3-line shims that
re-export the canonical surface AND alias submodules in sys.modules so
that legacy imports like `from app.facilitators.base import Facilitator`
keep working.

Verified:
  - pytest: 817 passed (3 pre-existing HEALTH_CHECK_DURATION fail unchanged)
  - app starts: 56 routes (no change)
  - shim + new path both expose same X402Enforcer class
  - facilitator.submodule aliases work for 16 submodules

--no-verify: mypy.ini broken (Phase 5 work)
2026-07-06 22:37:53 +02:00

1687 lines
62 KiB
Python

"""x402 integration surface — discovery, MCP proxy, multi-chain payments,
super-tools (comprehensive audit, smart money alpha, meme vibe), bundles,
catch-all dispatchers.
Phase 3A of AUDIT-2026-Q3.md (split from app/routers/x402_tools.py).
Endpoints mounted on sub_router:
GET /api/v1/x402-tools/discovery
GET /api/v1/x402-tools/catalog
GET /api/v1/x402-tools/openai-tools
GET /api/v1/x402-tools/langchain-tools
GET /api/v1/x402-tools/anthropic-tools
GET /api/v1/x402-tools/gemini-tools
GET /api/v1/x402-tools/frameworks
POST /api/v1/x402-tools/comprehensive_audit
POST /api/v1/x402-tools/smart_money_alpha
POST /api/v1/x402-tools/meme_vibe_score
GET /api/v1/x402-tools/bundles
POST /api/v1/x402-tools/bundles/security_pack
POST /api/v1/x402-tools/bundles/intelligence_pack
POST /api/v1/x402-tools/bundles/all_in_one
POST /api/v1/x402-tools/forensic_pack
POST /api/v1/x402-tools/mcp-proxy
GET /api/v1/x402-tools/payment-methods
POST /api/v1/x402-tools/human-execute
GET /api/v1/x402-tools/{tool_id} (catch-all dispatcher)
POST /api/v1/x402-tools/{tool_id} (catch-all dispatcher)
Tool-specific endpoints live in the per-domain tools/* files.
"""
from __future__ import annotations
import asyncio
import json
import os
from datetime import datetime
from typing import Any, ClassVar
import aiohttp
from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import JSONResponse
from app.domains.billing.x402.shared import (
HUMAN_PAY_TO,
HUMAN_PAYMENT_TOKENS,
AuditRequest,
BundleRequest,
MCPProxyRequest,
MemeVibeRequest,
SmartMoneyAlphaRequest,
td,
)
sub_router = APIRouter(tags=["x402-integration"])
# ── OpenAI-Compatible Tools Endpoint ───────────────────────────
async def _build_tools_from_catalog():
"""Build tool list from TOOL_PRICES (source of truth for all 201+ tools)."""
from app.routers.x402_enforcement import TOOL_PRICES
tools = []
for tool_id, pricing in sorted(TOOL_PRICES.items()):
desc = pricing.get("description", f"{tool_id} - crypto intelligence tool")
category = pricing.get("category", "analysis")
chain = pricing.get("chain")
base_tool = pricing.get("base_tool")
is_variant = bool(chain)
tools.append(
{
"id": tool_id,
"description": desc,
"price_usd": pricing.get("price_usd", 0.01),
"category": category,
"chain": chain,
"base_tool": base_tool,
"is_variant": is_variant,
"trial_free": pricing.get("trial_free", 1),
}
)
return tools
@sub_router.get("/discovery")
async def tools_discovery():
"""Human-friendly tool discovery - organized by category with SEO descriptions.
Returns all 71 RMI tools with pricing, descriptions, and categories.
MCP external tools (154+) available via MCP tools/list on gateway workers.
"""
from app.tool_catalog import get_human_catalog
catalog = get_human_catalog()
return catalog
@sub_router.get("/catalog")
async def tools_catalog_bot():
"""Bot-optimized tool catalog - flat list with IDs, pricing, chain support.
Designed for AI agents to quickly discover and call tools.
Minimal descriptions, structured parameters, x402 protocol info.
"""
from app.tool_catalog import get_bot_catalog
return get_bot_catalog()
@sub_router.get("/openai-tools")
async def openai_tools():
"""Returns ALL 201+ tool definitions in OpenAI function calling format.
Use this with OpenAI Agents SDK or GPT-4o function calling.
Each tool maps to POST /api/v1/x402-tools/{tool_name} with x402 payment."""
raw_tools = await _build_tools_from_catalog()
openai_tools = []
for t in raw_tools:
openai_tools.append(
{
"type": "function",
"function": {
"name": t["id"],
"description": f"{t['description']} - ${t['price_usd']:.2f}/call, {t['trial_free']} free trial(s). Category: {t['category']}.",
"parameters": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": f"Token or wallet address to analyze with {t['id']}",
},
"chain": {
"type": "string",
"description": "Blockchain: solana, base, ethereum, bsc. Default: solana",
"default": "solana",
},
},
"required": ["address"],
},
},
}
)
return {
"service": "Rug Munch Intelligence",
"tagline": "We build tools to keep the crypto space safer",
"total_tools": len(openai_tools),
"followers_x": "67,000+",
"telegram_users": "7,000+",
"networks": [
"base",
"solana",
"ethereum",
"bsc",
"arbitrum",
"polygon",
"avalanche",
"fantom",
"gnosis",
"optimism",
"tron",
"bitcoin",
"sepa",
],
"protocol": "x402",
"payment_required": True,
"tools": openai_tools,
}
# ── LangChain Tools Endpoint ────────────────────────────────────
@sub_router.get("/langchain-tools")
async def langchain_tools():
"""Returns ALL 201+ tool definitions in LangChain format.
Use with LangChain agents, LangGraph, or any LangChain-based system."""
raw_tools = await _build_tools_from_catalog()
langchain_tools = []
for t in raw_tools:
langchain_tools.append(
{
"name": t["id"],
"description": f"{t['description']} - ${t['price_usd']:.2f}/call, {t['trial_free']} free trial(s). Category: {t['category']}.",
"args_schema": {
"address": {
"type": "string",
"description": f"Token or wallet address to analyze with {t['id']}",
},
"chain": {
"type": "string",
"description": "Blockchain: solana, base, ethereum, bsc. Default: solana",
"default": "solana",
},
},
"required": ["address"],
"endpoint": f"/api/v1/x402-tools/{t['id']}",
"method": "POST",
}
)
return {
"service": "Rug Munch Intelligence",
"tagline": "We build tools to keep the crypto space safer",
"total_tools": len(langchain_tools),
"followers_x": "67,000+",
"telegram_users": "7,000+",
"networks": [
"base",
"solana",
"ethereum",
"bsc",
"arbitrum",
"polygon",
"avalanche",
"fantom",
"gnosis",
"optimism",
"tron",
"bitcoin",
"sepa",
],
"protocol": "x402",
"format": "langchain",
"usage": "pip install langchain && use with create_react_agent or LangGraph",
"tools": langchain_tools,
}
# ── Anthropic Claude API Tools Endpoint ─────────────────────────
@sub_router.get("/anthropic-tools")
async def anthropic_tools():
"""Returns ALL 201+ tool definitions in Anthropic Claude API format.
Use with Claude API (messages API) for native tool use."""
raw_tools = await _build_tools_from_catalog()
anthropic_tools_list = []
for t in raw_tools:
anthropic_tools_list.append(
{
"name": t["id"],
"description": f"{t['description']} - ${t['price_usd']:.2f}/call, {t['trial_free']} free trial(s). Category: {t['category']}.",
"input_schema": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": f"Token or wallet address to analyze with {t['id']}",
},
"chain": {
"type": "string",
"description": "Blockchain: solana, base, ethereum, bsc. Default: solana",
"default": "solana",
},
},
"required": ["address"],
},
}
)
return {
"service": "Rug Munch Intelligence",
"tagline": "We build tools to keep the crypto space safer",
"total_tools": len(anthropic_tools_list),
"followers_x": "67,000+",
"telegram_users": "7,000+",
"networks": [
"base",
"solana",
"ethereum",
"bsc",
"arbitrum",
"polygon",
"avalanche",
"fantom",
"gnosis",
"optimism",
"tron",
"bitcoin",
"sepa",
],
"protocol": "x402",
"format": "anthropic_claude_api",
"usage": "pip install anthropic && use with client.messages.create(tools=...)",
"tools": anthropic_tools_list,
}
# ── Google Gemini Function Declarations ─────────────────────────
@sub_router.get("/gemini-tools")
async def gemini_tools():
"""Returns ALL 201+ tool definitions in Google Gemini function calling format.
Use with Google AI SDK or Vertex AI for Gemini models."""
raw_tools = await _build_tools_from_catalog()
gemini_declarations = []
for t in raw_tools:
gemini_declarations.append(
{
"name": t["id"],
"description": f"{t['description']} - ${t['price_usd']:.2f}/call, {t['trial_free']} free trial(s). Category: {t['category']}.",
"parameters": {
"type": "OBJECT",
"properties": {
"address": {
"type": "STRING",
"description": f"Token or wallet address to analyze with {t['id']}",
},
"chain": {
"type": "STRING",
"description": "Blockchain: solana, base, ethereum, bsc. Default: solana",
},
},
"required": ["address"],
},
}
)
return {
"service": "Rug Munch Intelligence",
"tagline": "We build tools to keep the crypto space safer",
"followers_x": "67,000+",
"telegram_users": "7,000+",
"total_tools": len(gemini_declarations),
"networks": [
"base",
"solana",
"ethereum",
"bsc",
"arbitrum",
"polygon",
"avalanche",
"fantom",
"gnosis",
"optimism",
"tron",
"bitcoin",
"sepa",
],
"protocol": "x402",
"format": "google_gemini",
"usage": "pip install google-genai && use with model.generate_content(tools=...)",
"function_declarations": gemini_declarations,
}
# ── Framework Discovery Endpoint ────────────────────────────────
@sub_router.get("/frameworks")
async def framework_discovery():
"""Returns all available framework integrations with endpoints.
This is the master discovery endpoint for AI frameworks."""
base = "https://mcp.rugmunch.io/api/v1/x402-tools"
return {
"service": "Rug Munch Intelligence",
"tagline": "We build tools to keep the crypto space safer",
"followers_x": "67,000+",
"telegram_users": "7,000+",
"networks": ["base", "solana"],
"protocol": "x402",
"frameworks": {
"openai": {
"endpoint": f"{base}/openai-tools",
"format": "OpenAI function calling",
"usage": "client.chat.completions.create(tools=...)",
"models": ["gpt-4o", "gpt-4o-mini", "o3-mini"],
"free": True,
},
"anthropic": {
"endpoint": f"{base}/anthropic-tools",
"format": "Claude API tool use",
"usage": "client.messages.create(tools=...)",
"models": ["claude-sonnet-4", "claude-opus-4", "claude-haiku"],
"free": True,
},
"gemini": {
"endpoint": f"{base}/gemini-tools",
"format": "Google Gemini function calling",
"usage": "model.generate_content(tools=...)",
"models": ["gemini-2.0-flash", "gemini-2.5-pro"],
"free": True,
},
"langchain": {
"endpoint": f"{base}/langchain-tools",
"format": "LangChain tool definitions",
"usage": "create_react_agent or LangGraph",
"ecosystem": "langchain, langgraph, crewai, autogen",
"free": True,
},
"mcp": {
"endpoint": "python -m app.mcp.x402_mcp_server",
"format": "Model Context Protocol (stdio)",
"usage": "Claude Desktop, Claude Code, any MCP client",
"free": True,
},
"rest_api": {
"endpoint": f"{base}/{{tool_name}}",
"format": "HTTP POST/GET with JSON",
"usage": "Any language, any framework",
"payment": "x402 (USDC on Base/Solana)",
},
},
"x402_gateways": {
"base": "https://x402.rugmunch.io/tools/{tool}",
"solana": "https://x402-sol.rugmunch.io/tools/{tool}",
"payment": "USDC via x402 protocol",
},
}
# ═══════════════════════════════════════════════════════════════
# TOOL 36: Comprehensive Audit (Super Tool) ($0.15)
# ═══════════════════════════════════════════════════════════════
@sub_router.post("/comprehensive_audit")
async def comprehensive_audit(req: AuditRequest):
"""One-call deep audit combining rug check, forensics, social, and whale analysis."""
import httpx
try:
BASE_URL = "http://localhost:8000"
async def get_rug():
async with httpx.AsyncClient(timeout=10.0) as c:
r = await c.post(
f"{BASE_URL}/api/v1/x402-tools/rugshield",
json={"address": req.address, "chain": req.chain},
)
return r.json() if r.status_code == 200 else None
async def get_forensics():
async with httpx.AsyncClient(timeout=10.0) as c:
r = await c.post(
f"{BASE_URL}/api/v1/x402-tools/forensics",
json={"address": req.address, "chain": req.chain},
)
return r.json() if r.status_code == 200 else None
async def get_social():
async with httpx.AsyncClient(timeout=10.0) as c:
r = await c.post(
f"{BASE_URL}/api/v1/x402-tools/sentiment",
json={"token": req.address, "chain": req.chain},
)
return r.json() if r.status_code == 200 else None
async def get_whale():
async with httpx.AsyncClient(timeout=10.0) as c:
r = await c.post(
f"{BASE_URL}/api/v1/x402-tools/whale",
json={"address": req.address, "chain": req.chain},
)
return r.json() if r.status_code == 200 else None
rug_data, forensics_data, social_data, whale_data = await asyncio.gather(
get_rug(), get_forensics(), get_social(), get_whale(), return_exceptions=True
)
if isinstance(rug_data, Exception):
rug_data = None
if isinstance(forensics_data, Exception):
forensics_data = None
if isinstance(social_data, Exception):
social_data = None
if isinstance(whale_data, Exception):
whale_data = None
risk_score = 50
factors = []
recommendation = "HOLD"
if rug_data and rug_data.get("is_honeypot"):
risk_score += 30
factors.append("CRITICAL: Potential Honeypot detected")
if rug_data and not rug_data.get("liquidity_locked"):
risk_score += 15
factors.append("WARNING: Liquidity not locked")
if forensics_data:
liq_usd = forensics_data.get("liquidity_usd", 0)
if liq_usd > 0 and liq_usd < 1000:
risk_score += 20
factors.append(f"LOW LIQUIDITY: Only ${liq_usd:,.2f} locked")
vol_24h = forensics_data.get("volume_24h", 0)
if liq_usd > 0 and vol_24h > liq_usd * 5:
risk_score += 10
factors.append("High volume/liquidity ratio (possible wash trading)")
if social_data:
sentiment_score = social_data.get("sentiment_score", 0)
if sentiment_score > 0.7:
risk_score -= 10
factors.append("Positive social sentiment")
elif sentiment_score < 0.3:
risk_score += 10
factors.append("Negative social sentiment")
if whale_data:
whale_count = whale_data.get("whale_count", 0)
if whale_count > 0:
risk_score -= 5
factors.append(f"{whale_count} whale(s) detected")
risk_score = max(0, min(100, risk_score))
if risk_score >= 70:
recommendation = "HIGH RISK / AVOID"
elif risk_score >= 50:
recommendation = "CAUTION / HIGH VOLATILITY"
else:
recommendation = "RELATIVELY SAFE / MONITOR"
result = {
"tool": "Comprehensive Audit",
"address": req.address,
"chain": req.chain,
"timestamp": datetime.utcnow().isoformat(),
"summary": {
"risk_score": risk_score,
"recommendation": recommendation,
"factors": factors,
},
"sub_reports": {
"rug_check": rug_data,
"forensics": forensics_data,
"social": social_data,
"whale_analysis": whale_data,
},
"guarantee": "Data delivered or auto-refund via x402 receipt",
}
await _record_payment("comprehensive_audit", "0.15", req.address)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e)) from e
@sub_router.post("/smart_money_alpha")
async def smart_money_alpha(req: SmartMoneyAlphaRequest):
"""One-call smart money signal combining wallet analysis, smart money tracking, and cluster/sybil detection."""
import httpx
try:
BASE_URL = "http://localhost:8000"
async def get_wallet():
async with httpx.AsyncClient(timeout=10.0) as c:
r = await c.post(
f"{BASE_URL}/api/v1/x402-tools/wallet",
json={"address": req.wallet, "chain": req.chain},
)
return r.json() if r.status_code == 200 else None
async def get_smartmoney():
async with httpx.AsyncClient(timeout=10.0) as c:
r = await c.get(f"{BASE_URL}/api/v1/x402-tools/smartmoney")
return r.json() if r.status_code == 200 else None
async def get_cluster():
async with httpx.AsyncClient(timeout=10.0) as c:
r = await c.post(
f"{BASE_URL}/api/v1/x402-tools/cluster",
json={"address": req.wallet, "chain": req.chain},
)
return r.json() if r.status_code == 200 else None
async def get_insider():
async with httpx.AsyncClient(timeout=10.0) as c:
r = await c.post(
f"{BASE_URL}/api/v1/x402-tools/insider",
json={"address": req.wallet, "chain": req.chain},
)
return r.json() if r.status_code == 200 else None
wallet_data, smartmoney_data, cluster_data, insider_data = await asyncio.gather(
get_wallet(), get_smartmoney(), get_cluster(), get_insider(), return_exceptions=True
)
for i, d in enumerate([wallet_data, smartmoney_data, cluster_data, insider_data]):
if isinstance(d, Exception):
[wallet_data, smartmoney_data, cluster_data, insider_data][i] = None
alpha_score = 50
signals = []
if wallet_data:
tx_count = wallet_data.get("tx_count", 0)
if tx_count > 1000:
alpha_score += 10
signals.append(f"Active wallet ({tx_count}+ transactions)")
profit_ratio = wallet_data.get("profit_ratio", 0)
if profit_ratio > 0.5:
alpha_score += 15
signals.append(f"Profitable trader ({profit_ratio:.0%} win rate)")
if smartmoney_data:
sm_alerts = smartmoney_data.get("smart_money_alerts", [])
if sm_alerts:
alpha_score += 10
signals.append(f"{len(sm_alerts)} active smart money movements")
if cluster_data:
cluster_size = cluster_data.get("cluster_size", 0)
if cluster_size > 5:
alpha_score -= 10
signals.append(f"Large cluster ({cluster_size} wallets) - possible sybil")
if cluster_data.get("is_sybil", False):
alpha_score -= 20
signals.append("SYBIL DETECTED - wallet part of coordinated group")
if insider_data and insider_data.get("insider_detected", False):
alpha_score += 20
signals.append("INSIDER ACTIVITY - wallet linked to early token access")
alpha_score = max(0, min(100, alpha_score))
verdict = "NEUTRAL"
if alpha_score >= 75:
verdict = "STRONG ALPHA - high-value wallet signals"
elif alpha_score >= 60:
verdict = "MODERATE ALPHA - watch for follow-up"
elif alpha_score <= 30:
verdict = "LOW SIGNAL - avoid or monitor passively"
result = {
"tool": "Smart Money Alpha",
"wallet": req.wallet,
"chain": req.chain,
"timestamp": datetime.utcnow().isoformat(),
"summary": {
"alpha_score": alpha_score,
"verdict": verdict,
"signals": signals,
},
"sub_reports": {
"wallet_analysis": wallet_data,
"smart_money": smartmoney_data,
"cluster_analysis": cluster_data,
"insider_detection": insider_data,
},
"guarantee": "Data delivered or auto-refund via x402 receipt",
}
await _record_payment("smart_money_alpha", "0.25", req.wallet)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e)) from e
@sub_router.post("/meme_vibe_score")
async def meme_vibe_score(req: MemeVibeRequest):
"""One-call meme token vibe check combining sentiment, social signals, and launch analysis."""
import httpx
try:
BASE_URL = "http://localhost:8000"
async def get_sentiment():
async with httpx.AsyncClient(timeout=10.0) as c:
r = await c.post(
f"{BASE_URL}/api/v1/x402-tools/sentiment",
json={"token": req.token, "chain": req.chain},
)
return r.json() if r.status_code == 200 else None
async def get_social_signal():
async with httpx.AsyncClient(timeout=10.0) as c:
r = await c.post(
f"{BASE_URL}/api/v1/x402-tools/social_signal",
json={"token": req.token, "chain": req.chain},
)
return r.json() if r.status_code == 200 else None
async def get_launch():
async with httpx.AsyncClient(timeout=10.0) as c:
r = await c.get(
f"{BASE_URL}/api/v1/x402-tools/launch?address={req.token}&chain={req.chain}"
)
return r.json() if r.status_code == 200 else None
sentiment_data, social_data, launch_data = await asyncio.gather(
get_sentiment(), get_social_signal(), get_launch(), return_exceptions=True
)
for i, d in enumerate([sentiment_data, social_data, launch_data]):
if isinstance(d, Exception):
[sentiment_data, social_data, launch_data][i] = None
vibe_score = 50
vibes = []
if sentiment_data:
score = sentiment_data.get("sentiment_score", 0)
if score > 0.7:
vibe_score += 15
vibes.append("Strong positive sentiment")
elif score < 0.3:
vibe_score -= 15
vibes.append("Negative sentiment detected")
trend = sentiment_data.get("sentiment_trend", "")
if trend:
vibes.append(f"Sentiment trend: {trend}")
if social_data:
engagement = social_data.get("engagement_score", 0)
bot_ratio = social_data.get("bot_ratio", 0)
if engagement > 0.7:
vibe_score += 10
vibes.append("High social engagement")
if bot_ratio > 0.5:
vibe_score -= 20
vibes.append(f"High bot ratio ({bot_ratio:.0%}) - likely artificial hype")
if launch_data:
bonding = launch_data.get("bonding_curve_progress", 0)
if bonding > 0.8:
vibe_score += 10
vibes.append("Bonding curve near completion - strong launch momentum")
elif bonding < 0.2:
vibe_score -= 10
vibes.append("Early bonding curve - high risk")
if launch_data.get("is_fair_launch", False):
vibe_score += 5
vibes.append("Fair launch confirmed")
vibe_score = max(0, min(100, vibe_score))
if vibe_score >= 75:
verdict = "VIBE CHECK PASSED - organic momentum"
elif vibe_score >= 50:
verdict = "MIXED VIBES - monitor closely"
elif vibe_score >= 30:
verdict = "WEAK VIBES - high risk of dump"
else:
verdict = "RUG VIBES - likely artificial/scam"
result = {
"tool": "Meme Vibe Score",
"token": req.token,
"chain": req.chain,
"timestamp": datetime.utcnow().isoformat(),
"summary": {
"vibe_score": vibe_score,
"verdict": verdict,
"vibes": vibes,
},
"sub_reports": {
"sentiment": sentiment_data,
"social_signals": social_data,
"launch_analysis": launch_data,
},
"guarantee": "Data delivered or auto-refund via x402 receipt",
}
await _record_payment("meme_vibe_score", "0.01", req.token)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e)) from e
# ── Bundle Pricing Endpoints ─────────────────────────────────────
# Bundles aggregate multiple tools at a discount vs individual calls.
# Each bundle runs its component tools in parallel and returns a unified result.
from app.domains.billing.x402.shared import BUNDLES as _BUNDLES # noqa: E402
@sub_router.get("/bundles")
async def list_bundles():
"""List all available tool bundles with pricing and savings."""
return {
"bundles": {
bid: {
"name": b["name"],
"description": b["description"],
"tools": b["tools"],
"individual_total": f"${b['individual_total']:.2f}",
"bundle_price": f"${b['bundle_price_usd']:.2f}",
"savings": f"{int((1 - b['bundle_price_usd'] / b['individual_total']) * 100)}%",
"trial_free": b["trial_free"],
}
for bid, b in _BUNDLES.items()
}
}
@sub_router.post("/bundles/security_pack")
async def bundle_security_pack(req: BundleRequest):
"""Security Pack: rugshield + audit + urlcheck + honeypot_check at 23% discount."""
target = req.address or req.token or req.url
if not target:
raise HTTPException(status_code=400, detail="Provide address, token, or url")
tasks = {
"rugshield": "http://localhost:8000/api/v1/x402-tools/rugshield",
"audit": "http://localhost:8000/api/v1/x402-tools/audit",
"urlcheck": "http://localhost:8000/api/v1/x402-tools/urlcheck",
"honeypot_check": "http://localhost:8000/api/v1/x402-tools/honeypot_check",
}
results = {}
async with aiohttp.ClientSession() as session:
coros = {}
for name, url in tasks.items():
body = {"address": target, "token_address": target, "url": target, "chain": req.chain}
coros[name] = session.post(url, json=body, timeout=aiohttp.ClientTimeout(total=30))
for name, coro in coros.items():
try:
resp = await coro
results[name] = await resp.json() if resp.status == 200 else {"status": resp.status}
except Exception as e:
results[name] = {"error": str(e)}
verdict = "SAFE"
risk_scores = []
for r in results.values():
if isinstance(r, dict):
rs = r.get("risk_score")
if rs is not None:
risk_scores.append(rs)
if r.get("risk_level") in ("CRITICAL", "HIGH"):
verdict = "CAUTION"
avg_risk = sum(risk_scores) / len(risk_scores) if risk_scores else 0
if avg_risk > 60:
verdict = "HIGH_RISK"
elif avg_risk > 40:
verdict = "CAUTION"
return {
"tool": "Security Pack",
"bundle": "security_pack",
"target": target,
"chain": req.chain,
"timestamp": datetime.utcnow().isoformat(),
"results": results,
"summary": {
"verdict": verdict,
"avg_risk_score": round(avg_risk, 1),
"tools_checked": len(results),
},
"price_usd": "0.10",
"savings": "23% vs individual calls",
"guarantee": "Data delivered or auto-refund via x402 receipt",
}
@sub_router.post("/bundles/intelligence_pack")
async def bundle_intelligence_pack(req: BundleRequest):
"""Intelligence Pack: whale + smartmoney + cluster + insider at 29% discount."""
wallet = req.wallet or req.address
if not wallet:
raise HTTPException(status_code=400, detail="Provide wallet or address")
tasks = {
"whale": "http://localhost:8000/api/v1/x402-tools/whale",
"smartmoney": "http://localhost:8000/api/v1/x402-tools/smartmoney",
"cluster": "http://localhost:8000/api/v1/x402-tools/cluster",
"insider": "http://localhost:8000/api/v1/x402-tools/insider",
}
results = {}
async with aiohttp.ClientSession() as session:
coros = {}
for name, url in tasks.items():
body = {"address": wallet, "wallet_address": wallet, "chain": req.chain}
coros[name] = session.post(url, json=body, timeout=aiohttp.ClientTimeout(total=30))
for name, coro in coros.items():
try:
resp = await coro
results[name] = await resp.json() if resp.status == 200 else {"status": resp.status}
except Exception as e:
results[name] = {"error": str(e)}
return {
"tool": "Intelligence Pack",
"bundle": "intelligence_pack",
"wallet": wallet,
"chain": req.chain,
"timestamp": datetime.utcnow().isoformat(),
"results": results,
"price_usd": "0.25",
"savings": "29% vs individual calls",
"guarantee": "Data delivered or auto-refund via x402 receipt",
}
@sub_router.post("/bundles/all_in_one")
async def bundle_all_in_one(req: BundleRequest):
"""All-in-One: comprehensive_audit + smart_money_alpha + meme_vibe_score at 30% discount."""
target = req.address or req.token or req.wallet
if not target:
raise HTTPException(status_code=400, detail="Provide address, token, or wallet")
tasks = {
"comprehensive_audit": "http://localhost:8000/api/v1/x402-tools/comprehensive_audit",
"smart_money_alpha": "http://localhost:8000/api/v1/x402-tools/smart_money_alpha",
"meme_vibe_score": "http://localhost:8000/api/v1/x402-tools/meme_vibe_score",
}
results = {}
async with aiohttp.ClientSession() as session:
coros = {}
for name, url in tasks.items():
body = {"address": target, "token": target, "wallet": target, "chain": req.chain}
coros[name] = session.post(url, json=body, timeout=aiohttp.ClientTimeout(total=30))
for name, coro in coros.items():
try:
resp = await coro
results[name] = await resp.json() if resp.status == 200 else {"status": resp.status}
except Exception as e:
results[name] = {"error": str(e)}
return {
"tool": "All-in-One Audit",
"bundle": "all_in_one",
"target": target,
"chain": req.chain,
"timestamp": datetime.utcnow().isoformat(),
"results": results,
"price_usd": "0.35",
"savings": "30% vs individual calls",
"guarantee": "Data delivered or auto-refund via x402 receipt",
}
@sub_router.post("/forensic_pack")
async def bundle_forensic_pack(req: BundleRequest):
"""Forensic Investigation Pack - valuation + OSINT + report at 33% discount."""
target = req.address or req.token or req.wallet
if not target:
raise HTTPException(status_code=400, detail="Provide address, token, or wallet")
tasks = {
"forensic_valuation": "http://localhost:8000/api/v1/x402-tools/forensic_valuation",
"osint_identity_hunt": "http://localhost:8000/api/v1/x402-tools/osint_identity_hunt",
"investigation_report": "http://localhost:8000/api/v1/x402-tools/investigation_report",
}
results = {}
async with aiohttp.ClientSession() as session:
coros = {}
for name, url in tasks.items():
body = {"address": target, "token": target, "wallet": target, "chain": req.chain}
coros[name] = session.post(url, json=body, timeout=aiohttp.ClientTimeout(total=30))
for name, coro in coros.items():
try:
resp = await coro
results[name] = await resp.json() if resp.status == 200 else {"status": resp.status}
except Exception as e:
results[name] = {"error": str(e)}
return {
"tool": "Forensic Investigation Pack",
"bundle": "forensic_pack",
"target": target,
"chain": req.chain,
"timestamp": datetime.utcnow().isoformat(),
"results": results,
"price_usd": "0.40",
"savings": "33% vs individual calls",
"guarantee": "Data delivered or auto-refund via x402 receipt",
}
# ═══════════════════════════════════════════════════════════
# MCP Proxy - handles external MCP tool execution from workers
# ═══════════════════════════════════════════════════════════
@sub_router.post("/mcp-proxy")
async def mcp_proxy(req: MCPProxyRequest):
"""Proxy MCP tool calls from Cloudflare Workers to external APIs.
Maps service_tool to the appropriate external API and executes."""
svc = req.service.lower()
tool = req.tool
args = req.arguments
# Service routing table - maps to known external APIs
routes = {
"dexscreener": "https://api.dexscreener.com",
"jupiter": "https://quote-api.jup.ag/v6",
"pumpfun": "https://frontend-api.pump.fun",
"raydium": "https://api.raydium.io/v2",
"defillama": "https://api.llama.fi",
"dexpaprika": "https://api.dexpaprika.com",
"coincap": "https://api.coincap.io/v2",
"coinmarketcap": "https://pro-api.coinmarketcap.com/v1",
"cryptopanic": "https://cryptopanic.com/api/v1",
"cryptocompare": "https://min-api.cryptocompare.com/data",
"blockchair": "https://api.blockchair.com",
"blockchain": "https://blockchain.info",
"mempool": "https://mempool.space/api",
"solana": "https://api.mainnet-beta.solana.com",
"helius": "https://api.helius.xyz/v0",
"birdeye": "https://public-api.birdeye.com",
"coingecko": "https://api.coingecko.com/api/v3",
"cryptoiz": "https://api.cryptoiz.com",
"blockrun": "https://api.blockrun.ai",
"agentfi": "https://api.agentfi.xyz",
"moralis": "https://deep-index.moralis.io/api/v2.2",
"gmgn": "https://gmgn.ai/api",
"nansen": "https://api.nansen.ai",
"arkham": "https://api.arkhamintelligence.com",
"dune": "https://api.dune.com/api/v1",
"solscan": "https://public-api.solscan.io",
"quicknode": "https://api.quicknode.com",
}
base_url = routes.get(svc)
if not base_url:
return {"error": f"Unsupported service: {svc}", "available": list(routes.keys())}
# Construct the endpoint based on tool name
tool_endpoints = {
# DexScreener
"getLatestTokenProfiles": "/token-profiles/latest/v1",
"getLatestBoostedTokens": "/token-boosted/latest/v1",
"getPairs": f"/latest/dex/pairs/solana/{args.get('pairAddresses', args.get('tokenAddresses', ''))}",
# Jupiter
"getQuote": "/quote",
"getPrice": "/price",
"getTokens": "/tokens",
# CoinGecko
"getPrice": "/simple/price", # noqa: F601
# DeFiLlama
"getTVL": f"/tvl/{args.get('protocol', '')}",
"getProtocols": "/protocols",
# Solana RPC
"getHealth": "",
# General fallback
}
endpoint = tool_endpoints.get(tool, f"/{tool}")
try:
async with aiohttp.ClientSession() as session:
url = f"{base_url}{endpoint}"
headers = {"Accept": "application/json"}
# Add API keys for services that need them
if svc == "coingecko":
headers["x-cg-pro-api-key"] = os.getenv("COINGECKO_API_KEY_PRO", "")
elif svc == "helius":
headers["Authorization"] = f"Bearer {os.getenv('HELIUS_API_KEY', '')}"
elif svc == "moralis":
headers["X-API-Key"] = os.getenv("MORALIS_API_KEY", "")
elif svc == "birdeye":
headers["X-API-KEY"] = os.getenv("BIRDEYE_API_KEY", "")
async with session.get(
url, params=args, headers=headers, timeout=aiohttp.ClientTimeout(total=15)
) as resp:
data = await resp.json()
return {
"service": svc,
"tool": tool,
"status": "success" if resp.status < 400 else "error",
"data": data,
}
except Exception as e:
return {
"service": svc,
"tool": tool,
"status": "error",
"error": str(e),
"note": "Direct external API calls failed - try REST endpoint for cached/fallback data",
}
# ═══════════════════════════════════════════════════════════
# Human Payment - Multi-Chain Wallet Pay-Per-Call
# ═══════════════════════════════════════════════════════════
async def _record_payment(tool_id: str, price_usd: str, payer: str) -> None:
"""Surface for super-tool endpoints (comprehensive audit, etc.).
Reuses the P3A stub via the shared module; canonical recording lives
in app/billing/x402/enforcement.py and is wired in P3B+.
"""
from app.domains.billing.x402.shared import record_x402_payment
await record_x402_payment(tool_id, price_usd, payer)
@sub_router.get("/payment-methods")
async def get_payment_methods():
"""List all payment methods available for human wallet payments.
Returns every supported token, chain, and the destination wallet address.
Prices shown in USD; actual payment is in the selected token at market rate.
"""
return {
"tokens": [
{
"key": key,
"chain": info["chain"],
"network": info["network"],
"asset": info["asset"],
"label": info["label"],
"icon": info["icon"],
"pay_to": HUMAN_PAY_TO.get(info["chain"], ""),
"decimals": info["decimals"],
}
for key, info in HUMAN_PAYMENT_TOKENS.items()
],
"pay_to_addresses": {chain: addr for chain, addr in HUMAN_PAY_TO.items() if addr},
"chain_count": len({i["chain"] for i in HUMAN_PAYMENT_TOKENS.values()}),
"token_count": len(HUMAN_PAYMENT_TOKENS),
}
@sub_router.post("/human-execute")
async def human_execute(req: Any):
"""Execute a tool after human wallet payment verification.
Multi-chain: Base, Solana, Ethereum, BSC, Polygon, Arbitrum, TRON, Bitcoin, SEPA/EUR.
Verification routed through the same facilitator system as bot payments.
All payments go to your wallets - no middleman.
"""
# Re-import the typed request model for runtime validation
from app.domains.billing.x402.shared import HumanPaymentRequest
req = HumanPaymentRequest.model_validate(req) if isinstance(req, dict) else req
tool_name = req.tool
token_info = HUMAN_PAYMENT_TOKENS.get(req.payment_token)
if not token_info:
return {
"success": False,
"error": f"Unsupported payment token: {req.payment_token}",
"supported_tokens": list(HUMAN_PAYMENT_TOKENS.keys()),
}
chain = req.chain or token_info["chain"]
expected_pay_to = HUMAN_PAY_TO.get(chain, "")
# ── Payment Verification ──────────────────────────────────
verified = False
verification_method = "unknown"
facilitator_used = None
try:
# Try facilitator router first (same as bot payments)
from app.domains.billing.facilitators.router import get_facilitator_router
router = get_facilitator_router()
# Build a minimal payload that the router can work with
payload = {
"x402Version": 2,
"txHash": req.tx_hash,
"payer": req.wallet,
"accepted": {
"network": token_info["network"],
"asset": token_info["asset"],
"amount": "0", # Will be verified by facilitator
"payTo": expected_pay_to,
},
}
result = await router.verify(
payload=payload,
chain_key=chain,
token_symbol=token_info["asset"],
)
if result.verified:
verified = True
verification_method = f"facilitator:{result.facilitator}"
facilitator_used = result.facilitator
except ImportError:
pass
except Exception:
pass
# Fallback: direct on-chain verification
if not verified:
try:
verified = await _verify_onchain_direct(req.tx_hash, chain, token_info, expected_pay_to)
verification_method = "onchain-direct"
except Exception:
pass
if not verified:
return {
"success": False,
"error": "Payment verification failed. Transaction not confirmed or wrong recipient.",
"tx_hash": req.tx_hash,
"chain": chain,
"expected_pay_to": expected_pay_to[:10] + "...",
}
# ── Anti-abuse: check trial limits ────────────────────────
from app.routers.x402_enforcement import check_trial
_can_trial, _remaining = check_trial(tool_name, req.wallet)
# ── Execute the tool ──────────────────────────────────────
try:
# Determine gateway
if chain in ("base", "ethereum", "bsc", "polygon", "arbitrum"):
gw = "https://base.rugmunch.io"
elif chain == "tron":
gw = "https://base.rugmunch.io" # TRON tools proxied through base gateway
else:
gw = "https://sol.rugmunch.io"
async with (
aiohttp.ClientSession() as session,
session.post(
f"{gw}/tools/{tool_name}",
json=req.arguments,
headers={"Content-Type": "application/json", "X-RMI-Human-Payment": "verified"},
timeout=aiohttp.ClientTimeout(total=30),
) as resp,
):
text = await resp.text()
try:
result_data = json.loads(text)
except json.JSONDecodeError:
result_data = {"raw": text[:500]}
# Record payment in Redis (same as bot payments)
try:
from app.routers.x402_enforcement import get_redis
r = get_redis()
if r:
import time as _time
r.setex(
f"x402:spent_tx:{req.tx_hash}",
86400,
json.dumps(
{
"chain": chain,
"payer": req.wallet,
"amount": "0",
"tool": tool_name,
"timestamp": _time.time(),
"method": "human-wallet",
"facilitator": facilitator_used,
}
),
)
except Exception:
pass
return {
"success": resp.status < 400,
"tool": tool_name,
"payment_token": req.payment_token,
"chain": chain,
"tx_hash": req.tx_hash,
"verified": True,
"verification": verification_method,
"facilitator": facilitator_used,
"pay_to": expected_pay_to,
"result": result_data,
}
except Exception as e:
return {"success": False, "error": f"Tool execution failed: {e!s}"}
async def _verify_onchain_direct(
tx_hash: str, chain: str, token_info: dict, expected_pay_to: str
) -> bool:
"""Direct on-chain verification fallback for human payments."""
import aiohttp
async with aiohttp.ClientSession() as session:
if chain == "solana":
async with session.post(
"https://api.mainnet-beta.solana.com",
json={
"jsonrpc": "2.0",
"id": 1,
"method": "getTransaction",
"params": [
tx_hash,
{"encoding": "jsonParsed", "maxSupportedTransactionVersion": 0},
],
},
timeout=aiohttp.ClientTimeout(total=15),
) as resp:
data = await resp.json()
tx = data.get("result", {})
if not tx:
return False
# Check if any transfer goes to our wallet
meta = tx.get("meta", {})
return any(bal.get("owner") == expected_pay_to for bal in meta.get("postTokenBalances", []))
elif chain in ("tron", "bitcoin", "sepa"):
# For these chains, assume verified if facilitator passed
# (they don't have simple Etherscan-style APIs)
return True
else:
# EVM chains - use Etherscan-family APIs
explorers = {
"base": "https://api.basescan.org/api",
"ethereum": "https://api.etherscan.io/api",
"bsc": "https://api.bscscan.com/api",
"polygon": "https://api.polygonscan.com/api",
"arbitrum": "https://api.arbiscan.io/api",
}
explorer_url = explorers.get(chain, explorers["ethereum"])
api_key = os.getenv("ETHERSCAN_API_KEY", "")
async with session.get(
f"{explorer_url}?module=transaction&action=gettxreceiptstatus&txhash={tx_hash}&apikey={api_key}",
timeout=aiohttp.ClientTimeout(total=10),
) as resp:
data = await resp.json()
result = data.get("result", {})
if isinstance(result, dict):
return result.get("status") == "1"
return str(data.get("status")) == "1"
# ALIAS ROUTES - Map dead tool IDs to real handler endpoints
# These tools appear in TOOL_PRICES and x402 manifest but had no routes.
# Each alias proxies the request to the real implementation.
# ═══════════════════════════════════════════════════════════════
async def _check_rate_limit(request: Request) -> bool:
"""Simple IP-based rate limiter: 60 req/min per IP, 300 req/5min per IP.
Uses the same Redis as x402 enforcement. Fail-open if Redis is down."""
try:
from app.routers.x402_enforcement import get_redis
r = get_redis()
if not r:
return True # No Redis = allow
cf_ip = request.headers.get("CF-Connecting-IP", "") or (
request.client.host if request.client else "0"
)
# Normalize IP-key
import hashlib as _hl
ip_key = _hl.sha256(cf_ip.encode()).hexdigest()[:16]
# 1-minute window
key_min = f"rl:{ip_key}:min"
count = r.incr(key_min)
if count == 1:
r.expire(key_min, 60)
if count > 60:
return False
# 5-minute window
key_5m = f"rl:{ip_key}:5min"
count5 = r.incr(key_5m)
if count5 == 1:
r.expire(key_5m, 300)
return not count5 > 300
except Exception:
return True # Fail open
@sub_router.get("/{tool_id}")
async def tool_alias_dispatcher_get(tool_id: str, request: Request):
"""GET catch-all for paid tools - converts query params to JSON body and dispatches.
Handles x402 discovery agents and browser-based clients that use GET + query params
instead of POST + JSON body. The enforcement middleware has already verified payment
or granted a trial before this handler is reached.
"""
from app.domains.billing.x402.shared import TOOL_ALIASES
# Read query params as the request body
query_params = dict(request.query_params)
if not query_params:
# GET without params = discovery/info request, return basic tool info
pricing_info = {}
try:
from app.routers.x402_enforcement import TOOL_PRICES
pricing_info = TOOL_PRICES.get(tool_id, {})
except Exception:
pass
if pricing_info:
return JSONResponse(
content={
"tool": tool_id,
"description": pricing_info.get("description", ""),
"price_usd": pricing_info.get("price_usd", 0),
"trial_free": pricing_info.get("trial_free", 3),
"usage": "POST with JSON body or GET with query params (address, chain, etc.)",
"method": "GET or POST",
}
)
raise HTTPException(status_code=404, detail=f"Tool '{tool_id}' not found.")
# ── Rate limiting ──
if not await _check_rate_limit(request):
raise HTTPException(status_code=429, detail="Rate limit exceeded. Max 60 req/min per IP.")
# Resolve alias target (same logic as POST dispatcher)
target = TOOL_ALIASES.get(tool_id)
injected_chain = None
if target is None and "_" in tool_id:
_CHAIN_SUFFIXES: ClassVar[dict] = {
"solana",
"base",
"ethereum",
"bsc",
"polygon",
"arbitrum",
"optimism",
"avalanche",
"fantom",
"gnosis",
"tron",
"bitcoin",
}
try:
from app.routers.x402_enforcement import TOOL_PRICES as _TP
pricing = _TP.get(tool_id, {})
base_tool = pricing.get("base_tool")
if base_tool:
target = base_tool
injected_chain = pricing.get("chain")
except Exception:
pass
if target is None:
last_underscore = tool_id.rfind("_")
if last_underscore > 0:
suffix = tool_id[last_underscore + 1 :]
prefix = tool_id[:last_underscore]
if suffix in _CHAIN_SUFFIXES:
target = TOOL_ALIASES.get(prefix, prefix)
injected_chain = suffix
if target is None:
# No alias - use the tool_id itself as the target (it's a direct route or DataBus)
# Check if it has a direct POST route handler
target = tool_id
# Check if target has a dedicated POST handler - if so, dispatch via internal call
# If not (DataBus-only tool), use the DataBus caching shield
body = query_params
if injected_chain and isinstance(body, dict):
body.setdefault("chain", injected_chain)
# ── Dispatch via DataBus caching shield ──
try:
result = await td.call_tool(tool_id, body)
if result is None:
# Fallback: try internal POST to the catch-all dispatcher
target_url = f"http://localhost:8000/api/v1/x402-tools/{target}"
headers = {"Content-Type": "application/json", "User-Agent": "RMI-GET-Proxy/3.1"}
for h in (
"X-RMI-Payment",
"X-RMI-Trial",
"X-RMI-Trial-Remaining",
"X-RMI-Internal",
"X-Device-Id",
"X-Wallet-Address",
"Authorization",
):
val = request.headers.get(h)
if val:
headers[h] = val
import httpx
async with httpx.AsyncClient(timeout=45) as client:
resp = await client.post(target_url, json=body, headers=headers)
try:
result = resp.json()
except Exception:
result = {"data": resp.text, "status": resp.status_code}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Tool execution failed: {e!s}") from e
if isinstance(result, dict):
result["alias_of"] = target
result["tool"] = tool_id
result["method"] = "GET"
if injected_chain:
result["chain"] = injected_chain
# ── Wallet Intelligence Enrichment ──
try:
opt_out = request.query_params.get("enrich", "").lower() == "false"
from app.routers.x402_enrichment import enrich_tool_response
result = enrich_tool_response(tool_id, result, request_params=body, opt_out=opt_out)
except Exception:
pass
return JSONResponse(content=result, status_code=200)
@sub_router.post("/{tool_id}")
async def tool_alias_dispatcher(tool_id: str, request: Request):
"""Catch-all dispatcher for tool aliases and per-chain variants.
Handles three cases:
1. Named aliases (scam_database → urlcheck)
2. Per-chain variants (wallet_solana → wallet with chain=solana)
3. Expanded tools (flash_loan_detect → closest real handler)
Returns 404 for truly unknown tools."""
from app.domains.billing.x402.shared import TOOL_ALIASES
# ── Rate limiting ──
if not await _check_rate_limit(request):
raise HTTPException(status_code=429, detail="Rate limit exceeded. Max 60 req/min per IP.")
# Try exact alias first
target = TOOL_ALIASES.get(tool_id)
injected_chain = None
# If not a named alias, check if it's a per-chain variant (e.g., wallet_solana)
if target is None and "_" in tool_id:
# Try to extract chain suffix
_CHAIN_SUFFIXES: ClassVar[dict] = {
"solana",
"base",
"ethereum",
"bsc",
"polygon",
"arbitrum",
"optimism",
"avalanche",
"fantom",
"gnosis",
"tron",
"bitcoin",
}
# Also check TOOL_PRICES for the base_tool/chain fields
try:
from app.routers.x402_enforcement import TOOL_PRICES
pricing = TOOL_PRICES.get(tool_id, {})
base_tool = pricing.get("base_tool")
if base_tool:
target = base_tool
injected_chain = pricing.get("chain")
except Exception:
pass
# Fallback: parse tool_chain format
if target is None:
last_underscore = tool_id.rfind("_")
if last_underscore > 0:
suffix = tool_id[last_underscore + 1 :]
prefix = tool_id[:last_underscore]
if suffix in _CHAIN_SUFFIXES and prefix in TOOL_ALIASES:
target = TOOL_ALIASES[prefix]
injected_chain = suffix
elif suffix in _CHAIN_SUFFIXES:
# Check if prefix is a real tool endpoint
from app.routers.x402_enforcement import TOOL_PRICES as _TP
if prefix in _TP or any(prefix == t for t in TOOL_ALIASES if t == prefix):
target = TOOL_ALIASES.get(prefix, prefix)
injected_chain = suffix
if target is None:
# No alias found - try DataBus execution for all 127 catalog tools
# DataBus is the universal backend for all tools
body = {}
if request.headers.get("content-type", "").startswith("application/json"):
try:
body = await request.json()
except Exception:
body = {}
if injected_chain and isinstance(body, dict):
body.setdefault("chain", injected_chain)
try:
result = await td.call_tool(tool_id, body)
if result is not None:
if isinstance(result, dict):
result["tool"] = tool_id
result["alias_of"] = "databus"
if injected_chain:
result["chain"] = injected_chain
# Enrichment
try:
from app.routers.x402_enrichment import enrich_tool_response
opt_out = request.query_params.get("enrich", "").lower() == "false"
result = enrich_tool_response(
tool_id, result, request_params=body, opt_out=opt_out
)
except Exception:
pass
return JSONResponse(content=result, status_code=200)
except Exception:
pass
# DataBus failed - raise 404 only for truly unknown tools
raise HTTPException(
status_code=404,
detail=f"Tool '{tool_id}' not found. See /mcp/tools for available tools",
)
target_url = f"http://localhost:8000/api/v1/x402-tools/{target}"
try:
body = (
await request.json()
if request.headers.get("content-type", "").startswith("application/json")
else {}
)
except Exception:
body = {}
# Inject chain for per-chain variants
if injected_chain and isinstance(body, dict):
body.setdefault("chain", injected_chain)
headers = {}
for h in (
"x-pay",
"X-Pay",
"User-Agent",
"Authorization",
"x-wallet-address",
"X-Wallet-Address",
"x-turnstile-token",
"X-Turnstile-Token",
"X-Forwarded-For",
"X-Real-IP",
"CF-Connecting-IP",
"X-RMI-Internal",
"X-RMI-Payment",
"X-RMI-Trial",
"X-RMI-Trial-Remaining",
"X-Device-Id",
):
val = request.headers.get(h)
if val:
headers[h] = val
headers["content-type"] = "application/json"
headers["User-Agent"] = headers.get("User-Agent", "RMI-Alias-Proxy/3.1")
headers["X-RMI-Alias"] = tool_id
import httpx
async with httpx.AsyncClient(timeout=45) as client:
resp = await client.post(target_url, json=body, headers=headers)
try:
result = resp.json()
except Exception:
result = {"data": resp.text, "status": resp.status_code}
rh = {}
for h in ("X-RMI-Payment", "X-RMI-Trial", "X-RMI-Trial-Remaining", "X-RMI-Refund-Flagged"):
if resp.headers.get(h):
rh[h] = resp.headers[h]
# Wrap result with alias metadata
if isinstance(result, dict):
result["alias_of"] = target
result["tool"] = tool_id
if injected_chain:
result["chain"] = injected_chain
# ── Wallet Intelligence Enrichment ──
# Annotate responses with label data, scam patterns, and sanctions from
# the Wallet Memory Bank (389K labels / 155K addresses in ClickHouse).
# Controlled by ?enrich=false opt-out. Cached per-address in Redis (1hr TTL).
try:
opt_out = request.query_params.get("enrich", "").lower() == "false"
from app.routers.x402_enrichment import enrich_tool_response
result = enrich_tool_response(tool_id, result, request_params=body, opt_out=opt_out)
except Exception:
pass # Enrichment is best-effort - never block a response on it
return JSONResponse(content=result, status_code=resp.status_code, headers=rh)