Files
proxmox/scripts/verify/build-cw-public-price-table.py
defiQUG b8613905bd
Some checks failed
Deploy to Phoenix / validate (push) Failing after 15s
Deploy to Phoenix / deploy (push) Has been skipped
chore: sync workspace — configs, docs, scripts, CI, pnpm, submodules
- Submodule pins: dbis_core, cross-chain-pmm-lps, mcp-proxmox (local, push may be pending), metamask-integration, smom-dbis-138
- Atomic swap + cross-chain-pmm-lops-publish, deploy-portal workflow, phoenix deploy-targets, routing/aggregator matrices
- Docs, token-lists, forge proxy, phoenix API, runbooks, verify scripts

Made-with: Cursor
2026-04-21 22:01:33 -07:00

453 lines
15 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import json
import math
import re
import subprocess
import time
from collections import deque
from dataclasses import dataclass
from decimal import Decimal, InvalidOperation, getcontext
from pathlib import Path
getcontext().prec = 50
ROOT = Path(__file__).resolve().parents[2]
DEPLOYMENT_STATUS = ROOT / "cross-chain-pmm-lps" / "config" / "deployment-status.json"
UNISWAP_DISCOVERY = ROOT / "reports" / "extraction" / "promod-uniswap-v2-live-pair-discovery-latest.json"
JSON_OUT = ROOT / "reports" / "status" / "cw-public-prices-latest.json"
DOC_OUT = ROOT / "docs" / "03-deployment" / "CW_PUBLIC_NETWORK_PRICES.md"
ROOT_ENV_PATH = ROOT / ".env"
SMOM_ENV_PATH = ROOT / "smom-dbis-138" / ".env"
ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
UINT_RE = re.compile(r"\b\d+\b")
CHAIN_CONFIG = {
"1": {"rpc_keys": ["ETHEREUM_MAINNET_RPC"]},
"10": {"rpc_keys": ["OPTIMISM_RPC_URL", "OPTIMISM_MAINNET_RPC"]},
"25": {"rpc_keys": ["CRONOS_RPC_URL", "CRONOS_MAINNET_RPC"]},
"56": {"rpc_keys": ["BSC_RPC_URL", "BSC_MAINNET_RPC"]},
"100": {"rpc_keys": ["GNOSIS_RPC_URL", "GNOSIS_MAINNET_RPC", "GNOSIS_RPC"]},
"137": {"rpc_keys": ["POLYGON_MAINNET_RPC", "POLYGON_RPC_URL"]},
"1111": {"rpc_keys": ["WEMIX_RPC_URL", "WEMIX_MAINNET_RPC"]},
"8453": {"rpc_keys": ["BASE_RPC_URL", "BASE_MAINNET_RPC"]},
"42161": {"rpc_keys": ["ARBITRUM_RPC_URL", "ARBITRUM_MAINNET_RPC"]},
"42220": {"rpc_keys": ["CELO_RPC_URL", "CELO_MAINNET_RPC", "CELO_RPC"]},
"43114": {"rpc_keys": ["AVALANCHE_RPC_URL", "AVALANCHE_MAINNET_RPC"]},
}
STABLES = {"USDC": Decimal("1"), "USDT": Decimal("1")}
@dataclass(frozen=True)
class Edge:
src: str
dst: str
ratio: Decimal
venue: str
path_label: str
price_detail: str
liquidity_note: str
def now() -> str:
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
def load_json(path: Path) -> dict:
return json.loads(path.read_text())
def write_json(path: Path, payload: dict) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(payload, indent=2) + "\n")
def write_text(path: Path, text: str) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(text.rstrip() + "\n")
def load_env_file(path: Path) -> dict[str, str]:
values: dict[str, str] = {}
if not path.exists():
return values
for raw_line in path.read_text().splitlines():
line = raw_line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
values[key.strip()] = value.strip().strip('"').strip("'")
return values
def merged_env_values() -> dict[str, str]:
values: dict[str, str] = {}
values.update(load_env_file(ROOT_ENV_PATH))
values.update(load_env_file(SMOM_ENV_PATH))
values.update(load_env_from_shell())
return values
def load_env_from_shell() -> dict[str, str]:
loader = ROOT / "smom-dbis-138" / "scripts" / "load-env.sh"
if not loader.exists():
return {}
proc = subprocess.run(
["bash", "-lc", f"source {loader} >/dev/null 2>&1 && env"],
text=True,
capture_output=True,
timeout=15,
check=False,
cwd=ROOT,
)
if proc.returncode != 0:
return {}
values: dict[str, str] = {}
for raw_line in proc.stdout.splitlines():
if "=" not in raw_line:
continue
key, value = raw_line.split("=", 1)
values[key.strip()] = value.strip()
return values
def resolve_env_value(key: str, env_values: dict[str, str], seen: set[str] | None = None) -> str:
if seen is None:
seen = set()
if key in seen:
return env_values.get(key, "")
seen.add(key)
value = env_values.get(key, "")
if value.startswith("${") and value.endswith("}"):
inner = value[2:-1]
target = inner.split(":-", 1)[0]
fallback = inner.split(":-", 1)[1] if ":-" in inner else ""
resolved = resolve_env_value(target, env_values, seen)
return resolved or fallback
return value.rstrip("\r\n")
def parse_uint(value: str) -> int:
cleaned = re.sub(r"\[[^\]]*\]", "", value)
matches = UINT_RE.findall(cleaned)
if matches:
return int(matches[0])
for line in value.splitlines():
token = line.strip().split(" ", 1)[0]
if token.isdigit():
return int(token)
raise ValueError(f"could not parse integer from {value!r}")
def parse_uints(value: str, count: int) -> list[int]:
cleaned = re.sub(r"\[[^\]]*\]", "", value)
matches = [int(match) for match in UINT_RE.findall(cleaned)]
if len(matches) >= count:
return matches[:count]
matches = []
for line in value.splitlines():
token = line.strip().split(" ", 1)[0]
if token.isdigit():
matches.append(int(token))
if len(matches) < count:
raise ValueError(f"expected {count} integers, got {value!r}")
return matches[:count]
def cast_call(rpc_url: str, target: str, signature: str, *args: str) -> str:
cmd = ["cast", "call", target, signature, *args, "--rpc-url", rpc_url]
proc = subprocess.run(cmd, text=True, capture_output=True, timeout=3, check=False)
if proc.returncode != 0:
stderr = proc.stderr.strip() or proc.stdout.strip() or "cast call failed"
raise RuntimeError(stderr)
return proc.stdout.strip()
def safe_decimal(value: str | int | float | Decimal | None) -> Decimal | None:
if value is None:
return None
try:
return Decimal(str(value))
except (InvalidOperation, ValueError):
return None
def format_decimal(value: Decimal | None, places: int = 8) -> str:
if value is None:
return "not found"
quant = Decimal(10) ** -places
try:
rounded = value.quantize(quant)
except InvalidOperation:
return str(value)
return format(rounded, "f")
def normalize_18(raw: int) -> Decimal:
return Decimal(raw) / (Decimal(10) ** 18)
def rpc_for_chain(chain_id: str, env_values: dict[str, str]) -> str:
if chain_id == "1":
infura_project_id = resolve_env_value("INFURA_PROJECT_ID", env_values)
if infura_project_id:
return f"https://mainnet.infura.io/v3/{infura_project_id}"
config = CHAIN_CONFIG.get(chain_id, {})
for key in config.get("rpc_keys", []):
value = resolve_env_value(key, env_values)
if value:
return value
return ""
def build_uniswap_edges(entry: dict) -> list[Edge]:
edges: list[Edge] = []
for row in entry.get("pairsChecked") or []:
if not row.get("live"):
continue
health = row.get("health") or {}
price = safe_decimal(health.get("priceQuotePerBase"))
if price is None or price <= 0:
continue
base = row["base"]
quote = row["quote"]
pair = f"{base}/{quote}"
addr = row.get("poolAddress") or ""
reserves = f"base={health.get('baseReserveUnits', '?')}, quote={health.get('quoteReserveUnits', '?')}"
liquidity_note = (
f"Uniswap V2 pair {addr}; healthy={health.get('healthy')}; "
f"depthOk={health.get('depthOk')}; parityOk={health.get('parityOk')}; {reserves}"
)
edges.append(
Edge(
src=base,
dst=quote,
ratio=price,
venue="uniswap_v2",
path_label=pair,
price_detail=f"reserve ratio from {pair}",
liquidity_note=liquidity_note,
)
)
edges.append(
Edge(
src=quote,
dst=base,
ratio=Decimal(1) / price,
venue="uniswap_v2",
path_label=pair,
price_detail=f"inverse reserve ratio from {pair}",
liquidity_note=liquidity_note,
)
)
return edges
def build_pmm_edges(chain: dict, rpc_url: str) -> tuple[list[Edge], list[dict]]:
edges: list[Edge] = []
snapshots: list[dict] = []
if not rpc_url:
return edges, snapshots
for row in chain.get("pmmPools") or []:
pool = row.get("poolAddress") or ""
base = row.get("base")
quote = row.get("quote")
if not pool or pool.lower() == ZERO_ADDRESS or not base or not quote:
continue
try:
mid_price = normalize_18(parse_uint(cast_call(rpc_url, pool, "getMidPrice()(uint256)")))
except Exception as exc:
snapshots.append(
{
"base": base,
"quote": quote,
"poolAddress": pool,
"venue": row.get("venue", "dodo_pmm"),
"error": str(exc),
}
)
continue
if mid_price <= 0:
continue
pair = f"{base}/{quote}"
liquidity_note = f"DODO PMM {pool}; midPrice={mid_price}"
edges.append(
Edge(
src=base,
dst=quote,
ratio=mid_price,
venue="dodo_pmm",
path_label=pair,
price_detail=f"PMM mid price from {pair}",
liquidity_note=liquidity_note,
)
)
edges.append(
Edge(
src=quote,
dst=base,
ratio=Decimal(1) / mid_price,
venue="dodo_pmm",
path_label=pair,
price_detail=f"inverse PMM mid price from {pair}",
liquidity_note=liquidity_note,
)
)
snapshots.append(
{
"base": base,
"quote": quote,
"poolAddress": pool,
"venue": row.get("venue", "dodo_pmm"),
"midPrice": str(mid_price),
}
)
return edges, snapshots
def best_prices_for_chain(chain: dict, edges: list[Edge]) -> dict[str, dict]:
adjacency: dict[str, list[Edge]] = {}
for edge in edges:
adjacency.setdefault(edge.src, []).append(edge)
best: dict[str, dict] = {}
queue: deque[tuple[str, Decimal, list[str], list[str], list[str], int]] = deque()
for stable, price in STABLES.items():
best[stable] = {
"price": price,
"steps": [],
"venues": [],
"notes": [f"{stable} anchored at 1 USD"],
"hops": 0,
}
queue.append((stable, price, [], [], [f"{stable} anchored at 1 USD"], 0))
while queue:
token, usd_price, steps, venues, notes, hops = queue.popleft()
for edge in adjacency.get(token, []):
next_price = usd_price / edge.ratio
next_steps = steps + [edge.path_label]
next_venues = venues + [edge.venue]
next_notes = notes + [edge.liquidity_note]
next_hops = hops + 1
current = best.get(edge.dst)
should_replace = current is None or next_hops < current["hops"]
if not should_replace and current is not None and next_hops == current["hops"]:
current_venue_score = 0 if "dodo_pmm" in current["venues"] else 1
next_venue_score = 0 if "dodo_pmm" in next_venues else 1
should_replace = next_venue_score < current_venue_score
if should_replace:
best[edge.dst] = {
"price": next_price,
"steps": next_steps,
"venues": next_venues,
"notes": next_notes,
"hops": next_hops,
}
queue.append((edge.dst, next_price, next_steps, next_venues, next_notes, next_hops))
out: dict[str, dict] = {}
for symbol in sorted((chain.get("cwTokens") or {}).keys()):
resolution = best.get(symbol)
if resolution is None:
out[symbol] = {
"priceUsd": None,
"derivedFrom": "not found",
"sourceType": "not_found",
"notes": ["No live direct or bridged price path was found from USDC/USDT anchors."],
}
continue
out[symbol] = {
"priceUsd": str(resolution["price"]),
"derivedFrom": " -> ".join(resolution["steps"]) if resolution["steps"] else "stable anchor",
"sourceType": resolution["venues"][0] if resolution["venues"] else "stable_anchor",
"notes": resolution["notes"],
}
return out
def build_report() -> dict:
env_values = merged_env_values()
deployment = load_json(DEPLOYMENT_STATUS)
discovery = load_json(UNISWAP_DISCOVERY)
discovery_by_chain = {str(entry["chain_id"]): entry for entry in discovery.get("entries") or []}
chains_out: list[dict] = []
for chain_id, chain in sorted((deployment.get("chains") or {}).items(), key=lambda item: int(item[0])):
if int(chain_id) == 138:
continue
rpc_url = rpc_for_chain(chain_id, env_values)
uniswap_edges = build_uniswap_edges(discovery_by_chain.get(chain_id, {}))
pmm_edges, pmm_snapshots = build_pmm_edges(chain, rpc_url)
price_rows = best_prices_for_chain(chain, uniswap_edges + pmm_edges)
chains_out.append(
{
"chainId": int(chain_id),
"network": chain.get("name", ""),
"activationState": chain.get("activationState", ""),
"rpcConfigured": bool(rpc_url),
"prices": price_rows,
"pmmSnapshots": pmm_snapshots,
}
)
return {
"generatedAt": now(),
"inputs": {
"deploymentStatus": str(DEPLOYMENT_STATUS),
"uniswapDiscovery": str(UNISWAP_DISCOVERY),
},
"chains": chains_out,
}
def render_markdown(payload: dict) -> str:
lines = [
"# cW Public Network Prices",
"",
f"- Generated: `{payload['generatedAt']}`",
f"- Deployment inventory: `{payload['inputs']['deploymentStatus']}`",
f"- Uniswap discovery snapshot: `{payload['inputs']['uniswapDiscovery']}`",
"- Price convention: USD per 1 token.",
"- `not found` means the generator could not reach the token from a live USDC/USDT anchor using the current public-pair snapshot plus live PMM mid-price reads.",
"",
"| Chain | Token | Price (USD) | Derived From | Source | Notes |",
"|---|---|---:|---|---|---|",
]
for chain in payload["chains"]:
first_row = True
prices = chain["prices"]
for symbol in sorted(prices.keys()):
row = prices[symbol]
chain_cell = f"`{chain['chainId']}` {chain['network']}" if first_row else ""
first_row = False
notes = "; ".join(row["notes"][:2])
lines.append(
f"| {chain_cell} | `{symbol}` | `{format_decimal(safe_decimal(row['priceUsd']))}` | "
f"`{row['derivedFrom']}` | `{row['sourceType']}` | {notes} |"
)
if prices:
lines.append(
f"| | | | | | Activation state: `{chain['activationState'] or 'active'}`; RPC configured: `{chain['rpcConfigured']}` |"
)
return "\n".join(lines)
def main() -> None:
payload = build_report()
write_json(JSON_OUT, payload)
write_text(DOC_OUT, render_markdown(payload))
print(JSON_OUT)
print(DOC_OUT)
if __name__ == "__main__":
main()