#!/usr/bin/env python3 from __future__ import annotations import argparse import json import os import re import subprocess import sys from datetime import datetime, timezone from decimal import Decimal, ROUND_CEILING, getcontext from pathlib import Path getcontext().prec = 42 ROOT = Path(__file__).resolve().parents[2] LATEST_SNAPSHOT = ROOT / "reports" / "status" / "mainnet-cwusdc-usdc-preflight-latest.json" POLICY_PATH = ROOT / "config" / "extraction" / "mainnet-cwusdc-usdc-support-policy.json" ROOT_ENV_PATH = ROOT / ".env" SMOM_ENV_PATH = ROOT / "smom-dbis-138" / ".env" ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" DEFAULT_CWUSDC = "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a" DEFAULT_USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" SIX_DECIMALS = Decimal(10) ** 6 ADDRESS_RE = re.compile(r"0x[a-fA-F0-9]{40}") UINT_RE = re.compile(r"\b\d+\b") def load_json(path: Path) -> dict: return json.loads(path.read_text()) 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)) 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 = os.environ.get(key) or 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 normalize_units(raw: int, decimals: int = 6) -> Decimal: return Decimal(raw) / (Decimal(10) ** decimals) def units_to_raw(units: Decimal, decimals: int = 6) -> int: scale = Decimal(10) ** decimals return int((units * scale).to_integral_value(rounding=ROUND_CEILING)) def decimal_max(a: Decimal, b: Decimal) -> Decimal: return a if a >= b else b def parse_uint(value: str) -> int: matches = UINT_RE.findall(value) if not matches: raise ValueError(f"could not parse integer from {value!r}") return int(matches[0]) def parse_address(value: str) -> str: match = ADDRESS_RE.search(value) if not match: raise ValueError(f"could not parse address from {value!r}") return match.group(0) def cast_call(rpc_url: str, target: str, signature: str, *args: str) -> str: cmd = ["cast", "call", target, signature, *args, "--rpc-url", rpc_url] return subprocess.check_output(cmd, text=True).strip() def query_balance(rpc_url: str, token: str, holder: str) -> int: return parse_uint(cast_call(rpc_url, token, "balanceOf(address)(uint256)", holder)) def derive_holder_from_private_key(env_values: dict[str, str]) -> str: private_key = resolve_env_value("PRIVATE_KEY", env_values) or resolve_env_value("KEEPER_PRIVATE_KEY", env_values) if not private_key or "${" in private_key: return "" output = subprocess.check_output(["cast", "wallet", "address", "--private-key", private_key], text=True).strip() return parse_address(output) def shell_quote(value: str) -> str: return "'" + value.replace("'", "'\"'\"'") + "'" def command_block(lines: list[str]) -> str: return "\n".join(lines) def funding_status(required_raw: int, available_raw: int, decimals: int = 6) -> dict: shortfall_raw = max(required_raw - available_raw, 0) return { "requiredRaw": str(required_raw), "requiredUnits": str(normalize_units(required_raw, decimals)), "availableRaw": str(available_raw), "availableUnits": str(normalize_units(available_raw, decimals)), "shortfallRaw": str(shortfall_raw), "shortfallUnits": str(normalize_units(shortfall_raw, decimals)), "covered": shortfall_raw == 0, } def build_plan(snapshot: dict, policy: dict, env_values: dict[str, str], holder_override: str) -> dict: rpc_url = resolve_env_value("ETHEREUM_MAINNET_RPC", env_values) if not rpc_url: raise RuntimeError("missing ETHEREUM_MAINNET_RPC") summary = snapshot["summary"] public_health = snapshot["health"]["publicPairHealth"] defended_health = snapshot["health"]["defendedVenueHealth"] treasury = snapshot.get("treasuryManager") or {} base_reserve_raw = int(summary["defendedBaseReserveRaw"]) quote_reserve_raw = int(summary["defendedQuoteReserveRaw"]) target_reserve_raw = max(base_reserve_raw, quote_reserve_raw) add_quote_raw = max(base_reserve_raw - quote_reserve_raw, 0) add_base_raw = max(quote_reserve_raw - base_reserve_raw, 0) min_base_units = Decimal(str(policy["thresholds"]["minBaseReserveUnits"])) min_quote_units = Decimal(str(policy["thresholds"]["minQuoteReserveUnits"])) public_base_units = Decimal(str(summary["publicPairBaseReserveUnits"])) public_quote_units = Decimal(str(summary["publicPairQuoteReserveUnits"])) public_base_shortfall_units = decimal_max(min_base_units - public_base_units, Decimal(0)) public_quote_shortfall_units = decimal_max(min_quote_units - public_quote_units, Decimal(0)) public_base_shortfall_raw = units_to_raw(public_base_shortfall_units) public_quote_shortfall_raw = units_to_raw(public_quote_shortfall_units) max_automated_raw = int(policy["managedCycle"]["maxAutomatedFlashQuoteAmountRaw"]) manager_available_raw = int(treasury.get("availableQuoteRaw") or 0) holder = holder_override or derive_holder_from_private_key(env_values) cwusdc = resolve_env_value("CWUSDC_MAINNET", env_values) or DEFAULT_CWUSDC usdc = resolve_env_value("USDC_MAINNET", env_values) or DEFAULT_USDC manager = snapshot["resolvedAddresses"].get("treasuryManager") or "" receiver = snapshot["resolvedAddresses"].get("receiver") or "" defended_pool = snapshot["health"]["defendedVenue"]["poolAddress"] public_pair = snapshot["health"]["publicPair"]["poolAddress"] integration = resolve_env_value("DODO_PMM_INTEGRATION_MAINNET", env_values) router = resolve_env_value("CHAIN_1_UNISWAP_V2_ROUTER", env_values) holder_state = None holder_usdc_raw = 0 holder_cwusdc_raw = 0 holder_blockers: list[str] = [] if holder and holder.lower() != ZERO_ADDRESS: try: holder_cwusdc_raw = query_balance(rpc_url, cwusdc, holder) holder_usdc_raw = query_balance(rpc_url, usdc, holder) holder_state = { "address": holder, "cwusdcBalanceRaw": str(holder_cwusdc_raw), "cwusdcBalanceUnits": str(normalize_units(holder_cwusdc_raw)), "usdcBalanceRaw": str(holder_usdc_raw), "usdcBalanceUnits": str(normalize_units(holder_usdc_raw)), } except Exception as exc: holder_blockers.append(f"holder balance query failed: {exc}") manager_funding = funding_status(max_automated_raw, manager_available_raw) defended_quote_funding = funding_status(add_quote_raw, holder_usdc_raw) public_base_funding = funding_status(public_base_shortfall_raw, holder_cwusdc_raw) public_quote_funding = funding_status(public_quote_shortfall_raw, holder_usdc_raw) blockers: list[str] = [] warnings = snapshot.get("warnings") or [] if add_base_raw > 0: blockers.append("defended pool needs base-side top-up logic; current plan only supports quote-side top-up for this rail") if add_quote_raw > 0 and holder_state and not defended_quote_funding["covered"]: blockers.append( "operator wallet does not hold enough USDC to restore defended pool reserve parity; external funding is required" ) if public_base_shortfall_raw > 0 and holder_state and not public_base_funding["covered"]: blockers.append( "operator wallet does not hold enough cWUSDC to reseed the public pair to policy floor; external mint/bridge is required" ) if public_quote_shortfall_raw > 0 and holder_state and not public_quote_funding["covered"]: blockers.append( "operator wallet does not hold enough USDC to reseed the public pair to policy floor" ) if manager_funding["covered"] is False and holder_state and holder_usdc_raw < max_automated_raw: blockers.append("operator wallet cannot fully fund even one max-sized automated defense cycle from current USDC balance") if not integration: blockers.append("missing DODO_PMM_INTEGRATION_MAINNET") if not router: blockers.append("missing CHAIN_1_UNISWAP_V2_ROUTER") if any("defended quote query failed" in warning for warning in warnings): blockers.append("defended pool quote preview reverted; set MIN_BASE_OUT_RAW manually before any quote-in trade") operator_commands = { "rerunPreflight": "bash scripts/verify/snapshot-mainnet-cwusdc-usdc-preflight.sh", "rerunPlan": "bash scripts/verify/plan-mainnet-cwusdc-usdc-repeg.sh", } if manager and manager.lower() != ZERO_ADDRESS: operator_commands["fundManagerUsdc"] = command_block( [ "source smom-dbis-138/scripts/load-env.sh >/dev/null", f"export USDC={usdc}", f"export MANAGER={manager}", f"export AMOUNT_RAW={max_automated_raw}", 'cast send "$USDC" \'transfer(address,uint256)(bool)\' "$MANAGER" "$AMOUNT_RAW" \\', ' --private-key "$PRIVATE_KEY" --rpc-url "$ETHEREUM_MAINNET_RPC"', ] ) if integration and add_quote_raw > 0: operator_commands["tradeDefendedPoolQuoteIn"] = command_block( [ "source smom-dbis-138/scripts/load-env.sh >/dev/null", f"export CWUSDC={cwusdc}", f"export USDC={usdc}", f"export INTEGRATION={integration}", f"export POOL={defended_pool}", f"export QUOTE_IN_RAW={add_quote_raw}", "export MIN_BASE_OUT_RAW=REPLACE_AFTER_DRY_RUN", 'cast send "$USDC" \'approve(address,uint256)(bool)\' "$INTEGRATION" "$QUOTE_IN_RAW" \\', ' --private-key "$PRIVATE_KEY" --rpc-url "$ETHEREUM_MAINNET_RPC"', 'cast send "$INTEGRATION" \'swapExactIn(address,address,uint256,uint256)\' "$POOL" "$USDC" "$QUOTE_IN_RAW" "$MIN_BASE_OUT_RAW" \\', ' --private-key "$PRIVATE_KEY" --rpc-url "$ETHEREUM_MAINNET_RPC"', ] ) if router and public_base_shortfall_raw > 0 and public_quote_shortfall_raw > 0: operator_commands["reseedPublicPair"] = command_block( [ "source smom-dbis-138/scripts/load-env.sh >/dev/null", f"export ROUTER={router}", f"export CWUSDC={cwusdc}", f"export USDC={usdc}", f"export BASE_AMOUNT_RAW={public_base_shortfall_raw}", f"export QUOTE_AMOUNT_RAW={public_quote_shortfall_raw}", 'export DEADLINE="$(( $(date +%s) + 3600 ))"', 'export SIGNER="$(cast wallet address --private-key "$PRIVATE_KEY")"', 'cast send "$CWUSDC" \'approve(address,uint256)(bool)\' "$ROUTER" "$BASE_AMOUNT_RAW" \\', ' --private-key "$PRIVATE_KEY" --rpc-url "$ETHEREUM_MAINNET_RPC"', 'cast send "$USDC" \'approve(address,uint256)(bool)\' "$ROUTER" "$QUOTE_AMOUNT_RAW" \\', ' --private-key "$PRIVATE_KEY" --rpc-url "$ETHEREUM_MAINNET_RPC"', 'cast send "$ROUTER" \'addLiquidity(address,address,uint256,uint256,uint256,uint256,address,uint256)\' \\', ' "$CWUSDC" "$USDC" "$BASE_AMOUNT_RAW" "$QUOTE_AMOUNT_RAW" "$BASE_AMOUNT_RAW" "$QUOTE_AMOUNT_RAW" "$SIGNER" "$DEADLINE" \\', ' --private-key "$PRIVATE_KEY" --rpc-url "$ETHEREUM_MAINNET_RPC"', ] ) recommended_actions = [ { "step": "fund_manager_for_one_max_cycle", "quoteAmountRaw": str(max_automated_raw), "quoteAmountUnits": str(normalize_units(max_automated_raw)), "status": "ready" if manager_funding["covered"] else "needs_usdc", }, { "step": "sell_usdc_into_defended_pool_toward_simple_1_to_1_reserve_parity", "baseAmountRaw": str(add_base_raw), "quoteAmountRaw": str(add_quote_raw), "quoteAmountUnits": str(normalize_units(add_quote_raw)), "status": "ready" if add_quote_raw == 0 or defended_quote_funding["covered"] else "needs_usdc", }, { "step": "reseed_public_pair_to_policy_floor", "baseAmountRaw": str(public_base_shortfall_raw), "baseAmountUnits": str(normalize_units(public_base_shortfall_raw)), "quoteAmountRaw": str(public_quote_shortfall_raw), "quoteAmountUnits": str(normalize_units(public_quote_shortfall_raw)), "status": ( "ready" if public_base_shortfall_raw == 0 or (public_base_funding["covered"] and public_quote_funding["covered"]) else "needs_inventory" ), }, ] return { "generatedAt": datetime.now(timezone.utc).isoformat(), "mode": "read_only_repeg_plan", "snapshotPath": str(LATEST_SNAPSHOT), "policyPath": str(POLICY_PATH), "inferenceNotes": [ "Defended-pool 1:1 sizing is inferred from equal 6-decimal matched-rail tokens and reserve-balance parity.", "DODO PMM mid-price can still differ from reserve ratio; rerun preflight after each funding action.", "Public-pair reseed target uses the current policy reserve floors, not a smaller cosmetic liquidity target.", ], "resolvedAddresses": { "holder": holder or None, "cwusdc": cwusdc, "usdc": usdc, "publicPair": public_pair, "defendedPool": defended_pool, "treasuryManager": manager or None, "receiver": receiver or None, "dodoIntegration": integration or None, "uniswapV2Router": router or None, }, "defendedVenue": { "midPrice": summary["defendedMidPrice"], "deviationBps": summary["defendedDeviationBps"], "baseReserveRaw": str(base_reserve_raw), "baseReserveUnits": str(normalize_units(base_reserve_raw)), "quoteReserveRaw": str(quote_reserve_raw), "quoteReserveUnits": str(normalize_units(quote_reserve_raw)), "simpleReserveParity": { "targetReservePerSideRaw": str(target_reserve_raw), "targetReservePerSideUnits": str(normalize_units(target_reserve_raw)), "addBaseRaw": str(add_base_raw), "addBaseUnits": str(normalize_units(add_base_raw)), "addQuoteRaw": str(add_quote_raw), "addQuoteUnits": str(normalize_units(add_quote_raw)), }, }, "publicLane": { "pairAddress": public_pair, "priceQuotePerBase": public_health["priceQuotePerBase"], "deviationBps": summary["publicPairDeviationBps"], "baseReserveUnits": str(public_base_units), "quoteReserveUnits": str(public_quote_units), "policyFloorBaseUnits": str(min_base_units), "policyFloorQuoteUnits": str(min_quote_units), "policyFloorBaseShortfallRaw": str(public_base_shortfall_raw), "policyFloorBaseShortfallUnits": str(normalize_units(public_base_shortfall_raw)), "policyFloorQuoteShortfallRaw": str(public_quote_shortfall_raw), "policyFloorQuoteShortfallUnits": str(normalize_units(public_quote_shortfall_raw)), }, "automation": { "managerAvailableQuoteRaw": str(manager_available_raw), "managerAvailableQuoteUnits": str(normalize_units(manager_available_raw)), "maxAutomatedFlashQuoteAmountRaw": str(max_automated_raw), "maxAutomatedFlashQuoteAmountUnits": str(normalize_units(max_automated_raw)), "managerFundingForOneMaxCycle": manager_funding, }, "holderState": holder_state, "holderFundingChecks": { "defendedQuoteTopUp": defended_quote_funding, "publicPairBaseTopUp": public_base_funding, "publicPairQuoteTopUp": public_quote_funding, }, "recommendedActions": recommended_actions, "operatorCommands": operator_commands, "warnings": warnings, "blockers": holder_blockers + blockers, "status": { "canFullyReachSimple1To1WithCurrentHolder": len(holder_blockers + blockers) == 0, "needsExternalFunding": ( not defended_quote_funding["covered"] or not public_base_funding["covered"] or not public_quote_funding["covered"] ), "canFundManagerFromCurrentHolder": holder_usdc_raw >= max_automated_raw if holder_state else None, }, } def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--snapshot", default=str(LATEST_SNAPSHOT), help="Path to a preflight snapshot JSON.") parser.add_argument("--holder", default="", help="Optional holder address to inventory-check instead of deriving from PRIVATE_KEY.") parser.add_argument("--out", help="Write the plan JSON to this file.") args = parser.parse_args() snapshot_path = Path(args.snapshot) if not snapshot_path.exists(): raise RuntimeError(f"missing snapshot file: {snapshot_path}") snapshot = load_json(snapshot_path) policy = load_json(POLICY_PATH) env_values = merged_env_values() plan = build_plan(snapshot, policy, env_values, args.holder.strip()) output = json.dumps(plan, indent=2) if args.out: out_path = Path(args.out) out_path.parent.mkdir(parents=True, exist_ok=True) out_path.write_text(output + "\n") print(output) return 0 if __name__ == "__main__": sys.exit(main())