#!/usr/bin/env python3 from pathlib import Path import json import time ROOT = Path(__file__).resolve().parents[2] POOL_MATRIX = ROOT / "cross-chain-pmm-lps" / "config" / "pool-matrix.json" DEPLOYMENT_STATUS = ROOT / "cross-chain-pmm-lps" / "config" / "deployment-status.json" POLICY = ROOT / "config" / "extraction" / "promod-uniswap-v2-liquidity-policy.json" REPORT = ROOT / "reports" / "extraction" / "promod-uniswap-v2-liquidity-program-latest.json" DOC = ROOT / "docs" / "03-deployment" / "PROMOD_UNISWAP_V2_LIQUIDITY_PROGRAM.md" def load(path: Path): return json.loads(path.read_text()) def write_json(path: Path, payload): path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(payload, indent=2) + "\n") def write_text(path: Path, text: str): path.parent.mkdir(parents=True, exist_ok=True) path.write_text(text.rstrip() + "\n") def now(): return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) def tier_for(chain_id: str, tiers: dict) -> str: for tier_name, chain_ids in tiers.items(): if chain_id in chain_ids: return tier_name return "unassigned" CORE_RAILS = {"cWUSDC", "cWUSDT", "cWAUSDT"} def build_additional_gru_pairs(cw_symbols: list[str]) -> list[str]: extras = [symbol for symbol in cw_symbols if symbol not in CORE_RAILS] pairs: list[str] = [] for symbol in extras: if "cWUSDC" in cw_symbols: pairs.append(f"{symbol}/cWUSDC") if "cWUSDT" in cw_symbols: pairs.append(f"{symbol}/cWUSDT") return pairs def main(): matrix = load(POOL_MATRIX) status = load(DEPLOYMENT_STATUS) policy = load(POLICY) entries = [] tier_counts = {} for chain_id in sorted(matrix["chains"].keys(), key=lambda c: int(c)): chain_matrix = matrix["chains"][chain_id] chain_status = status["chains"].get(chain_id) if not chain_status or not chain_status.get("bridgeAvailable"): continue cw_tokens = chain_status.get("cwTokens", {}) cw_symbols = sorted(cw_tokens.keys()) additional_gru_tokens = [symbol for symbol in cw_symbols if symbol not in CORE_RAILS] hub = chain_matrix.get("hubStable") pools_first = chain_matrix.get("poolsFirst", []) existing_pairs = sorted( { f"{pool.get('base')}/{pool.get('quote')}" for pool in (chain_status.get("pmmPools") or []) if pool.get("base") and pool.get("quote") } ) wrapped_pairs = [] if "cWAUSDT" in cw_tokens and "cWUSDC" in cw_tokens: wrapped_pairs.append("cWAUSDT/cWUSDC") if "cWAUSDT" in cw_tokens and "cWUSDT" in cw_tokens: wrapped_pairs.append("cWAUSDT/cWUSDT") if "cWUSDT" in cw_tokens and "cWUSDC" in cw_tokens: wrapped_pairs.append("cWUSDT/cWUSDC") additional_wrapped_pairs = build_additional_gru_pairs(cw_symbols) settlement_candidates = [] for symbol in ("cWUSDC", "cWUSDT", "cWAUSDT"): pair = f"{symbol}/{hub}" if symbol in cw_tokens and pair in pools_first: settlement_candidates.append(pair) blockers = [ "Uniswap V2 factory/router addresses are not documented in-repo for this chain; env-backed deployment or factory mapping is still required.", "New Uniswap V2 pools must be added to token-aggregation indexing and MCP/API visibility before promotion." ] if "cWAUSDT" not in cw_tokens: blockers.append("cWAUSDT is not currently documented on this chain, so wrapped-depth phase is limited to cWUSDT/cWUSDC.") if not settlement_candidates: blockers.append("No canonical settlement candidate from the current pool-matrix matched the documented cW token set.") tier = tier_for(chain_id, policy["priority_tiers"]) tier_counts[tier] = tier_counts.get(tier, 0) + 1 entries.append( { "chain_id": int(chain_id), "network": chain_matrix.get("name"), "tier": tier, "hub_stable": hub, "bridge_available": True, "documented_cw_tokens": cw_symbols, "additional_gru_v2_cw_tokens": additional_gru_tokens, "wrapped_depth_phase_pairs": wrapped_pairs, "additional_wrapped_depth_pairs": additional_wrapped_pairs, "settlement_phase_pairs": settlement_candidates, "existing_pmm_analogs": [ pair for pair in existing_pairs if pair in wrapped_pairs or pair in settlement_candidates or pair in additional_wrapped_pairs ], "uniswap_v2_deployment_status": "planned", "required_uniswap_v2_env_suffixes": policy["uniswap_v2_requirements"]["required_env_suffixes"], "blockers": blockers } ) payload = { "generated_at": now(), "program_name": policy["program_name"], "purpose": policy["purpose"], "operator_rule": policy["operator_rule"], "mainnet_funding_posture": policy["mainnet_funding_posture"], "wrapped_depth_phase": policy["wrapped_depth_phase"], "settlement_phase": policy["settlement_phase"], "priority_tiers": policy["priority_tiers"], "tier_counts": tier_counts, "registry_sources": [ "cross-chain-pmm-lps/config/pool-matrix.json", "cross-chain-pmm-lps/config/deployment-status.json", "config/extraction/promod-uniswap-v2-liquidity-policy.json" ], "entries": entries } write_json(REPORT, payload) lines = [ "# Mr. Promod Uniswap V2 Liquidity Program", "", f"- Generated: `{payload['generated_at']}`", f"- Program: {payload['program_name']}", "- Strict note: this is a repo-native desired-state rollout package for Uniswap V2 or Uniswap-V2-compatible pools. It does not claim live deployment unless factory/router addresses and pool addresses are recorded.", f"- Mainnet funding posture: `{payload['mainnet_funding_posture']['mode']}` via `{', '.join(payload['mainnet_funding_posture']['required_deployer_assets'])}`", "", "## Deployment Model", "", "- Phase 1: build wrapped-depth support first when canonical USDC or USDT is scarce.", "- Phase 2: add or deepen canonical settlement rails once enough hub-stable inventory exists.", "- Promotion gate: do not treat any Uniswap V2 rail as live until its factory/router env, pool address, and indexer visibility are all recorded.", "", "## Wrapped-Depth Policy", "", f"- Preferred wrapped pairs: `{', '.join(payload['wrapped_depth_phase']['preferred_pairs_in_order'])}`", f"- Allocation split: flagship `{payload['wrapped_depth_phase']['allocation_share_pct']['flagship_pair']}%`, second `{payload['wrapped_depth_phase']['allocation_share_pct']['second_pair']}%`, third `{payload['wrapped_depth_phase']['allocation_share_pct']['third_pair']}%`", "", "## Operator Matrix", "", "| Chain | Network | Tier | Hub Stable | Core Wrapped Pairs | Additional GRU v2 cW* Tokens | Next Wrapped Expansion Pairs | Settlement Phase Pairs | Existing PMM Analogs |", "|---|---|---|---|---|---|---|---|---|", ] for entry in entries: wrapped = ", ".join(f"`{pair}`" for pair in entry["wrapped_depth_phase_pairs"]) or "—" extra_tokens = ", ".join(f"`{token}`" for token in entry["additional_gru_v2_cw_tokens"]) or "—" extra_pairs = ", ".join(f"`{pair}`" for pair in entry["additional_wrapped_depth_pairs"][:8]) or "—" settlement = ", ".join(f"`{pair}`" for pair in entry["settlement_phase_pairs"]) or "—" analogs = ", ".join(f"`{pair}`" for pair in entry["existing_pmm_analogs"]) or "—" lines.append( f"| `{entry['chain_id']}` | {entry['network']} | `{entry['tier']}` | `{entry['hub_stable']}` | {wrapped} | {extra_tokens} | {extra_pairs} | {settlement} | {analogs} |" ) lines.extend( [ "", "## Readiness Rules", "", "- Use `cWAUSDT` as a wrapped-depth support asset, not as a full replacement for `USDC` or `USDT` settlement.", "- Keep `cWUSDT/cWUSDC` available everywhere both core wrapped rails are documented.", "- After the first rail is live, expand the other documented GRU v2 `cW*` assets against `cWUSDC` and `cWUSDT` before opening broad canonical settlement exits.", "- Prefer `cWUSDC/USDC`, `cWUSDT/USDT`, or `cWAUSDT/USDC|USDT` only after canonical hub inventory is deep enough to matter.", "- For every promoted chain, add the new pools to `deployment-status.json`, Uniswap V2 env config, and token-aggregation indexing.", ] ) write_text(DOC, "\n".join(lines)) print(REPORT) if __name__ == "__main__": main()