Files
proxmox/scripts/verify/audit-cusdc-cwusdc-etherscan-feeds.py
defiQUG 4ebf2d7902
Some checks failed
Deploy to Phoenix / validate (push) Failing after 1s
Deploy to Phoenix / deploy (push) Has been skipped
Deploy to Phoenix / deploy-atomic-swap-dapp (push) Has been skipped
Deploy to Phoenix / cloudflare (push) Has been skipped
chore(repo): sync operator workspace (config, scripts, docs, multi-chain)
Add optional Cosmos/Engine-X/act-runner templates, CWUSDC/EI-matrix tooling,
non-EVM route planner in multi-chain-execution (tests passing), token list and
extraction updates, and documentation (MetaMask matrix, GRU/CWUSDC packets).

Ignore institutional evidence tarballs/sha256 under reports/status.

Validated with: bash scripts/verify/run-all-validation.sh --skip-genesis

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 16:25:08 -07:00

311 lines
13 KiB
Python
Executable File

#!/usr/bin/env python3
"""Audit Chain 138 cUSDC and Ethereum cWUSDC explorer feeds.
This produces an evidence packet for Etherscan/listing submissions. It does not
ask Etherscan to merge Chain 138 traffic into the Ethereum token tracker; rather,
it documents that Ethereum Mainnet cWUSDC is the wrapped public-network transport
representation of canonical Chain 138 cUSDC and summarizes both API feeds.
"""
from __future__ import annotations
import argparse
import datetime as dt
import json
import os
import sys
import urllib.parse
import urllib.request
from pathlib import Path
from typing import Any
CHAIN138_CUSDC = "0xf22258f57794CC8E06237084b353Ab30fFfa640b"
MAINNET_CWUSDC = "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a"
CHAIN138_EXPLORER_API = "https://explorer.d-bis.org/api/v2"
ETHERSCAN_V2_API = "https://api.etherscan.io/v2/api"
REPORT_BASE = Path("reports/status/cusdc-cwusdc-etherscan-feed-audit-latest")
def fetch_json(url: str, timeout: int = 30) -> Any:
req = urllib.request.Request(url, headers={"User-Agent": "dbis-cusdc-cwusdc-feed-audit/1.0"})
with urllib.request.urlopen(req, timeout=timeout) as response:
payload = response.read().decode("utf-8")
return json.loads(payload)
def human_units(raw: int, decimals: int) -> str:
sign = "-" if raw < 0 else ""
raw = abs(raw)
whole = raw // (10**decimals)
frac = str(raw % (10**decimals)).rjust(decimals, "0").rstrip("0")
return f"{sign}{whole:,}" + (f".{frac}" if frac else "")
def addresses_from_transfer(item: dict[str, Any], style: str) -> set[str]:
if style == "blockscout":
values = [
item.get("from", {}).get("hash"),
item.get("to", {}).get("hash"),
]
else:
values = [item.get("from"), item.get("to")]
return {str(v).lower() for v in values if v}
def summarize_blockscout_transfers(items: list[dict[str, Any]], decimals: int) -> dict[str, Any]:
total_raw = 0
addresses: set[str] = set()
methods: dict[str, int] = {}
latest = items[0] if items else None
for item in items:
value = item.get("total", {}).get("value", "0")
try:
total_raw += int(value)
except (TypeError, ValueError):
pass
addresses.update(addresses_from_transfer(item, "blockscout"))
method = item.get("method") or "unknown"
methods[method] = methods.get(method, 0) + 1
return {
"sample_count": len(items),
"sample_volume_raw": str(total_raw),
"sample_volume_units": human_units(total_raw, decimals),
"unique_addresses_in_sample": len(addresses),
"method_counts": methods,
"latest_transfer": {
"hash": latest.get("transaction_hash"),
"timestamp": latest.get("timestamp"),
"from": latest.get("from", {}).get("hash"),
"to": latest.get("to", {}).get("hash"),
"value_raw": latest.get("total", {}).get("value"),
"value_units": human_units(int(latest.get("total", {}).get("value", "0")), decimals),
"method": latest.get("method"),
}
if latest
else None,
"addresses": sorted(addresses),
}
def summarize_etherscan_transfers(items: list[dict[str, Any]], decimals: int) -> dict[str, Any]:
total_raw = 0
addresses: set[str] = set()
methods: dict[str, int] = {}
latest = items[0] if items else None
for item in items:
try:
total_raw += int(item.get("value", "0"))
except (TypeError, ValueError):
pass
addresses.update(addresses_from_transfer(item, "etherscan"))
method = item.get("methodId") or item.get("functionName") or "unknown"
methods[method] = methods.get(method, 0) + 1
return {
"sample_count": len(items),
"sample_volume_raw": str(total_raw),
"sample_volume_units": human_units(total_raw, decimals),
"unique_addresses_in_sample": len(addresses),
"method_counts": methods,
"latest_transfer": {
"hash": latest.get("hash"),
"blockNumber": latest.get("blockNumber"),
"timeStamp": latest.get("timeStamp"),
"from": latest.get("from"),
"to": latest.get("to"),
"value_raw": latest.get("value"),
"value_units": human_units(int(latest.get("value", "0")), decimals),
"methodId": latest.get("methodId"),
"functionName": latest.get("functionName"),
}
if latest
else None,
"addresses": sorted(addresses),
}
def blockscout_token_metadata(address: str) -> dict[str, Any]:
return fetch_json(f"{CHAIN138_EXPLORER_API}/tokens/{address}")
def blockscout_transfers(address: str, pages: int) -> list[dict[str, Any]]:
items: list[dict[str, Any]] = []
params: dict[str, Any] | None = None
for _ in range(pages):
url = f"{CHAIN138_EXPLORER_API}/tokens/{address}/transfers"
if params:
url += "?" + urllib.parse.urlencode(params)
payload = fetch_json(url)
items.extend(payload.get("items", []))
params = payload.get("next_page_params")
if not params:
break
return items
def etherscan_call(params: dict[str, str], api_key: str) -> Any:
query = {"chainid": "1", **params, "apikey": api_key}
payload = fetch_json(f"{ETHERSCAN_V2_API}?{urllib.parse.urlencode(query)}")
if payload.get("status") == "0" and "No transactions found" not in str(payload.get("message")):
raise RuntimeError(f"Etherscan API error: {payload.get('message')} {payload.get('result')}")
return payload.get("result", [])
def build_report(args: argparse.Namespace) -> dict[str, Any]:
api_key = args.etherscan_api_key or os.environ.get("ETHERSCAN_API_KEY", "")
if not api_key:
raise SystemExit("ETHERSCAN_API_KEY is required for Ethereum cWUSDC Etherscan API checks")
c138_meta = blockscout_token_metadata(args.chain138_cusdc)
c138_decimals = int(c138_meta.get("decimals") or 6)
c138_transfers = blockscout_transfers(args.chain138_cusdc, args.chain138_pages)
cw_supply_raw = etherscan_call(
{
"module": "stats",
"action": "tokensupply",
"contractaddress": args.mainnet_cwusdc,
},
api_key,
)
cw_transfers = etherscan_call(
{
"module": "account",
"action": "tokentx",
"contractaddress": args.mainnet_cwusdc,
"page": "1",
"offset": str(args.etherscan_offset),
"sort": "desc",
},
api_key,
)
if not isinstance(cw_transfers, list):
cw_transfers = []
c138_summary = summarize_blockscout_transfers(c138_transfers, c138_decimals)
cw_summary = summarize_etherscan_transfers(cw_transfers, 6)
common_addresses = sorted(set(c138_summary["addresses"]) & set(cw_summary["addresses"]))
c138_summary_public = {k: v for k, v in c138_summary.items() if k != "addresses"}
cw_summary_public = {k: v for k, v in cw_summary.items() if k != "addresses"}
return {
"generatedAt": dt.datetime.now(dt.UTC).isoformat().replace("+00:00", "Z"),
"purpose": "Evidence packet for Etherscan/listing feeds: Chain 138 cUSDC is the canonical source asset; Ethereum cWUSDC is the wrapped transport representation.",
"canonicalRelationship": {
"sourceChainId": 138,
"sourceToken": {
"symbol": "cUSDC",
"name": "USD Coin (Compliant)",
"address": args.chain138_cusdc,
"explorer": f"https://explorer.d-bis.org/token/{args.chain138_cusdc}",
"api": f"{CHAIN138_EXPLORER_API}/tokens/{args.chain138_cusdc}",
},
"wrappedChainId": 1,
"wrappedToken": {
"symbol": "cWUSDC",
"name": "Wrapped cUSDC",
"address": args.mainnet_cwusdc,
"explorer": f"https://etherscan.io/token/{args.mainnet_cwusdc}",
"api": ETHERSCAN_V2_API,
},
"mappingSource": "config/token-mapping-multichain.json: 138 cUSDC -> Ethereum Mainnet cWUSDC",
"trackerLanguage": "cWUSDC is the Ethereum Mainnet compliant wrapped transport representation of canonical Chain 138 cUSDC. It is not Circle-issued USDC.",
},
"chain138Cusdc": {
"metadata": {
"name": c138_meta.get("name"),
"symbol": c138_meta.get("symbol"),
"decimals": c138_meta.get("decimals"),
"holders": c138_meta.get("holders"),
"totalSupplyRaw": c138_meta.get("total_supply"),
"totalSupplyUnits": human_units(int(c138_meta.get("total_supply") or 0), c138_decimals),
},
"transferFeed": c138_summary_public,
},
"mainnetCwusdc": {
"metadata": {
"name": "Wrapped cUSDC",
"symbol": "cWUSDC",
"decimals": "6",
"totalSupplyRaw": str(cw_supply_raw),
"totalSupplyUnits": human_units(int(cw_supply_raw or 0), 6),
},
"transferFeed": cw_summary_public,
},
"crossFeedSignals": {
"commonAddressesInRecentSamples": common_addresses,
"commonAddressCount": len(common_addresses),
"interpretation": "Common addresses are supporting evidence only. Canonical linkage is established by the token mapping, metadata registry, and bridge/listing documentation; Etherscan itself will only index Ethereum Mainnet cWUSDC traffic for the token page.",
},
"etherscanSubmissionNote": "Ask Etherscan to list the Ethereum token as Wrapped cUSDC (cWUSDC), with Chain 138 cUSDC identified as the canonical source asset in the description/supporting links. Do not ask Etherscan to add Chain 138 transfer counts to the Ethereum token tracker totals.",
}
def write_markdown(report: dict[str, Any], path: Path) -> None:
rel = report["canonicalRelationship"]
c138 = report["chain138Cusdc"]
cw = report["mainnetCwusdc"]
signals = report["crossFeedSignals"]
lines = [
"# cUSDC / cWUSDC Etherscan Feed Audit",
"",
f"Generated: `{report['generatedAt']}`",
"",
"## Relationship",
"",
f"- Source asset: Chain 138 `cUSDC` at `{rel['sourceToken']['address']}`",
f"- Wrapped transport asset: Ethereum Mainnet `cWUSDC` at `{rel['wrappedToken']['address']}`",
f"- Mapping source: `{rel['mappingSource']}`",
f"- Tracker language: {rel['trackerLanguage']}",
"",
"## API Feed Summary",
"",
"| Feed | Supply | Recent sample transfers | Recent sample volume | Unique addresses in sample |",
"|---|---:|---:|---:|---:|",
f"| Chain 138 cUSDC Blockscout | {c138['metadata']['totalSupplyUnits']} | {c138['transferFeed']['sample_count']} | {c138['transferFeed']['sample_volume_units']} | {c138['transferFeed']['unique_addresses_in_sample']} |",
f"| Ethereum cWUSDC Etherscan | {cw['metadata']['totalSupplyUnits']} | {cw['transferFeed']['sample_count']} | {cw['transferFeed']['sample_volume_units']} | {cw['transferFeed']['unique_addresses_in_sample']} |",
"",
"## Latest Transfers",
"",
f"- Chain 138 cUSDC latest: `{(c138['transferFeed']['latest_transfer'] or {}).get('hash')}`",
f"- Ethereum cWUSDC latest: `{(cw['transferFeed']['latest_transfer'] or {}).get('hash')}`",
"",
"## Cross-Feed Signal",
"",
f"- Common addresses in recent API samples: `{signals['commonAddressCount']}`",
f"- Interpretation: {signals['interpretation']}",
"",
"## Etherscan Submission Note",
"",
report["etherscanSubmissionNote"],
"",
]
path.write_text("\n".join(lines), encoding="utf-8")
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--chain138-cusdc", default=CHAIN138_CUSDC)
parser.add_argument("--mainnet-cwusdc", default=MAINNET_CWUSDC)
parser.add_argument("--etherscan-api-key", default="")
parser.add_argument("--chain138-pages", type=int, default=3)
parser.add_argument("--etherscan-offset", type=int, default=150)
parser.add_argument("--json-out", default=f"{REPORT_BASE}.json")
parser.add_argument("--md-out", default=f"{REPORT_BASE}.md")
args = parser.parse_args()
report = build_report(args)
json_path = Path(args.json_out)
md_path = Path(args.md_out)
json_path.parent.mkdir(parents=True, exist_ok=True)
json_path.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
write_markdown(report, md_path)
print(f"Wrote {json_path}")
print(f"Wrote {md_path}")
return 0
if __name__ == "__main__":
sys.exit(main())