feat(cwusdc): propagation monitor hooks, CMC enrich, pnpm scripts, inventory refresh
- package.json: cwusdc:provider-checks:with-monitor, value-propagation, fetch/enrich CMC chain1 - run-cwusdc-value-propagation.sh: strip pnpm-forwarded -- for monitor args - enrich-cmc-chain1-report-with-live-pool-catalog.py: merge live pool catalog into CMC report - Harden external-tracker / propagation / provider-check scripts and CMC sanity checker - Docs: MetaMask matrix optional automation; CWUSDC non-manual tasks cross-links - Refresh drift + live_inventory collected_at from latest cluster poll - gitignore: rpc-502-diagnostics evidence dumps under verification-evidence Validated: bash scripts/verify/run-all-validation.sh --skip-genesis Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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. |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": {},
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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(
|
||||
{
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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())
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
11
scripts/verify/run-cwusdc-value-propagation.sh
Executable file
11
scripts/verify/run-cwusdc-value-propagation.sh
Executable file
@@ -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" "$@"
|
||||
Reference in New Issue
Block a user