Files
proxmox/scripts/lib/promod_uniswap_v2_phase1_funding_readiness.py
defiQUG 4fab998e51
All checks were successful
Deploy to Phoenix / deploy (push) Successful in 9s
chore: sync workspace docs, configs, and submodules
2026-04-18 12:07:15 -07:00

315 lines
12 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import json
import subprocess
import time
from decimal import Decimal, 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"
REPORT = ROOT / "reports" / "extraction" / "promod-uniswap-v2-phase1-funding-readiness-latest.json"
DOC = ROOT / "docs" / "03-deployment" / "PROMOD_UNISWAP_V2_PHASE1_FUNDING_READINESS.md"
TARGET_CHAINS = [1, 10, 25, 56, 100, 137, 8453, 42161, 42220, 43114]
RPC_ENV_KEYS = {
1: "ETHEREUM_MAINNET_RPC",
10: "OPTIMISM_MAINNET_RPC",
25: "CRONOS_RPC_URL",
56: "BSC_RPC_URL",
100: "GNOSIS_MAINNET_RPC",
137: "POLYGON_MAINNET_RPC",
8453: "BASE_MAINNET_RPC",
42161: "ARBITRUM_MAINNET_RPC",
42220: "CELO_MAINNET_RPC",
43114: "AVALANCHE_RPC_URL",
}
NETWORK_NAMES = {
1: "Ethereum Mainnet",
10: "Optimism",
25: "Cronos",
56: "BSC",
100: "Gnosis",
137: "Polygon",
8453: "Base",
42161: "Arbitrum One",
42220: "Celo",
43114: "Avalanche",
}
def now() -> str:
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
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 run(cmd: list[str], timeout: int = 8) -> tuple[int, str, str]:
proc = subprocess.run(cmd, text=True, capture_output=True, timeout=timeout)
return proc.returncode, proc.stdout.strip(), proc.stderr.strip()
def load_env() -> dict[str, str]:
script = (
"source smom-dbis-138/scripts/load-env.sh >/dev/null && "
"python3 - <<'PY'\n"
"import json, os\n"
"keys = [\n"
" 'PRIVATE_KEY',\n"
" 'ETHEREUM_MAINNET_RPC','OPTIMISM_MAINNET_RPC','CRONOS_RPC_URL','BSC_RPC_URL',\n"
" 'GNOSIS_MAINNET_RPC','POLYGON_MAINNET_RPC','BASE_MAINNET_RPC','ARBITRUM_MAINNET_RPC',\n"
" 'CELO_MAINNET_RPC','AVALANCHE_RPC_URL',\n"
" 'CHAIN_1_UNISWAP_V2_FACTORY','CHAIN_1_UNISWAP_V2_ROUTER',\n"
" 'CHAIN_10_UNISWAP_V2_FACTORY','CHAIN_10_UNISWAP_V2_ROUTER',\n"
" 'CHAIN_25_UNISWAP_V2_FACTORY','CHAIN_25_UNISWAP_V2_ROUTER',\n"
" 'CHAIN_56_UNISWAP_V2_FACTORY','CHAIN_56_UNISWAP_V2_ROUTER',\n"
" 'CHAIN_100_UNISWAP_V2_FACTORY','CHAIN_100_UNISWAP_V2_ROUTER',\n"
" 'CHAIN_137_UNISWAP_V2_FACTORY','CHAIN_137_UNISWAP_V2_ROUTER',\n"
" 'CHAIN_8453_UNISWAP_V2_FACTORY','CHAIN_8453_UNISWAP_V2_ROUTER',\n"
" 'CHAIN_42161_UNISWAP_V2_FACTORY','CHAIN_42161_UNISWAP_V2_ROUTER',\n"
" 'CHAIN_42220_UNISWAP_V2_FACTORY','CHAIN_42220_UNISWAP_V2_ROUTER',\n"
" 'CHAIN_43114_UNISWAP_V2_FACTORY','CHAIN_43114_UNISWAP_V2_ROUTER'\n"
"]\n"
"print(json.dumps({k: os.environ.get(k, '') for k in keys}))\n"
"PY"
)
rc, out, err = run(["bash", "-lc", script], timeout=10)
if rc != 0:
raise RuntimeError(f"failed to load env: {err or out}")
return json.loads(out)
def signer_address(private_key: str) -> str:
rc, out, err = run(["cast", "wallet", "address", "--private-key", private_key], timeout=10)
if rc != 0:
raise RuntimeError(f"failed to derive signer: {err or out}")
return out
def chain_entry(status: dict, chain_id: int) -> dict:
return status["chains"].get(str(chain_id)) or status["chains"].get(chain_id) or {}
def token_address(status: dict, chain_id: int, symbol: str) -> str:
value = chain_entry(status, chain_id).get("cwTokens", {}).get(symbol)
if isinstance(value, dict):
return value.get("address") or value.get("token") or ""
return value or ""
def token_decimals(rpc_url: str, token: str) -> int:
rc, out, _ = run(["cast", "call", token, "decimals()(uint8)", "--rpc-url", rpc_url], timeout=5)
return int(out) if rc == 0 and out else 18
def token_value_human(raw: str, decimals: int) -> str:
raw_int = int(raw.split()[0], 0)
return format(Decimal(raw_int) / (Decimal(10) ** decimals), "f")
def min_human(a: str, b: str) -> str:
da = Decimal(a)
db = Decimal(b)
return format(da if da <= db else db, "f")
def pair_reserves(rpc_url: str, pair: str) -> tuple[str, str]:
if pair in ("", "0x0000000000000000000000000000000000000000"):
return "0", "0"
rc, out, _ = run(["cast", "call", pair, "getReserves()(uint112,uint112,uint32)", "--rpc-url", rpc_url], timeout=5)
if rc != 0 or not out:
return "0", "0"
parts = out.split()
if len(parts) < 2:
return "0", "0"
return parts[0], parts[1]
def main() -> None:
env = load_env()
status = json.loads(DEPLOYMENT_STATUS.read_text())
signer = signer_address(env["PRIVATE_KEY"])
entries: list[dict] = []
completed = []
ready_now = []
needs_funding = []
for chain_id in TARGET_CHAINS:
rpc_key = RPC_ENV_KEYS[chain_id]
rpc_url = env.get(rpc_key, "")
factory = env.get(f"CHAIN_{chain_id}_UNISWAP_V2_FACTORY", "")
router = env.get(f"CHAIN_{chain_id}_UNISWAP_V2_ROUTER", "")
cwusdt = token_address(status, chain_id, "cWUSDT")
cwusdc = token_address(status, chain_id, "cWUSDC")
pair_rc, pair_out, _ = run(
["cast", "call", factory, "getPair(address,address)(address)", cwusdt, cwusdc, "--rpc-url", rpc_url],
timeout=5,
)
gas_rc, gas_out, _ = run(["cast", "balance", signer, "--ether", "--rpc-url", rpc_url], timeout=5)
cwusdt_decimals = token_decimals(rpc_url, cwusdt)
cwusdc_decimals = token_decimals(rpc_url, cwusdc)
b1_rc, b1_out, _ = run(
["cast", "call", cwusdt, "balanceOf(address)(uint256)", signer, "--rpc-url", rpc_url],
timeout=5,
)
b2_rc, b2_out, _ = run(
["cast", "call", cwusdc, "balanceOf(address)(uint256)", signer, "--rpc-url", rpc_url],
timeout=5,
)
a1_rc, a1_out, _ = run(
["cast", "call", cwusdt, "allowance(address,address)(uint256)", signer, router, "--rpc-url", rpc_url],
timeout=5,
)
a2_rc, a2_out, _ = run(
["cast", "call", cwusdc, "allowance(address,address)(uint256)", signer, router, "--rpc-url", rpc_url],
timeout=5,
)
cwusdt_raw = b1_out.split()[0] if b1_rc == 0 and b1_out else "0"
cwusdc_raw = b2_out.split()[0] if b2_rc == 0 and b2_out else "0"
cwusdt_human = token_value_human(cwusdt_raw, cwusdt_decimals)
cwusdc_human = token_value_human(cwusdc_raw, cwusdc_decimals)
max_seed = min_human(cwusdt_human, cwusdc_human)
gas_human = gas_out if gas_rc == 0 else "0"
pair_address = pair_out if pair_rc == 0 else ""
reserve0_raw, reserve1_raw = pair_reserves(rpc_url, pair_address)
pair_seeded_live = pair_address not in ("", "0x0000000000000000000000000000000000000000") and (
int(reserve0_raw, 0) > 0 or int(reserve1_raw, 0) > 0
)
has_both_sides = Decimal(cwusdt_human) > 0 and Decimal(cwusdc_human) > 0
has_gas = Decimal(gas_human) > Decimal("0.001")
funding_ready = has_both_sides and has_gas and pair_address == "0x0000000000000000000000000000000000000000"
entry = {
"chain_id": chain_id,
"network": NETWORK_NAMES[chain_id],
"phase_1_pair": "cWUSDT/cWUSDC",
"signer": signer,
"rpc_env_key": rpc_key,
"factory": factory,
"router": router,
"pair_address": pair_address,
"pair_exists": pair_address not in ("", "0x0000000000000000000000000000000000000000"),
"pair_seeded_live": pair_seeded_live,
"native_balance": gas_human,
"native_balance_ok_for_ops": has_gas,
"cwusdt": {
"address": cwusdt,
"decimals": cwusdt_decimals,
"balance_raw": cwusdt_raw,
"balance_human": cwusdt_human,
"allowance_raw": a1_out.split()[0] if a1_rc == 0 and a1_out else "0",
},
"cwusdc": {
"address": cwusdc,
"decimals": cwusdc_decimals,
"balance_raw": cwusdc_raw,
"balance_human": cwusdc_human,
"allowance_raw": a2_out.split()[0] if a2_rc == 0 and a2_out else "0",
},
"max_equal_seed_human": max_seed,
"reserve0_raw": reserve0_raw,
"reserve1_raw": reserve1_raw,
"execution_status": "completed" if pair_seeded_live else ("ready_now" if funding_ready else "needs_funding"),
"funding_ready_now": funding_ready,
"funding_blockers": [],
}
if not has_gas:
entry["funding_blockers"].append("native gas below 0.001")
if Decimal(cwusdt_human) <= 0:
entry["funding_blockers"].append("cWUSDT balance is zero")
if Decimal(cwusdc_human) <= 0:
entry["funding_blockers"].append("cWUSDC balance is zero")
if entry["pair_exists"] and not pair_seeded_live:
entry["funding_blockers"].append("pair exists but is not yet seeded")
if pair_seeded_live:
completed.append(chain_id)
elif funding_ready:
ready_now.append(chain_id)
else:
needs_funding.append(chain_id)
entries.append(entry)
payload = {
"generated_at": now(),
"program_name": "Mr. Promod Uniswap V2 phase 1 funding readiness",
"purpose": "Live deployer-wallet funding view for seeding cWUSDT/cWUSDC phase-1 pools chain by chain.",
"signer": signer,
"phase_1_pair": "cWUSDT/cWUSDC",
"completed_chain_ids": completed,
"ready_now_chain_ids": ready_now,
"needs_funding_chain_ids": needs_funding,
"entries": entries,
"source_artifacts": [
"cross-chain-pmm-lps/config/deployment-status.json",
"smom-dbis-138/.env",
"smom-dbis-138/scripts/load-env.sh",
],
}
write_json(REPORT, payload)
lines = [
"# Mr. Promod Uniswap V2 Phase 1 Funding Readiness",
"",
f"- Generated: `{payload['generated_at']}`",
f"- Signer: `{signer}`",
"- Purpose: live deployer-wallet funding view for seeding `cWUSDT/cWUSDC` phase-1 pools chain by chain.",
f"- Completed: {', '.join(f'`{cid}`' for cid in completed) if completed else 'none'}",
f"- Ready now: {', '.join(f'`{cid}`' for cid in ready_now) if ready_now else 'none'}",
f"- Needs funding: {', '.join(f'`{cid}`' for cid in needs_funding) if needs_funding else 'none'}",
"",
"| Chain | Network | Status | Pair Exists | Seeded Live | Native Gas | cWUSDT | cWUSDC | Max Equal Seed |",
"|---|---|---|---|---|---:|---:|---:|---:|",
]
for entry in entries:
lines.append(
f"| `{entry['chain_id']}` | {entry['network']} | "
f"`{entry['execution_status']}` | "
f"`{str(entry['pair_exists']).lower()}` | "
f"`{str(entry['pair_seeded_live']).lower()}` | "
f"`{entry['native_balance']}` | "
f"`{entry['cwusdt']['balance_human']}` | "
f"`{entry['cwusdc']['balance_human']}` | "
f"`{entry['max_equal_seed_human']}` |"
)
lines.extend(["", "## Blockers", ""])
for entry in entries:
lines.append(f"### Chain `{entry['chain_id']}` — {entry['network']}")
lines.append("")
lines.append(f"- execution status: `{entry['execution_status']}`")
lines.append(f"- pair seeded live: `{str(entry['pair_seeded_live']).lower()}`")
if entry["funding_blockers"]:
for blocker in entry["funding_blockers"]:
lines.append(f"- {blocker}")
else:
lines.append("- no funding blockers")
lines.append("")
write_text(DOC, "\n".join(lines))
print(REPORT)
if __name__ == "__main__":
main()