- Fix 71 invalid-syntax files (class-body newline-broken assignments) - Add from/None chain to 307 B904 raise-without-from sites - Add B008 ignore to ruff.toml (already in pyproject.toml) - Noqa F401 on __init__.py re-exports (137 sites) - Noqa E402 on deferred imports (63 sites) - Bulk-add stdlib/FastAPI/project imports for F821 (127 sites) - Replace ×→x, –→-, …→... in docstrings (4093 chars) - Manual refactor of 5 SIM103/SIM116 patterns Tests: 791 passed (66 deselected due to pre-existing Redis issues in test_rag.py) Co-authored-by: opencode <opencode@rugmunch.io>
120 lines
4.7 KiB
Python
120 lines
4.7 KiB
Python
"""
|
|
DeFiLlama DataBus Provider - Free, unlimited DeFi analytics.
|
|
7,661 protocols across 350+ chains. No API key needed.
|
|
"""
|
|
|
|
import logging
|
|
|
|
logger = logging.getLogger("databus.defillama")
|
|
|
|
|
|
async def fetch_defillama_tvl(
|
|
protocol: str | None = None, chain: str | None = None, limit: int = 20
|
|
) -> dict:
|
|
"""Fetch TVL data from DeFiLlama - free, no auth, no rate limits for standard traffic."""
|
|
import aiohttp
|
|
|
|
results = {}
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
# All protocols
|
|
try:
|
|
async with session.get(
|
|
"https://api.llama.fi/protocols", timeout=aiohttp.ClientTimeout(total=15)
|
|
) as resp:
|
|
if resp.status == 200:
|
|
data = await resp.json()
|
|
if protocol:
|
|
# Filter by protocol name
|
|
matches = [
|
|
p for p in data if protocol.lower() in p.get("name", "").lower()
|
|
][:limit]
|
|
results["protocols"] = matches
|
|
elif chain:
|
|
matches = [
|
|
p
|
|
for p in data
|
|
if chain.lower() in [c.lower() for c in p.get("chains", [])]
|
|
][:limit]
|
|
results["protocols"] = matches
|
|
else:
|
|
# Return top protocols by TVL
|
|
results["protocols"] = sorted(
|
|
data, key=lambda x: x.get("tvl", 0) or 0, reverse=True
|
|
)[:limit]
|
|
results["total"] = len(data)
|
|
except Exception as e:
|
|
logger.warning(f"DeFiLlama protocols fetch failed: {e}")
|
|
results["protocols"] = []
|
|
|
|
# Chain TVLs
|
|
try:
|
|
async with session.get(
|
|
"https://api.llama.fi/v2/chains", timeout=aiohttp.ClientTimeout(total=15)
|
|
) as resp:
|
|
if resp.status == 200:
|
|
chain_data = await resp.json()
|
|
results["chains"] = sorted(
|
|
chain_data, key=lambda x: x.get("tvl", 0) or 0, reverse=True
|
|
)[:limit]
|
|
except Exception as e:
|
|
logger.warning(f"DeFiLlama chains fetch failed: {e}")
|
|
results["chains"] = []
|
|
|
|
# Global TVL
|
|
try:
|
|
async with session.get(
|
|
"https://api.llama.fi/v2/historicalChainTvl/All",
|
|
timeout=aiohttp.ClientTimeout(total=15),
|
|
) as resp:
|
|
if resp.status == 200:
|
|
results["historical_tvl"] = (await resp.json())[-30:] # Last 30 days
|
|
except Exception as e:
|
|
logger.warning(f"DeFiLlama historical TVL failed: {e}")
|
|
|
|
results["source"] = "DeFiLlama (free, no auth)"
|
|
results["url"] = "https://defillama.com"
|
|
return results
|
|
|
|
|
|
async def fetch_pyth_prices(symbols: str | None = None, limit: int = 10) -> dict:
|
|
"""Fetch live prices from Pyth Network Hermes API - 125+ institutional publishers."""
|
|
import aiohttp
|
|
|
|
try:
|
|
# Pyth Hermes REST API for latest price feeds
|
|
url = "https://hermes.pyth.network/v2/updates/price/latest"
|
|
params = {}
|
|
if symbols:
|
|
# Convert symbols to Pyth feed IDs (simplified - production would use lookup)
|
|
params["ids"] = symbols
|
|
|
|
async with aiohttp.ClientSession() as session, session.get(
|
|
url, params=params, timeout=aiohttp.ClientTimeout(total=10)
|
|
) as resp:
|
|
if resp.status == 200:
|
|
data = await resp.json()
|
|
results = {
|
|
"prices": [],
|
|
"source": "Pyth Network (Hermes)",
|
|
"publishers": "125+ institutional (Jane Street, Wintermute, etc.)",
|
|
"rate_limit": "10 req / 10 seconds (free)",
|
|
}
|
|
for parsed in data.get("parsed", [])[:limit]:
|
|
price_info = parsed.get("price", {})
|
|
results["prices"].append(
|
|
{
|
|
"id": parsed.get("id", ""),
|
|
"price": float(price_info.get("price", 0))
|
|
* 10 ** float(price_info.get("expo", 0)),
|
|
"conf": float(price_info.get("conf", 0))
|
|
* 10 ** float(price_info.get("expo", 0)),
|
|
"publish_time": price_info.get("publish_time"),
|
|
}
|
|
)
|
|
return results
|
|
except Exception as e:
|
|
logger.warning(f"Pyth fetch failed: {e}")
|
|
return {"error": str(e), "source": "Pyth Network (Hermes)"}
|
|
|
|
return {"prices": [], "source": "Pyth Network (Hermes)"}
|