338 lines
15 KiB
Python
Executable File
338 lines
15 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Build a repo-backed status report for Solana, Tron, and XRPL lanes."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
SOLANA_LINEUP = ROOT / "config/solana-gru-bridge-lineup.json"
|
|
SNAPSHOT = ROOT / "reports/inventory/contract-inventory-onchain-snapshot.json"
|
|
COMPLETION_MATRIX = ROOT / "reports/inventory/deployed-contracts-completion-matrix.json"
|
|
HEALTH_JSON = ROOT / "reports/status/non-evm-network-health-latest.json"
|
|
OUT_JSON = ROOT / "reports/status/non-evm-lane-status-latest.json"
|
|
OUT_MD = ROOT / "reports/status/non-evm-lane-status-latest.md"
|
|
|
|
|
|
def load_json(path: Path) -> dict[str, Any]:
|
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
|
|
|
|
def find_snapshot_row(rows: list[dict[str, Any]], address: str) -> dict[str, Any]:
|
|
needle = address.lower()
|
|
for row in rows:
|
|
if str(row.get("address", "")).lower() == needle:
|
|
return row
|
|
raise KeyError(address)
|
|
|
|
|
|
def find_matrix_row(rows: list[dict[str, Any]], address: str) -> dict[str, Any]:
|
|
needle = address.lower()
|
|
for row in rows:
|
|
if str(row.get("address", "")).lower() == needle:
|
|
return row
|
|
raise KeyError(address)
|
|
|
|
|
|
def md_table(headers: list[str], rows: list[list[str]]) -> str:
|
|
out = ["| " + " | ".join(headers) + " |", "| " + " | ".join(["---"] * len(headers)) + " |"]
|
|
out.extend("| " + " | ".join(row) + " |" for row in rows)
|
|
return "\n".join(out)
|
|
|
|
|
|
def load_health() -> dict[str, Any] | None:
|
|
if not HEALTH_JSON.exists():
|
|
return None
|
|
return load_json(HEALTH_JSON)
|
|
|
|
|
|
def build_report() -> dict[str, Any]:
|
|
lineup = load_json(SOLANA_LINEUP)
|
|
snapshot = load_json(SNAPSHOT)
|
|
matrix = load_json(COMPLETION_MATRIX)
|
|
health = load_health()
|
|
|
|
snapshot_rows = snapshot.get("rows", [])
|
|
matrix_rows = matrix.get("rows", [])
|
|
solana_evidence = lineup["liveBridgeEvidence"]
|
|
solana_asset = next(
|
|
asset
|
|
for asset in lineup.get("assets", [])
|
|
if asset.get("chain138Symbol") == solana_evidence["chain138Symbol"]
|
|
)
|
|
|
|
inventory_targets = {
|
|
"tronAdapter": "0x28a94FB4bC415Ac3273211429338f768074CBEF6",
|
|
"xrplAdapter": "0x351f207F2DE66bF166ec730a0133613A10691439",
|
|
"mintBurnController": "0x44F79a3cec3fb829973d9b8d630839726D19E9C5",
|
|
"wXRP": "0xe8572f3ABD73Eff0A2e8AC5C88C2b6D180735f97",
|
|
}
|
|
|
|
inventory = {}
|
|
for key, address in inventory_targets.items():
|
|
snap_row = find_snapshot_row(snapshot_rows, address)
|
|
matrix_row = find_matrix_row(matrix_rows, address)
|
|
inventory[key] = {
|
|
"address": address,
|
|
"label": snap_row.get("labels_merged", ""),
|
|
"codeOnChain": snap_row.get("code_on_chain"),
|
|
"sourceBlockscout": snap_row.get("source_blockscout"),
|
|
"completionStatus": matrix_row.get("completion_status"),
|
|
"recommendedAction": matrix_row.get("recommended_action"),
|
|
}
|
|
|
|
generated_at = datetime.now(timezone.utc).replace(microsecond=0).isoformat()
|
|
return {
|
|
"generatedAt": generated_at,
|
|
"sources": {
|
|
"solanaLineup": str(SOLANA_LINEUP.relative_to(ROOT)),
|
|
"contractSnapshot": str(SNAPSHOT.relative_to(ROOT)),
|
|
"completionMatrix": str(COMPLETION_MATRIX.relative_to(ROOT)),
|
|
"healthReport": str(HEALTH_JSON.relative_to(ROOT)) if health else None,
|
|
},
|
|
"health": health,
|
|
"lanes": {
|
|
"solana": {
|
|
"classification": "documented_non_evm_gru_lane",
|
|
"networkHealth": next((c for c in (health or {}).get("checks", []) if c.get("network") == "Solana"), None),
|
|
"sourceAsset": {
|
|
"symbol": solana_evidence["chain138Symbol"],
|
|
"address": solana_evidence["chain138Address"],
|
|
},
|
|
"destinationAsset": {
|
|
"symbol": solana_evidence["solanaSymbol"],
|
|
"mint": solana_asset.get("solanaMint") or "pending",
|
|
},
|
|
"adapter": {
|
|
"address": solana_evidence["solanaAdapter"],
|
|
"contractPath": "smom-dbis-138/contracts/bridge/adapters/non-evm/SolanaAdapter.sol",
|
|
},
|
|
"evidence": {
|
|
"evidenceDate": solana_evidence["evidenceDate"],
|
|
"bridgeRequestId": solana_evidence["bridgeRequestId"],
|
|
"mintedRaw": solana_evidence["mintedRaw"],
|
|
"bridgedRaw": solana_evidence["bridgedRaw"],
|
|
"finalStatus": solana_evidence["finalStatus"],
|
|
"finalStatusLabel": solana_evidence["finalStatusLabel"],
|
|
"transactions": solana_evidence["transactions"],
|
|
},
|
|
"repoStatus": lineup["status"],
|
|
"gaps": [
|
|
"Solana SPL mint addresses are still null in config/solana-gru-bridge-lineup.json.",
|
|
"The repo records a confirmed cAUSDT -> cWAUSDT send/confirm, but broader mint inventory and production relay surface remain incomplete.",
|
|
],
|
|
},
|
|
"tron": {
|
|
"classification": "chain_138_adapter_only",
|
|
"networkHealth": next((c for c in (health or {}).get("checks", []) if c.get("network") == "Tron"), None),
|
|
"adapter": {
|
|
"address": inventory["tronAdapter"]["address"],
|
|
"contractPath": "smom-dbis-138/contracts/bridge/adapters/non-evm/TronAdapter.sol",
|
|
"completionStatus": inventory["tronAdapter"]["completionStatus"],
|
|
"sourceBlockscout": inventory["tronAdapter"]["sourceBlockscout"],
|
|
},
|
|
"flowSummary": [
|
|
"Chain 138 users initiate non-EVM sends through TronAdapter on Chain 138.",
|
|
"The adapter is an off-chain relay/oracle path, not a native Tron program inventory committed in this repo.",
|
|
"Repo closure work is currently explorer/source publication on Chain 138, not public Tron RPC reachability.",
|
|
],
|
|
"gaps": [
|
|
inventory["tronAdapter"]["recommendedAction"],
|
|
"No native Tron-side contract/program inventory is promoted in the unified repo-backed deployment status.",
|
|
],
|
|
},
|
|
"xrpl": {
|
|
"classification": "chain_138_adapter_plus_bridge_asset",
|
|
"networkHealth": next((c for c in (health or {}).get("checks", []) if c.get("network") == "XRPL"), None),
|
|
"adapter": {
|
|
"address": inventory["xrplAdapter"]["address"],
|
|
"contractPath": "smom-dbis-138/contracts/bridge/adapters/non-evm/XRPLAdapter.sol",
|
|
"completionStatus": inventory["xrplAdapter"]["completionStatus"],
|
|
"sourceBlockscout": inventory["xrplAdapter"]["sourceBlockscout"],
|
|
},
|
|
"wrappedAsset": {
|
|
"address": inventory["wXRP"]["address"],
|
|
"contractPath": "smom-dbis-138/contracts/bridge/interop/wXRP.sol",
|
|
"completionStatus": inventory["wXRP"]["completionStatus"],
|
|
},
|
|
"controller": {
|
|
"address": inventory["mintBurnController"]["address"],
|
|
"contractPath": "smom-dbis-138/contracts/bridge/interop/MintBurnController.sol",
|
|
"completionStatus": inventory["mintBurnController"]["completionStatus"],
|
|
},
|
|
"uiPath": "smom-dbis-138/frontend-dapp/src/components/bridge/XRPLBridgeForm.tsx",
|
|
"flowSummary": [
|
|
"XRPL sends are initiated on Chain 138 through XRPLAdapter with XRPL destination address and optional destination tag handling.",
|
|
"wXRP is the bridge-side ERC-20 representation of XRP locked on XRPL.",
|
|
"MintBurnController applies HSM-signed mint/burn authorization for the wXRP corridor.",
|
|
],
|
|
"gaps": [
|
|
f"XRPLAdapter: {inventory['xrplAdapter']['recommendedAction']}",
|
|
f"wXRP: {inventory['wXRP']['recommendedAction']}",
|
|
f"MintBurnController: {inventory['mintBurnController']['recommendedAction']}",
|
|
],
|
|
},
|
|
},
|
|
"auditConclusion": {
|
|
"summary": "Public network health for Solana, Tron, and XRPL is separate from Chain 138 explorer/source verification closure. The live endpoints can be healthy while the repo still reports NEEDS_BLOCKSCOUT_SOURCE_IMPORT for the corresponding Chain 138 adapter or bridge-asset contracts.",
|
|
"actionableDistinction": [
|
|
"Use reports/status/non-evm-network-health-latest.json for public endpoint reachability.",
|
|
"Use reports/inventory/deployed-contracts-completion-matrix.json for Chain 138 source-publication closure.",
|
|
"Use config/solana-gru-bridge-lineup.json as the source of truth for the current Solana AUSDT corridor evidence.",
|
|
],
|
|
},
|
|
}
|
|
|
|
|
|
def write_markdown(report: dict[str, Any]) -> None:
|
|
health = report.get("health") or {}
|
|
health_checks = {row["network"]: row for row in health.get("checks", [])}
|
|
lines = [
|
|
"# Non-EVM Lane Status",
|
|
"",
|
|
f"- Generated: `{report['generatedAt']}`",
|
|
f"- Solana lineup source: `{report['sources']['solanaLineup']}`",
|
|
f"- Contract snapshot source: `{report['sources']['contractSnapshot']}`",
|
|
f"- Completion matrix source: `{report['sources']['completionMatrix']}`",
|
|
]
|
|
if report["sources"]["healthReport"]:
|
|
lines.append(f"- Health source: `{report['sources']['healthReport']}`")
|
|
lines.append("")
|
|
|
|
if health_checks:
|
|
lines += [
|
|
"## Live Endpoint Health",
|
|
"",
|
|
md_table(
|
|
["Network", "Endpoint", "OK", "Key detail"],
|
|
[
|
|
[
|
|
"Solana",
|
|
"`https://api.mainnet-beta.solana.com`",
|
|
"yes" if health_checks["Solana"]["ok"] else "no",
|
|
f"health={health_checks['Solana'].get('health')}; slot={health_checks['Solana'].get('slot')}",
|
|
],
|
|
[
|
|
"Tron",
|
|
"`https://api.trongrid.io/jsonrpc`",
|
|
"yes" if health_checks["Tron"]["ok"] else "no",
|
|
f"chainId={health_checks['Tron'].get('chainId')}; block={health_checks['Tron'].get('blockNumber')}",
|
|
],
|
|
[
|
|
"XRPL",
|
|
"`https://s1.ripple.com:51234/`",
|
|
"yes" if health_checks["XRPL"]["ok"] else "no",
|
|
f"status={health_checks['XRPL'].get('status')}; validatedLedger={health_checks['XRPL'].get('validatedLedger')}",
|
|
],
|
|
],
|
|
),
|
|
"",
|
|
]
|
|
|
|
solana = report["lanes"]["solana"]
|
|
tron = report["lanes"]["tron"]
|
|
xrpl = report["lanes"]["xrpl"]
|
|
|
|
lines += [
|
|
"## Solana Lane",
|
|
"",
|
|
md_table(
|
|
["Field", "Value"],
|
|
[
|
|
["Classification", solana["classification"]],
|
|
["Source asset", f"`{solana['sourceAsset']['symbol']}` at `{solana['sourceAsset']['address']}`"],
|
|
["Destination asset", f"`{solana['destinationAsset']['symbol']}`; SPL mint `{solana['destinationAsset']['mint']}`"],
|
|
["Solana adapter", f"`{solana['adapter']['address']}`"],
|
|
["Evidence date", f"`{solana['evidence']['evidenceDate']}`"],
|
|
["Bridge request", f"`{solana['evidence']['bridgeRequestId']}`"],
|
|
["Final status", f"`{solana['evidence']['finalStatus']}` / `{solana['evidence']['finalStatusLabel']}`"],
|
|
["Mint / bridge / confirm txs", f"`{solana['evidence']['transactions']['mint']}`, `{solana['evidence']['transactions']['bridge']}`, `{solana['evidence']['transactions']['confirm']}`"],
|
|
["Repo status", f"`{solana['repoStatus']}`"],
|
|
],
|
|
),
|
|
"",
|
|
"Gaps:",
|
|
"",
|
|
*[f"- {item}" for item in solana["gaps"]],
|
|
"",
|
|
"## Tron Lane",
|
|
"",
|
|
md_table(
|
|
["Field", "Value"],
|
|
[
|
|
["Classification", tron["classification"]],
|
|
["Adapter", f"`{tron['adapter']['address']}`"],
|
|
["Contract path", f"`{tron['adapter']['contractPath']}`"],
|
|
["Completion status", f"`{tron['adapter']['completionStatus']}`"],
|
|
["Blockscout source", f"`{tron['adapter']['sourceBlockscout']}`"],
|
|
],
|
|
),
|
|
"",
|
|
"Flow summary:",
|
|
"",
|
|
*[f"- {item}" for item in tron["flowSummary"]],
|
|
"",
|
|
"Gaps:",
|
|
"",
|
|
*[f"- {item}" for item in tron["gaps"]],
|
|
"",
|
|
"## XRPL Lane",
|
|
"",
|
|
md_table(
|
|
["Field", "Value"],
|
|
[
|
|
["Classification", xrpl["classification"]],
|
|
["XRPL adapter", f"`{xrpl['adapter']['address']}`"],
|
|
["wXRP", f"`{xrpl['wrappedAsset']['address']}`"],
|
|
["MintBurnController", f"`{xrpl['controller']['address']}`"],
|
|
["XRPL UI", f"`{xrpl['uiPath']}`"],
|
|
["Adapter completion", f"`{xrpl['adapter']['completionStatus']}`"],
|
|
["wXRP completion", f"`{xrpl['wrappedAsset']['completionStatus']}`"],
|
|
["Controller completion", f"`{xrpl['controller']['completionStatus']}`"],
|
|
],
|
|
),
|
|
"",
|
|
"Flow summary:",
|
|
"",
|
|
*[f"- {item}" for item in xrpl["flowSummary"]],
|
|
"",
|
|
"Gaps:",
|
|
"",
|
|
*[f"- {item}" for item in xrpl["gaps"]],
|
|
"",
|
|
"## Audit Conclusion",
|
|
"",
|
|
f"- {report['auditConclusion']['summary']}",
|
|
*[f"- {item}" for item in report["auditConclusion"]["actionableDistinction"]],
|
|
"",
|
|
]
|
|
|
|
OUT_MD.write_text("\n".join(lines), encoding="utf-8")
|
|
|
|
|
|
def main() -> int:
|
|
if not SNAPSHOT.is_file() or not COMPLETION_MATRIX.is_file():
|
|
missing = [p.name for p in (SNAPSHOT, COMPLETION_MATRIX) if not p.is_file()]
|
|
print(
|
|
"SKIP non-evm lane status: missing "
|
|
+ ", ".join(missing)
|
|
+ ". Regenerate: python3 scripts/verify/build-deduped-onchain-inventory.py"
|
|
" && python3 scripts/verify/build-inventory-completion-matrix.py"
|
|
" (needs reports/inventory/DEPLOYED_CONTRACTS_UNIFIED_EXTENDED.md).",
|
|
file=sys.stderr,
|
|
)
|
|
return 0
|
|
report = build_report()
|
|
OUT_JSON.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
|
|
write_markdown(report)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|