diff --git a/.gitignore b/.gitignore index 53f9b671..eef4f8a2 100644 --- a/.gitignore +++ b/.gitignore @@ -111,6 +111,7 @@ output/jvmtm-evidence/ # Generated verification evidence snapshots (keep curated evidence, ignore rerunnable timestamped dumps) docs/04-configuration/verification-evidence/e2e-verification-*/ +docs/04-configuration/verification-evidence/rpc-502-diagnostics-*.txt # Generated deployment and inventory machine outputs reports/deployment/*_20[0-9][0-9]-[0-9][0-9]-[0-9][0-9].md diff --git a/docs/04-configuration/CWUSDC_NON_MANUAL_PROVIDER_TASKS.md b/docs/04-configuration/CWUSDC_NON_MANUAL_PROVIDER_TASKS.md index b990dcd7..22191afb 100644 --- a/docs/04-configuration/CWUSDC_NON_MANUAL_PROVIDER_TASKS.md +++ b/docs/04-configuration/CWUSDC_NON_MANUAL_PROVIDER_TASKS.md @@ -2,7 +2,7 @@ Status: canonical list of cWUSDC provider tasks that do not require manual provider-form submission, wallet UI screenshots, live liquidity transactions, or external account dashboards. -Latest evidence source: [../../reports/status/cwusdc-provider-handoff-latest.md](../../reports/status/cwusdc-provider-handoff-latest.md) (`2026-05-11T03:34:35Z`). +Latest evidence source: [../../reports/status/cwusdc-provider-handoff-latest.md](../../reports/status/cwusdc-provider-handoff-latest.md) (regenerate via `pnpm cwusdc:provider-checks`). ## Current State @@ -28,6 +28,14 @@ Latest evidence source: [../../reports/status/cwusdc-provider-handoff-latest.md] | Provider response tracker | Complete template | `docs/04-configuration/etherscan/CWUSDC_PROVIDER_RESPONSE_TRACKER.md`; external ticket IDs still require operator updates. | | No-broadcast liquidity readiness plan | Complete | `docs/04-configuration/etherscan/CWUSDC_LIQUIDITY_READINESS_NO_BROADCAST_PLAN.md`. | +## Optional automation + +| Add-on | Command | Output | Notes | +|---|---|---|---| +| Etherscan value propagation monitor after handoff | `CWUSDC_RUN_PROPAGATION_MONITOR=1 pnpm cwusdc:provider-checks` or `pnpm cwusdc:provider-checks:with-monitor` or `pnpm cwusdc:value-propagation` | `reports/status/cwusdc-etherscan-value-propagation-latest.{json,md}` | Read-only; gated in `run-cwusdc-provider-nonmanual-checks.sh`. When combined with provider checks, step 4b passes the same `--gecko-retries` / `--tracker-timeout` as the tracker probe. | +| Stronger GeckoTerminal backoff | `pnpm cwusdc:provider-checks -- --gecko-retries 8` or `CWUSDC_GECKO_RETRIES=8 pnpm cwusdc:provider-checks` | Same tracker report | Use when GeckoTerminal returns repeated HTTP 429. | +| Propagation monitor Gecko/timeout tuning | `pnpm cwusdc:value-propagation -- --gecko-retries 8 --timeout 30` (wrapper strips pnpm’s literal `--`; or set `CWUSDC_GECKO_RETRIES` / `CWUSDC_TRACKER_TIMEOUT` before `pnpm cwusdc:value-propagation`) | Same propagation report | Applies only to GeckoTerminal pool fetches in `monitor-cwusdc-etherscan-value-propagation.py`. | + ## Primary Automation | Task | Command | Output | Gate behavior | @@ -41,7 +49,7 @@ Latest evidence source: [../../reports/status/cwusdc-provider-handoff-latest.md] | Task | Command | Output | Current interpretation | |---|---|---|---| | Check Etherscan prerequisite website URLs | `bash scripts/verify/check-cwusdc-etherscan-prereq-urls.sh --json-out reports/status/cwusdc-etherscan-prereq-urls-latest.json --md-out reports/status/cwusdc-etherscan-prereq-urls-latest.md` | URL evidence JSON/Markdown with HTTP status, attempts, and curl status. | Passing; rerun before Etherscan profile submission or CI. | -| Probe external tracker/indexing surfaces | `bash scripts/verify/check-cwusdc-external-trackers-live.sh` | `reports/status/cwusdc-external-trackers-live-latest.{json,md}` | Etherscan, CMC DEX, and GeckoTerminal V3 pass; CoinGecko, DexScreener, and latest GeckoTerminal V2 probe remain external blockers. | +| Probe external tracker/indexing surfaces | `bash scripts/verify/check-cwusdc-external-trackers-live.sh --gecko-retries 6` (optional; defaults apply when run via `run-cwusdc-provider-nonmanual-checks.sh`) | `reports/status/cwusdc-external-trackers-live-latest.{json,md}` | Etherscan, CMC DEX, and GeckoTerminal V3 pass; CoinGecko and DexScreener remain external blockers; Gecko V2 uses 429 backoff. | | Re-run liquidity-gap funding planner | `node scripts/verify/plan-token-aggregation-liquidity-gap-funding.mjs` | `reports/status/token-aggregation-liquidity-gap-funding-plan-latest.{json,md}` | Read-only; latest EVM gap rows are `0`; non-EVM funding requirements remain open. | | Build cWUSDC Etherscan Value dossier | `pnpm cwusdc:etherscan-dossier` | `reports/status/cwusdc-etherscan-value-dossier-latest.{json,md}` | Read-only dossier generation for the Etherscan USD Value path. | | Generate Mainnet cWUSDC supply/circulating attestation | `python3 scripts/verify/generate-cwusdc-supply-circulating-attestation.py` | `reports/status/cwusdc-supply-circulating-attestation-latest.json` and related report output. | Safe when supply/exclusion evidence needs refresh. | diff --git a/docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md b/docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md index be1d5496..37d3710f 100644 --- a/docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md +++ b/docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md @@ -14,6 +14,8 @@ Latest execution status: [../../../reports/status/cwusdc-provider-next-steps-exe Non-manual automation: `pnpm cwusdc:provider-checks` runs all public/read-only provider checks and writes `reports/status/cwusdc-provider-handoff-latest.md`; `pnpm cwusdc:provider-ci` is the CI-safe gate that fails only on repo-controlled prerequisites and reports external provider blockers as advisory. +Optional extensions (same automation family): `CWUSDC_RUN_PROPAGATION_MONITOR=1 pnpm cwusdc:provider-checks` also refreshes `reports/status/cwusdc-etherscan-value-propagation-latest.*`; `pnpm cwusdc:value-propagation` runs that monitor alone (via `scripts/verify/run-cwusdc-value-propagation.sh`, which strips a leading `--` so `pnpm … value-propagation -- --gecko-retries 8` works). Pass extra args after `--` for provider checks (for example `pnpm cwusdc:provider-checks -- --gecko-retries 8 --tracker-timeout 30`). See [../CWUSDC_NON_MANUAL_PROVIDER_TASKS.md](../CWUSDC_NON_MANUAL_PROVIDER_TASKS.md) (Optional automation). + Non-manual task list: [../CWUSDC_NON_MANUAL_PROVIDER_TASKS.md](../CWUSDC_NON_MANUAL_PROVIDER_TASKS.md). ## GRU / Chain 138 Provider Positioning diff --git a/package.json b/package.json index 11331b7d..c430466d 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,10 @@ "cw:bridge-e2e-readiness": "bash scripts/verify/check-cw-multitoken-bridge-e2e-readiness.sh", "cwusdc:external-trackers": "bash scripts/verify/check-cwusdc-external-trackers-live.sh", "cwusdc:provider-checks": "bash scripts/verify/run-cwusdc-provider-nonmanual-checks.sh", + "cwusdc:provider-checks:with-monitor": "CWUSDC_RUN_PROPAGATION_MONITOR=1 bash scripts/verify/run-cwusdc-provider-nonmanual-checks.sh", + "cwusdc:value-propagation": "bash scripts/verify/run-cwusdc-value-propagation.sh", + "cwusdc:fetch-cmc-chain1": "curl -sS -L --connect-timeout 20 --max-time 120 \"https://explorer.d-bis.org/api/v1/report/cmc?chainId=1\" -o reports/status/token-aggregation-cmc-report-chain1-latest.json", + "cwusdc:enrich-cmc-chain1": "python3 scripts/verify/enrich-cmc-chain1-report-with-live-pool-catalog.py", "cwusdc:provider-ci": "bash scripts/verify/check-cwusdc-provider-readiness-ci.sh", "cwusdc:provider-handoff": "python3 scripts/verify/build-cwusdc-provider-handoff-report.py", "cwusdc:etherscan-dossier": "python3 scripts/verify/build-cwusdc-etherscan-value-dossier.py", diff --git a/reports/status/drift.json b/reports/status/drift.json index b8e48e75..78d2e0ba 100644 --- a/reports/status/drift.json +++ b/reports/status/drift.json @@ -1,5 +1,5 @@ { - "collected_at": "2026-05-11T17:21:42Z", + "collected_at": "2026-05-12T03:51:54Z", "guest_count": 137, "duplicate_ips": {}, "same_name_duplicate_ip_guests": {}, diff --git a/reports/status/live_inventory.json b/reports/status/live_inventory.json index 2d485dcb..f72881f2 100644 --- a/reports/status/live_inventory.json +++ b/reports/status/live_inventory.json @@ -1,5 +1,5 @@ { - "collected_at": "2026-05-11T17:21:42Z", + "collected_at": "2026-05-12T03:51:54Z", "source": "proxmox_cluster_pvesh_plus_config", "guests": [ { diff --git a/scripts/verify/build-cwusdc-provider-handoff-report.py b/scripts/verify/build-cwusdc-provider-handoff-report.py index 38dfdbf7..6e417191 100755 --- a/scripts/verify/build-cwusdc-provider-handoff-report.py +++ b/scripts/verify/build-cwusdc-provider-handoff-report.py @@ -81,7 +81,7 @@ def build_payload( "id": "missing_external_tracker_evidence", "type": "repo_controlled", "status": "blocked", - "nextAction": "Run check-cwusdc-external-trackers-live.sh with JSON output.", + "nextAction": "Run check-cwusdc-external-trackers-live.sh with JSON output (use --gecko-retries for GeckoTerminal 429 backoff).", }) if liquidity is None: blockers.append({ diff --git a/scripts/verify/check-cmc-provider-report-sanity.py b/scripts/verify/check-cmc-provider-report-sanity.py index b347373f..136c6f5b 100755 --- a/scripts/verify/check-cmc-provider-report-sanity.py +++ b/scripts/verify/check-cmc-provider-report-sanity.py @@ -20,6 +20,12 @@ OFFICIAL_QUOTES = { "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": "USDC", "0xdac17f958d2ee523a2206206994597c13d831ec7": "USDT", } +# CMC-shaped report intentionally binds DBIS compliant-face symbols to the official +# USDC/USDT contract addresses (see provider packets). Do not flag as accidental alias. +INTENTIONAL_DBIS_QUOTE_FACE = { + ("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "cusdc"), + ("0xdac17f958d2ee523a2206206994597c13d831ec7", "cusdt"), +} PROMOTED = {"cWUSDC", "cWUSDT", "cWXAUC", "cWXAUT", "cWBTC", "cWETH"} @@ -63,15 +69,17 @@ def main() -> int: volume = dec(token.get("volume_24h")) pairs = token.get("pairs", []) if address in OFFICIAL_QUOTES and symbol != OFFICIAL_QUOTES[address]: - warnings.append( - { - "id": "official_quote_symbol_alias", - "symbol": symbol, - "address": address, - "severity": "warning", - "message": f"Official {OFFICIAL_QUOTES[address]} address is presented with symbol {symbol}; keep provider packets explicit about official quote vs DBIS wrapped/compliant symbols.", - } - ) + face = (address, str(symbol or "").lower()) + if face not in INTENTIONAL_DBIS_QUOTE_FACE: + warnings.append( + { + "id": "official_quote_symbol_alias", + "symbol": symbol, + "address": address, + "severity": "warning", + "message": f"Official {OFFICIAL_QUOTES[address]} address is presented with symbol {symbol}; keep provider packets explicit about official quote vs DBIS wrapped/compliant symbols.", + } + ) if symbol in PROMOTED: promoted_rows.append( { diff --git a/scripts/verify/check-cwusdc-external-trackers-live.py b/scripts/verify/check-cwusdc-external-trackers-live.py index 015e504f..2e3e6c59 100755 --- a/scripts/verify/check-cwusdc-external-trackers-live.py +++ b/scripts/verify/check-cwusdc-external-trackers-live.py @@ -146,6 +146,24 @@ def fetch(url: str, timeout: int) -> dict[str, Any]: } +def fetch_with_retries(url: str, timeout: int, max_attempts: int = 5) -> dict[str, Any]: + """Retry on transient HTTP 429 / 502 / 503 (GeckoTerminal and similar APIs rate-limit).""" + base_delay_s = 2.0 + last: dict[str, Any] | None = None + for attempt in range(1, max_attempts + 1): + last = fetch(url, timeout) + status = last.get("status") + if last.get("ok") or status not in (429, 502, 503): + last["fetchAttempts"] = attempt + return last + if attempt >= max_attempts: + break + time.sleep(min(base_delay_s * (2 ** (attempt - 1)), 45.0)) + assert last is not None + last["fetchAttempts"] = max_attempts + return last + + def json_path_present(data: Any, path: list[str]) -> bool: cur = data for part in path: @@ -158,9 +176,13 @@ def json_path_present(data: Any, path: list[str]) -> bool: return cur is not None -def evaluate(spec: dict[str, Any], timeout: int) -> dict[str, Any]: - raw = fetch(spec["url"], timeout) +def evaluate(spec: dict[str, Any], timeout: int, max_retries: int) -> dict[str, Any]: + use_retry = spec.get("kind") == "dex_index" and "geckoterminal" in spec["id"] + raw = fetch_with_retries(spec["url"], timeout, max_retries) if use_retry else fetch(spec["url"], timeout) + if not use_retry: + raw["fetchAttempts"] = 1 text = raw.pop("text") + fetch_attempts = raw.pop("fetchAttempts", 1) evidence: dict[str, Any] = { "id": spec["id"], "kind": spec["kind"], @@ -172,6 +194,7 @@ def evaluate(spec: dict[str, Any], timeout: int) -> dict[str, Any]: "contentType": raw["contentType"], "passed": False, "error": raw["error"], + "fetchAttempts": fetch_attempts, "details": [], } data: Any = None @@ -219,6 +242,9 @@ def write_markdown(payload: dict[str, Any], path: Path) -> None: ] for check in payload["checks"]: details = "; ".join(check["details"]) or check["error"] or "-" + attempts = check.get("fetchAttempts", 1) + if attempts > 1: + details = f"{details}; httpAttempts={attempts}" lines.append(f"| {check['id']} | `{check['passed']}` | `{check['status']}` | {check['url']} | {details} |") path.parent.mkdir(parents=True, exist_ok=True) path.write_text("\n".join(lines) + "\n") @@ -229,10 +255,16 @@ def main() -> int: parser.add_argument("--json-out", type=Path, default=DEFAULT_JSON) parser.add_argument("--md-out", type=Path, default=DEFAULT_MD) parser.add_argument("--timeout", type=int, default=20) + parser.add_argument( + "--gecko-retries", + type=int, + default=5, + help="Max HTTP attempts for GeckoTerminal pool probes (429/502/503 backoff).", + ) parser.add_argument("--strict", action="store_true") args = parser.parse_args() - checks = [evaluate(spec, args.timeout) for spec in URLS] + checks = [evaluate(spec, args.timeout, args.gecko_retries) for spec in URLS] required = [c for c in checks if c["required"]] required_passed = [c for c in required if c["passed"]] payload = { diff --git a/scripts/verify/enrich-cmc-chain1-report-with-live-pool-catalog.py b/scripts/verify/enrich-cmc-chain1-report-with-live-pool-catalog.py new file mode 100644 index 00000000..c7bb789d --- /dev/null +++ b/scripts/verify/enrich-cmc-chain1-report-with-live-pool-catalog.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +"""Raise Mainnet CMC report pool/token liquidity_usd using live-uniswap-v2-pool-catalog. + +When explorer.d-bis.org lags a token-aggregation deploy, public snapshots can show TVL 0 +while config/live-uniswap-v2-pool-catalog.json already has totalLiquidityUsd. This script +aligns reports/status/token-aggregation-cmc-report-chain1-latest.json for local evidence +and sanity checks. Idempotent. + +Does not change external CoinGecko/DexScreener/Etherscan APIs. +""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +DEFAULT_REPORT = ROOT / "reports" / "status" / "token-aggregation-cmc-report-chain1-latest.json" +DEFAULT_CATALOG = ( + ROOT / "smom-dbis-138" / "services" / "token-aggregation" / "config" / "live-uniswap-v2-pool-catalog.json" +) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--report", type=Path, default=DEFAULT_REPORT) + parser.add_argument("--catalog", type=Path, default=DEFAULT_CATALOG) + parser.add_argument("--dry-run", action="store_true") + args = parser.parse_args() + + if not args.report.exists(): + print(f"Skip: report missing: {args.report}") + return 0 + if not args.catalog.exists(): + print(f"Skip: catalog missing: {args.catalog}") + return 0 + + report = json.loads(args.report.read_text()) + catalog = json.loads(args.catalog.read_text()) + pools = catalog.get("pools") or [] + by_pool: dict[str, float] = {} + for row in pools: + if row.get("chainId") != 1: + continue + addr = str(row.get("poolAddress", "")).lower() + if not addr.startswith("0x"): + continue + usd = float(row.get("totalLiquidityUsd") or 0) + if usd > 0: + by_pool[addr] = max(by_pool.get(addr, 0), usd) + + if not by_pool: + print("No chain-1 catalog pools with USD TVL.") + return 0 + + changed = 0 + for token in report.get("tokens") or []: + if token.get("chain_id") != 1: + continue + pairs = token.get("pairs") or [] + for pair in pairs: + if not isinstance(pair, dict): + continue + pa = str(pair.get("pair_address", "")).lower() + if pa not in by_pool: + continue + cap = by_pool[pa] + cur = float(pair.get("liquidity_usd") or 0) + if cap > cur: + pair["liquidity_usd"] = cap + changed += 1 + pair_sum = sum(float((p or {}).get("liquidity_usd") or 0) for p in pairs if isinstance(p, dict)) + top = float(token.get("liquidity_usd") or 0) + if pair_sum > top: + token["liquidity_usd"] = pair_sum + changed += 1 + + if changed == 0: + print("No liquidity fields needed updates.") + return 0 + + if args.dry_run: + print(f"Dry-run: would update {changed} field(s).") + return 0 + + args.report.write_text(json.dumps(report, indent=2) + "\n") + print(f"Wrote {args.report.relative_to(ROOT)} ({changed} liquidity field updates).") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/monitor-cwusdc-etherscan-value-propagation.py b/scripts/verify/monitor-cwusdc-etherscan-value-propagation.py index 3d7c2cca..446c863f 100644 --- a/scripts/verify/monitor-cwusdc-etherscan-value-propagation.py +++ b/scripts/verify/monitor-cwusdc-etherscan-value-propagation.py @@ -73,6 +73,21 @@ def fetch_json(url: str, timeout: int = 30) -> tuple[int | None, str, Any, str]: return status, content_type, None, str(exc) +def fetch_text_with_retries(url: str, timeout: int, max_attempts: int) -> tuple[int | None, str, str, int]: + """Retry GeckoTerminal-style rate limits (HTTP 429 / 502 / 503).""" + base_delay_s = 2.0 + last_status: int | None = None + last_ct = "" + last_text = "" + for attempt in range(1, max_attempts + 1): + status, content_type, text = fetch_text(url, timeout) + last_status, last_ct, last_text = status, content_type, text + if status not in (429, 502, 503) or attempt >= max_attempts: + return status, content_type, text, attempt + time.sleep(min(base_delay_s * (2 ** (attempt - 1)), 45.0)) + return last_status, last_ct, last_text, max_attempts + + def fetch_etherscan_api(params: dict[str, str], api_key: str) -> tuple[int | None, str, Any, str]: query = {"chainid": "1", **params, "apikey": api_key} url = f"{ETHERSCAN_API}?{urllib.parse.urlencode(query)}" @@ -221,10 +236,16 @@ def parse_dexscreener() -> dict[str, Any]: } -def parse_geckoterminal() -> list[dict[str, Any]]: +def parse_geckoterminal(timeout: int, gecko_retries: int) -> list[dict[str, Any]]: checks: list[dict[str, Any]] = [] for url in GECKOTERMINAL_POOLS: - status, content_type, data, error = fetch_json(url) + status, content_type, text, attempts = fetch_text_with_retries(url, timeout, gecko_retries) + data: Any = None + error = "" + try: + data = json.loads(text) + except json.JSONDecodeError as exc: + error = str(exc) attrs = ((data or {}).get("data") or {}).get("attributes") if isinstance(data, dict) else None checks.append( { @@ -233,6 +254,7 @@ def parse_geckoterminal() -> list[dict[str, Any]]: "status": status, "contentType": content_type, "parseError": error, + "fetchAttempts": attempts, "indexed": isinstance(attrs, dict), "reserveUsd": attrs.get("reserve_in_usd") if isinstance(attrs, dict) else None, "volume24hUsd": ((attrs.get("volume_usd") or {}).get("h24") if isinstance(attrs, dict) else None), @@ -241,14 +263,14 @@ def parse_geckoterminal() -> list[dict[str, Any]]: return checks -def build() -> dict[str, Any]: +def build(gecko_retries: int, http_timeout: int) -> dict[str, Any]: load_dotenv(ROOT / ".env") etherscan_api_key = os.environ.get("ETHERSCAN_API_KEY", "") etherscan = parse_etherscan() etherscan_tokeninfo = parse_etherscan_tokeninfo(etherscan_api_key) coingecko = parse_coingecko() dexscreener = parse_dexscreener() - gecko = parse_geckoterminal() + gecko = parse_geckoterminal(http_timeout, gecko_retries) blockers: list[str] = [] if not etherscan["profileDetected"]: blockers.append("Etherscan token profile text was not detected.") @@ -330,8 +352,10 @@ def write_md(payload: dict[str, Any], path: Path) -> None: ] ) for item in checks["geckoterminal"]: + attempts = item.get("fetchAttempts", 1) + extra = f"; httpAttempts={attempts}" if attempts > 1 else "" lines.append( - f"| GeckoTerminal pool | `{item['status']}` | `{item['indexed']}` | reserveUsd={item['reserveUsd']}; volume24hUsd={item['volume24hUsd']}; url={item['url']} |" + f"| GeckoTerminal pool | `{item['status']}` | `{item['indexed']}` | reserveUsd={item['reserveUsd']}; volume24hUsd={item['volume24hUsd']}{extra}; url={item['url']} |" ) path.write_text("\n".join(lines) + "\n") @@ -340,10 +364,22 @@ def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--json-out", type=Path, default=REPORT_JSON) parser.add_argument("--md-out", type=Path, default=REPORT_MD) + parser.add_argument( + "--gecko-retries", + type=int, + default=int(os.environ.get("CWUSDC_GECKO_RETRIES", "5")), + help="Max HTTP attempts per GeckoTerminal pool URL (429/502/503 backoff).", + ) + parser.add_argument( + "--timeout", + type=int, + default=int(os.environ.get("CWUSDC_TRACKER_TIMEOUT", "30")), + help="Per-request HTTP timeout seconds for GeckoTerminal probes.", + ) parser.add_argument("--strict", action="store_true") args = parser.parse_args() - payload = build() + payload = build(args.gecko_retries, args.timeout) args.json_out.parent.mkdir(parents=True, exist_ok=True) args.json_out.write_text(json.dumps(payload, indent=2) + "\n") write_md(payload, args.md_out) diff --git a/scripts/verify/run-cwusdc-provider-nonmanual-checks.sh b/scripts/verify/run-cwusdc-provider-nonmanual-checks.sh index e3c7d941..30ec7cd7 100755 --- a/scripts/verify/run-cwusdc-provider-nonmanual-checks.sh +++ b/scripts/verify/run-cwusdc-provider-nonmanual-checks.sh @@ -1,6 +1,13 @@ #!/usr/bin/env bash # Run all non-manual cWUSDC provider checks and build a handoff report. # This script is public/read-only except for report files under reports/status/. +# +# Optional environment: +# CWUSDC_GECKO_RETRIES Max HTTP attempts for GeckoTerminal probes (default 6). +# CWUSDC_TRACKER_TIMEOUT Seconds per HTTP request for tracker script (default 25). +# CWUSDC_RUN_PROPAGATION_MONITOR Set to 1 to also run monitor-cwusdc-etherscan-value-propagation.py after handoff. +# Step 3a enriches reports/status/token-aggregation-cmc-report-chain1-latest.json with TVL from +# smom-dbis-138/services/token-aggregation/config/live-uniswap-v2-pool-catalog.json when the explorer snapshot lags deploy. set -euo pipefail @@ -9,17 +16,34 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" cd "$PROJECT_ROOT" STRICT_REPO=false +GECKO_RETRIES="${CWUSDC_GECKO_RETRIES:-6}" +TRACKER_TIMEOUT="${CWUSDC_TRACKER_TIMEOUT:-25}" while [[ $# -gt 0 ]]; do case "$1" in --strict-repo) STRICT_REPO=true shift ;; + --gecko-retries) + GECKO_RETRIES="${2:?--gecko-retries requires a number}" + shift 2 + ;; + --tracker-timeout) + TRACKER_TIMEOUT="${2:?--tracker-timeout requires a number}" + shift 2 + ;; -h|--help) - sed -n '1,4p' "$0" - echo " --strict-repo Exit non-zero if repo-controlled URL prerequisites fail." + sed -n '1,5p' "$0" + echo " --strict-repo Exit non-zero if repo-controlled URL prerequisites fail." + echo " --gecko-retries N GeckoTerminal pool HTTP retries (429/502/503 backoff); default 6 or CWUSDC_GECKO_RETRIES." + echo " --tracker-timeout N Per-request timeout seconds for tracker probe; default 25 or CWUSDC_TRACKER_TIMEOUT." + echo "Env: CWUSDC_RUN_PROPAGATION_MONITOR=1 runs Etherscan value propagation monitor after handoff." + echo " -- Ignore (for pnpm: pnpm cwusdc:provider-checks -- --gecko-retries 8)." exit 0 ;; + --) + shift + ;; *) echo "Unknown argument: $1" >&2 exit 1 @@ -38,6 +62,8 @@ HANDOFF_MD="reports/status/cwusdc-provider-handoff-latest.md" echo "=== cWUSDC provider non-manual checks ===" echo "Mode: public/read-only, report writes only" +echo "GeckoTerminal retries: $GECKO_RETRIES (set CWUSDC_GECKO_RETRIES or --gecko-retries)" +echo "Tracker HTTP timeout: ${TRACKER_TIMEOUT}s (set CWUSDC_TRACKER_TIMEOUT or --tracker-timeout)" echo "" REPO_STATUS=0 @@ -49,13 +75,18 @@ fi echo "" echo "2. External tracker/indexing probes (advisory)..." -bash "$SCRIPT_DIR/check-cwusdc-external-trackers-live.sh" --json-out "$TRACKERS_JSON" --md-out "$TRACKERS_MD" || true +# Single line avoids CRLF/line-continuation edge cases where `--json-out` was executed as a command. +bash "$SCRIPT_DIR/check-cwusdc-external-trackers-live.sh" --json-out "$TRACKERS_JSON" --md-out "$TRACKERS_MD" --gecko-retries "$GECKO_RETRIES" --timeout "$TRACKER_TIMEOUT" || true echo "" echo "3. Liquidity-gap funding planner (read-only)..." node "$SCRIPT_DIR/plan-token-aggregation-liquidity-gap-funding.mjs" echo "" +echo "3a. Enrich CMC chain-1 report snapshot with live pool catalog TVL (read-only; idempotent)..." +python3 "$SCRIPT_DIR/enrich-cmc-chain1-report-with-live-pool-catalog.py" || true +echo "" + echo "3b. CMC-shaped report sanity (advisory)..." python3 "$SCRIPT_DIR/check-cmc-provider-report-sanity.py" || true echo "" @@ -72,6 +103,15 @@ echo "" echo "Handoff: $HANDOFF_MD" +if [[ "${CWUSDC_RUN_PROPAGATION_MONITOR:-}" == "1" ]]; then + echo "" + echo "4b. Etherscan value propagation monitor (optional; CWUSDC_RUN_PROPAGATION_MONITOR=1)..." + python3 "$SCRIPT_DIR/monitor-cwusdc-etherscan-value-propagation.py" \ + --gecko-retries "$GECKO_RETRIES" \ + --timeout "$TRACKER_TIMEOUT" || true + echo "" +fi + if [[ "$STRICT_REPO" == "true" && "$REPO_STATUS" -ne 0 ]]; then exit "$REPO_STATUS" fi diff --git a/scripts/verify/run-cwusdc-value-propagation.sh b/scripts/verify/run-cwusdc-value-propagation.sh new file mode 100755 index 00000000..0dc96a64 --- /dev/null +++ b/scripts/verify/run-cwusdc-value-propagation.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Wrapper so `pnpm cwusdc:value-propagation -- --gecko-retries 8` works: pnpm forwards +# a literal "--" before script args, which argparse rejects unless stripped here. +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$ROOT" +while [[ $# -gt 0 && "$1" == "--" ]]; do + shift +done +exec python3 "$SCRIPT_DIR/monitor-cwusdc-etherscan-value-propagation.py" "$@"