diff --git a/.cursor/rules/project-doc-and-deployment-refs.mdc b/.cursor/rules/project-doc-and-deployment-refs.mdc index f123d6a8..3a5fa70e 100644 --- a/.cursor/rules/project-doc-and-deployment-refs.mdc +++ b/.cursor/rules/project-doc-and-deployment-refs.mdc @@ -15,6 +15,14 @@ When answering token/PMM/deployment questions, prefer these docs over inferring **Operator commands:** `docs/00-meta/OPERATOR_READY_CHECKLIST.md` **Doc index:** `docs/MASTER_INDEX.md` +**Master reference — token / stablecoin launch (institutional compendium):** `docs/00-meta/BIBLE_FROM_NATHAN_TOKEN_LAUNCH_RESOURCE_COMPENDIUM.md` — regulation through aggregators and launch checklists; informational only. For Chain 138 token truth and Explorer alignment, still use `EXPLORER_TOKEN_LIST_CROSSCHECK.md` above. + +**Master reference — MetaMask Money/mUSD ↔ GRU, provider cross-links, DefiLlama DODO `dfio_meta_main` TVL:** `docs/00-meta/METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md` — replay steps, upstream **DefiLlama/DefiLlama-Adapters#19198**, touchpoints JSON, and **Part G** remaining tasks. + +**Gaps and inconsistencies (audit):** `docs/11-references/COSMOS_CHAIN138_GAPS_AND_INCONSISTENCIES.md` + +**Optional Cosmos / IBC to Chain 138 (streams A–E):** `docs/11-references/COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md` — templates: `config/cosmos-chain138-optional/README.md` + **Deployment phases (full coverage):** `docs/03-deployment/REMAINING_DEPLOYMENTS_FOR_FULL_NETWORK_COVERAGE.md` - A: mint + add liquidity (138) — `mint-for-liquidity.sh`, AddLiquidityPMMPoolsChain138 - B: Celo/Wemix CCIP + LINK (Wemix needs 0.4 WEMIX) diff --git a/.cursorindexingignore b/.cursorindexingignore index f1e0ff02..63cbba4a 100644 --- a/.cursorindexingignore +++ b/.cursorindexingignore @@ -1,5 +1,25 @@ # Exclude heavy paths from Cursor codebase indexing only (files stay editable). # Syntax: same as .gitignore — see https://cursor.com/docs/reference/ignore-file +# +# Aligns with pyrightconfig.json / .vscode settings to reduce IDE + Python LS load. +# See reports/status/pyright-and-ide-performance-investigation-2026-05-09.md + +ProxmoxVE/ +explorer-monorepo/ +MEV_Bot/ +miracles_in_motion/ +OMNIS/ +cross-chain-pmm-lps/ +tmp/ +.tmp/ +.codex-artifacts/ +.devin/ +.venv-checkjson/ +venv/ +smom-dbis-138/lib/ +smom-dbis-138/artifacts/ +smom-dbis-138/broadcast/ +smom-dbis-138/cache/ third-party/ vendor/ @@ -8,7 +28,6 @@ build/ dist/ out/ output/ -tmp/ reports/ cross-chain-pmm-lps-publish/ smom-dbis-138-publish/ diff --git a/.env.master.example b/.env.master.example index 75b53abf..0e8dcd54 100644 --- a/.env.master.example +++ b/.env.master.example @@ -119,6 +119,12 @@ RPC_URL_138_PUBLIC= INFURA_PROJECT_ID= INFURA_API_KEY= ETHEREUM_MAINNET_RPC= +# EI matrix readiness audit (scripts/verify/run-ei-matrix-full-readiness-audit.sh, optional CI step in run-all-validation.sh) +EI_MATRIX_AUDIT_SHARD_SIZE=400 +EI_MATRIX_AUDIT_WORKERS=3 +EI_MATRIX_AUDIT_MIN_MAINNET_RAW=12000000 +EI_MATRIX_AUDIT_MIN_138_RAW=0 +# CI: EI_MATRIX_ONCHAIN_AUDIT_CI=1 EI_MATRIX_ONCHAIN_AUDIT_CI_LIMIT=120 EI_MATRIX_AUDIT_MIN_MAINNET_RAW_CI=0 POLYGON_MAINNET_RPC= ARBITRUM_MAINNET_RPC= OPTIMISM_MAINNET_RPC= diff --git a/.gitignore b/.gitignore index 86fb4acc..53f9b671 100644 --- a/.gitignore +++ b/.gitignore @@ -181,3 +181,7 @@ reports/status/**/*.jsonl reports/status/ei-matrix-* reports/status/cw-multitoken-l2-remediation-*.jsonl reports/status/screenshots/ + +# Large evidence bundles / checksums under reports/status (retain outside git or use releases) +reports/status/**/*.tar.gz +reports/status/**/*.sha256 diff --git a/AGENTS.md b/AGENTS.md index 9c47b447..dc255465 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,7 +27,8 @@ Orchestration for Proxmox VE, Chain 138 (`smom-dbis-138/`), explorers, NPMplus, | Live vs template (read-only SSH) | `bash scripts/verify/audit-proxmox-operational-template.sh` | | Config validation | `bash scripts/validation/validate-config-files.sh` | | pnpm lockfile vs workspace (prevents `pnpm outdated` / importer bugs) | `bash scripts/verify/check-pnpm-workspace-lockfile.sh` — also run as **step 1b** in `run-all-validation.sh` | -| CI validation (no LAN) + cW* mesh matrix | `bash scripts/verify/run-all-validation.sh [--skip-genesis]` — same gate as **Gitea** push/PR: `run-all-validation` in `.gitea/workflows/deploy-to-phoenix.yml` (push) and `.gitea/workflows/validate-on-pr.yml` (PR only). After deploy, optional **Cloudflare** `cloudflare-sync` (Phoenix + `PHOENIX_REPO_ROOT`; set `PHOENIX_CLOUDFLARE_SYNC=1` on that host) via `scripts/deployment/gitea-cloudflare-sync.sh`. Steps: dependencies, **pnpm workspace/lockfile check**, config, cW* mesh (when pair-discovery exists), **`node cross-chain-pmm-lps/scripts/validate-deployment-status.cjs`**, optional genesis. Manual only: `bash scripts/verify/build-cw-mesh-deployment-matrix.sh [--json-out …]` | +| CI validation (no LAN) + cW* mesh matrix | `bash scripts/verify/run-all-validation.sh [--skip-genesis]` — same gate as **Gitea** push/PR: `run-all-validation` in `.gitea/workflows/deploy-to-phoenix.yml` (push) and `.gitea/workflows/validate-on-pr.yml` (PR only), **`runs-on: ubuntu-latest-heavy`** (CT **5700**). After deploy, optional **Cloudflare** `cloudflare-sync` (Phoenix + `PHOENIX_REPO_ROOT`; set `PHOENIX_CLOUDFLARE_SYNC=1` on that host) via `scripts/deployment/gitea-cloudflare-sync.sh`. Steps: dependencies, **pnpm workspace/lockfile check**, config, cW* mesh (when pair-discovery exists), **`node cross-chain-pmm-lps/scripts/validate-deployment-status.cjs`**, optional genesis. Manual only: `bash scripts/verify/build-cw-mesh-deployment-matrix.sh [--json-out …]` | +| Gitea Actions runners (heavy vs standard pool) | `docs/04-configuration/GITEA_ACT_RUNNER_SETUP.md` — **5700** `ubuntu-latest-heavy`, **5701** `ubuntu-latest`; apply configs `bash scripts/dev-vm/apply-act-runner-config.sh`; snapshot `bash scripts/dev-vm/act-runner-resource-snapshot.sh` | | FQDN / NPM E2E verifier | `bash scripts/verify/verify-end-to-end-routing.sh --profile=public` — inventory: `docs/04-configuration/E2E_ENDPOINTS_LIST.md`. Gitea Actions URLs (no API): `bash scripts/verify/print-gitea-actions-urls.sh` | | Submodule trees clean (CI / post-merge) | `bash scripts/verify/submodules-clean.sh` | | Submodule + explorer remotes | `docs/00-meta/SUBMODULE_HYGIENE.md` — `mcp-proxmox` uses **Gitea** `https://gitea.d-bis.org/d-bis/mcp-proxmox.git` (not the old GitHub-only URL). `cross-chain-pmm-lps-publish` is a **worktree** of `cross-chain-pmm-lps`, not a submodule. | diff --git a/config/cosmos-chain138-optional/README.md b/config/cosmos-chain138-optional/README.md new file mode 100644 index 00000000..36c67614 --- /dev/null +++ b/config/cosmos-chain138-optional/README.md @@ -0,0 +1,16 @@ +# Cosmos ↔ Chain 138 — optional config templates + +**Purpose:** Machine- and human-readable templates for [COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md](../../docs/11-references/COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md) streams **A–E**. These files are **examples** until operators copy them to a secured location and fill live values. + +| File | Stream | +|------|--------| +| [corridor-decision.example.md](corridor-decision.example.md) | A (Noble / stable corridor decisions) | +| [osmosis-routing-flags.example.yaml](osmosis-routing-flags.example.yaml) | B (routing mode, limits) | +| [denom-trace.example.json](denom-trace.example.json) | C (IBC trace / hash schema) | +| [cosmwasm-integration.example.md](cosmwasm-integration.example.md) | D (Wasm app pairing) | +| [cross-cutting.example.md](cross-cutting.example.md) | E (relayers, wallets, audits, closure) | +| [STATUS.md](STATUS.md) | Doc vs live completion tracking | + +**Gaps and inconsistencies (repo-wide audit, includes PMM/CCIP drift):** [COSMOS_CHAIN138_GAPS_AND_INCONSISTENCIES.md](../../docs/11-references/COSMOS_CHAIN138_GAPS_AND_INCONSISTENCIES.md) + +Do **not** commit production keys, channel IDs, or signed vendor contracts here. diff --git a/config/cosmos-chain138-optional/STATUS.md b/config/cosmos-chain138-optional/STATUS.md new file mode 100644 index 00000000..8240970e --- /dev/null +++ b/config/cosmos-chain138-optional/STATUS.md @@ -0,0 +1,18 @@ +# Streams A–E — completion status (repo) + +**Updated:** 2026-05-09 +**Meaning:** **Doc** = runbook + templates in this repo are complete. **Live** = requires operator decisions, contracts, keys, and vendor chains (not asserted here). + +**Last LAN operator pass (repo standard scripts, not Cosmos-specific):** `run-completable-tasks-from-anywhere.sh` OK; `run-all-operator-tasks-from-lan.sh --skip-backup` OK (NPMplus proxy refresh + Blockscout verification). Does not deploy IBC or Noble. + +| Stream | Doc / template | Live bridge & ops | +|--------|------------------|-------------------| +| **A** Noble / stable corridor | Complete (runbook §3 + `corridor-decision.example.md`) | Not live — operator fills decision record and executes on-chain when adopted | +| **B** Osmosis / Cosmos DEX routing | Complete (runbook §4 + `osmosis-routing-flags.example.yaml`) | Not live — route engine integration when adopted | +| **C** Generic IBC allowlist | Complete (runbook §5 + `denom-trace.example.json`) | Not live — governance allowlist + registry rows when adopted | +| **D** CosmWasm app ↔ 138 | Complete (runbook §6 + `cosmwasm-integration.example.md`) | Not live — per-app deploy when adopted | +| **E** Cross-cutting + program closure | Complete (runbook §7 + `cross-cutting.example.md`) | Partially live only if A–D adopted (relayers, audits, etc.) | + +**Deferred / out of scope until adoption:** Any mainnet IBC channel, Noble mint path, Osmosis swap execution, CosmWasm ICA, and production relayer keys. + +**Full gaps + inconsistency inventory:** [COSMOS_CHAIN138_GAPS_AND_INCONSISTENCIES.md](../../docs/11-references/COSMOS_CHAIN138_GAPS_AND_INCONSISTENCIES.md) \ No newline at end of file diff --git a/config/cosmos-chain138-optional/corridor-decision.example.md b/config/cosmos-chain138-optional/corridor-decision.example.md new file mode 100644 index 00000000..604ceb4c --- /dev/null +++ b/config/cosmos-chain138-optional/corridor-decision.example.md @@ -0,0 +1,43 @@ +# Stream A — corridor decision record (example) + +Copy to internal wiki or secured docs. Replace all `TODO`. + +## A1 Source and bridge stack + +| Field | Value | +|-------|--------| +| Source chain | TODO (e.g. Noble) | +| Bridge vendor / stack | TODO (e.g. general-message bridge, CCTP path, or oracle-operated) | +| Vendor documentation URL | TODO | + +## A2 138-side contracts and roles + +| Role | Address or multisig | Notes | +|------|---------------------|--------| +| Admin | TODO | | +| Pauser | TODO | | +| Oracle or verifier | TODO | | + +## A3 Denom → Chain 138 token mapping + +| IBC denom / trace | Chain 138 token address | Decimals | Canonical ref | +|-------------------|-------------------------|----------|---------------| +| TODO | TODO | TODO | EXPLORER_TOKEN_LIST_CROSSCHECK + ADDRESS_MATRIX | + +## A4 ChainRegistry + +| chainIdentifier | adapter (138) | additionalData summary | +|-----------------|----------------|-------------------------| +| TODO | TODO | TODO (channel IDs, bridge contract on counterparty) | + +## A5 Liquidity + +| Venue | Amount / policy | Owner sign-off | +|-------|-----------------|----------------| +| TODO | TODO | TODO | + +## A6 E2E evidence + +| Direction | Tx hash / link | Date | +|-----------|----------------|------| +| TODO | TODO | TODO | diff --git a/config/cosmos-chain138-optional/cosmwasm-integration.example.md b/config/cosmos-chain138-optional/cosmwasm-integration.example.md new file mode 100644 index 00000000..4e7b8cf3 --- /dev/null +++ b/config/cosmos-chain138-optional/cosmwasm-integration.example.md @@ -0,0 +1,29 @@ +# Stream D — CosmWasm app pairing (example) + +## D1 Contract pins + +| Network | Contract address | Code ID | Migrate policy | +|---------|------------------|---------|------------------| +| TODO | TODO | TODO | frozen / multisig / DAO | + +## D2 Control path + +Chosen path: TODO (ICA / ICQ / GMP to EVM / off-chain coordinator + oracle) + +Attach sequence diagram URL: TODO + +## D3 Chain 138 counterpart + +| Contract | Address | Purpose | +|----------|---------|---------| +| TODO | TODO | vault / adapter / settlement | + +## D4 Hyperledger + +Required: yes / no — if yes, cite Cacti/Firefly task from MULTI_CHAIN_DEPLOYMENT_GUIDE Phase 4. + +## D5 Wasm CI + +Repository: TODO +Build command: TODO +Upgrade runbook URL: TODO diff --git a/config/cosmos-chain138-optional/cross-cutting.example.md b/config/cosmos-chain138-optional/cross-cutting.example.md new file mode 100644 index 00000000..a2c9690e --- /dev/null +++ b/config/cosmos-chain138-optional/cross-cutting.example.md @@ -0,0 +1,43 @@ +# Stream E — cross-cutting + program closure (example) + +## E1 IBC relayers + +| Channel | Relayer operator | Key custody | SLA | +|---------|------------------|-------------|-----| +| TODO | TODO | TODO | TODO | + +## E2 Wallets (Cosmos legs) + +| Wallet | Supported flows | User doc URL | +|--------|-----------------|--------------| +| Keplr | TODO | TODO | +| Leap | TODO | TODO | +| MetaMask Snap (if used) | TODO | TODO | + +## E3 Indexing and observability + +| System | Cosmos / IBC events | Retention | +|--------|---------------------|-----------| +| TODO | TODO | TODO | + +## E4 Audits + +| Scope | Vendor | Report URL | Date | +|-------|--------|------------|------| +| TODO bridge | TODO | TODO | TODO | + +## E5 Rate limits + +| Surface | Limit | Enforced by | +|---------|-------|-------------| +| Mint API | TODO | TODO | +| On-chain mint/burn | TODO | contract | + +## E6 Program closure + +| Milestone | Owner | Date | +|-----------|-------|------| +| Preconditions P1–P5 signed | TODO | TODO | +| Streams adopted (product A–D + closure E) | TODO | TODO | +| CHAINS_AND_PROTOCOLS + GALATIC updated for live only | TODO | TODO | +| Annual review scheduled | TODO | TODO | diff --git a/config/cosmos-chain138-optional/denom-trace.example.json b/config/cosmos-chain138-optional/denom-trace.example.json new file mode 100644 index 00000000..f51875e4 --- /dev/null +++ b/config/cosmos-chain138-optional/denom-trace.example.json @@ -0,0 +1,19 @@ +{ + "$schema_comment": "Stream C — example IBC trace record for allowlist + ChainRegistry.additionalData mirror", + "tier": "TIER_1_GOVERNANCE", + "ibc_hash": "TODO_ibc_hash_or_full_denom", + "path": [ + { + "port_id": "transfer", + "channel_id": "TODO", + "counterparty_chain_id": "TODO", + "counterparty_channel_id": "TODO" + } + ], + "human_readable_denom": "TODO", + "bridge_stack_id": "TODO_vendor_or_internal_id", + "chain138_token_address": "0x0000000000000000000000000000000000000000", + "decimals": 6, + "canonical_crosscheck_ref": "docs/11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md", + "user_warning_long_trace": true +} diff --git a/config/cosmos-chain138-optional/osmosis-routing-flags.example.yaml b/config/cosmos-chain138-optional/osmosis-routing-flags.example.yaml new file mode 100644 index 00000000..0d0187b9 --- /dev/null +++ b/config/cosmos-chain138-optional/osmosis-routing-flags.example.yaml @@ -0,0 +1,13 @@ +# Stream B — routing integration flags (example) +# Copy into route-engine or service config when Osmosis/Cosmos DEX leg is adopted. + +cosmos_dex_routing: + mode: quotes_only # quotes_only | executable_swaps + max_slippage_bps: 50 + ibc_timeout_seconds: 600 + quote_ttl_seconds: 30 + chains_allowlist: [] # e.g. cosmoshub-4, osmosis-1 — fill when live + fail_closed_on_timeout: true + aggregator_alignment: + use_axelar_squid_patterns: false # set true when wired to explorer/squid stack + review_doc: docs/11-references/BRIDGE_CHAINS_IMPLEMENTATION_COMPLETE.md diff --git a/config/defillama-chain138-touchpoints.json b/config/defillama-chain138-touchpoints.json index 8df72aca..5d5d07bb 100644 --- a/config/defillama-chain138-touchpoints.json +++ b/config/defillama-chain138-touchpoints.json @@ -11,6 +11,10 @@ "regulatoryFraming": "Compliant digital money / e-money settlement rail — TVL adapters measure on-chain contract balances, not issuer licensing status.", "upstream": { "defillamaAdapters": "https://github.com/DefiLlama/DefiLlama-Adapters", + "upstreamPullRequest": "https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198", + "upstreamPullRequestState": "open", + "upstreamPullRequestStateAsOf": "2026-05-10", + "upstreamPullRequestLastGhCheckUtc": "2026-05-10T19:59Z", "fork": "https://github.com/Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters", "defillamaServer": "https://github.com/DefiLlama/defillama-server", "dimensionAdapters": "https://github.com/DefiLlama/dimension-adapters" @@ -113,6 +117,9 @@ ], "docs": { "ecosystemMap": "docs/04-configuration/defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md", - "submissionChecklist": "docs/04-configuration/defillama/DEFILLAMA_CHAIN138_SUBMISSION_CHECKLIST.md" + "submissionChecklist": "docs/04-configuration/defillama/DEFILLAMA_CHAIN138_SUBMISSION_CHECKLIST.md", + "dodoDfioMetaMainTvlNote": "docs/11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md", + "masterReference": "docs/00-meta/METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md", + "repositoriesAndPrs": "docs/00-meta/REPOSITORIES_AND_PRS_CHAIN138.md" } } diff --git a/config/elemental-imperium-network-registry.json b/config/elemental-imperium-network-registry.json index 05a982d6..1c7b0221 100644 --- a/config/elemental-imperium-network-registry.json +++ b/config/elemental-imperium-network-registry.json @@ -1,6 +1,6 @@ { "registryVersion": 1, - "purpose": "Authoritative network codes for Elemental Imperium (33×33×6) wallet labels. ASNs are optional; add when assigned.", + "purpose": "Authoritative network codes for Elemental Imperium (33×33×6) wallet labels. Grid addresses are treasuries for sovereign nations in the EI model; ASNs are optional; add when assigned.", "labelSpec": { "id": "ei-wallet-label-v1", "cellIdFormat": "EI-L{lpbca:02d}-B{branch:02d}-C{class}", diff --git a/config/engine-x/automation-policy.json b/config/engine-x/automation-policy.json new file mode 100644 index 00000000..12b5865f --- /dev/null +++ b/config/engine-x/automation-policy.json @@ -0,0 +1,122 @@ +{ + "version": "1.0.0", + "updated": "2026-05-09", + "component": "DBIS Engine X", + "purpose": "Automation policy for API-fed liquidity advice, XAUt-backed USDC capacity calculation, cW* output planning, on-chain execution gates, and endpoint reporting.", + "mode": { + "default": "read_only_advisor", + "liveExecutionRequires": [ + "operator_approval", + "protected_transaction_rpc", + "fresh_live_quotes", + "funded_usdc_lender_or_public_quote_inventory", + "passing_solidity_preview", + "nonzero_iso_audit_peg_hashes" + ] + }, + "inputs": { + "requestedOutput": { + "symbolEnv": "ENGINE_X_REQUESTED_CW_SYMBOL", + "defaultSymbol": "cWUSDC", + "amountRawEnv": "ENGINE_X_REQUESTED_OUTPUT_RAW", + "amountUnitsEnv": "ENGINE_X_REQUESTED_OUTPUT_UNITS", + "supportedInitialSymbols": [ + "cWUSDC" + ] + }, + "collateral": { + "symbol": "XAUt", + "availableRawEnv": "ENGINE_X_XAUT_AVAILABLE_RAW", + "availableUnitsEnv": "ENGINE_X_XAUT_AVAILABLE_UNITS", + "usdPrice6Env": "ENGINE_X_XAUT_USD_PRICE6", + "defaultUsdPrice6": "3226640000" + }, + "risk": { + "ltvBpsEnv": "ENGINE_X_BORROW_LTV_BPS", + "defaultLtvBps": 7500, + "minHealthFactorBpsEnv": "ENGINE_X_BORROW_MIN_HEALTH_FACTOR_BPS", + "defaultMinHealthFactorBps": 11000, + "maxRoundTripLossBpsEnv": "ENGINE_X_MAX_ROUND_TRIP_LOSS_BPS", + "defaultMaxRoundTripLossBps": 100, + "maxPublicSwapLossBpsEnv": "ENGINE_X_MAX_PUBLIC_SWAP_LOSS_BPS", + "defaultMaxPublicSwapLossBps": 75, + "minGasReserveWeiEnv": "ENGINE_X_MIN_GAS_RESERVE_WEI", + "defaultMinGasReserveWei": "5000000000000000" + } + }, + "feeds": { + "repoReports": [ + "reports/status/mainnet-cwusdc-usdc-support-health-latest.json", + "reports/status/engine-x-public-indexed-readiness-latest.json", + "reports/status/engine-x-mev-defense-readiness-latest.json", + "reports/status/mainnet-cwusdc-weth-liquidity-surfaces-latest.json" + ], + "onChainReadinessCommands": [ + "pnpm engine-x:public-readiness", + "pnpm engine-x:mev-defense", + "pnpm engine-x:weth-liquidity-eval", + "python3 scripts/verify/check-mainnet-cwusdc-usdc-support-health.py" + ], + "externalPublicationTargets": [ + "token-aggregation public report API", + "CoinGecko tracker package", + "CoinMarketCap tracker package", + "Etherscan public transaction and pool pages", + "DexScreener and GeckoTerminal indexed pool pages", + "exchange listing or OTC due-diligence packet", + "Forex desk liquidity and proof packet" + ] + }, + "decisionGates": { + "apiAdvisor": [ + "all required report feeds are present or regenerated", + "requested output token is supported", + "calculator produces positive capacity", + "all blocker fields are explicit" + ], + "liveExecution": [ + "MEV defense readiness is ready", + "selected liquidity surface has live indexable pool evidence", + "selected public swap has nonzero quote and minOut", + "USDC debt after loop is zero", + "XAUt collateral withdrawal is previewed after repayment", + "no route substitutes DODO, UniV2, UniV3, WETH, or USDC roles silently", + "operator has reviewed generated commands" + ], + "publication": [ + "on-chain transaction hashes are present", + "before and after reserves or slot0/liquidity are present", + "ISO 20022-style proof hash is present when applicable", + "audit envelope hash is present", + "peg proof hash is present", + "public endpoint package was regenerated after execution" + ] + }, + "automationPhases": [ + { + "id": "phase_0_advisor", + "name": "Read-only API and calculator advice", + "broadcast": false + }, + { + "id": "phase_1_canary", + "name": "Protected tiny public canary execution", + "broadcast": "operator_approved_only" + }, + { + "id": "phase_2_liquidity_defense", + "name": "Automated protected rebalance and quote defense", + "broadcast": "after_canary_and_loss_limits" + }, + { + "id": "phase_3_endpoint_publication", + "name": "Regenerate reports and publish endpoint packets", + "broadcast": false + }, + { + "id": "phase_4_multi_asset_forex_crypto", + "name": "Extend cW* request symbols and FX/crypto endpoint packets", + "broadcast": "per_asset_policy" + } + ] +} diff --git a/config/extraction/mainnet-cwusdc-usdc-support-policy.json b/config/extraction/mainnet-cwusdc-usdc-support-policy.json index bc67399f..3830a589 100644 --- a/config/extraction/mainnet-cwusdc-usdc-support-policy.json +++ b/config/extraction/mainnet-cwusdc-usdc-support-policy.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "updated": "2026-04-18", + "updated": "2026-05-08", "rail": "mainnet_cwusdc_usdc", "network": { "chainId": 1, @@ -24,6 +24,62 @@ "poolAddress": "0x69776fc607e9edA8042e320e7e43f54d06c68f0E", "managedCycleScript": "smom-dbis-138/script/flash/RunManagedMainnetAaveCwusdcUsdcQuotePushCycle.s.sol:RunManagedMainnetAaveCwusdcUsdcQuotePushCycle" }, + "quoteDefenseSurfaces": [ + { + "id": "mainnet-cwusdc-usdc-univ3-100", + "base": "cWUSDC", + "quote": "USDC", + "role": "primary_public_indexed_quote_defense", + "venue": "uniswap_v3_pool", + "poolAddress": "0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3", + "fee": 100, + "routerAddress": "0xE592427A0AEce92De3Edee1F18E0157C05861564", + "quoterAddress": "0x61fFE014bA17989E743c5F6cB21bF9697530B21e", + "activeRange": { + "preferredLowerTick": -100, + "preferredUpperTick": 100, + "targetTick": 0 + }, + "defenseMode": "quote_side_tick_reentry_then_proof_swaps", + "automationStatus": "operator_review_required", + "notes": [ + "Primary public/indexable quote-defense surface for tiny 1:1 canaries.", + "If current tick is below -100, first push quote side with USDC->cWUSDC until the old -100..100 position re-enters range.", + "Do not assume historical 75/75 or 85/85 liquidity is active; verify slot0 tick, active liquidity, and token balances before proof swaps." + ] + }, + { + "id": "mainnet-cwusdc-usdc-univ2", + "base": "cWUSDC", + "quote": "USDC", + "role": "secondary_public_indexed_quote_defense", + "venue": "uniswap_v2_pair", + "poolAddress": "0xC28706F899266b36BC43cc072b3a921BDf2C48D9", + "routerAddress": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", + "defenseMode": "quote_side_reserve_repair", + "automationStatus": "capital_gated", + "notes": [ + "Public/indexable reserve surface for larger swaps.", + "Use only after fresh reserve read and quote-side repair sizing.", + "Current role is secondary because the pair is quote-starved." + ] + }, + { + "id": "mainnet-cwusdc-usdc-dodo-managed", + "base": "cWUSDC", + "quote": "USDC", + "role": "managed_defended_quote_push", + "venue": "dodo_pmm", + "poolAddress": "0x69776fc607e9edA8042e320e7e43f54d06c68f0E", + "defenseMode": "managed_quote_push_cycle", + "automationStatus": "capital_gated", + "notes": [ + "Managed defended lane remains distinct from public indexed proof lanes.", + "Do not silently substitute this pool for the public spend settlement replacement pool.", + "Requires passing DODO quote preview and sufficient official Mainnet USDC quote inventory before automation." + ] + } + ], "thresholds": { "warnDeviationBps": 25, "interveneDeviationBps": 75, @@ -46,10 +102,37 @@ } ] }, + "mevDefense": { + "status": "required_for_sensitive_execution", + "failClosedByDefault": true, + "acceptedPrivateRpcEnvKeys": [ + "ENGINE_X_PRIVATE_TX_RPC", + "MEV_BLOCKER_RPC_URL", + "FLASHBOTS_RPC_URL", + "BLOXROUTE_RPC_URL", + "BLINK_RPC_URL" + ], + "publicBroadcastOverrideEnvKey": "ENGINE_X_ALLOW_PUBLIC_BROADCAST", + "disableGuardEnvKey": "ENGINE_X_MEV_PROTECTION", + "protectedActions": [ + "engine-x-univ3-public-swap-proof", + "engine-x-univ2-public-indexed-loop", + "mainnet-cwusdc-usdc-univ2-canary-repair", + "engine-x-univ3-indexed-lp-migration" + ], + "rules": [ + "Use private/protected RPC for any live Engine X public-indexed swap, quote repair, LP migration, or rebalance.", + "Dry-runs and read-only health checks may use the normal Mainnet RPC.", + "Do not broadcast quote-defense transactions through the public mempool unless the operator explicitly sets ENGINE_X_ALLOW_PUBLIC_BROADCAST=1 for a reviewed canary.", + "Never print protected RPC URLs in reports; report only the configured provider label or env key." + ] + }, "notes": [ "The public Uniswap V2 pair is for visible routing and discovery, not for hard parity guarantees on its own.", + "The UniV3 fee-100 pool is the preferred public/indexable quote-defense canary lane when its tick is in or near the configured active range.", "When the public pair drifts beyond the intervention corridor, use the managed DODO quote-push stack as the first automated defense path.", "Do not trust POOL_CWUSDC_USDC_MAINNET from dotenv blindly; this policy is the canonical defended venue binding.", + "Automation must select quoteDefenseSurfaces by explicit role and venue; never silently substitute UniV2, UniV3, or DODO for each other.", "Automation is capped to the currently tested safe tranche until the defended lane is explicitly re-sized from fresh liquidity measurements." ] } diff --git a/config/gitea-act-runner/README.md b/config/gitea-act-runner/README.md new file mode 100644 index 00000000..7794f11c --- /dev/null +++ b/config/gitea-act-runner/README.md @@ -0,0 +1,8 @@ +# Gitea act_runner config templates + +| File | CT | Purpose | +|------|-----|---------| +| `config-5700-heavy.yaml` | 5700 (`dev-vm`) | `ubuntu-latest-heavy` — large pnpm/validation jobs | +| `config-5701-standard.yaml` | 5701 (`gitea-runner-1`) | `ubuntu-latest` / `ubuntu-22.04` / `ubuntu-20.04` — default pool | + +Deploy with `bash scripts/dev-vm/apply-act-runner-config.sh` from the repo root (LAN). Full procedure: `docs/04-configuration/GITEA_ACT_RUNNER_SETUP.md`. diff --git a/config/gitea-act-runner/config-5700-heavy.yaml b/config/gitea-act-runner/config-5700-heavy.yaml new file mode 100644 index 00000000..1ba59101 --- /dev/null +++ b/config/gitea-act-runner/config-5700-heavy.yaml @@ -0,0 +1,41 @@ +# act_runner — CT 5700 (dev-vm): heavy / monorepo CI (label ubuntu-latest-heavy). +# Applied by: bash scripts/dev-vm/apply-act-runner-config.sh +# Image: https://gitea.com/docker.gitea.com/runner-images + +log: + level: info + +runner: + file: .runner + capacity: 1 + timeout: 3h + shutdown_timeout: 0s + insecure: false + fetch_timeout: 5s + fetch_interval: 5s + github_mirror: '' + labels: + - "ubuntu-latest-heavy:docker://docker.gitea.com/runner-images:ubuntu-latest" + +cache: + enabled: true + dir: "" + host: "" + port: 0 + external_server: "" + +container: + network: bridge + privileged: false + options: "--cpus=4 --memory=10g --pids-limit=8192" + workdir_parent: /var/lib/act_runner/workdir + valid_volumes: + - '**' + docker_host: "" + force_pull: false + force_rebuild: false + require_docker: false + docker_timeout: 0s + +host: + workdir_parent: /var/lib/act_runner/workdir diff --git a/config/gitea-act-runner/config-5701-standard.yaml b/config/gitea-act-runner/config-5701-standard.yaml new file mode 100644 index 00000000..de42b8d8 --- /dev/null +++ b/config/gitea-act-runner/config-5701-standard.yaml @@ -0,0 +1,42 @@ +# act_runner — CT 5701 (gitea-runner-1): default pool (labels ubuntu-latest / 22.04 / 20.04). +# Applied by: bash scripts/dev-vm/apply-act-runner-config.sh + +log: + level: info + +runner: + file: .runner + capacity: 1 + timeout: 3h + shutdown_timeout: 0s + insecure: false + fetch_timeout: 5s + fetch_interval: 5s + github_mirror: '' + labels: + - "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest" + - "ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04" + - "ubuntu-20.04:docker://docker.gitea.com/runner-images:ubuntu-20.04" + +cache: + enabled: true + dir: "" + host: "" + port: 0 + external_server: "" + +container: + network: bridge + privileged: false + options: "--cpus=2 --memory=4g --pids-limit=512" + workdir_parent: /var/lib/act_runner/workdir + valid_volumes: + - '**' + docker_host: "" + force_pull: false + force_rebuild: false + require_docker: false + docker_timeout: 0s + +host: + workdir_parent: /var/lib/act_runner/workdir diff --git a/config/gitea-workflow-templates/deploy-via-phoenix-api.yml b/config/gitea-workflow-templates/deploy-via-phoenix-api.yml index cab02045..c7cc2ad5 100644 --- a/config/gitea-workflow-templates/deploy-via-phoenix-api.yml +++ b/config/gitea-workflow-templates/deploy-via-phoenix-api.yml @@ -24,7 +24,8 @@ jobs: SHA="$(git rev-parse HEAD)" BRANCH="$(git rev-parse --abbrev-ref HEAD)" REPO="${{ gitea.repository }}" - curl -sSf -X POST "${PHOENIX_DEPLOY_URL}" \ + curl -sSf --connect-timeout 10 --max-time 3600 \ + -X POST "${PHOENIX_DEPLOY_URL}" \ -H "Authorization: Bearer ${PHOENIX_DEPLOY_TOKEN}" \ -H "Content-Type: application/json" \ -d "{\"repo\":\"${REPO}\",\"sha\":\"${SHA}\",\"branch\":\"${BRANCH}\",\"target\":\"${TARGET}\"}" diff --git a/config/gitea-workflow-templates/repos/cromero-default.yml b/config/gitea-workflow-templates/repos/cromero-default.yml index c60641d0..022c4627 100644 --- a/config/gitea-workflow-templates/repos/cromero-default.yml +++ b/config/gitea-workflow-templates/repos/cromero-default.yml @@ -17,7 +17,8 @@ jobs: run: | SHA="$(git rev-parse HEAD)" BRANCH="$(git rev-parse --abbrev-ref HEAD)" - curl -sSf -X POST "${{ secrets.PHOENIX_DEPLOY_URL }}" \ + curl -sSf --connect-timeout 10 --max-time 3600 \ + -X POST "${{ secrets.PHOENIX_DEPLOY_URL }}" \ -H "Authorization: Bearer ${{ secrets.PHOENIX_DEPLOY_TOKEN }}" \ -H "Content-Type: application/json" \ -d "{\"repo\":\"d-bis/CROMERO\",\"sha\":\"${SHA}\",\"branch\":\"${BRANCH}\",\"target\":\"default\"}" diff --git a/config/gitea-workflow-templates/repos/currencicombo-default.yml b/config/gitea-workflow-templates/repos/currencicombo-default.yml index 2dfca6da..39a09260 100644 --- a/config/gitea-workflow-templates/repos/currencicombo-default.yml +++ b/config/gitea-workflow-templates/repos/currencicombo-default.yml @@ -17,7 +17,8 @@ jobs: run: | SHA="$(git rev-parse HEAD)" BRANCH="$(git rev-parse --abbrev-ref HEAD)" - curl -sSf -X POST "${{ secrets.PHOENIX_DEPLOY_URL }}" \ + curl -sSf --connect-timeout 10 --max-time 3600 \ + -X POST "${{ secrets.PHOENIX_DEPLOY_URL }}" \ -H "Authorization: Bearer ${{ secrets.PHOENIX_DEPLOY_TOKEN }}" \ -H "Content-Type: application/json" \ -d "{\"repo\":\"d-bis/CurrenciCombo\",\"sha\":\"${SHA}\",\"branch\":\"${BRANCH}\",\"target\":\"default\"}" diff --git a/config/gitea-workflow-templates/repos/dbis-portal-live.yml b/config/gitea-workflow-templates/repos/dbis-portal-live.yml index cae10088..84d59949 100644 --- a/config/gitea-workflow-templates/repos/dbis-portal-live.yml +++ b/config/gitea-workflow-templates/repos/dbis-portal-live.yml @@ -17,7 +17,8 @@ jobs: run: | SHA="$(git rev-parse HEAD)" BRANCH="$(git rev-parse --abbrev-ref HEAD)" - curl -sSf -X POST "${{ secrets.PHOENIX_DEPLOY_URL }}" \ + curl -sSf --connect-timeout 10 --max-time 3600 \ + -X POST "${{ secrets.PHOENIX_DEPLOY_URL }}" \ -H "Authorization: Bearer ${{ secrets.PHOENIX_DEPLOY_TOKEN }}" \ -H "Content-Type: application/json" \ -d "{\"repo\":\"Gov_Web_Portals/DBIS\",\"sha\":\"${SHA}\",\"branch\":\"${BRANCH}\",\"target\":\"dbis-portal-live\"}" diff --git a/config/non-evm-lane-requirements.json b/config/non-evm-lane-requirements.json new file mode 100644 index 00000000..2c03a724 --- /dev/null +++ b/config/non-evm-lane-requirements.json @@ -0,0 +1,89 @@ +{ + "schema": "non-evm-lane-requirements/v1", + "generatedAt": "2026-05-11T22:34:46.673427+00:00", + "status": "stubs_bound_repo_side", + "lanes": [ + { + "network": "solana", + "nativeAsset": "SOL", + "walletStatus": "bound_from_SOLANA_KEYPAIR_PATH_public_key", + "canonicalWallet": "9b4ebHVimuhMqbiCh6tUMMY2S48VyEHpqg5nxMMFe5Pf", + "requiredBindings": [ + "splMintAddresses", + "rentReserveTarget", + "venueMinimumLiquidity" + ], + "minimumFundingTarget": "TBD", + "claimBoundary": "Do not claim native Solana liquidity until SPL mints, rent/gas, and venue inventory are bound." + }, + { + "network": "tron", + "nativeAsset": "TRX", + "walletStatus": "derived_wallet_needs_canonical_confirmation", + "canonicalWallet": "TGkbidE5LfVJZ3QGj6DaPqzCTcTe9tJDxm", + "requiredBindings": [ + "canonicalWalletApproval", + "energyBandwidthTarget", + "trc20Inventory" + ], + "minimumFundingTarget": "TBD", + "claimBoundary": "Do not claim native Tron liquidity until the canonical wallet and TRC-20 inventory are confirmed." + }, + { + "network": "xrpl", + "nativeAsset": "XRP", + "walletStatus": "missing", + "canonicalWallet": null, + "requiredBindings": [ + "xrplAccount", + "destinationTagPolicy", + "trustlineIssuerPolicy", + "xrpReserveTarget" + ], + "minimumFundingTarget": "TBD", + "claimBoundary": "Do not claim XRPL corridor readiness until account reserve, tags, trustlines, and wXRP controller evidence are closed." + }, + { + "network": "bitcoin", + "nativeAsset": "BTC", + "walletStatus": "missing", + "canonicalWallet": null, + "requiredBindings": [ + "btcCustodyAddress", + "proofOfReservesPolicy", + "wrappedAssetMapping", + "venueTarget" + ], + "minimumFundingTarget": "TBD", + "claimBoundary": "Use BTC as a planning lane only until custody/reserve evidence and wrapping policy are bound." + }, + { + "network": "dogecoin", + "nativeAsset": "DOGE", + "walletStatus": "missing", + "canonicalWallet": null, + "requiredBindings": [ + "dogeCustodyAddress", + "bridgeOrCustodyModel", + "venueTarget" + ], + "minimumFundingTarget": "TBD", + "claimBoundary": "Use DOGE as a planning lane only until native custody and bridge model are bound." + }, + { + "network": "hyperliquid", + "nativeAsset": "HYPE", + "walletStatus": "research_required", + "canonicalWallet": null, + "requiredBindings": [ + "chainIdentifier", + "assetIdentifier", + "custodyPath", + "venueOrApiEvidence" + ], + "minimumFundingTarget": "TBD", + "claimBoundary": "Use HYPE only as a market-cap watch item until identifiers and custody path are verified." + } + ], + "validationRule": "A lane is claimable only after canonicalWallet, asset IDs, native gas/reserve target, venue target, and evidence source are non-TBD." +} diff --git a/config/pmm-soak-wallet-grid.json b/config/pmm-soak-wallet-grid.json index ea2dac51..ba7466ff 100644 --- a/config/pmm-soak-wallet-grid.json +++ b/config/pmm-soak-wallet-grid.json @@ -1,5 +1,8 @@ { "version": 2, + "semanticModel": { + "summary": "The 33×33×6 Elemental Imperium (EI) grid wallets in this file are the canonical Ethereum addresses designated as on-chain treasuries for sovereign nations in the DBIS/EI routing model. Operational mapping from cell (lpbca × branch × class) to a specific nation uses config/elemental-imperium-network-registry.json and overlays; legal recognition and reserve policy are outside this repo." + }, "dimensions": { "lpbcaCount": 33, "branchCount": 33, diff --git a/config/systemd/cwusdc-provider-monitor.service.example b/config/systemd/cwusdc-provider-monitor.service.example new file mode 100644 index 00000000..0bc2d427 --- /dev/null +++ b/config/systemd/cwusdc-provider-monitor.service.example @@ -0,0 +1,16 @@ +# Example install on a monitoring host with this repo checked out: +# sudo cp config/systemd/cwusdc-provider-monitor.service.example /etc/systemd/system/cwusdc-provider-monitor.service +# sudo cp config/systemd/cwusdc-provider-monitor.timer.example /etc/systemd/system/cwusdc-provider-monitor.timer +# sudo systemctl daemon-reload +# sudo systemctl enable --now cwusdc-provider-monitor.timer + +[Unit] +Description=cWUSDC provider propagation monitor +Wants=network-online.target +After=network-online.target + +[Service] +Type=oneshot +User=intlc +WorkingDirectory=/home/intlc/projects/proxmox +ExecStart=/bin/bash /home/intlc/projects/proxmox/scripts/verify/run-cwusdc-provider-monitoring-snapshot.sh diff --git a/config/systemd/cwusdc-provider-monitor.timer.example b/config/systemd/cwusdc-provider-monitor.timer.example new file mode 100644 index 00000000..4211cc6a --- /dev/null +++ b/config/systemd/cwusdc-provider-monitor.timer.example @@ -0,0 +1,10 @@ +[Unit] +Description=Run cWUSDC provider propagation monitor daily + +[Timer] +OnCalendar=*-*-* 13:00:00 UTC +Persistent=true +RandomizedDelaySec=15m + +[Install] +WantedBy=timers.target diff --git a/docs/00-meta/BIBLE_FROM_NATHAN_TOKEN_LAUNCH_RESOURCE_COMPENDIUM.md b/docs/00-meta/BIBLE_FROM_NATHAN_TOKEN_LAUNCH_RESOURCE_COMPENDIUM.md new file mode 100644 index 00000000..0a7f3a4e --- /dev/null +++ b/docs/00-meta/BIBLE_FROM_NATHAN_TOKEN_LAUNCH_RESOURCE_COMPENDIUM.md @@ -0,0 +1,479 @@ +--- +title: Bible from Nathan — Token Launch Resource Compendium +alias: Bible from Nathan +document_class: master-reference +master_index_role: Cross-cutting institutional token and stablecoin launch compendium (regulatory through aggregators and checklists). Not a substitute for repo-specific canonicals below. +source_file: Token_Launch_Resource_Compendium_260510_215234.md +contributed_by: Nathan +generated: 2026-05-10 +scope: Sovereign chain / stablecoin / pegged and non-pegged token launches — institutional reference links and checklists. +note: Informational only; not legal, financial, or tax advice. URLs were verified at original generation; update before redistribution. +--- + + +## SOVEREIGN CHAIN / STABLECOIN LAUNCH +## Token Launch +## Resource Compendium +A Reference for Pegged & Non-Pegged Token Launches +## Custody · Banking · Audits · Aggregators · Regulation · Tooling +## INSTITUTIONAL-GRADE LAUNCH REFERENCE +From Legal Foundation to Mainnet +## Generated 10 May 2026 + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 2 +## Purpose & Scope +This compendium consolidates the operational, technical, financial, legal, and ecosystem resources +required to launch a sovereign Layer-1 blockchain and to issue a pegged stablecoin or any other +token across multiple chains. +It is structured to be reused for any token launch — both pegged and non-pegged — with the rigor +that institutional counterparties expect. Every link below has been verified at the time of generation. +Bookmark this PDF and treat it as a working document; update it whenever a vendor or regulator +changes. +How to use this document +Sections 1–4 are foundational and must be addressed before any contract code is deployed. Sections 5–8 +are technical infrastructure. Sections 9–11 are aggregator, monitoring, and ecosystem integrations. +Section 12 contains reusable launch checklists. The final section lists the recommended ordering and the +preliminary calls to make before writing a single line of Solidity. +Table of Contents +## 1. Regulatory & Compliance +## 2. Legal Counsel +## 3. Custody & Reserves +## 4. Banking & Fiat Rails +## 5. Smart Contract Tooling +## 6. Audits & Bug Bounty +## 7. Multisig, Timelock & Liquidity Locking +## 8. Reference Protocols & Bridge Architecture +## 9. Oracles & Monitoring +## 10. Aggregators & Data Integrity +- Block Explorers, DEXs & Launch Venues +- KYC / AML / Compliance Tooling +## 13. Reserve Attestation & Insurance +## 14. Reusable Launch Checklists +## 15. Recommended Sequencing + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 3 +## REGULATORY & COMPLIANCE +## 1. Regulatory Bodies & Frameworks +Issuing a USD-pegged stablecoin in 2026 is among the most legally exposed activities in crypto. +Architecture is downstream of legal structure — get the legal foundation in place before contract +design. Identify your primary jurisdiction first, then layer in any additional jurisdictions where you +intend to serve users. +## United States +FinCEN — Money Services Business Registration · +https://www.fincen.gov/resources/money-services-business-msb-registration +FinCEN — BSA E-File System (Form 107) · https://bsaefiling.fincen.treas.gov/ +Required filing system for MSB registration. +IRS — Money Services Business Information Center · https://www.irs.gov/businesses/small-businesses +## -self-employed/money-services-business-msb-information-center +CSBS / NMLS — State Money Transmitter Licensing · https://www.csbs.org/ +State-by-state MTL is required if serving US persons. +## European Union +ESMA — Markets in Crypto-Assets (MiCA) Hub · https://www.esma.europa.eu/esmas-activities/digital-fin +ance-and-innovation/markets-crypto-assets-regulation-mica +EBA — Stablecoin & ART/EMT Guidelines · https://www.eba.europa.eu/ +## United Kingdom +FCA — Crypto-Asset Registration · https://www.fca.org.uk/firms/cryptoassets +## Australia +AUSTRAC — Digital Currency Exchange Provider Registration · https://online.austrac.gov.au/ +AUSTRAC — DCE Guidance · https://www.austrac.gov.au/business/how-comply-and-report-guidance-and +## -resources/guidance-resources/digital-currency-exchange-providers +ASIC — Crypto-Asset Regulation · +https://asic.gov.au/regulatory-resources/digital-transformation/crypto-assets/ +## United Arab Emirates +VARA — Dubai Virtual Asset Regulatory Authority · https://www.vara.ae/ +ADGM Financial Services Regulatory Authority (Abu Dhabi) · https://www.adgm.com/ +## Singapore +MAS — Payment Services Act · https://www.mas.gov.sg/regulation/payments + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 4 +## LEGAL COUNSEL +## 2. Specialist Crypto Law Firms +Institutional credibility requires written legal opinions in every jurisdiction where the token is offered. +Engage counsel in the primary jurisdiction first, then layer in the additional jurisdictions in priority +order. +## United States +Cooley LLP — Cryptocurrency Practice · https://www.cooley.com/ +Davis Polk & Wardwell — Digital Assets · https://www.davispolk.com/ +Morrison Cohen — Crypto Group · https://www.morrisoncohen.com/ +Wilson Sonsini — Fintech & Crypto · https://www.wsgr.com/ +United Kingdom & EU +Allen Overy Shearman Sterling (A&O; Shearman) — MiCA · https://www.aoshearman.com/ +Mishcon de Reya — Crypto & Blockchain · https://www.mishcon.com/ +Bird & Bird — Crypto Regulation · https://www.twobirds.com/ +## Australia +Hall & Wilcox — Crypto & Digital Asset Practice · https://hallandwilcox.com.au/ +Piper Alderman — Blockchain & Crypto Group · https://piperalderman.com.au/ +King & Wood Mallesons — Digital Assets · https://www.kwm.com/ +## CUSTODY +## 3. Qualified Custodians +Reserves backing a stablecoin must be held in segregated custody at a qualified custodian — never +on an exchange, never in the operational treasury. Plan for redundancy across two custodians for +systemic risk reduction. +Coinbase Custody · https://www.coinbase.com/institutional/custody +NY-DFS regulated trust company. Standard institutional choice. +BitGo · https://www.bitgo.com/ +Qualified custodian, broad asset support, multi-jurisdiction. +Anchorage Digital · https://www.anchorage.com/ +OCC-chartered digital asset bank — institutional gold standard. +Fireblocks · https://www.fireblocks.com/ +MPC-based hot/warm wallet ops. Use alongside cold custody. +Copper · https://copper.co/ +ClearLoop network. Strong for trading desks. +Komainu · https://www.komainu.com/ +JV between Nomura, Ledger and CoinShares. EU-friendly. + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 5 +Standard Custody & Trust · https://www.standardcustody.com/ +NY trust, smaller and nimble for emerging issuers. + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 6 +## BANKING +## 4. Banking & Fiat Rails +Banking is the single hardest dependency in stablecoin operations. Plan redundancy across at least +two crypto-friendly institutions in different jurisdictions. Banking relationships are fragile — a single +bank failure or de-banking event can halt operations. +## United States / Global +Customers Bank — Digital Asset Banking · +https://www.customersbank.com/business/digital-asset-banking/ +Cross River Bank · https://www.crossriver.com/ +Mercury — Crypto-Adjacent Fintech Banking · https://mercury.com/ +Europe / UK / Switzerland +BCB Group — Crypto Payment & Banking · https://www.bcbgroup.com/ +AMINA Bank (Switzerland, FINMA-licensed) · https://www.aminagroup.com/ +Sygnum Bank (Switzerland) · https://www.sygnum.com/ +## Australia +Independent Reserve — Institutional Banking Layer · https://www.independentreserve.com/business +Most accessible Australian on/off ramp for crypto businesses. +Cuscal — Mutual ADI · https://www.cuscal.com.au/ +Has historically banked some Australian crypto businesses; case-by-case. +## BUILD STACK +## 5. Smart Contract Tooling +OpenZeppelin Contracts (v5) · https://github.com/OpenZeppelin/openzeppelin-contracts +ERC-20, AccessControl, upgradeable proxies, Pausable. Industry standard. +OpenZeppelin Defender · https://defender.openzeppelin.com/ +Operational layer: monitoring, multisig, automated tasks. +Hardhat · https://hardhat.org/ +Mature dev/test/deploy framework. +hardhat-deploy · https://github.com/wighawag/hardhat-deploy +Foundry · https://book.getfoundry.sh/ +Faster fuzz/invariant testing. Strongly recommended for bridge invariants. +Slither — Static Analyzer · https://github.com/crytic/slither +Echidna — Property-Based Fuzzer · https://github.com/crytic/echidna +Tenderly — Simulation & Monitoring · https://tenderly.co/ + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 7 +## SECURITY +## 6. Audits & Bug Bounty +Plan for three independent audits of any bridge, mint/burn, and PSM contracts. Different firms catch +different classes of bugs. Bug bounty must be live before mainnet. +## Tier-1 Audit Firms +Trail of Bits · https://www.trailofbits.com/ +OpenZeppelin Audits · https://www.openzeppelin.com/security-audits +ConsenSys Diligence · https://consensys.io/diligence +Spearbit · https://spearbit.com/ +Cantina (Spearbit Marketplace) · https://cantina.xyz/ +## Competitive Audit Marketplaces +Code4rena · https://code4rena.com/ +Crowd-sourced audits with strong economics. Use after a private audit. +Sherlock · https://www.sherlock.xyz/ +Audit + on-chain coverage. Combines both lines of defense. +## Other Reputable Firms +CertiK · https://www.certik.com/ +Halborn · https://www.halborn.com/ +Quantstamp · https://quantstamp.com/ +## Bug Bounty Platforms +Immunefi · https://immunefi.com/ +The only one institutional counterparties care about. $1M cap minimum. +HackenProof · https://hackenproof.com/ +## KEY MANAGEMENT +- Multisig, Timelock & LP Locking +Safe (formerly Gnosis Safe) · https://app.safe.global/ +Institutional-grade multisig. Use 4-of-7 minimum for bridge admin. +OpenZeppelin TimelockController · +https://docs.openzeppelin.com/contracts/5.x/api/governance#TimelockController +24–72hr timelock on all admin functions. Non-negotiable. +UNCX Network — LP Locker · https://app.uncx.network/ +Most established LP locker. +Team Finance — Token & LP Locker · https://www.team.finance/ +PinkSale Lockers · https://www.pinksale.finance/ + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 8 +## ARCHITECTURE REFERENCES +## 8. Reference Protocols & Bridge Architecture +## Liquidity & Pool Patterns +Curve Stableswap (the math for stablecoin pools) · https://github.com/curvefi/curve-contract +Uniswap V3 (concentrated liquidity) · https://github.com/Uniswap/v3-core +Balancer LBP (fair-launch bootstrapping) · https://docs.balancer.fi/concepts/pools/liquidity-bootstrapping +DODO PMM (Proactive Market Maker reference) · https://github.com/DODOEX/contractV2 +## Stablecoin Architecture References +MakerDAO Peg Stability Module (PSM) · https://github.com/makerdao/dss-psm +The canonical PSM implementation. Fork this pattern. +Circle USDC Smart Contracts · https://github.com/circlefin/stablecoin-evm +Institutional reference: pausable, blacklistable, upgradeable. +## Bridge Architecture References +Circle CCTP — Cross-Chain Transfer Protocol · https://www.circle.com/cross-chain-transfer-protocol +Native mint/burn — gold-standard cross-chain stablecoin pattern. +LayerZero · https://layerzero.network/ +Wormhole · https://wormhole.com/ +Axelar · https://www.axelar.network/ +Chainlink CCIP — Institutional Cross-Chain · https://chain.link/cross-chain +Hyperlane — Modular Interoperability · https://www.hyperlane.xyz/ +## PRICE & MONITORING +## 9. Oracles & Operational Monitoring +## Oracle Providers +Chainlink Data Feeds · https://docs.chain.link/data-feeds +Pyth Network — Low-Latency Institutional Feeds · https://pyth.network/ +RedStone — Modular Oracles · https://redstone.finance/ +Chronicle (formerly MakerDAO oracles) · https://chroniclelabs.org/ +## Monitoring & Threat Detection +Tenderly — Real-Time Contract Monitoring · https://tenderly.co/ +Forta — On-Chain Threat Detection · https://forta.org/ +Blocknative — Mempool Monitoring · https://www.blocknative.com/ +Dune Analytics — Public Dashboards · https://dune.com/ +The Graph — Subgraph Indexing · https://thegraph.com/ + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 9 +## DATA INTEGRITY +## 10. Aggregators & Listing +These are the surfaces where institutions and retail will see the token. Submit to all four with +consistent metadata. Link wrapped versions as additional contracts of the native token to prevent +double-counted market cap. +CoinGecko +Self-Serve Request Form (token listing, info, supply updates) · +https://support.coingecko.com/hc/en-us/articles/33084534107289-Self-Serve-Request-Form +How to List a New Cryptocurrency · https://support.coingecko.com/hc/en-us/articles/7291312302617-Ho +w-to-List-a-New-Cryptocurrency-on-CoinGecko +Support Directory (all forms) · https://support.coingecko.com/hc/en-us/articles/23960919544345-Support +-Directory-CoinGecko-Request-Forms +CoinMarketCap +Request Form (the only official channel) · https://coinmarketcap.com/request/ +Listing Criteria · https://support.coinmarketcap.com/hc/en-us/articles/360043659351-Listings-Criteria +How to Add a Coin/Token · +https://support.coinmarketcap.com/hc/en-us/articles/360016191971-How-to-Add-a-Coin-Token +DEX Screener +Token Listing Documentation · https://docs.dexscreener.com/token-listing +Enhanced Token Info (paid metadata, ~$299) · https://marketplace.dexscreener.com/product/token-info +Marketplace (advertising, trending) · https://marketplace.dexscreener.com/ +DefiLlama +Adapters Repository (TVL adapters) · https://github.com/DefiLlama/DefiLlama-Adapters +How to Submit a Project · https://docs.llama.fi/list-your-project/submit-a-project +Dimension Adapters (DEX volume, fees) · https://github.com/DefiLlama/dimension-adapters +Listing Info Edits · https://github.com/DefiLlama/defillama-server/blob/master/defi/src/protocols/data2.ts +New chain integration note +Both DEX Screener and DefiLlama require chain-level integration before they will index tokens or pools on +a new sovereign chain. Open a chain integration request with each via their Discord/contact channels — +provide RPC endpoints, a working block explorer, a stable subgraph or indexer, and at least one DEX +deployment. This is a multi-week process; start early. + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 10 +## CHAIN INFRA +- Block Explorers, DEXs & Launch Venues +Explorers (verify contracts here) +Etherscan — Verify Contract · https://etherscan.io/verifyContract +BscScan — Verify Contract · https://bscscan.com/verifyContract +Polygonscan — Verify Contract · https://polygonscan.com/verifyContract +Arbiscan — Verify Contract · https://arbiscan.io/verifyContract +Blockscout (open-source explorer for sovereign chains) · https://github.com/blockscout/blockscout +Standard self-hosted explorer. Required for DEX Screener / DefiLlama integration. +Liquidity Bootstrapping (Fair Launches) +Fjord Foundry — LBP-as-a-Service · https://www.fjordfoundry.com/ +Copper Launch · https://copperlaunch.com/ +Balancer Pools · https://balancer.fi/pools +DEXs to Deploy LPs On (Satellites) +Uniswap V3 (ETH, Arbitrum, Polygon, Base, BSC) · https://app.uniswap.org/ +PancakeSwap V3 (BSC) · https://pancakeswap.finance/ +Curve (best for stablecoin pools) · https://curve.fi/ +Balancer (multi-chain) · https://balancer.fi/ +Camelot (Arbitrum) · https://app.camelot.exchange/ +## COMPLIANCE STACK +- KYC / AML / Compliance Tooling +## Transaction Monitoring & Sanctions Screening +Chainalysis · https://www.chainalysis.com/ +TRM Labs · https://www.trmlabs.com/ +Elliptic · https://www.elliptic.co/ +KYC / KYB / Identity Verification +Sumsub · https://sumsub.com/ +Persona · https://withpersona.com/ +Jumio · https://www.jumio.com/ +Onfido · https://onfido.com/ + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 11 +## RESERVES & RISK +## 13. Reserve Attestation & Insurance +Monthly attestation by an independent firm is the institutional baseline for any reserve-backed +stablecoin. Start with mid-tier and graduate to Big Four as TVL grows. +## Reserve Attestation Firms +Armanino · https://www.armanino.com/ +Withum · https://www.withum.com/ +The Network Firm (crypto-native) · https://thenetworkfirm.com/ +Grant Thornton · https://www.grantthornton.com/ +BDO · https://www.bdo.com/ +Deloitte (Big 4 — audits Circle's USDC) · https://www.deloitte.com/ +## Insurance / Coverage +Nexus Mutual — DeFi Cover · https://nexusmutual.io/ +Sherlock — Smart Contract Coverage · https://www.sherlock.xyz/ +Evertas — Institutional Crypto Insurance · https://evertas.com/ +Native — Treasury Insurance · https://www.native-fi.com/ + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 12 +## REUSABLE +## 14. Launch Checklists +These checklists work for any token launch. The first is for pegged stablecoins. The second is for +non-pegged tokens. The third applies to every aggregator listing. The fourth applies to every +multi-chain expansion. +## Checklist A — Pegged Stablecoin Launch +## Foundation +n Legal entity in regulated jurisdiction with crypto-friendly framework +n Custodian agreement signed with qualified custodian (with redundancy) +n Banking relationship with at least two crypto-friendly banks +n Reserve composition policy documented (cash %, T-bills %) +n KYC/AML program for institutional minters +n Compliance officer designated and primary-jurisdiction registration filed +## Smart Contracts +n Token contract: ERC-20 upgradeable proxy, pausable, blacklistable +n Minting contract: multisig-gated, whitelisted minters +n PSM contract: public 1:1 swap with caps and fees +n Bridge contracts with strict supply invariant enforcement +n Locker contract for protocol-owned liquidity with timelock +n All contracts use OpenZeppelin v5 +n Three independent audits completed +n Bug bounty live on Immunefi with $1M+ cap +## Reserves +n Reserves seeded 1:1 before any mint +n Monthly attestation engagement signed +n Public reserves dashboard live +n Real-time supply API endpoint deployed +## Liquidity +n Hub pool: stableswap or V3 with $0.999–$1.001 range +n LP locked 2+ years with timelock contract +n LP held by treasury multisig (POL) +n Satellite PMMs deployed and inventory funded +n PSM deployed on every chain +## Bridge +n Two invariants enforced: wrapped supply == native locked, total supply == reserves + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 13 +n Rate limits configured (1%/hour, 5%/day starting) +n 4-of-7 multisig + 24–72hr timelock on admin +n Pause guardian configured (single-signer pause) +n Daily automated invariant check live +## Monitoring +n Price deviation alerts at 25 bps +n Auto-pause at 100 bps deviation +n Independent monitoring agent on separate infrastructure +n 24/7 incident response rota documented +## Aggregator / Data +n All contracts verified on every chain explorer +n Logo (256×256 PNG transparent), description, links prepared +n CoinGecko submission with reserve attestation +n CoinMarketCap submission with reserve attestation +n DEX Screener pairs claimed and metadata added +n DefiLlama adapter written and PR'd +n Public Supply API endpoint live +n Multi-chain contracts linked to prevent double-counting +n Transparency page with reserves, audits, attestations + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 14 +Checklist B — Non-Pegged Token Launch +## Foundation +n Tokenomics document (supply, distribution, vesting, emissions) +n Whitepaper or lite paper published +n Team / founder allocation: 12-month cliff, 36-month linear vest minimum +## Smart Contracts +n Token contract: ERC-20, ideally non-upgradeable for trust +n Vesting contracts for team / treasury +n Bridge contracts with supply invariant (if multi-chain) +n LP locker with timelock +n At least one audit (two preferred) +n Bug bounty proportional to TVL +## Launch Mechanism +n LBP for fair price discovery (strongly preferred over direct seeding) +n Hub pool seeded after LBP at discovered price +n V3 concentrated range chosen for expected volatility +n LP locked 2 years +n LP held by treasury (POL) +Multi-Chain Expansion +n PMM on each satellite chain instead of thin LPs +n Inventory matched to expected volume per chain +n PMM oracle-anchored to hub TWAP (30-min) +n Bridge with rate limits +n Multisig + timelock on bridge admin +## Oracle / Stability +n Three-source median oracle (hub TWAP + signed off-chain + Chainlink if available) +n Auto-pause at 5% deviation between sources +n Monitoring agent independent of main infrastructure +## Aggregator / Data +n Contracts verified on every chain explorer +n CoinGecko + CoinMarketCap submissions +n DEX Screener claim +n DefiLlama adapter (if protocol has TVL) +n Multi-chain contracts properly linked + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 15 +Checklist C — Aggregator Listing (every token) +n Verified contracts on every chain explorer +n Logo: 256×256 PNG with transparent background +n One-paragraph description (50–200 words) +n Long description (500–1500 words) +n Official website with HTTPS +n Twitter / X account active >30 days +n Telegram / Discord with real members +n Whitepaper PDF +n Audit reports public +n Active liquidity pools with $50k+ daily volume +n CoinGecko submission filed +n CoinMarketCap submission filed +n DEX Screener pairs claimed +n DefiLlama adapter merged (if applicable) +n All wrapped / bridged versions listed as additional contracts on the same listing +n Supply API endpoint exposed for canonical supply queries +Checklist D — Multi-Chain Expansion (any token) +n 30+ days clean operation on hub chain before expanding +n Bridge contracts audited specifically for the new chain +n Satellite chain RPC endpoints stable and redundant +n PMM (preferred) or properly-sized LP deployed +n Oracle feed extended to new chain +n Monitoring extended to new chain +n Wrapped contract verified on chain explorer +n Aggregator listings updated with new contract address +n Bridge rate limits set conservatively for first 30 days +n Documentation updated with new chain details +n Incident runbook updated for new chain failure modes + +## TOKEN LAUNCH · RESOURCE COMPENDIUMINSTITUTIONAL REFERENCE +Generated May 2026Page 16 +## EXECUTION +## 15. Recommended Sequencing +Three calls to make before writing a single line of contract code. These conversations reshape the +architecture in ways that save many multiples of their cost downstream. +01 Primary-Jurisdiction Regulator +Identify the primary jurisdiction and complete the relevant registration (e.g., AUSTRAC DCE in Australia, +FinCEN MSB in the US, MiCA authorisation in the EU). Filing fees are usually nominal; the AML/CTF +program is where consulting cost lives. +## 02 Local Crypto Counsel +Engage a specialist law firm in the primary jurisdiction. Obtain a written legal opinion on token structure, +regulatory classification, and treasury arrangements before any contract design is finalised. +03 US Crypto Counsel (if serving US persons) +If any US persons will hold or transact the token, get advice on FinCEN MSB registration, state money +transmitter implications, and SEC posture from a tier-1 US firm such as Cooley or Davis Polk. +Closing note +The technical infrastructure is the part most engineering teams already know how to build. Legal structure +determines whether the project can actually take institutional money. Sequence accordingly: legal first, +contracts second, audits third, mainnet last. The checklists above are reusable for every future launch — +pegged or non-pegged. +This document is informational and does not constitute legal, financial, or tax advice. URLs verified at time of +generation; vendor and regulatory links may change. Update before redistribution. diff --git a/docs/00-meta/CW_BRIDGE_TASK_LIST.md b/docs/00-meta/CW_BRIDGE_TASK_LIST.md index 8ab9dd50..a47378ff 100644 --- a/docs/00-meta/CW_BRIDGE_TASK_LIST.md +++ b/docs/00-meta/CW_BRIDGE_TASK_LIST.md @@ -21,7 +21,8 @@ | **CCIPWETH9Bridge.sol** (all chains) | Same: validates token == weth9, then `transfer(recipient, amount)`. **Does not mint cW*.** | | **CCIPWETH10Bridge.sol** | Same for WETH10; no cW* logic. | | **CompliantWrappedToken.sol** | Has `mint`, `burn`, and `burnFrom` (BURNER_ROLE). `burnFrom` added in Phase C1 for TwoWayTokenBridgeL2 outbound. | -| **TwoWayTokenBridgeL2.sol** | `ccipReceive` calls `IMintableERC20(mirroredToken).mint(recipient, amount)` — **would mint cW*** if `mirroredToken` = cWUSDT/cWUSDC. Outbound uses `burnFrom`; CompliantWrappedToken does not implement `burnFrom`. | +| **CWMultiTokenBridgeL1/L2.sol** | Preferred cW mint/burn bridge path. L1 locks/releases canonical c* on Chain 138; L2 mints/burns configured cW* mirrors on public chains. | +| **TwoWayTokenBridgeL2.sol** | Per-token fallback. `ccipReceive` calls `IMintableERC20(mirroredToken).mint(recipient, amount)` and outbound uses `burnFrom`. | | **DeployCWTokens.s.sol** | Grants MINTER_ROLE and BURNER_ROLE to `CW_BRIDGE_ADDRESS` (per-chain in .env). So the *address* we set will have roles, but the *contract code* at that address (CCIPWETH9Bridge/CCIPRelayBridge) never calls `mint`/`burn` on cW*. | **Conclusion:** The current bridge suite is **WETH-only**. Granting MINTER/BURNER to it allows deployment of cW* but **does not** enable cross-chain mint/burn of cW* until the receiver logic is extended or a dedicated cW* receiver is deployed. @@ -44,7 +45,7 @@ | # | Task | Owner | Notes | Status | |---|------|--------|-------|--------| -| A1 | **Decide cW* receive strategy:** (1) Extend existing CCIPWETH9Bridge/CCIPRelayBridge to support cW* mint in ccipReceive, or (2) Deploy dedicated cW* receiver per chain (e.g. TwoWayTokenBridgeL2 or new CCIPReceiverCW). | Operator/Dev | Option 2 chosen. | ✅ Done | +| A1 | **Decide cW* receive strategy:** (1) Extend existing CCIPWETH9Bridge/CCIPRelayBridge to support cW* mint in ccipReceive, or (2) Deploy dedicated cW* receiver per chain. | Operator/Dev | Option 2 chosen; preferred implementation is CWMultiTokenBridgeL1/L2. | ✅ Done | | A2 | **Document chosen approach** in `docs/07-ccip/CW_BRIDGE_APPROACH.md` (create): flow 138→chain (lock c* on 138, mint cW* on dest), chain→138 (burn cW*, release c*), and which contract(s) implement receive/send. | Dev | | ✅ Done | ### Phase B: Contract changes (if extending existing bridge) @@ -62,8 +63,8 @@ | # | Task | Owner | Notes | Status | |---|------|--------|-------|--------| | C1 | **CompliantWrappedToken:** Add `burnFrom(address from, uint256 amount)` that checks BURNER_ROLE and calls `burn(from, amount)` (or implement ERC20Burnable and grant BURNER to bridge). | Dev | Required if using TwoWayTokenBridgeL2 for outbound. | ✅ Done | -| C2 | **Deploy TwoWayTokenBridgeL2** (or new CCIPReceiverCW) per chain: constructor(router, cWUSDT, feeToken). Configure destination (Chain 138 selector, L1 bridge address). | Operator | TwoWayTokenBridgeL2 is not upgradeable; one deployment per (chain, token) or generic with token in message. See [CW_DEPLOY_AND_WIRE_RUNBOOK.md](CW_DEPLOY_AND_WIRE_RUNBOOK.md). | Pending | -| C3 | **Point CW_BRIDGE_ to new receiver** and re-run DeployCWTokens so MINTER/BURNER are on the new receiver, or grant roles to new receiver after deploy. | Operator | If receiver is separate from CCIPWETH9_BRIDGE, set CW_BRIDGE_ to receiver address. | Pending | +| C2 | **Deploy CWMultiTokenBridgeL1/L2**: L1 on Chain 138, L2 on each active public mesh chain. Configure destinations and canonical-to-mirrored token pairs. | Operator | Preferred multi-token path; TwoWayTokenBridgeL2 remains a per-token fallback. See [CW_DEPLOY_AND_WIRE_RUNBOOK.md](CW_DEPLOY_AND_WIRE_RUNBOOK.md). | Pending | +| C3 | **Point CW_BRIDGE_ to new receiver** and re-run DeployCWTokens so MINTER/BURNER are on the new receiver, or grant roles to new receiver after deploy. | Operator | If receiver is separate from CCIPWETH9_BRIDGE, set CW_BRIDGE_ to the CWMultiTokenBridgeL2 address. | Pending | | C4 | **Chain 138 send side:** Ensure a sender/bridge on 138 sends CCIP messages with receiver = new cW* receiver on destination and data encoding (recipient, amount). | Dev/Operator | Documented in [CW_BRIDGE_APPROACH.md](../07-ccip/CW_BRIDGE_APPROACH.md). | ✅ Documented | ### Phase D: Deploy cW* and wire config @@ -80,7 +81,7 @@ | # | Task | Owner | Notes | Status | |---|------|--------|-------|--------| | E1 | **Relay service (138→Mainnet):** If using CCIPRelayBridge for cW*, extend relay to support cUSDT/cUSDC: relay must send CCIP with token = cUSDT (or lock-and-mint semantics) and destination = Mainnet bridge; Mainnet bridge must mint cWUSDT. | Operator/Dev | See RELAY_BRIDGE_ADD_LINK_SUPPORT_RUNBOOK.md pattern (extend bridge or new receiver). Runbook § E1. | Operator | -| E2 | **Direct CCIP (138→chain):** If Chain 138 uses UniversalCCIPBridge or CCIPWETH9Bridge to send c* to destination, add destination config for c* and ensure receiver on destination mints cW*. | Dev | Documented in approach; runbook § E2. | Documented | +| E2 | **Direct CCIP (138→chain):** Configure CWMultiTokenBridgeL1 destinations for canonical c* and CWMultiTokenBridgeL2 token pairs so the destination receiver mints cW*. | Dev/Operator | Documented in approach; runbook § E2. | Documented | | E3 | **Test E2E:** Lock cUSDT on 138, trigger send, verify cWUSDT minted on destination to recipient. | Operator | Runbook § E3. | Operator | ### Phase F: Documentation and runbooks @@ -114,5 +115,7 @@ - [CW_DEPLOY_AND_WIRE_RUNBOOK.md](../07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md) — Operator steps for D1–D4, E1–E3. - [RELAY_BRIDGE_ADD_LINK_SUPPORT_RUNBOOK.md](../07-ccip/RELAY_BRIDGE_ADD_LINK_SUPPORT_RUNBOOK.md) (pattern for extending bridge) - `smom-dbis-138/contracts/relay/CCIPRelayBridge.sol`, `contracts/ccip/CCIPWETH9Bridge.sol` +- `smom-dbis-138/contracts/bridge/CWMultiTokenBridgeL1.sol` +- `smom-dbis-138/contracts/bridge/CWMultiTokenBridgeL2.sol` - `smom-dbis-138/contracts/bridge/TwoWayTokenBridgeL2.sol` - `smom-dbis-138/contracts/tokens/CompliantWrappedToken.sol` (includes `burnFrom` for TwoWayTokenBridgeL2) diff --git a/docs/00-meta/DEPLOY_CONFIRM_AND_FULL_E2E_RUNBOOK.md b/docs/00-meta/DEPLOY_CONFIRM_AND_FULL_E2E_RUNBOOK.md index e4952635..2a03b57f 100644 --- a/docs/00-meta/DEPLOY_CONFIRM_AND_FULL_E2E_RUNBOOK.md +++ b/docs/00-meta/DEPLOY_CONFIRM_AND_FULL_E2E_RUNBOOK.md @@ -31,7 +31,7 @@ source smom-dbis-138/.env 2>/dev/null # Or one contract: ./scripts/verify/run-contract-verification-with-proxy.sh --only Multicall ``` -Check https://explorer.d-bis.org/address/ for verification status. +Check https://explorer.d-bis.org/addresses/ for verification status. --- diff --git a/docs/00-meta/METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md b/docs/00-meta/METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md new file mode 100644 index 00000000..ea1699ca --- /dev/null +++ b/docs/00-meta/METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md @@ -0,0 +1,295 @@ +# Master reference — MetaMask Money / mUSD ↔ GRU, provider cross-links, and DefiLlama DODO (`dfio_meta_main`) TVL + +**Status:** canonical **replay and maintenance** document for work completed in-session (narrative alignment, documentation cross-references, DefiLlama DODO Chain 138 TVL diagnosis, upstream PR tracking, and machine-readable touchpoints). + +**Audience:** operators, doc maintainers, and agents who need to **reproduce**, **extend**, or **close the loop** when upstream merges or MetaMask publishes official bindings. + +**Scope boundaries:** This document does **not** replace canonical token economics or on-chain verification — use `docs/11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md` (sections 5 and 8), `docs/11-references/CONTRACT_ADDRESSES_REFERENCE.md`, and `docs/11-references/ADDRESS_MATRIX_AND_STATUS.md` for those. It **does** record **how** the repo was wired for narrative consistency, discoverability, and DefiLlama adapter follow-up. + +--- + +## 1. Executive summary — what was achieved + +| Track | Outcome | +|-------|---------| +| **A. MetaMask Money / mUSD ↔ GRU (internal narrative)** | Added a guarded subsection to the MetaMask provider matrix mapping **Money rail** and **mUSD** to hub **`cUSDC` / `cUSDC_V2`** and wrapped Mainnet **`cWUSDC`**, with explicit disclaimer until MetaMask publishes official CAIP-19 / controller bindings. | +| **B. Discoverability (cross-references)** | Linked that narrative from EIP747 packet, GRU positioning packet, CWUSDC packet, DefiLlama ecosystem map, Explorer/address canonicals, and the provider matrix DeFiLlama row. | +| **C. DefiLlama DODO TVL on `dfio_meta_main`** | Documented why per-chain breakdown can show **`dfio_meta_main: 0.00`** with a finite multi-chain **total** (e.g. ~12.36M), how PR **#19198** addresses it, and how to verify locally. | +| **D. Upstream PR alignment** | Standardized on **[DefiLlama/DefiLlama-Adapters#19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198)** with verified **OPEN** state (as of **2026-05-10** in repo copy — **re-verify on GitHub** before relying on stale text). | +| **E. Machine-readable registry** | Extended `config/defillama-chain138-touchpoints.json` with **`upstreamPullRequest`**, **`upstreamPullRequestState`**, **`upstreamPullRequestStateAsOf`**, **`upstreamPullRequestLastGhCheckUtc`**, and **`docs`** keys (`dodoDfioMetaMainTvlNote`, `masterReference`, `repositoriesAndPrs`). | +| **F. Index registration** | `docs/MASTER_INDEX.md` (quick links + MetaMask + DefiLlama rows), `AGENTS.md`, `.cursor/rules/project-doc-and-deployment-refs.mdc`, `docs/00-meta/NEXT_STEPS_INDEX.md`, `docs/00-meta/TODOS_CONSOLIDATED.md`, `docs/00-meta/REPOSITORIES_AND_PRS_CHAIN138.md` — this file is the **single replay** entry point. | + +--- + +## 2. Part A — MetaMask Money / mUSD ↔ GRU narrative (step-by-step) + +### 2.1 Goal + +Provide **internal DBIS / provider-facing narrative alignment** between MetaMask **Money** / **mUSD** concepts and existing GRU hub and `cW*` transport language, **without** claiming MetaMask has officially bound product strings to specific contracts. + +### 2.2 Where it lives + +**Primary file:** `docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md` + +**Subsection heading:** `### MetaMask Money rail and mUSD — internal GRU alignment (narrative)` + +**Content checklist (when editing or extending):** + +1. **Disclaimer block** at top of subsection: internal narrative only; reconcile when MetaMask publishes CAIP-19, token lists, or controller mappings. +2. **Three-column table:** External/wallet concept | GRU/DBIS interpretation | On-chain/transport anchor. +3. **Hub `cUSDC`:** address `0xf22258f57794CC8E06237084b353Ab30fFfa640b` (6 decimals) — align with canonical token rules in workspace (Explorer cross-check, chain138 token rules). +4. **Hub `cUSDC_V2`:** address `0x219522c60e83dEe01FC5b0329d6fA8fD84b9D13d` — reference `smom-dbis-138/services/token-aggregation/src/config/canonical-tokens.ts` for `familySymbol`, `preferredForX402`, and cutover notes. +5. **Mainnet `cWUSDC`:** `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` — explicit separation from Circle-issued USDC; same submission discipline as rest of matrix. +6. **Operational rule** paragraph: link **`docs/04-configuration/GRU_RISK_AND_DISCLOSURE_LANGUAGE.md`** for disclaimers. + +### 2.3 Cross-references added from the matrix (discoverability) + +Under the same subsection, maintain a **Cross-references (discoverability)** bullet list pointing to: + +| Target | Path | +|--------|------| +| EIP747 / contract metadata packet | `docs/04-configuration/metamask/METAMASK_EIP747_CONTRACT_METADATA_REFERENCE_PACKET.md` | +| GRU provider positioning | `docs/04-configuration/GRU_PROVIDER_POSITIONING_PACKET.md` | +| cWUSDC provider packet | `docs/04-configuration/CWUSDC_PROVIDER_SUBMISSION_PACKET.md` | +| Explorer token cross-check | `docs/11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md` | +| Contract addresses reference | `docs/11-references/CONTRACT_ADDRESSES_REFERENCE.md` | +| Address matrix and status | `docs/11-references/ADDRESS_MATRIX_AND_STATUS.md` | +| DefiLlama `dfio_meta_main` TVL note | `docs/11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md` | + +**Maintenance rule:** When you add a new consumer doc for Money/mUSD, add **one reciprocal link** either here or in the matrix cross-ref list so navigation stays symmetric. + +### 2.4 Reciprocal edits (other files) + +Perform these when introducing the matrix subsection (or keep them in sync on edits): + +1. **`docs/04-configuration/metamask/METAMASK_EIP747_CONTRACT_METADATA_REFERENCE_PACKET.md`** + - Add **`## Related repo documents`** (after “Sources ingested”) with bullets to: the provider matrix (call out Money/mUSD subsection), GRU packet, CWUSDC packet. + +2. **`docs/04-configuration/GRU_PROVIDER_POSITIONING_PACKET.md`** + - In **Canonical References**, extend the matrix bullet to state it **includes MetaMask Money / mUSD ↔ GRU hub and `cW*` transport** (subsection title in prose). + +3. **`docs/04-configuration/CWUSDC_PROVIDER_SUBMISSION_PACKET.md`** + - In **Evidence Links**, extend the provider matrix row to mention Money/mUSD ↔ hub `cUSDC` / `cUSDC_V2` and Mainnet `cWUSDC`. + +4. **`docs/MASTER_INDEX.md`** + - **MetaMask / wallet price-provider submissions** row: ensure it mentions **MetaMask Money / mUSD** narrative in the matrix. + +--- + +## 3. Part B — DefiLlama DODO TVL and `dfio_meta_main` (step-by-step) + +### 3.1 Symptom to recognize + +A **per-chain** DODO (or protocol) TVL table where: + +- Major chains (e.g. `bsc`, `ethereum`, `avax`, `arbitrum`, …) show non-zero USD. +- **`dfio_meta_main`** shows **`0.00`** (DefiLlama SDK key for **Chain 138**). +- **`total`** is the sum of attributed chains and **does not** reflect meta-main liquidity you know exists on-chain. + +**Interpretation:** Usually **adapter / pricing / chain-registration** gap for Chain 138 in that protocol’s DefiLlama adapter — **not** proof of zero DODO TVL on 138. + +### 3.2 Reference note (detailed diagnosis) + +**Authoritative short doc:** `docs/11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md` + +That file covers: + +- Symptom and **CI summary abbreviation** caveat (from upstream PR body). +- Root cause in terms of **`dfio_meta_main`** integration: `chains.json`, **`fixBalancesTokens.dfio_meta_main`** in `tokenMapping.js`, and DODO **`projects/dodo/index.js`** factory / `fromBlock` for 138. +- **Open upstream PR** link and “resolved locally vs resolved on defillama.com” table. +- Local verification command. + +**Do not duplicate** long methodology here; link and maintain **one** technical note (`DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md`). + +### 3.3 Ecosystem map and checklist (repo steps) + +1. Read **`docs/04-configuration/defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md`** for fork → upstream flow, methodology framing, and **open PR #19198** callout. +2. Use **`docs/04-configuration/defillama/DEFILLAMA_CHAIN138_SUBMISSION_CHECKLIST.md`** for **defillama-server** and optional metrics **after** adapters PR merges (checklist opening paragraph ties to **#19198**). + +### 3.4 DefiLlama-Adapters fork (engineering replay) + +**Prerequisites:** Clone or use existing clone; GitHub CLI optional but recommended. + +1. **Clone fork and add upstream** + + ```bash + git clone https://github.com/Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters.git + cd DefiLlama-Adapters + git remote add upstream https://github.com/DefiLlama/DefiLlama-Adapters.git + git fetch upstream + ``` + +2. **Work on feature branch** (example name used in this effort) + + ```bash + git checkout feat/chain-138-dfio-meta-main-dodo + # or: git checkout -b feat/chain-138-dfio-meta-main-dodo origin/main + ``` + +3. **Implement (per PR #19198 scope — verify against current PR diff)** + + - `projects/helper/chains.json` — register **`dfio_meta_main`**. + - `projects/helper/tokenMapping.js` — **`fixBalancesTokens.dfio_meta_main`** for canonical hub **cUSDT** / **cUSDC** → CoinGecko-style IDs used by Llama pricing (`tether`, `usd-coin`), with correct decimals. + - `projects/dodo/index.js` — DODO V2 **DVM** on **`dfio_meta_main`**: factory **`0xc93870594C7f83A0aE076c2e30b494Efc526b68E`**, **`fromBlock` `3510162`** (values also in `config/defillama-chain138-touchpoints.json` under `implemented.tvlAdapter`). + + **Note:** Final balance helper (`sumTokens` vs `sumTokens2`, etc.) is whatever **upstream maintainers merge**. The operator goal is **end-to-end `dfio_meta_main` in the DODO path** plus **priced hub stables**. + +4. **Local test** + + ```bash + cd DefiLlama-Adapters + npm install # if not already + node test.js projects/dodo/index.js + ``` + + **Success signal:** Output includes a block like `--- dfio_meta_main ---` with a **non-zero** USD line (magnitude varies with on-chain balances and pricing). + +5. **Optional: grep for dfio line only** + + ```bash + node test.js projects/dodo/index.js 2>&1 | grep -E 'dfio_meta_main|^total' | tail -5 + ``` + +6. **Open / track upstream PR** + + - Open PR from fork **`Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters`** → **`DefiLlama/DefiLlama-Adapters`**. + - Canonical tracking PR used in this repo: **[#19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198)**. + +7. **Verify PR state (repeat whenever user asks “is it still open?”)** + + ```bash + gh pr view 19198 --repo DefiLlama/DefiLlama-Adapters --json state,title,url + ``` + + When **`state`** becomes **`MERGED`** (or PR superseded): + + - Update **`docs/11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md`** (remove stale “verified OPEN on …” or rewrite to merged date). + - Update **`docs/04-configuration/defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md`** same. + - Update **`config/defillama-chain138-touchpoints.json`**: set **`upstreamPullRequestState`** to `merged` (or remove if PR number changes), refresh **`upstreamPullRequestStateAsOf`**. + +### 3.5 Provider matrix DeFiLlama row + +In **`docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md`**, external table **row 16 (DeFiLlama)** should: + +- Name **open upstream PR #19198** with full URL. +- Link fork **`Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters`**. +- Link **`DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md`** for `0.00` breakdown questions. +- Link **`CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md`** and **`config/defillama-chain138-touchpoints.json`**. + +--- + +## 4. Part C — Machine-readable touchpoints (`config/defillama-chain138-touchpoints.json`) + +### 4.1 Fields introduced or standardized + +Under **`upstream`**: + +| Key | Purpose | +|-----|---------| +| `upstreamPullRequest` | Canonical HTTPS URL for the DefiLlama-Adapters PR (currently **#19198**). | +| `upstreamPullRequestState` | Short lifecycle label: e.g. **`open`**, later **`merged`** — **must be updated** when GitHub state changes. | +| `upstreamPullRequestStateAsOf` | ISO date (**YYYY-MM-DD**) of last manual or scripted verification — avoids silent staleness. | +| `upstreamPullRequestLastGhCheckUtc` | Optional UTC timestamp of last `gh pr view` (or equivalent) used to confirm PR state. | + +Existing fields **`defillamaSdkChainKey`**: **`dfio_meta_main`**, **`implemented.tvlAdapter`** (protocol `dodo`, paths, factory, `fromBlock`, stablecoin mappings) remain the **single JSON source** for scripts and humans. + +### 4.2 Doc pointers (`docs` object in JSON) + +Under **`docs`** in the same JSON file: + +| Key | Path | +|-----|------| +| `ecosystemMap` | `docs/04-configuration/defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md` | +| `submissionChecklist` | `docs/04-configuration/defillama/DEFILLAMA_CHAIN138_SUBMISSION_CHECKLIST.md` | +| `dodoDfioMetaMainTvlNote` | `docs/11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md` | +| `masterReference` | `docs/00-meta/METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md` (this replay document) | +| `repositoriesAndPrs` | `docs/00-meta/REPOSITORIES_AND_PRS_CHAIN138.md` (external repos + PR status table) | + +### 4.3 Validation + +After any JSON edit: + +```bash +python3 -m json.tool config/defillama-chain138-touchpoints.json > /dev/null +``` + +--- + +## 5. Part D — MASTER_INDEX and AGENTS registration + +### 5.1 `docs/MASTER_INDEX.md` + +- **Quick links** table: includes a row for this **master reference** (alongside other 00-meta master docs). +- **MetaMask / wallet price-provider submissions** row: bundles matrix, **this master reference**, GRU/CWUSDC/EIP747 packets. +- **DefiLlama metrics ↔ Chain 138** row: references ecosystem map, TVL breakdown doc, **this master reference**, touchpoints JSON, and **#19198**. + +When adding new entry points, prefer linking **this file** once rather than duplicating long PR URLs in many tables. + +### 5.2 `AGENTS.md` + +**Quick pointers** includes **MetaMask + GRU + DefiLlama Chain 138 master replay** → path to **this file**, so IDE agents land here without searching chat history. + +--- + +## 6. Part E — Canonical external links (bookmark set) + +| Resource | URL | +|----------|-----| +| Upstream adapters PR (tracking) | https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198 | +| Upstream adapters repo | https://github.com/DefiLlama/DefiLlama-Adapters | +| Org fork | https://github.com/Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters | +| DefiLlama docs (methodology) | https://docs.llama.fi/ | + +--- + +## 7. Part F — Verification checklist (operator sign-off) + +- [ ] **`gh pr view 19198`** shows expected **`state`**; if merged, update all **2026-05-10 “OPEN”** notes and JSON **`upstreamPullRequestState`**. +- [ ] **`node test.js projects/dodo/index.js`** on fork branch: **`dfio_meta_main`** line present and non-zero (unless on-chain state truly empty). +- [ ] **`defillama.com`** DODO page: after merge + indexer lag, **`dfio_meta_main`** reflects non-zero (spot-check; not instant on merge day). +- [ ] Matrix **Money/mUSD** subsection still matches **`canonical-tokens.ts`** and Explorer cross-check for **cUSDC** / **cUSDC_V2** addresses. +- [ ] Cross-reference bullets in matrix still resolve (no moved files without redirects). + +--- + +## 8. Related documents (read order for new maintainers) + +1. This file (**replay + maintenance**), especially **Part G** (remaining tasks). +2. `docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md` — operational provider tracker + Money/mUSD narrative. +3. `docs/11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md` — TVL **`0.00`** symptom and PR linkage. +4. `docs/04-configuration/defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md` — DefiLlama ↔ Chain 138 map. +5. `docs/00-meta/REPOSITORIES_AND_PRS_CHAIN138.md` — external repos and PR table (includes DefiLlama **#19198**). +6. `config/defillama-chain138-touchpoints.json` — machine-readable constants and PR URL. +7. `docs/11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md` — canonical token list alignment (sections 5 and 8). + +--- + +--- + +## 9. Part G — Remaining tasks (rolling checklist) + +Use this section as the **authoritative “what is still left”** list for the MetaMask Money/mUSD narrative, provider cross-links, and DefiLlama DODO **`dfio_meta_main`** workstream. Update row **Status** when items close. + +| # | Task | Owner | Status | How to verify / close | +|---|------|--------|--------|------------------------| +| G1 | **Merge [DefiLlama/DefiLlama-Adapters#19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198)** (or equivalent upstream commit for DODO + `dfio_meta_main`) | DefiLlama maintainers + fork author | **OPEN** (2026-05-10) | `gh pr view 19198 --repo DefiLlama/DefiLlama-Adapters --json state` → `MERGED` | +| G2 | **Refresh repo docs after merge:** remove or rewrite “verified OPEN on 2026-05-10” lines | Doc maintainer | Pending G1 | `CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md`, `DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md`, this file §1 / §7 | +| G3 | **Update `config/defillama-chain138-touchpoints.json`:** `upstreamPullRequestState` → `merged` (or new PR URL); `upstreamPullRequestStateAsOf` | Doc maintainer | Pending G1 | JSON valid; fields match GitHub | +| G4 | **Spot-check `defillama.com` DODO** — per-chain breakdown includes priced **`dfio_meta_main`** (allow indexer lag) | Operator | Pending G1 | Public UI vs local `node test.js projects/dodo/index.js` | +| G5 | **Optional:** `defillama-server` companion PR (chain label, DODO `chains[]`, listing copy) | Operator | Optional | `DEFILLAMA_CHAIN138_SUBMISSION_CHECKLIST.md` | +| G6 | **Optional:** `dimension-adapters` fees/dex for DODO on `dfio_meta_main` | Operator | Blocked on DODO GraphQL | `CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md` blockedExternal | +| G7 | **Reconcile Money/mUSD table** with MetaMask official CAIP-19 / token-list / controller docs when published | Product + doc | Pending MetaMask | Update matrix subsection + disclaimers | +| G8 | **Keep `canonical-tokens.ts` ↔ matrix ↔ Explorer cross-check** in sync whenever hub or v2 face changes | Dev + doc | Ongoing | `EXPLORER_TOKEN_LIST_CROSSCHECK.md` §5 / §8 | +| G9 | **Continue standard ladders** (unrelated to this PR but repo hygiene) | Operator | Ongoing | `run-completable-tasks-from-anywhere.sh`; `run-all-operator-tasks-from-lan.sh` (see `OPERATOR_READY_CHECKLIST.md`) | + +### 9.1 Last completed ladder (repo hygiene) + +| Step | Command | Result (2026-05-10 ~20:00 UTC) | +|------|---------|--------------------------------| +| Completable (no LAN) | `./scripts/run-completable-tasks-from-anywhere.sh --json-out reports/status/run-completable-tasks-latest.json` | Config OK, **61/61** on-chain, `run-all-validation.sh --skip-genesis` OK, non-EVM advisory OK | +| Operator (LAN) | `./scripts/run-all-operator-tasks-from-lan.sh --skip-backup` | NPMplus **40** proxy hosts updated (**0** failed); Blockscout verification submissions OK; backup skipped | + +--- + +**Document ID:** `00-meta/METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md` +**Role:** single **master reference** for reproducing and maintaining the workstream above; supersede scattered chat-only instructions by updating **this file** when the process changes. diff --git a/docs/00-meta/REMAINING_WORK_BREAKDOWN_AND_ANSWERS.md b/docs/00-meta/REMAINING_WORK_BREAKDOWN_AND_ANSWERS.md index 4d81304d..6b7b2e32 100644 --- a/docs/00-meta/REMAINING_WORK_BREAKDOWN_AND_ANSWERS.md +++ b/docs/00-meta/REMAINING_WORK_BREAKDOWN_AND_ANSWERS.md @@ -107,7 +107,7 @@ | Question | Answer | |----------|--------| | **What is it?** | That address holds either Multicall or Oracle Aggregator; docs mention both. Need a single source of truth. | -| **Prerequisites** | Access to explorer (e.g. https://explorer.d-bis.org/address/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506). | +| **Prerequisites** | Access to explorer (e.g. https://explorer.d-bis.org/addresses/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506). | | **Who** | Operator or dev. | | **Steps to complete** | 1. Open the explorer link; check contract name/source. 2. Document in [CONTRACT_ADDRESSES_REFERENCE](../11-references/CONTRACT_ADDRESSES_REFERENCE.md) (and CONTRACT_INVENTORY if needed): “At 0x99b35... the contract is [Multicall | Oracle Aggregator].” Remove or qualify the other. | | **Where to update when done** | CONTRACT_ADDRESSES_REFERENCE § Pre-Deployed / Newly Deployed; ADDRESS_MATRIX_AND_STATUS; [REMAINING](REMAINING_COMPONENTS_TASKS_AND_RECOMMENDATIONS.md) task 19. | diff --git a/docs/00-meta/REPOSITORIES_AND_PRS_CHAIN138.md b/docs/00-meta/REPOSITORIES_AND_PRS_CHAIN138.md index 036eb6a7..62e0dd07 100644 --- a/docs/00-meta/REPOSITORIES_AND_PRS_CHAIN138.md +++ b/docs/00-meta/REPOSITORIES_AND_PRS_CHAIN138.md @@ -1,6 +1,6 @@ # Repositories and Pull Requests — Chain 138 (Forms Submitted, Awaiting Feedback) -**Last Updated:** 2026-02-28 +**Last Updated:** 2026-05-10 **Purpose:** Single reference for all repositories you can add, connect, or submit PRs to for Chain 138 support — including wallets, explorers, on-ramps/off-ramps. **Forms have been submitted where applicable; we are awaiting feedback.** Use this doc to open or connect PRs when partners respond or when you are ready to contribute. --- @@ -13,6 +13,7 @@ | **Trust Wallet** | PR to wallet-core (materials ready) | Can open PR anytime; [ADD_CHAIN138_TO_TRUST_WALLET](../04-configuration/ADD_CHAIN138_TO_TRUST_WALLET.md) | | **Consensys / MetaMask** | Outreach (contact form, business@consensys.io) | Awaiting response; no public “add chain” PR repo for Swaps/Bridge | | **CoinGecko / CMC** | Submission via platform forms (chain/token listing) | Manual submit when ready; [CMC_COINGECKO_SUBMISSION_RUNBOOK](../04-configuration/coingecko/CMC_COINGECKO_SUBMISSION_RUNBOOK.md) | +| **DefiLlama — DODO TVL (`dfio_meta_main`)** | Open upstream PR from org fork | **OPEN:** [DefiLlama/DefiLlama-Adapters#19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198) — fork [Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters](https://github.com/Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters); replay [METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md](METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md); symptom note [../11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md](../11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md) | --- @@ -91,6 +92,23 @@ Chain 138 may already be listed (see chainlist.org/chain/138). If you need to ad **Master doc:** [PRICE_FEED_CHAIN138_METAMASK_AND_WALLETS.md](../04-configuration/PRICE_FEED_CHAIN138_METAMASK_AND_WALLETS.md) — single reference for adding Chain 138 USD prices to MetaMask and wallets. +**Related (provider matrix + Money/mUSD narrative + DefiLlama TVL replay):** [METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md](METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md) — cross-links [METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md](../04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md), EIP747 / GRU / CWUSDC packets, and DefiLlama DODO **`dfio_meta_main`** upstream **#19198**. + +--- + +## 5a. DefiLlama — TVL adapters (DODO on Chain 138) + +| Item | Detail | +|------|--------| +| **Upstream PR** | [DefiLlama/DefiLlama-Adapters#19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198) — *Add dfio_meta_main (chain 138) to DODO adapter + token mappings* | +| **Status** | **OPEN** (verify on GitHub before acting; update this doc when merged) | +| **Fork** | [Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters](https://github.com/Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters) | +| **Ecosystem map** | [../04-configuration/defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md](../04-configuration/defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md) | +| **Touchpoints JSON** | [`config/defillama-chain138-touchpoints.json`](../../config/defillama-chain138-touchpoints.json) | +| **Operator replay + remaining tasks** | [METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md](METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md) **§3, §9 (Part G)** | + +**Local verify (fork clone):** `cd DefiLlama-Adapters && node test.js projects/dodo/index.js` — expect `--- dfio_meta_main ---` with non-zero TVL when adapter + pricing path is complete. + --- ## 6. CoinGecko / CoinMarketCap (submission, not PR) @@ -136,12 +154,14 @@ Most on-ramp/off-ramp providers do not accept public PRs to “add a chain”; t | **Consensys** | Use [CONSENSYS_OUTREACH_PACKAGE](../../metamask-integration/docs/CONSENSYS_OUTREACH_PACKAGE.md); await response | | **Price Feed (MetaMask/wallets)** | See [PRICE_FEED_CHAIN138_METAMASK_AND_WALLETS.md](../04-configuration/PRICE_FEED_CHAIN138_METAMASK_AND_WALLETS.md); CoinGecko, CMC, Consensys | | **CoinGecko/CMC** | Export from report API; submit via platform forms per runbook | +| **DefiLlama (DODO TVL)** | Track [DefiLlama#19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198); after merge update `config/defillama-chain138-touchpoints.json` and docs per [METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md](METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md) | | **On-ramps/off-ramps** | Contact each provider (MoonPay, Ramp, Transak, etc.) to request Chain 138; no public PR repos | --- ## See also +- [METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md](METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md) — MetaMask Money/mUSD ↔ GRU + DefiLlama DODO **`dfio_meta_main`** master replay - [ADD_CHAIN138_TO_LEDGER_LIVE.md](../04-configuration/ADD_CHAIN138_TO_LEDGER_LIVE.md) - [ADD_CHAIN138_TO_TRUST_WALLET.md](../04-configuration/ADD_CHAIN138_TO_TRUST_WALLET.md) - [WHATS_LEFT_OPERATOR_AND_EXTERNAL.md](WHATS_LEFT_OPERATOR_AND_EXTERNAL.md) diff --git a/docs/00-meta/REQUIRED_FIXES_GAPS_AND_DEPLOYMENTS_LIST.md b/docs/00-meta/REQUIRED_FIXES_GAPS_AND_DEPLOYMENTS_LIST.md index 8123a131..d59dbb9f 100644 --- a/docs/00-meta/REQUIRED_FIXES_GAPS_AND_DEPLOYMENTS_LIST.md +++ b/docs/00-meta/REQUIRED_FIXES_GAPS_AND_DEPLOYMENTS_LIST.md @@ -1,6 +1,6 @@ # Required Fixes, Gaps, and Additional Deployments — Master List -> Historical note (2026-03-26): this master list contains older PMM verification snapshots. The current canonical Chain 138 PMM stack is `DODOPMMIntegration=0x5BDc62f1ae7D630c37A8B363a1d49845356Ee72d` and `DODOPMMProvider=0x5CAe6Ce155b7f08D3a956F5Dc82fC9945f29B381`. +> Historical note (2026-05-10): the **live, traded** Chain 138 PMM stack is `DODOPMMIntegration=0x86ADA6Ef91A3B450F89f2b751e93B1b7A3218895` and `DODOPMMProvider=0x3f729632E9553EBacCdE2e9b4c8F2B285b014F2e`. A parallel deployment (`0x5BDc62f1…` / `0x5CAe6Ce1…`) has seeded but un-traded pools — do not treat it as canonical. Source: [ADDRESS_MATRIX_AND_STATUS.md](../11-references/ADDRESS_MATRIX_AND_STATUS.md), [EXPLORER_TOKEN_LIST_CROSSCHECK.md](../11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md). **Last Updated:** 2026-03-04 **Purpose:** Single consolidated list of all required fixes, gaps, and additional deployments. Sources: REQUIRED_FIXES_AND_DEPLOYMENTS_STATUS, REMAINING_SUMMARY, TOKEN_CONTRACT_DEPLOYMENTS_REMAINING, PRE_DEPLOYMENT_CHECKLIST, RECOMMENDATIONS_AND_FIXES_BEFORE_DEPLOY, DETAILED_GAPS_AND_ISSUES_LIST, GAPS_STATUS, WHATS_LEFT_OPERATOR_AND_EXTERNAL, and token-aggregation build. diff --git a/docs/00-meta/STEPS_FROM_PROXMOX_OR_LAN_WITH_SECRETS.md b/docs/00-meta/STEPS_FROM_PROXMOX_OR_LAN_WITH_SECRETS.md index 8457e6a1..22b902c2 100644 --- a/docs/00-meta/STEPS_FROM_PROXMOX_OR_LAN_WITH_SECRETS.md +++ b/docs/00-meta/STEPS_FROM_PROXMOX_OR_LAN_WITH_SECRETS.md @@ -70,7 +70,7 @@ Requires: SSH to Proxmox host (e.g. `root@192.168.11.10` or r630-01/r630-02). Se | DBIS Core containers (6) | `./dbis_core/scripts/deployment/create-dbis-core-containers.sh` | Creates 10100, 10101, 10120, 10150, 10151, 10130 on PROXMOX_HOST. | | Create missing RPC containers | `./scripts/create-missing-containers-2506-2508.sh` (if VMIDs 2506–2508 needed) | Per VMID allocation. | | Chain 138 containers | `./scripts/create-chain138-containers.sh` (if defined) | Besu/RPC/explorer as per docs. | -| Spread VMs across nodes | Prefer r630-01 / r630-02 for new VMs to balance load; ml110 already has 34 containers | See PROXMOX_COMPLETE_RECOMMENDATIONS. | +| Spread VMs across nodes | Prefer balancing across **r630-01** … **r630-04**; **ml110** currently has **0** guests but stays low-spec — see [PROXMOX_CLUSTER_ARCHITECTURE.md](../02-architecture/PROXMOX_CLUSTER_ARCHITECTURE.md) | See PROXMOX_COMPLETE_RECOMMENDATIONS. | --- @@ -111,7 +111,7 @@ From repo root, with `smom-dbis-138/.env` and RPC reachable: |------|--------|--------| | Reconcile .env | `./scripts/verify/reconcile-env-canonical.sh --print` | Emit canonical lines; merge into smom-dbis-138/.env. | | Update CONTRACT_ADDRESSES_REFERENCE and master JSON | Add any new addresses to `config/smart-contracts-master.json` and [CONTRACT_ADDRESSES_REFERENCE](../11-references/CONTRACT_ADDRESSES_REFERENCE.md) | Keep CONTRACT_INVENTORY in sync. | -| Verify on explorer | Open https://explorer.d-bis.org/address/ for new contracts | Confirm bytecode and verification. | +| Verify on explorer | Open https://explorer.d-bis.org/addresses/ for new contracts | Confirm bytecode and verification. | --- diff --git a/docs/02-architecture/PROXMOX_CLUSTER_ARCHITECTURE.md b/docs/02-architecture/PROXMOX_CLUSTER_ARCHITECTURE.md index 0091acb9..b7fb0f44 100644 --- a/docs/02-architecture/PROXMOX_CLUSTER_ARCHITECTURE.md +++ b/docs/02-architecture/PROXMOX_CLUSTER_ARCHITECTURE.md @@ -1,14 +1,23 @@ # Proxmox Cluster Architecture -**Last Updated:** 2025-01-20 -**Document Version:** 1.0 -**Status:** Active Documentation +**Last Updated:** 2026-05-09 +**Document Version:** 1.1 +**Status:** Active Documentation + +**Live reconcile:** `pvesh get /nodes`, `/cluster/resources`, `pvecm status`, `pvesm status` via SSH to cluster members (LAN). Physical DIMM totals may exceed **cluster-reported `maxmem`** on some nodes; runtime figures below follow the API. --- ## Overview -This document describes the Proxmox cluster architecture, including node configuration, storage setup, network bridges, and VM/container distribution. +This document summarizes the **five-node** Proxmox VE cluster (**name `h`**), storage footprint, and **LXC/QEMU placement**. Authoritative VMID/IP/FQDN detail remains in [`../04-configuration/ALL_VMIDS_ENDPOINTS.md`](../04-configuration/ALL_VMIDS_ENDPOINTS.md). + +| Item | Live value (2026-05-09) | +|------|-------------------------| +| **Proxmox VE** | `pve-manager/9.1.7` (kernel **6.17.13-2-pve** on all nodes) | +| **Quorum** | **Quorate** — 5 votes, expected 5 | +| **Guests (running)** | **136** LXC + QEMU cluster-wide | +| **Guests on ml110** | **0** (workloads run on **r630-01** … **r630-04**) | --- @@ -17,82 +26,70 @@ This document describes the Proxmox cluster architecture, including node configu ```mermaid graph TB Cluster[Proxmox Cluster
Name: h] - - ML110[ML110 Management Node
192.168.11.10
6 cores, 125GB RAM] - R6301[R630-01
192.168.11.11
32 cores, 503GB RAM] - R6302[R630-02
192.168.11.12
32 cores, 503GB RAM] - R6303[R630-03
192.168.11.13
32 cores, 512GB RAM] - R6304[R630-04
192.168.11.14
32 cores, 512GB RAM] - + + ML110[ML110
192.168.11.10
6 cores · ~63 GiB reported RAM
0 guests] + R6301[r630-01
192.168.11.11
32 cores · ~126 GiB RAM
57 guests] + R6302[r630-02
192.168.11.12
56 cores · ~126 GiB RAM
41 guests] + R6303[r630-03
192.168.11.13
32 cores · ~126 GiB RAM
19 guests] + R6304[r630-04
192.168.11.14
32 cores · ~126 GiB RAM
19 guests] + Cluster --> ML110 Cluster --> R6301 Cluster --> R6302 Cluster --> R6303 Cluster --> R6304 - - ML110 --> Storage1[local: 94GB
local-lvm: 813GB] - R6301 --> Storage2[local: 536GB
local-lvm: Available] - R6302 --> Storage3[local: Available
local-lvm: Available] - R6303 --> Storage4[Storage: Available] - R6304 --> Storage5[Storage: Available] - - ML110 --> Bridge1[vmbr0
VLAN-aware] - R6301 --> Bridge2[vmbr0
VLAN-aware] - R6302 --> Bridge3[vmbr0
VLAN-aware] - R6303 --> Bridge4[vmbr0
VLAN-aware] - R6304 --> Bridge5[vmbr0
VLAN-aware] + + ML110 --> S0[Thin pools ~1.7TB data — nearly empty] + R6301 --> S1[local + thin1 + data — high utilization] + R6302 --> S2[thin1-r630-02 … thin6 — active] + R6303 --> S3[data + thin*-r630-03 pools] + R6304 --> S4[data + mev-local-lvm] ``` --- ## Cluster Nodes -### Node Summary +### Node summary -| Hostname | IP Address | CPU | RAM | Storage | VMs/Containers | Status | -|----------|------------|-----|-----|---------|----------------|--------| -| ml110 | 192.168.11.10 | 6 cores @ 1.60GHz | 125GB | local (94GB), local-lvm (813GB) | 34 | ✅ Active | -| r630-01 | 192.168.11.11 | 32 cores @ 2.40GHz | 503GB | local (536GB), local-lvm (available) | 0 | ✅ Active | -| r630-02 | 192.168.11.12 | 32 cores @ 2.40GHz | 503GB | local (available), local-lvm (available) | 0 | ✅ Active | -| r630-03 | 192.168.11.13 | 32 cores | 512GB | Available | 0 | ✅ Active | -| r630-04 | 192.168.11.14 | 32 cores | 512GB | Available | 0 | ✅ Active | +| Hostname | IP Address | CPU | RAM (API) | Cluster disk metric¹ | LXC/QEMU | Status | +|----------|------------|-----|-----------|----------------------|----------|--------| +| ml110 | 192.168.11.10 | 6 @ 1.60GHz | ~63 GiB max | ~94 GiB | **0** | online | +| r630-01 | 192.168.11.11 | 32 @ 2.40GHz | ~126 GiB max | ~353 GiB | **57** | online | +| r630-02 | 192.168.11.12 | 56 @ 2.00GHz | ~126 GiB max | ~196 GiB | **41** | online | +| r630-03 | 192.168.11.13 | 32 cores | ~126 GiB max | ~94 GiB | **19** | online | +| r630-04 | 192.168.11.14 | 32 cores | ~126 GiB max | ~78 GiB | **19** | online | + +¹ **`maxdisk`** from `/cluster/resources` node entries — useful for relative sizing; per-pool detail is on each node (`pvesm status`). + +### Memory (cluster API) + +| Node | Used (approx.) | Total (reported) | Usage % | +|------|----------------|------------------|---------| +| ml110 | 2 GiB | 62 GiB | ~3.5% | +| r630-01 | 83 GiB | 125 GiB | ~66% | +| r630-02 | 77 GiB | 125 GiB | ~62% | +| r630-03 | 77 GiB | 125 GiB | ~62% | +| r630-04 | 21 GiB | 125 GiB | ~17% | --- ## Storage Configuration -### Storage Types +### Types -**local (Directory Storage):** -- Type: Directory-based storage -- Used for: ISO images, container templates, backups -- Location: `/var/lib/vz` +- **`local`:** directory (`/var/lib/vz`) — ISOs, templates, backups +- **`local-lvm` / `data` / `thin*`:** LVM-thin guest disks (node-specific names) -**local-lvm (LVM Thin Storage):** -- Type: LVM thin provisioning -- Used for: VM/container disk images -- Benefits: Thin provisioning, snapshots, efficient space usage +### Snapshot by host (`pvesm status`, 2026-05-09) -### Storage by Node +- **ml110:** `data` / `local-lvm` thin pools ~1.7TB each — **~0.03%** used; **`local`** ~77 GiB free of ~94 GiB. +- **r630-01:** **`thin1`** and **`data`**/**`local-lvm`** — **~60–77%** used on thin pools; **`local`** ~345 GiB free of ~532 GiB. +- **r630-02:** **`thin1-r630-02`** … **`thin6`** — active; utilization **~4–58%** per pool (varies by pool); **`local`** ~190 GiB free. +- **r630-03:** **`data`**/**`local-lvm`** ~31% used; **`thin1-r630-03`** … **`thin6-r630-03`** — several active (some **0%** used). +- **r630-04:** **`data`**/**`local-lvm`** ~36% used; **`mev-local-lvm`** ~7% used. -**ml110:** -- `local`: 94GB total, 7.4GB used (7.87%) -- `local-lvm`: 813GB total, 214GB used (26.29%) -- Status: ✅ Active and operational - -**r630-01:** -- `local`: 536GB total, 0% used -- `local-lvm`: Available (needs activation) -- Status: ⏳ Storage available, ready for use - -**r630-02:** -- `local`: Available -- `local-lvm`: Available (needs activation) -- Status: ⏳ Storage available, ready for use - -**r630-03/r630-04:** -- Storage: Available -- Status: ⏳ Ready for configuration +**Note:** Disabled pool stubs (e.g. foreign node names) may still appear in `pvesm` output; rely on **active** rows for capacity planning. --- @@ -115,136 +112,52 @@ iface vmbr0 inet static bridge-vids 11 110 111 112 120 121 130 132 133 134 140 141 150 160 200 201 202 203 ``` -**Bridge Features:** -- **VLAN-aware:** Supports multiple VLANs on single bridge -- **Native VLAN:** 11 (MGMT-LAN) -- **Tagged VLANs:** All service VLANs (110-203) -- **802.1Q Trunking:** Enabled for VLAN support +**Bridge features:** VLAN-aware; native VLAN 11 (MGMT-LAN); tagged service VLANs per [`NETWORK_ARCHITECTURE.md`](NETWORK_ARCHITECTURE.md). --- ## VM/Container Distribution -### Current Distribution +| Node | Running guests | Notes | +|------|----------------|-------| +| ml110 | **0** | Cluster member; no LXC/QEMU registered on this node in API | +| r630-01 | **57** | Highest guest count | +| r630-02 | **41** | Includes NPMplus, infra CTs per inventory docs | +| r630-03 | **19** | Besu / RPC capacity | +| r630-04 | **19** | Besu / Gov portals / additional workloads | -**ml110 (192.168.11.10):** -- **Total:** 34 containers/VMs -- **Services:** All current services running here -- **Breakdown:** - - Besu validators: 5 (VMIDs 1000-1004) - - Besu sentries: 4 (VMIDs 1500-1503) - - Besu RPC: 3+ (VMIDs 2500-2502+) - - Blockscout: 1 (VMID 5000) - - DBIS services: Multiple - - Other services: Various - -**r630-01, r630-02, r630-03, r630-04:** -- **Total:** 0 containers/VMs -- **Status:** Ready for VM migration/deployment +For VMID → IP → node mapping, use [`../04-configuration/ALL_VMIDS_ENDPOINTS.md`](../04-configuration/ALL_VMIDS_ENDPOINTS.md) and `scripts/verify/check-cluster-besu-inventory.sh`. --- ## High Availability -### Current Setup - -- **Cluster Name:** "h" -- **HA Mode:** Active/Standby (manual) -- **Quorum:** 3+ nodes required for quorum -- **Storage:** Local storage (not shared) - -### HA Considerations - -**Current Limitations:** -- No shared storage (each node has local storage) -- Manual VM migration required -- No automatic failover - -**Future Enhancements:** -- Consider shared storage (NFS, Ceph, etc.) for true HA -- Implement automatic VM migration -- Configure HA groups for critical services +- **Cluster:** corosync **quorate** (5 nodes). +- **Storage:** primarily **local / node-local thin pools** — plan migrations explicitly; no cluster-wide shared disk assumed here. +- **HA groups:** configure in UI/API if automatic failover is required for specific VMIDs. --- -## Resource Allocation +## Storage Recommendations (planning) -### CPU Resources - -| Node | CPU Cores | CPU Usage | Available | -|------|-----------|-----------|-----------| -| ml110 | 6 @ 1.60GHz | High | Limited | -| r630-01 | 32 @ 2.40GHz | Low | Excellent | -| r630-02 | 32 @ 2.40GHz | Low | Excellent | -| r630-03 | 32 cores | Low | Excellent | -| r630-04 | 32 cores | Low | Excellent | - -### Memory Resources - -| Node | Total RAM | Used | Available | Usage % | -|------|-----------|------|-----------|---------| -| ml110 | 125GB | 94GB | 31GB | 75% ⚠️ | -| r630-01 | 503GB | ~5GB | ~498GB | 1% ✅ | -| r630-02 | 503GB | ~5GB | ~498GB | 1% ✅ | -| r630-03 | 512GB | Low | High | Low ✅ | -| r630-04 | 512GB | Low | High | Low ✅ | - ---- - -## Storage Recommendations - -### For R630 Nodes - -**Boot Drives (2×600GB):** -- **Recommended:** ZFS mirror or hardware RAID1 -- **Purpose:** Proxmox OS and boot files -- **Benefits:** Redundancy, data integrity - -**Data SSDs (6×250GB):** -- **Option 1:** ZFS striped mirrors (3 pairs) - - Capacity: ~750GB usable - - Performance: High - - Redundancy: Good - -- **Option 2:** ZFS RAIDZ1 (5 drives + 1 parity) - - Capacity: ~1.25TB usable - - Performance: Good - - Redundancy: Single drive failure tolerance - -- **Option 3:** ZFS RAIDZ2 (4 drives + 2 parity) - - Capacity: ~1TB usable - - Performance: Good - - Redundancy: Dual drive failure tolerance +Boot vs data disk layout remains node-specific. For new pools, follow thin provisioning discipline and monitor **`thin`** pool **%** on **r630-01** and **r630-02** (historically stressed pools during explorer incidents). --- ## Network Recommendations -### VLAN Configuration - -**All Proxmox hosts should:** -- Use VLAN-aware bridge (vmbr0) -- Support all 19 VLANs -- Maintain native VLAN 11 for management -- Enable 802.1Q trunking on physical interfaces - -### Network Performance - -- **Link Speed:** Ensure 1Gbps or higher for trunk ports -- **Jumbo Frames:** Consider enabling if supported -- **Bonding:** Consider link aggregation for redundancy +- Trunk links at **1 Gbps+**; **`r630-02`** NICs support **10 Gbps** where the switch allows. +- See **[NETWORK_ARCHITECTURE.md](NETWORK_ARCHITECTURE.md)** for VLAN inventory. --- ## Related Documentation -- **[NETWORK_ARCHITECTURE.md](NETWORK_ARCHITECTURE.md)** ⭐⭐⭐ - Network architecture with VLAN plan -- **[PHYSICAL_HARDWARE_INVENTORY.md](PHYSICAL_HARDWARE_INVENTORY.md)** ⭐⭐⭐ - Physical hardware inventory -- **[PROXMOX_COMPREHENSIVE_REVIEW.md](PROXMOX_COMPREHENSIVE_REVIEW.md)** ⭐⭐ - Comprehensive Proxmox review -- **[ORCHESTRATION_DEPLOYMENT_GUIDE.md](ORCHESTRATION_DEPLOYMENT_GUIDE.md)** ⭐⭐⭐ - Deployment orchestration +- **[NETWORK_ARCHITECTURE.md](NETWORK_ARCHITECTURE.md)** — VLAN plan +- **[PROXMOX_HOSTS_COMPLETE_HARDWARE_CONFIG.md](PROXMOX_HOSTS_COMPLETE_HARDWARE_CONFIG.md)** — DIMMs, disks, NIC models (physical) +- **[PROXMOX_COMPREHENSIVE_REVIEW.md](PROXMOX_COMPREHENSIVE_REVIEW.md)** — historical deep-dive (**partially superseded** by this file for placement counts) +- **[../03-deployment/PROXMOX_VM_CREATION_RUNBOOK.md](../03-deployment/PROXMOX_VM_CREATION_RUNBOOK.md)** — where to create **new** CTs/VMs --- -**Last Updated:** 2025-01-20 -**Document Version:** 1.0 -**Review Cycle:** Quarterly +**Review cycle:** Quarterly or after major migrations. diff --git a/docs/02-architecture/PROXMOX_COMPREHENSIVE_REVIEW.md b/docs/02-architecture/PROXMOX_COMPREHENSIVE_REVIEW.md index 34c330fc..2d9c9b9c 100644 --- a/docs/02-architecture/PROXMOX_COMPREHENSIVE_REVIEW.md +++ b/docs/02-architecture/PROXMOX_COMPREHENSIVE_REVIEW.md @@ -1,6 +1,8 @@ # Proxmox VE Comprehensive Configuration Review -**Last Updated:** 2025-01-20 +**Supersession:** Placement counts, storage enablement, and guest distribution in much of this document reflect **2025** cluster state. **Current** cluster topology (guests per node, PVE version, memory use) is maintained in **[PROXMOX_CLUSTER_ARCHITECTURE.md](PROXMOX_CLUSTER_ARCHITECTURE.md)** (reconcile **2026-05-09**). Retain this file for historical procedure (hostname migration, IP audit methodology). + +**Last Updated:** 2026-05-09 **Document Version:** 1.0 **Status:** Active Documentation @@ -14,10 +16,9 @@ - [x] Proxmox services verified (all operational) - [x] Storage configuration reviewed -### ⚠️ Issues Identified -- r630-01 and r630-02 have LVM thin storage **disabled** -- All VMs/containers currently on ml110 only -- Storage not optimized for performance on r630-01/r630-02 +### ⚠️ Issues Identified (historical snapshot — verify against live docs) + +- Older notes cited **r630-01**/**02** LVM disabled and **all guests on ml110** — **obsolete**: guest placement has migrated; see [PROXMOX_CLUSTER_ARCHITECTURE.md](PROXMOX_CLUSTER_ARCHITECTURE.md). --- @@ -43,15 +44,8 @@ ssh root@192.168.11.12 "hostname" # Returns: r630-02 ✅ ## IP Address Audit - COMPLETE ✅ ### Results -- **Total VMs/Containers:** 34 with static IPs -- **IP Conflicts:** 0 ✅ -- **Invalid IPs:** 0 ✅ -- **DHCP IPs:** 2 (VMIDs 3500, 3501) - -### All VMs Currently On -- **ml110** (192.168.11.10): All 34 VMs/containers -- **r630-01** (192.168.11.11): 0 VMs/containers -- **r630-02** (192.168.11.12): 0 VMs/containers +- **Total VMs/Containers (historical IP audit):** 34 with static IPs — **superseded**; live cluster **136** guests (not on ml110 only). See [PROXMOX_CLUSTER_ARCHITECTURE.md](PROXMOX_CLUSTER_ARCHITECTURE.md). +- **ml110** (192.168.11.10): **0** cluster guests (2026-05-09); table below is historical ### IP Allocation Summary | IP Range | Count | Purpose | @@ -80,85 +74,46 @@ ssh root@192.168.11.12 "hostname" # Returns: r630-02 ✅ | Property | Value | Status | |----------|-------|--------| | **Hostname** | ml110 | ✅ Correct | -| **Proxmox Version** | 9.1.0 (kernel 6.17.4-1-pve) | ✅ Current | +| **Proxmox Version** | 9.1.7 (kernel 6.17.13-2-pve) | ✅ Current | | **CPU** | Intel Xeon E5-2603 v3 @ 1.60GHz (6 cores) | ⚠️ Older, slower | -| **Memory** | 125GB total, 94GB used, 31GB available | ⚠️ High usage | -| **Storage - local** | 94GB total, 7.4GB used (7.87%) | ✅ Good | -| **Storage - local-lvm** | 813GB total, 214GB used (26.29%) | ✅ Active | -| **VMs/Containers** | 34 total | ✅ All here | +| **Memory** | ~63 GiB reported (`maxmem`); ~3.5% used (API **2026-05-09**) | ✅ Low vs historical 75% narrative | +| **Storage - local** | ~94 GiB dir — see `pvesm` | ✅ | +| **Storage - local-lvm / data** | Thin pools ~1.8TB — nearly empty | ✅ | +| **VMs/Containers** | **0** on node (**2026-05-09**) | ✅ | -**Storage Details:** -- `local`: Directory storage, active, 94GB available -- `local-lvm`: LVM thin, active, 600GB available -- `thin1-thin6`: Configured but disabled (not in use) +**Storage Details (live snapshot — use `pvesm status` on host):** +- **`local`:** directory ISO/template store (~94 GiB). +- **`data` / `local-lvm`:** shared thin pool name — **~0.03%** used on **2026-05-09** (nearly empty). **Recommendations:** -- ⚠️ **CPU is older/slower** - Consider workload distribution -- ⚠️ **Memory usage high (75%)** - Monitor closely -- ✅ **Storage well configured** - LVM thin active and working +- **CPU** remains weaker than **r630-*** — bias new workloads to Dell nodes unless intentional. +- **Memory pressure narrative superseded** — API shows **~3.5%** used (**2026-05-09**). ### r630-01 (192.168.11.11) - Previously "pve" -| Property | Value | Status | -|----------|-------|--------| -| **Hostname** | r630-01 | ✅ Migrated | -| **Proxmox Version** | 9.1.0 (kernel 6.17.4-1-pve) | ✅ Current | -| **CPU** | Intel Xeon E5-2630 v3 @ 2.40GHz (32 cores) | ✅ Good | -| **Memory** | 503GB total, 6.4GB used, 497GB available | ✅ Excellent | -| **Storage - local** | 536GB total, 0.1GB used (0.00%) | ✅ Available | -| **Storage - local-lvm** | **DISABLED** | ⚠️ **Issue** | -| **Storage - thin1-thin6** | **DISABLED** | ⚠️ **Issue** | -| **VMs/Containers** | 0 | ⏳ Ready for deployment | +> **Current operations:** See [PROXMOX_CLUSTER_ARCHITECTURE.md](PROXMOX_CLUSTER_ARCHITECTURE.md) — **57** guests, **~66%** memory use, **thin1**/**data** pools in active use. The table below is a **2025** snapshot; do not use for capacity planning. -**Storage Details:** -- **Volume Group:** `pve` exists with 2 physical volumes -- **Thin Pools:** `data` (200GB) and `thin1` (208GB) exist -- **Disks:** 4 disks (sda, sdb: 558GB each; sdc, sdd: 232GB each) -- **LVM Setup:** Properly configured -- **Storage Config Issue:** Storage configured but node references point to "pve" (old hostname) or "pve2" - -**Issues:** -- ⚠️ **Storage configured but node references outdated** - Points to "pve" instead of "r630-01" -- ⚠️ **Storage may show as disabled** - Due to hostname mismatch in config -- ⚠️ **Need to update storage.cfg** - Update node references to r630-01 - -**Recommendations:** -- 🔴 **CRITICAL:** Enable local-lvm storage to use existing LVM thin pools -- 🔴 **CRITICAL:** Activate thin1 storage for better performance -- ✅ **Ready for VMs** - Excellent resources available +| Property | Value (historical) | Status | +|----------|--------------------|--------| +| **Proxmox Version** | 9.1.7 (kernel 6.17.13-2-pve) | ✅ Current build | +| **VMs/Containers** | 0 in 2025 table | **Superseded** — see cluster doc | ### r630-02 (192.168.11.12) - Previously "pve2" -| Property | Value | Status | -|----------|-------|--------| -| **Hostname** | r630-02 | ✅ Migrated | -| **Proxmox Version** | 9.1.0 (kernel 6.17.4-1-pve) | ✅ Current | -| **CPU** | Intel Xeon E5-2660 v4 @ 2.00GHz (56 cores) | ✅ Excellent | -| **Memory** | 251GB total, 4.4GB used, 247GB available | ✅ Excellent | -| **Storage - local** | 220GB total, 0.1GB used (0.06%) | ✅ Available | -| **Storage - local-lvm** | **DISABLED** | ⚠️ **Issue** | -| **Storage - thin1-thin6** | **DISABLED** | ⚠️ **Issue** | -| **VMs/Containers** | 0 | ⏳ Ready for deployment | +> **Current operations:** See [PROXMOX_CLUSTER_ARCHITECTURE.md](PROXMOX_CLUSTER_ARCHITECTURE.md) — **41** guests; **`thin1-r630-02` … `thin6`** pools active. Historical “all disabled” narrative below is **obsolete**. -**Storage Details:** -- Need to check LVM configuration (command timed out) -- Storage shows as disabled in Proxmox - -**Issues:** -- ⚠️ **Storage configured but node references outdated** - Points to "pve2" instead of "r630-02" -- ⚠️ **VMs already exist on storage** - Need to verify they're accessible -- ⚠️ **Need to update storage.cfg** - Update node references to r630-02 - -**Recommendations:** -- 🔴 **CRITICAL:** Check and configure LVM storage -- 🔴 **CRITICAL:** Enable local-lvm or thin storage -- ✅ **Ready for VMs** - Excellent resources available +| Property | Value (historical) | Status | +|----------|----------------------|--------| +| **Proxmox Version** | 9.1.7 (kernel 6.17.13-2-pve) | ✅ Current build | +| **VMs/Containers** | 0 in 2025 table | **Superseded** — see cluster doc | --- ## Storage Configuration Analysis -### Current Storage Status +> **Migration-era inventory:** Tables and `sed` recipes in this section date from hostname/storage fixes. **Confirm with `pvesm status` on each host** before changing `/etc/pve/storage.cfg`. Live snapshot: **2026-05-09**. + +### Current Storage Status (historical UI export — audit only) | Host | Storage Type | Status | Size | Usage | Recommendation | |------|--------------|--------|------|-------|----------------| @@ -224,7 +179,7 @@ done ### 2. Distribute VMs Across Hosts ⚠️ RECOMMENDED -**Current State:** All 34 VMs on ml110 (overloaded) +**Current State (superseded narrative):** Guests are distributed across **r630-01** … **r630-04** (**136** running, **2026-05-09**). **Recommendation:** - Migrate some VMs to r630-01 and r630-02 @@ -478,6 +433,6 @@ pvecm nodes --- -**Last Updated:** 2025-01-20 +**Last Updated:** 2026-05-09 **Document Version:** 1.0 **Review Cycle:** Quarterly diff --git a/docs/02-architecture/PROXMOX_HOSTS_COMPLETE_HARDWARE_CONFIG.md b/docs/02-architecture/PROXMOX_HOSTS_COMPLETE_HARDWARE_CONFIG.md index bcc2061d..39647df3 100644 --- a/docs/02-architecture/PROXMOX_HOSTS_COMPLETE_HARDWARE_CONFIG.md +++ b/docs/02-architecture/PROXMOX_HOSTS_COMPLETE_HARDWARE_CONFIG.md @@ -1,6 +1,6 @@ # Proxmox Hosts — Complete Hardware Configuration -**Last Updated:** 2026-02-13 +**Last Updated:** 2026-05-09 **Document Version:** 1.0 **Status:** Active Documentation **Source:** Live query from hosts via `lspci`, `ethtool`, `ip link` @@ -9,7 +9,7 @@ ## Overview -Complete hardware specifications for the three Proxmox VE hosts, including CPU, memory, storage, and **all NIC models** with interface mapping and link status. +Complete hardware specifications for **three** Proxmox VE hosts (detailed NIC inventory). The **five-node** cluster also includes **r630-03** and **r630-04** — see [PROXMOX_CLUSTER_ARCHITECTURE.md](PROXMOX_CLUSTER_ARCHITECTURE.md) for live counts. --- @@ -19,10 +19,10 @@ Complete hardware specifications for the three Proxmox VE hosts, including CPU, |-----------|---------------| | **Manufacturer** | HP | | **Model** | ProLiant ML110 Gen9 | -| **Proxmox** | 9.1.0, kernel 6.17.4-1-pve | +| **Proxmox** | 9.1.7, kernel 6.17.13-2-pve | | **CPU** | Intel Xeon E5-2603 v3 @ 1.60 GHz | | **Cores** | 6 (1 socket × 6 cores) | -| **RAM** | 125 GiB | +| **RAM** | 125 GiB installed (cluster API **~63 GiB** `maxmem` for scheduling — see [PROXMOX_CLUSTER_ARCHITECTURE.md](PROXMOX_CLUSTER_ARCHITECTURE.md)) | | **Storage** | 2× ST1000DM003-1ER162 (931.5 GB 7.2k HDD each) | | **Storage layout** | LVM-thin (data, local-lvm) + local dir | | **Network** | vmbr0 bridge | @@ -45,10 +45,10 @@ Complete hardware specifications for the three Proxmox VE hosts, including CPU, |-----------|---------------| | **Manufacturer** | Dell Inc. | | **Model** | PowerEdge R630 | -| **Proxmox** | 9.1.0, kernel 6.17.4-1-pve | +| **Proxmox** | 9.1.7, kernel 6.17.13-2-pve | | **CPU** | Intel Xeon E5-2630 v3 @ 2.40 GHz | | **Cores** | 32 (2 sockets × 8 cores) | -| **RAM** | 503 GiB | +| **RAM** | ~126 GiB (`maxmem` API **2026-05-09**); prior builds cited different installed totals — verify DIMMs if needed | | **Storage** | 2× HUC109060CSS600 (559 GB enterprise SSD boot) + 6× Crucial CT250MX500SSD1 (233 GB SATA SSD) + DVD-ROM | | **Storage layout** | LVM-thin (data, local-lvm, thin1) + local dir | | **Network** | vmbr0 bridge | @@ -73,10 +73,10 @@ Complete hardware specifications for the three Proxmox VE hosts, including CPU, |-----------|---------------| | **Manufacturer** | Dell Inc. | | **Model** | PowerEdge R630 | -| **Proxmox** | 9.1.0, kernel 6.17.4-1-pve | +| **Proxmox** | 9.1.7, kernel 6.17.13-2-pve | | **CPU** | Intel Xeon E5-2660 v4 @ 2.00 GHz | | **Cores** | 56 (2 sockets × 14 cores) | -| **RAM** | 251 GiB | +| **RAM** | ~126 GiB (`maxmem` API **2026-05-09**); prior doc 251 GiB — reconcile with DIMM audit | | **Storage** | 8× Crucial CT250MX500SSD1 (233 GB SATA SSD each) | | **Storage layout** | LVM-thin (thin1-r630-02, thin2–thin6) + local dir | | **Network** | vmbr0 bridge (VLAN-aware) | diff --git a/docs/03-deployment/BLOCKSCOUT_FIX_RUNBOOK.md b/docs/03-deployment/BLOCKSCOUT_FIX_RUNBOOK.md index c480640b..03eb407a 100644 --- a/docs/03-deployment/BLOCKSCOUT_FIX_RUNBOOK.md +++ b/docs/03-deployment/BLOCKSCOUT_FIX_RUNBOOK.md @@ -139,7 +139,7 @@ BLOCKSCOUT_URL=http://192.168.11.140:4000 node forge-verification-proxy/server.j ### Fallbacks - **Nginx fix:** `./scripts/fix-blockscout-forge-verification.sh` then retry (may still fail due to API format) -- **Manual verification:** https://explorer.d-bis.org/address/#verify-contract +- **Manual verification:** https://explorer.d-bis.org/addresses/#verify-contract --- diff --git a/docs/03-deployment/CONTRACT_DEPLOYMENT_RUNBOOK.md b/docs/03-deployment/CONTRACT_DEPLOYMENT_RUNBOOK.md index 304a24f8..4c39bca5 100644 --- a/docs/03-deployment/CONTRACT_DEPLOYMENT_RUNBOOK.md +++ b/docs/03-deployment/CONTRACT_DEPLOYMENT_RUNBOOK.md @@ -342,7 +342,7 @@ BLOCKSCOUT_URL=http://192.168.11.140:4000 node forge-verification-proxy/server.j ./scripts/verify-contracts-blockscout.sh ``` -**See:** [forge-verification-proxy/README.md](../../forge-verification-proxy/README.md), [BLOCKSCOUT_FORGE_VERIFICATION_EVALUATION.md](BLOCKSCOUT_FORGE_VERIFICATION_EVALUATION.md). Fallback: manual verification at https://explorer.d-bis.org/address/#verify-contract +**See:** [forge-verification-proxy/README.md](../../forge-verification-proxy/README.md), [BLOCKSCOUT_FORGE_VERIFICATION_EVALUATION.md](BLOCKSCOUT_FORGE_VERIFICATION_EVALUATION.md). Fallback: manual verification at https://explorer.d-bis.org/addresses/#verify-contract **Runbooks in sync (R12):** [BLOCKSCOUT_FIX_RUNBOOK](BLOCKSCOUT_FIX_RUNBOOK.md), [BLOCKSCOUT_FORGE_VERIFICATION_EVALUATION](BLOCKSCOUT_FORGE_VERIFICATION_EVALUATION.md), this runbook. **Full recommendations (R1–R24):** [RECOMMENDATIONS_OPERATOR_CHECKLIST](../00-meta/RECOMMENDATIONS_OPERATOR_CHECKLIST.md). diff --git a/docs/03-deployment/DBIS_ENGINE_X_AUTOMATED_LIQUIDITY_ADVISOR.md b/docs/03-deployment/DBIS_ENGINE_X_AUTOMATED_LIQUIDITY_ADVISOR.md new file mode 100644 index 00000000..b9ad950d --- /dev/null +++ b/docs/03-deployment/DBIS_ENGINE_X_AUTOMATED_LIQUIDITY_ADVISOR.md @@ -0,0 +1,109 @@ +# DBIS Engine X Automated Liquidity Advisor + +**Status:** repo-side advisor implemented; live autonomous execution remains gated. + +## Purpose + +Engine X needs one control surface that can accept API feeds, advise on liquidity positions, calculate the requested `cW*` output, evaluate available `XAUt` collateral, and only then promote the request into protected on-chain execution plus endpoint reporting. + +This is implemented as a read-only planner first: + +```bash +pnpm engine-x:automation-advisor +``` + +The planner writes: + +- `reports/status/engine-x-automated-liquidity-advisor-latest.json` +- `reports/status/engine-x-automated-liquidity-advisor-latest.md` + +## Feed Inputs + +The advisor consumes the current Engine X evidence surfaces: + +- `mainnet-cwusdc-usdc-support-health-latest.json` +- `engine-x-public-indexed-readiness-latest.json` +- `engine-x-mev-defense-readiness-latest.json` +- `mainnet-cwusdc-weth-liquidity-surfaces-latest.json` + +Regenerate them with: + +```bash +python3 scripts/verify/check-mainnet-cwusdc-usdc-support-health.py +pnpm engine-x:public-readiness +pnpm engine-x:mev-defense +pnpm engine-x:weth-liquidity-eval +``` + +## Request Model + +Initial supported request: + +```bash +ENGINE_X_REQUESTED_CW_SYMBOL=cWUSDC \ +ENGINE_X_REQUESTED_OUTPUT_UNITS=0.01 \ +ENGINE_X_XAUT_AVAILABLE_UNITS=0.000027 \ +pnpm engine-x:automation-advisor +``` + +The advisor currently treats `cWUSDC` as a six-decimal USD-denominated output token. Additional `cW*` assets should be added only after each token has a token policy, public indexed surface mapping, FX/crypto reference source, and endpoint publication packet. + +## Calculator Model + +The calculator combines: + +- requested `cW*` output; +- wallet official `USDC`; +- wallet `cWUSDC`; +- available `XAUt` collateral; +- configured `XAUt/USD` price; +- LTV and health-factor guardrails; +- MEV/protected transaction readiness; +- live public-indexed pool readiness; +- quote-defense surface status. + +The debt-neutral loop invariant remains: + +```text +Borrow USDC +Swap cWUSDC -> USDC +Repay borrowed USDC debt +Swap borrowed USDC -> cWUSDC +Stop +Withdraw XAUt collateral only after debt is zero +``` + +The advisor must not mark live automation ready unless ending USDC debt is zero and all protected execution gates pass. + +## Automation Phases + +1. `phase_0_advisor`: read-only API and calculator advice. +2. `phase_1_canary`: protected tiny public canary execution. +3. `phase_2_liquidity_defense`: automated protected rebalance and quote defense. +4. `phase_3_endpoint_publication`: regenerate public reports and endpoint packets. +5. `phase_4_multi_asset_forex_crypto`: extend to additional `cW*`, Forex, and crypto publication packets. + +## Endpoint Reporting + +Endpoint packets must contain transaction hashes, public pool state, quote values, ISO/audit/peg hashes where applicable, and clear boundaries between internal Engine X accounting and public DEX evidence. + +Targets: + +- token-aggregation public report API; +- CoinGecko tracker package; +- CoinMarketCap tracker package; +- Etherscan transaction and pool pages; +- DexScreener / GeckoTerminal pool pages; +- exchange listing or OTC due-diligence packets; +- Forex desk liquidity and proof packets. + +## Live Execution Boundary + +No advisor output is a broadcast instruction. Live execution still requires: + +- protected/private transaction RPC; +- fresh live quotes; +- funded official-USDC lender or quote inventory; +- passing Solidity preview; +- nonzero ISO/audit/peg hashes; +- operator approval. diff --git a/docs/03-deployment/DBIS_ENGINE_X_RECIPIENT_PROOF_RUNBOOK.md b/docs/03-deployment/DBIS_ENGINE_X_RECIPIENT_PROOF_RUNBOOK.md index 810a1982..8a45b451 100644 --- a/docs/03-deployment/DBIS_ENGINE_X_RECIPIENT_PROOF_RUNBOOK.md +++ b/docs/03-deployment/DBIS_ENGINE_X_RECIPIENT_PROOF_RUNBOOK.md @@ -9,7 +9,7 @@ This runbook is the durable operator path for the DBIS Engine X proof package wh The final 2026-05-07 execution delivered exactly `5,000,000,000.000000 cWUSDC` to the Meta wallet and exactly `5,000,000,000.000000 cWUSDC` to the master wallet. The final execution report is `reports/status/dbis-engine-x-final-recipient-proof-execution-20260507.md`. -The current implementation proves the Engine X internal maintained rail, not public DEX market depth. A public `cWUSDC/USDC` peg still requires separate public liquidity and quote evidence. +The current implementation proves the Engine X internal maintained rail, not public DEX market depth. A public `cWUSDC/USDC` peg still requires separate public liquidity and quote evidence. Engine X also tracks `cWUSDC/WETH9` as an indirect USD peg-support surface because WETH has deep public USD markets; that lane supports, but does not replace, direct official-USDC redemption-style evidence. **Public LP compliance boundary:** if the proof standard requires a public, Etherscan-indexable `cWUSDC/USDC` swap against official Mainnet `USDC`, Engine X evidence alone fails that standard. The proof must use an actual public LP surface, preserve before/after reserves plus swap transaction hashes, and avoid labeling virtual internal netting as public DEX volume. See `reports/status/mainnet-cwusdc-cross-protocol-public-lp-proof-plan-20260507.md` and `reports/status/mainnet-engine-x-indexed-liquidity-upgrade-plan-20260507.md`. @@ -42,6 +42,8 @@ Official ISO references used by this package: | Engine X ERC-3156 proof borrower | `smom-dbis-138/contracts/flash/DBISEngineXFlashProofBorrower.sol` | | XAUt-backed USDC borrow vault | `smom-dbis-138/contracts/flash/DBISEngineXXautUsdcBorrowVault.sol` | | XAUt-backed USDC borrow vault tests | `smom-dbis-138/test/flash/DBISEngineXXautUsdcBorrowVault.t.sol` | +| Single-sided DODO cWUSDC wrapper | `smom-dbis-138/contracts/flash/DBISEngineXSingleSidedDodoCwusdcVault.sol` | +| Single-sided DODO cWUSDC wrapper tests | `smom-dbis-138/test/flash/DBISEngineXSingleSidedDodoCwusdcVault.t.sol` | | Recipient deposit planner | `scripts/verify/plan-dbis-engine-x-recipient-deposits.py` | | Planner wrapper | `scripts/verify/plan-dbis-engine-x-recipient-deposits.sh` | | ISO/audit proof generator | `scripts/verify/generate-dbis-engine-x-iso20022-proofs.py` | @@ -56,6 +58,7 @@ Official ISO references used by this package: | UniV3 public swap proof dry-run | `scripts/deployment/run-engine-x-univ3-public-swap-proof.sh` | | Indexed proof recorder dry-run | `scripts/deployment/record-engine-x-indexed-liquidity-proof.sh` | | Public indexed readiness report | `scripts/verify/check-engine-x-public-indexed-readiness.sh` | +| WETH-backed peg-support evaluator | `scripts/verify/evaluate-mainnet-cwusdc-weth-liquidity-surfaces.sh` | | Audit manifest builder | `scripts/verify/build-engine-x-audit-manifest.sh` | | Main status report | `reports/status/dbis-engine-x-5b-cwusdc-ladder-recalc-20260506.md` | | Final execution report | `reports/status/dbis-engine-x-final-recipient-proof-execution-20260507.md` | @@ -153,12 +156,35 @@ Engine X proof execution must satisfy all of these conditions: This proves the maintained Engine X internal accounting rail. It does not prove public market price discovery or public DEX volume. +## MEV Defense + +Live Engine X public swap, rebalance, and LP migration scripts now use the shared protected-broadcast guard in `scripts/lib/mev-protection.sh`. + +```bash +pnpm engine-x:mev-defense +``` + +For sensitive execution, configure one protected write RPC through `ENGINE_X_PRIVATE_TX_RPC`, `MEV_BLOCKER_RPC_URL`, `FLASHBOTS_RPC_URL`, `BLOXROUTE_RPC_URL`, or `BLINK_RPC_URL`. Dry-runs and read-only quote checks still use `ETHEREUM_MAINNET_RPC`. If no protected RPC is configured, live Engine X quote-defense scripts fail closed before broadcasting. An operator can intentionally use the public mempool only by setting `ENGINE_X_ALLOW_PUBLIC_BROADCAST=1` for a reviewed canary. + +## Automated Liquidity Advisor + +Engine X now has a read-only advisor layer for API-fed liquidity recommendations, requested `cW*` output sizing, XAUt-backed USDC capacity, execution gates, and endpoint reporting: + +```bash +pnpm engine-x:automation-advisor +``` + +The advisor policy is `config/engine-x/automation-policy.json`; the durable runbook is `docs/03-deployment/DBIS_ENGINE_X_AUTOMATED_LIQUIDITY_ADVISOR.md`. It does not broadcast transactions. It decides whether a requested output can move from calculator advice into protected on-chain execution and reporting. + ## Public LP And Flash-Loan Boundary Cross-protocol routing can be used for a public proof, but the roles must stay explicit: - UniV2 `cWUSDC/USDC` is the public indexed proof surface. - UniV3 `cWUSDC/USDC` can also be used as the public indexed proof surface once created and funded with actual `cWUSDC` plus official Mainnet `USDC`. +- UniV3, UniV2, and DODO PMM `cWUSDC/WETH9` surfaces are Engine X peg-support lanes. They support the USD peg indirectly by comparing the `cWUSDC/WETH9` pool price against live WETH/USD or WETH/USDC reference markets. +- `cWUSDC/ETH` and `cWUSDC/WETH` should be treated as the same public WETH9-backed market. UniV2 `addLiquidityETH` wraps native ETH and creates a WETH9 pair; UniV3 and DODO require ERC-20 WETH9 directly. +- Single-sided DODO cWUSDC can be tracked as Engine X inventory, but it is not an executable DODO swap surface through the current integration. `DODOPMMIntegration.addLiquidity(pool, baseAmount, quoteAmount)` requires both base and quote amounts to be nonzero, so a true cWUSDC-only DODO lane needs either a dedicated single-sided wrapper or enough WETH9/USDC quote inventory to make the PMM executable. - Aave USDC flash liquidity is same-block working capital, not permanent LP funding. - The upgraded Engine X vault can provide ERC-3156 flash loans from its accounted USDC lender bucket only; it cannot lend pool USDC and it keeps flash fees in lender accounting. - DODO or cW mesh pools may be unwind legs only when live min-out quotes repay Aave principal plus premium in official USDC. @@ -166,6 +192,36 @@ Cross-protocol routing can be used for a public proof, but the roles must stay e With current balances, the only honest public LP path is a tiny wallet-funded UniV2 canary. Larger policy-floor or listing-quality depth still needs external official Mainnet USDC. +Evaluate the WETH-backed support lane before creating any cWUSDC/ETH, cWUSDC/WETH, UniV2, UniV3, or DODO PMM pool: + +```bash +pnpm engine-x:weth-liquidity-eval +``` + +The evaluator writes `reports/status/mainnet-cwusdc-weth-liquidity-surfaces-latest.json` and `.md`, checks the canonical Mainnet WETH9 Cc2 address, detects existing UniV2/UniV3 pools, confirms DODO manager readiness, quotes WETH against official USDC, and sizes the current wallet's maximum WETH-backed seed. + +For single-sided cWUSDC DODO inventory, use this promotion rule: + +1. Record cWUSDC-only inventory as Engine X accounted support inventory. +2. Do not claim executable public DODO liquidity until the pool has nonzero quote-side inventory and a passing `querySellBase` / `querySellQuote` canary. +3. Prefer a wrapper for cWUSDC-only deposits if the goal is single-sided operator UX; the wrapper must expose solvency, min-out, pause, and withdrawal controls and must not label idle inventory as public swap depth. + +The Engine X single-sided DODO wrapper deployment command is dry-run by default: + +```bash +pnpm engine-x:single-sided-dodo-deploy +``` + +The wrapper: + +- accepts `cWUSDC` as single-sided accounted inventory; +- optionally accepts a quote token, defaulting to Mainnet WETH9 Cc2; +- reports solvency as actual balances greater than or equal to accounted inventory; +- rejects DODO promotion unless both cWUSDC and quote amounts are nonzero; +- approves the DODO integration only for the exact promotion amounts and then clears allowances; +- requires a configured DODO pool plus passing `querySellBase` / `querySellQuote` canaries before promoted liquidity is treated as executable; +- keeps idle cWUSDC inventory separate from public DODO depth. + ## XAUt-Backed USDC Borrow Vault Engine X now has a dedicated repo-side borrowing component for the banking model where `XAUt` represents XAU collateral and `USDC` represents USD debt: @@ -210,14 +266,79 @@ The indexed LP upgrade creates public proof surface only. It does not meet the ` **2026-05-07 live attempt:** the legacy vault balances were swept and used to create/fund the official UniV3 `cWUSDC/USDC` pool at `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3`. External swaps immediately moved the pool out of range; current active liquidity is `0` and deployer USDC is `0`. See `reports/status/engine-x-univ3-public-lp-migration-attempt-20260507.md`. +## Mainnet cWUSDC Tracker And Public LP Completion Packet + +The 2026-05-08 internal completion pass finished every non-broadcast task available from the repo/operator workspace and left external/capital-dependent gates explicit. + +Fresh internal evidence: + +- `reports/status/mainnet-cwusdc-tracker-readiness-20260508.md` +- `reports/status/mainnet-cwusdc-technical-completion-20260508.md` +- `reports/status/mainnet-cwusdc-technical-completion-20260508.json` +- `reports/status/mainnet-cwusdc-supply-proof-20260508.json` +- `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md` +- `docs/04-configuration/coingecko/CWUSDC_MAINNET_EXTERNAL_SUBMISSION_CHECKLIST.md` +- `reports/status/engine-x-public-indexed-readiness-latest.json` +- `reports/status/mainnet-cwusdc-usdc-preflight-latest.json` +- `reports/status/mainnet-cwusdc-usdc-repeg-plan-latest.json` +- `reports/status/mainnet-cwusdc-weth-liquidity-surfaces-latest.json` + +Completed internal tasks: + +- Verified the cWUSDC contract source and supply surfaces. +- Recorded machine-readable supply proof and circulating-supply methodology. +- Confirmed cWUSDC is in the internal canonical metadata registry and CoinGecko-style export. +- Confirmed DexScreener and GeckoTerminal index the public UniV2 and UniV3 `cWUSDC/USDC` pairs. +- Confirmed CoinMarketCap DEX has a reachable Ethereum token page for cWUSDC; full CMC listing acceptance remains external. +- Documented LP caveats: UniV2 is official-USDC quote-starved and public depth remains too thin for listing-quality proof. +- Generated the latest public indexed readiness, public-pair preflight, repeg plan, and WETH support-surface reports. +- Deployed the upgraded accounting-aware Engine X v2 vault: `0xf108586d1FC330EA1D4EA4ff8fd983cde94279B1`. +- Deployed the public indexed-liquidity proof vault: `0xC264005EC6C3C74Ae2DfD0c60fb1EF5E792B45EE`. +- Verified both new Engine X contracts on Etherscan. +- Re-ran public indexed readiness against the new vaults; latest readiness is `readyForPublicIndexedProof=true`. +- Confirmed dry-run deployment command for the single-sided DODO wrapper. +- Fixed the indexed LP migration dry-run so it cleanly reports that no balanced vault liquidity remains to migrate. +- Granted cWUSDC `BURNER_ROLE` to `CW_BRIDGE_MAINNET=0x2bF74583206A49Be07E0E8A94197C12987AbD7B5`; bridge now has `MINTER_ROLE=true` and `BURNER_ROLE=true`. +- Documented valuation precedence while no direct third-party cWUSDC oracle feed is live. +- Prepared the external submission checklist with copy-paste asset fields, tracker URLs, ownership gates, and liquidity caveats. + +2026-05-08 on-chain completion transactions: + +| Action | Address / tx | +|---|---| +| Grant bridge cWUSDC burner role | `0x38d292d274158eb3a6015466cd3a4a9f9013c0e75be048a38214b64ae1f73eae` | +| Deploy Engine X v2 virtual batch vault | `0xf108586d1FC330EA1D4EA4ff8fd983cde94279B1` / `0xa02dffa042be591d256b75500d7774da1cf63f69e935d3058632d07871021bb4` | +| Set v2 flash fee to `5` bps | `0x7ae74c050b5e051e195e2a30912ae8064fbdbace20f96b53f3d213704f22e46a` | +| Set v2 max flash loan amount to `0` | `0x87aed485c0adac22452f14e89d6524e8db21226538bbe75652d0b3a94ed2b76c` | +| Deploy indexed-liquidity proof vault | `0xC264005EC6C3C74Ae2DfD0c60fb1EF5E792B45EE` / `0x3534657de3d8de2a56fc74d8b4b42f51dc7d56397c3ed33526927c684aa177d5` | + +Current public liquidity/capital gates: + +| Gate | Latest status | +|---|---| +| Public UniV2 policy-floor quote-side top-up | Needs `2,497.832239 USDC` | +| DODO defended reserve-parity quote top-up | Needs `290,995.072514 USDC` | +| Operator wallet official Mainnet USDC | `0.839784 USDC` | +| Public UniV2 quote-side shortfall after wallet balance | `2,496.992455 USDC` | +| DODO defended parity shortfall after wallet balance | `290,994.232730 USDC` | +| UniV3 active liquidity | `66,836,248` raw active liquidity; latest indexed readiness passes | +| CoinGecko public contract lookup | External pending; API returns `404` | +| CoinMarketCap DEX token page | Reachable | +| Full CoinMarketCap listing | External pending | +| Circulating supply accepted by public tracker | External pending | + +Do not broadcast public LP repair, claim listing-quality depth, or claim accepted tracker circulating supply until official Mainnet USDC is funded and the relevant external tracker accepts the submission. + ## Engine X V2 Public Proof Sequence All commands are dry-run by default: ```bash pnpm engine-x:public-readiness +pnpm engine-x:weth-liquidity-eval pnpm engine-x:v2-deploy pnpm engine-x:borrow-vault-deploy +pnpm engine-x:single-sided-dodo-deploy pnpm engine-x:indexed-lp-migration pnpm engine-x:indexed-vault-deploy pnpm engine-x:loop-proof @@ -228,17 +349,20 @@ pnpm engine-x:audit-manifest Recommended order: -1. Run `pnpm engine-x:public-readiness` and confirm the blockers. -2. Deploy the upgraded accounting-aware Engine X v2 vault with `pnpm engine-x:v2-deploy`. -3. Optionally deploy the XAUt-backed USDC borrow vault with `pnpm engine-x:borrow-vault-deploy`; fund it only with real official USDC. -4. Seed the upgraded vault with actual `cWUSDC/USDC` and a bounded USDC lender bucket. -5. Create/fund the official UniV3 `cWUSDC/USDC` public LP from the upgraded vault using `pnpm engine-x:indexed-lp-migration`. -6. Deploy `DBISEngineXIndexedLiquidityVault` with `pnpm engine-x:indexed-vault-deploy`. -7. For internal Engine X loop evidence, dry-run the exact `0.01 cWUSDC` loop proof with `pnpm engine-x:loop-proof`. -8. Run a tiny actual UniV3 public swap proof with `pnpm engine-x:univ3-swap-proof`. -9. Record the indexed proof with real swap/liquidity/proof hashes using `pnpm engine-x:indexed-proof-record`. -10. Build the ABI/proof audit manifest with `pnpm engine-x:audit-manifest`. -11. Retire the legacy vault only after v2 proof state is live and externally reviewed with `pnpm engine-x:legacy-retirement`. +1. Run `pnpm engine-x:public-readiness` and confirm the current no-blocker indexed proof state when using the 2026-05-08 v2 and indexed vault addresses. +2. Run `pnpm engine-x:weth-liquidity-eval` and confirm whether the WETH-backed support lane can be created at a meaningful size. +3. Use the deployed accounting-aware Engine X v2 vault at `0xf108586d1FC330EA1D4EA4ff8fd983cde94279B1`. +4. Optionally deploy the XAUt-backed USDC borrow vault with `pnpm engine-x:borrow-vault-deploy`; fund it only with real official USDC. +5. Deploy the single-sided DODO cWUSDC wrapper with `pnpm engine-x:single-sided-dodo-deploy` if cWUSDC-only inventory needs a solvency-controlled holding lane. +6. Seed the upgraded vault with actual `cWUSDC/USDC` and a bounded USDC lender bucket. +7. Create/fund the official UniV3 `cWUSDC/USDC` public LP from the upgraded vault using `pnpm engine-x:indexed-lp-migration`. +8. Create only one WETH-backed support surface first, preferably UniV3 `cWUSDC/WETH9`; add UniV2 or DODO PMM only after the evaluator report and dry-runs pass. +9. Use the deployed `DBISEngineXIndexedLiquidityVault` at `0xC264005EC6C3C74Ae2DfD0c60fb1EF5E792B45EE`. +10. For internal Engine X loop evidence, dry-run the exact `0.01 cWUSDC` loop proof with `pnpm engine-x:loop-proof`. +11. Run a tiny actual UniV3 public swap proof with `pnpm engine-x:univ3-swap-proof`. +12. Record the indexed proof with real swap/liquidity/proof hashes using `pnpm engine-x:indexed-proof-record`. +13. Build the ABI/proof audit manifest with `pnpm engine-x:audit-manifest`. +14. Retire the legacy vault only after v2 proof state is live and externally reviewed with `pnpm engine-x:legacy-retirement`. The indexed proof vault enforces the public proof gates: nonzero public swap hash, nonzero liquidity tx hash, tick drift limit, minimum liquidity, optional pool token-balance floors, optional max proof amount, pause, and operator allowlist. diff --git a/docs/03-deployment/DEPLOYMENT_ORDER_OF_OPERATIONS.md b/docs/03-deployment/DEPLOYMENT_ORDER_OF_OPERATIONS.md index 84c6f51f..f3c9acce 100644 --- a/docs/03-deployment/DEPLOYMENT_ORDER_OF_OPERATIONS.md +++ b/docs/03-deployment/DEPLOYMENT_ORDER_OF_OPERATIONS.md @@ -114,7 +114,7 @@ Design and pool matrix: [POOLS_AND_NETWORKS_FULL_DESIGN.md](../11-references/POO | # | Item | Action | |---|------|--------| -| 5.1 | **Per-chain RPC and env** | Set `CHAIN_*_RPC_URL`, `CHAIN_*_DODO_PMM_INTEGRATION` (or pool source) for each of 1, 10, 25, 56, 100, 137, 42161, 42220, 43114, 8453, 1111 if indexing via token-aggregation. | +| 5.1 | **Per-chain RPC and env** | Set `CHAIN_*_RPC_URL`, `CHAIN_*_DODO_PMM_INTEGRATION` (or pool source) for each of 1, 10, 56, 100, 137, 42161, 42220, 43114, 8453, 1111 if indexing via token-aggregation (nine-chain promoted `cW*` surface; add `25` only if you explicitly index Cronos). | | 5.2 | **Deploy and fund** | Per chain: deploy or bridge cW* tokens; create and fund PMM pools per pool-matrix; add to MCP allowlist per chain if using MCP. | --- diff --git a/docs/03-deployment/DEPLOY_AND_VERIFY_ONE_BY_ONE.md b/docs/03-deployment/DEPLOY_AND_VERIFY_ONE_BY_ONE.md index f649098b..70a822d1 100644 --- a/docs/03-deployment/DEPLOY_AND_VERIFY_ONE_BY_ONE.md +++ b/docs/03-deployment/DEPLOY_AND_VERIFY_ONE_BY_ONE.md @@ -130,7 +130,7 @@ Check that the contract exists and (if applicable) is verified: ``` Or open the contract on the explorer: -https://explorer.d-bis.org/address/
+https://explorer.d-bis.org/addresses/
--- diff --git a/docs/03-deployment/EI_MATRIX_CWUSDC_MINT_PIPELINE.md b/docs/03-deployment/EI_MATRIX_CWUSDC_MINT_PIPELINE.md new file mode 100644 index 00000000..577136fa --- /dev/null +++ b/docs/03-deployment/EI_MATRIX_CWUSDC_MINT_PIPELINE.md @@ -0,0 +1,43 @@ +# EI matrix — mainnet cWUSDC mint pipeline + +**Purpose:** Mint **Ethereum mainnet** `cWUSDC` (`CWUSDC_MAINNET`, default `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a`) directly to every address in `config/pmm-soak-wallet-grid.json` (EI matrix), without bridging from Chain 138. + +**Treasury semantics:** The **33 × 33 × 6** grid (**6 534** wallets) is the canonical **sovereign-nation treasury address set** for the Elemental Imperium model—each cell’s Ethereum address is the designated on-chain treasury for that routing cell (see `semanticModel` in `config/pmm-soak-wallet-grid.json`). Nation↔cell attribution and reserve governance are layered via `config/elemental-imperium-network-registry.json` and optional overlays; scripts operate on addresses regardless of label assignment. + +**Entry point (recommended):** + +```bash +./scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh --dry-run --mint-raw 10000000 +./scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh --mint-raw 10000000 +``` + +Pass extra flags to the mint script after `--`: + +```bash +./scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh --dry-run --total-mint-raw 72000000000000 --spread-pct 15 -- --offset 0 --limit 100 +``` + +**Resume after partial run:** + +```bash +./scripts/deployment/continue-mint-cwusdc-ei-matrix-wallets.sh --mint-raw 10000000 +``` + +(progress file: `reports/status/ei-matrix-cwusdc-mint-last-idx.txt`) + +**Modes:** + +| Flag | Meaning | +|------|--------| +| `--mint-raw R` | Same raw amount (6 decimals) minted to each wallet in the slice. | +| `--total-mint-raw B` | Total minted across the slice equals `B`; per-wallet amounts use ±`--spread-pct` jitter then renormalize (default spread 15). | + +**Requirements:** + +- `PRIVATE_KEY` with **`mint(address,uint256)`** authority on the token (typically `MINTER_ROLE` on `CompliantWrappedToken`-style `cWUSDC`). +- `ETHEREUM_MAINNET_RPC` (or `RPC_URL_1`) and ETH for gas. +- On-chain **reserve / policy** on the token must allow mints (otherwise transactions revert). + +**Not in scope:** Minting **c\*** on Chain 138 and bridging to mainnet as `cWUSDC` — use bridge / Engine X runbooks separately (`docs/03-deployment/DBIS_ENGINE_X_RECIPIENT_PROOF_RUNBOOK.md`, `docs/07-ccip/`). + +**Artifacts:** `reports/status/ei-matrix-cwusdc-mint-*.json` manifests, `ei-matrix-cwusdc-mint-failures.log`, flock `ei-matrix-cwusdc-mint.lock` (one live mint job). diff --git a/docs/03-deployment/PHASE_D_OPTIONAL_CHECKLIST.md b/docs/03-deployment/PHASE_D_OPTIONAL_CHECKLIST.md index fa80802a..527464ff 100644 --- a/docs/03-deployment/PHASE_D_OPTIONAL_CHECKLIST.md +++ b/docs/03-deployment/PHASE_D_OPTIONAL_CHECKLIST.md @@ -1,6 +1,6 @@ # Phase D — Optional Extended Coverage Checklist -**Last Updated:** 2026-03-04 +**Last Updated:** 2026-05-09 **Purpose:** Checklist for Phase D of [REMAINING_DEPLOYMENTS_FOR_FULL_NETWORK_COVERAGE.md](REMAINING_DEPLOYMENTS_FOR_FULL_NETWORK_COVERAGE.md): XAU, vaults, ALL Mainnet, and trustless stack. --- @@ -43,8 +43,19 @@ --- +## D.5 Cosmos ecosystem (optional — not Phase D required) + +Cross-chain connectivity to Cosmos Hub, Noble, Osmosis, IBC app tokens, and CosmWasm apps is **out of band** for Phase D core (XAU, vaults, ALL, trustless) unless explicitly adopted. + +| Step | Action | Ref | +|------|--------|-----| +| D.5.1 | If adopting any Cosmos leg, follow streams **A–E** and completion criteria. | [COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK](../11-references/COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md), [`config/cosmos-chain138-optional/`](../../config/cosmos-chain138-optional/README.md) | + +--- + ## References - [REMAINING_DEPLOYMENTS_FOR_FULL_NETWORK_COVERAGE.md](REMAINING_DEPLOYMENTS_FOR_FULL_NETWORK_COVERAGE.md) Phase D - [LIQUIDITY_POOLS_MASTER_MAP](../11-references/LIQUIDITY_POOLS_MASTER_MAP.md) - [VAULT_SYSTEM_MASTER_TECHNICAL_PLAN](../02-architecture/VAULT_SYSTEM_MASTER_TECHNICAL_PLAN.md) +- [COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK](../11-references/COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md) (optional; D.5) diff --git a/docs/03-deployment/PROMOD_UNISWAP_V2_LIVE_PAIR_DISCOVERY.md b/docs/03-deployment/PROMOD_UNISWAP_V2_LIVE_PAIR_DISCOVERY.md index fe3a9f3d..33b13983 100644 --- a/docs/03-deployment/PROMOD_UNISWAP_V2_LIVE_PAIR_DISCOVERY.md +++ b/docs/03-deployment/PROMOD_UNISWAP_V2_LIVE_PAIR_DISCOVERY.md @@ -1,9 +1,9 @@ # Mr. Promod Uniswap V2 Live Pair Discovery -- Generated: `2026-04-22T04:59:28Z` +- Generated: `2026-05-09T05:43:24Z` - Live pairs discovered: `19` - Healthy live pairs: `10` -- Write mode: `False` +- Write mode: `True` | Chain | Network | Env Ready | Live Pairs Found | Healthy Live Pairs | |---|---|---|---|---| diff --git a/docs/03-deployment/PROMOD_UNISWAP_V2_PHASE1_FUNDING_ACTIONS.md b/docs/03-deployment/PROMOD_UNISWAP_V2_PHASE1_FUNDING_ACTIONS.md index 41852eb0..37989d3e 100644 --- a/docs/03-deployment/PROMOD_UNISWAP_V2_PHASE1_FUNDING_ACTIONS.md +++ b/docs/03-deployment/PROMOD_UNISWAP_V2_PHASE1_FUNDING_ACTIONS.md @@ -1,72 +1,46 @@ # Mr. Promod Uniswap V2 Phase 1 Funding Actions -- Generated: `2026-04-17T18:13:05Z` +- Generated: `2026-05-09T21:07:05Z` - Signer: `0x4A666F96fC8764181194447A7dFdb7d471b301C8` - Purpose: strict per-chain action plan for phase-1 funding and deployment. | Chain | Network | Action | Tokens Missing | Gas Issue | Recommended Seed | |---|---|---|---|---|---:| -| `1` | Ethereum Mainnet | `seed_now` | `none` | `false` | `8888511.867466` | -| `10` | Optimism | `mint_destination_then_seed` | `cWUSDT`, `cWUSDC` | `true` | `1000` | -| `25` | Cronos | `mint_destination_then_seed` | `cWUSDT`, `cWUSDC` | `false` | `1000` | -| `56` | BSC | `mint_destination_then_seed` | `cWUSDT`, `cWUSDC` | `false` | `1000` | +| `1` | Ethereum Mainnet | `mint_destination_then_seed` | `cWUSDC` | `false` | `1000` | +| `10` | Optimism | `mint_destination_then_seed` | `cWUSDT`, `cWUSDC` | `false` | `1000` | +| `25` | Cronos | `mint_destination_then_seed` | `none` | `false` | `1000` | +| `56` | BSC | `mint_destination_then_seed` | `none` | `false` | `1000` | | `100` | Gnosis | `mint_destination_then_seed` | `cWUSDT`, `cWUSDC` | `false` | `1000` | -| `137` | Polygon | `seed_now` | `none` | `false` | `996.297636` | -| `8453` | Base | `mint_destination_then_seed` | `cWUSDT`, `cWUSDC` | `true` | `1000` | +| `137` | Polygon | `mint_destination_then_seed` | `cWUSDC` | `false` | `1000` | +| `8453` | Base | `mint_destination_then_seed` | `cWUSDT`, `cWUSDC` | `false` | `1000` | | `42161` | Arbitrum One | `mint_destination_then_seed` | `cWUSDT`, `cWUSDC` | `false` | `1000` | | `42220` | Celo | `mint_destination_then_seed` | `cWUSDT`, `cWUSDC` | `false` | `1000` | -| `43114` | Avalanche | `mint_missing_side_then_seed` | `cWUSDC` | `false` | `0.8` | +| `43114` | Avalanche | `mint_destination_then_seed` | `cWUSDT`, `cWUSDC` | `false` | `1000` | ## Per-Chain Actions ### Chain `1` — Ethereum Mainnet -- Action: `seed_now` -- Tokens missing: `none` +- Action: `mint_destination_then_seed` +- Tokens missing: `cWUSDC` - Gas issue: `false` - Gas note: No minimum gas top-up issue from the latest preflight snapshot. - Bridge possible: `true` - Bridge note: Bridge path is structurally available for chain `1` via `CW_BRIDGE_MAINNET` and `bridgeAvailable=true`, but the repo-native executable path today is destination-side cW minting. Cross-chain c* -> cW delivery still follows `docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md` and `docs/07-ccip/CW_BRIDGE_APPROACH.md` rather than a single helper script. -Post-funding deploy block: +Mint steps: +- Mint `cWUSDC` `1000` with: ```bash source smom-dbis-138/scripts/load-env.sh >/dev/null -export RPC_URL="${ETHEREUM_MAINNET_RPC}" -export FACTORY="${CHAIN_1_UNISWAP_V2_FACTORY}" -export ROUTER="${CHAIN_1_UNISWAP_V2_ROUTER}" -export CWUSDT="0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE" -export CWUSDC="0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a" -export SIGNER="$(cast wallet address --private-key "$PRIVATE_KEY")" -export AMOUNT_RAW="8888511867466" -export DEADLINE="$(( $(date +%s) + 3600 ))" - -PAIR="$(cast call "$FACTORY" 'getPair(address,address)(address)' "$CWUSDT" "$CWUSDC" --rpc-url "$RPC_URL")" -if [[ "$PAIR" == "0x0000000000000000000000000000000000000000" ]]; then - cast send "$FACTORY" 'createPair(address,address)(address)' "$CWUSDT" "$CWUSDC" \ - --private-key "$PRIVATE_KEY" --rpc-url "$RPC_URL" -fi - -cast send "$CWUSDT" 'approve(address,uint256)(bool)' "$ROUTER" "$AMOUNT_RAW" \ - --private-key "$PRIVATE_KEY" --rpc-url "$RPC_URL" - -cast send "$CWUSDC" 'approve(address,uint256)(bool)' "$ROUTER" "$AMOUNT_RAW" \ - --private-key "$PRIVATE_KEY" --rpc-url "$RPC_URL" - -cast send "$ROUTER" \ - 'addLiquidity(address,address,uint256,uint256,uint256,uint256,address,uint256)' \ - "$CWUSDT" "$CWUSDC" "$AMOUNT_RAW" "$AMOUNT_RAW" "$AMOUNT_RAW" "$AMOUNT_RAW" "$SIGNER" "$DEADLINE" \ - --private-key "$PRIVATE_KEY" --rpc-url "$RPC_URL" - -python3 scripts/lib/promod_uniswap_v2_live_pair_discovery.py --write-discovered -bash scripts/verify/build-promod-uniswap-v2-promotion-gates.sh -node cross-chain-pmm-lps/scripts/validate-deployment-status.cjs cross-chain-pmm-lps/config/deployment-status.json +cast send "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a" 'mint(address,uint256)' "$(cast wallet address --private-key "$PRIVATE_KEY")" "1000000000" \ + --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "$PRIVATE_KEY" --legacy --gas-limit 100000 ``` ### Chain `10` — Optimism - Action: `mint_destination_then_seed` - Tokens missing: `cWUSDT`, `cWUSDC` -- Gas issue: `true` +- Gas issue: `false` - Gas note: Top up native gas on Optimism before minting or seeding; current balance is below the 0.001 safety threshold. - Bridge possible: `true` - Bridge note: Bridge path is structurally available for chain `10` via `CW_BRIDGE_OPTIMISM` and `bridgeAvailable=true`, but the repo-native executable path today is destination-side cW minting. Cross-chain c* -> cW delivery still follows `docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md` and `docs/07-ccip/CW_BRIDGE_APPROACH.md` rather than a single helper script. @@ -88,48 +62,22 @@ cast send "0x377a5FaA3162b3Fc6f4e267301A3c817bAd18105" 'mint(address,uint256)' " ### Chain `25` — Cronos - Action: `mint_destination_then_seed` -- Tokens missing: `cWUSDT`, `cWUSDC` +- Tokens missing: `none` - Gas issue: `false` - Gas note: No minimum gas top-up issue from the latest preflight snapshot. - Bridge possible: `true` - Bridge note: Bridge path is structurally available for chain `25` via `CW_BRIDGE_CRONOS` and `bridgeAvailable=true`, but the repo-native executable path today is destination-side cW minting. Cross-chain c* -> cW delivery still follows `docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md` and `docs/07-ccip/CW_BRIDGE_APPROACH.md` rather than a single helper script. -Mint steps: -- Mint `cWUSDT` `1000` with: -```bash -source smom-dbis-138/scripts/load-env.sh >/dev/null -cast send "0x72948a7a813B60b37Cd0c920C4657DbFF54312b8" 'mint(address,uint256)' "$(cast wallet address --private-key "$PRIVATE_KEY")" "1000000000" \ - --rpc-url "${CRONOS_RPC_URL}" --private-key "$PRIVATE_KEY" --legacy --gas-limit 100000 -``` -- Mint `cWUSDC` `1000` with: -```bash -source smom-dbis-138/scripts/load-env.sh >/dev/null -cast send "0x932566E5bB6BEBF6B035B94f3DE1f75f126304Ec" 'mint(address,uint256)' "$(cast wallet address --private-key "$PRIVATE_KEY")" "1000000000" \ - --rpc-url "${CRONOS_RPC_URL}" --private-key "$PRIVATE_KEY" --legacy --gas-limit 100000 -``` ### Chain `56` — BSC - Action: `mint_destination_then_seed` -- Tokens missing: `cWUSDT`, `cWUSDC` +- Tokens missing: `none` - Gas issue: `false` - Gas note: No minimum gas top-up issue from the latest preflight snapshot. - Bridge possible: `true` - Bridge note: Bridge path is structurally available for chain `56` via `CW_BRIDGE_BSC` and `bridgeAvailable=true`, but the repo-native executable path today is destination-side cW minting. Cross-chain c* -> cW delivery still follows `docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md` and `docs/07-ccip/CW_BRIDGE_APPROACH.md` rather than a single helper script. -Mint steps: -- Mint `cWUSDT` `1000` with: -```bash -source smom-dbis-138/scripts/load-env.sh >/dev/null -cast send "0x9a1D0dBEE997929ED02fD19E0E199704d20914dB" 'mint(address,uint256)' "$(cast wallet address --private-key "$PRIVATE_KEY")" "1000000000" \ - --rpc-url "${BSC_RPC_URL}" --private-key "$PRIVATE_KEY" --legacy --gas-limit 100000 -``` -- Mint `cWUSDC` `1000` with: -```bash -source smom-dbis-138/scripts/load-env.sh >/dev/null -cast send "0x5355148C4740fcc3D7a96F05EdD89AB14851206b" 'mint(address,uint256)' "$(cast wallet address --private-key "$PRIVATE_KEY")" "1000000000" \ - --rpc-url "${BSC_RPC_URL}" --private-key "$PRIVATE_KEY" --legacy --gas-limit 100000 -``` ### Chain `100` — Gnosis @@ -156,52 +104,26 @@ cast send "0xd6969bC19b53f866C64f2148aE271B2Dae0C58E4" 'mint(address,uint256)' " ### Chain `137` — Polygon -- Action: `seed_now` -- Tokens missing: `none` +- Action: `mint_destination_then_seed` +- Tokens missing: `cWUSDC` - Gas issue: `false` - Gas note: No minimum gas top-up issue from the latest preflight snapshot. - Bridge possible: `true` - Bridge note: Bridge path is structurally available for chain `137` via `CW_BRIDGE_POLYGON` and `bridgeAvailable=true`, but the repo-native executable path today is destination-side cW minting. Cross-chain c* -> cW delivery still follows `docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md` and `docs/07-ccip/CW_BRIDGE_APPROACH.md` rather than a single helper script. -Post-funding deploy block: +Mint steps: +- Mint `cWUSDC` `1000` with: ```bash source smom-dbis-138/scripts/load-env.sh >/dev/null -export RPC_URL="${POLYGON_MAINNET_RPC}" -export FACTORY="${CHAIN_137_UNISWAP_V2_FACTORY}" -export ROUTER="${CHAIN_137_UNISWAP_V2_ROUTER}" -export CWUSDT="0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF" -export CWUSDC="0xd6969bC19b53f866C64f2148aE271B2Dae0C58E4" -export SIGNER="$(cast wallet address --private-key "$PRIVATE_KEY")" -export AMOUNT_RAW="996297636" -export DEADLINE="$(( $(date +%s) + 3600 ))" - -PAIR="$(cast call "$FACTORY" 'getPair(address,address)(address)' "$CWUSDT" "$CWUSDC" --rpc-url "$RPC_URL")" -if [[ "$PAIR" == "0x0000000000000000000000000000000000000000" ]]; then - cast send "$FACTORY" 'createPair(address,address)(address)' "$CWUSDT" "$CWUSDC" \ - --private-key "$PRIVATE_KEY" --rpc-url "$RPC_URL" -fi - -cast send "$CWUSDT" 'approve(address,uint256)(bool)' "$ROUTER" "$AMOUNT_RAW" \ - --private-key "$PRIVATE_KEY" --rpc-url "$RPC_URL" - -cast send "$CWUSDC" 'approve(address,uint256)(bool)' "$ROUTER" "$AMOUNT_RAW" \ - --private-key "$PRIVATE_KEY" --rpc-url "$RPC_URL" - -cast send "$ROUTER" \ - 'addLiquidity(address,address,uint256,uint256,uint256,uint256,address,uint256)' \ - "$CWUSDT" "$CWUSDC" "$AMOUNT_RAW" "$AMOUNT_RAW" "$AMOUNT_RAW" "$AMOUNT_RAW" "$SIGNER" "$DEADLINE" \ - --private-key "$PRIVATE_KEY" --rpc-url "$RPC_URL" - -python3 scripts/lib/promod_uniswap_v2_live_pair_discovery.py --write-discovered -bash scripts/verify/build-promod-uniswap-v2-promotion-gates.sh -node cross-chain-pmm-lps/scripts/validate-deployment-status.cjs cross-chain-pmm-lps/config/deployment-status.json +cast send "0xd6969bC19b53f866C64f2148aE271B2Dae0C58E4" 'mint(address,uint256)' "$(cast wallet address --private-key "$PRIVATE_KEY")" "1000000000" \ + --rpc-url "${POLYGON_MAINNET_RPC}" --private-key "$PRIVATE_KEY" --legacy --gas-limit 100000 ``` ### Chain `8453` — Base - Action: `mint_destination_then_seed` - Tokens missing: `cWUSDT`, `cWUSDC` -- Gas issue: `true` +- Gas issue: `false` - Gas note: Top up native gas on Base before minting or seeding; current balance is below the 0.001 safety threshold. - Bridge possible: `true` - Bridge note: Bridge path is structurally available for chain `8453` via `CW_BRIDGE_BASE` and `bridgeAvailable=true`, but the repo-native executable path today is destination-side cW minting. Cross-chain c* -> cW delivery still follows `docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md` and `docs/07-ccip/CW_BRIDGE_APPROACH.md` rather than a single helper script. @@ -268,17 +190,23 @@ cast send "0x4C38F9A5ed68A04cd28a72E8c68C459Ec34576f3" 'mint(address,uint256)' " ### Chain `43114` — Avalanche -- Action: `mint_missing_side_then_seed` -- Tokens missing: `cWUSDC` +- Action: `mint_destination_then_seed` +- Tokens missing: `cWUSDT`, `cWUSDC` - Gas issue: `false` - Gas note: No minimum gas top-up issue from the latest preflight snapshot. - Bridge possible: `true` - Bridge note: Bridge path is structurally available for chain `43114` via `CW_BRIDGE_AVALANCHE` and `bridgeAvailable=true`, but the repo-native executable path today is destination-side cW minting. Cross-chain c* -> cW delivery still follows `docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md` and `docs/07-ccip/CW_BRIDGE_APPROACH.md` rather than a single helper script. Mint steps: -- Mint `cWUSDC` `0.8` with: +- Mint `cWUSDT` `1000` with: ```bash source smom-dbis-138/scripts/load-env.sh >/dev/null -cast send "0x0C242b513008Cd49C89078F5aFb237A3112251EB" 'mint(address,uint256)' "$(cast wallet address --private-key "$PRIVATE_KEY")" "800000" \ +cast send "0x8142BA530B08f3950128601F00DaaA678213DFdf" 'mint(address,uint256)' "$(cast wallet address --private-key "$PRIVATE_KEY")" "1000000000" \ + --rpc-url "${AVALANCHE_RPC_URL}" --private-key "$PRIVATE_KEY" --legacy --gas-limit 100000 +``` +- Mint `cWUSDC` `1000` with: +```bash +source smom-dbis-138/scripts/load-env.sh >/dev/null +cast send "0x0C242b513008Cd49C89078F5aFb237A3112251EB" 'mint(address,uint256)' "$(cast wallet address --private-key "$PRIVATE_KEY")" "1000000000" \ --rpc-url "${AVALANCHE_RPC_URL}" --private-key "$PRIVATE_KEY" --legacy --gas-limit 100000 ``` diff --git a/docs/03-deployment/PROMOD_UNISWAP_V2_PHASE1_FUNDING_READINESS.md b/docs/03-deployment/PROMOD_UNISWAP_V2_PHASE1_FUNDING_READINESS.md index f88e8bf9..b9b41589 100644 --- a/docs/03-deployment/PROMOD_UNISWAP_V2_PHASE1_FUNDING_READINESS.md +++ b/docs/03-deployment/PROMOD_UNISWAP_V2_PHASE1_FUNDING_READINESS.md @@ -1,6 +1,6 @@ # Mr. Promod Uniswap V2 Phase 1 Funding Readiness -- Generated: `2026-04-17T20:05:54Z` +- Generated: `2026-05-09T20:53:57Z` - Signer: `0x4A666F96fC8764181194447A7dFdb7d471b301C8` - Purpose: live deployer-wallet funding view for seeding `cWUSDT/cWUSDC` phase-1 pools chain by chain. - Completed: `1`, `10`, `25`, `56`, `100`, `137`, `8453`, `42161`, `42220`, `43114` @@ -9,16 +9,16 @@ | Chain | Network | Status | Pair Exists | Seeded Live | Native Gas | cWUSDT | cWUSDC | Max Equal Seed | |---|---|---|---|---|---:|---:|---:|---:| -| `1` | Ethereum Mainnet | `completed` | `true` | `true` | `0.028982898459925766` | `397.197013` | `0` | `0` | -| `10` | Optimism | `completed` | `true` | `true` | `0.001980670026642148` | `0` | `0` | `0` | -| `25` | Cronos | `completed` | `true` | `true` | `22.401143346977489259` | `1000` | `1000` | `1000` | -| `56` | BSC | `completed` | `true` | `true` | `0.010626271367072709` | `1000` | `1000` | `1000` | -| `100` | Gnosis | `completed` | `true` | `true` | `2.407080479781350729` | `0` | `0` | `0` | -| `137` | Polygon | `completed` | `true` | `true` | `24.318080574425380349` | `2.686028` | `0` | `0` | -| `8453` | Base | `completed` | `true` | `true` | `0.002877934067617928` | `0` | `0` | `0` | -| `42161` | Arbitrum One | `completed` | `true` | `true` | `0.001055113904178255` | `0` | `0` | `0` | -| `42220` | Celo | `completed` | `true` | `true` | `8.537307308649465530` | `0` | `0` | `0` | -| `43114` | Avalanche | `completed` | `true` | `true` | `0.446784013286210977` | `0` | `0` | `0` | +| `1` | Ethereum Mainnet | `completed` | `true` | `true` | `0.002397937659907006` | `8523829.088635` | `56705345.129226` | `8523829.088635` | +| `10` | Optimism | `completed` | `true` | `true` | `0.000430532335030017` | `3000.984101` | `3000.9869` | `3000.984101` | +| `25` | Cronos | `completed` | `true` | `true` | `19.154691909389182692` | `999.9918` | `999.9938` | `999.9918` | +| `56` | BSC | `completed` | `true` | `true` | `0.003681967051288879` | `997.9959` | `997.9959` | `997.9959` | +| `100` | Gnosis | `completed` | `true` | `true` | `0.551078988224567304` | `999.9958` | `998.9959` | `998.9959` | +| `137` | Polygon | `completed` | `true` | `true` | `15.030270707053166727` | `3001.992967` | `2999.903173` | `2999.903173` | +| `8453` | Base | `completed` | `true` | `true` | `0.004958123781375043` | `5998.9839` | `5998.9859` | `5998.9839` | +| `42161` | Arbitrum One | `completed` | `true` | `true` | `0.000971971100341626` | `1999.8848` | `999.8848` | `999.8848` | +| `42220` | Celo | `completed` | `true` | `true` | `5.373727945247599766` | `6999.9889` | `5999.9889` | `5999.9889` | +| `43114` | Avalanche | `completed` | `true` | `true` | `0.014891739291454170` | `998.9859` | `998.9859` | `998.9859` | ## Blockers @@ -26,14 +26,13 @@ - execution status: `completed` - pair seeded live: `true` -- cWUSDC balance is zero +- no funding blockers ### Chain `10` — Optimism - execution status: `completed` - pair seeded live: `true` -- cWUSDT balance is zero -- cWUSDC balance is zero +- native gas below 0.001 ### Chain `25` — Cronos @@ -51,39 +50,34 @@ - execution status: `completed` - pair seeded live: `true` -- cWUSDT balance is zero -- cWUSDC balance is zero +- no funding blockers ### Chain `137` — Polygon - execution status: `completed` - pair seeded live: `true` -- cWUSDC balance is zero +- no funding blockers ### Chain `8453` — Base - execution status: `completed` - pair seeded live: `true` -- cWUSDT balance is zero -- cWUSDC balance is zero +- no funding blockers ### Chain `42161` — Arbitrum One - execution status: `completed` - pair seeded live: `true` -- cWUSDT balance is zero -- cWUSDC balance is zero +- native gas below 0.001 ### Chain `42220` — Celo - execution status: `completed` - pair seeded live: `true` -- cWUSDT balance is zero -- cWUSDC balance is zero +- no funding blockers ### Chain `43114` — Avalanche - execution status: `completed` - pair seeded live: `true` -- cWUSDT balance is zero -- cWUSDC balance is zero +- no funding blockers diff --git a/docs/03-deployment/PROMOD_UNISWAP_V2_PHASE2_WAVE1_COMPLETION_STATUS.md b/docs/03-deployment/PROMOD_UNISWAP_V2_PHASE2_WAVE1_COMPLETION_STATUS.md index 1c8353f5..f3715e9c 100644 --- a/docs/03-deployment/PROMOD_UNISWAP_V2_PHASE2_WAVE1_COMPLETION_STATUS.md +++ b/docs/03-deployment/PROMOD_UNISWAP_V2_PHASE2_WAVE1_COMPLETION_STATUS.md @@ -4,7 +4,8 @@ **Overall Status:** `complete` -**Completed Chains:** `1, 10, 25, 56, 100, 137, 8453, 42161, 42220, 43114` +**Evidence chains (this report):** `1, 10, 25, 56, 100, 137, 8453, 42161, 42220, 43114` +**Nine-chain promoted public `cW*` surface (portal-aligned):** `1, 10, 56, 100, 137, 8453, 42161, 42220, 43114` — Cronos `25` rows below are historical mesh evidence only; do **not** count `25` toward the nine-chain public claim. ## Reserve Verification diff --git a/docs/03-deployment/PROXMOX_VM_CREATION_RUNBOOK.md b/docs/03-deployment/PROXMOX_VM_CREATION_RUNBOOK.md index 9f2a7b0b..908e4695 100644 --- a/docs/03-deployment/PROXMOX_VM_CREATION_RUNBOOK.md +++ b/docs/03-deployment/PROXMOX_VM_CREATION_RUNBOOK.md @@ -1,6 +1,6 @@ # Proxmox VM/Container Creation Runbook — Capacity and Availability -**Last Updated:** 2026-02-13 +**Last Updated:** 2026-05-09 **Purpose:** Create all planned VMs/containers on the Proxmox cluster using capacity and availability best practices. Use this when SSH'd to the Proxmox host or from a host that can SSH to Proxmox with access to env/config. **Related:** [VMID_ALLOCATION_FINAL](../02-architecture/VMID_ALLOCATION_FINAL.md) | [PROXMOX_CLUSTER_ARCHITECTURE](../02-architecture/PROXMOX_CLUSTER_ARCHITECTURE.md) | [PROXMOX_COMPLETE_RECOMMENDATIONS](../10-best-practices/PROXMOX_COMPLETE_RECOMMENDATIONS.md) | [STEPS_FROM_PROXMOX_OR_LAN_WITH_SECRETS](../00-meta/STEPS_FROM_PROXMOX_OR_LAN_WITH_SECRETS.md) @@ -9,7 +9,7 @@ ## 1. Capacity and availability principles -- **Node spread:** Prefer **r630-01** (192.168.11.11) and **r630-02** (192.168.11.12) for new workloads; **ml110** (192.168.11.10) already runs 34 containers and is CPU/memory constrained. Distribute to improve redundancy and performance. +- **Node spread:** Prefer **r630-01** / **r630-02** when balancing write-heavy pools; **r630-03** / **r630-04** have spare capacity vs **r630-01**/**02**. **ml110** (192.168.11.10) currently hosts **0** cluster guests (cluster reconcile **2026-05-09**) but remains lower-spec — keep it for lightweight roles unless you deliberately expand it. - **VMID ranges:** Use [VMID_ALLOCATION_FINAL](../02-architecture/VMID_ALLOCATION_FINAL.md) — e.g. DBIS Core 10000–10199, Sankofa 7800–8999, Besu 1000–4999. Do not reuse VMIDs. - **Storage:** Use `local-lvm` or node-specific thin pools (e.g. r630-01 thin1, r630-02 thin2–thin6). Set `PROXMOX_STORAGE` per node if creating on a specific host. - **High availability:** For critical services (DB, API), create primary and secondary on **different nodes** where possible (e.g. DB primary on r630-01, replica on r630-02). @@ -20,13 +20,15 @@ ## 2. Cluster node summary -| Node | IP | CPU / RAM | Storage | Current load | Use for | -|----------|----------------|------------|----------------|-------------------|----------------------------------| -| ml110 | 192.168.11.10 | 6c / 125GB | 907GB (26% used) | 34 containers | Lightweight / management only | -| r630-01 | 192.168.11.11 | 32c / 503GB| thin1 200GB+ | 0 | New VMs (DBIS, Sankofa, RPC) | -| r630-02 | 192.168.11.12 | 56c / 251GB| thin2–thin6 | Some VMs | New VMs (HA pair, heavy workload)| -| r630-03 | 192.168.11.13 | 32c / 512GB| Available | 0 | Future expansion | -| r630-04 | 192.168.11.14 | 32c / 512GB| Available | 0 | Future expansion | +| Node | IP | CPU / RAM (API) | Storage (high level) | Guests (live) | Use for | +|----------|----------------|-----------------|----------------------|---------------|----------------------------------| +| ml110 | 192.168.11.10 | 6c / ~63 GiB | Thin pools mostly empty | **0** | Lightweight / quorum member | +| r630-01 | 192.168.11.11 | 32c / ~126 GiB | thin1 + data busy | **57** | Primary compute (check thin %) | +| r630-02 | 192.168.11.12 | 56c / ~126 GiB | thin1-r630-02 … thin6 | **41** | Heavy workloads / infra CTs | +| r630-03 | 192.168.11.13 | 32c / ~126 GiB | data + thin*-r630-03 | **19** | Besu / RPC expansion | +| r630-04 | 192.168.11.14 | 32c / ~126 GiB | data + mev-local-lvm | **19** | Besu / app CTs | + +Source: `pvesh get /nodes`, `/cluster/resources` (**2026-05-09**). See [PROXMOX_CLUSTER_ARCHITECTURE.md](../02-architecture/PROXMOX_CLUSTER_ARCHITECTURE.md). --- diff --git a/docs/03-deployment/REMAINING_DEPLOYMENTS_FOR_FULL_NETWORK_COVERAGE.md b/docs/03-deployment/REMAINING_DEPLOYMENTS_FOR_FULL_NETWORK_COVERAGE.md index 20ee8c4b..9199c5f9 100644 --- a/docs/03-deployment/REMAINING_DEPLOYMENTS_FOR_FULL_NETWORK_COVERAGE.md +++ b/docs/03-deployment/REMAINING_DEPLOYMENTS_FOR_FULL_NETWORK_COVERAGE.md @@ -52,7 +52,7 @@ | Chain 138 liquidity | **Re-verify required** — prior run reported cUSDT/cUSDC liquidity add; this checklist previously showed zero liquidity. Treat liquidity state as unknown until reconfirmed on-chain. | | CCIP 138 → 1, 56, 137, 10, 42161, 43114, 8453, 100, 25, **42220 (Celo)** | Configured/live in current repo-backed status. Wemix (1111) is still the unresolved lane. | | Alltra 138 ↔ 651940 | ALT path live. | -| cW* on public chains | No longer design-only. Current machine-readable deployment artifacts show active `cW*` inventory on `1, 10, 25, 56, 100, 137, 8453, 42161, 42220, 43114`; `1111` remains deferred. | +| cW* on public chains | No longer design-only. Current machine-readable deployment artifacts show active `cW*` inventory on **nine** public chains `1, 10, 56, 100, 137, 8453, 42161, 42220, 43114` (Cronos `25` excluded from the promoted surface); `1111` remains deferred. | | LINK for CCIP | Fund bridges per lane so cross-chain messages execute. | --- @@ -92,7 +92,7 @@ | Step | Action | Ref | |------|--------|-----| -| C.1 | **Remaining token gaps:** focus on chains still deferred or undocumented, with Wemix first. The active set already includes `1, 10, 25, 56, 100, 137, 8453, 42161, 42220, 43114`. | [PHASE_C_CW_AND_EDGE_POOLS_RUNBOOK](PHASE_C_CW_AND_EDGE_POOLS_RUNBOOK.md), [TOKEN_CONTRACT_DEPLOYMENTS_REMAINING](../11-references/TOKEN_CONTRACT_DEPLOYMENTS_REMAINING.md) §3 | +| C.1 | **Remaining token gaps:** focus on chains still deferred or undocumented, with Wemix first. The nine-chain promoted active set is `1, 10, 56, 100, 137, 8453, 42161, 42220, 43114` (exclude Cronos `25` from that public claim). | [PHASE_C_CW_AND_EDGE_POOLS_RUNBOOK](PHASE_C_CW_AND_EDGE_POOLS_RUNBOOK.md), [TOKEN_CONTRACT_DEPLOYMENTS_REMAINING](../11-references/TOKEN_CONTRACT_DEPLOYMENTS_REMAINING.md) §3 | | C.2 | **Remaining pool gaps:** do not assume public-chain edge pools are globally missing. The current generated artifacts show meaningful PMM and wrapped-mesh rollout already present; work remaining is now per-chain/per-venue and should be taken from the latest mesh matrix and phase-completion artifacts. | [PHASE_C_CW_AND_EDGE_POOLS_RUNBOOK](PHASE_C_CW_AND_EDGE_POOLS_RUNBOOK.md), [LIQUIDITY_POOLS_MASTER_MAP](../11-references/LIQUIDITY_POOLS_MASTER_MAP.md) § Public-chain cW* | | C.3 | **Stabilization bot / peg bands** (optional): Run bot and peg-band config from cross-chain-pmm-lps for cW* peg maintenance. | [cross-chain-pmm-lps/README.md](../../cross-chain-pmm-lps/README.md) | diff --git a/docs/04-configuration/CHAIN138_SYSTEM_OF_RECORD_MODEL.md b/docs/04-configuration/CHAIN138_SYSTEM_OF_RECORD_MODEL.md new file mode 100644 index 00000000..ce150dd1 --- /dev/null +++ b/docs/04-configuration/CHAIN138_SYSTEM_OF_RECORD_MODEL.md @@ -0,0 +1,73 @@ +# Chain 138 System-of-Record Model + +Status: canonical boundary document for explaining what Chain 138 records, verifies, and does not claim to replace. + +## Summary + +Chain 138 is the synchronized settlement and verification layer for the GRU ecosystem. It records reserve-linked asset state, transactional state, collateral-validation state, token mappings, proof references, and settlement events. + +Chain 138 should be presented as an auditable distributed-ledger system-of-record and proof layer. It should not be presented as the legal custodian of every underlying off-chain asset. + +## Boundary + +| Domain | Chain 138 role | External evidence / authority | +|---|---|---| +| Reserve balances | Records and synchronizes reserve-linked state and proof references. | Custodians, banks, trustees, UCC filings, Euroclear records, exchange reporting, and qualified counterparties. | +| Settlement positions | Records transactional state, balances, mappings, and settlement transitions. | Participant ledgers, payment systems, banking rails, RTGS/EMI systems, and reconciliation APIs. | +| Collateral validation | Anchors validation state and provides audit trails. | Custodian attestations, inventory records, legal filings, exchange reports, and internal reserve-grade controls. | +| c* assets | Native Chain 138 compliant representations. | GRU registry, UniversalAssetRegistry, token contracts, and reserve evidence. | +| cW* assets | Public-chain transport representations of c* source assets. | Public-chain token contracts, bridge/mapping evidence, and Chain 138 source-asset records. | +| Prices | May expose internal accounting, oracle, or report API data for dApps. | Wallet fiat displays depend on external providers such as CoinGecko, CMC, Etherscan, or provider-specific pricing systems. | + +## What Chain 138 Is + +- An EVM-compatible settlement and verification network. +- A synchronized record of GRU reserve-linked asset and settlement state. +- A proof and reconciliation layer for c* and cW* asset relationships. +- A source of token metadata, mappings, contract state, and report API evidence. +- An audit trail for collateral validation, reserve checks, and settlement events. + +## What Chain 138 Is Not + +- Not a speculative cryptocurrency thesis. +- Not an algorithmic stablecoin mechanism. +- Not a replacement for regulated custodians, banks, trustees, or official filing systems. +- Not a guarantee that external wallets will display USD prices. +- Not a claim that cW* wrappers are official third-party issuer tokens. + +## API and Evidence Flow + +Recommended provider-facing evidence flow: + +```text +Underlying reserve evidence + -> reserve catalog / custodian / filing / exchange record + -> Chain 138 reserve or asset state + -> c* native compliant representation + -> cW* public-chain transport representation + -> token-aggregation report API + -> provider packet / wallet metadata / tracker submission +``` + +## Operational Cadence + +Reserve verification, reconciliation, rebasing, and audit workflows should be described as rolling checks that operate at 6-second intervals where the integration is live. Chain 138's 2-second block interval supports settlement-state updates, while 6-second reserve verification aligns with the PMM mesh and keeper cadence used across the ecosystem. + +## Acceptance Criteria for Provider Claims + +Do not promote a claim as externally accepted until there is evidence for the specific provider surface: + +| Claim | Required evidence | +|---|---| +| Chain metadata accepted | Provider or registry page shows Chain 138 with correct chain ID, RPC, explorer, icon, and native currency. | +| Token metadata accepted | Provider page or wallet shows correct symbol, name, decimals, logo, and contract. | +| Price accepted | Provider returns a current nonzero price for the exact CAIP-19 asset ID or contract. | +| Supply accepted | Provider accepts total or circulating supply method and displays it on the public profile/API. | +| Market accepted | Public indexed pool, depth, volume, and pair page are visible and stable. | + +## References + +- [GRU_PROVIDER_POSITIONING_PACKET.md](GRU_PROVIDER_POSITIONING_PACKET.md) +- [GRU_RESERVE_LAYER_EXPLAINER.md](GRU_RESERVE_LAYER_EXPLAINER.md) +- [metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md](metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md) +- [PRICE_FEED_CHAIN138_METAMASK_AND_WALLETS.md](PRICE_FEED_CHAIN138_METAMASK_AND_WALLETS.md) diff --git a/docs/04-configuration/CMC_TOP10_ECOSYSTEM_ACCESSIBILITY_MATRIX.md b/docs/04-configuration/CMC_TOP10_ECOSYSTEM_ACCESSIBILITY_MATRIX.md new file mode 100644 index 00000000..291bb57d --- /dev/null +++ b/docs/04-configuration/CMC_TOP10_ECOSYSTEM_ACCESSIBILITY_MATRIX.md @@ -0,0 +1,23 @@ +# CMC Top 10 Ecosystem Accessibility Matrix + +- Generated: `2026-05-11T22:34:46.683039+00:00` +- Source: [CoinMarketCap coins page](https://coinmarketcap.com/coins/) +- Observed: `2026-05-11` +- Boundary: Ranks are volatile; rerun or update this snapshot before external outreach. + +| Rank | Token | Accessibility | DBIS touchpoint | Repo-side timeframe | Repo-doable next work | +| --- | --- | --- | --- | --- | --- | +| 1 | BTC (Bitcoin) | non_evm_wrapped_lane_required | cWBTC / BTC reserve-or-wrapper evidence lane | 1-2 weeks repo evidence after wallet/venue binding | Bind canonical BTC custody/address evidence fields.
Add BTC venue and wrapped-asset evidence placeholders.
Keep claims as provenance-only until custody and liquidity are independently evidenced. | +| 2 | ETH (Ethereum) | native_evm_core_surface | WETH/cWETH, Ethereum Mainnet cWUSDC, gas/quote evidence | 1-3 days repo hardening | Refresh Ethereum pool and quote-side evidence.
Add CMC/Dex/Gecko sanity checks for ETH-paired surfaces. | +| 3 | USDT (Tether USDt) | evm_quote_asset_and_wrapped_transport | cUSDT / cWUSDT | 1-3 days repo-side; provider acceptance external | Refresh cUSDT/cWUSDT provider packet fields.
Validate official USDT quote addresses per chain. | +| 4 | XRP (XRP) | xrpl_lane_required | XRPLAdapter / wXRP / MintBurnController | 1-2 weeks after wallet/trustline binding | Bind XRPL account and destination tag policy placeholders.
Document XRP reserve, trustline, and issuer requirements. | +| 5 | BNB (BNB) | evm_compatible_bsc_lane | BSC cW* routing and gas surface | 2-5 days repo-side | Refresh BSC cW* pool and official quote evidence.
Check BNB gas budget and CMC report values. | +| 6 | USDC (USD Coin) | primary_focus_ready_for_submission | cUSDC / cWUSDC | submission-ready now; price/listing acceptance external | Keep Etherscan/CoinGecko/CMC/DexScreener packets current.
Maintain exact CAIP-19 discipline for Mainnet cWUSDC. | +| 7 | SOL (Solana) | solana_spl_lane_required | SolanaAdapter and config/solana-gru-bridge-lineup.json | 3-7 days repo-side if mints are bound | Bind SPL mint placeholders and minimum rent/gas targets.
Separate confirmed Chain 138 adapter evidence from native Solana liquidity claims. | +| 8 | TRX (TRON) | tron_wallet_and_energy_lane_required | TronAdapter and derived/canonical Tron wallet evidence | 3-7 days after address confirmation | Bind canonical Tron address policy placeholder.
Document TRX energy/bandwidth and TRC-20 inventory requirements. | +| 9 | DOGE (Dogecoin) | new_non_evm_adapter_or_custody_lane | future DOGE wrapper/custody evidence lane | 1-3 weeks for serious repo evidence | Create DOGE custody and bridge evidence stub.
Keep DOGE out of provider claims until wallet, reserve, and venue evidence exist. | +| 10 | HYPE (Hyperliquid) | new_chain_or_venue_research_required | future Hyperliquid venue/asset touchpoint | 1-3 weeks for discovery/evidence | Open a research stub for chain/asset identifiers and supported custody paths.
Do not include HYPE in liquidity or settlement claims until identifiers are bound. | + +## Operating Rule + +This matrix is a repo-side planning artifact. It improves DBIS coverage discipline, but it does not imply that any external tracker, wallet, exchange, custodian, or market-data provider has accepted a token. diff --git a/docs/04-configuration/CWUSDC_NON_MANUAL_PROVIDER_TASKS.md b/docs/04-configuration/CWUSDC_NON_MANUAL_PROVIDER_TASKS.md new file mode 100644 index 00000000..593ec276 --- /dev/null +++ b/docs/04-configuration/CWUSDC_NON_MANUAL_PROVIDER_TASKS.md @@ -0,0 +1,108 @@ +# cWUSDC Non-Manual Provider Tasks + +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`). + +## Current State + +| Area | Status | Evidence | +|---|---|---| +| Repo-controlled public URL prerequisites | Passing | `8 / 8` `d-bis.org` URLs returned HTTP `200` in `reports/status/cwusdc-etherscan-prereq-urls-latest.md`. | +| CI-safe provider readiness | Passing for repo-controlled checks | `reports/status/cwusdc-provider-readiness-ci-latest.md`; external blockers are advisory. | +| Etherscan token page visibility | Passing | External tracker probe sees the token page and required text. | +| CMC DEX visibility | Passing | External tracker probe sees the CMC DEX token page. | +| GeckoTerminal V3 pool visibility | Passing | V3 pool API returns `data`. | +| GeckoTerminal V2 pool visibility | External / rate-limit blocker | Latest probe returned HTTP `429`, so it is treated as an external advisory blocker until the next successful probe. | +| CoinGecko price API | External blocker | API responds but does not include the cWUSDC contract key. | +| DexScreener token APIs | External blocker | Token-pairs and tokens endpoints return empty arrays. | +| EVM liquidity-gap planner rows | No current rows | Latest planner summary shows `rows = 0`. | +| Non-EVM funding requirements | Open, operator-bound | Solana, Tron, XRPL, and other non-EVM requirements need wallet/asset/target binding before claims. | +| Mainnet cWUSDC supply attestation | Refreshed | `reports/status/cwusdc-supply-circulating-attestation-latest.md`; circulating supply `10451316981.309788`. | +| Global cUSDC/cWUSDC family proof | Refreshed | `reports/status/global-cusdc-cwusdc-family-supply-proof-latest.md`; `22 / 23` entries proved for aggregate. | +| Etherscan Value dossier | Refreshed | `reports/status/cwusdc-etherscan-value-dossier-latest.md`; ready for external submission, but Etherscan USD Value not ready. | +| Etherscan Value propagation monitor | Refreshed | `reports/status/cwusdc-etherscan-value-propagation-latest.md`; market cap fields remain blank. | +| Evidence bundle index | Complete | `docs/04-configuration/etherscan/CWUSDC_EVIDENCE_BUNDLE_INDEX.md`. | +| Supply/circulating methodology | Complete | `docs/04-configuration/etherscan/CWUSDC_SUPPLY_AND_CIRCULATING_METHODOLOGY.md`. | +| Security/audit disclosure | Complete | `docs/04-configuration/etherscan/CWUSDC_SECURITY_AND_AUDIT_DISCLOSURE.md`; formal third-party audit URL still not recorded. | +| 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`. | + +## Primary Automation + +| Task | Command | Output | Gate behavior | +|---|---|---|---| +| Run all non-manual provider checks | `pnpm cwusdc:provider-checks` | `reports/status/cwusdc-provider-handoff-latest.{json,md}` plus prerequisite/tracker/liquidity reports | Does not fail on external provider blockers unless the wrapped command itself fails unexpectedly. | +| Run CI-safe provider readiness | `pnpm cwusdc:provider-ci` | `reports/status/cwusdc-provider-readiness-ci-latest.{json,md}` | Fails only when repo-controlled public URL prerequisites fail. External provider blockers remain advisory. | +| Rebuild handoff from latest JSON | `pnpm cwusdc:provider-handoff` | `reports/status/cwusdc-provider-handoff-latest.{json,md}` | Uses existing latest JSON files; no network calls. | + +## Individual Non-Manual Tasks + +| 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. | +| 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. | +| Generate global cUSDC/cWUSDC family supply proof | `python3 scripts/verify/generate-global-cusdc-cwusdc-family-supply-proof.py` | `reports/status/global-cusdc-cwusdc-family-supply-proof-latest.json` and related report output. | Safe when provider packets need refreshed family supply context. | +| Monitor Etherscan Value propagation | `python3 scripts/verify/monitor-cwusdc-etherscan-value-propagation.py` | `reports/status/cwusdc-etherscan-value-propagation-latest.json` and related report output. | Read-only; useful after provider submissions or indexing changes. | + +## Public Probe Commands + +Use these only for quick spot checks; the wrapper scripts above are preferred because they write evidence files. + +```bash +curl -fsS 'https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true' | jq + +curl -fsS 'https://api.dexscreener.com/token-pairs/v1/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a' | jq + +curl -fsS 'https://api.dexscreener.com/tokens/v1/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a' | jq + +curl -fsS 'https://api.geckoterminal.com/api/v2/networks/eth/pools/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3' | jq + +curl -fsS 'https://api.geckoterminal.com/api/v2/networks/eth/pools/0xc28706f899266b36bc43cc072b3a921bdf2c48d9' | jq +``` + +## Tasks Excluded From This Non-Manual List + +These still require manual or external intervention: + +| Excluded task | Reason | +|---|---| +| Submit Etherscan token profile/logo/social update | Requires authenticated Etherscan account and Etherscan review. | +| Submit CoinGecko listing/update | Requires CoinGecko form/support flow and provider review. | +| Submit CoinMarketCap listing/update | Requires CMC form/support flow and provider review. | +| Submit DexScreener paid/profile/update flow | Requires DexScreener provider flow or marketplace action. | +| Capture wallet screenshots | Requires wallet UI interaction. | +| Add liquidity, swap, bridge, mint, approve, or rebalance | Broadcasts transactions or moves funds; operator approval required. | +| Attach UCC, Euroclear, custodian, exchange, or counterparty records | Requires off-repo authenticated evidence sources. | +| Bind non-EVM wallets/assets/minimum funding targets | Requires operator confirmation and network-specific custody data. | + +## Maintenance Rule + +After any non-manual run, update or review: + +- [metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md](metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md) +- [CWUSDC_PROVIDER_SUBMISSION_PACKET.md](CWUSDC_PROVIDER_SUBMISSION_PACKET.md) +- [RESERVE_VERIFICATION_EVIDENCE_INDEX.md](RESERVE_VERIFICATION_EVIDENCE_INDEX.md) +- `reports/status/cwusdc-provider-handoff-latest.md` +- `docs/04-configuration/etherscan/CWUSDC_EVIDENCE_BUNDLE_INDEX.md` +- `docs/04-configuration/etherscan/CWUSDC_PROVIDER_RESPONSE_TRACKER.md` + +Do not mark external provider work as `accepted` until the public provider page/API confirms acceptance. + +## Last Completion Run + +Completed on 2026-05-11: + +```bash +pnpm cwusdc:provider-checks +pnpm cwusdc:provider-ci +python3 scripts/verify/generate-cwusdc-supply-circulating-attestation.py +python3 scripts/verify/generate-global-cusdc-cwusdc-family-supply-proof.py +python3 scripts/verify/monitor-cwusdc-etherscan-value-propagation.py +pnpm cwusdc:etherscan-dossier +``` + +All repo-safe commands completed. Remaining blockers require external provider indexing/review or operator-bound non-EVM/funding data. diff --git a/docs/04-configuration/CWUSDC_OPERATOR_SAFE_PROVIDER_CHECKLIST.md b/docs/04-configuration/CWUSDC_OPERATOR_SAFE_PROVIDER_CHECKLIST.md new file mode 100644 index 00000000..ed5d4b8c --- /dev/null +++ b/docs/04-configuration/CWUSDC_OPERATOR_SAFE_PROVIDER_CHECKLIST.md @@ -0,0 +1,33 @@ +# cWUSDC Operator-Safe Provider Checklist + +Status: repo-side checklist for improving the cWUSDC / Chain 138 provider posture without submitting external forms, moving funds, approving tokens, swapping, bridging, or broadcasting transactions. + +## Repo-Doable Now + +| Task | Command / artifact | Output | +|---|---|---| +| Refresh provider CI | `pnpm cwusdc:provider-ci` | `reports/status/cwusdc-provider-readiness-ci-latest.*` | +| Refresh provider monitor snapshot | `pnpm cwusdc:provider-monitor` | `reports/status/cwusdc-provider-monitoring-snapshot-latest.*` | +| Build provider submission prefill | `pnpm cwusdc:submission-prefill` | `reports/status/cwusdc-provider-submission-prefill-latest.*` | +| Build external packet index | `pnpm provider:submission-index` | `docs/04-configuration/EXTERNAL_SUBMISSION_PACKET_INDEX.md` | +| Build CMC top-10 coverage | `pnpm ecosystem:cmc-top10` | `docs/04-configuration/CMC_TOP10_ECOSYSTEM_ACCESSIBILITY_MATRIX.md` | +| Build non-EVM requirement stubs | `pnpm non-evm:requirements` | `config/non-evm-lane-requirements.json` | +| Check CMC-shaped report sanity | `pnpm provider:cmc-sanity` | `reports/status/cmc-provider-report-sanity-latest.*` | + +## External Or Operator-Bound + +| Task | Boundary | +|---|---| +| Etherscan profile approval | Requires Etherscan review and acceptance. | +| CoinGecko price API entry | Requires CoinGecko listing/indexing acceptance. | +| CoinMarketCap full token profile/value | Requires CMC review/indexing acceptance. | +| DexScreener pair indexing/profile | Requires DexScreener indexing/profile acceptance. | +| Market-depth or peg claim | Requires real quote-side liquidity, fresh public events, and truthful caveats. | +| Non-EVM production liquidity claim | Requires canonical wallets, asset IDs, gas/reserve targets, venue evidence, and funding policy closure. | + +## Claim Discipline + +- Use `eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` for Mainnet cWUSDC provider work. +- Keep Chain 138 cUSDC evidence as provenance/system-of-record context, not as Ethereum Mainnet cWUSDC supply. +- Do not describe cWUSDC as Circle-issued USDC. +- Treat repo reports as submission evidence until a provider visibly accepts the asset. diff --git a/docs/04-configuration/CWUSDC_PROVIDER_SUBMISSION_PACKET.md b/docs/04-configuration/CWUSDC_PROVIDER_SUBMISSION_PACKET.md new file mode 100644 index 00000000..d9e3616a --- /dev/null +++ b/docs/04-configuration/CWUSDC_PROVIDER_SUBMISSION_PACKET.md @@ -0,0 +1,135 @@ +# cWUSDC Provider Submission Packet + +Status: consolidated cross-provider packet for Ethereum Mainnet `cWUSDC`. Use provider-specific packets for final form fields, but keep this file as the shared narrative and evidence source. + +## Asset Identity + +| Field | Value | +|---|---| +| Network | Ethereum Mainnet | +| Chain ID / CAIP-2 | `1` / `eip155:1` | +| Contract | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| CAIP-19 | `eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Token name | `Wrapped cUSDC` | +| Symbol | `cWUSDC` | +| Decimals | `6` | +| Asset class | Compliant wrapped public-network transport representation | +| Canonical source asset | Chain 138 `cUSDC` | +| Source asset address | `0xf22258f57794CC8E06237084b353Ab30fFfa640b` | + +## Provider Description + +Use this for Etherscan, CoinGecko, CMC, wallet registries, and support tickets: + +```text +cWUSDC is the Ethereum Mainnet compliant wrapped transport representation of Chain 138 cUSDC in the DBIS GRU asset family. It is used for public-network mirrored settlement, proof, and interoperability workflows. cWUSDC is a DBIS/GRU transport asset and should not be presented as official Circle-issued USDC. +``` + +Short version: + +```text +cWUSDC is a DBIS/GRU compliant wrapped transport representation of canonical Chain 138 cUSDC on Ethereum Mainnet. +``` + +## Submission Boundaries + +| Boundary | Required wording | +|---|---| +| Official issuer status | cWUSDC is not Circle-issued USDC. | +| Source-chain relationship | Chain 138 `cUSDC` is the canonical source asset. | +| Public-chain role | Ethereum Mainnet `cWUSDC` is the wrapped transport and proof surface. | +| Price display | Fiat price display depends on external provider acceptance. | +| Liquidity | Public pools are supporting evidence only when indexed and sufficiently liquid. | +| Supply | Submit exact total supply and any provider-requested circulating-supply exclusions. | + +## Evidence Links + +| Evidence | Location | +|---|---| +| Evidence bundle index | [etherscan/CWUSDC_EVIDENCE_BUNDLE_INDEX.md](etherscan/CWUSDC_EVIDENCE_BUNDLE_INDEX.md) | +| Supply/circulating methodology | [etherscan/CWUSDC_SUPPLY_AND_CIRCULATING_METHODOLOGY.md](etherscan/CWUSDC_SUPPLY_AND_CIRCULATING_METHODOLOGY.md) | +| Security and audit disclosure | [etherscan/CWUSDC_SECURITY_AND_AUDIT_DISCLOSURE.md](etherscan/CWUSDC_SECURITY_AND_AUDIT_DISCLOSURE.md) | +| Provider response tracker | [etherscan/CWUSDC_PROVIDER_RESPONSE_TRACKER.md](etherscan/CWUSDC_PROVIDER_RESPONSE_TRACKER.md) | +| Liquidity readiness plan | [etherscan/CWUSDC_LIQUIDITY_READINESS_NO_BROADCAST_PLAN.md](etherscan/CWUSDC_LIQUIDITY_READINESS_NO_BROADCAST_PLAN.md) | +| Etherscan token page | `https://etherscan.io/token/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Provider submission prefill | `reports/status/cwusdc-provider-submission-prefill-latest.md` | +| Screenshot capture checklist | `reports/status/cwusdc-provider-submission-prefill-latest.md` | +| Provider monitoring snapshot | `reports/status/cwusdc-provider-monitoring-snapshot-latest.md` | +| Token logo | `https://explorer.d-bis.org/api/v1/report/logo/cUSDC` | +| CoinGecko report | `https://explorer.d-bis.org/api/v1/report/coingecko?chainId=1` | +| CMC report | `https://explorer.d-bis.org/api/v1/report/cmc?chainId=1` | +| All report | `https://explorer.d-bis.org/api/v1/report/all` | +| Etherscan profile packet | [etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md](etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md) | +| Tracker packet | [coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md](coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md) | +| Etherscan E2E recommendations | [etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md](etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md) | +| Provider matrix | [metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md](metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md) (incl. Money / mUSD ↔ hub `cUSDC` / `cUSDC_V2` and Mainnet `cWUSDC` narrative) | +| Reserve evidence index | [RESERVE_VERIFICATION_EVIDENCE_INDEX.md](RESERVE_VERIFICATION_EVIDENCE_INDEX.md) | + +## Provider-Specific Notes + +| Provider | Submit / emphasize | +|---|---| +| Etherscan | Contract, logo, website, description, verified source, non-Circle disclosure. | +| CoinGecko | Contract, supply proof, logo, website, LP evidence, liquidity caveats, non-affiliation disclosure. | +| CMC | Same as CoinGecko plus CMC DEX discoverability if applicable. | +| MetaMask | EIP-747 can refresh custom-asset metadata; native fiat value requires provider-side price acceptance. | +| DexScreener / GeckoTerminal | Indexed pair URLs, logo/profile links, and liquidity caveats. | +| Wallet registries | Exact CAIP-19 asset ID, logo, name, symbol, decimals, and non-affiliation disclosure. | + +## Incident Response and Escalation + +Use these contacts for provider and reviewer escalation: + +| Purpose | Contact / URL | +|---|---| +| Provider submissions | `submissions@d-bis.org` | +| User support | `support@d-bis.org` | +| Security / responsible disclosure | `https://d-bis.org/security` | +| General contact | `https://d-bis.org/contact` | +| Trust metadata | `https://d-bis.org/.well-known/trust.json` | + +Provider-facing incident language: + +```text +For token metadata, supply, market-data, or security questions related to Ethereum Mainnet cWUSDC, contact submissions@d-bis.org. For security concerns, use the DBIS security page and responsible-disclosure route at https://d-bis.org/security. +``` + +## Submission Checklist + +- [x] Exact Ethereum Mainnet contract address. +- [x] CAIP-19 asset ID. +- [x] Name, symbol, decimals. +- [x] Chain 138 source-asset relationship. +- [x] Non-Circle disclosure. +- [x] Logo URL. +- [x] Etherscan profile packet. +- [x] CoinGecko/CMC report endpoints. +- [x] Supply proof pointer. +- [x] Liquidity caveat. +- [ ] External Etherscan profile accepted. +- [ ] External CoinGecko listing accepted. +- [ ] External CMC listing accepted. +- [ ] MetaMask provider-side price visible. +- [ ] DexScreener token/pair profile accepted. +- [ ] GeckoTerminal profile evidence captured. + +## Follow-Up Evidence + +After each submission, add a dated report under `reports/status/` with: + +- provider name; +- submission date; +- account or contact used; +- ticket, PR, issue, or form confirmation ID; +- exact submitted URLs and contract address; +- provider response; +- public profile URL or screenshot when accepted; +- rejection reason or blocker when blocked; +- next action and owner. + +## Canonical References + +- [GRU_PROVIDER_POSITIONING_PACKET.md](GRU_PROVIDER_POSITIONING_PACKET.md) +- [GRU_RISK_AND_DISCLOSURE_LANGUAGE.md](GRU_RISK_AND_DISCLOSURE_LANGUAGE.md) +- [GRU_NOT_CRYPTO_NOT_STABLECOIN_FAQ.md](GRU_NOT_CRYPTO_NOT_STABLECOIN_FAQ.md) +- [etherscan/CWUSDC_EVIDENCE_BUNDLE_INDEX.md](etherscan/CWUSDC_EVIDENCE_BUNDLE_INDEX.md) diff --git a/docs/04-configuration/DEVIN_GITEA_PROXMOX_CICD.md b/docs/04-configuration/DEVIN_GITEA_PROXMOX_CICD.md index 027134d9..f40fdacf 100644 --- a/docs/04-configuration/DEVIN_GITEA_PROXMOX_CICD.md +++ b/docs/04-configuration/DEVIN_GITEA_PROXMOX_CICD.md @@ -10,7 +10,7 @@ Create a repeatable path where: 1. Devin lands code in Gitea. -2. Gitea Actions validates the repo on the site-wide `act_runner`. +2. Gitea Actions validates the repo on **`act_runner`** (**`ubuntu-latest-heavy`** → CT **5700** `dev-vm`; lighter repos may use **`ubuntu-latest`** on CT **5701**). 3. A successful workflow calls `phoenix-deploy-api`. 4. `phoenix-deploy-api` resolves the repo/branch to a deploy target and runs the matching Proxmox publish command. 5. The deploy service checks the target health URL before it reports success. @@ -63,7 +63,7 @@ The deploy and PR workflows both fetch `origin/main` and `origin/master` before ```text Devin -> push to Gitea - -> Gitea Actions on act_runner (5700) + -> Gitea Actions on act_runner (heavy pool: 5700; standard pool: 5701) -> bash scripts/verify/run-all-validation.sh --skip-genesis -> validates deploy-targets.json structure -> POST /api/deploy to phoenix-deploy-api diff --git a/docs/04-configuration/DEV_VM_GITOPS_PLAN.md b/docs/04-configuration/DEV_VM_GITOPS_PLAN.md index 29302404..27c3f349 100644 --- a/docs/04-configuration/DEV_VM_GITOPS_PLAN.md +++ b/docs/04-configuration/DEV_VM_GITOPS_PLAN.md @@ -117,13 +117,22 @@ bash scripts/create-dev-vm-5700.sh From your workstation (where `/home/intlc/projects` exists): +```bash +# Recommended: preserves .git, excludes heavy artifacts (see script header). +cd ~/projects/proxmox +./scripts/deployment/sync-local-projects-to-dev-vm.sh --dry-run +./scripts/deployment/sync-local-projects-to-dev-vm.sh +``` + +Full cutover steps (Cursor Remote-SSH, secrets, checklist): [DEV_VM_WORKSTATION_MIGRATION_RUNBOOK.md](DEV_VM_WORKSTATION_MIGRATION_RUNBOOK.md). + +Legacy one-liner (drops `.git` — only for disposable trees): + ```bash rsync -avz --exclude='.git' --exclude='node_modules' \ /home/intlc/projects/ dev1@192.168.11.59:/srv/projects/ ``` -Then on the dev VM, for each project: `git init` (if not already), add Gitea remote, push. - --- ## 9. Security and Access diff --git a/docs/04-configuration/DEV_VM_WORKSTATION_MIGRATION_RUNBOOK.md b/docs/04-configuration/DEV_VM_WORKSTATION_MIGRATION_RUNBOOK.md new file mode 100644 index 00000000..09aeed34 --- /dev/null +++ b/docs/04-configuration/DEV_VM_WORKSTATION_MIGRATION_RUNBOOK.md @@ -0,0 +1,183 @@ +# Workstation → Dev VM migration (shared SSH development) + +**Purpose:** Move canonical day-to-day development from **`~/projects` on a laptop/WSL box** to **LXC 5700 (Dev VM, `192.168.11.59`)** under **`/srv/projects`**, so all contributors and tooling use the **same tree** over **SSH** (including Cursor Remote-SSH). + +**Canonical references:** [DEV_VM_GITOPS_PLAN.md](DEV_VM_GITOPS_PLAN.md), [DEV_VM_SSH_REMOTE_ACCESS.md](DEV_VM_SSH_REMOTE_ACCESS.md), `scripts/create-dev-vm-5700.sh`, `scripts/setup-dev-vm-users-and-gitea.sh`. + +--- + +## 1. What moves vs what stays local + +| Item | Where it runs | +|------|----------------| +| **Source tree, builds, tests, Git** | **Dev VM** `/srv/projects` (after migration) | +| **Cursor / IDE UI** | Your laptop — connect with **Remote-SSH** to the Dev VM (code executes on 5700) | +| **Proxmox host shell** (`pct`, `qm`) | **SSH to the node** that runs CT 5700 (see `DEV_VM_SSH_REMOTE_ACCESS.md` — 5700 may live on **r630-04**, not r630-01) | + +Pyright/Cursor language services then analyze files **on the Dev VM** when you use Remote-SSH, which also reduces “huge local monorepo” indexing on WSL. + +--- + +## 2. Preconditions + +1. **CT 5700** exists, is started, **`sshd` reachable:** + `ssh -o ConnectTimeout=5 "${DEV_VM_USER:-dev1}@${IP_DEV_VM:-192.168.11.59}" pwd` +2. **`/srv/projects`** exists and is writable by your dev user (group `dev` or ACLs per setup script). +3. **SSH key** for that user is loaded (`ssh-add -l`). +4. **Network:** Choose one path: + - **LAN / VPN:** `192.168.11.59:22` (same as `IP_DEV_VM`). + - **Cloudflare Tunnel + Access (FQDN):** **`ssh.dev.d-bis.org`** — does **not** work as plain `ssh user@ssh.dev.d-bis.org` to port 22 through the public proxy; use **`cloudflared access ssh`** (see below and [DEV_VM_SSH_REMOTE_ACCESS.md](DEV_VM_SSH_REMOTE_ACCESS.md)). + - **UDM WAN forward:** e.g. **`76.53.10.40:22` → `192.168.11.59:22`** (restrict by allowlist) — then SSH to the **public IP/hostname** on port 22, not the Cloudflare tunnel hostname. + +### 2.1 Probes (copy/paste) + +**LAN (works when this workstation is on VLAN 11 / VPN to `192.168.11.0/24`):** + +```bash +ssh -o BatchMode=yes -o ConnectTimeout=8 dev1@192.168.11.59 true && echo OK +``` + +**FQDN via Cloudflare Access (requires `cloudflared` on PATH + policy / service token):** + +```bash +ssh -o BatchMode=yes -o ConnectTimeout=25 \ + -o ProxyCommand="cloudflared access ssh --hostname %h" \ + dev1@ssh.dev.d-bis.org true && echo OK +``` + +**All-in-one script (from repo root):** + +```bash +./scripts/deployment/probe-dev-vm-ssh.sh +``` + +**Note:** A plain `ssh dev1@ssh.dev.d-bis.org` without `ProxyCommand` typically **times out** or errors: the hostname resolves to **Cloudflare anycast**, not a raw open `:22` on the public Internet. That is expected for tunnel-routed SSH. + +### 2.2 Install `cloudflared` on the workstation (for FQDN SSH / rsync) + +```bash +mkdir -p ~/bin +ARCH=$(uname -m); case "$ARCH" in x86_64) CF_ARCH=amd64;; aarch64|arm64) CF_ARCH=arm64;; *) CF_ARCH=amd64;; esac +curl -fsSL "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${CF_ARCH}" -o ~/bin/cloudflared +chmod +x ~/bin/cloudflared +export PATH="$HOME/bin:$PATH" +``` + +Then configure **Cloudflare Access** (browser login or `CF_ACCESS_CLIENT_ID` / `CF_ACCESS_CLIENT_SECRET` per [DEV_VM_SSH_REMOTE_ACCESS.md](DEV_VM_SSH_REMOTE_ACCESS.md)). A **TLS handshake failure** from `cloudflared access ssh` usually means missing or invalid Access credentials. + +--- + +## 3. One-time: create or verify the Dev VM + +From repo root (operator machine with SSH to a Proxmox node): + +```bash +bash scripts/create-dev-vm-5700.sh --dry-run # optional +bash scripts/create-dev-vm-5700.sh +``` + +Inside **5700** (or via `pct exec 5700 -- …`): + +```bash +bash /path/to/proxmox/scripts/setup-dev-vm-users-and-gitea.sh +``` + +Add **all** developers’ public keys to `dev1`–`dev4` (or your chosen accounts). + +--- + +## 4. Sync `~/projects` → `/srv/projects` (first migration) + +**Default** source is **`~/projects`** (matches `~/projects` on WSL when `HOME` is `/home/intlc`). + +Dry-run: + +```bash +cd ~/projects/proxmox +./scripts/deployment/sync-local-projects-to-dev-vm.sh --dry-run +``` + +Live sync (**default: no remote deletes** — safe for first migration; remote-only trees like `the_order` are kept): + +```bash +./scripts/deployment/sync-local-projects-to-dev-vm.sh +``` + +**Mirror** the workstation onto the VM (removes remote files not present locally — use only after review): + +```bash +./scripts/deployment/sync-local-projects-to-dev-vm.sh --delete-remote +``` + +**Over Cloudflare Access SSH** (requires `cloudflared` + Access policy / service token; set hostname to the tunnel FQDN): + +```bash +export PATH="$HOME/bin:$PATH" +export RSYNC_RSH='ssh -o BatchMode=yes -o ConnectTimeout=30 -o ProxyCommand="cloudflared access ssh --hostname ssh.dev.d-bis.org"' +export DEV_VM_HOST=ssh.dev.d-bis.org +cd ~/projects/proxmox +./scripts/deployment/sync-local-projects-to-dev-vm.sh --dry-run +``` + +Environment overrides: + +```bash +SOURCE=/home/intlc/projects DEV_VM_USER=dev1 ./scripts/deployment/sync-local-projects-to-dev-vm.sh +``` + +**Behavior:** + +- **Preserves `.git`** (full migration with history). +- **Excludes** `node_modules`, Python venvs, typical build outputs, and by default **`MEV_Bot`** and **`the-order`** (large trees). Set `SKIP_LARGE_LOCAL_TREES=0` to include them. +- **Without `--delete-remote`**, rsync does **not** delete extra files on the Dev VM (recommended first pass). + +**Secrets:** `.env` files may sync; on the Dev VM run `chmod 600 .env` (and siblings). Prefer **Gitea + CI secrets** for long-term; do not commit `.env`. + +--- + +## 5. Cursor: use Remote-SSH from now on + +1. Install **Remote - SSH** (built into Cursor). +2. **SSH config** (`~/.ssh/config` on the laptop), example: + + ```sshconfig + Host dev-vm + HostName 192.168.11.59 + User dev1 + ForwardAgent yes + ``` + + If you use **Cloudflare Access SSH**, follow [DEV_VM_SSH_REMOTE_ACCESS.md](DEV_VM_SSH_REMOTE_ACCESS.md) for `ProxyCommand` / `cloudflared`. + +3. **Connect:** `Cursor` → **Remote-SSH** → **dev-vm** → open folder **`/srv/projects/proxmox`** (or the monorepo root you need). + +4. **Terminal inside Cursor** will be **on 5700** — that is the intended “single place” for `git`, `pnpm`, `forge`, and operator scripts that need LAN RPC. + +--- + +## 6. Git coordination (recommended) + +After the first sync: + +1. Ensure **Gitea** on the Dev VM (or cluster) holds the **authoritative** remotes ([DEV_VM_GITOPS_PLAN.md](DEV_VM_GITOPS_PLAN.md) §6). +2. On the Dev VM: `git remote -v` in each repo; push **`main`** (or default branch) to Gitea. +3. **Stop** using the old WSL tree for shared work, or treat it as **read-only archive** after cutover to avoid drift. + +--- + +## 7. Cutover checklist + +- [ ] 5700 running; `ssh dev1@192.168.11.59` works. +- [ ] `/srv/projects` ready and backed up if it already had content. +- [ ] `sync-local-projects-to-dev-vm.sh --dry-run` reviewed. +- [ ] Full sync completed; large dirs (`SKIP_LARGE_LOCAL_TREES`) decided. +- [ ] Remote `.env` permissions fixed; no secrets committed. +- [ ] Cursor Remote-SSH opens `/srv/projects/proxmox` successfully. +- [ ] `pnpm install` / `forge` smoke test on Dev VM for critical packages. +- [ ] Team notified: **canonical path = Dev VM `/srv/projects`**. + +--- + +## 8. Rollback + +Keep the original **`~/projects`** tree until the team confirms the Dev VM workflow. Rollback = continue editing locally; no automatic deletion of the workstation copy is performed by the sync script. diff --git a/docs/04-configuration/EXPLORER_GAPS_AND_RECOMMENDATIONS.md b/docs/04-configuration/EXPLORER_GAPS_AND_RECOMMENDATIONS.md index b04b69ae..c067cadf 100644 --- a/docs/04-configuration/EXPLORER_GAPS_AND_RECOMMENDATIONS.md +++ b/docs/04-configuration/EXPLORER_GAPS_AND_RECOMMENDATIONS.md @@ -62,11 +62,17 @@ To verify a contract from the explorer when automated verification is unavailable or returns 502: -1. Open **https://explorer.d-bis.org/address/** +1. Open **https://explorer.d-bis.org/addresses/** 2. Go to the **Contract** tab → **Verify & Publish** 3. Choose method: **Via Standard JSON Input** (recommended), **Via Sourcify**, or **Via Multi-file** 4. Upload source (or paste) and provide constructor arguments if needed; submit. +**If you see no “Verify & Publish” (or no contract-verification controls) on the address page:** the hostname may be serving the **custom explorer SPA** only, which does not embed every Blockscout screen. Use one of these instead: + +- **Forge (recommended):** `source scripts/lib/load-project-env.sh` then `./scripts/verify/run-contract-verification-with-proxy.sh` (submits to Blockscout via the local proxy; no explorer form required). +- **Native Blockscout (LAN):** open **`http://192.168.11.140:4000`** (VM Blockscout) and use the instance’s **contract verification** entry (in many Blockscout versions: **Other → Verify contract**, or the **Code / contract** area on the address page — labels vary by release). +- **Cronos:** manual UI is always at **`https://explorer.cronos.org/verifyContract`** (separate stack). + See [08-monitoring/BLOCKSCOUT_VERIFICATION_GUIDE.md](../08-monitoring/BLOCKSCOUT_VERIFICATION_GUIDE.md) for Forge/proxy batch verification and troubleshooting (502, HTML response). ### 3.3 Recommendations diff --git a/docs/04-configuration/EXPLORER_TOKENS_GRU_POLICY.md b/docs/04-configuration/EXPLORER_TOKENS_GRU_POLICY.md index b3377ff8..572c9023 100644 --- a/docs/04-configuration/EXPLORER_TOKENS_GRU_POLICY.md +++ b/docs/04-configuration/EXPLORER_TOKENS_GRU_POLICY.md @@ -2,6 +2,8 @@ **Purpose:** All compliant (c*) tokens listed on [https://explorer.d-bis.org/tokens](https://explorer.d-bis.org/tokens) must be part of the GRU (Global Reserve Unit) — i.e. registered in `UniversalAssetRegistry` as `AssetType.GRU`. +Provider-facing language for explaining GRU, Chain 138, c*, and cW* assets lives in [GRU_PROVIDER_POSITIONING_PACKET.md](GRU_PROVIDER_POSITIONING_PACKET.md), with reserve-layer details in [GRU_RESERVE_LAYER_EXPLAINER.md](GRU_RESERVE_LAYER_EXPLAINER.md) and disclosure language in [GRU_RISK_AND_DISCLOSURE_LANGUAGE.md](GRU_RISK_AND_DISCLOSURE_LANGUAGE.md). + ## Policy 1. **Token lists** diff --git a/docs/04-configuration/EXTERNAL_SUBMISSION_PACKET_INDEX.md b/docs/04-configuration/EXTERNAL_SUBMISSION_PACKET_INDEX.md new file mode 100644 index 00000000..02b42955 --- /dev/null +++ b/docs/04-configuration/EXTERNAL_SUBMISSION_PACKET_INDEX.md @@ -0,0 +1,17 @@ +# External Submission Packet Index + +- Generated: `2026-05-11T22:35:29.997089+00:00` +- All artifacts present: `True` + +| Provider | Status | Primary packet | Supporting artifacts | Next repo action | +| --- | --- | --- | --- | --- | +| Etherscan | repo_ready_external_acceptance_pending | `docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md` (True) | `docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md` (True)
`docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_VALUE_EXECUTION_PLAN.md` (True)
`reports/status/cwusdc-etherscan-value-dossier-latest.json` (True) | Refresh dossier and capture post-submit response evidence. | +| CoinGecko | repo_ready_external_price_entry_missing | `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md` (True) | `docs/04-configuration/coingecko/CWUSDC_MAINNET_EXTERNAL_SUBMISSION_CHECKLIST.md` (True)
`docs/04-configuration/coingecko/submissions/cwusdc-coingecko-listing-request-20260509.json` (True)
`reports/status/cwusdc-external-trackers-live-latest.json` (True) | Keep token-price API blocker visible and attach current supply/liquidity caveats. | +| CoinMarketCap | dex_page_visible_full_value_acceptance_pending | `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md` (True) | `reports/status/token-aggregation-cmc-report-chain1-latest.json` (True)
`reports/status/cmc-provider-report-sanity-latest.json` (True)
`reports/status/cmc-top10-ecosystem-coverage-latest.json` (True) | Use CMC sanity report to avoid overclaiming liquidity or quote-asset identity. | +| DexScreener | api_not_indexing_pairs | `docs/04-configuration/dexscreener/CWUSDC_DEXSCREENER_INDEXING_AND_PROFILE_PACKET_20260509.md` (True) | `reports/status/cwusdc-external-trackers-live-latest.json` (True)
`reports/status/cwusdc-provider-handoff-latest.md` (True) | Keep pair/profile request evidence updated after fresh public swap/liquidity events. | +| GeckoTerminal | pool_api_visible_low_reserve | `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md` (True) | `reports/status/cwusdc-external-trackers-live-latest.json` (True)
`reports/status/cmc-provider-report-sanity-latest.json` (True) | Track reserve USD and 24h volume separately from listing acceptance. | +| MetaMask | metadata_path_ready_price_provider_external | `docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md` (True) | `docs/04-configuration/metamask/METAMASK_EIP747_CONTRACT_METADATA_REFERENCE_PACKET.md` (True)
`docs/04-configuration/metamask/METAMASK_CWUSDC_API_FEED_SPIDER_WEB_RESEARCH.md` (True)
`reports/status/cwusdc-provider-readiness-ci-latest.json` (True) | Keep CAIP-19, EIP-747, logo URL, and external price-provider blockers aligned. | + +## Boundary + +This index tracks repo-side evidence availability only. Provider submission, review, acceptance, and price propagation remain external states. diff --git a/docs/04-configuration/GITEA_ACT_RUNNER_SETUP.md b/docs/04-configuration/GITEA_ACT_RUNNER_SETUP.md index 1ccb81d1..ca76d1ef 100644 --- a/docs/04-configuration/GITEA_ACT_RUNNER_SETUP.md +++ b/docs/04-configuration/GITEA_ACT_RUNNER_SETUP.md @@ -1,102 +1,133 @@ # Gitea act_runner Setup -**Last Updated:** 2026-03-27 (bootstrap script + `ubuntu-latest` labels) +**Last Updated:** 2026-05-11 **Gitea:** https://gitea.d-bis.org -**Runner host:** dev-vm (VMID 5700) at 192.168.11.59 (Gitea HTTP on that host: port 3000) -**Which Proxmox node?** VMID 5700 is not fixed to one server—confirm before `pct exec`: +## Runner layout (two pools) + +| CT (VMID) | Hostname | Labels | Use | +|-----------|----------|--------|-----| +| **5700** | `dev-vm` | **`ubuntu-latest-heavy`** | Heavy monorepo CI (`pnpm`, `run-all-validation`) — **proxmox** workflows use `runs-on: ubuntu-latest-heavy`. | +| **5701** | `gitea-runner-1` | **`ubuntu-latest`**, **`ubuntu-22.04`**, **`ubuntu-20.04`** | Default pool for other repos / lighter workflows that keep `runs-on: ubuntu-latest`. | + +Both CTs live on **r630-04** (`192.168.11.14`) as of live inventory; confirm before `pct exec`: ```bash -ssh root@192.168.11.10 'pct list | grep 5700' -ssh root@192.168.11.11 'pct list | grep 5700' -ssh root@192.168.11.12 'pct list | grep 5700' +ssh root@192.168.11.14 'pct list | grep -E "5700|5701"' ``` -Use the node where 5700 is **running** (often r630-02 / 192.168.11.12). +**Gitea HTTP** on dev-vm: **`192.168.11.59:3000`** (from CT **5700**, same host as Gitea when Actions hit the LAN URL). --- -## Prerequisites +## Config templates (repo) -1. **Registration token** — Get from Gitea Admin → Actions → Runners: - - https://gitea.d-bis.org/-/admin/actions/runners - - Or org-level: https://gitea.d-bis.org/d-bis/settings/actions/runners +Canonical **`act_runner`** YAML (Docker limits, `fetch_interval`, labels): -2. **Docker** (optional but recommended) — For running jobs in isolated containers. Install on dev-vm if not present. +- `config/gitea-act-runner/config-5700-heavy.yaml` +- `config/gitea-act-runner/config-5701-standard.yaml` + +Deploy to both CTs and restart daemons (LAN, repo root): + +```bash +bash scripts/dev-vm/apply-act-runner-config.sh +``` + +Job containers use **bridge** networking and **`privileged: false`** on both runners; resource caps differ by design (**5700**: 4 CPU / 10 GiB RAM; **5701**: 2 CPU / 4 GiB RAM). --- -## Install act_runner +## Register or re-register runners -### Site-wide (admin API token, recommended) +Requires **`GITEA_TOKEN`** (admin) in **repo root** `.env**. -From the **proxmox** repo root, with **`GITEA_TOKEN`** (admin) in root `.env`: +### Heavy pool (5700) + +Default labels: **`ubuntu-latest-heavy:docker://docker.gitea.com/runner-images:ubuntu-latest`** ```bash bash scripts/dev-vm/bootstrap-gitea-act-runner-site-wide.sh ``` -This calls `GET /api/v1/admin/runners/registration-token`, registers **act_runner** on CT **5700** with label **`ubuntu-latest`** (daemon shows `ubuntu-latest:host`, which matches workflow `runs-on: ubuntu-latest`), installs **systemd**, and starts the service. To re-register (e.g. change labels), run with `RUNNER_FORCE_REREGISTER=1`. - -### Manual registration token +To pick up new labels after changing Docker image tokens in Gitea: ```bash -# From repo root; replace with the node that hosts 5700 (e.g. 192.168.11.12): -GITEA_RUNNER_REGISTRATION_TOKEN= ssh root@ "pct exec 5700 -- bash -s" < scripts/dev-vm/setup-act-runner.sh +RUNNER_FORCE_REREGISTER=1 bash scripts/dev-vm/bootstrap-gitea-act-runner-site-wide.sh ``` -Or SSH into dev-vm (192.168.11.59) and run manually: +### Standard pool (5701) + +Registers **`ubuntu-latest`** + **`ubuntu-22.04`** + **`ubuntu-20.04`** against `http://IP_DEV_VM:3000`: ```bash -cd /opt/act_runner -GITEA_RUNNER_REGISTRATION_TOKEN= bash /path/to/setup-act-runner.sh +bash scripts/dev-vm/bootstrap-gitea-act-runner-secondary-lan.sh ``` -**Instance URL for `register`:** From inside dev-vm, Gitea is usually reachable as `http://127.0.0.1:3000` (same host). The setup script defaults to `http://192.168.11.59:3000`; override if needed: +Re-register: ```bash -INSTANCE=http://127.0.0.1:3000 GITEA_RUNNER_REGISTRATION_TOKEN= bash setup-act-runner.sh +RUNNER_FORCE_REREGISTER=1 bash scripts/dev-vm/bootstrap-gitea-act-runner-secondary-lan.sh +``` + +Low-level script (custom VMID / labels / instance URL): + +```bash +export DEV_VM_VMID=5701 +export GITEA_RUNNER_INSTANCE='http://192.168.11.59:3000' +export RUNNER_LABELS='ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest' +bash scripts/dev-vm/bootstrap-gitea-act-runner.sh +``` + +After **`RUNNER_FORCE_REREGISTER=1`**, Gitea may show an **old offline runner** — remove the stale entry under **Admin → Actions → Runners** if desired. + +--- + +## Systemd unit + +The unit passes **`act_runner daemon -c /etc/act_runner/config.yaml`**. Install or refresh: + +```bash +ssh root@192.168.11.14 "pct exec 5700 -- env GITEA_ACTION_URL=http://127.0.0.1:3000 bash -s" < scripts/dev-vm/install-act-runner-systemd.sh +ssh root@192.168.11.14 "pct exec 5701 -- env GITEA_ACTION_URL=http://192.168.11.59:3000 bash -s" < scripts/dev-vm/install-act-runner-systemd.sh ``` --- -## Run as systemd service +## Workflows in **d-bis/proxmox** -Prefer the install script (writes the unit, enables and starts the service): +Canonical validation/deploy workflows use **`runs-on: ubuntu-latest-heavy`** so jobs land on **5700**. Submodule copies under this repo (explorer-monorepo, cross-chain-pmm-lps, OMNIS) keep **`ubuntu-latest`** so they prefer the **5701** pool. + +--- + +## Cache + +Both runners keep **built-in Actions cache** enabled. Optional next step: point **`cache.external_server`** at a shared cache URL in both YAML files so npm/docker layers dedupe across runners (requires a reachable cache service). + +--- + +## Monitoring (per-runner CPU) + +Host **`loadavg` inside LXCs** tracks the **Proxmox node**, not the CT — do not use it to compare **5700** vs **5701**. While jobs run, prefer **`docker stats`** on the runner CT: ```bash -ssh root@ "pct exec 5700 -- bash -s" < scripts/dev-vm/install-act-runner-systemd.sh +bash scripts/dev-vm/act-runner-resource-snapshot.sh ``` -Optional: if Gitea is not on localhost from the runner’s view, set `GITEA_ACTION_URL` (must match a URL the runner can reach): - -```bash -ssh root@ "pct exec 5700 -- env GITEA_ACTION_URL=http://192.168.11.59:3000 bash -s" < scripts/dev-vm/install-act-runner-systemd.sh -``` - -Manual unit (equivalent): `/etc/systemd/system/act-runner.service` with `Environment=GITEA_ACTION_URL=http://127.0.0.1:3000`, then `systemctl daemon-reload && systemctl enable --now act-runner`. - --- ## Troubleshooting | Symptom | Check | |--------|--------| -| Jobs queued, never start | Gitea **Admin → Actions → Runners**: at least one runner **online**. Repo **Settings → Enable Repository Actions**. | -| “No matching runner” / label errors | Workflow `runs-on:` must match runner labels (e.g. `ubuntu-latest`). In Gitea, open the runner details and compare labels. | -| Runner exits / register errors | Ensure `INSTANCE` URL is reachable from the container (`curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3000/`). Re-register with a **new** token if the old one was rotated (remove `.runner` first, then run `setup-act-runner.sh` again). | -| Docker steps fail | Install Docker on dev-vm and ensure the `act_runner` user (or root) can run `docker`. | -| Binary but no service | If `/opt/act_runner/act_runner` exists but there is **no** `/opt/act_runner/.runner`, registration never completed—run `setup-act-runner.sh` with a token. If `.runner` exists but no unit, run `install-act-runner-systemd.sh`. | - ---- - -## Enable Actions per repository - -Repositories must enable Actions: Repository → Settings → Enable Repository Actions +| Jobs queued, never start | **Admin → Actions → Runners**: at least one runner **online** with a label matching **`runs-on`**. Repo **Settings → Actions** enabled. | +| “No matching runner” | Workflow **`runs-on`** must match a label on an online runner (`ubuntu-latest-heavy` vs `ubuntu-latest`). | +| Old **offline** duplicate runners after re-register | Delete via Admin API or: **`bash scripts/dev-vm/delete-offline-gitea-actions-runners.sh --dry-run`** then **`--apply`** (needs **`GITEA_TOKEN`**). Not removable by SSH alone — Gitea stores runner rows in its DB. | +| Runner exits / register errors | **`curl`** from the CT to **`GITEA_RUNNER_INSTANCE`**. Re-register with a fresh admin token after **`RUNNER_FORCE_REREGISTER=1`**. | +| Docker steps fail | Docker installed on the CT; **`act_runner`** runs as **root** in the default unit. | --- ## References -- [Gitea Actions Quick Start](https://docs.gitea.com/usage/actions/quickstart) -- [act_runner](https://gitea.com/gitea/act_runner) +- [Gitea Actions Quick Start](https://docs.gitea.com/usage/actions/quickstart) +- [act_runner](https://gitea.com/gitea/act_runner) diff --git a/docs/04-configuration/GOV_PORTALS_XOM_DEV_DEPLOYMENT.md b/docs/04-configuration/GOV_PORTALS_XOM_DEV_DEPLOYMENT.md index 0c18066d..b7706f60 100644 --- a/docs/04-configuration/GOV_PORTALS_XOM_DEV_DEPLOYMENT.md +++ b/docs/04-configuration/GOV_PORTALS_XOM_DEV_DEPLOYMENT.md @@ -70,7 +70,11 @@ Request Let's Encrypt certificates in NPMplus UI for each domain, or use a wildc - **502 Bad Gateway:** Ensure LXC 7804 is running and portals are built: `pct exec 7804 -- systemctl status gov-portal-DBIS gov-portal-ICCC gov-portal-OMNL gov-portal-XOM` -- **Rebuild a portal:** - `pct exec 7804 -- bash -c 'cd /srv/gov-portals/DBIS && pnpm run build && systemctl restart gov-portal-DBIS'` +- **Rebuild a portal (after code change):** Prefer the repo script (git fetch + rsync + build on CT **7804** on r630-04): + `bash scripts/deployment/sync-gov-portals-ct-7804-from-git.sh` + Requires `GITEA_TOKEN` in project `.env` (or `export GITEA_TOKEN=…`). Use `--skip-fetch` to rebuild from an already-updated local `GOV_PORTALS_SOURCE` tree. + Manual one-liner on the **Proxmox node that runs CT 7804** (default `root@192.168.11.14`): + `pct exec 7804 -- bash -c 'cd /srv/gov-portals/DBIS && git pull origin main && pnpm install && pnpm run build && systemctl restart gov-portal-DBIS'` + (Only works if `/srv/gov-portals/DBIS` is a git checkout; tarball deploys omit `.git` — use the script above.) - **Update from Gitea:** `pct exec 7804 -- bash -c 'cd /srv/gov-portals && git pull && git submodule update --remote && pnpm install'` diff --git a/docs/04-configuration/GRU_NOT_CRYPTO_NOT_STABLECOIN_FAQ.md b/docs/04-configuration/GRU_NOT_CRYPTO_NOT_STABLECOIN_FAQ.md new file mode 100644 index 00000000..d08841d0 --- /dev/null +++ b/docs/04-configuration/GRU_NOT_CRYPTO_NOT_STABLECOIN_FAQ.md @@ -0,0 +1,55 @@ +# GRU / Chain 138 Not-Crypto FAQ + +Status: canonical defensive FAQ for provider submissions, institutional reviews, and support responses. + +## Is GRU a cryptocurrency? + +No. GRU is a monetary-policy and collateral framework for reserve-backed institutional settlement instruments. Chain 138 is the settlement and verification layer that records and reconciles state for the GRU ecosystem. + +## Is Chain 138 a speculative asset network? + +No. Chain 138 is used as programmable settlement, synchronization, and verification infrastructure. It is EVM-compatible so it can use mature tooling, wallets, explorers, and smart-contract interfaces, but its purpose is institutional settlement and auditability rather than speculative token mechanics. + +## Is GRU an algorithmic stablecoin? + +No. GRU should not be described as an algorithmic stablecoin. The framework uses reserve layers, collateral evidence, utilization constraints, and settlement gates. It does not rely on reflexive token incentives as the source of value support. + +## Are c* assets official third-party issuer tokens? + +No, unless a separate issuer agreement explicitly says so. For example, Chain 138 `cUSDC` and Ethereum Mainnet `cWUSDC` should not be represented as official Circle-issued USDC. They are DBIS/GRU compliant representations and wrapped transport assets tied to the Chain 138 source-asset model. + +## Why do cW* assets exist on public chains? + +cW* assets provide public-network transport, proof, and interoperability surfaces for canonical Chain 138 c* assets. They help external wallets, explorers, DEX terminals, and providers index public-chain activity while preserving a clear source-asset relationship back to Chain 138. + +## Why does MetaMask show a token balance but no fiat value? + +Wallet token balances, token metadata, and fiat price data are separate paths. A wallet can show a balance after token detection or EIP-747 import while still missing a provider-accepted price. MetaMask fiat values require upstream provider acceptance and cannot be forced by this repo. + +## Does Chain 138 replace custodians or official records? + +No. Chain 138 synchronizes and records proof-linked settlement and reserve state. Underlying off-chain evidence may remain with UCC filings, Euroclear records, exchanges, regulated custodians, banks, trustees, or qualified counterparties. + +## How should providers classify GRU assets? + +Use infrastructure and settlement language: + +- reserve-backed monetary instrument; +- compliant wrapped transport representation; +- settlement and verification layer; +- collateral validation and reserve reconciliation; +- auditable system-of-record. + +Avoid speculative or promotional language: + +- cryptocurrency; +- algorithmic stablecoin; +- yield product; +- unbacked token; +- official third-party issuer token unless explicitly approved. + +## Canonical References + +- [GRU_PROVIDER_POSITIONING_PACKET.md](GRU_PROVIDER_POSITIONING_PACKET.md) +- [GRU_RISK_AND_DISCLOSURE_LANGUAGE.md](GRU_RISK_AND_DISCLOSURE_LANGUAGE.md) +- [CWUSDC_PROVIDER_SUBMISSION_PACKET.md](CWUSDC_PROVIDER_SUBMISSION_PACKET.md) diff --git a/docs/04-configuration/GRU_PROVIDER_POSITIONING_PACKET.md b/docs/04-configuration/GRU_PROVIDER_POSITIONING_PACKET.md new file mode 100644 index 00000000..ba4c78cc --- /dev/null +++ b/docs/04-configuration/GRU_PROVIDER_POSITIONING_PACKET.md @@ -0,0 +1,71 @@ +# GRU / Chain 138 Provider Positioning Packet + +Status: canonical provider-facing narrative for GRU, Chain 138, c* assets, and cW* wrapped transport assets. + +Use this packet when preparing Etherscan, CoinGecko, CMC, MetaMask, Chainlist, wallet, explorer, DEX-terminal, institutional-counterparty, and support-ticket submissions. Keep provider submissions factual, narrow, and evidence-linked. + +## Core Positioning + +GRU is a reserve-backed monetary-policy and collateral framework. Chain 138 is the programmable settlement, synchronization, and verification layer that records, reconciles, and validates reserve positions, transactional states, and institutional settlement activity. + +The ecosystem should be presented as reserve-backed institutional settlement and verification infrastructure, not as a cryptocurrency, speculative digital asset, algorithmic stablecoin, or yield product. + +Recommended short description: + +```text +GRU defines a reserve-backed monetary-policy framework for compliant monetary instruments and institutional collateral workflows. Chain 138 is the EVM-compatible settlement and verification layer used to synchronize reserve positions, transactional states, and proof records across the GRU asset family. +``` + +Recommended cW* description: + +```text +cW* assets are public-network compliant wrapped transport representations of canonical Chain 138 c* assets. They are used for mirrored settlement, proof, interoperability, and provider-indexed visibility. They should not be represented as official third-party issuer assets unless a separate issuer agreement explicitly supports that claim. +``` + +Recommended Chain 138 description: + +```text +Chain 138 is an EVM-compatible settlement, synchronization, and verification network for GRU reserve-backed instruments, institutional collateral reconciliation, and compliant asset transport. +``` + +## Language Rules + +| Use | Avoid | +|---|---| +| Reserve-backed settlement infrastructure | Cryptocurrency project | +| Monetary-policy and collateral framework | Speculative token model | +| Compliant wrapped transport representation | Official issuer token unless issuer-approved | +| Auditable reserve and settlement synchronization | Algorithmic stablecoin | +| System-of-record and proof layer | Yield, APY, or return-generating product | +| Institutional reconciliation and verification | Unbacked token economy | + +## Audience Calibration + +| Audience | Emphasize | +|---|---| +| Retail wallet users | Token identity, contract address, logo, decimals, network, and non-affiliation disclaimers. | +| Commercial enterprises | Settlement reliability, API reconciliation, auditability, and asset transport boundaries. | +| Banks and EMIs | Reserve evidence, compliance controls, transaction monitoring, custody boundaries, and settlement finality. | +| Sovereign institutions | Monetary-policy framework, reserve eligibility, governance controls, and independent evidence sources. | +| Macroeconomic reviewers | M00/M0/M1 reserve model, utilization constraints, operational buffer, and tranche controls. | +| External data providers | Exact CAIP identifiers, contract addresses, supply proof, market caveats, logo URLs, and explorer links. | + +## Provider Submission Rules + +1. Submit exact chain and contract identifiers, not symbol-only references. +2. Keep Chain 138 source assets separate from public-chain cW* wrappers. +3. Treat wallet metadata, explorer profile metadata, and fiat price metadata as separate acceptance paths. +4. Include explicit non-affiliation language for any asset that references a third-party denomination or ticker. +5. Provide supply proof and reserve evidence as evidence inputs, not as a request for providers to infer facts. +6. Avoid market-depth claims unless public liquidity is indexed, sustained, and linked. +7. Track external submission status as `repo_ready`, `submitted`, `accepted`, or `blocked`. + +## Canonical References + +- [GRU_RESERVE_LAYER_EXPLAINER.md](GRU_RESERVE_LAYER_EXPLAINER.md) +- [CHAIN138_SYSTEM_OF_RECORD_MODEL.md](CHAIN138_SYSTEM_OF_RECORD_MODEL.md) +- [GRU_NOT_CRYPTO_NOT_STABLECOIN_FAQ.md](GRU_NOT_CRYPTO_NOT_STABLECOIN_FAQ.md) +- [GRU_RISK_AND_DISCLOSURE_LANGUAGE.md](GRU_RISK_AND_DISCLOSURE_LANGUAGE.md) +- [RESERVE_VERIFICATION_EVIDENCE_INDEX.md](RESERVE_VERIFICATION_EVIDENCE_INDEX.md) +- [CWUSDC_PROVIDER_SUBMISSION_PACKET.md](CWUSDC_PROVIDER_SUBMISSION_PACKET.md) +- [metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md](metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md) — includes **MetaMask Money / mUSD ↔ GRU hub and `cW*` transport** (subsection “MetaMask Money rail and mUSD — internal GRU alignment”). diff --git a/docs/04-configuration/GRU_RESERVE_LAYER_EXPLAINER.md b/docs/04-configuration/GRU_RESERVE_LAYER_EXPLAINER.md new file mode 100644 index 00000000..02cba142 --- /dev/null +++ b/docs/04-configuration/GRU_RESERVE_LAYER_EXPLAINER.md @@ -0,0 +1,80 @@ +# GRU Reserve Layer Explainer + +Status: canonical plain-language explainer for the GRU M00, M0, and M1 reserve and settlement layers. + +## Summary + +The GRU monetary structure operates across three integrated layers: `M00`, `M0`, and `M1`. + +`M00` is the physical and commodity reserve index layer. `M0` is the cash, cash-equivalent, sovereign, and institutional paper layer. `M1` is the transactional settlement layer that bridges and rebalances M00 and M0 under strict utilization constraints. + +## Layer Model + +| Layer | Role | Provider-safe explanation | +|---|---|---| +| `M00` GRU | Physical and commodity reserve layer | Exposure is maintained across XAU, Precious Metals, Battery Materials, Base Metals, and Building Materials. Each class starts at `1.2` indexed units before collateral adjustment. | +| `M0` GRU | Cash and institutional financial instrument layer | Includes M0 and M1 monetary cash reserves and equivalents, sovereign and central bank-issued instruments, bonds, promissory notes, MTNs, and LTNs that satisfy internal reserve-grade standards. | +| `M1` GRU | Transactional settlement and rebalancing layer | Supports settlement activity while respecting reserve-capacity limits against available M00 and M0 reserves. | + +## M00 Collateral Adjustment + +For each `1 M00 GRU` allocation, the framework maintains exposure across five asset-class indices: + +- XAU +- Precious Metals +- Battery Materials +- Base Metals +- Building Materials + +Each asset class carries an indexed allocation of `1.2` units before collateral adjustment. Physical assets apply the collateral adjustment factor: + +```text +(0.9475^4) = approximately 0.80596628 +``` + +After this adjustment, each `1.2` indexed allocation corresponds to approximately `1.489` SKR-adjusted physical units. + +Provider-language rule: describe this as a collateral-adjusted physical reserve methodology, not as leverage or speculative token appreciation. + +## M0 Reserve Coverage + +The M0 GRU basket is composed of cash reserves, cash equivalents, and qualifying sovereign, central-bank, and top-tier institutional instruments. + +No LTV adjustment is applied inside the M0 accounting framework because the associated instruments are supported by underlying cash reserves maintained at a `5:1` reserve coverage ratio. + +Eligibility should be described conservatively: + +- seasoned AAA-rated paper; +- top-tier institutional paper; +- qualifying emerging-market sovereign and quasi-sovereign issuances; +- instruments satisfying internal credit, liquidity, and reserve-grade standards. + +## M1 Utilization Constraints + +M1 GRU settlement activity dynamically bridges and rebalances the M00 and M0 reserve layers. It must remain within both reserve-capacity limits: + +| Constraint | Maximum utilization | +|---|---:| +| M1 relative to available M00 GRU reserves | `25:1` | +| M1 relative to available M0 GRU reserves | `5:1` | + +When transactional activity approaches reserve utilization thresholds, execution must be segmented into sequential tranches so settlement remains collateralized within available M00 and M0 reserve capacity. + +Provider-language rule: describe the ratios as utilization constraints and reserve gates, not as promotional multipliers. + +## Operational Buffer + +The system target is to maintain and exceed the internal `20%` operational buffer requirement above required collateralization. Rolling reserve checks are expected at 6-second intervals through API-driven validation, reconciliation, rebasing, and audit processes. + +## Recommended Short Text + +```text +GRU operates across M00 physical and commodity reserves, M0 cash and institutional reserve instruments, and M1 transactional settlement capacity. Chain 138 records and verifies reserve-linked settlement state while enforcing utilization constraints and tranche controls designed to keep settlement activity within available collateral capacity. +``` + +## References + +- [GRU_PROVIDER_POSITIONING_PACKET.md](GRU_PROVIDER_POSITIONING_PACKET.md) +- [CHAIN138_SYSTEM_OF_RECORD_MODEL.md](CHAIN138_SYSTEM_OF_RECORD_MODEL.md) +- [RESERVE_VERIFICATION_EVIDENCE_INDEX.md](RESERVE_VERIFICATION_EVIDENCE_INDEX.md) +- [GRU_M00_DIAMOND_INSTITUTIONAL_SPEC.md](GRU_M00_DIAMOND_INSTITUTIONAL_SPEC.md) diff --git a/docs/04-configuration/GRU_RISK_AND_DISCLOSURE_LANGUAGE.md b/docs/04-configuration/GRU_RISK_AND_DISCLOSURE_LANGUAGE.md new file mode 100644 index 00000000..4864ca6d --- /dev/null +++ b/docs/04-configuration/GRU_RISK_AND_DISCLOSURE_LANGUAGE.md @@ -0,0 +1,64 @@ +# GRU / Chain 138 Risk and Disclosure Language + +Status: canonical disclosure language for external provider packets and public-facing submission support. + +## Standard Non-Affiliation Disclosure + +Use this whenever an asset references a third-party currency, ticker, issuer, or public-chain denomination: + +```text +This asset is a DBIS/GRU compliant representation or wrapped transport asset and should not be presented as an official third-party issuer token unless a separate issuer agreement explicitly supports that claim. +``` + +For cWUSDC specifically: + +```text +cWUSDC is the Ethereum Mainnet compliant wrapped transport representation of Chain 138 cUSDC in the DBIS GRU asset family. cWUSDC is not Circle-issued USDC and should not be represented as an official Circle asset. +``` + +## Standard Price Disclosure + +```text +Wallet and explorer fiat values depend on external provider acceptance. Repo-controlled metadata, token lists, report APIs, and EIP-747 wallet prompts can provide correct asset facts and logos, but they do not force MetaMask, Etherscan, CoinGecko, CMC, or other providers to display a fiat price. +``` + +## Standard Liquidity Disclosure + +```text +Public DEX liquidity and volume should be described only from indexed, visible, and current pool evidence. Candidate pool URLs, internal accounting pegs, or sparse liquidity should not be presented as listing-quality price discovery. +``` + +## Standard Reserve Evidence Disclosure + +```text +Reserve evidence may include Chain 138 records, token-aggregation reports, supply proofs, custodian or counterparty records, UCC filings, Euroclear records, exchange reporting, and other qualified institutional sources. Chain 138 records proof-linked state and settlement activity; it does not replace the legal authority of the underlying off-chain evidence source. +``` + +## Standard Utilization Disclosure + +```text +M1 GRU settlement utilization is constrained by available reserve capacity and reserve gates. The M00 and M0 ratios are maximum utilization constraints, not promotional leverage, return, or yield claims. +``` + +## Provider Status Disclosure + +```text +External provider acceptance is provider-controlled. A repo status of repo_ready means the submission package, endpoints, metadata, and evidence are ready for submission; it does not imply that the provider has accepted or displayed the asset. +``` + +## Avoided Claims + +Do not claim: + +- guaranteed fiat price display in wallets; +- official issuer status without explicit authorization; +- CoinGecko, CMC, Etherscan, DexScreener, GeckoTerminal, or MetaMask acceptance before public evidence exists; +- listing-quality liquidity from unindexed or quote-starved pools; +- investor returns, yield, appreciation, or speculative upside; +- legal custody of off-chain assets solely because Chain 138 records proof-linked state. + +## References + +- [GRU_PROVIDER_POSITIONING_PACKET.md](GRU_PROVIDER_POSITIONING_PACKET.md) +- [GRU_NOT_CRYPTO_NOT_STABLECOIN_FAQ.md](GRU_NOT_CRYPTO_NOT_STABLECOIN_FAQ.md) +- [CHAIN138_SYSTEM_OF_RECORD_MODEL.md](CHAIN138_SYSTEM_OF_RECORD_MODEL.md) diff --git a/docs/04-configuration/NPMPLUS_HA_SETUP_GUIDE.md b/docs/04-configuration/NPMPLUS_HA_SETUP_GUIDE.md index ff7c4edf..2638599b 100644 --- a/docs/04-configuration/NPMPLUS_HA_SETUP_GUIDE.md +++ b/docs/04-configuration/NPMPLUS_HA_SETUP_GUIDE.md @@ -4,6 +4,8 @@ **Document Version:** 1.0 **Status:** Active Documentation +**Related execution plan:** [NPMPLUS_MISSION_CRITICAL_DISTRIBUTION_AND_HA_PLAN.md](NPMPLUS_MISSION_CRITICAL_DISTRIBUTION_AND_HA_PLAN.md) (node distribution, sizing, verification checklist). + --- **Date**: 2026-01-20 diff --git a/docs/04-configuration/NPMPLUS_MISSION_CRITICAL_DISTRIBUTION_AND_HA_PLAN.md b/docs/04-configuration/NPMPLUS_MISSION_CRITICAL_DISTRIBUTION_AND_HA_PLAN.md new file mode 100644 index 00000000..7a23367f --- /dev/null +++ b/docs/04-configuration/NPMPLUS_MISSION_CRITICAL_DISTRIBUTION_AND_HA_PLAN.md @@ -0,0 +1,164 @@ +# NPMplus mission-critical distribution and HA plan + +**Status:** Active execution plan +**Last updated:** 2026-05-10 +**Audience:** Operators with Proxmox SSH, LAN access, and edge (UDM / Cloudflare) access. + +This document **completes** the roadmap for **high availability**, **node diversity**, and **measurable uptime** for NPMplus instances **192.168.11.167–.170** (VMIDs **10233–10236**). It is **not** a guarantee of five-nines by itself; pair it with **external synthetics**, **edge failover**, and **error-budget discipline**. + +**Related:** [NPMPLUS_HA_SETUP_GUIDE.md](NPMPLUS_HA_SETUP_GUIDE.md), [ALL_VMIDS_ENDPOINTS.md](ALL_VMIDS_ENDPOINTS.md), [OPERATIONAL_RUNBOOKS.md](../03-deployment/OPERATIONAL_RUNBOOKS.md), [config/ip-addresses.conf](../../config/ip-addresses.conf). + +--- + +## 1. Target architecture (summary) + +| Goal | Action | +|------|--------| +| **No correlated NPM failure on one hypervisor** | Move **10235** and **10236** off **r630-01** to **distinct** nodes (see §4). | +| **Primary + secondary HA** | **Keepalived VIP** (or equivalent) + **replicated/shared NPM state** between **10233** and **10234** per HA guide. | +| **Right-sized CTs** | **10233**: ≥ **2 vCPU**, ≥ **2048 MiB** RAM; **10234**: **verify/correct** memory in UI (config once showed ~24 GiB — align with primary). **10235/10236**: remain **2 vCPU / 2048 MiB** unless metrics dictate otherwise. | +| **Edge** | Cloudflare (or similar): **health checks**, **secondary origin** (**76.53.10.37** for secondary NPM public IP per template), **documented** UDM forwards. | +| **Proof** | **Regional synthetics**, **quarterly game-day** failover, **SLO** tracking (see §8). | + +--- + +## 2. Inventory and roles (canonical) + +| LAN IP | VMID | Role | Public IP (template) | Preferred failover | +|--------|------|------|----------------------|--------------------| +| **.167** | **10233** | Primary NPM (main ingress) | **76.53.10.36** | Pair with **10234** via VIP | +| **.168** | **10234** | Secondary NPM (HA standby) | **76.53.10.37** | Same VIP / sync | +| **.169** | **10235** | Alltra/HYBX / rpc-core-2 NPM | **76.53.10.38** (+ **.42** alt) | Isolate on **non–r630-01** node | +| **.170** | **10236** | Fourth NPM (dev, tunnel, Gitea) | **76.53.10.40** | Isolate on **non–r630-01** node | + +**Live placement note:** Reconcile with cluster before change: +`bash scripts/maintenance/npmplus-cluster-placement-status.sh` + +--- + +## 3. Phase A — Normalize sizing (maintenance window) + +**10233 (primary)** — on node hosting it (e.g. `r630-01`): + +```bash +# Example: 2 vCPU, 2048 MiB RAM (adjust if policy differs) +pct shutdown 10233 # or schedule maintenance; minimize downtime +pct set 10233 -cores 2 -memory 2048 +pct start 10233 +# Verify: curl -sI --max-time 10 http://192.168.11.167:81/ | head -3 +``` + +**10234 (secondary)** — on **r630-02**: + +1. Open **Proxmox UI → CT 10234 → Resources** and confirm **memory** and **swap** are intentional (target **match primary** after resize, e.g. **2048 MiB** RAM, modest swap — **not** multi-TiB values). +2. If CLI shows corrupted limits, fix in UI and **Apply**. +3. `pct reboot 10234` only if needed after correction. + +**10235 / 10236:** No change unless monitoring shows pressure. + +--- + +## 4. Phase B — Redistribute CTs across nodes + +**Objective:** Only **one** of **10233 / 10235 / 10236** on **r630-01** after migration (typically keep **10233** primary on **r630-01**, move **10235** to **r630-03**, **10236** to **r630-04** — adjust names to your capacity). + +**Prerequisites:** + +- **Shared storage** or **migration-compatible storage** between source and target (Proxmox migrate prerequisites). +- **Maintenance window** for each move (brief disconnect). +- Confirm target node: `pvesh get /nodes//status` or UI. + +**Example migrations (run from any cluster shell with privileges):** + +```bash +# Offline migration pattern (replace TARGET_NODE with r630-03, r630-04, etc.) +pct shutdown 10235 +pct migrate 10235 TARGET_NODE +pct start 10235 +# Verify IP .169 still pings and :81 responds + +pct shutdown 10236 +pct migrate 10236 OTHER_NODE +pct start 10236 +# Verify .170 +``` + +**Online migration** if your storage supports it (Proxmox version–dependent): + +```bash +pct migrate 10235 TARGET_NODE --restart +``` + +Document **actual** target nodes in **ALL_VMIDS_ENDPOINTS.md** after completion. + +--- + +## 5. Phase C — NPM application HA (primary + secondary) + +Execute **end-to-end** steps in [NPMPLUS_HA_SETUP_GUIDE.md](NPMPLUS_HA_SETUP_GUIDE.md): + +1. **VIP** (e.g. **192.168.11.166** or documented VIP) with **Keepalived** between **10233** and **10234**. +2. **Shared or replicated** NPM database and certificate paths (NFS / replicated volume / controlled rsync + procedures). +3. **UDM / DNS:** Ensure **primary forward** still matches production; **secondary public IP** ready for **manual or automated** failover per runbook. +4. Point **operator scripts** at **VIP URL** once stable: `NPM_URL=https://:81` (or hostname). + +--- + +## 6. Phase D — Edge and DNS + +| Step | Detail | +|------|--------| +| **Cloudflare** | Load balancer / health checks to **two origins** where possible (**76.53.10.36**, **76.53.10.37**). | +| **UDM** | Document **all** forwards; align **76.53.10.37** with **secondary** NPM when HA goes live. | +| **TLS** | Admin **:81** restricted by IP/VPN where possible. | + +See [DEV_CODESPACES_NEXT_STEPS_CHECKLIST.md](DEV_CODESPACES_NEXT_STEPS_CHECKLIST.md) for **.170** / **76.53.10.40** specifics. + +--- + +## 7. Phase E — Verification scripts (repo) + +| Script | Purpose | +|--------|---------| +| `scripts/maintenance/npmplus-cluster-placement-status.sh` | Shows which node runs **10233–10236**. | +| `scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh` | After NPM stable; use **`NPM_URL`** pointing at VIP when ready. | +| `scripts/verify/verify-end-to-end-routing.sh --profile=public` | Public path regression check. | +| `scripts/maintenance/verify-pve-cluster-health.sh` | Cluster quorum sanity. | + +--- + +## 8. Phase F — SLOs, game day, error budget + +1. **Define SLO** (example): **99.95%** monthly for **HTTPS availability** of critical FQDNs from **two external probe regions** before claiming **99.999%**. +2. **Synthetic checks:** External uptime monitor on **sankofa.nexus**, **explorer.d-bis.org**, **rpc.public-0138.defi-oracle.io**, etc. +3. **Game day (quarterly):** Stop **10233** Docker/nginx safely; confirm **VIP** and/or **Cloudflare** shifts traffic; **RTO** recorded. +4. **Error budget:** Pause optional releases when probes burn budget. + +--- + +## 9. Completion checklist + +Use this as the **definition of done** for “plan completed”: + +- [ ] **10233** resized to ≥ **2 vCPU / 2048 MiB** (or documented alternative). +- [ ] **10234** memory/swap **verified sane** and matches failover intent. +- [ ] **10235** migrated off **r630-01** (or documented exception). +- [ ] **10236** migrated off **r630-01** (or documented exception). +- [ ] **HA**: VIP + **shared/replicated state** **or** documented interim with **secondary origin** in Cloudflare. +- [ ] **Docs updated:** **ALL_VMIDS_ENDPOINTS.md** placement rows; **this file** dated notes. +- [ ] **External probes** green for **30 days** post-change (or agreed burn-in). + +--- + +## 10. Rollback + +- **Migration rollback:** `pct migrate r630-01` (if that was original node) during a window. +- **Sizing rollback:** reduce **cores/memory** with shutdown if instability appears (capture metrics first). + +--- + +## References + +- NPM HA options: [NPMPLUS_HA_SETUP_GUIDE.md](NPMPLUS_HA_SETUP_GUIDE.md) +- Inventory: [ALL_VMIDS_ENDPOINTS.md](ALL_VMIDS_ENDPOINTS.md) +- Edge / RPC: [RPC_ENDPOINTS_MASTER.md](RPC_ENDPOINTS_MASTER.md), [NETWORK_CONFIGURATION_MASTER.md](../11-references/NETWORK_CONFIGURATION_MASTER.md) diff --git a/docs/04-configuration/ORACLE_AND_COINGECKO_COMPLETE.md b/docs/04-configuration/ORACLE_AND_COINGECKO_COMPLETE.md index 07992ea3..91e7a48d 100644 --- a/docs/04-configuration/ORACLE_AND_COINGECKO_COMPLETE.md +++ b/docs/04-configuration/ORACLE_AND_COINGECKO_COMPLETE.md @@ -137,13 +137,13 @@ cd /home/intlc/projects/proxmox/smom-dbis-138 - **Address:** `0x93E66202A11B1772E55407B32B44e5Cd8eda7f22` - **Decimals:** 6 - **Peg:** USD (1:1) -- **Explorer:** https://explorer.d-bis.org/address/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22 +- **Explorer:** https://explorer.d-bis.org/addresses/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22 #### cUSDC (Compliant USD Coin) - **Address:** `0xf22258f57794CC8E06237084b353Ab30fFfa640b` - **Decimals:** 6 - **Peg:** USD (1:1) -- **Explorer:** https://explorer.d-bis.org/address/0xf22258f57794CC8E06237084b353Ab30fFfa640b +- **Explorer:** https://explorer.d-bis.org/addresses/0xf22258f57794CC8E06237084b353Ab30fFfa640b ### Submission Status diff --git a/docs/04-configuration/PRICE_FEED_CHAIN138_METAMASK_AND_WALLETS.md b/docs/04-configuration/PRICE_FEED_CHAIN138_METAMASK_AND_WALLETS.md index a6c61d6e..eaeb4624 100644 --- a/docs/04-configuration/PRICE_FEED_CHAIN138_METAMASK_AND_WALLETS.md +++ b/docs/04-configuration/PRICE_FEED_CHAIN138_METAMASK_AND_WALLETS.md @@ -16,6 +16,8 @@ **Important:** MetaMask does **not** query on-chain oracle contracts for USD display. Even with a working ETH/USD oracle on Chain 138 (`0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6`), MetaMask will not show native USD until Chain 138 and tokens are listed on CoinGecko (or MetaMask’s provider adds support). +Provider-facing positioning and disclosure language now lives in [GRU_PROVIDER_POSITIONING_PACKET.md](GRU_PROVIDER_POSITIONING_PACKET.md), [GRU_RISK_AND_DISCLOSURE_LANGUAGE.md](GRU_RISK_AND_DISCLOSURE_LANGUAGE.md), and [metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md](metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md). Use those packets before submitting Chain 138 or cW* assets to price providers. + --- ## 2. Paths to Add Price Feeds diff --git a/docs/04-configuration/PR_ADDITIONS_VALIDATION_REPORT.md b/docs/04-configuration/PR_ADDITIONS_VALIDATION_REPORT.md index 4448437f..00dc5f89 100644 --- a/docs/04-configuration/PR_ADDITIONS_VALIDATION_REPORT.md +++ b/docs/04-configuration/PR_ADDITIONS_VALIDATION_REPORT.md @@ -119,15 +119,13 @@ DefiLlama adapters return TVL (total value locked), not token lists. Each protoc ### Chain 138 support -- DefiLlama uses chain keys: `ethereum`, `bsc`, `polygon`, `arbitrum`, etc. -- Chain 138 is likely **not** a built-in chain key. Check `DefiLlama-Adapters/helper/chains.js` or similar. -- If adding DODO on Chain 138, you must: - 1. Confirm DefiLlama supports chain 138 (may need PR to add chain first) - 2. Add config entry and tvl logic for the chain key they use (e.g. `chain138` or `defi-oracle-meta`) +- `@defillama/sdk` exposes **`dfio_meta_main`** with chain id **138** (RPC in SDK providers). +- DefiLlama-Adapters uses string keys in `projects/helper/chains.json`; **`dfio_meta_main`** must appear there. +- DODO on Chain 138: add **`dfio_meta_main`** in `projects/dodo/index.js` (DVM factory + `fromBlock`) and stablecoin rows in `projects/helper/tokenMapping.js` where needed. -### No PR-ready file +### PR status -We do not have a DefiLlama adapter file. Create one only after confirming chain support. +Upstream contribution is prepared as a PR from the org fork — see [`defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md`](defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md) and [`config/defillama-chain138-touchpoints.json`](../../config/defillama-chain138-touchpoints.json). --- @@ -138,7 +136,7 @@ We do not have a DefiLlama adapter file. Create one only after confirming chain | **Token list** | dbis-138.tokenlist.json | ✅ WETH10 address fixed to checksum per CHAIN138_TOKEN_ADDRESSES | | **Chainlist** | eip155-138.json | ✅ networkId→138; icon removed (avoids needing _data/icons file) | | **Trust Wallet** | trust-wallet-registry-chain138.json | ✅ sampleTx and sampleAccount added (real tx from explorer) | -| **DefiLlama** | N/A | No PR until chain 138 is supported; adapter structure differs | +| **DefiLlama** | `DefiLlama-Adapters` (`dfio_meta_main` + DODO) | PR from fork when ready; see `defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md` | --- diff --git a/docs/04-configuration/RESERVE_VERIFICATION_EVIDENCE_INDEX.md b/docs/04-configuration/RESERVE_VERIFICATION_EVIDENCE_INDEX.md new file mode 100644 index 00000000..60dff931 --- /dev/null +++ b/docs/04-configuration/RESERVE_VERIFICATION_EVIDENCE_INDEX.md @@ -0,0 +1,84 @@ +# Reserve Verification Evidence Index + +Status: canonical evidence map for reserve, supply, collateral, and settlement verification packets. + +## Purpose + +This index defines the evidence categories to attach to GRU, Chain 138, c*, and cW* provider submissions. It is intentionally evidence-first: providers should receive exact documents, URLs, APIs, hashes, and timestamps rather than broad reserve claims. + +## Evidence Chain + +Use this evidence chain for every asset family: + +```text +asset + -> reserve source + -> custodian / filing / exchange / counterparty evidence + -> Chain 138 representation + -> public-chain wrapped representation where applicable + -> supply proof + -> provider packet + -> external acceptance evidence +``` + +## Evidence Categories + +| Category | Evidence examples | Repo or operator artifact | +|---|---|---| +| Chain state | Contract address, token metadata, balances, supply, mappings, explorer API responses. | Chain 138 explorer, Etherscan, `token-lists/`, `config/token-mapping-multichain.json`. | +| Supply proof | Total supply, circulating supply method, non-circulating exclusions, signed balance inventory. | `reports/status/mainnet-cwusdc-supply-proof-20260508.json`, generated supply attestations. | +| Reserve records | Reserve inventory, reserve-grade eligibility, coverage ratio, operational buffer status. | Reserve catalog, operator attestations, linked external evidence. | +| Legal / filing evidence | UCC filings, Euroclear records, trustee/custodian statements, exchange reports. | External documents or references attached by operator. | +| Counterparty evidence | Bank, custodian, exchange, or institutional counterparty reports. | External reports, authenticated API exports, signed statements. | +| Settlement evidence | Transaction hashes, bridge proofs, settlement batch IDs, RTGS/sidecar reconciliation records. | Explorer links, `reports/status/`, RTGS runbooks, sidecar logs. | +| Market evidence | Indexed LP URLs, volume, reserves, depth, pool health, DEX terminal pages. | GeckoTerminal, DexScreener, CMC DEX, token-aggregation reports. | +| Provider acceptance | Ticket IDs, PRs, screenshots, public token pages, API responses. | `reports/status/*`, submission checklist docs. | + +## Asset Evidence Template + +```text +Asset: +Network / CAIP-2: +Contract / CAIP-19: +Canonical source asset: +Wrapped representation: +Decimals: +Logo URL: +Explorer URL: +Supply proof: +Reserve evidence: +Custodian / filing / exchange references: +Market evidence: +Provider packet: +Submission status: +Last verified: +Known caveats: +``` + +## cWUSDC Current Evidence Pointers + +| Evidence | Location | +|---|---| +| Consolidated provider packet | [CWUSDC_PROVIDER_SUBMISSION_PACKET.md](CWUSDC_PROVIDER_SUBMISSION_PACKET.md) | +| Etherscan profile packet | [etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md](etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md) | +| Etherscan E2E recommendations | [etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md](etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md) | +| Tracker submission packet | [coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md](coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md) | +| Mainnet supply proof | [../../reports/status/mainnet-cwusdc-supply-proof-20260508.json](../../reports/status/mainnet-cwusdc-supply-proof-20260508.json) | +| Technical completion proof | [../../reports/status/mainnet-cwusdc-technical-completion-20260508.json](../../reports/status/mainnet-cwusdc-technical-completion-20260508.json) | +| Token aggregation readiness | [../../reports/status/token-aggregation-adoption-readiness-live-20260509.json](../../reports/status/token-aggregation-adoption-readiness-live-20260509.json) | +| MetaMask / provider matrix | [metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md](metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md) | + +## Acceptance Status Rules + +| Status | Meaning | +|---|---| +| `repo_ready` | Repo-controlled endpoint, packet, metadata, proof, or report is prepared. | +| `submitted` | Operator submitted through a form, ticket, PR, or support channel. | +| `accepted` | Provider publicly displays or confirms the asset, metadata, price, supply, chain, or profile. | +| `blocked` | Provider requires missing evidence, liquidity, chain support, profile verification, or manual review. | + +## References + +- [GRU_PROVIDER_POSITIONING_PACKET.md](GRU_PROVIDER_POSITIONING_PACKET.md) +- [GRU_RESERVE_LAYER_EXPLAINER.md](GRU_RESERVE_LAYER_EXPLAINER.md) +- [CHAIN138_SYSTEM_OF_RECORD_MODEL.md](CHAIN138_SYSTEM_OF_RECORD_MODEL.md) diff --git a/docs/04-configuration/TOKEN_LIST_PR_REVIEW.md b/docs/04-configuration/TOKEN_LIST_PR_REVIEW.md index 9b645983..94de08cb 100644 --- a/docs/04-configuration/TOKEN_LIST_PR_REVIEW.md +++ b/docs/04-configuration/TOKEN_LIST_PR_REVIEW.md @@ -169,13 +169,12 @@ Object.keys(config).forEach(chain => { ### PR process 1. Add adapter under `projects/{protocol-name}/` 2. Export `tvl` (and optionally `staking`, `borrowed`) per chain -3. Chain 138 would need DefiLlama to add `chain138` as a supported chain key first (see docs.llama.fi) -4. Open PR to https://github.com/DefiLlama/DefiLlama-Adapters +3. Chain 138 uses SDK chain key **`dfio_meta_main`** (not `chain138`); add it to `projects/helper/chains.json` and wire the protocol config (see [How to add a new blockchain](https://docs.llama.fi/list-your-project/how-to-add-a-new-blockchain)) +4. Open PR to https://github.com/DefiLlama/DefiLlama-Adapters (org fork: [Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters](https://github.com/Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters)) ### Chain 138 action -- DefiLlama may not have `chain138` as a chain key yet -- Check `helper/chains.js` or similar for supported chains -- If adding DODO/other protocol on Chain 138, add config entry + tvl logic +- **`dfio_meta_main`** is the key aligned with `@defillama/sdk` providers for chain id 138 +- DODO: `projects/dodo/index.js` + `tokenMapping.js` — status and optional metrics: [`defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md`](defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md) --- diff --git a/docs/04-configuration/coingecko/COINGECKO_CHAIN138_SUBMISSION_ATTACHMENT.md b/docs/04-configuration/coingecko/COINGECKO_CHAIN138_SUBMISSION_ATTACHMENT.md index 3727c2dd..0677774f 100644 --- a/docs/04-configuration/coingecko/COINGECKO_CHAIN138_SUBMISSION_ATTACHMENT.md +++ b/docs/04-configuration/coingecko/COINGECKO_CHAIN138_SUBMISSION_ATTACHMENT.md @@ -9,7 +9,7 @@ DeFi Oracle Meta Mainnet is the canonical home network for the GRU v2 `c*` compliant asset family. The current repo-backed inventory shows native Chain 138 assets live on the public explorer, including `cUSDT`, `cUSDC`, the broader compliant fiat set, `cXAUC`, `cXAUT`, and `cAUSDT`, with public-network transport mirrors (`cW*`) active across supported destination networks. -Per the current machine-readable deployment graph and generated audit summaries, the public `cW*` surface is active on chain IDs `1, 10, 25, 56, 100, 137, 8453, 42161, 42220, 43114`, while `1111` remains deferred. +Per the current machine-readable deployment graph and generated audit summaries, the public `cW*` surface is active on **nine** chain IDs `1, 10, 56, 100, 137, 8453, 42161, 42220, 43114` (Cronos `25` excluded from the promoted count), while `1111` remains deferred. # Official Links @@ -35,19 +35,19 @@ These are the canonical GRU v2 `c*` assets on Chain 138 with direct explorer lin | Symbol | Name | Address | Explorer link | |---|---|---|---| -| `cUSDT` | Tether USD (Compliant) | `0x93E66202A11B1772E55407B32B44e5Cd8eda7f22` | `https://explorer.d-bis.org/address/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22` | -| `cUSDC` | USD Coin (Compliant) | `0xf22258f57794CC8E06237084b353Ab30fFfa640b` | `https://explorer.d-bis.org/address/0xf22258f57794CC8E06237084b353Ab30fFfa640b` | -| `cEURC` | Euro Coin (Compliant) | `0x8085961F9cF02b4d800A3c6d386D31da4B34266a` | `https://explorer.d-bis.org/address/0x8085961F9cF02b4d800A3c6d386D31da4B34266a` | -| `cEURT` | Tether EUR (Compliant) | `0xdf4b71c61E5912712C1Bdd451416B9aC26949d72` | `https://explorer.d-bis.org/address/0xdf4b71c61E5912712C1Bdd451416B9aC26949d72` | -| `cGBPC` | Pound Sterling (Compliant) | `0x003960f16D9d34F2e98d62723B6721Fb92074aD2` | `https://explorer.d-bis.org/address/0x003960f16D9d34F2e98d62723B6721Fb92074aD2` | -| `cGBPT` | Tether GBP (Compliant) | `0x350f54e4D23795f86A9c03988c7135357CCaD97c` | `https://explorer.d-bis.org/address/0x350f54e4D23795f86A9c03988c7135357CCaD97c` | -| `cAUDC` | Australian Dollar (Compliant) | `0xD51482e567c03899eecE3CAe8a058161FD56069D` | `https://explorer.d-bis.org/address/0xD51482e567c03899eecE3CAe8a058161FD56069D` | -| `cJPYC` | Japanese Yen (Compliant) | `0xEe269e1226a334182aace90056EE4ee5Cc8A6770` | `https://explorer.d-bis.org/address/0xEe269e1226a334182aace90056EE4ee5Cc8A6770` | -| `cCHFC` | Swiss Franc (Compliant) | `0x873990849DDa5117d7C644f0aF24370797C03885` | `https://explorer.d-bis.org/address/0x873990849DDa5117d7C644f0aF24370797C03885` | -| `cCADC` | Canadian Dollar (Compliant) | `0x54dBd40cF05e15906A2C21f600937e96787f5679` | `https://explorer.d-bis.org/address/0x54dBd40cF05e15906A2C21f600937e96787f5679` | -| `cXAUC` | Gold (Compliant) | `0x290E52a8819A4fbD0714E517225429aA2B70EC6b` | `https://explorer.d-bis.org/address/0x290E52a8819A4fbD0714E517225429aA2B70EC6b` | -| `cXAUT` | Tether XAU (Compliant) | `0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E` | `https://explorer.d-bis.org/address/0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E` | -| `cAUSDT` | Alltra USD Token (Compliant) | `0x5fdDF65733e3d590463F68f93Cf16E8c04081271` | `https://explorer.d-bis.org/address/0x5fdDF65733e3d590463F68f93Cf16E8c04081271` | +| `cUSDT` | Tether USD (Compliant) | `0x93E66202A11B1772E55407B32B44e5Cd8eda7f22` | `https://explorer.d-bis.org/addresses/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22` | +| `cUSDC` | USD Coin (Compliant) | `0xf22258f57794CC8E06237084b353Ab30fFfa640b` | `https://explorer.d-bis.org/addresses/0xf22258f57794CC8E06237084b353Ab30fFfa640b` | +| `cEURC` | Euro Coin (Compliant) | `0x8085961F9cF02b4d800A3c6d386D31da4B34266a` | `https://explorer.d-bis.org/addresses/0x8085961F9cF02b4d800A3c6d386D31da4B34266a` | +| `cEURT` | Tether EUR (Compliant) | `0xdf4b71c61E5912712C1Bdd451416B9aC26949d72` | `https://explorer.d-bis.org/addresses/0xdf4b71c61E5912712C1Bdd451416B9aC26949d72` | +| `cGBPC` | Pound Sterling (Compliant) | `0x003960f16D9d34F2e98d62723B6721Fb92074aD2` | `https://explorer.d-bis.org/addresses/0x003960f16D9d34F2e98d62723B6721Fb92074aD2` | +| `cGBPT` | Tether GBP (Compliant) | `0x350f54e4D23795f86A9c03988c7135357CCaD97c` | `https://explorer.d-bis.org/addresses/0x350f54e4D23795f86A9c03988c7135357CCaD97c` | +| `cAUDC` | Australian Dollar (Compliant) | `0xD51482e567c03899eecE3CAe8a058161FD56069D` | `https://explorer.d-bis.org/addresses/0xD51482e567c03899eecE3CAe8a058161FD56069D` | +| `cJPYC` | Japanese Yen (Compliant) | `0xEe269e1226a334182aace90056EE4ee5Cc8A6770` | `https://explorer.d-bis.org/addresses/0xEe269e1226a334182aace90056EE4ee5Cc8A6770` | +| `cCHFC` | Swiss Franc (Compliant) | `0x873990849DDa5117d7C644f0aF24370797C03885` | `https://explorer.d-bis.org/addresses/0x873990849DDa5117d7C644f0aF24370797C03885` | +| `cCADC` | Canadian Dollar (Compliant) | `0x54dBd40cF05e15906A2C21f600937e96787f5679` | `https://explorer.d-bis.org/addresses/0x54dBd40cF05e15906A2C21f600937e96787f5679` | +| `cXAUC` | Gold (Compliant) | `0x290E52a8819A4fbD0714E517225429aA2B70EC6b` | `https://explorer.d-bis.org/addresses/0x290E52a8819A4fbD0714E517225429aA2B70EC6b` | +| `cXAUT` | Tether XAU (Compliant) | `0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E` | `https://explorer.d-bis.org/addresses/0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E` | +| `cAUSDT` | Alltra USD Token (Compliant) | `0x5fdDF65733e3d590463F68f93Cf16E8c04081271` | `https://explorer.d-bis.org/addresses/0x5fdDF65733e3d590463F68f93Cf16E8c04081271` | # Bridged Wrapped Assets diff --git a/docs/04-configuration/coingecko/COINGECKO_SUBMISSION_CUSDC.md b/docs/04-configuration/coingecko/COINGECKO_SUBMISSION_CUSDC.md index 35db9133..5ef1aac9 100644 --- a/docs/04-configuration/coingecko/COINGECKO_SUBMISSION_CUSDC.md +++ b/docs/04-configuration/coingecko/COINGECKO_SUBMISSION_CUSDC.md @@ -13,7 +13,7 @@ | **Chain ID** | `138` | | **Chain Name** | `DeFi Oracle Meta Mainnet` | | **Decimals** | `6` | -| **Explorer Link** | `https://explorer.d-bis.org/address/0xf22258f57794CC8E06237084b353Ab30fFfa640b` | +| **Explorer Link** | `https://explorer.d-bis.org/addresses/0xf22258f57794CC8E06237084b353Ab30fFfa640b` | ## Public References diff --git a/docs/04-configuration/coingecko/COINGECKO_SUBMISSION_CUSDT.md b/docs/04-configuration/coingecko/COINGECKO_SUBMISSION_CUSDT.md index 581a960a..d6012756 100644 --- a/docs/04-configuration/coingecko/COINGECKO_SUBMISSION_CUSDT.md +++ b/docs/04-configuration/coingecko/COINGECKO_SUBMISSION_CUSDT.md @@ -13,7 +13,7 @@ | **Chain ID** | `138` | | **Chain Name** | `DeFi Oracle Meta Mainnet` | | **Decimals** | `6` | -| **Explorer Link** | `https://explorer.d-bis.org/address/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22` | +| **Explorer Link** | `https://explorer.d-bis.org/addresses/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22` | ## Public References diff --git a/docs/04-configuration/coingecko/CWUSDC_EXTERNAL_TRACKER_BLOCKER_REMEDIATION.md b/docs/04-configuration/coingecko/CWUSDC_EXTERNAL_TRACKER_BLOCKER_REMEDIATION.md new file mode 100644 index 00000000..08bfcbce --- /dev/null +++ b/docs/04-configuration/coingecko/CWUSDC_EXTERNAL_TRACKER_BLOCKER_REMEDIATION.md @@ -0,0 +1,58 @@ +# cWUSDC External Tracker Blocker Remediation + +Generated: `2026-05-09` + +## Current Evidence + +Run: + +```bash +pnpm cwusdc:external-trackers +pnpm cw:full-readiness +``` + +Evidence files: + +- `reports/status/cwusdc-external-trackers-live-latest.json` +- `reports/status/cwusdc-external-trackers-live-latest.md` +- `reports/status/cw-full-operational-readiness-latest.json` +- `reports/status/cw-full-operational-readiness-latest.md` + +Latest status: + +| Surface | Status | +|---|---| +| Etherscan token page | Reachable | +| CoinMarketCap DEX token page | Reachable | +| GeckoTerminal V3 pool | Reachable | +| GeckoTerminal V2 pool | Reachable | +| CoinGecko token price API | Not listed yet | +| DexScreener V3 pair API | Pair payload not returned | +| DexScreener V2 pair API | Pair payload not returned | + +## Required Fix + +1. Submit or update the CoinGecko listing packet for Ethereum Mainnet `cWUSDC`. +2. Submit/update DexScreener token and pair metadata for: + - `0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3` + - `0xc28706f899266b36bc43cc072b3a921bdf2c48d9` +3. Keep GeckoTerminal pool URLs in the submission packet because both GeckoTerminal API probes are live. +4. Re-run `pnpm cwusdc:external-trackers`. +5. Re-run `pnpm cw:full-readiness`. + +## Evidence URLs + +- Etherscan: `https://etherscan.io/token/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a` +- CoinMarketCap DEX: `https://dex.coinmarketcap.com/token/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a/` +- GeckoTerminal V3 pool: `https://www.geckoterminal.com/eth/pools/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3` +- GeckoTerminal V2 pool: `https://www.geckoterminal.com/eth/pools/0xc28706f899266b36bc43cc072b3a921bdf2c48d9` +- DexScreener V3 pool: `https://dexscreener.com/ethereum/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3` +- DexScreener V2 pool: `https://dexscreener.com/ethereum/0xc28706f899266b36bc43cc072b3a921bdf2c48d9` + +## Submission Packet + +Use: + +- `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md` +- `docs/04-configuration/coingecko/CWUSDC_MAINNET_EXTERNAL_SUBMISSION_CHECKLIST.md` +- `docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md` diff --git a/docs/04-configuration/coingecko/CWUSDC_MAINNET_EXTERNAL_SUBMISSION_CHECKLIST.md b/docs/04-configuration/coingecko/CWUSDC_MAINNET_EXTERNAL_SUBMISSION_CHECKLIST.md new file mode 100644 index 00000000..624513f3 --- /dev/null +++ b/docs/04-configuration/coingecko/CWUSDC_MAINNET_EXTERNAL_SUBMISSION_CHECKLIST.md @@ -0,0 +1,87 @@ +# cWUSDC Mainnet External Submission Checklist + +Status: ready for account holders / external reviewers. This file contains the remaining tasks that cannot be completed by repo-only automation. + +## Copy-Paste Asset Fields + +| Field | Value | +|---|---| +| Asset name | `Wrapped cUSDC` | +| Symbol | `cWUSDC` | +| Network | Ethereum Mainnet | +| Chain ID | `1` | +| Contract | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Decimals | `6` | +| Contact email | `submissions@d-bis.org` | +| Support email | `support@d-bis.org` | +| Social/profile email | `social.media@d-bis.org` | +| Token type | Compliant wrapped public-network transport mirror of Chain 138 `cUSDC` | +| Total supply | `10,451,316,981.309788 cWUSDC` | +| Supply proof | `reports/status/mainnet-cwusdc-supply-proof-20260508.json` | +| Logo URI | `https://raw.githubusercontent.com/Order-of-Hospitallers/proxmox-cp/main/token-lists/logos/gru/cUSDC.svg` | +| Etherscan 32x32 SVG logo | `https://d-bis.org/tokens/cwusdc.svg` | +| Etherscan 32x32 SVG source | `docs/04-configuration/etherscan/assets/cWUSDC-32x32.svg` | +| Internal token-list entry | `docs/04-configuration/coingecko/exports/token-list-all.json` | +| Tracker packet | `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md` | +| Etherscan profile packet | `docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md` | +| Branding system analysis | `docs/04-configuration/etherscan/CW_TOKEN_BRANDING_SYSTEM_ANALYSIS.md` | +| E2E recommendations | `docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md` | + +## Public Tracker Evidence + +| Tracker | URL | Status | +|---|---|---| +| Etherscan | `https://etherscan.io/token/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | Contract source verified | +| DexScreener token API | `https://api.dexscreener.com/token-pairs/v1/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a` | Not indexed yet; returns an empty pair list | +| DexScreener V3 candidate | `https://dexscreener.com/ethereum/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3` | Candidate pool URL; public API does not yet return the pair | +| DexScreener V2 candidate | `https://dexscreener.com/ethereum/0xc28706f899266b36bc43cc072b3a921bdf2c48d9` | Candidate pool URL; public API does not yet return the pair | +| GeckoTerminal V3 | `https://www.geckoterminal.com/eth/pools/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3` | Indexed | +| GeckoTerminal V2 | `https://www.geckoterminal.com/eth/pools/0xc28706f899266b36bc43cc072b3a921bdf2c48d9` | Indexed | +| CoinMarketCap DEX | `https://dex.coinmarketcap.com/token/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a/` | Token page reachable | + +## Remaining External Tasks + +| Task | Owner | Completion evidence | +|---|---|---| +| Submit CoinGecko listing/update | External account holder | CoinGecko contract page/API resolves the token instead of returning `404` | +| Submit full CoinMarketCap listing/update | External account holder | CMC token page exists outside DEX-only discoverability and accepts supply/profile data | +| Submit Etherscan profile/logo/social update | Verified contract owner/account | Etherscan token profile shows approved logo/profile metadata | +| Trigger DexScreener auto-indexing | Operator + treasury | `token-pairs/v1/ethereum/` returns at least one pair; requires pool liquidity and at least one transaction | +| Submit DexScreener Enhanced Token Info/profile | External account holder | DexScreener `orders/v1/ethereum/` shows approved profile order, or token page shows profile metadata | +| Submit GeckoTerminal profile/logo/social update, if available | External account holder | GeckoTerminal token/pool pages show approved profile metadata | +| Provide tracker circulating-supply attestations | Treasury/operator signer | Tracker accepts the circulating-supply value or requested exclusions | +| Fund official Mainnet USDC for listing-quality LP | Treasury/operator | Fresh repeg plan shows policy-floor quote shortfall covered | +| Execute public LP repair after funding | Operator | On-chain swap/add-liquidity txs plus updated preflight/repeg reports | + +## Liquidity Caveat To Include In Submissions + +Current public LP evidence proves GeckoTerminal and CMC DEX discoverability, not listing-quality depth and not DexScreener API indexing. DexScreener documents that automatic listing requires a token to be in a liquidity pool with at least one transaction, and the current API probes still return no cWUSDC pairs. The V2 pair is quote-starved and the V3 pool has no durable listing-quality liquidity. Use the latest repeg plan before making any depth claim: + +```bash +bash scripts/verify/snapshot-mainnet-cwusdc-usdc-preflight.sh +bash scripts/verify/plan-mainnet-cwusdc-usdc-repeg.sh +``` + +Latest known capital gaps from the 2026-05-09 remediation pass: + +| Need | Amount | +|---|---:| +| Public V2 quote-side policy-floor top-up | `2,497.832239 USDC` | +| DODO defended reserve-parity top-up | `290,995.072514 USDC` | +| Operator wallet official Mainnet USDC | `0.343655 USDC` | +| Public V2 quote-side shortfall after wallet balance | `2,497.488584 USDC` | +| DODO defended parity shortfall after wallet balance | `290,994.728859 USDC` | +| Smallest current UniV2 repair canary requirement | `1,657.650239 USDC` | +| Smallest current UniV2 repair canary shortfall | `1,657.306584 USDC` | + +## Suggested Submission Note + +`cWUSDC is the Ethereum Mainnet compliant wrapped transport representation of canonical Chain 138 cUSDC. The contract is verified on Etherscan and Sourcify, has 6 decimals, and is discoverable on GeckoTerminal and CoinMarketCap DEX. DexScreener public API indexing is still pending; current cWUSDC/USDC public LPs are shallow/quote-starved and require official-USDC funding before making listing-quality liquidity claims.` + +## Etherscan-Specific Pre-Submit Checks + +- Use `submissions@d-bis.org`, matching the `https://d-bis.org/` website domain. +- Use the hosted 32x32 SVG at `https://d-bis.org/tokens/cwusdc.svg`; local source mirror is `docs/04-configuration/etherscan/assets/cWUSDC-32x32.svg`. +- Confirm `https://d-bis.org/leadership` presents team/founder entries with supporting professional links, such as LinkedIn or equivalent. +- Confirm `https://d-bis.org/gru/tokens` visibly explains the GRU token family and the cWUSDC/cUSDC relationship. +- Avoid language that implies cWUSDC is official Circle-issued USDC. diff --git a/docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md b/docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md new file mode 100644 index 00000000..14029e8f --- /dev/null +++ b/docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md @@ -0,0 +1,124 @@ +# cWUSDC Ethereum Mainnet Tracker Submission Packet + +Status: internal package ready; external tracker acceptance pending. + +External handoff checklist: `docs/04-configuration/coingecko/CWUSDC_MAINNET_EXTERNAL_SUBMISSION_CHECKLIST.md`. + +Consolidated provider packet: `docs/04-configuration/CWUSDC_PROVIDER_SUBMISSION_PACKET.md`. + +## Asset + +| Field | Value | +|---|---| +| Network | Ethereum Mainnet | +| Chain ID | `1` | +| Contract | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Token name | `Wrapped cUSDC` | +| Symbol | `cWUSDC` | +| Decimals | `6` | +| Asset class | Compliant wrapped public-network transport mirror | +| Canonical source asset | Chain 138 `cUSDC` | +| Internal registry symbol/name | `USD Coin (Compliant Wrapped ISO-4217 M1)` | + +## Verification Links + +- Etherscan token page: `https://etherscan.io/token/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` +- Sourcify status: full match for Ethereum Mainnet contract `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` +- Token source contract: `smom-dbis-138/contracts/tokens/CompliantWrappedToken.sol` +- Canonical metadata registry: `smom-dbis-138/services/token-aggregation/src/config/canonical-tokens.ts` +- Chain 138 to Mainnet mapping: `config/token-mapping-multichain.json` +- Compact cWUSDC price/supply evidence API: `https://explorer.d-bis.org/token-aggregation/api/v1/report/token-price/cWUSDC?chainId=1` +- Chain 138 source cUSDC price/supply evidence API: `https://explorer.d-bis.org/token-aggregation/api/v1/report/token-price/cUSDC?chainId=138` +- CoinGecko-format report API: `https://explorer.d-bis.org/token-aggregation/api/v1/report/coingecko?chainId=1` +- CMC-format report API: `https://explorer.d-bis.org/token-aggregation/api/v1/report/cmc?chainId=1` + +## Supply + +Machine-readable proof: `reports/status/mainnet-cwusdc-supply-proof-20260508.json`. + +| Field | Value | +|---|---:| +| Total supply raw | `10451316981309788` | +| Total supply units | `10,451,316,981.309788 cWUSDC` | +| Proposed public circulating-supply method | `circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances` | +| Current conservative reportable supply before tracker-specific exclusions | `10,451,316,981.309788 cWUSDC` | + +If CoinGecko, CMC, Etherscan, or another tracker requests treasury exclusions, provide a signed balance inventory for operator, bridge, treasury, and protocol-owned addresses at the requested block. + +## Market / LP Evidence + +| Venue | Pair | Address | Tracker URL | Status | +|---|---|---|---|---| +| Uniswap V3 | `cWUSDC/USDC` | `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3` | `https://dexscreener.com/ethereum/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3` | Candidate DexScreener URL; current DexScreener public API does not return the pair | +| Uniswap V3 | `cWUSDC/USDC` | `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3` | `https://www.geckoterminal.com/eth/pools/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3` | Indexed; GeckoTerminal reserve about `$179.1416`; 24h volume about `$90.67` | +| Uniswap V2 | `cWUSDC/USDC` | `0xC28706F899266b36BC43cc072b3a921BDf2C48D9` | `https://dexscreener.com/ethereum/0xc28706f899266b36bc43cc072b3a921bdf2c48d9` | Candidate DexScreener URL; current DexScreener public API does not return the pair | +| Uniswap V2 | `cWUSDC/USDC` | `0xC28706F899266b36BC43cc072b3a921BDf2C48D9` | `https://www.geckoterminal.com/eth/pools/0xc28706f899266b36bc43cc072b3a921bdf2c48d9` | Indexed; GeckoTerminal reserve about `$4.3241`; 24h volume about `$114,770.23` | + +Current DexScreener API probes: + +- `https://api.dexscreener.com/token-pairs/v1/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a` returns an empty array. +- `https://api.dexscreener.com/tokens/v1/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a` returns an empty array. +- `https://api.dexscreener.com/orders/v1/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a` returns no approved profile order. + +CoinMarketCap DEX token page: + +- `https://dex.coinmarketcap.com/token/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a/` + +This confirms CMC DEX discoverability only. It does not mean the asset has been accepted as a full CoinMarketCap token listing with circulating supply, market cap, verified profile, and ranking. + +Latest internal readiness evidence: + +- `reports/status/mainnet-cwusdc-tracker-readiness-20260508.md` +- `reports/status/engine-x-public-indexed-readiness-latest.json` +- `reports/status/mainnet-cwusdc-usdc-preflight-latest.json` +- `reports/status/mainnet-cwusdc-usdc-repeg-plan-latest.json` + +## Valuation / Oracle Position + +No direct third-party cWUSDC oracle feed is live. Until a direct feed is approved, use this precedence for internal reporting: + +1. Accounting peg policy for cWUSDC as a compliant wrapped USD transport asset. +2. Public DEX evidence only when official Mainnet USDC quote-side depth is sufficient. +3. DexScreener, GeckoTerminal, and CMC DEX public LP/token pages as market evidence, with liquidity warnings when quote-starved. +4. Internal token-aggregation canonical metadata and export reports. + +Do not present the current V2 or V3 pools as listing-quality price discovery. The public V2 pair is heavily quote-starved, and the V3 pool has no active liquidity. + +## Required Disclosure Language + +Use the canonical disclosure language in `docs/04-configuration/GRU_RISK_AND_DISCLOSURE_LANGUAGE.md`. + +Minimum tracker disclosure: + +```text +cWUSDC is the Ethereum Mainnet compliant wrapped transport representation of Chain 138 cUSDC in the DBIS GRU asset family. cWUSDC is not Circle-issued USDC and should not be represented as an official Circle asset. Public DEX liquidity should be interpreted only from current indexed pool evidence and should not be treated as listing-quality price discovery until depth is sufficient and sustained. +``` + +## Submission Checklist + +- [x] Verified contract link. +- [x] Token name, symbol, decimals, and supply. +- [x] Internal metadata registry path. +- [x] Supply proof JSON. +- [x] DexScreener candidate LP URLs. +- [x] GeckoTerminal indexed LP URLs. +- [x] CoinMarketCap DEX token URL. +- [x] Public liquidity caveats. +- [x] CoinGecko/CMC-style internal export path. +- [x] External submission checklist prepared. +- [ ] External CoinGecko listing accepted. +- [ ] Full external CMC listing accepted. +- [ ] External Etherscan token profile/logo update accepted. +- [ ] DexScreener API indexing accepted. +- [ ] External DexScreener token profile/social/logo update accepted. +- [ ] Public circulating supply accepted by tracker. +- [ ] Listing-quality official-USDC liquidity funded and sustained. + +## External Submission Tasks + +1. Submit or update CoinGecko using the contract, supply proof, metadata, LP URLs, and public docs in this packet. +2. Submit or update CoinMarketCap with the same packet and the CMC-specific form fields; reference the existing CMC DEX token URL as discoverability evidence. +3. Submit Etherscan token profile/logo/social updates from an account authorized for the token contract. +4. Fund official Mainnet USDC, rerun `bash scripts/verify/plan-mainnet-cwusdc-usdc-repeg.sh`, then execute public LP repair only after quotes and min-outs are reviewed. +5. Submit DexScreener Enhanced Token Info/profile updates after the token is accepted by the public API or through the marketplace flow. +6. Provide any tracker-requested circulating-supply exclusion attestations. diff --git a/docs/04-configuration/coingecko/submissions/cwusdc-coingecko-listing-request-20260509.json b/docs/04-configuration/coingecko/submissions/cwusdc-coingecko-listing-request-20260509.json new file mode 100644 index 00000000..b6579cc3 --- /dev/null +++ b/docs/04-configuration/coingecko/submissions/cwusdc-coingecko-listing-request-20260509.json @@ -0,0 +1,68 @@ +{ + "requestType": "New Coin/Token Listing", + "listingMode": "Active Listing", + "asset": { + "name": "Wrapped cUSDC", + "symbol": "cWUSDC", + "network": "Ethereum Mainnet", + "chainId": 1, + "contractAddress": "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a", + "decimals": 6, + "assetClass": "Compliant wrapped public-network transport mirror", + "canonicalSourceAsset": "Chain 138 cUSDC", + "canonicalSourceAssetAddress": "0xf22258f57794CC8E06237084b353Ab30fFfa640b", + "canonicalSourceAssetExplorer": "https://explorer.d-bis.org/token/0xf22258f57794CC8E06237084b353Ab30fFfa640b", + "description": "cWUSDC is the Ethereum Mainnet compliant wrapped transport representation of canonical Chain 138 cUSDC. It is a wrapped settlement and interoperability asset for the DBIS GRU/cW token mesh. It is not Circle-issued USDC." + }, + "officialLinks": { + "website": "https://d-bis.org/", + "contactEmail": "submissions@d-bis.org", + "supportEmail": "support@d-bis.org", + "socialMediaEmail": "social.media@d-bis.org", + "tokenInformation": "https://d-bis.org/gru/tokens", + "logo32x32Svg": "https://d-bis.org/tokens/cwusdc.svg", + "explorerToken": "https://etherscan.io/token/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a", + "sourceVerification": "https://repo.sourcify.dev/contracts/full_match/1/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a/" + }, + "apiFeedEvidence": { + "chain138CusdcBlockscoutTokenApi": "https://explorer.d-bis.org/api/v2/tokens/0xf22258f57794CC8E06237084b353Ab30fFfa640b", + "chain138CusdcBlockscoutTransfersApi": "https://explorer.d-bis.org/api/v2/tokens/0xf22258f57794CC8E06237084b353Ab30fFfa640b/transfers", + "mainnetCwusdcEtherscanApi": "https://api.etherscan.io/v2/api?chainid=1&module=account&action=tokentx&contractaddress=0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a", + "staticAuditNote": "docs/04-configuration/etherscan/CUSDC_CWUSDC_ETHERSCAN_FEED_AUDIT.md", + "latestInternalAuditJson": "reports/status/cusdc-cwusdc-etherscan-feed-audit-latest.json", + "latestInternalAuditMarkdown": "reports/status/cusdc-cwusdc-etherscan-feed-audit-latest.md", + "submissionNote": "Etherscan should display Ethereum Mainnet cWUSDC transfers and supply for the token page. Chain 138 cUSDC traffic is supporting source-asset evidence for the wrapped-token relationship, not traffic that should be merged into Etherscan's Ethereum token transfer totals." + }, + "supply": { + "totalSupplyUnits": "10451316981.309788", + "totalSupplyRaw": "10451316981309788", + "circulatingSupplyMethod": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances", + "supplyProof": "reports/status/mainnet-cwusdc-supply-proof-20260508.json" + }, + "markets": { + "geckoTerminalPools": [ + "https://www.geckoterminal.com/eth/pools/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3", + "https://www.geckoterminal.com/eth/pools/0xc28706f899266b36bc43cc072b3a921bdf2c48d9" + ], + "coinMarketCapDexToken": "https://dex.coinmarketcap.com/token/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a/", + "candidateDexScreenerPools": [ + "https://dexscreener.com/ethereum/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3", + "https://dexscreener.com/ethereum/0xc28706f899266b36bc43cc072b3a921bdf2c48d9" + ], + "liquidityCaveat": "Current public LP evidence is shallow and quote-starved. Do not treat current pools as listing-quality depth until official Mainnet USDC funding and public LP repair are complete." + }, + "attachments": [ + "docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md", + "docs/04-configuration/coingecko/CWUSDC_MAINNET_EXTERNAL_SUBMISSION_CHECKLIST.md", + "docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md", + "https://d-bis.org/tokens/cwusdc.svg", + "docs/04-configuration/etherscan/assets/cWUSDC-32x32.svg", + "docs/04-configuration/etherscan/CUSDC_CWUSDC_ETHERSCAN_FEED_AUDIT.md", + "reports/status/cusdc-cwusdc-etherscan-feed-audit-latest.json", + "reports/status/cusdc-cwusdc-etherscan-feed-audit-latest.md", + "reports/status/cwusdc-external-trackers-live-latest.json", + "reports/status/mainnet-cwusdc-supply-proof-20260508.json", + "reports/status/mainnet-cwusdc-usdc-repeg-plan-latest.json" + ], + "submissionNote": "The contract is verified on Etherscan and Sourcify and has 6 decimals. GeckoTerminal and CoinMarketCap DEX discoverability are live. CoinGecko simple token price API currently returns an empty object for the contract address, so this request is needed before Etherscan USD value and CoinGecko token-price integrations can resolve cWUSDC." +} diff --git a/docs/04-configuration/defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md b/docs/04-configuration/defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md index e2bfc300..805fdebc 100644 --- a/docs/04-configuration/defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md +++ b/docs/04-configuration/defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md @@ -6,6 +6,8 @@ **Fork for upstream PRs:** [Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters](https://github.com/Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters) → PR target [DefiLlama/DefiLlama-Adapters](https://github.com/DefiLlama/DefiLlama-Adapters). +**Open upstream adapters PR (DODO + `dfio_meta_main`):** [DefiLlama/DefiLlama-Adapters#19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198) — merge status and CI logs are authoritative for when production TVL reflects Chain 138. **Still `OPEN` on GitHub as of 2026-05-10.** + **Machine-readable touchpoints:** [`config/defillama-chain138-touchpoints.json`](../../../config/defillama-chain138-touchpoints.json). **Public chain profile (X):** [Defi Oracle Meta](https://x.com/DefiOracleMeta) (`@DefiOracleMeta`) — use for **network / operator** messaging. Per-protocol listings (e.g. DODO on DefiLlama) may still reference that protocol’s own social accounts where upstream metadata requires it. @@ -16,13 +18,15 @@ DefiLlama defines TVL as **value locked in protocol contracts**, valued largely via **CoinGecko** where possible, with **on-chain** methods when needed ([methodology overview](https://docs.llama.fi/)). That is **instrument-agnostic**: compliant stablecoins and PMM pools are still ERC-20 balances in contracts. The **regulatory** story (EMI, issuer, reserve attestations) lives in **your** disclosures and listings; DefiLlama remains a **neutral TVL/volume/yields** surface — complementary to CoinGecko/CMC/Etherscan paths in [`METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md`](../metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md). +**DODO protocol breakdown:** if the per-chain table shows **`dfio_meta_main` as `0.00`** while other chains look normal, that is usually an **adapter pricing path** issue, not “no liquidity” on chain 138 — see [`../../11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md`](../../11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md). + --- ## Metrics ↔ repositories (from docs.llama.fi “All Metrics We Track”) | Metric | Repository (upstream) | Chain 138 relevance | |--------|-------------------------|---------------------| -| TVL | [DefiLlama-Adapters](https://github.com/DefiLlama/DefiLlama-Adapters) | **Prepared for upstream PR:** DODO V2 DVM on `dfio_meta_main` (factory + `fromBlock`); companion **`defillama-server`** chain metadata + DODO listing rows committed locally — see [`DEFILLAMA_CHAIN138_SUBMISSION_CHECKLIST.md`](DEFILLAMA_CHAIN138_SUBMISSION_CHECKLIST.md) and [`config/defillama-chain138-touchpoints.json`](../../../config/defillama-chain138-touchpoints.json). Merge on upstream completes the public UI slice. | +| TVL | [DefiLlama-Adapters](https://github.com/DefiLlama/DefiLlama-Adapters) | **Open:** [PR #19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198) (DODO V2 DVM on `dfio_meta_main`, `chains.json`, hub stable **`fixBalancesTokens`**). Companion **`defillama-server`** chain metadata + DODO listing rows remain optional — see [`DEFILLAMA_CHAIN138_SUBMISSION_CHECKLIST.md`](DEFILLAMA_CHAIN138_SUBMISSION_CHECKLIST.md) and [`config/defillama-chain138-touchpoints.json`](../../../config/defillama-chain138-touchpoints.json). **Merge #19198** completes the adapters slice for public TVL attribution to `dfio_meta_main`. | | Fees / revenue / DEX volume / options / aggregators | [dimension-adapters](https://github.com/DefiLlama/dimension-adapters) | **Optional later** when you want volume/fees series on supported venues. | | Yields | [yield-server](https://github.com/DefiLlama/yield-server) | Optional if you expose yield-bearing pools with standard adapter patterns. | | Stablecoins (peg/supply) | [peggedassets-server](https://github.com/DefiLlama/peggedassets-server) | Optional if you want pegged-asset tracking for compliant stables. | @@ -85,7 +89,7 @@ DefiLlama defines TVL as **value locked in protocol contracts**, valued largely Covers **exports patterns** (e.g. empty core TVL + staking), **`sumTokensExport`** from `./helper/unwrapLPs` for known owners/tokens (`owners`, `tokens`, `tokensAndOwners`, **`resolveLP`** for LP unwrap), **Solana** helpers, **unwrap** flows (`sumTokens2` + `resolveLP`), and **`getBlock`** from `../helper/http` when block height is missing for lesser-known EVM chains. -**Chain 138:** The current **DODO** contribution uses **factory event logs + `sumTokens`** (same family as upstream `projects/dodo`). For future adapters that TVL **fixed treasury wallets**, **vault contracts**, or **Uni V2/V3-style LP** balances on `dfio_meta_main`, prefer these helpers instead of reinventing balance aggregation. +**Chain 138:** Upstream **`projects/dodo`** gains **`dfio_meta_main`** via [PR #19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198) (factory logs + balance aggregation consistent with that PR’s final diff). For **fixed treasury wallets**, **vault contracts**, or **Uni V2/V3-style LP** TVL on `dfio_meta_main`, prefer the documented helpers instead of reinventing balance aggregation. --- @@ -94,7 +98,7 @@ Covers **exports patterns** (e.g. empty core TVL + staking), **`sumTokensExport` | Item | Status | Notes | |------|--------|--------| | `@defillama/sdk` chain RPC + id | **Already present** | `dfio_meta_main` → 138 in SDK providers (no fork of SDK required for RPC). | -| `chains.json` + `tokenMapping.js` + DODO `dfio_meta_main` | **PR to DefiLlama-Adapters** | Canonical cUSDT/cUSDC mapping for USD valuation; DVM factory `0xc93870594C7f83A0aE076c2e30b494Efc526b68E`, `fromBlock` factory deploy. | +| `chains.json` + `tokenMapping.js` + DODO `dfio_meta_main` | **[PR #19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198)** (open) | Canonical cUSDT/cUSDC mapping for USD valuation; DVM factory `0xc93870594C7f83A0aE076c2e30b494Efc526b68E`, `fromBlock` factory deploy. | | Protocol card / listing text on defillama.com | **Optional separate PR** | Often [`defillama-server` protocol data](https://github.com/DefiLlama/defillama-server) — only if maintainers want a **new** or updated listing slug. | | Gitea mirror of fork | **Optional** | Use [`scripts/deployment/mirror-github-fork-to-gitea.sh`](../../../scripts/deployment/mirror-github-fork-to-gitea.sh) with tokens set. | | dimension-adapters / yield-server / … | **Optional** | Add when you have clear subgraph/RPC sources and reviewer-ready methodology. | diff --git a/docs/04-configuration/defillama/DEFILLAMA_CHAIN138_SUBMISSION_CHECKLIST.md b/docs/04-configuration/defillama/DEFILLAMA_CHAIN138_SUBMISSION_CHECKLIST.md index 76a31a70..111e59c2 100644 --- a/docs/04-configuration/defillama/DEFILLAMA_CHAIN138_SUBMISSION_CHECKLIST.md +++ b/docs/04-configuration/defillama/DEFILLAMA_CHAIN138_SUBMISSION_CHECKLIST.md @@ -1,6 +1,6 @@ # Chain 138 — DefiLlama gaps checklist (server, dimensions, forms) -Use this after the **DefiLlama-Adapters** TVL PR ([`dfio_meta_main` + DODO](../CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md)). Touchpoints JSON: [`config/defillama-chain138-touchpoints.json`](../../../config/defillama-chain138-touchpoints.json). +Use this after the **DefiLlama-Adapters** TVL PR for **`dfio_meta_main` + DODO** — track merge status at **[DefiLlama/DefiLlama-Adapters#19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198)** ([ecosystem map](../CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md), [TVL breakdown note](../../11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md)). Touchpoints JSON: [`config/defillama-chain138-touchpoints.json`](../../../config/defillama-chain138-touchpoints.json). **Chain vs protocol socials:** Official **Chain 138 / network** profile on X is [**@DefiOracleMeta**](https://x.com/DefiOracleMeta). A **DODO** adapter or listing PR may still cite **DODO’s** upstream Twitter where that matches DefiLlama’s existing **DODO AMM** row — do not conflate the two in copy. @@ -17,7 +17,7 @@ Use this after the **DefiLlama-Adapters** TVL PR ([`dfio_meta_main` + DODO](../C | `defi/src/utils/normalizeChain.ts` | `chainCoingeckoIds["Defi Oracle Meta Mainnet"]` with `chainId: 138`; `chainLabelMap.dfio_meta_main` → display name | | `defi/src/protocols/data1.ts` | **DODO AMM** `chains` includes **Defi Oracle Meta Mainnet**; `oraclesBreakdown[0].proof` populated from audit docs | -**PR notes:** explain chain extension + listing alignment with merged TVL adapter; enable **Allow edits by maintainers**. +**PR notes:** explain chain extension + listing alignment **after** DefiLlama-Adapters **PR #19198** (TVL on `dfio_meta_main`) **merges**; enable **Allow edits by maintainers**. --- diff --git a/docs/04-configuration/dexscreener/CWUSDC_DEXSCREENER_INDEXING_AND_PROFILE_PACKET_20260509.md b/docs/04-configuration/dexscreener/CWUSDC_DEXSCREENER_INDEXING_AND_PROFILE_PACKET_20260509.md new file mode 100644 index 00000000..73ccae1d --- /dev/null +++ b/docs/04-configuration/dexscreener/CWUSDC_DEXSCREENER_INDEXING_AND_PROFILE_PACKET_20260509.md @@ -0,0 +1,65 @@ +# cWUSDC DexScreener Indexing and Profile Packet - 2026-05-09 + +## Asset + +| Field | Value | +|---|---| +| Token | `Wrapped cUSDC` | +| Symbol | `cWUSDC` | +| Chain | Ethereum Mainnet | +| Contract | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Decimals | `6` | +| Website | `https://d-bis.org/` | +| Contact email | `submissions@d-bis.org` | +| Support email | `support@d-bis.org` | +| Social/profile email | `social.media@d-bis.org` | +| Token page | `https://d-bis.org/gru/tokens` | +| Logo | `https://d-bis.org/tokens/cwusdc.svg` | +| Logo source | `docs/04-configuration/etherscan/assets/cWUSDC-32x32.svg` | + +## Current DexScreener Status + +The public DexScreener API does not yet index cWUSDC: + +- `GET https://api.dexscreener.com/token-pairs/v1/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a` returns `[]`. +- `GET https://api.dexscreener.com/tokens/v1/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a` returns `[]`. +- `GET https://api.dexscreener.com/orders/v1/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a` returns no approved token profile order. + +Candidate pool URLs: + +- `https://dexscreener.com/ethereum/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3` +- `https://dexscreener.com/ethereum/0xc28706f899266b36bc43cc072b3a921bdf2c48d9` + +## Indexing Prerequisites + +DexScreener documents automatic token listing after the token is added to a liquidity pool and has at least one transaction. Current cWUSDC pools are not passing DexScreener API discovery, and the smallest current UniV2 repair canary is blocked by official Mainnet USDC funding. + +Current funding facts: + +| Item | Amount | +|---|---:| +| Wallet USDC | `0.343655 USDC` | +| Smallest UniV2 repair canary requirement | `1,657.650239 USDC` | +| Smallest UniV2 repair canary shortfall | `1,657.306584 USDC` | +| Policy-floor public quote-side requirement | `2,497.832239 USDC` | +| DODO defended parity requirement | `290,995.072514 USDC` | + +## Actions + +1. Fund at least `1,657.306584 USDC` for the smallest public UniV2 repair canary, or `2,497.488584 USDC` for the policy-floor public quote-side repair based on the current wallet balance. +2. Rerun: + +```bash +bash scripts/verify/snapshot-mainnet-cwusdc-usdc-preflight.sh +bash scripts/verify/plan-mainnet-cwusdc-usdc-repeg.sh +EXECUTE=0 bash scripts/deployment/repair-mainnet-cwusdc-usdc-univ2-canary.sh +``` + +3. Broadcast only after reviewing the quote and min-out values. +4. Recheck: + +```bash +pnpm cwusdc:external-trackers +``` + +5. If the token remains unindexed after on-chain repair, submit Enhanced Token Info/profile through DexScreener's marketplace flow using the fields in this packet. diff --git a/docs/04-configuration/etherscan/CUSDC_CWUSDC_ETHERSCAN_FEED_AUDIT.md b/docs/04-configuration/etherscan/CUSDC_CWUSDC_ETHERSCAN_FEED_AUDIT.md new file mode 100644 index 00000000..51792c5a --- /dev/null +++ b/docs/04-configuration/etherscan/CUSDC_CWUSDC_ETHERSCAN_FEED_AUDIT.md @@ -0,0 +1,63 @@ +# cUSDC / cWUSDC Etherscan Feed Audit + +Status: repeatable API audit added. Run: + +```bash +set -a +source .env +set +a +scripts/verify/audit-cusdc-cwusdc-etherscan-feeds.py +``` + +Latest generated local outputs: + +- `reports/status/cusdc-cwusdc-etherscan-feed-audit-latest.md` +- `reports/status/cusdc-cwusdc-etherscan-feed-audit-latest.json` + +## Relationship + +| Field | Value | +|---|---| +| Canonical source asset | Chain 138 `cUSDC` | +| Chain 138 cUSDC address | `0xf22258f57794CC8E06237084b353Ab30fFfa640b` | +| Wrapped public-network asset | Ethereum Mainnet `cWUSDC` | +| Ethereum cWUSDC address | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Mapping source | `config/token-mapping-multichain.json` | +| Metadata registry | `smom-dbis-138/services/token-aggregation/src/config/canonical-tokens.ts` | + +## Feed Interpretation + +Etherscan should display Ethereum Mainnet `cWUSDC` transfer activity and supply for the Ethereum token page. + +Chain 138 `cUSDC` traffic should be provided as supporting canonical source-asset evidence. It should not be represented as Ethereum Mainnet `cWUSDC` transfer activity, because Etherscan indexes Ethereum Mainnet for the submitted token contract. + +Use this language when replying to Etherscan or tracker follow-up: + +```text +cWUSDC is the Ethereum Mainnet wrapped transport representation of canonical Chain 138 cUSDC. Please use the Ethereum Mainnet cWUSDC contract for Etherscan token-page transfers and supply, and use the Chain 138 cUSDC explorer/API feed as supporting evidence for the canonical source asset relationship. +``` + +## API Endpoints + +| Feed | Endpoint | +|---|---| +| Chain 138 cUSDC metadata | `https://explorer.d-bis.org/api/v2/tokens/0xf22258f57794CC8E06237084b353Ab30fFfa640b` | +| Chain 138 cUSDC transfers | `https://explorer.d-bis.org/api/v2/tokens/0xf22258f57794CC8E06237084b353Ab30fFfa640b/transfers` | +| Ethereum cWUSDC transfers | `https://api.etherscan.io/v2/api?chainid=1&module=account&action=tokentx&contractaddress=0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Ethereum cWUSDC supply | `https://api.etherscan.io/v2/api?chainid=1&module=stats&action=tokensupply&contractaddress=0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | + +## Latest Sample Summary + +The latest local run generated: + +| Feed | Supply | Recent sample transfers | Recent sample volume | Unique addresses in sample | +|---|---:|---:|---:|---:| +| Chain 138 cUSDC Blockscout | `38,601,011,267` | `150` | `75,129,540,009.210138` | `33` | +| Ethereum cWUSDC Etherscan | `10,451,316,981.309788` | `150` | `1,001,693.375967` | `152` | + +Latest observed sample hashes: + +- Chain 138 cUSDC: `0xa3a9b9a6c21a90adacf88f06baddd2c10c8bcf8302b5b04c791dcbab81c734b1` +- Ethereum cWUSDC: `0xc973c614014adc6d3678f184f8051147e615c0c8d4d809db51bc9d46f3af9488` + +Common addresses in the recent API samples are a supporting signal only. The canonical linkage is established by the token mapping, metadata registry, and bridge/listing documentation. diff --git a/docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_BRIDGE_CROSSCHAIN_LAYER_MAP.md b/docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_BRIDGE_CROSSCHAIN_LAYER_MAP.md new file mode 100644 index 00000000..8fcda03b --- /dev/null +++ b/docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_BRIDGE_CROSSCHAIN_LAYER_MAP.md @@ -0,0 +1,160 @@ +# cWUSDC Etherscan Bridge and Cross-Chain Layer Map + +Status: focused research packet for making Etherscan show USD value for Ethereum Mainnet `cWUSDC`. + +## Goal + +Target asset: + +```text +Network: Ethereum Mainnet +Chain ID: 1 +Contract: 0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a +Name: Wrapped cUSDC +Symbol: cWUSDC +Decimals: 6 +CAIP-19: eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a +``` + +The objective is not merely to prove that cUSDC exists elsewhere. The objective is to make Etherscan display nonzero USD value for this exact Mainnet contract. + +## Official Etherscan Surfaces + +| Layer | Etherscan surface | What it can prove | Effect on USD Value | +|---|---|---|---| +| Contract | Verified source, ABI, creator, transactions; Foundry verification via `forge verify-contract` | The Mainnet contract identity and implementation | Required trust baseline; not a price source | +| Token page | Logo, name, profile, links, token details | Etherscan-recognized token identity | Important prerequisite; not enough alone | +| Token info API | `module=token&action=tokeninfo&chainid=1&contractaddress=...` | Etherscan-side metadata plus `tokenPriceUSD` if populated | Direct monitor surface for profile and price propagation | +| Token holdings | Holder pages and token inventory rows | Whether Etherscan is applying a USD mark to balances | Final user-visible outcome | +| Market cap fields | Onchain Market Cap and Circulating Supply Market Cap | Whether Etherscan has accepted a price and supply basis | Final token-page outcome | +| V2 multichain API | Same API key and base URL, different `chainid` | Data for Etherscan-supported EVM chains | Useful for related wrapped deployments, but each chain remains separate | +| V2 chainlist | `https://api.etherscan.io/v2/chainlist` | Which chains are first-class Etherscan V2 API surfaces | Defines what can be called Etherscan-family evidence | +| L2 deposit/withdrawal APIs | `module=account&action=getdeposittxs` and related endpoints | Native Etherscan-indexed bridge movement for supported L2s | Evidence only unless tied to exact Mainnet price acceptance | +| Nametags/labels | Address names and labels | Bridge, operator, treasury, exchange, or protocol context | Trust/provenance support only | +| External links | Website/socials/whitepaper/official endpoints | Project legitimacy and support paths | Supports profile approval | +| Blockscan family | Etherscan-family explorers for other EVM networks | Other chain deployments and activity | Separate listings; do not merge into Ethereum supply | + +## Bridge and Cross-Chain Interpretation + +Bridge evidence can help explain why `cWUSDC` exists on Ethereum and how it relates to base `cUSDC`, but it does not replace the USD price feed Etherscan needs for the Mainnet token page. + +The Etherscan V2 `chainlist` endpoint is the boundary for Etherscan-family chain claims. If a chain ID is absent from `https://api.etherscan.io/v2/chainlist`, treat that chain's evidence as external provenance rather than native Etherscan V2 evidence. As of the latest local check, Chain 138 is not present in the Etherscan V2 chainlist. + +Use bridge evidence for: + +- Source asset provenance: base `cUSDC` to wrapped Mainnet `cWUSDC`. +- Wrapped-token accounting: mint, burn, lock, release, or bridge-vault evidence. +- Cross-chain family context: where related `cUSDC` and `cWUSDC` contracts live. +- Supply reconciliation: why global family supply differs from Ethereum Mainnet `cWUSDC` supply. +- Submission support: CoinGecko, CMC, Etherscan profile, and exchange/indexer packets. + +Do not use bridge evidence for: + +- Replacing Ethereum Mainnet `totalSupply()`. +- Combining all cUSDC and cWUSDC supplies into the Ethereum token page supply. +- Claiming Etherscan Value without a provider-accepted USD price for the exact Mainnet contract. +- Treating Chain 138 balances as Mainnet circulating supply. + +## Action Matrix + +| Action | Repo-controlled | External approval needed | Priority | +|---|---:|---:|---:| +| Ethereum Mainnet supply/circulating attestation | yes | tracker acceptance | P0 | +| Etherscan token profile packet | yes | Etherscan acceptance | P0 | +| Etherscan `tokeninfo` monitor | yes | Etherscan propagation | P0 | +| CoinGecko contract listing/update | packet yes | CoinGecko acceptance | P0 | +| CMC listing/update | packet yes | CMC acceptance | P0 | +| Global cUSDC/cWUSDC family proof | yes | optional reviewer acceptance | P1 | +| Bridge provenance packet | yes | optional reviewer acceptance | P1 | +| Multichain Etherscan-family checks | yes for supported chains | none for reads | P1 | +| L2 deposit/withdrawal API checks | yes for supported chains | none for reads | P2 | +| Chain 138 MetaMask logo/prices | partially | wallet/provider acceptance | Out of Etherscan Value critical path | + +## Required Evidence Packet + +For Etherscan and market-data submissions, attach or reference: + +| Evidence | Location | +|---|---| +| Mainnet supply/circulating proof | `reports/status/cwusdc-supply-circulating-attestation-latest.json` and `.md` | +| Global family proof, clearly marked separate | `reports/status/global-cusdc-cwusdc-family-supply-proof-latest.json` and `.md` | +| Etherscan value monitor | `reports/status/cwusdc-etherscan-value-propagation-latest.json` and `.md` | +| Etherscan profile packet | `docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md` | +| Etherscan E2E recommendations | `docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md` | +| CoinGecko/CMC tracker packet | `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md` | +| Public report APIs | `https://explorer.d-bis.org/api/v1/report/coingecko?chainId=1`, `https://explorer.d-bis.org/api/v1/report/cmc?chainId=1`, `https://explorer.d-bis.org/api/v1/report/all` | + +## Operator Commands + +Generate the exact Mainnet supply proof: + +```bash +python3 scripts/verify/generate-cwusdc-supply-circulating-attestation.py +``` + +Generate the separate global cUSDC/cWUSDC family proof: + +```bash +python3 scripts/verify/generate-global-cusdc-cwusdc-family-supply-proof.py +``` + +Monitor Etherscan page, Etherscan `tokeninfo`, CoinGecko, DexScreener, and GeckoTerminal propagation: + +```bash +python3 scripts/verify/monitor-cwusdc-etherscan-value-propagation.py +``` + +Direct Etherscan `tokeninfo` probe: + +```bash +curl -fsS 'https://api.etherscan.io/v2/api?chainid=1&module=token&action=tokeninfo&contractaddress=0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a&apikey=YourApiKeyToken' | jq . +``` + +Direct Etherscan V2 chainlist probe: + +```bash +curl -fsS 'https://api.etherscan.io/v2/chainlist' | jq '{totalcount, chainids:[.result[].chainid]}' +``` + +Direct Etherscan source-verification probe: + +```bash +curl -fsS 'https://api.etherscan.io/v2/api?chainid=1&module=contract&action=getsourcecode&address=0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a&apikey=YourApiKeyToken' | jq '.result[0] | {ContractName, CompilerVersion, OptimizationUsed, Proxy, Implementation, LicenseType}' +``` + +Foundry verification pattern for a deployed contract: + +```bash +forge verify-contract --watch --chain mainnet 0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a path/to/Contract.sol:ContractName --verifier etherscan --etherscan-api-key "$ETHERSCAN_API_KEY" +``` + +Direct L2 deposit probe for the deployer/operator wallet: + +```bash +curl -fsS 'https://api.etherscan.io/v2/api?chainid=42161&module=account&action=getdeposittxs&address=0x4A666F96fC8764181194447A7dFdb7d471b301C8&page=1&offset=10&sort=desc&apikey=YourApiKeyToken' | jq . +``` + +For this endpoint, `value` is native value in raw wei, while `tokenValue` is raw units for the reported `tokenAddress`. For example, `value=1195403000000000` is `0.001195403 ETH`; `tokenValue=598200000000000` with `tokenAddress=ETH` is `0.0005982 ETH`. + +## Current Blocking Model + +Etherscan Value should be treated as blocked until all of these are true: + +1. Etherscan token profile is accepted and visible. +2. Etherscan `tokeninfo` returns correct metadata for `cWUSDC`. +3. Etherscan `tokeninfo.tokenPriceUSD` or token-page market cap fields become positive/nonblank. +4. CoinGecko or CMC accepts a USD price for the exact Ethereum Mainnet contract. +5. The Mainnet supply proof is submitted as the Ethereum token supply basis. + +The global cUSDC/cWUSDC family proof is useful context, but it is intentionally not the supply basis for the Ethereum token page. + +## References + +- Etherscan V2 introduction: `https://docs.etherscan.io/etherscan-v2` +- Etherscan V2 chainlist: `https://docs.etherscan.io/api-reference/endpoint/chainlist` +- Etherscan L2 deposit transactions by address: `https://docs.etherscan.io/api-reference/endpoint/getdeposittxs` +- Etherscan Foundry verification: `https://docs.etherscan.io/contract-verification/verify-with-foundry` +- Etherscan supported chains: `https://docs.etherscan.io/supported-chains` +- Etherscan token info API: `https://docs.etherscan.io/api-reference/endpoint/tokeninfo` +- Etherscan token update guide: `https://info.etherscan.com/how-to-update-token-information-on-token-page/` +- Etherscan token page guide: `https://info.etherscan.com/understanding-token-page/` diff --git a/docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md b/docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md new file mode 100644 index 00000000..7c2d7692 --- /dev/null +++ b/docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md @@ -0,0 +1,145 @@ +# cWUSDC Etherscan E2E Recommendations + +Status: end-to-end submission and follow-up checklist for the Ethereum Mainnet cWUSDC Etherscan token profile. + +## Goal + +Complete the Etherscan token-profile submission with a coherent public identity, validated profile assets, domain-matched contact information, and clean post-submission follow-up evidence. + +## Recommended Sequence + +1. Confirm public website readiness. +2. Confirm leadership/team professional-profile links. +3. Submit the Etherscan token profile with the canonical fields. +4. Monitor Etherscan approval and profile rendering. +5. Reuse the same approved profile facts across CoinGecko, CMC, DexScreener, GeckoTerminal, wallets, and token-list submissions. + +Canonical submission matrix: [../metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md](../metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md). Use it to track repo-ready metadata, EIP-747 wallet payloads, Etherscan, CoinGecko, CMC, Chainlist, token-list, and DEX-terminal submissions without implying that MetaMask fiat prices can be forced from this repo alone. + +Consolidated cWUSDC provider packet: [../CWUSDC_PROVIDER_SUBMISSION_PACKET.md](../CWUSDC_PROVIDER_SUBMISSION_PACKET.md). Use this as the shared narrative and evidence source, then use this Etherscan document for the Etherscan-specific sequence and post-submit checks. + +Latest live page review: [../../../reports/status/cwusdc-etherscan-token-page-review-20260511.md](../../../reports/status/cwusdc-etherscan-token-page-review-20260511.md). As of the review, Etherscan resolves the Ethereum Mainnet wrapped `cWUSDC` contract and verified source, but the visible token surface still shows generic `ERC20 ***`, token reputation is `Unknown`, and both market-cap fields are blank. Treat Chain 138 Explorer `cUSDC` as source-asset/system-of-record evidence only; Etherscan submissions must remain scoped to Mainnet `cWUSDC`. + +Decimal normalization fix: [../../../reports/status/cusdc-chain138-explorer-decimal-normalization-fix-20260511.md](../../../reports/status/cusdc-chain138-explorer-decimal-normalization-fix-20260511.md). The Chain 138 Explorer visible-liquidity issue was a 6-decimal raw-unit display problem: `5,180,095,723,066,127` raw units should render as `5,180,095,723.066127` for 6-decimal `cUSDC`/`cWUSDC` assets. + +## Pre-Submit Checks + +| Check | Recommendation | +|---|---| +| Website | Use `https://d-bis.org/` as the official website. | +| Email | Use `submissions@d-bis.org`. | +| Support email | Use `support@d-bis.org` only where a form asks for user support. | +| Social/profile email | Use `social.media@d-bis.org` only where a form asks for social-media/profile ownership. | +| Token contract | Submit exactly `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a`. | +| Logo | Use `https://d-bis.org/tokens/cwusdc.svg`; source mirror is `docs/04-configuration/etherscan/assets/cWUSDC-32x32.svg`. | +| Token name | Use `Wrapped cUSDC`. | +| Symbol | Use `cWUSDC`. | +| Description | Use the description in `CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md`. | +| Team/profiles | Confirm `https://d-bis.org/leadership` has public team entries and professional profile links. | +| Token explainer | Confirm `https://d-bis.org/gru/tokens` explains cUSDC / cWUSDC clearly. | +| Contact page | Confirm `https://d-bis.org/contact` is live and does not contain placeholders. | + +## Public Link Checks + +Run before submission: + +```bash +for url in \ + https://d-bis.org/ \ + https://d-bis.org/contact \ + https://d-bis.org/leadership \ + https://d-bis.org/gru/tokens \ + https://d-bis.org/security \ + https://d-bis.org/.well-known/trust.json +do + curl -L --max-time 12 -o /dev/null -s -w "$url %{http_code} %{content_type}\n" "$url" +done +``` + +**Automate (repo):** from the workspace root, `bash scripts/verify/check-cwusdc-etherscan-prereq-urls.sh` (same URLs; exits non-zero if any response is not HTTP `200`). + +Expected result: every URL returns HTTP `200`. + +## Submission Language + +Use neutral infrastructure wording: + +`cWUSDC is the Ethereum Mainnet compliant wrapped transport representation of Chain 138 cUSDC in the DBIS GRU asset family. The token uses 6 decimals, is source-verified on Etherscan and Sourcify, and is intended for public-network mirrored settlement and proof workflows. cWUSDC is a DBIS transport asset and should not be presented as an official Circle-issued USDC token.` + +Avoid: + +- claiming cWUSDC is Circle-issued USDC; +- claiming listing-quality public liquidity before USDC funding/repair is complete; +- claiming CoinGecko or full CMC approval before external acceptance; +- using personal email addresses or non-`d-bis.org` contact domains. + +## Brand Consistency + +| Surface | Recommendation | +|---|---| +| Etherscan | Use the 32x32 SVG and short institutional description. | +| Wallets | Use the same cW* icon grammar; no chain badges inside token artwork. | +| CoinGecko / CMC | Use the 512x512 PNG and the same token description, with liquidity caveats. | +| DexScreener / GeckoTerminal | Use matching logo/social/profile links; include indexed LP URLs. | +| Docs / whitepapers | Reference the semantic stack in `CW_TOKEN_BRANDING_SYSTEM_ANALYSIS.md`. | + +## Post-Submit Checks + +After Etherscan approval: + +1. Capture the approved token page URL and screenshot. +2. Confirm the logo renders clearly at token-list size and in the token page header. +3. Confirm website, email, and social/profile fields are correct. +4. Update `docs/04-configuration/coingecko/CWUSDC_MAINNET_EXTERNAL_SUBMISSION_CHECKLIST.md` from pending to accepted. +5. Add the approval evidence to a status report under `reports/status/`. + +## Follow-On Work + +| Work item | Why it matters | +|---|---| +| CoinGecko profile submission | Needed for tracker lookup, public supply acceptance, and Etherscan USD value path. | +| Full CMC token submission | Complements the CMC DEX page with full token metadata and supply profile. | +| DexScreener / GeckoTerminal profile updates | Keeps LP surfaces visually consistent with Etherscan. | +| Wallet token-list submissions | Improves token recognition in user wallets. | +| Official-USDC liquidity repair | Required before making listing-quality market-depth claims. | +| Bridge route validation | Required before claiming full two-way bridge production readiness. | + +## MetaMask EIP-747 Path + +Use the explorer wallet page as the primary practical MetaMask path: + +`https://explorer.d-bis.org/wallet` + +The page now reads the live Chain 138 MetaMask payload: + +`https://explorer.d-bis.org/api/v1/config/metamask?chainId=138` + +Operator/user sequence: + +1. Add Chain 138 with `wallet_addEthereumChain`. +2. Add featured tokens or all Chain 138 tokens with EIP-747 `wallet_watchAsset`. +3. Approve each MetaMask prompt; EIP-747 intentionally requires user confirmation per token. +4. Use the API report endpoints for prices and supply proof. MetaMask built-in fiat prices still depend on MetaMask/Consensys upstream asset and price providers, so this path provides wallet logos/token metadata but does not force native MetaMask price rendering. + +Live metadata checks: + +```bash +curl -fsS 'https://explorer.d-bis.org/api/v1/config/metamask?chainId=138' \ + | jq '.addEthereumChain.chainName, (.watchAssets | length), .watchAssets[0]' + +curl -fsS 'https://explorer.d-bis.org/api/v1/report/token-list?chainId=138' \ + | jq '.logoURI, (.tokens[] | select(.symbol=="cUSDC") | .logoURI)' +``` + +## Canonical References + +- Etherscan packet: `docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md` +- Consolidated cWUSDC provider packet: `docs/04-configuration/CWUSDC_PROVIDER_SUBMISSION_PACKET.md` +- GRU provider positioning packet: `docs/04-configuration/GRU_PROVIDER_POSITIONING_PACKET.md` +- Branding analysis: `docs/04-configuration/etherscan/CW_TOKEN_BRANDING_SYSTEM_ANALYSIS.md` +- Brand assets page: `https://d-bis.org/brand-assets` +- 32x32 SVG: `https://d-bis.org/tokens/cwusdc.svg` +- 32x32 SVG source mirror: `docs/04-configuration/etherscan/assets/cWUSDC-32x32.svg` +- External tracker checklist: `docs/04-configuration/coingecko/CWUSDC_MAINNET_EXTERNAL_SUBMISSION_CHECKLIST.md` +- Technical completion proof: `reports/status/mainnet-cwusdc-technical-completion-20260508.json` +- Supply proof: `reports/status/mainnet-cwusdc-supply-proof-20260508.json` diff --git a/docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_VALUE_EXECUTION_PLAN.md b/docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_VALUE_EXECUTION_PLAN.md new file mode 100644 index 00000000..73083cb4 --- /dev/null +++ b/docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_VALUE_EXECUTION_PLAN.md @@ -0,0 +1,185 @@ +# cWUSDC Etherscan Value Execution Plan + +Status: single-goal execution plan for making Etherscan show USD value for Ethereum Mainnet `cWUSDC`. + +## Goal + +Make Etherscan show a nonzero USD value for: + +```text +0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a +``` + +This is the only immediate objective for the current workstream. Chain 138 wallet UX, MetaMask local logos, Chainlink feeds, and third-party API probes are supporting work only when they help this Etherscan Value goal. + +## Bottom Line + +Etherscan Value is an off-chain indexer output. It is not controlled by the cWUSDC contract, by Chain 138 accounting, by token-list JSON, or by MetaMask EIP-747. + +The likely Etherscan Value path is: + +```text +Etherscan token profile accepted + + trusted market-data provider accepts Mainnet cWUSDC + + provider has usable USD price / market data for this exact contract + + Etherscan refreshes its external price feed + = Etherscan holder/token Value can become nonzero +``` + +## Exact Asset Identity + +| Field | Value | +|---|---| +| Network | Ethereum Mainnet | +| Chain ID | `1` | +| CAIP-19 | `eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Contract | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Name | `Wrapped cUSDC` | +| Symbol | `cWUSDC` | +| Decimals | `6` | +| Etherscan page | `https://etherscan.io/token/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Logo for Etherscan | `https://d-bis.org/tokens/cwusdc.svg` | +| DBIS report logo | `https://explorer.d-bis.org/api/v1/report/logo/cUSDC` | + +## Critical Path + +| Priority | Action | Why it matters for Etherscan Value | Evidence of completion | +|---:|---|---|---| +| 1 | Submit/update Etherscan token profile/logo/socials | Etherscan must trust the token identity before downstream value display is credible | Etherscan page shows approved logo/profile | +| 2 | Submit Mainnet cWUSDC to CoinGecko | CoinGecko is a likely Etherscan USD price source for ERC-20s | CoinGecko contract lookup returns cWUSDC with price | +| 3 | Submit Mainnet cWUSDC to CoinMarketCap | CMC reinforces market-data coverage and DexScan presence | CMC token page/quotes exist beyond DexScan-only page | +| 4 | Keep public cWUSDC/USDC DEX evidence current | Trackers may require market pair evidence before price acceptance | GeckoTerminal/CMC DEX/DexScreener pages show pair data | +| 5 | Provide supply/circulating-supply proof | Market cap and price feeds often require supply provenance | Tracker accepts total/circulating supply | +| 6 | Monitor Etherscan propagation | Etherscan may lag after provider acceptance | Etherscan holder/token rows show nonzero USD value | + +## Repo-Ready Evidence + +| Evidence | Status | Location | +|---|---|---| +| Etherscan bridge/cross-chain layer map | Ready | `docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_BRIDGE_CROSSCHAIN_LAYER_MAP.md` | +| Etherscan Value dossier | Ready | `reports/status/cwusdc-etherscan-value-dossier-latest.json` and `.md` | +| Etherscan profile packet | Ready | `docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md` | +| Etherscan E2E recommendations | Ready | `docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md` | +| External tracker checklist | Ready | `docs/04-configuration/coingecko/CWUSDC_MAINNET_EXTERNAL_SUBMISSION_CHECKLIST.md` | +| Tracker submission packet | Ready | `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md` | +| Supply proof | Ready | `reports/status/mainnet-cwusdc-supply-proof-20260508.json` | +| Fresh supply/circulating attestation | Ready | `reports/status/cwusdc-supply-circulating-attestation-latest.json` and `.md` | +| Global cUSDC/cWUSDC family supply proof | Separate reference only | `reports/status/global-cusdc-cwusdc-family-supply-proof-latest.json` and `.md`; do not use for Ethereum Etherscan Value | +| Etherscan value propagation monitor | Ready | `reports/status/cwusdc-etherscan-value-propagation-latest.json` and `.md` | +| CoinGecko-shaped API | Ready | `https://explorer.d-bis.org/api/v1/report/coingecko?chainId=1` | +| CMC-shaped API | Ready | `https://explorer.d-bis.org/api/v1/report/cmc?chainId=1` | +| Token-list API | Ready | `https://explorer.d-bis.org/api/v1/report/token-list?chainId=1` | +| cWUSDC logo endpoint | Ready | `https://explorer.d-bis.org/api/v1/report/logo/cUSDC` | +| GeckoTerminal V3 pool | Indexed | `https://www.geckoterminal.com/eth/pools/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3` | +| GeckoTerminal V2 pool | Indexed | `https://www.geckoterminal.com/eth/pools/0xc28706f899266b36bc43cc072b3a921bdf2c48d9` | +| CMC DEX token page | Reachable | `https://dex.coinmarketcap.com/token/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a/` | + +## Immediate Operator Tasks + +1. Submit Etherscan token profile update. + - Use `submissions@d-bis.org`. + - Use `https://d-bis.org/tokens/cwusdc.svg`. + - Use `Wrapped cUSDC` / `cWUSDC`. + - Use the neutral description from `CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md`. + +2. Submit CoinGecko Mainnet cWUSDC listing/update. + - Submit the Ethereum Mainnet contract, not Chain 138 cUSDC. + - Include supply proof and tracker caveats. + - Include GeckoTerminal and CMC DEX evidence. + +3. Submit CMC listing/update. + - Reference the existing CMC DEX token page. + - Attach CMC report endpoint and supply proof. + +4. Record all external confirmations. + - Ticket ID, account/email used, submitted URLs, screenshots, and response. + - Write status evidence under `reports/status/`. + +5. Monitor for propagation. + - Etherscan token page. + - Etherscan holder pages. + - CoinGecko contract lookup. + - CMC token/quotes page. + +## API Checks + +Generate the current supply/circulating-supply attestation: + +```bash +python3 scripts/verify/generate-cwusdc-supply-circulating-attestation.py +``` + +Generate the separate global cUSDC/cWUSDC family proof: + +```bash +python3 scripts/verify/generate-global-cusdc-cwusdc-family-supply-proof.py +``` + +Monitor Etherscan value propagation and upstream blockers: + +```bash +python3 scripts/verify/monitor-cwusdc-etherscan-value-propagation.py +``` + +Build the full Etherscan Value dossier: + +```bash +pnpm cwusdc:etherscan-dossier +``` + +Repo-controlled readiness: + +```bash +curl -fsS 'https://explorer.d-bis.org/api/v1/report/coingecko?chainId=1' \ + | jq '.tokens[] | select(.symbol=="cWUSDC") | {symbol,contract_address,logo_uri,total_supply,circulating_supply,market_cap:(.market_data.market_cap // .marketCapUsd // null),tracker_caveats}' +``` + +Etherscan public page: + +```bash +curl -L --max-time 15 -s -o /dev/null -w '%{http_code} %{url_effective}\n' \ + 'https://etherscan.io/token/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a' +``` + +CoinGecko after acceptance: + +```bash +curl -fsS 'https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_last_updated_at=true' +``` + +DexScreener indexing check: + +```bash +curl -fsS 'https://api.dexscreener.com/token-pairs/v1/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a' +``` + +## Out of Scope Unless It Supports Etherscan Value + +| Work | Status for this goal | +|---|---| +| Chain 138 logo in MetaMask | Helpful for UX; not an Etherscan Value blocker | +| MetaMask EIP-747 refresh | Helpful for local wallet metadata; not an Etherscan Value blocker | +| MetaMask Snap allowlist | Not required for Etherscan Value | +| Chainlink cWUSDC/USD feed | Future institutional oracle target; not immediate Etherscan Value path | +| Alchemy/Moralis/thirdweb API probes | Useful diagnostics; not primary until Etherscan/CoinGecko/CMC submissions are done | +| Additional cW* pools | Useful market evidence only if they produce trusted public price data | + +## Blocking Reality + +Etherscan Value cannot be forced from this repo alone. The repo can provide a clean evidence package, public APIs, logos, supply proof, and DEX evidence. The value flips only after Etherscan or its trusted upstream price providers accept a USD mark for the exact Ethereum Mainnet cWUSDC contract. + +## Current Latest Monitor Result + +As of the latest local run, the supply/circulating-supply attestation is generated and ready for submission. The propagation monitor still reports: + +- Etherscan Onchain Market Cap is blank. +- Etherscan Circulating Supply Market Cap is blank. +- CoinGecko contract price API does not return a positive USD price. + +This means the two focused tasks are split as follows: + +| Task | Current state | Next action | +|---|---|---| +| Provide supply/circulating-supply proof | Complete repo-side; latest attestation generated | Attach `cwusdc-supply-circulating-attestation-latest.*` to Etherscan/CoinGecko/CMC submissions | +| Provide global family supply proof | Complete repo-side; separate proof generated | Use only as family/context evidence; do not use as Ethereum Mainnet cWUSDC supply | +| Monitor Etherscan propagation | Complete repo-side; monitor generated | Re-run monitor after every external submission/approval until Etherscan market cap/value fields become nonblank | diff --git a/docs/04-configuration/etherscan/CWUSDC_EVIDENCE_BUNDLE_INDEX.md b/docs/04-configuration/etherscan/CWUSDC_EVIDENCE_BUNDLE_INDEX.md new file mode 100644 index 00000000..2c09da10 --- /dev/null +++ b/docs/04-configuration/etherscan/CWUSDC_EVIDENCE_BUNDLE_INDEX.md @@ -0,0 +1,81 @@ +# cWUSDC Evidence Bundle Index + +Status: institutional submission index for Ethereum Mainnet `cWUSDC`. + +## Scope + +This bundle is for: + +```text +Network: Ethereum Mainnet +Contract: 0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a +CAIP-19: eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a +Name: Wrapped cUSDC +Symbol: cWUSDC +Decimals: 6 +``` + +Chain 138 `cUSDC` and global cUSDC/cWUSDC family evidence are context only. They are not the Ethereum Mainnet cWUSDC token-page supply basis. + +## Primary Evidence + +| Artifact | Path / URL | Purpose | Status | +|---|---|---|---| +| Etherscan token page | `https://etherscan.io/token/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | Public Mainnet token surface | Live; market cap blank | +| Etherscan Value dossier | `reports/status/cwusdc-etherscan-value-dossier-latest.md` | Single current readiness packet | Repo-ready | +| Institutional readiness review | `reports/status/cwusdc-institutional-readiness-review-20260511.md` | Scored gap and recommendation report | Current | +| Supply/circulating attestation | `reports/status/cwusdc-supply-circulating-attestation-latest.md` | Mainnet supply basis | Current | +| Global family supply proof | `reports/status/global-cusdc-cwusdc-family-supply-proof-latest.md` | Cross-chain context only | Current | +| cUSDC/cWUSDC feed audit | `reports/status/cusdc-cwusdc-etherscan-feed-audit-latest.md` | Source/wrapped relationship evidence | Current | +| Etherscan token-page review | `reports/status/cwusdc-etherscan-token-page-review-20260511.md` | Manual/live page-state review | Current | +| Mainnet role audit | `reports/status/cwusdc-mainnet-role-audit-latest.md` | Known-candidate admin/minter/burner role snapshot | Current | +| Role deployment appendix | `reports/status/cwusdc-role-deployment-appendix-latest.md` | Role-event to repo-record reconciliation appendix | Current | +| Institutional doc link check | `reports/status/cwusdc-institutional-doc-link-check-latest.md` | Narrow link-check for this packet | Current | +| Provider submission prefill | `reports/status/cwusdc-provider-submission-prefill-latest.md` | Prefilled fields and screenshot capture checklist | Current | +| Provider monitor snapshot | `reports/status/cwusdc-provider-monitoring-snapshot-latest.md` | Scheduled-monitor output surface | Current | +| Provider handoff | `reports/status/cwusdc-provider-handoff-latest.md` | Public provider readiness state | Current | +| External tracker probes | `reports/status/cwusdc-external-trackers-live-latest.md` | CoinGecko/CMC/DexScreener/GeckoTerminal checks | Current | + +## Submission Documents + +| Document | Purpose | +|---|---| +| `docs/04-configuration/CWUSDC_PROVIDER_SUBMISSION_PACKET.md` | Shared cross-provider narrative and facts | +| `docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md` | Etherscan profile copy/paste packet | +| `docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md` | Etherscan submission and follow-up checklist | +| `docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_VALUE_EXECUTION_PLAN.md` | Etherscan USD Value execution path | +| `docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_BRIDGE_CROSSCHAIN_LAYER_MAP.md` | Bridge/cross-chain evidence boundaries | +| `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md` | CoinGecko/CMC tracker packet | +| `docs/04-configuration/dexscreener/CWUSDC_DEXSCREENER_INDEXING_AND_PROFILE_PACKET_20260509.md` | DexScreener indexing/profile packet | +| `docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md` | Wallet/provider matrix | + +## Institutional Control Documents + +| Document | Purpose | +|---|---| +| `docs/04-configuration/etherscan/CWUSDC_SUPPLY_AND_CIRCULATING_METHODOLOGY.md` | Mainnet supply/circulating methodology | +| `docs/04-configuration/etherscan/CWUSDC_SECURITY_AND_AUDIT_DISCLOSURE.md` | Audit posture and controls disclosure | +| `docs/04-configuration/etherscan/CWUSDC_PROVIDER_RESPONSE_TRACKER.md` | External ticket/status tracker | +| `docs/04-configuration/etherscan/CWUSDC_LIQUIDITY_READINESS_NO_BROADCAST_PLAN.md` | No-broadcast liquidity readiness plan | + +## Refresh Commands + +```bash +pnpm cwusdc:etherscan-dossier +pnpm cwusdc:role-audit +pnpm cwusdc:role-appendix +pnpm cwusdc:doc-links +pnpm cwusdc:submission-prefill +pnpm cwusdc:provider-checks +pnpm cwusdc:provider-ci +pnpm cwusdc:provider-monitor +pnpm cwusdc:evidence-bundle +``` + +## Evidence Rules + +- Use only Ethereum Mainnet cWUSDC supply for Etherscan Value submissions. +- Use Chain 138 cUSDC as source-asset provenance only. +- Use global family supply only as context and disclose double-counting risk. +- Do not claim accepted CoinGecko, CMC, DexScreener, MetaMask, or Etherscan Value status until the public provider page/API confirms it. +- Do not claim 1:1 peg or institutional liquidity depth while official Mainnet USDC quote-side liquidity remains thin. diff --git a/docs/04-configuration/etherscan/CWUSDC_LIQUIDITY_READINESS_NO_BROADCAST_PLAN.md b/docs/04-configuration/etherscan/CWUSDC_LIQUIDITY_READINESS_NO_BROADCAST_PLAN.md new file mode 100644 index 00000000..0c490b7c --- /dev/null +++ b/docs/04-configuration/etherscan/CWUSDC_LIQUIDITY_READINESS_NO_BROADCAST_PLAN.md @@ -0,0 +1,65 @@ +# cWUSDC Liquidity Readiness No-Broadcast Plan + +Status: planning document only. This file does not authorize transactions. + +## Purpose + +Prepare the repo-side plan for improving public cWUSDC/USDC liquidity evidence without broadcasting transactions or moving funds from this document. + +## Current Boundary + +The current provider work is submission-ready, but liquidity is not institutionally deep. Etherscan/token trackers can index public facts, but a robust value claim requires accepted external price feeds and visible, non-dust official quote-side liquidity. + +## No-Broadcast Rules + +- Do not approve, swap, add liquidity, remove liquidity, bridge, or mint from this plan. +- Do not claim 1:1 peg quality until official Mainnet USDC quote-side depth supports it. +- Do not use Chain 138 cUSDC activity as Ethereum Mainnet cWUSDC trading volume. +- Do not use internal netting as public DEX activity. + +## Readiness Inputs + +| Input | Required before live action | +|---|---| +| Official Mainnet USDC available | Amount, source, wallet, tx proof | +| ETH gas reserve | Enough for approval, add-liquidity, swap proof, and monitoring | +| Target pool | Uniswap v3/v2/DODO address and expected quote behavior | +| Slippage policy | Max slippage, minOut, deadline | +| Event policy | Required swaps/liquidity events for tracker freshness | +| Rollback policy | What to do if pool price moves outside tolerance | + +## Current Public Surfaces + +| Surface | Role | Current interpretation | +|---|---|---| +| Uniswap v3 cWUSDC/USDC | Primary indexed evidence surface | Visible on GeckoTerminal; liquidity thin | +| Uniswap v2 cWUSDC/USDC | Legacy indexed evidence surface | Visible on GeckoTerminal; liquidity very thin | +| DexScreener token APIs | Tracker discoverability | Currently empty for cWUSDC | +| CMC DEX | DEX discoverability | Public probe passes | + +## If USDC Becomes Available + +1. Re-run: + +```bash +pnpm cwusdc:etherscan-dossier +pnpm cwusdc:provider-checks +``` + +2. Snapshot balances and pool state. +3. Simulate target liquidity action with no broadcast. +4. Confirm expected reserve and price impact. +5. Execute only with explicit operator approval. +6. Re-run provider checks after confirmations. +7. Capture pair URLs, tx hashes, and reserve changes. + +## Institutional Claim Gate + +Do not make the following claims until evidence supports them: + +| Claim | Required evidence | +|---|---| +| cWUSDC has Etherscan USD Value | Etherscan holder/token page shows nonblank USD value/market cap | +| cWUSDC has accepted public market price | CoinGecko or CMC returns exact-contract positive USD price | +| cWUSDC/USDC is 1:1 supported | Official USDC quote-side liquidity and real public swaps support the claim | +| cWUSDC has DEX discoverability | DexScreener/GeckoTerminal/CMC DEX return visible pair data | diff --git a/docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md b/docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md new file mode 100644 index 00000000..7c392d83 --- /dev/null +++ b/docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md @@ -0,0 +1,95 @@ +# cWUSDC Mainnet Etherscan Profile Packet + +Status: ready for Etherscan token profile submission, using `submissions@d-bis.org` as the official contact email. The submitter still must confirm the public team/profile links on the live website. + +## Copy-Paste Fields + +| Field | Value | +|---|---| +| Token contract | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Network | Ethereum Mainnet | +| Token name | `Wrapped cUSDC` | +| Symbol | `cWUSDC` | +| Decimals | `6` | +| Official website | `https://d-bis.org/` | +| Contact email | `submissions@d-bis.org` | +| Support email | `support@d-bis.org` | +| Social/profile email | `social.media@d-bis.org` | +| 32x32 SVG logo | `https://d-bis.org/tokens/cwusdc.svg` | +| 32x32 SVG source | `docs/04-configuration/etherscan/assets/cWUSDC-32x32.svg` | +| Larger PNG logo | `docs/04-configuration/coingecko/logos/cUSDC-512x512.png` | +| Token SVG source logo | `smom-dbis-138/services/token-aggregation/public/token-logos/gru/cUSDC.svg` | +| Etherscan token page | `https://etherscan.io/token/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Source contract | `smom-dbis-138/contracts/tokens/CompliantWrappedToken.sol` | +| Tracker packet | `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md` | +| Supply proof | `reports/status/mainnet-cwusdc-supply-proof-20260508.json` | +| cUSDC/cWUSDC API feed proof | `docs/04-configuration/etherscan/CUSDC_CWUSDC_ETHERSCAN_FEED_AUDIT.md`; latest generated local outputs are `reports/status/cusdc-cwusdc-etherscan-feed-audit-latest.md` and `reports/status/cusdc-cwusdc-etherscan-feed-audit-latest.json` | +| Technical completion proof | `reports/status/mainnet-cwusdc-technical-completion-20260508.json` | +| Branding system analysis | `docs/04-configuration/etherscan/CW_TOKEN_BRANDING_SYSTEM_ANALYSIS.md` | +| E2E recommendations | `docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md` | + +## Etherscan Notice Matrix + +| Etherscan requirement | Current evidence | Submitter action | +|---|---|---| +| Mandatory website, email, and 32x32 SVG logo provided | Website is `https://d-bis.org/`; contact email is `submissions@d-bis.org`; 32x32 SVG asset is hosted at `https://d-bis.org/tokens/cwusdc.svg` and mirrored in this packet. | Use `submissions@d-bis.org` in the Etherscan form. | +| Email/contact email matches official website domain | Required domain is `d-bis.org`; selected mailbox is `submissions@d-bis.org`. | Use `submissions@d-bis.org`. | +| Support and social contact paths are domain-matched | Support is `support@d-bis.org`; social/profile contact is `social.media@d-bis.org`. | Use these only where a form asks for support or social-media/profile ownership. | +| Official website accessible and safe | `curl -L https://d-bis.org/` returned HTTP `200` on 2026-05-09. | Confirm in browser from the Etherscan account session. | +| Website links work and placeholders are updated | Live homepage exposes project pages including `/about`, `/leadership`, `/gru/tokens`, `/legal`, `/contact`, `/security`, and `/.well-known/trust.json`; sampled pages returned HTTP `200` on 2026-05-09. | Before submitting, click through the public nav and remove or fix any visible placeholders. | +| Website has clear project/token information | `https://d-bis.org/gru/tokens` is the public token directory; `https://d-bis.org/about` and `https://d-bis.org/gru/overview` describe DBIS/GRU context. | Ensure the `cWUSDC` row or token detail is visible and points to the Mainnet contract. | +| Contract adheres to ERC-20 specs | Contract is verified and implements the compliant wrapped token ERC-20 surface; local source is `CompliantWrappedToken.sol`. | No extra action unless Etherscan asks for audit or source notes. | +| Contract is valid and the right address | Mainnet cWUSDC contract is `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a`; supply proof and tracker packet point to the same address. | Paste the exact checksum address. | +| Name/symbol/creative properties avoid infringement | Submit as `Wrapped cUSDC` / `cWUSDC`, a DBIS compliant wrapped transport token. | Do not imply official Circle/USDC sponsorship; describe it as a wrapped transport representation of Chain 138 `cUSDC`. | +| Not fraudulent or misrepresenting public entities/projects | Website, contract, and packet describe DBIS/GRU and cWUSDC as project-owned infrastructure. | Keep submission language narrow; avoid claiming governmental or third-party issuer status unless separately documented. | +| Uses/deployed on Ethereum blockchain | Contract is on Ethereum Mainnet chain ID `1`. | Select Ethereum Mainnet in Etherscan. | +| Team/founders clearly presented with professional profile links | `https://d-bis.org/leadership` is live. | Confirm leadership entries include supporting professional links such as LinkedIn or equivalent. If not, add those links to the website before submitting. | + +## Suggested Description + +`cWUSDC is the Ethereum Mainnet compliant wrapped transport representation of Chain 138 cUSDC in the DBIS GRU asset family. The token uses 6 decimals, is source-verified on Etherscan and Sourcify, and is intended for public-network mirrored settlement and proof workflows. cWUSDC is a DBIS transport asset and should not be presented as an official Circle-issued USDC token.` + +## cUSDC / cWUSDC API Feed Position + +Etherscan should index and display Ethereum Mainnet activity for the `cWUSDC` token contract only. Chain 138 `cUSDC` traffic is not expected to be added to the Etherscan token transfer totals, but it should be referenced as the canonical source-asset feed that `cWUSDC` wraps. + +Latest local API audit: + +- Chain 138 source token: `cUSDC` at `0xf22258f57794CC8E06237084b353Ab30fFfa640b` +- Ethereum wrapped token: `cWUSDC` at `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` +- Mapping source: `config/token-mapping-multichain.json` +- Static audit note: `docs/04-configuration/etherscan/CUSDC_CWUSDC_ETHERSCAN_FEED_AUDIT.md` +- Evidence packet: `reports/status/cusdc-cwusdc-etherscan-feed-audit-latest.md` + +Submission language: + +`cWUSDC is the Ethereum Mainnet wrapped transport representation of canonical Chain 138 cUSDC. Please use the Ethereum Mainnet cWUSDC contract for Etherscan token-page transfers and supply, and use the Chain 138 cUSDC explorer/API feed only as supporting evidence for the canonical source asset relationship.` + +## Logo Rationale + +The submitted 32x32 mark uses a repeatable cW* symbolic grammar: the outer `C` represents currency-class fiat cash, the inner `c` represents compliant electronic money, the `W` represents a wrapped / bridged transport form, and the `USD` label represents the ISO-4217 denomination. The design is intentionally institutional and settlement-oriented, not a claim of Circle-issued USDC branding. + +## Links To Include + +- Website: `https://d-bis.org/` +- Brand assets: `https://d-bis.org/brand-assets` +- 32x32 SVG logo: `https://d-bis.org/tokens/cwusdc.svg` +- Token directory: `https://d-bis.org/gru/tokens` +- Leadership: `https://d-bis.org/leadership` +- Contact: `https://d-bis.org/contact` +- Security: `https://d-bis.org/security` +- Trust anchors: `https://d-bis.org/.well-known/trust.json` +- Explorer: `https://etherscan.io/token/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` +- Chain 138 explorer: `https://explorer.d-bis.org/` +- Compact cWUSDC price/supply evidence API: `https://explorer.d-bis.org/token-aggregation/api/v1/report/token-price/cWUSDC?chainId=1` +- Chain 138 source cUSDC evidence API: `https://explorer.d-bis.org/token-aggregation/api/v1/report/token-price/cUSDC?chainId=138` +- CoinGecko-format cWUSDC report API: `https://explorer.d-bis.org/token-aggregation/api/v1/report/coingecko?chainId=1` +- CMC-format cWUSDC report API: `https://explorer.d-bis.org/token-aggregation/api/v1/report/cmc?chainId=1` +- Docs: `https://docs.d-bis.org/` + +## Submission Guardrails + +- Do not claim listing-quality public liquidity until the official-USDC liquidity gaps in `reports/status/mainnet-cwusdc-usdc-repeg-plan-latest.json` are funded and repaired. +- Do not claim CoinGecko or full CoinMarketCap listing acceptance until the external tracker pages are live. +- Do not describe cWUSDC as Circle-issued USDC. Use `compliant wrapped transport representation of Chain 138 cUSDC`. +- Use `submissions@d-bis.org` and ensure the mailbox can respond to Etherscan verification follow-up. diff --git a/docs/04-configuration/etherscan/CWUSDC_PROVIDER_RESPONSE_TRACKER.md b/docs/04-configuration/etherscan/CWUSDC_PROVIDER_RESPONSE_TRACKER.md new file mode 100644 index 00000000..6cf440a0 --- /dev/null +++ b/docs/04-configuration/etherscan/CWUSDC_PROVIDER_RESPONSE_TRACKER.md @@ -0,0 +1,53 @@ +# cWUSDC Provider Response Tracker + +Status: operator-updated tracker for external provider submissions and responses. + +## Submission Tracker + +| Provider | Status | Submitted date | Account/contact | Ticket / PR / form ID | Public URL | Latest response | Next action | +|---|---|---|---|---|---|---|---| +| Etherscan profile | `not_recorded` | TBD | `submissions@d-bis.org` | TBD | `https://etherscan.io/token/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | Not recorded | Submit/re-submit profile packet. | +| CoinGecko | `not_recorded` | TBD | TBD | TBD | TBD | Not recorded | Submit tracker packet for exact Mainnet contract. | +| CoinMarketCap | `not_recorded` | TBD | TBD | TBD | CMC DEX page visible | Not recorded | Submit/update full token profile. | +| DexScreener | `not_recorded` | TBD | TBD | TBD | TBD | API currently returns no pairs | Refresh pair evidence and request profile/indexing. | +| GeckoTerminal | `visible_partial` | TBD | TBD | TBD | V2/V3 pool APIs visible | Pool data visible, but liquidity thin | Keep pool evidence fresh. | +| MetaMask provider price | `not_recorded` | TBD | TBD | TBD | N/A | No native price acceptance recorded | Wait on upstream provider acceptance. | + +## Status Values + +Use only these values: + +```text +not_recorded +drafted +submitted +provider_question +accepted +rejected +blocked_external +visible_partial +closed_no_action +``` + +## Update Rules + +- Record the exact contract and CAIP-19 in every ticket. +- Record the account/email used. +- Record submission timestamp. +- Attach screenshots or API responses under `reports/status/`. +- Do not mark `accepted` unless the public provider page/API confirms acceptance. +- Keep Etherscan, CoinGecko, CMC, DexScreener, and MetaMask price statuses separate. + +## Standard Submission Identity + +```text +Network: Ethereum Mainnet +Contract: 0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a +CAIP-19: eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a +Name: Wrapped cUSDC +Symbol: cWUSDC +Decimals: 6 +Website: https://d-bis.org/ +Logo: https://d-bis.org/tokens/cwusdc.svg +Contact: submissions@d-bis.org +``` diff --git a/docs/04-configuration/etherscan/CWUSDC_SECURITY_AND_AUDIT_DISCLOSURE.md b/docs/04-configuration/etherscan/CWUSDC_SECURITY_AND_AUDIT_DISCLOSURE.md new file mode 100644 index 00000000..396ec429 --- /dev/null +++ b/docs/04-configuration/etherscan/CWUSDC_SECURITY_AND_AUDIT_DISCLOSURE.md @@ -0,0 +1,100 @@ +# cWUSDC Security and Audit Disclosure + +Status: institutional controls disclosure for provider submissions while formal external audit evidence is not yet recorded in this repo. + +## Current Audit Posture + +No formal third-party audit URL is recorded in the current cWUSDC Etherscan evidence packet. + +Until a formal audit is available and submitted through Etherscan's audit route, provider-facing materials must use this posture: + +```text +The cWUSDC Mainnet contract is source-verified and ABI-visible on Etherscan. A formal third-party audit URL has not yet been recorded in the current submission packet. DBIS maintains operational controls, source verification evidence, supply attestations, and monitoring reports; this should not be represented as a completed third-party audit. +``` + +## Verified Contract Evidence + +Latest dossier evidence reports: + +| Field | Value | +|---|---| +| Contract | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Verified | `true` | +| Contract name | `CompliantWrappedToken` | +| Compiler | `v0.8.20+commit.a1b79de6` | +| Optimization | enabled, `200` runs | +| EVM version | `london` | +| Proxy | `0` | +| ABI available | `true` | + +Evidence: + +```text +reports/status/cwusdc-etherscan-value-dossier-latest.json +``` + +## Operational Controls + +| Control | Current evidence | +|---|---| +| Source verification | Etherscan `getsourcecode` check in dossier | +| Supply monitoring | `cwusdc-supply-circulating-attestation-latest.*` | +| Provider readiness monitoring | `cwusdc-provider-handoff-latest.*` | +| Etherscan Value monitoring | `cwusdc-etherscan-value-propagation-latest.*` | +| Cross-chain boundary disclosure | `CWUSDC_ETHERSCAN_BRIDGE_CROSSCHAIN_LAYER_MAP.md` | +| Mainnet-only supply policy | `CWUSDC_SUPPLY_AND_CIRCULATING_METHODOLOGY.md` | +| Role/control snapshot | `reports/status/cwusdc-mainnet-role-audit-latest.md` | + +## Remaining Audit Tasks + +| Priority | Task | Status | +|---:|---|---| +| P0 | Identify whether a formal audit exists for `CompliantWrappedToken` | Open | +| P0 | If audit exists, add URL/hash to this packet and Etherscan submission | Open | +| P1 | If no audit exists, publish unaudited status in provider packet | Complete in this disclosure | +| P1 | Add admin/owner/mint/burn role review artifact | Complete as read-only known-candidate snapshot; unknown role members still require event/deployment-log review | +| P1 | Add incident-response contact and escalation path to public docs | Complete in provider packet | + +## Latest Role Snapshot + +Latest read-only role audit: + +```text +reports/status/cwusdc-mainnet-role-audit-latest.json +reports/status/cwusdc-mainnet-role-audit-latest.md +``` + +Current observed candidate role state: + +- `deployer` has `DEFAULT_ADMIN_ROLE`. +- `cwBridgeMainnet` (`0x2bF74583206A49Be07E0E8A94197C12987AbD7B5`) has `MINTER_ROLE` and `BURNER_ROLE`. +- The checked relay/router/pool/vault candidates do not have `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE`, or `BURNER_ROLE`. +- `MINTER_ROLE` and `BURNER_ROLE` are administered by `DEFAULT_ADMIN_ROLE`. + +The event-log reconstruction currently observes the deployer as the effective admin and the Mainnet cW bridge as the effective minter/burner. Limitation: provider log limits or pruned responses can still require independent deployment-record review before treating this as a formal audit. + +## Incident Response + +| Purpose | Contact / URL | +|---|---| +| Provider submissions | `submissions@d-bis.org` | +| User support | `support@d-bis.org` | +| Security / responsible disclosure | `https://d-bis.org/security` | +| General contact | `https://d-bis.org/contact` | +| Trust metadata | `https://d-bis.org/.well-known/trust.json` | + +## Provider Boundary + +Do not write: + +```text +cWUSDC is audited. +``` + +Unless a formal audit URL is attached and verified. + +Use: + +```text +cWUSDC is source-verified on Etherscan; formal third-party audit evidence is not yet recorded in the current submission packet. +``` diff --git a/docs/04-configuration/etherscan/CWUSDC_SUPPLY_AND_CIRCULATING_METHODOLOGY.md b/docs/04-configuration/etherscan/CWUSDC_SUPPLY_AND_CIRCULATING_METHODOLOGY.md new file mode 100644 index 00000000..a3a9a3d6 --- /dev/null +++ b/docs/04-configuration/etherscan/CWUSDC_SUPPLY_AND_CIRCULATING_METHODOLOGY.md @@ -0,0 +1,84 @@ +# cWUSDC Supply and Circulating Methodology + +Status: institutional methodology for Ethereum Mainnet `cWUSDC` provider submissions. + +## Asset Boundary + +| Field | Value | +|---|---| +| Network | Ethereum Mainnet | +| Chain ID | `1` | +| Contract | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| CAIP-19 | `eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Name | `Wrapped cUSDC` | +| Symbol | `cWUSDC` | +| Decimals | `6` | + +## Methodology + +Total supply is read from the Ethereum Mainnet cWUSDC contract through Etherscan/API evidence and normalized by the token's 6-decimal divisor. + +Circulating supply defaults to total supply unless a tracker requests a specific exclusion policy and the excluded balances are explicitly identified, documented, and re-generated with that policy. + +Current formula: + +```text +circulatingSupply = totalSupply - explicitlyExcludedProtocolControlledNonCirculatingBalances +``` + +Current default: + +```text +excludedProtocolControlledNonCirculatingBalances = 0 +``` + +## Current Attestation + +Latest generated evidence: + +```text +reports/status/cwusdc-supply-circulating-attestation-latest.json +reports/status/cwusdc-supply-circulating-attestation-latest.md +``` + +Latest values: + +```text +totalSupply = 10,451,316,981.309788 cWUSDC +circulatingSupply = 10,451,316,981.309788 cWUSDC +``` + +## Exclusion Policy + +No wallet, vault, pool, operator, bridge, or treasury balance is excluded by default. + +If a tracker requests exclusions: + +1. Identify the exact address. +2. Identify the legal/control reason for exclusion. +3. Capture the on-chain balance at a reference block. +4. Re-run the supply attestation with explicit exclusion labels. +5. Attach the generated JSON and Markdown to the tracker response. + +## Cross-Chain Boundary + +Chain 138 `cUSDC`, other cUSDC contracts, and other cWUSDC deployments are not part of the Ethereum Mainnet cWUSDC token-page supply. + +Global family supply proof may be attached only as context: + +```text +reports/status/global-cusdc-cwusdc-family-supply-proof-latest.json +reports/status/global-cusdc-cwusdc-family-supply-proof-latest.md +``` + +Provider-facing warning: + +```text +Global cUSDC/cWUSDC family totals should not be used as Ethereum Mainnet cWUSDC market-cap input. They are cross-chain family inventory and may double-count source and wrapped representations unless a tracker-approved methodology is applied. +``` + +## Refresh Command + +```bash +python3 scripts/verify/generate-cwusdc-supply-circulating-attestation.py +``` diff --git a/docs/04-configuration/etherscan/CW_TOKEN_BRANDING_SYSTEM_ANALYSIS.md b/docs/04-configuration/etherscan/CW_TOKEN_BRANDING_SYSTEM_ANALYSIS.md new file mode 100644 index 00000000..8c699275 --- /dev/null +++ b/docs/04-configuration/etherscan/CW_TOKEN_BRANDING_SYSTEM_ANALYSIS.md @@ -0,0 +1,97 @@ +# cW Token Branding System Analysis + +Status: brand-governance reference for cWUSDC and the broader cW* compliant wrapped token family. + +## Purpose + +This note records the branding framework used for Etherscan, wallet, explorer, protocol, and tracker submissions. It treats the cWUSDC mark as part of a repeatable monetary-symbol system, not as a one-off token icon. + +## Semantic Stack + +| Element | Meaning | +|---|---| +| Outer `C` | Currency-class fiat cash instrument | +| Inner `c` | Compliant electronic money | +| `W` | Wrapped / bridged representation | +| `USD` | ISO-4217 dollar denomination | + +The icon encodes `compliant wrapped USD-denominated fiat currency` in a compact 32x32 asset. This is the preferred explanation when a reviewer asks why the mark uses currency-ring geometry or blue stable-value visual language. + +## Revised Design Evaluation + +| Category | Score | +|---|---:| +| Originality | `8.6 / 10` | +| Brand architecture | `9.2 / 10` | +| Symbolic coherence | `9.4 / 10` | +| Regulatory / institutional positioning | `9.1 / 10` | +| Final professional score | `9.0 / 10` | + +Professional grade: `A`. + +## Ecosystem Fit + +The mark is strongest in regulated financial-infrastructure contexts: + +| Context | Fit | Notes | +|---|---|---| +| Etherscan token profile | High | Compact, legible, and institutionally conservative. | +| Wallet token lists | High | The nested currency grammar survives small icon presentation. | +| Explorer token pages | High | White-on-blue reserve-money language reads cleanly in dark and light UI. | +| Protocol documentation | High | The glyph stack maps directly to the cW* asset model. | +| Institutional decks | High | The visual tone suggests settlement, compliance, and treasury rails. | +| Retail meme-token positioning | Low | The mark intentionally avoids speculative or mascot-driven crypto cues. | + +## Currency Scalability + +The model scales across ISO-4217 variants by preserving the outer/inner/wrapped structure and changing only the denomination label and optional currency accent. + +| Variant | Denomination label | Notes | +|---|---|---| +| `cWUSDC` | `USD` | Current Etherscan profile asset. | +| `cWEURC` / `cWEURT` | `EUR` | Use the same structure with euro-denomination label. | +| `cWGBPC` / `cWGBPT` | `GBP` | Preserve outer C; avoid pound-sign clutter at 32x32. | +| `cWAUDC` / `cWAUDT` | `AUD` | Works directly with the existing label geometry. | +| `cWJPYC` / `cWJPYT` | `JPY` | Prefer ISO label over yen symbol for small-size legibility. | +| `cWCHFC` / `cWCHFT` | `CHF` | ISO label is clearer than symbol-based treatment. | +| `cWCADC` / `cWCADT` | `CAD` | Same structure; optional maple/red accents should be avoided for institutional neutrality. | + +## Network Scalability + +The brand should remain token-first and chain-neutral. Network identity belongs in the surrounding wallet/explorer UI, not inside the 32x32 token mark. + +| Network context | Fit | Recommendation | +|---|---|---| +| Ethereum Mainnet | High | Use the canonical cW* mark and chain metadata separately. | +| Base / Optimism / Arbitrum | High | Do not add L2 badges inside the token icon. | +| Polygon / Gnosis / Avalanche / BSC / Celo | High | Keep the token mark stable across networks for recognizability. | +| Chain 138 origin context | High | Use c* native marks for canonical assets and cW* marks for public wrapped transport forms. | + +## UX Risks + +| Risk | Mitigation | +|---|---| +| Visual proximity to existing stablecoin marks | Use the DBIS blue spectrum, interrupted outer C, custom W bridge stroke, and explicit `cW*` naming. | +| Bottom denomination compression at 32x32 | Use three-letter ISO labels, moderate tracking, and a small backing shape when needed. | +| Overloading the icon with chain/network badges | Keep network identity out of the icon; express it in token-list metadata. | +| Confusion with official third-party issuers | State clearly that cWUSDC is a DBIS compliant wrapped transport representation of Chain 138 cUSDC, not Circle-issued USDC. | +| Ticker variants becoming visually inconsistent | Preserve the outer C, inner c, W, and ISO-label layout across all cW* assets. | + +## Strategic Recommendations + +- Position cW* as regulated tokenized money infrastructure, not speculative crypto branding. +- Use a consistent cW* mark family across all public networks. +- Keep the canonical `c*` and wrapped `cW*` families visually related but distinct. +- Prefer ISO-4217 labels over currency symbols inside 32x32 icons. +- Avoid chain-specific badges in the token artwork; use token-list metadata for network context. +- Use the Etherscan 32x32 SVG as the small-size reference and the 512x512 PNG family for tracker/wallet contexts. + +## Current Canonical Assets + +| Asset | Path | +|---|---| +| cWUSDC 32x32 Etherscan SVG | `docs/04-configuration/etherscan/assets/cWUSDC-32x32.svg` | +| cUSDC / cWUSDC 512x512 PNG | `docs/04-configuration/coingecko/logos/cUSDC-512x512.png` | +| cUSDC source SVG | `smom-dbis-138/services/token-aggregation/public/token-logos/gru/cUSDC.svg` | +| Etherscan profile packet | `docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md` | + diff --git a/docs/04-configuration/etherscan/assets/cWUSDC-32x32.svg b/docs/04-configuration/etherscan/assets/cWUSDC-32x32.svg new file mode 100644 index 00000000..73dc4880 --- /dev/null +++ b/docs/04-configuration/etherscan/assets/cWUSDC-32x32.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + USD + 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 new file mode 100644 index 00000000..bf61ee26 --- /dev/null +++ b/docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md @@ -0,0 +1,392 @@ +# MetaMask Asset and Price Provider Submission Matrix + +Status: canonical repo-side path for making Chain 138 and GRU / GRU v2 `c*` and `cW*` metadata correct, then submitting the same facts to the external providers that MetaMask, wallets, explorers, and market-data surfaces commonly depend on. + +Source-summary packet: [METAMASK_EIP747_CONTRACT_METADATA_REFERENCE_PACKET.md](METAMASK_EIP747_CONTRACT_METADATA_REFERENCE_PACKET.md). + +API feed spider-web packet: [METAMASK_CWUSDC_API_FEED_SPIDER_WEB_RESEARCH.md](METAMASK_CWUSDC_API_FEED_SPIDER_WEB_RESEARCH.md). + +Provider positioning packet: [../GRU_PROVIDER_POSITIONING_PACKET.md](../GRU_PROVIDER_POSITIONING_PACKET.md). + +Current execution focus: make Etherscan show USD Value for Ethereum Mainnet `cWUSDC` at `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a`. Use [../etherscan/CWUSDC_ETHERSCAN_VALUE_EXECUTION_PLAN.md](../etherscan/CWUSDC_ETHERSCAN_VALUE_EXECUTION_PLAN.md) as the primary runbook. All other provider work is supporting evidence unless it directly helps Etherscan's trusted upstream price path. + +Latest execution status: [../../../reports/status/cwusdc-provider-next-steps-execution-status-latest.md](../../../reports/status/cwusdc-provider-next-steps-execution-status-latest.md). Current public probes show Etherscan page, CMC DEX page, and GeckoTerminal pool APIs visible; CoinGecko token-price API and DexScreener token APIs are still external blockers. + +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. + +Non-manual task list: [../CWUSDC_NON_MANUAL_PROVIDER_TASKS.md](../CWUSDC_NON_MANUAL_PROVIDER_TASKS.md). + +## GRU / Chain 138 Provider Positioning + +The GRU framework is not a cryptocurrency, stablecoin, or speculative digital asset system. GRU is the governing monetary-policy framework for a reserve-backed institutional settlement architecture. Chain 138 is the synchronized settlement, verification, and reconciliation layer through which reserve positions, transactional states, and collateral validations are recorded and continuously checked. + +Provider-facing language should describe the ecosystem as programmable settlement and verification infrastructure for reserve-backed monetary instruments, sovereign-grade financial assets, commodities, and institutional settlement workflows. It should not describe GRU or Chain 138 as a speculative token model, yield product, algorithmic stablecoin, or crypto-economic asset system. + +Chain 138 acts as the aggregated system-of-record for underlying holdings, reserve balances, and settlement positions. Reserve evidence is expected to remain independently verifiable through institutional sources such as UCC filings, Euroclear records, exchange reporting systems, regulated custodians, and qualified financial counterparties. API-driven validation, reconciliation, rebasing, and audit processes are expected to operate on rolling 6-second verification intervals and maintain the internal 20% operational buffer above required collateralization. + +| Layer | Provider-facing interpretation | Reserve / settlement rule | +|---|---|---| +| `M00` GRU | Physical and commodity reserve layer across XAU, Precious Metals, Battery Materials, Base Metals, and Building Materials. | Each 1 M00 GRU allocation carries five asset-class indices at `1.2` indexed units each before collateral adjustment. Physical assets apply the collateral adjustment factor `(0.9475^4) = ~0.80596628`, so each `1.2` indexed allocation corresponds to approximately `1.489` SKR-adjusted physical units. | +| `M0` GRU | Cash reserves, cash equivalents, sovereign and central bank-issued instruments, bonds, promissory notes, MTNs, and LTNs. | No LTV adjustment is applied inside the accounting framework because qualifying instruments are supported by underlying cash reserves maintained at a `5:1` reserve coverage ratio. Portfolio eligibility is limited to seasoned AAA-rated and top-tier institutional paper, including qualifying emerging-market sovereign and quasi-sovereign issuances that satisfy internal credit, liquidity, and reserve-grade standards. | +| `M1` GRU | Transactional settlement layer that dynamically bridges and rebalances the M00 and M0 reserve layers. | Settlement exposure may not exceed `25:1` relative to available M00 GRU reserves or `5:1` relative to available M0 GRU reserves. When utilization approaches thresholds, execution must be segmented into sequential tranches so settlement remains collateralized within available M00 and M0 reserve capacity. | + +### MetaMask Money rail and mUSD — internal GRU alignment (narrative) + +Use this subsection for **DBIS / provider-facing narrative consistency** only. It does **not** assert that MetaMask has officially bound product names (`Money`, `mUSD`, “cash”) to these contracts; when MetaMask publishes CAIP-19 IDs, token-list rows, or controller mappings, **reconcile this table to their identifiers** before any external claim. + +| External / wallet concept | GRU / DBIS interpretation | On-chain / transport anchor | +|----------------------------|---------------------------|-----------------------------| +| **MetaMask Money rail** | Wallet UX and balance/rate surfaces for a **cash-like** segment, sitting above ordinary “crypto” accounts. Aligns narratively with **GRU** as the **monetary-policy and settlement framing** (M0/M1 roles in the table above), not as a substitute for reserve evidence or Chain 138 system-of-record duties. | No single hub contract; depends on MetaMask Money account + balance service implementation. | +| **mUSD** (MetaMask product denomination, *internal mapping*) | **ISO-4217 USD–compliant** GRU family on the hub, and **wrapped transport** off the hub. Same **economic denomination** as compliant USD on Chain 138; off-hub mirrors are **cW\*** transports, not Circle-issued USDC. | **Chain 138 (hub):** primary settlement token **`cUSDC`** at `0xf22258f57794CC8E06237084b353Ab30fFfa640b` (6 decimals) — PMM and liquidity rail per canonical token config. **GRU v2 / staged x402 face:** **`cUSDC_V2`** at `0x219522c60e83dEe01FC5b0329d6fA8fD84b9D13d` on Chain 138 (`familySymbol: cUSDC`, `preferredForX402`, liquidity still on `cUSDC` until cutover — see `smom-dbis-138/services/token-aggregation/src/config/canonical-tokens.ts`). **Non–Chain 138 (wrapped):** **`cWUSDC`** on Ethereum Mainnet at `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` (6 decimals) — same submission and CAIP discipline as the rest of this matrix; do not mix Chain 138 `cUSDC` activity with Mainnet `cWUSDC` provider stats. | + +**Operational rule:** treat **mUSD ↔ cUSDC / cUSDC_V2** as **hub-native GRU USD**, and **mUSD ↔ cWUSDC** as **public-network wrapped GRU USD transport** (`cW*`), always with the disclaimers in [GRU_RISK_AND_DISCLOSURE_LANGUAGE.md](../GRU_RISK_AND_DISCLOSURE_LANGUAGE.md) and the non-crypto positioning in this document. + +**Cross-references (discoverability):** + +- [METAMASK_EIP747_CONTRACT_METADATA_REFERENCE_PACKET.md](METAMASK_EIP747_CONTRACT_METADATA_REFERENCE_PACKET.md) — EIP-747 and legacy contract-metadata constraints; links back here for Money/mUSD narrative. +- [../GRU_PROVIDER_POSITIONING_PACKET.md](../GRU_PROVIDER_POSITIONING_PACKET.md) — provider-facing GRU / Chain 138 / `cW*` framing. +- [../CWUSDC_PROVIDER_SUBMISSION_PACKET.md](../CWUSDC_PROVIDER_SUBMISSION_PACKET.md) — Mainnet `cWUSDC` identity and evidence table. +- Canonical tokens and Explorer alignment: [../../11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md](../../11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md) (hub `cUSDC` / supply), [../../11-references/CONTRACT_ADDRESSES_REFERENCE.md](../../11-references/CONTRACT_ADDRESSES_REFERENCE.md), [../../11-references/ADDRESS_MATRIX_AND_STATUS.md](../../11-references/ADDRESS_MATRIX_AND_STATUS.md). +- Hub vs v2 face token: `smom-dbis-138/services/token-aggregation/src/config/canonical-tokens.ts` (`cUSDC`, `cUSDC_V2`). +- DefiLlama DODO breakdown when **`dfio_meta_main` shows `0.00`:** [../../11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md](../../11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md). +- **Full replay and maintenance (master reference):** [../../00-meta/METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md](../../00-meta/METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md) — MetaMask Money/mUSD steps, DefiLlama fork/PR checklist, PR **#19198** lifecycle, JSON touchpoints. + +Provider submissions should keep the participant-language broad enough for retail users, commercial enterprises, banking professionals, sovereign institutions, and macroeconomic reviewers: GRU defines the monetary-policy framework; Chain 138 provides the auditable distributed-ledger environment where fiat currencies, commodities, securities, reserve positions, and settlement states can be synchronized without relying on speculative token mechanics. + +### Canonical Positioning Packets + +| Packet | Use | +|---|---| +| [../GRU_PROVIDER_POSITIONING_PACKET.md](../GRU_PROVIDER_POSITIONING_PACKET.md) | Short provider-facing narrative and language rules for GRU, Chain 138, c*, and cW*. | +| [../GRU_RESERVE_LAYER_EXPLAINER.md](../GRU_RESERVE_LAYER_EXPLAINER.md) | M00, M0, and M1 reserve-layer explanation with collateral adjustment and utilization constraints. | +| [../CHAIN138_SYSTEM_OF_RECORD_MODEL.md](../CHAIN138_SYSTEM_OF_RECORD_MODEL.md) | Boundary model for what Chain 138 records, verifies, and does not replace. | +| [../GRU_NOT_CRYPTO_NOT_STABLECOIN_FAQ.md](../GRU_NOT_CRYPTO_NOT_STABLECOIN_FAQ.md) | Defensive FAQ for providers, wallet users, and institutional reviewers. | +| [../GRU_RISK_AND_DISCLOSURE_LANGUAGE.md](../GRU_RISK_AND_DISCLOSURE_LANGUAGE.md) | Reusable non-affiliation, price, liquidity, reserve evidence, and utilization disclaimers. | +| [../RESERVE_VERIFICATION_EVIDENCE_INDEX.md](../RESERVE_VERIFICATION_EVIDENCE_INDEX.md) | Evidence map for reserve, supply, collateral, settlement, and provider-acceptance artifacts. | +| [../CWUSDC_PROVIDER_SUBMISSION_PACKET.md](../CWUSDC_PROVIDER_SUBMISSION_PACKET.md) | Consolidated cross-provider cWUSDC identity, evidence, caveats, and submission checklist. | + +## Bottom Line + +The repo can make wallet metadata, token-list metadata, logo hosting, report APIs, supply proof, and market-cap fields correct. The repo cannot force MetaMask to show fiat prices for custom assets. MetaMask price rendering depends on MetaMask/Consensys internals and upstream asset/price providers after they ingest the asset. + +Use this document as the external submission tracker. Treat each external provider status as one of: + +- `repo_ready`: our endpoint/package is ready. +- `submitted`: operator submitted through a form, issue, PR, or support ticket. +- `accepted`: provider has accepted and displays the asset. +- `blocked`: provider requires liquidity, volume, chain support, profile verification, or manual review that is not yet satisfied. + +## Live Repo-Controlled Sources + +| Surface | URL / file | Purpose | Current state | +|---|---|---|---| +| Chain 138 MetaMask payload | `https://explorer.d-bis.org/api/v1/config/metamask?chainId=138` | `wallet_addEthereumChain` + EIP-747 `wallet_watchAsset` payloads | `repo_ready`; 31 watch assets | +| Chain 138 token list | `https://explorer.d-bis.org/api/v1/report/token-list?chainId=138` | Uniswap token-list compatible Chain 138 list | `repo_ready` | +| All report | `https://explorer.d-bis.org/api/v1/report/all` | Internal plus external reporting packet across chains | `repo_ready` | +| CoinGecko report | `https://explorer.d-bis.org/api/v1/report/coingecko?chainId=1` | CoinGecko-shaped token, pool, supply, logo fields | `repo_ready` | +| CMC report | `https://explorer.d-bis.org/api/v1/report/cmc?chainId=1` | CMC-shaped token, pool, supply, logo fields | `repo_ready` | +| Chain 138 logo | `https://explorer.d-bis.org/api/v1/report/logo/chain-138` | First `iconUrls` entry for Chain 138 | `repo_ready` | +| Token logos | `https://explorer.d-bis.org/api/v1/report/logo/` | DBIS-hosted token art for EIP-747 and reports | `repo_ready` | +| Static Chain 138 token list | `token-lists/lists/dbis-138.tokenlist.json` | Repo-pinned token-list artifact | `repo_ready` | +| Etherscan profile packet | `docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md` | Etherscan cWUSDC profile source | `repo_ready` | +| cWUSDC tracker packet | `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md` | Tracker-facing cWUSDC facts | `repo_ready` | + +## External Repositories and Provider Paths + +| Priority | Provider / repo | What it controls | Submission method | Required package | Current status / caveat | +|---:|---|---|---|---|---| +| 1 | CoinGecko | Public token metadata, token image, supply, price/market data, downstream wallet/tracker discovery | Listing / update form; API and support review | Website, contract, logo, supply proof, public LP evidence, caveats | `repo_ready`; acceptance external | +| 2 | CoinMarketCap | Public token metadata, supply, market cap, DEX token/pool data | Listing / update form; CMC DEX APIs | Same as CoinGecko plus CMC report endpoint | `repo_ready`; acceptance external | +| 3 | Etherscan token profile | Ethereum token logo/profile/socials and often Etherscan display quality | Etherscan token profile update flow | `CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md`, logo, website, contact | `repo_ready`; approval external | +| 4 | GeckoTerminal | DEX pair metadata and on-chain pool discoverability | Pool/profile request or organic indexing | Real public pools, token metadata, logo/social links | Blocked unless public indexed pools remain visible and liquid | +| 5 | DexScreener | DEX pair page metadata/profile | DexScreener profile update/request | Pair URLs, token logo, socials, website | Blocked unless DEX pairs are indexed and accepted | +| 6 | MetaMask EIP-747 | User-approved custom-token display | Our dApp calls `wallet_watchAsset` | `config/metamask` endpoint and live logos | `repo_ready`; user must approve; local cache may require remove/re-add | +| 7 | MetaMask Snaps allowlist | Stable MetaMask npm Snap install eligibility | Snaps Directory submission / version update | npm package, manifest, security posture, published version | External gate; `npm:chain138-open-snap@0.2.3` is blocked until allowlisted | +| 8 | MetaMask contract metadata | Legacy token logo/name mapping in `MetaMask/contract-metadata` | GitHub PR if accepted | CAIP-19 metadata and logo | Low probability; repo states it is effectively frozen and recommends EIP-747 | +| 9 | MetaMask / assets controllers | Token detection/rates implementation | Not a normal token submission path | N/A | Information source only; do not expect PR acceptance for a single token listing | +| 10 | MetaMask multichain API client | Scoped wallet sessions and RPC invocation by CAIP-2 scope | dApp integration, not provider submission | `@metamask/multichain-api-client`, scopes like `eip155:1` / `eip155:138` | Useful for wallet session/RPC hygiene; does not provide token price metadata | +| 11 | Chainlist / ethereum-lists/chains | EVM chain metadata used by Chainlist-like surfaces | GitHub PR to `ethereum-lists/chains` | Chain JSON, RPCs, explorers, icon | Useful for Chain 138 discoverability; not token prices | +| 12 | Trust Wallet assets | Token logos for Trust Wallet and some ecosystem consumers | GitHub PR to `trustwallet/assets` | Per-chain token folder, `logo.png`, `info.json` where required | Useful for recognized chains/tokens; custom Chain 138 support may be limited | +| 13 | Token Lists ecosystem | Importable token lists for wallets/dApps | Host token list and submit/share URL | `token-lists/lists/dbis-138.tokenlist.json` and live endpoint | `repo_ready`; adoption per wallet/dApp | +| 14 | Uniswap token-list / app ecosystem | Token-list display in Uniswap-like UIs | Token-list import / provider ingestion | Public token-list URL and logo URLs | Useful metadata path; does not guarantee default-list inclusion | +| 15 | 0x / swap API providers | Token quotes/routes where supported | Provider support/listing request | Verified contracts, liquidity, token metadata | Needs supported chain/pools; not a pure metadata fix | +| 16 | DeFiLlama / DefiLlama Yields / adapters | Public protocol/TVL context | GitHub adapter PR or listing request | Protocol docs, contracts, pool data | **TVL path:** `dfio_meta_main` + DODO — **open upstream [DefiLlama/DefiLlama-Adapters#19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198)** (fork [Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters](https://github.com/Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters)); if UI shows **`dfio_meta_main` 0.00** while other chains sum, see [../../11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md](../../11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md); **scope:** [../defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md](../defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md), [config/defillama-chain138-touchpoints.json](../../../config/defillama-chain138-touchpoints.json); optional dimension/yield/peg servers per [docs.llama.fi metrics](https://docs.llama.fi/) | +| 17 | Moralis / Alchemy / QuickNode token APIs | Third-party token metadata and balances | Provider support ticket/API metadata ingestion | Token list, logo, contracts, chain support | Optional; provider-specific | +| 18 | DEXTools / Birdeye / other DEX terminals | Pair metadata and charting | Provider request or organic pool indexing | Pair URLs, liquidity, token profile | Optional; depends on indexed pools and chain support | + +## Current cWUSDC Submission Facts + +| Field | Value | +|---|---| +| Token | `cWUSDC` | +| Name | `Wrapped cUSDC` | +| Ethereum Mainnet address | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Decimals | `6` | +| Logo | `https://explorer.d-bis.org/api/v1/report/logo/cUSDC` | +| CoinGecko report | `https://explorer.d-bis.org/api/v1/report/coingecko?chainId=1` | +| CMC report | `https://explorer.d-bis.org/api/v1/report/cmc?chainId=1` | +| All report | `https://explorer.d-bis.org/api/v1/report/all` | +| Supply proof source | `repo-supply-proof-catalog` | +| Tracker caveat | Public tracker acceptance is external and not implied by the API response | + +Live check command: + +```bash +curl -fsS 'https://explorer.d-bis.org/api/v1/report/coingecko?chainId=1' \ + | jq '.tokens[] | select(.symbol=="cWUSDC") | {symbol,contract_address,logo_uri,total_supply,circulating_supply,market_cap:(.market_data.market_cap // null),supply_proof_provenance,tracker_caveats}' +``` + +## MetaMask-Specific Interpretation + +MetaMask has three separate layers that are easy to confuse: + +| Layer | What it affects | What we can do | +|---|---|---| +| Custom network metadata | Network name, RPC, explorer, native currency, possible chain icon | Supply `wallet_addEthereumChain` payload with Chain 138 logo in `iconUrls` | +| Watched asset metadata | Token address, symbol, decimals, image shown after user approval | Supply EIP-747 `wallet_watchAsset` payload with correct `image` | +| Fiat price rendering | Token USD value in MetaMask UI | Submit to external trackers/providers; cannot force from our endpoint | + +MetaMask token auto-detection is not a Chain 138 guarantee. MetaMask documents enhanced token detection for selected supported networks and says it uses aggregated community token lists rather than one proprietary accepted-token list. For Chain 138, EIP-747 remains the practical repo-controlled path. + +## MetaMask AssetsController Implications + +MetaMask's `AssetsController` confirms that balance, token metadata, and price are separate data paths. A token can have a visible balance while still missing logo metadata and USD price. + +| AssetsController layer | What it means for DBIS assets | Repo response | +|---|---|---| +| Balance data sources | MetaMask can detect a held token balance through backend, Accounts API, Snap, or RPC data sources. | Do not treat visible balance as proof that metadata or price providers accepted the asset. | +| Detection middleware | Assets can be marked as detected before complete metadata exists. | A fallback glyph such as `C` means the asset exists in wallet state but still needs metadata enrichment. | +| Token metadata data source | Metadata includes symbol, name, decimals, image, spam flag, and verification status. | Keep CAIP-19-addressable token facts and logo URLs stable, HTTPS, and submission-ready. | +| Price data source | USD price, market cap, volume, and 24h change arrive through a separate price path. | Submit to CoinGecko, CMC, Etherscan, DEX terminals, and provider channels; EIP-747 alone cannot force USD display. | +| CAIP-19 asset IDs | Assets are keyed like `eip155:1/erc20:
` and native ETH as `eip155:1/slip44:60`. | Use exact CAIP-19 IDs in provider packets and support requests. | +| Force refresh | MetaMask can refresh balance, metadata, and price together only from sources it trusts. | The explorer `Refresh Mainnet cWUSDC` button re-issues EIP-747 metadata; provider-side price still waits on external ingestion. | + +Relevant Mainnet cWUSDC CAIP-19: + +```text +eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a +``` + +Operational conclusion: the Mainnet screenshot with a visible `56.71M cWUSDC` balance but no logo/price is consistent with successful balance discovery and incomplete metadata/price enrichment. The fix path is not a contract change. It is provider ingestion plus local EIP-747 refresh for the image. + +### `AssetsController:getAssets` Reading + +MetaMask's `AssetsController:getAssets` returns a per-account, per-asset map keyed by CAIP-19 asset ID. Each returned asset can include: + +- `balance` +- `metadata` +- `price` +- `fiatValue` + +The important operator detail is that `fiatValue` is computed from the human-readable balance amount multiplied by `price.price`. Therefore, a visible token balance with a dash for USD value means the wallet has a balance entry but the price entry is missing or zero. + +For cWUSDC, the desired complete return shape is conceptually: + +```text +assets[accountId]["eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a"] + balance.amount = visible cWUSDC balance + metadata.image = DBIS cWUSDC/cUSDC logo URL + price.price = accepted USD price from MetaMask provider path + fiatValue = balance.amount * price.price +``` + +This confirms three separate acceptance checks: + +| Check | What proves it | Current meaning for screenshot | +|---|---|---| +| Balance | cWUSDC amount appears in wallet | Passing | +| Metadata | DBIS logo replaces fallback `C` glyph | Not yet passing in MetaMask global metadata; can be locally refreshed with EIP-747 | +| Price | USD value appears under the token | Not yet passing; needs provider/tracker ingestion | + +When testing a wallet refresh, the closest MetaMask-side behavior to request is a force refresh with `dataTypes: ['balance', 'metadata', 'price']`. From our dApp surface, we cannot call MetaMask's internal messenger action directly; we can only provide the correct public inputs through EIP-747, token-list/report APIs, and external provider submissions. + +### `AssetsController:priceChanged` Reading + +MetaMask's `AssetsController:priceChanged` event is emitted when asset prices update. Its payload is a CAIP-19 keyed price map: + +```text +prices: Record +``` + +For cWUSDC to display a native USD value in MetaMask, the price payload needs an entry equivalent to: + +```text +prices["eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a"] = { + price: 1, + priceChange24h: , + lastUpdated: , + marketCap: , + volume24h: +} +``` + +If MetaMask never emits or stores a `priceChanged` entry for that CAIP-19 asset ID, the UI can show the cWUSDC balance while continuing to show a dash for fiat value. This is why the repo report APIs include `marketCapUsd`, `totalSupply`, `circulatingSupply`, supply-proof provenance, and tracker caveats: those fields are the submission package needed for external providers to produce the price payload MetaMask consumes. + +Practical implication: a correct local logo refresh and a visible on-chain balance do not prove price readiness. Price readiness is proven only when MetaMask's provider path recognizes the CAIP-19 asset and returns a nonzero `price.price` with a current `lastUpdated` timestamp. + +### Identifier Types Reading + +MetaMask uses CAIP-19 asset identifiers and CAIP-2 chain identifiers. This means submissions, support tickets, and local validation must avoid symbol-only references. + +| Identifier | Meaning | cWUSDC value | +|---|---|---| +| CAIP-2 chain ID | Chain identity | `eip155:1` | +| CAIP-19 asset ID | Full chain plus asset identity | `eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Asset namespace | Token standard namespace | `erc20` | +| Asset reference | Contract address | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Native ETH comparison | Native asset ID format | `eip155:1/slip44:60` | + +Operational requirements: + +- Every MetaMask-facing cWUSDC provider request must include the CAIP-19 ID, not only `cWUSDC`. +- All price, metadata, and balance acceptance checks must be keyed to `eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a`. +- Token-list and EIP-747 payloads still use `chainId: 1` plus the ERC-20 contract address, but provider tickets should include both the token-list form and the CAIP-19 form. +- Chain 138 source `cUSDC` must stay separate from Mainnet `cWUSDC`; do not submit Chain 138 transfer totals as Ethereum Mainnet `cWUSDC` activity. + +### For Other Controllers Reading + +MetaMask's example for other controllers sums portfolio/account value by reading every asset's `fiatValue` and adding it to the total. This matters for large cWUSDC balances: + +```text +accountValue = sum(asset.fiatValue for each fungible asset) +asset.fiatValue = balance.amount * price.price +``` + +If cWUSDC has a visible balance but no accepted price entry, then cWUSDC contributes `0` or no reliable value to MetaMask's account-value calculation. That is why the Mainnet screenshot can show a large `cWUSDC` unit balance while the portfolio total ignores it. + +Acceptance target: + +| Portfolio field | Required cWUSDC condition | +|---|---| +| Token unit balance | `assetsBalance[accountId][cWUSDC_CAIP19].amount` exists | +| Token display row | `assetsInfo[cWUSDC_CAIP19]` has symbol/name/decimals/image | +| Token USD row | `assetsPrice[cWUSDC_CAIP19].price` exists and is current | +| Portfolio/account value | `fiatValue` is computed and included in account-value sums | + +Repo implication: tracker submissions must emphasize that cWUSDC is currently visible as a balance but needs provider-side `AssetPrice` acceptance before MetaMask account-value calculations can include it. + +### Middlewares Enrichment Reading + +MetaMask documents the enrichment order after primary balance fetch: + +```text +DetectionMiddleware -> TokenDataSource -> PriceDataSource +``` + +For cWUSDC, that maps to this acceptance ladder: + +| Enrichment step | cWUSDC pass condition | Screenshot symptom if missing | +|---|---|---| +| DetectionMiddleware | Wallet sees `eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` as a held asset | Token absent entirely | +| TokenDataSource | Metadata provider resolves symbol, name, decimals, image, spam status, and verification status | Fallback `C` glyph, weak/partial token row | +| PriceDataSource | Price provider resolves `price`, `lastUpdated`, optional `marketCap`, and optional `volume24h` | Dash for USD value and no portfolio contribution | + +Current Mainnet screenshot reading: + +```text +DetectionMiddleware: passing +TokenDataSource: partially failing or not globally accepted +PriceDataSource: failing or not accepted +``` + +Repo implication: the next external work is not another balance action. It is metadata and price-provider proof: Etherscan token profile, tracker submissions, DEX pair pages, CAIP-19 identifiers, logo URLs, supply proof, market cap fields, and caveats. + +## MetaMask Multichain API Client Implications + +`MetaMask/multichain-api-client` is a TypeScript client for scoped wallet sessions and RPC invocation. It uses CAIP-2 scopes such as `eip155:1`, not loose network names. It is useful for cleaner wallet session plumbing, but it is not an asset metadata or price-provider submission path. + +Example shape from the package: + +```text +createSession({ requiredScopes: ['eip155:1'] }) +invokeMethod({ scope: 'eip155:1', request: { method: 'eth_call', ... } }) +revokeSession() +``` + +Repo interpretation: + +| Capability | Useful for DBIS | Not useful for | +|---|---|---| +| Scoped sessions | Explicit Ethereum Mainnet and Chain 138 wallet scopes | Forcing MetaMask to accept cWUSDC metadata | +| RPC invocation | Typed wallet/RPC calls under `eip155:1` or `eip155:138` | Creating `AssetsController:priceChanged` entries | +| Transport timeout control | Avoiding premature timeouts when wallet confirmation is required | Fixing provider-side token price absence | +| Custom RPC typing | Adding project-specific typed RPC methods if needed | Replacing EIP-747 token watch flows | +| Error handling | Separating transport errors from wallet/API errors | Bypassing MetaMask Snap allowlists | + +Possible future wallet-page upgrade: + +```text +Use multichain-api-client for scoped wallet sessions: + eip155:1 -> Ethereum Mainnet cWUSDC refresh / proof checks + eip155:138 -> Chain 138 add-chain and c* watch-asset workflow + +Keep EIP-747 for wallet_watchAsset. +Keep provider submissions for logo/price enrichment. +``` + +Operational conclusion: this repo can use the multichain API client to make the wallet UX more explicit and typed across Mainnet and Chain 138. It does not change the cWUSDC listing blocker: MetaMask still needs metadata and price enrichment for `eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a`. + +## Mainnet cWUSDC Screenshot Interpretation + +If Ethereum Mainnet MetaMask shows `cWUSDC` with a fallback `C` glyph, no fiat value, and only the Ethereum network badge, the wallet has detected or imported the token balance but has not resolved the Mainnet token through MetaMask's global asset metadata and price providers. + +Repo-side status for Mainnet cWUSDC is ready: + +| Check | Expected value | +|---|---| +| Chain | Ethereum Mainnet, `chainId=1` | +| Contract | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Symbol | `cWUSDC` | +| Decimals | `6` | +| Token-list URL | `https://explorer.d-bis.org/api/v1/report/token-list?chainId=1` | +| Logo URL | `https://explorer.d-bis.org/api/v1/report/logo/cUSDC` | +| CoinGecko report | `https://explorer.d-bis.org/api/v1/report/coingecko?chainId=1` | +| CMC report | `https://explorer.d-bis.org/api/v1/report/cmc?chainId=1` | + +Operator interpretation: + +- `wallet_watchAsset` can refresh the custom-asset image when the user removes/re-adds or re-approves the token. +- The explorer wallet page includes a `Refresh Mainnet cWUSDC` action that switches to Ethereum Mainnet and re-issues the EIP-747 request with the DBIS image URL. +- The fallback glyph is still expected until Etherscan, CoinGecko, CMC, and/or MetaMask's internal providers ingest and accept the Mainnet asset metadata. +- The missing fiat value is not fixed by EIP-747; it requires external tracker/provider acceptance and enough public evidence for those providers. + +## Submission Order + +1. Keep repo endpoints correct and live. +2. Submit/update Etherscan cWUSDC token profile. +3. Submit cWUSDC to CoinGecko with supply proof, logo, website, and liquidity caveats. +4. Submit cWUSDC to CMC with the same facts and CMC report endpoint. +5. Submit/update GeckoTerminal and DexScreener pair/profile metadata for any visible public pools. +6. Submit Chain 138 metadata to `ethereum-lists/chains` / Chainlist path if not already accepted. +7. Submit token/logo metadata to wallet/community registries where they accept custom-chain assets. +8. Re-run EIP-747 wallet flow and document MetaMask before/after screenshots. +9. Track every external acceptance or rejection in a status report under `reports/status/` (e.g. `cwusdc-external-trackers-live-latest.md`, `*etherscan*`, operator JSON summaries). + +## Operator-generated status artifacts + +Search or add dated evidence under **`reports/status/`** after each submission batch; link PRs/tickets in commit messages. This matrix stays **canonical for intent**; file-based reports hold **evidence**. + +## Acceptance Evidence to Capture + +For each provider: + +- submission date and account/email used; +- ticket, PR, issue, or form confirmation ID; +- exact submitted URLs and token address; +- provider response; +- approval screenshot or public profile URL; +- any rejection reason; +- next required blocker such as liquidity, volume, contract verification, or chain support. + +## Useful References + +- MetaMask contract metadata: `https://github.com/MetaMask/contract-metadata` +- MetaMask extension: `https://github.com/MetaMask/metamask-extension` +- MetaMask core assets controllers: `https://github.com/MetaMask/core/tree/main/packages/assets-controllers` +- EIP-747 wallet watchAsset: `https://eips.ethereum.org/EIPS/eip-747` +- MetaMask token display help: `https://support.metamask.io/manage-crypto/tokens/how-to-display-tokens-in-metamask` +- Chain metadata registry: `https://github.com/ethereum-lists/chains` +- Trust Wallet assets: `https://github.com/trustwallet/assets` +- CoinGecko API docs: `https://docs.coingecko.com/` +- CoinMarketCap API docs: `https://coinmarketcap.com/api/documentation/` +- Token Lists standard: `https://tokenlists.org/` diff --git a/docs/04-configuration/metamask/METAMASK_CWUSDC_API_FEED_SPIDER_WEB_RESEARCH.md b/docs/04-configuration/metamask/METAMASK_CWUSDC_API_FEED_SPIDER_WEB_RESEARCH.md new file mode 100644 index 00000000..de77cb8a --- /dev/null +++ b/docs/04-configuration/metamask/METAMASK_CWUSDC_API_FEED_SPIDER_WEB_RESEARCH.md @@ -0,0 +1,346 @@ +# MetaMask cWUSDC API Feed Spider Web Research + +Status: research and implementation map for how Mainnet `cWUSDC` can move from "visible balance with fallback glyph" to fully enriched wallet, explorer, tracker, and portfolio value display. + +Canonical Mainnet asset identity: + +```text +CAIP-19: eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a +Chain: eip155:1 +Symbol: cWUSDC +Name: Wrapped cUSDC +Decimals: 6 +Logo: https://explorer.d-bis.org/api/v1/report/logo/cUSDC +``` + +## Core Finding + +MetaMask's asset stack separates balance, token metadata, price, and fiat value. The current Mainnet screenshot proves that balance detection is working. It does not prove that MetaMask's metadata source or price source has accepted the asset. + +```text +visible cWUSDC units -> balance path is working +fallback C glyph -> metadata/image path is incomplete +dash for USD value -> price path is incomplete +portfolio omission -> fiatValue cannot be computed +``` + +The provider spider web is therefore not one API. It is a chain of independent systems: + +```text +On-chain ERC-20 state + -> explorer/indexer token row + -> token metadata provider + -> DEX pair/indexer evidence + -> price provider + -> wallet asset controller + -> portfolio/account value controller +``` + +## Provider Classes + +| Provider class | Examples | Controls | Does not control | +|---|---|---|---| +| Wallet local prompt | EIP-747 `wallet_watchAsset` | User-approved local symbol, decimals, image | Global MetaMask token acceptance or price | +| Wallet asset controller | MetaMask `AssetsController` | How MetaMask joins balance, metadata, price, fiat value | External acceptance of cWUSDC | +| Explorer profile | Etherscan | Token logo/profile on Etherscan and downstream trust evidence | Wallet price by itself | +| Market tracker | CoinGecko, CoinMarketCap | Metadata, market cap, supply, price, volume, API discovery | Immediate wallet refresh guarantees | +| DEX terminal | GeckoTerminal, DexScreener, CMC DexScan, DEXTools, Birdeye | Pair visibility, liquidity/volume evidence, chart pages | Audited circulating supply | +| Data API provider | Alchemy, Moralis, thirdweb, QuickNode, Covalent, Zerion, SimpleHash | API-accessible balances, metadata, prices, search | Listing on MetaMask unless MetaMask consumes them | +| Oracle network | Chainlink Data Feeds/Data Streams/DataLink | Onchain or institutional price publication | Wallet UI enrichment unless wallet consumes it | +| Chain registry | Chainlist / ethereum-lists/chains | Chain metadata/RPC/explorer discoverability | Token price | +| Token-list registry | Token Lists, Trust Wallet assets, Uniswap token-list imports | Logo and token metadata for apps that ingest lists | MetaMask global price | + +## Current Repo-Controlled Inputs + +| Input | Status | URL / path | +|---|---|---| +| Mainnet cWUSDC token list | Ready | `https://explorer.d-bis.org/api/v1/report/token-list?chainId=1` | +| Chain 138 EIP-747 payload | Ready | `https://explorer.d-bis.org/api/v1/config/metamask?chainId=138` | +| cWUSDC logo | Ready | `https://explorer.d-bis.org/api/v1/report/logo/cUSDC` | +| CoinGecko report | Ready | `https://explorer.d-bis.org/api/v1/report/coingecko?chainId=1` | +| CMC report | Ready | `https://explorer.d-bis.org/api/v1/report/cmc?chainId=1` | +| All report | Ready | `https://explorer.d-bis.org/api/v1/report/all` | +| Supply proof | Ready | `reports/status/mainnet-cwusdc-supply-proof-20260508.json` | +| Etherscan profile packet | Ready | `docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md` | +| Wallet local refresh | Ready | `https://explorer.d-bis.org/wallet` -> `Refresh Mainnet cWUSDC` | + +Root `.env` key coverage observed on this workstation: + +| Key family | Present at root | Meaning | +|---|---:|---| +| `ETHERSCAN_API_KEY` | yes | Can query/verify Etherscan paths where scripts support it | +| `COINGECKO_API_KEY` | yes | Can query CoinGecko with configured rate limits | +| `COINMARKETCAP_API_KEY` / `CMC_*` | no | CMC API probes need key or manual submission | +| `ALCHEMY_*` | no | Alchemy price-by-address probes need key | +| `MORALIS_*` | no | Moralis token/price/search probes need key | +| `THIRDWEB_*` | no at root | thirdweb token/Insight probes need key/client in the relevant app env | +| `QUICKNODE_*` | no | QuickNode token API probes need account/API key | +| `COVALENT_*` | no | Covalent balances/prices need key | +| `ZERION_*` | no | Zerion portfolio/asset APIs need key | +| `SIMPLEHASH_*` | no | SimpleHash fungible/metadata probes need key | + +## Provider-by-Provider Map + +### MetaMask + +Source URLs: + +- `https://github.com/MetaMask/core/blob/main/packages/assets-controller/src/README.md` +- `https://support.metamask.io/develop/how-to-add-a-token-logo/` +- `https://github.com/MetaMask/multichain-api-client` + +What matters: + +- MetaMask encourages EIP-747 for making tokens visible to users. +- MetaMask price display comes from several upstream sources and is best influenced by getting listed on prominent DEXs/CEXs and data providers. +- `AssetsController:getAssets` returns balance, metadata, price, and fiat value. +- `AssetsController:priceChanged` is keyed by CAIP-19 asset ID. +- `multichain-api-client` helps dApps manage scoped wallet sessions/RPC calls; it does not create metadata or price acceptance. + +Action: + +1. Keep EIP-747 local refresh for Mainnet cWUSDC and Chain 138 c* assets. +2. Use CAIP-19 in every support ticket and provider submission. +3. Do not claim MetaMask price readiness until the wallet shows a nonzero price and computed fiat value. + +### Etherscan + +Source URL: + +- `https://info.etherscan.com/how-to-update-token-information-on-token-page/` + +What matters: + +- Etherscan token profile updates are account/form driven after token ownership verification. +- Etherscan profile/logo acceptance is strong downstream evidence for other providers. + +Action: + +1. Submit/update token info for `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a`. +2. Use `docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md`. +3. Capture ticket/form confirmation and final screenshot. + +### CoinGecko + +Source URLs: + +- `https://docs.coingecko.com/reference/endpoint-overview` +- `https://support.coingecko.com/hc/en-us/sections/32146983631641-Token-Coin-Listing` + +What matters: + +- CoinGecko exposes price, token price by contract, metadata, tickers, market cap, volume, circulating supply, and total supply endpoints after asset support. +- CoinGecko acceptance is one of the highest-leverage paths for wallet price propagation. + +Action: + +1. Submit Mainnet cWUSDC with the exact Mainnet contract, not only Chain 138 cUSDC. +2. Include supply proof, logo, website, Etherscan profile packet, DEX pair evidence, and caveats. +3. After submission, test `/simple/token_price/ethereum` and `/coins/{id}` once an ID exists. + +### CoinMarketCap + +Source URLs: + +- `https://support.coinmarketcap.com/hc/en-us/articles/360043533632-Cryptoasset-Listings` +- `https://coinmarketcap.com/api/documentation/` + +What matters: + +- CMC has separate flows for new assets, market pairs, supply updates, swaps, and info updates. +- CMC API exposes listings, quotes, metadata, DEX data, market pairs, and exchange proof-of-reserves categories. +- DexScan visibility is useful evidence but is not the same as a full tracked listing. + +Action: + +1. Submit cWUSDC as a cryptoasset listing/update using the CMC report endpoint. +2. Submit visible Mainnet DEX pairs as market-pair evidence. +3. Track CMC DexScan verification separately from full CMC listing. + +### GeckoTerminal and DexScreener + +What matters: + +- These providers are pair-driven. They generally need an indexed DEX pair with at least some liquidity and activity. +- They are useful as public trade/pair evidence for CoinGecko/CMC and wallets. + +Action: + +1. Keep public cWUSDC/USDC pair addresses, reserves, tx hashes, and screenshots current. +2. Do not overstate depth; report actual reserves and activity. +3. Submit profile/logo/pair metadata where each provider allows. + +### Alchemy + +Source URLs: + +- `https://www.alchemy.com/docs/data/prices-api/prices-api-endpoints/prices-api-endpoints/get-token-prices-by-address` +- `https://www.alchemy.com/docs/reference/get-token-prices-by-symbol` + +What matters: + +- Alchemy Prices API can query token prices by symbol or by network/address pairs. +- The by-address route is the correct cWUSDC probe because `cWUSDC` is not globally unique. +- Successful API response may still contain an empty/missing price for unknown assets. + +Action: + +1. Add an `ALCHEMY_API_KEY` secret if API probes are desired. +2. Probe: + +```text +POST /prices/v1/{apiKey}/tokens/by-address +network: eth-mainnet +address: 0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a +``` + +3. Record missing-price response as provider gap evidence. + +### Moralis + +Source URLs: + +- `https://docs.moralis.com/web3-data-api/evm/reference/token-api` +- `https://docs.moralis.com/web3-data-api/evm/token-search` +- `https://moralis.com/api/token/` + +What matters: + +- Moralis Token API covers balances, transfers, prices, holders, liquidity, swaps, metadata, and token search. +- It can return real-time prices and net worth when a token is covered. +- Token search can reveal whether cWUSDC is discoverable by address/name/symbol. + +Action: + +1. Add a `MORALIS_API_KEY` secret if provider probes are desired. +2. Probe token metadata, token price, token search, and pair/liquidity endpoints for Mainnet cWUSDC. +3. Record whether `logo`, `usdPrice`, `marketCap`, and `isVerifiedContract` appear. + +### thirdweb + +Source URLs: + +- `https://portal.thirdweb.com/bridge/tokens` +- `https://github.com/thirdweb-dev/js` + +What matters: + +- thirdweb Bridge token utility returns token metadata and `priceUsd` for supported tokens. +- thirdweb Insight can also be relevant for indexed chain/pair data where configured. +- thirdweb is an application/API consumer path, not a direct MetaMask provider path. + +Action: + +1. Confirm whether relevant `THIRDWEB_SECRET_KEY` or `THIRDWEB_CLIENT_ID` exists in the app/service env that will run probes. +2. Query `Bridge.tokens({ chainId: 1, tokenAddress: cWUSDC })`. +3. If missing, treat as API-provider gap evidence and include DEX/tracker submission links. + +### QuickNode + +Source URL: + +- `https://www.quicknode.com/token-api/` + +What matters: + +- QuickNode Token API can return ERC-20 metadata, balances, transfers, and token details. +- It is primarily an API consumer/provider path, not a public listing authority. + +Action: + +1. Add QuickNode token API key if this path is needed. +2. Probe metadata and price support for Mainnet cWUSDC if available in the account product tier. + +### Chainlink + +Source URLs: + +- `https://chain.link/data-feeds` +- `https://data.chain.link/` + +What matters: + +- Chainlink Data Feeds aggregate data from premium data providers and independent nodes into onchain reports. +- Chainlink is excellent for onchain protocol pricing after feed creation, but it is not a direct MetaMask token-logo or wallet-price listing route. +- A new cWUSDC/USD feed would require enough credible market data and engagement with Chainlink/provider channels. + +Action: + +1. Do not position Chainlink as the immediate MetaMask fix. +2. Use Chainlink as an institutional/onchain oracle target after CoinGecko/CMC/DEX evidence is accepted. +3. If pursuing, prepare a feed request package with DEX/CEX venues, liquidity, volume, supply proof, and data-provider acceptance. + +### Covalent, Zerion, SimpleHash, Ankr, OKLink, Blockscout-class Indexers + +What matters: + +- These are secondary enrichment and portfolio APIs. +- They can improve ecosystem discoverability if they index cWUSDC correctly. +- Most require API keys, support tickets, or organic indexing from onchain activity and known token lists. + +Action: + +1. Create provider probes after primary Etherscan/CoinGecko/CMC submissions. +2. Use the same CAIP-19 identity and report endpoints. +3. Track each provider's response separately; do not assume one provider propagates to another. + +## Probe Matrix + +| Probe | Requires key | Expected passing result | +|---|---:|---| +| Etherscan token page/profile | no for public page; account for update | cWUSDC logo/profile visible on Etherscan | +| CoinGecko contract lookup | maybe no/pro key depending rate | cWUSDC returns coin ID, image, price, market cap | +| CoinGecko `/simple/token_price/ethereum` | maybe | cWUSDC contract returns `usd` | +| CMC listing/quotes | yes for API; form for listing | cWUSDC has CMC ID, quotes, market pairs | +| Alchemy price by address | yes | `eth-mainnet` + cWUSDC returns USD price | +| Moralis token search | yes | cWUSDC appears by contract with metadata/logo | +| Moralis token price | yes | cWUSDC returns `usdPrice` and pair source | +| thirdweb Bridge.tokens | client/secret | cWUSDC returns `iconUri` and `priceUsd` | +| GeckoTerminal pair page | no | cWUSDC/USDC pair page exists with reserves/activity | +| DexScreener pair search | no | cWUSDC pair appears in API/UI | +| Chainlink Data Feed | external engagement | cWUSDC/USD feed exists on `data.chain.link` | + +## Priority Order + +1. Etherscan profile/logo update for Mainnet cWUSDC. +2. CoinGecko Mainnet cWUSDC listing/update with exact contract and supply proof. +3. CMC listing/update plus DexScan pair/profile evidence. +4. GeckoTerminal/DexScreener/DEXTools/Birdeye pair metadata updates. +5. Provider probes: Alchemy, Moralis, thirdweb, QuickNode, Covalent, Zerion, SimpleHash. +6. Chainlist / chain registry for Chain 138 metadata discoverability. +7. Chainlink feed request only after public market-data providers have enough accepted evidence. + +## Submission Package Checklist + +Every provider packet should include: + +- CAIP-19: `eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` +- Contract address and Etherscan URL +- Symbol/name/decimals +- Logo URL and static logo asset +- Website and token explainer +- Supply proof and circulating-supply caveats +- Market cap formula +- Public DEX pair addresses and tx hashes +- Liquidity/reserve caveats +- Chain 138 source cUSDC relationship, clearly separated from Mainnet cWUSDC activity +- Contact email/account used for submission +- Confirmation ID or ticket ID after submission + +## Non-Negotiable Interpretation + +Do not tell operators that MetaMask price can be forced by: + +- contract changes; +- adding a token list alone; +- EIP-747 alone; +- Chain 138 Snap alone; +- Chainlink alone; +- private/internal accounting rails; +- minted supply without public market-data acceptance. + +The only defensible statement is: + +```text +Repo metadata and proofs are ready. MetaMask-native logo and price require external metadata/price provider ingestion for the exact Mainnet cWUSDC CAIP-19 asset. +``` diff --git a/docs/04-configuration/metamask/METAMASK_EIP747_CONTRACT_METADATA_REFERENCE_PACKET.md b/docs/04-configuration/metamask/METAMASK_EIP747_CONTRACT_METADATA_REFERENCE_PACKET.md new file mode 100644 index 00000000..82e57465 --- /dev/null +++ b/docs/04-configuration/metamask/METAMASK_EIP747_CONTRACT_METADATA_REFERENCE_PACKET.md @@ -0,0 +1,193 @@ +# MetaMask EIP-747 and Contract Metadata Reference Packet + +Status: local summarized reference packet for Chain 138, GRU / GRU v2 `c*`, and public-network `cW*` wallet metadata. + +Sources ingested: + +- MetaMask contract metadata: `https://github.com/MetaMask/contract-metadata` +- EIP-747 wallet watchAsset: `https://eips.ethereum.org/EIPS/eip-747` +- MetaMask token display help: `https://support.metamask.io/manage-crypto/tokens/how-to-display-tokens-in-metamask` + +## Related repo documents + +- [METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md](METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md) — canonical provider submission matrix, live report URLs, and **MetaMask Money rail / mUSD ↔ GRU hub (`cUSDC` / `cUSDC_V2`) and `cWUSDC` transport** narrative (internal alignment only until MetaMask publishes official bindings). +- [../GRU_PROVIDER_POSITIONING_PACKET.md](../GRU_PROVIDER_POSITIONING_PACKET.md), [../CWUSDC_PROVIDER_SUBMISSION_PACKET.md](../CWUSDC_PROVIDER_SUBMISSION_PACKET.md) — GRU positioning and Mainnet `cWUSDC` evidence packet. + +## Executive Interpretation + +For this repo, MetaMask has two usable paths: + +1. EIP-747 `wallet_watchAsset` is the primary practical path for Chain 138 and DBIS assets. +2. `MetaMask/contract-metadata` is useful as a legacy reference and possible low-probability PR path, but MetaMask states that the repository is effectively frozen and recommends EIP-747 for new tokens. + +MetaMask fiat prices are a separate upstream-provider problem. Correct EIP-747 metadata can add a token and its image, but it does not force MetaMask to show USD values. + +## Actionable Rules + +| Rule | Repo implication | +|---|---| +| Use EIP-747 for new or custom tokens | Keep `https://explorer.d-bis.org/api/v1/config/metamask?chainId=138` as the canonical wallet payload. | +| The wallet decides what "listed" means | Treat a `wallet_watchAsset` success as "request recognized", not guaranteed display or user approval. | +| The wallet may reject unsupported asset types | Keep `type: "ERC20"` for MetaMask compatibility in the current implementation. | +| The asset address must be valid for the wallet's chain context | Continue emitting Chain 138 addresses only for `chainId=138` payloads. | +| MetaMask validates contract-facing symbols | Keep V2 wallet symbols aligned to on-chain `symbol()`, with catalog symbols preserved in metadata. | +| The image URL must load successfully | Use DBIS HTTPS image endpoints and avoid localhost/private URLs. | +| Wallets may sanitize schemes/ports for SSRF protection | Keep image URLs HTTPS on `explorer.d-bis.org`; do not emit raw LAN, IPFS-only, or non-HTTPS images in wallet payloads. | +| Similar names/symbols can trigger phishing warnings | Use clear DBIS descriptions and avoid presenting cWUSDC as official Circle USDC. | +| Token detection is not universal | Do not assume Chain 138 autodetection; use add-chain and watch-asset flows. | +| Custom-token cache may persist | Version wallet image URLs and instruct users to remove/re-add stale tokens. | +| Snap allowlisting is external | Do not present the Chain 138 Snap as the production wallet path until the exact npm package/version is accepted by MetaMask. | +| CSP eval warnings must be separated from Snap allowlist failures | The production wallet flow should avoid repo-authored eval; a Snap allowlist rejection is not caused by CSP. | + +## Source-Specific Notes + +### MetaMask Contract Metadata + +MetaMask contract metadata maps EIP-55 checksum addresses / CAIP-19 asset IDs to names and logos. It supports CLI-driven asset additions using a CAIP identifier, token name, symbol, decimals, and image. + +Important constraints: + +- Repository is described by MetaMask as effectively frozen. +- New-token developers are directed toward EIP-747. +- A PR, if attempted, needs: + - CAIP-19 asset ID. + - Small square high-resolution SVG or PNG. + - Checksum address. + - Official project website referencing the address. + - Clear project explanation. + - Activity evidence. + - Verified block-explorer source where possible. + - Ethereum asset reputation of `NEUTRAL` or `OK`. + - ERC-20 only; not ERC-721 or ERC-1155. + +Repo implication: do not block production on MetaMask contract-metadata acceptance. Prepare a PR package only after Etherscan profile and external tracker posture are strong. + +### EIP-747 + +EIP-747 defines a wallet-scoped `wallet_watchAsset` method that lets a site suggest a token for a wallet to track. + +Important semantics: + +- The method returns `true` when the request is recognized, not when the user definitely accepted. +- Errors can occur for unsupported asset type, allowlist/denylist blocking, or failed image/metadata loading. +- `address` is required. +- `chainId` must match a wallet-recognized chain. +- Wallets should warn on suspiciously similar names or symbols. +- Wallets should protect against SSRF by sanitizing URI schemes/ports. + +Repo implication: the current `config/metamask` endpoint must remain conservative, HTTPS-only, chain-specific, and contract-symbol accurate. + +### MetaMask Token Display Docs + +MetaMask says it automatically displays popular ERC-20 and SPL tokens, but it does not maintain an authoritative list of all valid tokens. Users can add tokens through enhanced token detection, token search/address, block explorers, coin listing sites, and Portfolio. + +Important constraints: + +- Enhanced token detection is available only on listed networks such as Ethereum Mainnet, Linea, Avalanche, BNB Smart Chain, Polygon, Arbitrum, Optimism, Base, zkSync, and Solana. +- Chain 138 is not listed as an enhanced-token-detection network. +- MetaMask says enhanced detection uses aggregated community token lists. +- If a token is not discoverable by search/detection, users can add it by address. +- Block explorers and coin listing sites can expose add-to-MetaMask flows. + +Repo implication: Chain 138 must rely on `wallet_addEthereumChain`, EIP-747, token-list endpoints, explorer UI, and external provider submissions. + +## Required Repo Guarantees + +| Area | Required condition | Current repo path | +|---|---|---| +| Chain metadata | Chain 138 has valid EIP-3085 fields and logo URLs | `src/config/networks.ts`; `/api/v1/config/metamask?chainId=138` | +| Token metadata | Every watch asset has address, symbol, decimals, image | `src/api/routes/config.ts` | +| Symbol correctness | `options.symbol` matches contract symbol for MetaMask validation | `resolveWalletWatchAssetSymbol()` | +| Logo hosting | Logos are HTTPS and image endpoints return image content types | `/api/v1/report/logo/` | +| Cache refresh | Wallet image URLs can be versioned | `WALLET_METADATA_IMAGE_VERSION` / default version | +| User caveat | API states MetaMask prices are upstream controlled | `caveats` in `/api/v1/config/metamask` | +| External tracker packet | CoinGecko/CMC/all reports include logo and supply fields | `/api/v1/report/coingecko`, `/api/v1/report/cmc`, `/api/v1/report/all` | + +## Validation Commands + +```bash +curl -fsS 'https://explorer.d-bis.org/api/v1/config/metamask?chainId=138' \ + | jq '{chain:.addEthereumChain.chainName, iconUrls:.addEthereumChain.iconUrls, watchAssets:(.watchAssets|length), sample:.watchAssets[0]}' +``` + +```bash +curl -fsSI 'https://explorer.d-bis.org/api/v1/report/logo/chain-138?v=20260510' +curl -fsSI 'https://explorer.d-bis.org/api/v1/report/logo/cUSDC?v=20260510' +curl -fsSI 'https://explorer.d-bis.org/api/v1/report/logo/ETH?v=20260510' +``` + +```bash +curl -fsS 'https://explorer.d-bis.org/api/v1/report/coingecko?chainId=1' \ + | jq '.tokens[] | select(.symbol=="cWUSDC") | {symbol,contract_address,logo_uri,total_supply,circulating_supply,market_cap:(.market_data.market_cap // null)}' +``` + +## Submission Constraints + +| Target | Constraint | Recommendation | +|---|---|---| +| EIP-747 | User must approve; result does not prove token accepted permanently | Keep explorer wallet flow simple and auditable. | +| MetaMask custom network | Network icon may cache or be ignored by some MetaMask builds | Keep Chain 138 SVG first and document remove/re-add path. | +| MetaMask token logo | Existing custom tokens may cache old image URLs | Version `image` values and tell users to remove/re-add stale assets. | +| MetaMask fiat price | Controlled by MetaMask/upstream providers | Submit to CoinGecko, CMC, Etherscan, DEX indexers; do not claim direct control. | +| MetaMask Snap install | Stable MetaMask installs only allowlisted npm Snap package versions | Keep the Snap optional; use MetaMask Flask for testing or complete allowlist/update review. | +| CSP eval warning | Browser security panels may report blocked eval for third-party or stale bundles | Verify live `Content-Security-Policy` response headers and scan first-party bundles before treating it as a site break. | +| Contract metadata PR | Repo is effectively frozen and no timeline guaranteed | Treat as optional after stronger external acceptance. | +| Token autodetection | Chain 138 is not a listed enhanced detection network | Use explicit EIP-747 and token list import. | + +## CSP and Snap Failure Triage + +The screenshot failure mode `Cannot install version "0.2.3" of snap "npm:chain138-open-snap": The snap is not on the allowlist` is a MetaMask Snaps install gate. It is not resolved by changing explorer CSP. + +Use these checks to separate the two concerns: + +```bash +curl -fsSI 'https://explorer.d-bis.org/wallet' \ + | awk 'BEGIN{IGNORECASE=1} NR==1 || /^content-security-policy:/ {print}' +``` + +```bash +rg -n "eval\\(|new Function|setTimeout\\(['\\\"]|setInterval\\(['\\\"]" \ + explorer-monorepo/frontend/src explorer-monorepo/frontend/pages explorer-monorepo/frontend/libs +``` + +Current repo policy: + +- production wallet setup is `wallet_addEthereumChain` plus EIP-747 `wallet_watchAsset`; +- the Chain 138 Snap is optional and should be labelled Flask-or-allowlisted only; +- do not add `unsafe-eval` just to fix a Snap allowlist failure; +- if a real CSP break appears, first identify whether the active CSP comes from the Next frontend, nginx/NPMplus, or Cloudflare. + +## Exact cWUSDC Package + +| Field | Value | +|---|---| +| CAIP-19 | `eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Name | `Wrapped cUSDC` | +| Symbol | `cWUSDC` | +| Decimals | `6` | +| Logo | `https://explorer.d-bis.org/api/v1/report/logo/cUSDC` | +| Website | `https://d-bis.org/` | +| Token explainer | `https://d-bis.org/gru/tokens` | +| Etherscan profile packet | `docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md` | +| Tracker packet | `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md` | + +## Implementation Checklist + +- [x] Keep Chain 138 wallet payload live. +- [x] Emit DBIS-hosted HTTPS logo URLs. +- [x] Match V2 wallet symbols to on-chain symbols. +- [x] Version wallet image URLs for cache refresh. +- [x] Add caveat that MetaMask fiat prices are upstream controlled. +- [x] Add CoinGecko/CMC logo fields. +- [x] Add supply proof and market-cap fields for tracker packets. +- [ ] Submit/update Etherscan token profile. +- [ ] Submit/update CoinGecko token profile. +- [ ] Submit/update CMC token profile. +- [ ] Submit/update DEX pair metadata where public indexed pools exist. +- [ ] Optionally prepare MetaMask contract-metadata PR after Etherscan/tracker posture improves. + +## References + +- MetaMask contract metadata: https://github.com/MetaMask/contract-metadata +- EIP-747: https://eips.ethereum.org/EIPS/eip-747 +- MetaMask token display docs: https://support.metamask.io/manage-crypto/tokens/how-to-display-tokens-in-metamask diff --git a/docs/04-configuration/verification-evidence/rpc-502-diagnostics-20260510-130753.txt b/docs/04-configuration/verification-evidence/rpc-502-diagnostics-20260510-130753.txt new file mode 100644 index 00000000..84e00f5c --- /dev/null +++ b/docs/04-configuration/verification-evidence/rpc-502-diagnostics-20260510-130753.txt @@ -0,0 +1,437 @@ +============================================== +RPC 502 diagnostics — 2026-05-10T13:07:53-07:00 +============================================== + +--- VMID 2101 @ 192.168.11.11 (status: running) --- + Listening ports (ss -tlnp): + State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess + LISTEN 0 511 0.0.0.0:8443 0.0.0.0:* users:(("nginx",pid=168,fd=16),("nginx",pid=167,fd=16),("nginx",pid=166,fd=16),("nginx",pid=165,fd=16),("nginx",pid=163,fd=16)) + LISTEN 0 511 0.0.0.0:8544 0.0.0.0:* users:(("node",pid=114,fd=18)) + LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=168,fd=12),("nginx",pid=167,fd=12),("nginx",pid=166,fd=12),("nginx",pid=165,fd=12),("nginx",pid=163,fd=12)) + LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=168,fd=14),("nginx",pid=167,fd=14),("nginx",pid=166,fd=14),("nginx",pid=165,fd=14),("nginx",pid=163,fd=14)) + LISTEN 0 100 127.0.0.1:25 0.0.0.0:* users:(("master",pid=349,fd=13)) + LISTEN 0 511 127.0.0.1:8080 0.0.0.0:* users:(("nginx",pid=168,fd=18),("nginx",pid=167,fd=18),("nginx",pid=166,fd=18),("nginx",pid=165,fd=18),("nginx",pid=163,fd=18)) + LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=104,fd=14)) + LISTEN 0 5 0.0.0.0:18545 0.0.0.0:* users:(("python3",pid=2498,fd=3)) + LISTEN 0 511 [::]:8443 [::]:* users:(("nginx",pid=168,fd=17),("nginx",pid=167,fd=17),("nginx",pid=166,fd=17),("nginx",pid=165,fd=17),("nginx",pid=163,fd=17)) + LISTEN 0 4096 *:8545 *:* users:(("java",pid=968674,fd=402)) + LISTEN 0 4096 *:8546 *:* users:(("java",pid=968674,fd=403)) + LISTEN 0 3 *:9545 *:* users:(("java",pid=968674,fd=399)) + LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=168,fd=13),("nginx",pid=167,fd=13),("nginx",pid=166,fd=13),("nginx",pid=165,fd=13),("nginx",pid=163,fd=13)) + LISTEN 0 4096 *:22 *:* users:(("systemd",pid=1,fd=44)) + LISTEN 0 511 [::]:443 [::]:* users:(("nginx",pid=168,fd=15),("nginx",pid=167,fd=15),("nginx",pid=166,fd=15),("nginx",pid=165,fd=15),("nginx",pid=163,fd=15)) + LISTEN 0 4096 *:30303 *:* users:(("java",pid=968674,fd=404)) + LISTEN 0 100 [::1]:25 [::]:* users:(("master",pid=349,fd=14)) + Besu service (systemctl list-units): + besu-rpc.service loaded active running Hyperledger Besu RPC Node + journalctl -u besu-rpc -n 25: + May 10 13:07:17 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:17.042-0700 | EthScheduler-Workers-3 | INFO | PersistBlockTask | Imported empty block #5,024,276 / 0 tx / 0 om / 0 (0.0%) gas / (0x2094aa9b9ae76cbc0a606f406a018e1e8cb0ca11360502eb3f0c3c5761c863c7) in 0.019s. Peers: 36 + May 10 13:07:19 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:19.032-0700 | EthScheduler-Workers-2 | INFO | PersistBlockTask | Imported empty block #5,024,277 / 0 tx / 0 om / 0 (0.0%) gas / (0xbeb244e324cb7d9e5609ac86d479aa53945cbe1e149f07dba4b0e688976571e9) in 0.008s. Peers: 36 + May 10 13:07:21 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:21.054-0700 | EthScheduler-Workers-7 | INFO | PersistBlockTask | Imported empty block #5,024,278 / 0 tx / 0 om / 0 (0.0%) gas / (0xfa04fb4276d4b42b2820c248a162dd2fbd929d444326d85752546d260f2db594) in 0.013s. Peers: 36 + May 10 13:07:23 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:23.059-0700 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported empty block #5,024,279 / 0 tx / 0 om / 0 (0.0%) gas / (0x83331c7d6c1cc405dfa3ce44f811fa670e0720af9852eea60818d302100bcee4) in 0.021s. Peers: 36 + May 10 13:07:25 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:25.034-0700 | EthScheduler-Workers-4 | INFO | PersistBlockTask | Imported empty block #5,024,280 / 0 tx / 0 om / 0 (0.0%) gas / (0x29088a9862365e23503f2a699ffa276601cb900f9c4226a320bb5a123858136f) in 0.005s. Peers: 36 + May 10 13:07:27 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:27.036-0700 | EthScheduler-Workers-1 | INFO | PersistBlockTask | Imported empty block #5,024,281 / 0 tx / 0 om / 0 (0.0%) gas / (0x6e46414deb4d4fd37e20450932014f5f98da653b66086a5e1b96683dc0d64768) in 0.003s. Peers: 36 + May 10 13:07:29 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:29.044-0700 | EthScheduler-Workers-6 | INFO | PersistBlockTask | Imported empty block #5,024,282 / 0 tx / 0 om / 0 (0.0%) gas / (0x645dc2185ced198ad01d48bac309875cb4584a6076a25170d9c0e2a5db5c2c00) in 0.009s. Peers: 36 + May 10 13:07:31 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:31.042-0700 | EthScheduler-Workers-5 | INFO | PersistBlockTask | Imported empty block #5,024,283 / 0 tx / 0 om / 0 (0.0%) gas / (0xfd49171d44666e7ce3dfdb54e4976bcdc8a67aec3a64efb00cb5bc9491b8d5f6) in 0.019s. Peers: 36 + May 10 13:07:33 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:33.036-0700 | EthScheduler-Workers-3 | INFO | PersistBlockTask | Imported empty block #5,024,284 / 0 tx / 0 om / 0 (0.0%) gas / (0x98fdc422789289b5092568ffd4ea353763c25558255220a198e3ece601588890) in 0.016s. Peers: 36 + May 10 13:07:35 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:35.037-0700 | EthScheduler-Workers-2 | INFO | PersistBlockTask | Imported empty block #5,024,285 / 0 tx / 0 om / 0 (0.0%) gas / (0x4d0563c29eb1604d5fc11f7cc7a6c7f8ead7708a3f6fc986e86ec89ecadf7918) in 0.006s. Peers: 36 + May 10 13:07:37 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:37.052-0700 | EthScheduler-Workers-7 | INFO | PersistBlockTask | Imported empty block #5,024,286 / 0 tx / 0 om / 0 (0.0%) gas / (0x34fca6abe3994518643f6cdcb779715972ce818e85125b7b429ab8b53bedd824) in 0.004s. Peers: 36 + May 10 13:07:39 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:39.033-0700 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported empty block #5,024,287 / 0 tx / 0 om / 0 (0.0%) gas / (0x83a8d4b5ec4305d6395f2991b0b93a47fcf496968b0c2b5ad9bb05cd810fe019) in 0.011s. Peers: 36 + May 10 13:07:41 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:41.027-0700 | EthScheduler-Workers-4 | INFO | PersistBlockTask | Imported empty block #5,024,288 / 0 tx / 0 om / 0 (0.0%) gas / (0x1213cbba02637a5f78680949bbd9630ec7ac937a24678047be7522aed4a1be8a) in 0.007s. Peers: 36 + May 10 13:07:43 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:43.030-0700 | EthScheduler-Workers-1 | INFO | PersistBlockTask | Imported empty block #5,024,289 / 0 tx / 0 om / 0 (0.0%) gas / (0xf3bf0b503fc62847f06bcada375fc872386ff305464f0eeeb26aa5c940324b5c) in 0.007s. Peers: 36 + May 10 13:07:45 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:45.035-0700 | EthScheduler-Workers-6 | INFO | PersistBlockTask | Imported empty block #5,024,290 / 0 tx / 0 om / 0 (0.0%) gas / (0x3195f831c6c1ffb592c92fe91e954c6c91510593adb22528c1b798e79d6b76bc) in 0.009s. Peers: 36 + May 10 13:07:47 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:47.032-0700 | EthScheduler-Workers-5 | INFO | PersistBlockTask | Imported empty block #5,024,291 / 0 tx / 0 om / 0 (0.0%) gas / (0x4ac5495e7ec8f93d2a3f325b5363be8c841a4b5e69b34e9518a573cdcc780bcd) in 0.007s. Peers: 36 + May 10 13:07:49 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:49.044-0700 | EthScheduler-Workers-3 | INFO | PersistBlockTask | Imported empty block #5,024,292 / 0 tx / 0 om / 0 (0.0%) gas / (0x8fdde14826ff657d086dc80c426310b2ca9257372b32c407c56bd8fbc07044a5) in 0.008s. Peers: 36 + May 10 13:07:51 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:51.035-0700 | EthScheduler-Workers-2 | INFO | PersistBlockTask | Imported empty block #5,024,293 / 0 tx / 0 om / 0 (0.0%) gas / (0x7fd14f183435fd4f59d0ab43270f301be6ddbd1fbba0b87f4f7bf154111d8ef8) in 0.004s. Peers: 36 + May 10 13:07:53 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:53.034-0700 | EthScheduler-Workers-6 | INFO | PersistBlockTask | Imported empty block #5,024,294 / 0 tx / 0 om / 0 (0.0%) gas / (0xa8823b3e59fce3ad85549cd5f7a9ec401ea95273413519838bb7bd8cb3c1d958) in 0.013s. Peers: 36 + May 10 13:07:55 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:55.101-0700 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported empty block #5,024,295 / 0 tx / 0 om / 0 (0.0%) gas / (0x43a6c37d20fd151058ffe73f45f4de8e40a841920f1410122e4b4a80acf5c5a3) in 0.007s. Peers: 36 + May 10 13:07:57 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:57.027-0700 | EthScheduler-Workers-4 | INFO | PersistBlockTask | Imported empty block #5,024,296 / 0 tx / 0 om / 0 (0.0%) gas / (0x44cec125480959167c90854b461cc295dbf82d179a6416d054d3580a24d11c30) in 0.005s. Peers: 36 + May 10 13:07:59 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:07:59.040-0700 | EthScheduler-Workers-1 | INFO | PersistBlockTask | Imported empty block #5,024,297 / 0 tx / 0 om / 0 (0.0%) gas / (0xaf105e58bc058e39f6035940c7d5a7b431a1a5f2f10a900086d48f247c3b9f09) in 0.009s. Peers: 36 + May 10 13:08:01 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:08:01.036-0700 | EthScheduler-Workers-7 | INFO | PersistBlockTask | Imported empty block #5,024,298 / 0 tx / 0 om / 0 (0.0%) gas / (0xedd7307ce16d10c89a58e8f43e30cfd1d3f68c3fa3e6bfc1fe1206bddb914b4a) in 0.010s. Peers: 36 + May 10 13:08:03 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:08:03.043-0700 | EthScheduler-Workers-5 | INFO | PersistBlockTask | Imported empty block #5,024,299 / 0 tx / 0 om / 0 (0.0%) gas / (0xad665942dd0bc93fa7d674a6c03b4254409b953e35cd728d23835790290e8361) in 0.004s. Peers: 36 + May 10 13:08:05 besu-rpc-core-1 besu-rpc[968674]: 2026-05-10 13:08:05.057-0700 | EthScheduler-Workers-3 | INFO | PersistBlockTask | Imported empty block #5,024,300 / 0 tx / 0 om / 0 (0.0%) gas / (0xcae412ef6e563ed5653f7dbe40559fe4a1f0a0e48566e11d84f9a6d472814676) in 0.031s. Peers: 36 + journalctl -u besu -n 25: + -- No entries -- + +--- VMID 2500 @ 192.168.11.11 (status: running) --- + Listening ports (ss -tlnp): + State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess + LISTEN 0 100 127.0.0.1:25 0.0.0.0:* users:(("master",pid=325,fd=13)) + LISTEN 0 5 0.0.0.0:18545 0.0.0.0:* users:(("python3",pid=981,fd=3)) + LISTEN 0 4096 *:22 *:* users:(("sshd",pid=161,fd=3),("systemd",pid=1,fd=66)) + LISTEN 0 4096 *:8545 *:* users:(("java",pid=518,fd=359)) + LISTEN 0 4096 *:8546 *:* users:(("java",pid=518,fd=361)) + LISTEN 0 4096 *:8547 *:* users:(("java",pid=518,fd=360)) + LISTEN 0 4096 *:9545 *:* users:(("java",pid=518,fd=358)) + LISTEN 0 4096 *:30303 *:* users:(("java",pid=518,fd=362)) + LISTEN 0 100 [::1]:25 [::]:* users:(("master",pid=325,fd=14)) + Besu service (systemctl list-units): + besu.service loaded active running Hyperledger Besu + journalctl -u besu-rpc -n 25: + -- No entries -- + journalctl -u besu -n 25: + May 10 20:07:31 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:31.020+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,283 / 0 tx / 0 om / 0 (0.0%) gas / (0xfd49171d44666e7ce3dfdb54e4976bcdc8a67aec3a64efb00cb5bc9491b8d5f6) in 0.002s. Peers: 24 + May 10 20:07:33 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:33.022+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,284 / 0 tx / 0 om / 0 (0.0%) gas / (0x98fdc422789289b5092568ffd4ea353763c25558255220a198e3ece601588890) in 0.002s. Peers: 24 + May 10 20:07:35 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:35.019+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,285 / 0 tx / 0 om / 0 (0.0%) gas / (0x4d0563c29eb1604d5fc11f7cc7a6c7f8ead7708a3f6fc986e86ec89ecadf7918) in 0.003s. Peers: 24 + May 10 20:07:37 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:37.032+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,286 / 0 tx / 0 om / 0 (0.0%) gas / (0x34fca6abe3994518643f6cdcb779715972ce818e85125b7b429ab8b53bedd824) in 0.002s. Peers: 24 + May 10 20:07:39 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:39.020+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,287 / 0 tx / 0 om / 0 (0.0%) gas / (0x83a8d4b5ec4305d6395f2991b0b93a47fcf496968b0c2b5ad9bb05cd810fe019) in 0.002s. Peers: 24 + May 10 20:07:41 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:41.022+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,288 / 0 tx / 0 om / 0 (0.0%) gas / (0x1213cbba02637a5f78680949bbd9630ec7ac937a24678047be7522aed4a1be8a) in 0.002s. Peers: 24 + May 10 20:07:43 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:43.023+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,289 / 0 tx / 0 om / 0 (0.0%) gas / (0xf3bf0b503fc62847f06bcada375fc872386ff305464f0eeeb26aa5c940324b5c) in 0.002s. Peers: 24 + May 10 20:07:45 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:45.028+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,290 / 0 tx / 0 om / 0 (0.0%) gas / (0x3195f831c6c1ffb592c92fe91e954c6c91510593adb22528c1b798e79d6b76bc) in 0.001s. Peers: 24 + May 10 20:07:47 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:47.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,291 / 0 tx / 0 om / 0 (0.0%) gas / (0x4ac5495e7ec8f93d2a3f325b5363be8c841a4b5e69b34e9518a573cdcc780bcd) in 0.002s. Peers: 24 + May 10 20:07:49 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:49.027+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,292 / 0 tx / 0 om / 0 (0.0%) gas / (0x8fdde14826ff657d086dc80c426310b2ca9257372b32c407c56bd8fbc07044a5) in 0.001s. Peers: 24 + May 10 20:07:51 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:51.031+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,293 / 0 tx / 0 om / 0 (0.0%) gas / (0x7fd14f183435fd4f59d0ab43270f301be6ddbd1fbba0b87f4f7bf154111d8ef8) in 0.001s. Peers: 24 + May 10 20:07:53 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:53.022+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,294 / 0 tx / 0 om / 0 (0.0%) gas / (0xa8823b3e59fce3ad85549cd5f7a9ec401ea95273413519838bb7bd8cb3c1d958) in 0.002s. Peers: 24 + May 10 20:07:55 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:55.026+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,295 / 0 tx / 0 om / 0 (0.0%) gas / (0x43a6c37d20fd151058ffe73f45f4de8e40a841920f1410122e4b4a80acf5c5a3) in 0.003s. Peers: 24 + May 10 20:07:57 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:57.026+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,296 / 0 tx / 0 om / 0 (0.0%) gas / (0x44cec125480959167c90854b461cc295dbf82d179a6416d054d3580a24d11c30) in 0.002s. Peers: 24 + May 10 20:07:59 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:07:59.029+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,297 / 0 tx / 0 om / 0 (0.0%) gas / (0xaf105e58bc058e39f6035940c7d5a7b431a1a5f2f10a900086d48f247c3b9f09) in 0.002s. Peers: 24 + May 10 20:08:01 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:08:01.026+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,298 / 0 tx / 0 om / 0 (0.0%) gas / (0xedd7307ce16d10c89a58e8f43e30cfd1d3f68c3fa3e6bfc1fe1206bddb914b4a) in 0.002s. Peers: 24 + May 10 20:08:03 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:08:03.023+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,299 / 0 tx / 0 om / 0 (0.0%) gas / (0xad665942dd0bc93fa7d674a6c03b4254409b953e35cd728d23835790290e8361) in 0.002s. Peers: 24 + May 10 20:08:05 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:08:05.024+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,300 / 0 tx / 0 om / 0 (0.0%) gas / (0xcae412ef6e563ed5653f7dbe40559fe4a1f0a0e48566e11d84f9a6d472814676) in 0.001s. Peers: 24 + May 10 20:08:07 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:08:07.018+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,301 / 0 tx / 0 om / 0 (0.0%) gas / (0x3c66fc3e5b9410c83a25e0acea6844fd1eee1c28f3a1fc348322bd0e32f1762e) in 0.002s. Peers: 24 + May 10 20:08:09 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:08:09.023+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,302 / 0 tx / 0 om / 0 (0.0%) gas / (0xa878205b88dd420820cb7becfe730fbbeb8f33570e0023f459c2df6413278717) in 0.001s. Peers: 24 + May 10 20:08:11 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:08:11.020+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,303 / 0 tx / 0 om / 0 (0.0%) gas / (0x64b03849152b3815fb6aae30e0c5ed048d2aacce05aff2982257c8c16489c54c) in 0.001s. Peers: 24 + May 10 20:08:13 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:08:13.018+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,304 / 0 tx / 0 om / 0 (0.0%) gas / (0xc68f2c0daf0f9c455f84025fe98ae66ba8bd2960c5b91a739f5cc7417e440e5e) in 0.001s. Peers: 24 + May 10 20:08:15 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:08:15.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,305 / 0 tx / 0 om / 0 (0.0%) gas / (0x5ad2ce582093542f5a323b841a1dac5edc1fc3d81436a6f4d891334018b4ba16) in 0.001s. Peers: 24 + May 10 20:08:17 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:08:17.028+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,306 / 0 tx / 0 om / 0 (0.0%) gas / (0xe169947f77231d8bca4c146b714a93ee64abd8f3a7bbebec5b9414bb43226613) in 0.001s. Peers: 24 + May 10 20:08:19 besu-rpc-alltra-1 besu[518]: 2026-05-10 20:08:19.022+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,307 / 0 tx / 0 om / 0 (0.0%) gas / (0xb6282cb029b9970909da3fa580879ccf6071c60373e11868c4bf982f4c75c3e9) in 0.001s. Peers: 24 + +--- VMID 2501 @ 192.168.11.11 (status: running) --- + Listening ports (ss -tlnp): + State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess + LISTEN 0 100 127.0.0.1:25 0.0.0.0:* users:(("master",pid=336,fd=13)) + LISTEN 0 5 0.0.0.0:18545 0.0.0.0:* users:(("python3",pid=959,fd=3)) + LISTEN 0 100 [::1]:25 [::]:* users:(("master",pid=336,fd=14)) + LISTEN 0 4096 *:22 *:* users:(("sshd",pid=164,fd=3),("systemd",pid=1,fd=56)) + LISTEN 0 4096 *:8545 *:* users:(("java",pid=548,fd=360)) + LISTEN 0 4096 *:8546 *:* users:(("java",pid=548,fd=361)) + LISTEN 0 4096 *:9545 *:* users:(("java",pid=548,fd=359)) + LISTEN 0 4096 *:30303 *:* users:(("java",pid=548,fd=362)) + Besu service (systemctl list-units): + besu-rpc.service loaded activating auto-restart Hyperledger Besu RPC Node + besu.service loaded active running Hyperledger Besu + journalctl -u besu-rpc -n 25: + May 10 20:07:51 besu-rpc-alltra-2 systemd[1]: besu-rpc.service: Consumed 7.509s CPU time. + May 10 20:07:51 besu-rpc-alltra-2 systemd[1]: Started besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:07:54 besu-rpc-alltra-2 besu-rpc[6282]: Unable to read TOML configuration, file not found. + May 10 20:07:54 besu-rpc-alltra-2 besu-rpc[6282]: To display full help: + May 10 20:07:54 besu-rpc-alltra-2 besu-rpc[6282]: besu [COMMAND] --help + May 10 20:07:54 besu-rpc-alltra-2 systemd[1]: besu-rpc.service: Deactivated successfully. + May 10 20:07:54 besu-rpc-alltra-2 systemd[1]: besu-rpc.service: Consumed 7.373s CPU time. + May 10 20:08:04 besu-rpc-alltra-2 systemd[1]: besu-rpc.service: Scheduled restart job, restart counter is at 16. + May 10 20:08:04 besu-rpc-alltra-2 systemd[1]: Stopped besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:04 besu-rpc-alltra-2 systemd[1]: besu-rpc.service: Consumed 7.373s CPU time. + May 10 20:08:04 besu-rpc-alltra-2 systemd[1]: Started besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:06 besu-rpc-alltra-2 besu-rpc[6321]: Unable to read TOML configuration, file not found. + May 10 20:08:06 besu-rpc-alltra-2 besu-rpc[6321]: To display full help: + May 10 20:08:06 besu-rpc-alltra-2 besu-rpc[6321]: besu [COMMAND] --help + May 10 20:08:06 besu-rpc-alltra-2 systemd[1]: besu-rpc.service: Deactivated successfully. + May 10 20:08:06 besu-rpc-alltra-2 systemd[1]: besu-rpc.service: Consumed 6.732s CPU time. + May 10 20:08:16 besu-rpc-alltra-2 systemd[1]: besu-rpc.service: Scheduled restart job, restart counter is at 17. + May 10 20:08:16 besu-rpc-alltra-2 systemd[1]: Stopped besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:16 besu-rpc-alltra-2 systemd[1]: besu-rpc.service: Consumed 6.732s CPU time. + May 10 20:08:16 besu-rpc-alltra-2 systemd[1]: Started besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:19 besu-rpc-alltra-2 besu-rpc[6361]: Unable to read TOML configuration, file not found. + May 10 20:08:19 besu-rpc-alltra-2 besu-rpc[6361]: To display full help: + May 10 20:08:19 besu-rpc-alltra-2 besu-rpc[6361]: besu [COMMAND] --help + May 10 20:08:19 besu-rpc-alltra-2 systemd[1]: besu-rpc.service: Deactivated successfully. + May 10 20:08:19 besu-rpc-alltra-2 systemd[1]: besu-rpc.service: Consumed 7.815s CPU time. + journalctl -u besu -n 25: + May 10 20:07:43 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:07:43.021+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,289 / 0 tx / 0 om / 0 (0.0%) gas / (0xf3bf0b503fc62847f06bcada375fc872386ff305464f0eeeb26aa5c940324b5c) in 0.003s. Peers: 40 + May 10 20:07:45 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:07:45.028+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,290 / 0 tx / 0 om / 0 (0.0%) gas / (0x3195f831c6c1ffb592c92fe91e954c6c91510593adb22528c1b798e79d6b76bc) in 0.003s. Peers: 40 + May 10 20:07:47 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:07:47.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,291 / 0 tx / 0 om / 0 (0.0%) gas / (0x4ac5495e7ec8f93d2a3f325b5363be8c841a4b5e69b34e9518a573cdcc780bcd) in 0.003s. Peers: 40 + May 10 20:07:49 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:07:49.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,292 / 0 tx / 0 om / 0 (0.0%) gas / (0x8fdde14826ff657d086dc80c426310b2ca9257372b32c407c56bd8fbc07044a5) in 0.002s. Peers: 40 + May 10 20:07:51 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:07:51.030+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,293 / 0 tx / 0 om / 0 (0.0%) gas / (0x7fd14f183435fd4f59d0ab43270f301be6ddbd1fbba0b87f4f7bf154111d8ef8) in 0.001s. Peers: 40 + May 10 20:07:53 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:07:53.019+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,294 / 0 tx / 0 om / 0 (0.0%) gas / (0xa8823b3e59fce3ad85549cd5f7a9ec401ea95273413519838bb7bd8cb3c1d958) in 0.001s. Peers: 40 + May 10 20:07:55 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:07:55.024+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,295 / 0 tx / 0 om / 0 (0.0%) gas / (0x43a6c37d20fd151058ffe73f45f4de8e40a841920f1410122e4b4a80acf5c5a3) in 0.004s. Peers: 40 + May 10 20:07:57 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:07:57.021+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,296 / 0 tx / 0 om / 0 (0.0%) gas / (0x44cec125480959167c90854b461cc295dbf82d179a6416d054d3580a24d11c30) in 0.001s. Peers: 40 + May 10 20:07:59 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:07:59.027+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,297 / 0 tx / 0 om / 0 (0.0%) gas / (0xaf105e58bc058e39f6035940c7d5a7b431a1a5f2f10a900086d48f247c3b9f09) in 0.002s. Peers: 40 + May 10 20:08:01 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:01.024+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,298 / 0 tx / 0 om / 0 (0.0%) gas / (0xedd7307ce16d10c89a58e8f43e30cfd1d3f68c3fa3e6bfc1fe1206bddb914b4a) in 0.001s. Peers: 40 + May 10 20:08:03 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:03.023+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,299 / 0 tx / 0 om / 0 (0.0%) gas / (0xad665942dd0bc93fa7d674a6c03b4254409b953e35cd728d23835790290e8361) in 0.002s. Peers: 40 + May 10 20:08:05 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:05.022+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,300 / 0 tx / 0 om / 0 (0.0%) gas / (0xcae412ef6e563ed5653f7dbe40559fe4a1f0a0e48566e11d84f9a6d472814676) in 0.001s. Peers: 40 + May 10 20:08:07 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:07.015+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,301 / 0 tx / 0 om / 0 (0.0%) gas / (0x3c66fc3e5b9410c83a25e0acea6844fd1eee1c28f3a1fc348322bd0e32f1762e) in 0.002s. Peers: 40 + May 10 20:08:09 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:09.023+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,302 / 0 tx / 0 om / 0 (0.0%) gas / (0xa878205b88dd420820cb7becfe730fbbeb8f33570e0023f459c2df6413278717) in 0.002s. Peers: 40 + May 10 20:08:11 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:11.019+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,303 / 0 tx / 0 om / 0 (0.0%) gas / (0x64b03849152b3815fb6aae30e0c5ed048d2aacce05aff2982257c8c16489c54c) in 0.001s. Peers: 40 + May 10 20:08:13 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:13.016+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,304 / 0 tx / 0 om / 0 (0.0%) gas / (0xc68f2c0daf0f9c455f84025fe98ae66ba8bd2960c5b91a739f5cc7417e440e5e) in 0.001s. Peers: 40 + May 10 20:08:15 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:15.027+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,305 / 0 tx / 0 om / 0 (0.0%) gas / (0x5ad2ce582093542f5a323b841a1dac5edc1fc3d81436a6f4d891334018b4ba16) in 0.002s. Peers: 40 + May 10 20:08:17 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:17.027+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,306 / 0 tx / 0 om / 0 (0.0%) gas / (0xe169947f77231d8bca4c146b714a93ee64abd8f3a7bbebec5b9414bb43226613) in 0.002s. Peers: 40 + May 10 20:08:19 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:19.019+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,307 / 0 tx / 0 om / 0 (0.0%) gas / (0xb6282cb029b9970909da3fa580879ccf6071c60373e11868c4bf982f4c75c3e9) in 0.002s. Peers: 40 + May 10 20:08:21 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:21.018+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,308 / 0 tx / 0 om / 0 (0.0%) gas / (0x75a3910d8f3b58d4b41f068ab566796a63c9faaeea46eb65b8e9f54a15e04626) in 0.001s. Peers: 40 + May 10 20:08:23 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:23.023+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,309 / 0 tx / 0 om / 0 (0.0%) gas / (0x2ee8399e6ad15381ad3d9bdcbd3168efe9c696ddf12dd873be17c5403ca6d12b) in 0.002s. Peers: 40 + May 10 20:08:25 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:25.034+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,310 / 0 tx / 0 om / 0 (0.0%) gas / (0xd9cbe80cfa42f0d206064b2a8583513d05afd67790b95f1b1fa5ed8d0ce62399) in 0.004s. Peers: 40 + May 10 20:08:27 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:27.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,311 / 0 tx / 0 om / 0 (0.0%) gas / (0x5cde394c2fa6e2b606f788801314f7847b1b8568459b7b65e08a8988a54e2692) in 0.002s. Peers: 40 + May 10 20:08:29 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:29.030+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,312 / 0 tx / 0 om / 0 (0.0%) gas / (0xc2378a23383699dbd7c9eeab3c4ef8ec737bbb7087b166301f747d14787ee5eb) in 0.001s. Peers: 40 + May 10 20:08:31 besu-rpc-alltra-2 besu[548]: 2026-05-10 20:08:31.019+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,313 / 0 tx / 0 om / 0 (0.0%) gas / (0x0c3fbd2036fd5d87db5a84b06ae272d12196dfc0c437e5789868c8a1db874a26) in 0.001s. Peers: 40 + +--- VMID 2502 @ 192.168.11.11 (status: running) --- + Listening ports (ss -tlnp): + State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess + LISTEN 0 100 127.0.0.1:25 0.0.0.0:* users:(("master",pid=335,fd=13)) + LISTEN 0 5 0.0.0.0:18545 0.0.0.0:* users:(("python3",pid=974,fd=3)) + LISTEN 0 4096 *:8545 *:* users:(("java",pid=541,fd=358)) + LISTEN 0 4096 *:8546 *:* users:(("java",pid=541,fd=359)) + LISTEN 0 4096 *:9545 *:* users:(("java",pid=541,fd=357)) + LISTEN 0 100 [::1]:25 [::]:* users:(("master",pid=335,fd=14)) + LISTEN 0 4096 *:22 *:* users:(("sshd",pid=166,fd=3),("systemd",pid=1,fd=48)) + LISTEN 0 4096 *:30303 *:* users:(("java",pid=541,fd=360)) + Besu service (systemctl list-units): + besu-rpc.service loaded activating auto-restart Hyperledger Besu RPC Node + besu.service loaded active running Hyperledger Besu + journalctl -u besu-rpc -n 25: + May 10 20:08:04 besu-rpc-alltra-3 besu-rpc[6617]: besu [COMMAND] --help + May 10 20:08:04 besu-rpc-alltra-3 systemd[1]: besu-rpc.service: Deactivated successfully. + May 10 20:08:04 besu-rpc-alltra-3 systemd[1]: besu-rpc.service: Consumed 8.394s CPU time. + May 10 20:08:14 besu-rpc-alltra-3 systemd[1]: besu-rpc.service: Scheduled restart job, restart counter is at 16. + May 10 20:08:14 besu-rpc-alltra-3 systemd[1]: Stopped besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:14 besu-rpc-alltra-3 systemd[1]: besu-rpc.service: Consumed 8.394s CPU time. + May 10 20:08:14 besu-rpc-alltra-3 systemd[1]: Started besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:17 besu-rpc-alltra-3 besu-rpc[6657]: Unable to read TOML configuration, file not found. + May 10 20:08:17 besu-rpc-alltra-3 besu-rpc[6657]: To display full help: + May 10 20:08:17 besu-rpc-alltra-3 besu-rpc[6657]: besu [COMMAND] --help + May 10 20:08:17 besu-rpc-alltra-3 systemd[1]: besu-rpc.service: Deactivated successfully. + May 10 20:08:17 besu-rpc-alltra-3 systemd[1]: besu-rpc.service: Consumed 8.643s CPU time. + May 10 20:08:28 besu-rpc-alltra-3 systemd[1]: besu-rpc.service: Scheduled restart job, restart counter is at 17. + May 10 20:08:28 besu-rpc-alltra-3 systemd[1]: Stopped besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:28 besu-rpc-alltra-3 systemd[1]: besu-rpc.service: Consumed 8.643s CPU time. + May 10 20:08:28 besu-rpc-alltra-3 systemd[1]: Started besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:30 besu-rpc-alltra-3 besu-rpc[6696]: Unable to read TOML configuration, file not found. + May 10 20:08:30 besu-rpc-alltra-3 besu-rpc[6696]: To display full help: + May 10 20:08:30 besu-rpc-alltra-3 besu-rpc[6696]: besu [COMMAND] --help + May 10 20:08:30 besu-rpc-alltra-3 systemd[1]: besu-rpc.service: Deactivated successfully. + May 10 20:08:30 besu-rpc-alltra-3 systemd[1]: besu-rpc.service: Consumed 7.949s CPU time. + May 10 20:08:41 besu-rpc-alltra-3 systemd[1]: besu-rpc.service: Scheduled restart job, restart counter is at 18. + May 10 20:08:41 besu-rpc-alltra-3 systemd[1]: Stopped besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:41 besu-rpc-alltra-3 systemd[1]: besu-rpc.service: Consumed 7.949s CPU time. + May 10 20:08:41 besu-rpc-alltra-3 systemd[1]: Started besu-rpc.service - Hyperledger Besu RPC Node. + journalctl -u besu -n 25: + May 10 20:07:55 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:07:55.024+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,295 / 0 tx / 0 om / 0 (0.0%) gas / (0x43a6c37d20fd151058ffe73f45f4de8e40a841920f1410122e4b4a80acf5c5a3) in 0.004s. Peers: 40 + May 10 20:07:57 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:07:57.024+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,296 / 0 tx / 0 om / 0 (0.0%) gas / (0x44cec125480959167c90854b461cc295dbf82d179a6416d054d3580a24d11c30) in 0.003s. Peers: 40 + May 10 20:07:59 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:07:59.030+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,297 / 0 tx / 0 om / 0 (0.0%) gas / (0xaf105e58bc058e39f6035940c7d5a7b431a1a5f2f10a900086d48f247c3b9f09) in 0.004s. Peers: 40 + May 10 20:08:01 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:01.027+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,298 / 0 tx / 0 om / 0 (0.0%) gas / (0xedd7307ce16d10c89a58e8f43e30cfd1d3f68c3fa3e6bfc1fe1206bddb914b4a) in 0.003s. Peers: 40 + May 10 20:08:03 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:03.022+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,299 / 0 tx / 0 om / 0 (0.0%) gas / (0xad665942dd0bc93fa7d674a6c03b4254409b953e35cd728d23835790290e8361) in 0.003s. Peers: 40 + May 10 20:08:05 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:05.026+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,300 / 0 tx / 0 om / 0 (0.0%) gas / (0xcae412ef6e563ed5653f7dbe40559fe4a1f0a0e48566e11d84f9a6d472814676) in 0.003s. Peers: 40 + May 10 20:08:07 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:07.021+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,301 / 0 tx / 0 om / 0 (0.0%) gas / (0x3c66fc3e5b9410c83a25e0acea6844fd1eee1c28f3a1fc348322bd0e32f1762e) in 0.004s. Peers: 40 + May 10 20:08:09 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:09.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,302 / 0 tx / 0 om / 0 (0.0%) gas / (0xa878205b88dd420820cb7becfe730fbbeb8f33570e0023f459c2df6413278717) in 0.003s. Peers: 40 + May 10 20:08:11 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:11.024+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,303 / 0 tx / 0 om / 0 (0.0%) gas / (0x64b03849152b3815fb6aae30e0c5ed048d2aacce05aff2982257c8c16489c54c) in 0.004s. Peers: 40 + May 10 20:08:13 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:13.018+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,304 / 0 tx / 0 om / 0 (0.0%) gas / (0xc68f2c0daf0f9c455f84025fe98ae66ba8bd2960c5b91a739f5cc7417e440e5e) in 0.002s. Peers: 40 + May 10 20:08:15 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:15.028+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,305 / 0 tx / 0 om / 0 (0.0%) gas / (0x5ad2ce582093542f5a323b841a1dac5edc1fc3d81436a6f4d891334018b4ba16) in 0.004s. Peers: 40 + May 10 20:08:17 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:17.033+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,306 / 0 tx / 0 om / 0 (0.0%) gas / (0xe169947f77231d8bca4c146b714a93ee64abd8f3a7bbebec5b9414bb43226613) in 0.004s. Peers: 40 + May 10 20:08:19 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:19.029+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,307 / 0 tx / 0 om / 0 (0.0%) gas / (0xb6282cb029b9970909da3fa580879ccf6071c60373e11868c4bf982f4c75c3e9) in 0.009s. Peers: 40 + May 10 20:08:21 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:21.020+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,308 / 0 tx / 0 om / 0 (0.0%) gas / (0x75a3910d8f3b58d4b41f068ab566796a63c9faaeea46eb65b8e9f54a15e04626) in 0.002s. Peers: 40 + May 10 20:08:23 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:23.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,309 / 0 tx / 0 om / 0 (0.0%) gas / (0x2ee8399e6ad15381ad3d9bdcbd3168efe9c696ddf12dd873be17c5403ca6d12b) in 0.003s. Peers: 40 + May 10 20:08:25 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:25.035+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,310 / 0 tx / 0 om / 0 (0.0%) gas / (0xd9cbe80cfa42f0d206064b2a8583513d05afd67790b95f1b1fa5ed8d0ce62399) in 0.008s. Peers: 40 + May 10 20:08:27 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:27.024+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,311 / 0 tx / 0 om / 0 (0.0%) gas / (0x5cde394c2fa6e2b606f788801314f7847b1b8568459b7b65e08a8988a54e2692) in 0.001s. Peers: 40 + May 10 20:08:29 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:29.048+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,312 / 0 tx / 0 om / 0 (0.0%) gas / (0xc2378a23383699dbd7c9eeab3c4ef8ec737bbb7087b166301f747d14787ee5eb) in 0.018s. Peers: 40 + May 10 20:08:31 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:31.021+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,313 / 0 tx / 0 om / 0 (0.0%) gas / (0x0c3fbd2036fd5d87db5a84b06ae272d12196dfc0c437e5789868c8a1db874a26) in 0.002s. Peers: 40 + May 10 20:08:33 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:33.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,314 / 0 tx / 0 om / 0 (0.0%) gas / (0xf3190d68f60a316816ac9a8f02296c4a447a58fe0b238aac38d84b35ba16553e) in 0.001s. Peers: 40 + May 10 20:08:35 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:35.023+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,315 / 0 tx / 0 om / 0 (0.0%) gas / (0x7976585940bc4e7edfee434d2a793638888035c8ad7bbadd8362787e184df6ed) in 0.003s. Peers: 40 + May 10 20:08:37 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:37.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,316 / 0 tx / 0 om / 0 (0.0%) gas / (0x9689fc7ef463f1fcc789523264f09fff2138531ad3b67ffca451d914d61a7721) in 0.005s. Peers: 40 + May 10 20:08:39 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:39.028+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,317 / 0 tx / 0 om / 0 (0.0%) gas / (0x11a5ba96b9f99699b944326450ed367048dd99a0e939adf16027ab38cd91c2db) in 0.004s. Peers: 40 + May 10 20:08:41 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:41.024+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,318 / 0 tx / 0 om / 0 (0.0%) gas / (0x68d4479715cd4bdfcb34871b5f763b89d7cf3dcddb6b80ad639137e5ffc4caa5) in 0.003s. Peers: 40 + May 10 20:08:43 besu-rpc-alltra-3 besu[541]: 2026-05-10 20:08:43.022+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,319 / 0 tx / 0 om / 0 (0.0%) gas / (0xfeaba9671447b66b6f0b4b88716a4cc9dde77e726c654e4119bda51486d839e4) in 0.003s. Peers: 40 + +--- VMID 2503 @ 192.168.11.11 (status: running) --- + Listening ports (ss -tlnp): + State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess + LISTEN 0 5 0.0.0.0:18545 0.0.0.0:* users:(("python3",pid=964,fd=3)) + LISTEN 0 100 127.0.0.1:25 0.0.0.0:* users:(("master",pid=336,fd=13)) + LISTEN 0 4096 *:30303 *:* users:(("java",pid=553,fd=361)) + LISTEN 0 4096 *:9545 *:* users:(("java",pid=553,fd=358)) + LISTEN 0 4096 *:8545 *:* users:(("java",pid=553,fd=359)) + LISTEN 0 4096 *:8546 *:* users:(("java",pid=553,fd=360)) + LISTEN 0 4096 *:22 *:* users:(("sshd",pid=163,fd=3),("systemd",pid=1,fd=54)) + LISTEN 0 100 [::1]:25 [::]:* users:(("master",pid=336,fd=14)) + Besu service (systemctl list-units): + besu-rpc.service loaded activating auto-restart Hyperledger Besu RPC Node + besu.service loaded active running Hyperledger Besu + journalctl -u besu-rpc -n 25: + May 10 20:08:21 besu-rpc-hybx-1 systemd[1]: besu-rpc.service: Consumed 7.722s CPU time. + May 10 20:08:21 besu-rpc-hybx-1 systemd[1]: Started besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:24 besu-rpc-hybx-1 besu-rpc[8618]: Unable to read TOML configuration, file not found. + May 10 20:08:24 besu-rpc-hybx-1 besu-rpc[8618]: To display full help: + May 10 20:08:24 besu-rpc-hybx-1 besu-rpc[8618]: besu [COMMAND] --help + May 10 20:08:24 besu-rpc-hybx-1 systemd[1]: besu-rpc.service: Deactivated successfully. + May 10 20:08:24 besu-rpc-hybx-1 systemd[1]: besu-rpc.service: Consumed 8.020s CPU time. + May 10 20:08:34 besu-rpc-hybx-1 systemd[1]: besu-rpc.service: Scheduled restart job, restart counter is at 17. + May 10 20:08:34 besu-rpc-hybx-1 systemd[1]: Stopped besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:34 besu-rpc-hybx-1 systemd[1]: besu-rpc.service: Consumed 8.020s CPU time. + May 10 20:08:34 besu-rpc-hybx-1 systemd[1]: Started besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:37 besu-rpc-hybx-1 besu-rpc[8657]: Unable to read TOML configuration, file not found. + May 10 20:08:37 besu-rpc-hybx-1 besu-rpc[8657]: To display full help: + May 10 20:08:37 besu-rpc-hybx-1 besu-rpc[8657]: besu [COMMAND] --help + May 10 20:08:37 besu-rpc-hybx-1 systemd[1]: besu-rpc.service: Deactivated successfully. + May 10 20:08:37 besu-rpc-hybx-1 systemd[1]: besu-rpc.service: Consumed 7.759s CPU time. + May 10 20:08:47 besu-rpc-hybx-1 systemd[1]: besu-rpc.service: Scheduled restart job, restart counter is at 18. + May 10 20:08:47 besu-rpc-hybx-1 systemd[1]: Stopped besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:47 besu-rpc-hybx-1 systemd[1]: besu-rpc.service: Consumed 7.759s CPU time. + May 10 20:08:47 besu-rpc-hybx-1 systemd[1]: Started besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:50 besu-rpc-hybx-1 besu-rpc[8697]: Unable to read TOML configuration, file not found. + May 10 20:08:50 besu-rpc-hybx-1 besu-rpc[8697]: To display full help: + May 10 20:08:50 besu-rpc-hybx-1 besu-rpc[8697]: besu [COMMAND] --help + May 10 20:08:50 besu-rpc-hybx-1 systemd[1]: besu-rpc.service: Deactivated successfully. + May 10 20:08:50 besu-rpc-hybx-1 systemd[1]: besu-rpc.service: Consumed 7.647s CPU time. + journalctl -u besu -n 25: + May 10 20:08:07 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:07.016+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,301 / 0 tx / 0 om / 0 (0.0%) gas / (0x3c66fc3e5b9410c83a25e0acea6844fd1eee1c28f3a1fc348322bd0e32f1762e) in 0.002s. Peers: 40 + May 10 20:08:09 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:09.026+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,302 / 0 tx / 0 om / 0 (0.0%) gas / (0xa878205b88dd420820cb7becfe730fbbeb8f33570e0023f459c2df6413278717) in 0.004s. Peers: 40 + May 10 20:08:11 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:11.019+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,303 / 0 tx / 0 om / 0 (0.0%) gas / (0x64b03849152b3815fb6aae30e0c5ed048d2aacce05aff2982257c8c16489c54c) in 0.001s. Peers: 40 + May 10 20:08:13 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:13.018+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,304 / 0 tx / 0 om / 0 (0.0%) gas / (0xc68f2c0daf0f9c455f84025fe98ae66ba8bd2960c5b91a739f5cc7417e440e5e) in 0.001s. Peers: 40 + May 10 20:08:15 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:15.026+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,305 / 0 tx / 0 om / 0 (0.0%) gas / (0x5ad2ce582093542f5a323b841a1dac5edc1fc3d81436a6f4d891334018b4ba16) in 0.002s. Peers: 40 + May 10 20:08:17 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:17.034+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,306 / 0 tx / 0 om / 0 (0.0%) gas / (0xe169947f77231d8bca4c146b714a93ee64abd8f3a7bbebec5b9414bb43226613) in 0.004s. Peers: 40 + May 10 20:08:19 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:19.033+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,307 / 0 tx / 0 om / 0 (0.0%) gas / (0xb6282cb029b9970909da3fa580879ccf6071c60373e11868c4bf982f4c75c3e9) in 0.005s. Peers: 40 + May 10 20:08:21 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:21.019+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,308 / 0 tx / 0 om / 0 (0.0%) gas / (0x75a3910d8f3b58d4b41f068ab566796a63c9faaeea46eb65b8e9f54a15e04626) in 0.002s. Peers: 40 + May 10 20:08:23 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:23.034+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,309 / 0 tx / 0 om / 0 (0.0%) gas / (0x2ee8399e6ad15381ad3d9bdcbd3168efe9c696ddf12dd873be17c5403ca6d12b) in 0.008s. Peers: 40 + May 10 20:08:25 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:25.032+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,310 / 0 tx / 0 om / 0 (0.0%) gas / (0xd9cbe80cfa42f0d206064b2a8583513d05afd67790b95f1b1fa5ed8d0ce62399) in 0.002s. Peers: 40 + May 10 20:08:27 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:27.026+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,311 / 0 tx / 0 om / 0 (0.0%) gas / (0x5cde394c2fa6e2b606f788801314f7847b1b8568459b7b65e08a8988a54e2692) in 0.002s. Peers: 40 + May 10 20:08:29 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:29.030+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,312 / 0 tx / 0 om / 0 (0.0%) gas / (0xc2378a23383699dbd7c9eeab3c4ef8ec737bbb7087b166301f747d14787ee5eb) in 0.002s. Peers: 40 + May 10 20:08:31 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:31.027+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,313 / 0 tx / 0 om / 0 (0.0%) gas / (0x0c3fbd2036fd5d87db5a84b06ae272d12196dfc0c437e5789868c8a1db874a26) in 0.002s. Peers: 40 + May 10 20:08:33 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:33.027+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,314 / 0 tx / 0 om / 0 (0.0%) gas / (0xf3190d68f60a316816ac9a8f02296c4a447a58fe0b238aac38d84b35ba16553e) in 0.004s. Peers: 40 + May 10 20:08:35 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:35.030+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,315 / 0 tx / 0 om / 0 (0.0%) gas / (0x7976585940bc4e7edfee434d2a793638888035c8ad7bbadd8362787e184df6ed) in 0.010s. Peers: 40 + May 10 20:08:37 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:37.026+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,316 / 0 tx / 0 om / 0 (0.0%) gas / (0x9689fc7ef463f1fcc789523264f09fff2138531ad3b67ffca451d914d61a7721) in 0.004s. Peers: 40 + May 10 20:08:39 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:39.023+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,317 / 0 tx / 0 om / 0 (0.0%) gas / (0x11a5ba96b9f99699b944326450ed367048dd99a0e939adf16027ab38cd91c2db) in 0.001s. Peers: 40 + May 10 20:08:41 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:41.022+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,318 / 0 tx / 0 om / 0 (0.0%) gas / (0x68d4479715cd4bdfcb34871b5f763b89d7cf3dcddb6b80ad639137e5ffc4caa5) in 0.002s. Peers: 40 + May 10 20:08:43 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:43.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,319 / 0 tx / 0 om / 0 (0.0%) gas / (0xfeaba9671447b66b6f0b4b88716a4cc9dde77e726c654e4119bda51486d839e4) in 0.004s. Peers: 40 + May 10 20:08:45 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:45.028+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,320 / 0 tx / 0 om / 0 (0.0%) gas / (0xc8764a00f5a9e756ad63fc538f763782c5a6090bcaee01e509b2ebacc84d26f6) in 0.002s. Peers: 40 + May 10 20:08:47 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:47.020+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,321 / 0 tx / 0 om / 0 (0.0%) gas / (0x564a59dd1f25de2dfcfe58aa892b7e8e0bed5916a042fa3f9c56b557d982f1d3) in 0.002s. Peers: 40 + May 10 20:08:49 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:49.020+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,322 / 0 tx / 0 om / 0 (0.0%) gas / (0xea4bb1aff7c0be0fdd5fad6be2b362b8edaf207b810371ad94ac22a8b4ef97b0) in 0.001s. Peers: 40 + May 10 20:08:51 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:51.017+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,323 / 0 tx / 0 om / 0 (0.0%) gas / (0x7bf1becee96d9f6c4d3cbadb6d25dd383e189cb181b2b5d37763c4d8db52db1f) in 0.002s. Peers: 40 + May 10 20:08:53 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:53.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,324 / 0 tx / 0 om / 0 (0.0%) gas / (0xc5ec25641f4c396b319e1efe18494b4779a61300b9eaf5bad120bef5332097d8) in 0.003s. Peers: 40 + May 10 20:08:55 besu-rpc-hybx-1 besu[553]: 2026-05-10 20:08:55.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,325 / 0 tx / 0 om / 0 (0.0%) gas / (0x0335249ccbc498a3ab8cca4d1424f3ec2f34b4151aa64f63e21968e97a98d36c) in 0.002s. Peers: 40 + +--- VMID 2504 @ 192.168.11.11 (status: running) --- + Listening ports (ss -tlnp): + State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess + LISTEN 0 5 0.0.0.0:18545 0.0.0.0:* users:(("python3",pid=958,fd=3)) + LISTEN 0 100 127.0.0.1:25 0.0.0.0:* users:(("master",pid=335,fd=13)) + LISTEN 0 100 [::1]:25 [::]:* users:(("master",pid=335,fd=14)) + LISTEN 0 4096 *:30303 *:* users:(("java",pid=545,fd=360)) + LISTEN 0 4096 *:22 *:* users:(("sshd",pid=173,fd=3),("systemd",pid=1,fd=70)) + LISTEN 0 4096 *:9545 *:* users:(("java",pid=545,fd=357)) + LISTEN 0 4096 *:8546 *:* users:(("java",pid=545,fd=359)) + LISTEN 0 4096 *:8545 *:* users:(("java",pid=545,fd=358)) + Besu service (systemctl list-units): + besu-rpc.service loaded activating auto-restart Hyperledger Besu RPC Node + besu.service loaded active running Hyperledger Besu + journalctl -u besu-rpc -n 25: + May 10 20:08:27 besu-rpc-hybx-2 besu-rpc[6316]: besu [COMMAND] --help + May 10 20:08:27 besu-rpc-hybx-2 systemd[1]: besu-rpc.service: Deactivated successfully. + May 10 20:08:27 besu-rpc-hybx-2 systemd[1]: besu-rpc.service: Consumed 6.947s CPU time. + May 10 20:08:37 besu-rpc-hybx-2 systemd[1]: besu-rpc.service: Scheduled restart job, restart counter is at 16. + May 10 20:08:37 besu-rpc-hybx-2 systemd[1]: Stopped besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:37 besu-rpc-hybx-2 systemd[1]: besu-rpc.service: Consumed 6.947s CPU time. + May 10 20:08:37 besu-rpc-hybx-2 systemd[1]: Started besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:40 besu-rpc-hybx-2 besu-rpc[6356]: Unable to read TOML configuration, file not found. + May 10 20:08:40 besu-rpc-hybx-2 besu-rpc[6356]: To display full help: + May 10 20:08:40 besu-rpc-hybx-2 besu-rpc[6356]: besu [COMMAND] --help + May 10 20:08:40 besu-rpc-hybx-2 systemd[1]: besu-rpc.service: Deactivated successfully. + May 10 20:08:40 besu-rpc-hybx-2 systemd[1]: besu-rpc.service: Consumed 7.220s CPU time. + May 10 20:08:50 besu-rpc-hybx-2 systemd[1]: besu-rpc.service: Scheduled restart job, restart counter is at 17. + May 10 20:08:50 besu-rpc-hybx-2 systemd[1]: Stopped besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:50 besu-rpc-hybx-2 systemd[1]: besu-rpc.service: Consumed 7.220s CPU time. + May 10 20:08:50 besu-rpc-hybx-2 systemd[1]: Started besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:08:53 besu-rpc-hybx-2 besu-rpc[6396]: Unable to read TOML configuration, file not found. + May 10 20:08:53 besu-rpc-hybx-2 besu-rpc[6396]: To display full help: + May 10 20:08:53 besu-rpc-hybx-2 besu-rpc[6396]: besu [COMMAND] --help + May 10 20:08:53 besu-rpc-hybx-2 systemd[1]: besu-rpc.service: Deactivated successfully. + May 10 20:08:53 besu-rpc-hybx-2 systemd[1]: besu-rpc.service: Consumed 7.525s CPU time. + May 10 20:09:03 besu-rpc-hybx-2 systemd[1]: besu-rpc.service: Scheduled restart job, restart counter is at 18. + May 10 20:09:03 besu-rpc-hybx-2 systemd[1]: Stopped besu-rpc.service - Hyperledger Besu RPC Node. + May 10 20:09:03 besu-rpc-hybx-2 systemd[1]: besu-rpc.service: Consumed 7.525s CPU time. + May 10 20:09:03 besu-rpc-hybx-2 systemd[1]: Started besu-rpc.service - Hyperledger Besu RPC Node. + journalctl -u besu -n 25: + May 10 20:08:19 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:19.038+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,307 / 0 tx / 0 om / 0 (0.0%) gas / (0xb6282cb029b9970909da3fa580879ccf6071c60373e11868c4bf982f4c75c3e9) in 0.004s. Peers: 40 + May 10 20:08:21 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:21.026+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,308 / 0 tx / 0 om / 0 (0.0%) gas / (0x75a3910d8f3b58d4b41f068ab566796a63c9faaeea46eb65b8e9f54a15e04626) in 0.004s. Peers: 40 + May 10 20:08:23 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:23.026+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,309 / 0 tx / 0 om / 0 (0.0%) gas / (0x2ee8399e6ad15381ad3d9bdcbd3168efe9c696ddf12dd873be17c5403ca6d12b) in 0.004s. Peers: 40 + May 10 20:08:25 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:25.038+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,310 / 0 tx / 0 om / 0 (0.0%) gas / (0xd9cbe80cfa42f0d206064b2a8583513d05afd67790b95f1b1fa5ed8d0ce62399) in 0.008s. Peers: 40 + May 10 20:08:27 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:27.031+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,311 / 0 tx / 0 om / 0 (0.0%) gas / (0x5cde394c2fa6e2b606f788801314f7847b1b8568459b7b65e08a8988a54e2692) in 0.001s. Peers: 40 + May 10 20:08:29 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:29.050+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,312 / 0 tx / 0 om / 0 (0.0%) gas / (0xc2378a23383699dbd7c9eeab3c4ef8ec737bbb7087b166301f747d14787ee5eb) in 0.021s. Peers: 40 + May 10 20:08:31 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:31.041+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,313 / 0 tx / 0 om / 0 (0.0%) gas / (0x0c3fbd2036fd5d87db5a84b06ae272d12196dfc0c437e5789868c8a1db874a26) in 0.021s. Peers: 40 + May 10 20:08:33 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:33.023+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,314 / 0 tx / 0 om / 0 (0.0%) gas / (0xf3190d68f60a316816ac9a8f02296c4a447a58fe0b238aac38d84b35ba16553e) in 0.003s. Peers: 40 + May 10 20:08:35 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:35.022+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,315 / 0 tx / 0 om / 0 (0.0%) gas / (0x7976585940bc4e7edfee434d2a793638888035c8ad7bbadd8362787e184df6ed) in 0.003s. Peers: 40 + May 10 20:08:37 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:37.027+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,316 / 0 tx / 0 om / 0 (0.0%) gas / (0x9689fc7ef463f1fcc789523264f09fff2138531ad3b67ffca451d914d61a7721) in 0.009s. Peers: 40 + May 10 20:08:39 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:39.024+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,317 / 0 tx / 0 om / 0 (0.0%) gas / (0x11a5ba96b9f99699b944326450ed367048dd99a0e939adf16027ab38cd91c2db) in 0.001s. Peers: 40 + May 10 20:08:41 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:41.024+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,318 / 0 tx / 0 om / 0 (0.0%) gas / (0x68d4479715cd4bdfcb34871b5f763b89d7cf3dcddb6b80ad639137e5ffc4caa5) in 0.003s. Peers: 40 + May 10 20:08:43 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:43.019+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,319 / 0 tx / 0 om / 0 (0.0%) gas / (0xfeaba9671447b66b6f0b4b88716a4cc9dde77e726c654e4119bda51486d839e4) in 0.001s. Peers: 40 + May 10 20:08:45 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:45.024+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,320 / 0 tx / 0 om / 0 (0.0%) gas / (0xc8764a00f5a9e756ad63fc538f763782c5a6090bcaee01e509b2ebacc84d26f6) in 0.005s. Peers: 40 + May 10 20:08:47 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:47.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,321 / 0 tx / 0 om / 0 (0.0%) gas / (0x564a59dd1f25de2dfcfe58aa892b7e8e0bed5916a042fa3f9c56b557d982f1d3) in 0.005s. Peers: 40 + May 10 20:08:49 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:49.033+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,322 / 0 tx / 0 om / 0 (0.0%) gas / (0xea4bb1aff7c0be0fdd5fad6be2b362b8edaf207b810371ad94ac22a8b4ef97b0) in 0.004s. Peers: 40 + May 10 20:08:51 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:51.024+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,323 / 0 tx / 0 om / 0 (0.0%) gas / (0x7bf1becee96d9f6c4d3cbadb6d25dd383e189cb181b2b5d37763c4d8db52db1f) in 0.006s. Peers: 40 + May 10 20:08:53 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:53.022+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,324 / 0 tx / 0 om / 0 (0.0%) gas / (0xc5ec25641f4c396b319e1efe18494b4779a61300b9eaf5bad120bef5332097d8) in 0.001s. Peers: 40 + May 10 20:08:55 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:55.030+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,325 / 0 tx / 0 om / 0 (0.0%) gas / (0x0335249ccbc498a3ab8cca4d1424f3ec2f34b4151aa64f63e21968e97a98d36c) in 0.007s. Peers: 40 + May 10 20:08:57 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:57.031+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,326 / 0 tx / 0 om / 0 (0.0%) gas / (0x49cc6f5a406e88d66dca74c8b40dc84c87e613c8fab4840b5311a8822f3a8d93) in 0.003s. Peers: 40 + May 10 20:08:59 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:08:59.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,327 / 0 tx / 0 om / 0 (0.0%) gas / (0x0829f4ca670a62c4bb5945fd0e63102ff3ff424666d2bb3282d624598d6912cf) in 0.004s. Peers: 40 + May 10 20:09:01 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:09:01.023+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,328 / 0 tx / 0 om / 0 (0.0%) gas / (0x342f7292c3089ef7e491396bb6fc538ab4595326bcd4045226d9bf5c110fdb5f) in 0.004s. Peers: 40 + May 10 20:09:03 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:09:03.022+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,329 / 0 tx / 0 om / 0 (0.0%) gas / (0x4f5a731793f00ab464bcd1996854c76223c9bfb3d70df2872dda3ddd2015de86) in 0.003s. Peers: 40 + May 10 20:09:05 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:09:05.021+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,330 / 0 tx / 0 om / 0 (0.0%) gas / (0x510ff027c71458a02dd0b22a2661c2fbff33c7ab930b6bb6b84b790cf33ca66c) in 0.002s. Peers: 40 + May 10 20:09:07 besu-rpc-hybx-2 besu[545]: 2026-05-10 20:09:07.032+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,331 / 0 tx / 0 om / 0 (0.0%) gas / (0xf88444f460fd2db69273e3dd265ab06b940b12abcd3ad440bfd4e1af68311dcd) in 0.002s. Peers: 40 + +--- VMID 2505 @ 192.168.11.11 (status: running) --- + Listening ports (ss -tlnp): + State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess + LISTEN 0 5 0.0.0.0:18545 0.0.0.0:* users:(("python3",pid=941,fd=3)) + LISTEN 0 100 127.0.0.1:25 0.0.0.0:* users:(("master",pid=325,fd=13)) + LISTEN 0 100 [::1]:25 [::]:* users:(("master",pid=325,fd=14)) + LISTEN 0 4096 *:30303 *:* users:(("java",pid=531,fd=360)) + LISTEN 0 4096 *:22 *:* users:(("sshd",pid=169,fd=3),("systemd",pid=1,fd=62)) + LISTEN 0 4096 *:9545 *:* users:(("java",pid=531,fd=357)) + LISTEN 0 4096 *:8545 *:* users:(("java",pid=531,fd=358)) + LISTEN 0 4096 *:8546 *:* users:(("java",pid=531,fd=359)) + Besu service (systemctl list-units): + besu-rpc.service loaded activating auto-restart Hyperledger Besu RPC Node + besu.service loaded active running Hyperledger Besu + journalctl -u besu-rpc -n 25: + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: org.hyperledger.besu.util.InvalidConfigurationException: Port(s) '[30303, 8545, 8546, 9545]' already in use. Check for other processes using the port(s). + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at org.hyperledger.besu.cli.BesuCommand.checkIfRequiredPortsAreAvailable(BesuCommand.java:3317) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at org.hyperledger.besu.cli.BesuCommand.configure(BesuCommand.java:2074) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at org.hyperledger.besu.cli.BesuCommand.run(BesuCommand.java:1461) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at picocli.CommandLine.executeUserObject(CommandLine.java:2026) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at picocli.CommandLine.access$1500(CommandLine.java:148) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2461) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at picocli.CommandLine$RunLast.handle(CommandLine.java:2453) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at picocli.CommandLine$RunLast.handle(CommandLine.java:2415) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2273) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at picocli.CommandLine$RunLast.execute(CommandLine.java:2417) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at picocli.CommandLine.execute(CommandLine.java:2170) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at org.hyperledger.besu.cli.util.ConfigOptionSearchAndRunHandler.handle(ConfigOptionSearchAndRunHandler.java:62) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at org.hyperledger.besu.cli.util.ConfigOptionSearchAndRunHandler.handle(ConfigOptionSearchAndRunHandler.java:33) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2273) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at picocli.CommandLine$RunLast.execute(CommandLine.java:2417) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at picocli.CommandLine.execute(CommandLine.java:2170) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at org.hyperledger.besu.cli.BesuCommand.parse(BesuCommand.java:1628) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at org.hyperledger.besu.cli.BesuCommand.parse(BesuCommand.java:1423) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: at org.hyperledger.besu.Besu.main(Besu.java:39) + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: Port(s) '[30303, 8545, 8546, 9545]' already in use. Check for other processes using the port(s). + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: To display full help: + May 10 20:09:13 besu-rpc-hybx-3 besu-rpc[9759]: besu [COMMAND] --help + May 10 20:09:13 besu-rpc-hybx-3 systemd[1]: besu-rpc.service: Deactivated successfully. + May 10 20:09:13 besu-rpc-hybx-3 systemd[1]: besu-rpc.service: Consumed 9.083s CPU time. + journalctl -u besu -n 25: + May 10 20:08:31 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:31.021+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,313 / 0 tx / 0 om / 0 (0.0%) gas / (0x0c3fbd2036fd5d87db5a84b06ae272d12196dfc0c437e5789868c8a1db874a26) in 0.001s. Peers: 40 + May 10 20:08:33 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:33.023+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,314 / 0 tx / 0 om / 0 (0.0%) gas / (0xf3190d68f60a316816ac9a8f02296c4a447a58fe0b238aac38d84b35ba16553e) in 0.002s. Peers: 40 + May 10 20:08:35 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:35.021+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,315 / 0 tx / 0 om / 0 (0.0%) gas / (0x7976585940bc4e7edfee434d2a793638888035c8ad7bbadd8362787e184df6ed) in 0.003s. Peers: 40 + May 10 20:08:37 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:37.022+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,316 / 0 tx / 0 om / 0 (0.0%) gas / (0x9689fc7ef463f1fcc789523264f09fff2138531ad3b67ffca451d914d61a7721) in 0.005s. Peers: 40 + May 10 20:08:39 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:39.027+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,317 / 0 tx / 0 om / 0 (0.0%) gas / (0x11a5ba96b9f99699b944326450ed367048dd99a0e939adf16027ab38cd91c2db) in 0.003s. Peers: 40 + May 10 20:08:41 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:41.028+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,318 / 0 tx / 0 om / 0 (0.0%) gas / (0x68d4479715cd4bdfcb34871b5f763b89d7cf3dcddb6b80ad639137e5ffc4caa5) in 0.009s. Peers: 40 + May 10 20:08:43 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:43.019+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,319 / 0 tx / 0 om / 0 (0.0%) gas / (0xfeaba9671447b66b6f0b4b88716a4cc9dde77e726c654e4119bda51486d839e4) in 0.002s. Peers: 40 + May 10 20:08:45 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:45.026+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,320 / 0 tx / 0 om / 0 (0.0%) gas / (0xc8764a00f5a9e756ad63fc538f763782c5a6090bcaee01e509b2ebacc84d26f6) in 0.005s. Peers: 40 + May 10 20:08:47 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:47.018+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,321 / 0 tx / 0 om / 0 (0.0%) gas / (0x564a59dd1f25de2dfcfe58aa892b7e8e0bed5916a042fa3f9c56b557d982f1d3) in 0.002s. Peers: 40 + May 10 20:08:49 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:49.026+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,322 / 0 tx / 0 om / 0 (0.0%) gas / (0xea4bb1aff7c0be0fdd5fad6be2b362b8edaf207b810371ad94ac22a8b4ef97b0) in 0.006s. Peers: 40 + May 10 20:08:51 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:51.018+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,323 / 0 tx / 0 om / 0 (0.0%) gas / (0x7bf1becee96d9f6c4d3cbadb6d25dd383e189cb181b2b5d37763c4d8db52db1f) in 0.003s. Peers: 40 + May 10 20:08:53 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:53.023+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,324 / 0 tx / 0 om / 0 (0.0%) gas / (0xc5ec25641f4c396b319e1efe18494b4779a61300b9eaf5bad120bef5332097d8) in 0.002s. Peers: 40 + May 10 20:08:55 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:55.020+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,325 / 0 tx / 0 om / 0 (0.0%) gas / (0x0335249ccbc498a3ab8cca4d1424f3ec2f34b4151aa64f63e21968e97a98d36c) in 0.001s. Peers: 40 + May 10 20:08:57 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:57.047+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,326 / 0 tx / 0 om / 0 (0.0%) gas / (0x49cc6f5a406e88d66dca74c8b40dc84c87e613c8fab4840b5311a8822f3a8d93) in 0.002s. Peers: 40 + May 10 20:08:59 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:08:59.025+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,327 / 0 tx / 0 om / 0 (0.0%) gas / (0x0829f4ca670a62c4bb5945fd0e63102ff3ff424666d2bb3282d624598d6912cf) in 0.006s. Peers: 40 + May 10 20:09:01 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:09:01.027+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,328 / 0 tx / 0 om / 0 (0.0%) gas / (0x342f7292c3089ef7e491396bb6fc538ab4595326bcd4045226d9bf5c110fdb5f) in 0.008s. Peers: 40 + May 10 20:09:03 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:09:03.019+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,329 / 0 tx / 0 om / 0 (0.0%) gas / (0x4f5a731793f00ab464bcd1996854c76223c9bfb3d70df2872dda3ddd2015de86) in 0.001s. Peers: 40 + May 10 20:09:05 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:09:05.026+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,330 / 0 tx / 0 om / 0 (0.0%) gas / (0x510ff027c71458a02dd0b22a2661c2fbff33c7ab930b6bb6b84b790cf33ca66c) in 0.008s. Peers: 40 + May 10 20:09:07 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:09:07.034+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,331 / 0 tx / 0 om / 0 (0.0%) gas / (0xf88444f460fd2db69273e3dd265ab06b940b12abcd3ad440bfd4e1af68311dcd) in 0.011s. Peers: 40 + May 10 20:09:09 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:09:09.022+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,332 / 0 tx / 0 om / 0 (0.0%) gas / (0x78a049e5bfd7eb2d1b8aa7d056c6dccfdd4aed12ba22cecb5a4e1dc9c0ab0e71) in 0.004s. Peers: 40 + May 10 20:09:11 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:09:11.036+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,333 / 0 tx / 0 om / 0 (0.0%) gas / (0xee5ce8bd9df13b9219f863f6890cb9cd175f0528bd85e8f1871cedc1a3db5629) in 0.019s. Peers: 40 + May 10 20:09:13 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:09:13.018+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,334 / 0 tx / 0 om / 0 (0.0%) gas / (0x1b0a2f6ccdb8eb31ac1ed5334b418e567fd6a455b88b49be9585f99830e494e4) in 0.001s. Peers: 40 + May 10 20:09:15 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:09:15.039+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,335 / 0 tx / 0 om / 0 (0.0%) gas / (0x5dfaa1f90dfde05da98750145fac9b20aa65ad11836cee7bc5f70af02fe232f1) in 0.011s. Peers: 40 + May 10 20:09:17 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:09:17.026+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,336 / 0 tx / 0 om / 0 (0.0%) gas / (0x15abc63a4280bea8ee1d58a56e1fdcfbf7ef10764091185b840a901dcc68f724) in 0.001s. Peers: 40 + May 10 20:09:19 besu-rpc-hybx-3 besu[531]: 2026-05-10 20:09:19.030+00:00 | EthScheduler-Workers-0 | INFO | PersistBlockTask | Imported #5,024,337 / 0 tx / 0 om / 0 (0.0%) gas / (0xa8fe82729501302fb1fd545ffe22367c4e22b170eb36efde15524fff003269f6) in 0.009s. Peers: 40 + +============================================== +If 8545 is not in ss -tlnp, Besu is not binding. Check journal for genesis/nodekey/config errors. +Then run: ./scripts/besu/fix-all-besu-nodes.sh (optionally --no-restart first) +============================================== diff --git a/docs/07-ccip/CCIP_SENDER_CONTRACT_REFERENCE.md b/docs/07-ccip/CCIP_SENDER_CONTRACT_REFERENCE.md index ff3d9743..13840472 100644 --- a/docs/07-ccip/CCIP_SENDER_CONTRACT_REFERENCE.md +++ b/docs/07-ccip/CCIP_SENDER_CONTRACT_REFERENCE.md @@ -248,7 +248,7 @@ The CCIP Monitor service (VMID 3501) listens to these events and tracks: ## 🔗 External Links -- **Blockscout (Chain 138)**: `https://explorer.d-bis.org/address/0x105F8A15b819948a89153505762444Ee9f324684` ✅ **Use this** +- **Blockscout (Chain 138)**: `https://explorer.d-bis.org/addresses/0x105F8A15b819948a89153505762444Ee9f324684` ✅ **Use this** - **Chainlink CCIP Documentation**: https://docs.chain.link/ccip - **Source Project**: `/home/intlc/projects/smom-dbis-138` @@ -279,7 +279,7 @@ RPC_URL_138=http://192.168.11.211:8545 RPC_URL_138=https://rpc-core.d-bis.org # Block Explorer -EXPLORER_URL=https://explorer.d-bis.org/address/0x105F8A15b819948a89153505762444Ee9f324684 +EXPLORER_URL=https://explorer.d-bis.org/addresses/0x105F8A15b819948a89153505762444Ee9f324684 ``` --- diff --git a/docs/07-ccip/CW_BRIDGE_APPROACH.md b/docs/07-ccip/CW_BRIDGE_APPROACH.md index 8267e28b..e11f5e32 100644 --- a/docs/07-ccip/CW_BRIDGE_APPROACH.md +++ b/docs/07-ccip/CW_BRIDGE_APPROACH.md @@ -13,7 +13,7 @@ - **Option 1 (extend existing bridge):** Would require changing CCIPWETH9Bridge / CCIPRelayBridge to accept more than WETH9 and mint cW* in `ccipReceive`. That mixes WETH and cW* in one contract and complicates upgrades. - **Option 2 (dedicated receiver):** Use a contract that only handles cW* mint-on-receive and burn-on-send (e.g. **TwoWayTokenBridgeL2** or a minimal **CCIPReceiverCW**). Keeps WETH bridges unchanged; cW* flow is separate and easier to reason about. -**Concrete choice:** Use **TwoWayTokenBridgeL2** (or equivalent) per (chain, token) — one deployment per chain for cWUSDT and one for cWUSDC, or a generic receiver that supports multiple cW* via message data. **CompliantWrappedToken** is extended with **burnFrom** so TwoWayTokenBridgeL2’s outbound `burnFrom` works (Phase C1). +**Concrete choice:** Use **CWMultiTokenBridgeL1** on Chain 138 and **CWMultiTokenBridgeL2** on each active public mesh chain. This is the preferred implementation because one receiver address can support the full cW* family through canonical-to-mirrored token-pair configuration. **TwoWayTokenBridgeL2** remains a narrow per-token fallback. **CompliantWrappedToken** is extended with **burnFrom** so outbound burn-and-send flows work. --- @@ -21,27 +21,27 @@ 1. **User on Chain 138** locks cUSDT (or cUSDC) in a sender contract (e.g. UniversalCCIPBridge, or a dedicated c*→cW* bridge on 138). 2. **Sender** sends a CCIP message to the **destination chain** with: - - **Receiver:** Dedicated cW* receiver (e.g. TwoWayTokenBridgeL2 instance for cWUSDT on that chain). - - **Data:** `abi.encode(recipient, amount)` (same as TwoWayTokenBridgeL2). + - **Receiver:** Dedicated cW* receiver, preferably CWMultiTokenBridgeL2 on that chain. + - **Data:** `abi.encode(canonicalToken, recipient, amount)`. - **Token amounts:** Either none (lock-and-mint: 138 locks c*, destination mints cW*) or source token as specified by the bridge design. -3. **On destination chain,** the **cW* receiver**’s `ccipReceive` is called by the CCIP router. It decodes `(recipient, amount)` and calls **cWUSDT.mint(recipient, amount)** (receiver must have MINTER_ROLE on the cW* token). +3. **On destination chain,** the **cW* receiver**’s `ccipReceive` is called by the CCIP router. It decodes `(canonicalToken, recipient, amount)`, resolves the configured mirrored cW* token, and mints that token to the recipient (receiver must have MINTER_ROLE on the cW* token). 4. **User on destination** receives cWUSDT. -**Contracts implementing receive:** Dedicated cW* receiver (e.g. TwoWayTokenBridgeL2 with `mirroredToken` = cWUSDT or cWUSDC). Not CCIPWETH9Bridge / CCIPRelayBridge (they remain WETH-only). +**Contracts implementing receive:** Dedicated cW* receiver, preferably CWMultiTokenBridgeL2 with canonical-to-mirrored token pairs configured. Not CCIPWETH9Bridge / CCIPRelayBridge (they remain WETH-only). -**Contracts implementing send (138 side):** UniversalCCIPBridge (if extended for c* and destination = cW* receiver), or a dedicated “Lock c* and send CCIP” contract that configures receiver = TwoWayTokenBridgeL2 on the target chain. +**Contracts implementing send (138 side):** CWMultiTokenBridgeL1, which locks supported canonical c* tokens on Chain 138 and sends CCIP messages to the configured CWMultiTokenBridgeL2 receiver on the target chain. --- ## 3. Flow chain → 138 (burn cW* on chain, release c* on 138) -1. **User on destination chain** calls the **cW* receiver**’s outbound function (e.g. TwoWayTokenBridgeL2.**burnAndSend**(destSelector, recipient, amount)). -2. **Receiver** burns cW* from the user (`cWUSDT.burnFrom(user, amount)` — requires CompliantWrappedToken to implement **burnFrom**; see Phase C1) and sends a CCIP message to **Chain 138** with receiver = L1 bridge and data `(recipient, amount)`. +1. **User on destination chain** calls the **cW* receiver**’s outbound function (CWMultiTokenBridgeL2.**burnAndSend**(mirroredToken, destSelector, recipient, amount)). +2. **Receiver** burns cW* from the user (`cW*.burnFrom(user, amount)`) and sends a CCIP message to **Chain 138** with receiver = L1 bridge and data `(canonicalToken, recipient, amount)`. 3. **On Chain 138,** the L1 bridge (or release contract) receives the message and releases cUSDT to the recipient (e.g. transfer from escrow or mint if L1 is mintable). -**Contracts implementing burn-and-send:** Same dedicated cW* receiver (TwoWayTokenBridgeL2) that has BURNER_ROLE on the cW* token and implements **burnAndSend**. CompliantWrappedToken must expose **burnFrom** for TwoWayTokenBridgeL2. +**Contracts implementing burn-and-send:** Same dedicated cW* receiver (CWMultiTokenBridgeL2) that has BURNER_ROLE on each configured cW* token and implements **burnAndSend**. -**Contracts implementing receive on 138:** L1 bridge or release contract that holds or mints c* and credits the recipient. +**Contracts implementing receive on 138:** CWMultiTokenBridgeL1, which verifies the configured peer and releases the locked canonical c* token to the recipient. --- @@ -49,17 +49,19 @@ | Role | Contract(s) | |------|-------------| -| **cW* token (destination chain)** | CompliantWrappedToken (cWUSDT, cWUSDC). MINTER_ROLE and BURNER_ROLE granted to the dedicated receiver (e.g. TwoWayTokenBridgeL2). | -| **Receive on destination (mint cW*)** | TwoWayTokenBridgeL2 (or CCIPReceiverCW). Constructor(router, cWUSDT, feeToken). Implements ccipReceive → mint(recipient, amount). | -| **Send from destination (burn cW*, send CCIP)** | Same TwoWayTokenBridgeL2. burnAndSend → burnFrom(user) → ccipSend to 138. | -| **Send from 138 (lock c*, send CCIP)** | UniversalCCIPBridge (with c* and cW* receiver config) or dedicated lock-and-send contract. Receiver address = TwoWayTokenBridgeL2 on destination. | -| **Receive on 138 (release c*)** | L1 bridge or release contract (existing or new) that credits recipient when message received from destination chain. | +| **cW* token (destination chain)** | CompliantWrappedToken (cWUSDT, cWUSDC, and broader cW* family). MINTER_ROLE and BURNER_ROLE granted to CWMultiTokenBridgeL2. | +| **Receive on destination (mint cW*)** | CWMultiTokenBridgeL2. Constructor(sendRouter, receiveRouter, feeToken). Implements ccipReceive -> resolve mirrored token -> mint(recipient, amount). | +| **Send from destination (burn cW*, send CCIP)** | Same CWMultiTokenBridgeL2. burnAndSend(mirroredToken, ...) -> burnFrom(user) -> ccipSend to 138. | +| **Send from 138 (lock c*, send CCIP)** | CWMultiTokenBridgeL1. Receiver address = CWMultiTokenBridgeL2 on destination. | +| **Receive on 138 (release c*)** | CWMultiTokenBridgeL1 releases locked canonical c* after configured peer verification. | --- ## 5. References -- [TwoWayTokenBridgeL2.sol](../../smom-dbis-138/contracts/bridge/TwoWayTokenBridgeL2.sol) — Mint on receive, burnAndSend for outbound. +- [CWMultiTokenBridgeL1.sol](../../smom-dbis-138/contracts/bridge/CWMultiTokenBridgeL1.sol) — Chain 138 escrow, send, receive, and release side. +- [CWMultiTokenBridgeL2.sol](../../smom-dbis-138/contracts/bridge/CWMultiTokenBridgeL2.sol) — Public-chain mint, burn, and return side. +- [TwoWayTokenBridgeL2.sol](../../smom-dbis-138/contracts/bridge/TwoWayTokenBridgeL2.sol) — Per-token fallback receiver. - [CompliantWrappedToken.sol](../../smom-dbis-138/contracts/tokens/CompliantWrappedToken.sol) — mint, burn, burnFrom (Phase C1). - [CW_BRIDGE_TASK_LIST.md](../00-meta/CW_BRIDGE_TASK_LIST.md) — Full task list and phases. - [CW_DEPLOY_AND_WIRE_RUNBOOK.md](CW_DEPLOY_AND_WIRE_RUNBOOK.md) — Operator steps to deploy cW*, wire config, verify. diff --git a/docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md b/docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md index 17947c2a..4608a0b8 100644 --- a/docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md +++ b/docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md @@ -8,7 +8,7 @@ ## Prerequisites - `smom-dbis-138/.env` has `CW_BRIDGE_` set (already done from deployed bridge suite for Mainnet, Cronos, BSC, Polygon, Gnosis, Avalanche, Base, Arbitrum, Optimism). -- For **cross-chain mint** to work, the bridge at that address must either be extended to mint cW* in `ccipReceive` or you must deploy a dedicated cW* receiver (e.g. TwoWayTokenBridgeL2) and point `CW_BRIDGE_` to it; see [CW_BRIDGE_APPROACH.md](CW_BRIDGE_APPROACH.md). +- For **cross-chain mint** to work, the bridge at that address must either be extended to mint cW* in `ccipReceive` or you must deploy the dedicated cW* bridge path. The preferred path is **CWMultiTokenBridgeL1** on Chain 138 plus **CWMultiTokenBridgeL2** on each public mesh chain; see [CW_BRIDGE_APPROACH.md](CW_BRIDGE_APPROACH.md). - RPC URL and `PRIVATE_KEY` for the target chain(s). --- @@ -108,10 +108,14 @@ If using the existing CCIPRelayBridge for cW* on Mainnet, that contract is WETH- ### E2. Direct CCIP (138 → chain) -If Chain 138 uses UniversalCCIPBridge or a dedicated sender to send c* to a destination chain: +If Chain 138 uses the preferred CWMultiTokenBridgeL1 sender to send c* to a destination chain: -- Add destination config for the c* token with **receiver** = the cW* receiver on the destination (e.g. TwoWayTokenBridgeL2 address). -- Ensure the receiver on the destination has MINTER_ROLE on the cW* token and implements `ccipReceive` → `cW*.mint(recipient, amount)` (see [CW_BRIDGE_APPROACH.md](CW_BRIDGE_APPROACH.md)). +- Deploy CWMultiTokenBridgeL1 on Chain 138 with the Chain 138 send/receive routers and fee token. +- Deploy CWMultiTokenBridgeL2 on each active public mesh chain with that chain's send/receive routers and fee token. +- Configure each canonical c* token on L1 with destination selector -> CWMultiTokenBridgeL2 receiver. +- Configure each L2 canonical-to-mirrored token pair and L2 destination selector -> CWMultiTokenBridgeL1 receiver. +- Ensure the receiver on the destination has MINTER_ROLE and BURNER_ROLE on each configured cW* token. +- Freeze configured token pairs/destinations only after the canary route passes. ### E3. Test E2E @@ -130,7 +134,7 @@ If Chain 138 uses UniversalCCIPBridge or a dedicated sender to send c* to a dest | 3 | Set `CWUSDT_`, `CWUSDC_` in .env (D2). | | 4 | Update `config/token-mapping-multichain.json` `addressTo` for _cW entries (D3). | | 5 | Verify MINTER_ROLE and BURNER_ROLE on cW* for the bridge (D4). | -| 6 | If cross-chain mint is required, ensure the bridge/receiver code mints cW* in ccipReceive (Phase B or C); then wire relay/direct CCIP (E1, E2) and run E2E test (E3). | +| 6 | If cross-chain mint is required, deploy/configure CWMultiTokenBridgeL1/L2; then wire relay/direct CCIP (E1, E2) and run E2E test (E3). | --- @@ -211,5 +215,9 @@ Then run `./scripts/deployment/run-cw-remaining-steps.sh --update-mapping`. - [CW_BRIDGE_TASK_LIST.md](../00-meta/CW_BRIDGE_TASK_LIST.md) - [CW_BRIDGE_APPROACH.md](CW_BRIDGE_APPROACH.md) +- [CW_MULTITOKEN_BRIDGE_BLOCKER_REMEDIATION.md](CW_MULTITOKEN_BRIDGE_BLOCKER_REMEDIATION.md) - [CW_TOKENS_AND_NETWORKS.md](../11-references/CW_TOKENS_AND_NETWORKS.md) - [C_TO_CW_MAPPER_MAPPING.md](../04-configuration/C_TO_CW_MAPPER_MAPPING.md) +- `pnpm cw:full-readiness` — read-only readiness report for source-of-truth, indexing, bridge, and external tracker gates. +- `pnpm cw:bridge-e2e-readiness` — read-only CWMultiToken L1/L2 route and role evidence. +- `pnpm cwusdc:external-trackers` — read-only cWUSDC external tracker/indexing evidence. diff --git a/docs/07-ccip/CW_MULTITOKEN_BRIDGE_BLOCKER_REMEDIATION.md b/docs/07-ccip/CW_MULTITOKEN_BRIDGE_BLOCKER_REMEDIATION.md new file mode 100644 index 00000000..e7b28175 --- /dev/null +++ b/docs/07-ccip/CW_MULTITOKEN_BRIDGE_BLOCKER_REMEDIATION.md @@ -0,0 +1,62 @@ +# CW MultiToken Bridge Blocker Remediation + +Generated: `2026-05-09` + +## Current Evidence + +Run: + +```bash +pnpm cw:bridge-e2e-readiness +pnpm cw:full-readiness +``` + +Evidence files: + +- `reports/status/cw-multitoken-bridge-e2e-latest.json` +- `reports/status/cw-multitoken-bridge-e2e-latest.md` +- `reports/status/cw-full-operational-readiness-latest.json` +- `reports/status/cw-full-operational-readiness-latest.md` + +Current bridge state from the latest evidence: + +| Area | Status | +|---|---| +| Chain 138 CWMultiTokenBridgeL1 | Pass | +| Ethereum Mainnet CWMultiTokenBridgeL2 | Pass | +| Avalanche CWMultiTokenBridgeL2 | Pass | +| Optimism | Blocked: receiver lacks CWMultiToken canonical-to-mirrored mappings | +| Cronos | Blocked: receiver lacks CWMultiToken canonical-to-mirrored mappings | +| BSC | Blocked: receiver lacks CWMultiToken canonical-to-mirrored mappings | +| Gnosis | Blocked: receiver lacks CWMultiToken canonical-to-mirrored mappings | +| Polygon | Blocked: receiver lacks CWMultiToken canonical-to-mirrored mappings | +| Base | Blocked: receiver lacks CWMultiToken canonical-to-mirrored mappings | +| Arbitrum | Blocked: receiver lacks CWMultiToken canonical-to-mirrored mappings | +| Celo | Blocked: receiver lacks CWMultiToken canonical-to-mirrored mappings | + +## Required Fix + +For each failed chain: + +1. Deploy `CWMultiTokenBridgeL2` with that chain's send router, receive router, and fee token. +2. Update `CW_BRIDGE_` to the new CWMultiTokenBridgeL2 address. +3. Configure `canonicalToMirrored` pairs for at least `cUSDT -> cWUSDT` and `cUSDC -> cWUSDC`; use `--full-family` once all wrapped family routes are intended to be production-live. +4. Configure destination `138 -> CWMultiTokenBridgeL1`. +5. Grant `MINTER_ROLE` and `BURNER_ROLE` on each cW token to the new L2 bridge. +6. Re-run: + +```bash +pnpm cw:bridge-e2e-readiness +pnpm cw:full-readiness +``` + +## Existing Helpers + +- `smom-dbis-138/script/DeployCWMultiTokenBridgeL2.s.sol` +- `smom-dbis-138/scripts/deployment/cw-l1-bootstrap-gru-v2-ccip-routes.sh` +- `smom-dbis-138/scripts/deployment/configure-cw-public-bridge-mesh.sh` +- `scripts/deployment/run-cw-remaining-steps.sh --verify` + +## Safety Note + +Do not point `CW_BRIDGE_` at a WETH-only bridge and claim cW mint/burn support. WETH-oriented receivers can hold roles, but they do not prove c* -> cW* -> c* bridge semantics. diff --git a/docs/08-monitoring/BLOCKSCOUT_START_INSTRUCTIONS.md b/docs/08-monitoring/BLOCKSCOUT_START_INSTRUCTIONS.md index f68efd82..5b2e2d6b 100644 --- a/docs/08-monitoring/BLOCKSCOUT_START_INSTRUCTIONS.md +++ b/docs/08-monitoring/BLOCKSCOUT_START_INSTRUCTIONS.md @@ -188,8 +188,8 @@ Or manually: ### 2. Verify Individual Contracts Navigate to contract on Blockscout: -- Oracle Proxy: https://explorer.d-bis.org/address/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6 -- CCIP Router: https://explorer.d-bis.org/address/0x42DAb7b888Dd382bD5Adcf9E038dBF1fD03b4817 +- Oracle Proxy: https://explorer.d-bis.org/addresses/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6 +- CCIP Router: https://explorer.d-bis.org/addresses/0x42DAb7b888Dd382bD5Adcf9E038dBF1fD03b4817 ### 3. Check Verification Status diff --git a/docs/08-monitoring/BLOCKSCOUT_VERIFICATION_GUIDE.md b/docs/08-monitoring/BLOCKSCOUT_VERIFICATION_GUIDE.md index 64004bf4..94674588 100644 --- a/docs/08-monitoring/BLOCKSCOUT_VERIFICATION_GUIDE.md +++ b/docs/08-monitoring/BLOCKSCOUT_VERIFICATION_GUIDE.md @@ -206,7 +206,7 @@ Or run the batch script with the proxy pointing at the public explorer URL (from **Solution**: - The contract is already verified on Blockscout -- Check the contract on the explorer: `https://explorer.d-bis.org/address/` +- Check the contract on the explorer: `https://explorer.d-bis.org/addresses/` ### Issue: Blockscout API Timeout @@ -232,7 +232,7 @@ Or run the batch script with the proxy pointing at the public explorer URL (from If automated verification fails, you can verify contracts manually through the Blockscout web interface: -1. Navigate to the contract address: `https://explorer.d-bis.org/address/` +1. Navigate to the contract address: `https://explorer.d-bis.org/addresses/` 2. Click on **"Verify & Publish"** tab 3. Select verification method: - **Via Standard JSON Input** (recommended) diff --git a/docs/10-best-practices/PROXMOX_COMPLETE_RECOMMENDATIONS.md b/docs/10-best-practices/PROXMOX_COMPLETE_RECOMMENDATIONS.md index 962df805..55aeda3c 100644 --- a/docs/10-best-practices/PROXMOX_COMPLETE_RECOMMENDATIONS.md +++ b/docs/10-best-practices/PROXMOX_COMPLETE_RECOMMENDATIONS.md @@ -1,6 +1,6 @@ # Proxmox VE Complete Recommendations and Review -**Last Updated:** 2026-01-31 +**Last Updated:** 2026-05-09 **Document Version:** 1.0 **Status:** Active Documentation @@ -20,7 +20,7 @@ All pre-start tasks have been completed successfully: - ✅ Storage enabled and configured - ✅ All Proxmox services operational -**Status:** Ready to start VMs on all hosts. +**Status:** Cluster operational (**136** guests on **r630-01** … **r630-04**, **ml110** idle — **2026-05-09**). See [PROXMOX_CLUSTER_ARCHITECTURE.md](../02-architecture/PROXMOX_CLUSTER_ARCHITECTURE.md). --- @@ -41,16 +41,17 @@ All pre-start tasks have been completed successfully: ### 2. IP Address Audit ✅ COMPLETE -**Results:** -- **Total VMs/Containers:** 34 with static IPs -- **IP Conflicts:** 0 ✅ +**Results (historical IP audit — counts superseded):** +- **Cluster guests (2026-05-09):** **136** running LXC/QEMU; **ml110** **0**; see [PROXMOX_CLUSTER_ARCHITECTURE.md](PROXMOX_CLUSTER_ARCHITECTURE.md). +- **IP Conflicts:** 0 ✅ (re-verify when adding CTs) - **Invalid IPs:** 0 ✅ - **DHCP IPs:** 2 (VMIDs 3500, 3501) -**All VMs Currently On:** ml110 (192.168.11.10) +**All VMs Currently On (obsolete summary):** -**IP Allocation:** -- 192.168.11.57, .60-.64, .80, .100-.106, .112, .120, .130, .150-.156, .201-.204, .240-.242, .250-.254 +Guest placement is distributed across **r630-01** … **r630-04** — **not** consolidated on ml110. Use live inventory scripts. + +**Historical IP listing (representative ranges):** 192.168.11.57, .60-.64, .80, .100-.106, .112, .120, .130, .150-.156, .201-.204, .240-.242, .250-.254 ### 3. Storage Configuration ✅ COMPLETE @@ -83,58 +84,46 @@ All pre-start tasks have been completed successfully: | Property | Value | Status | |----------|-------|--------| | **Hostname** | ml110 | ✅ Correct | -| **Proxmox Version** | 9.1.0 | ✅ Current | +| **Proxmox Version** | **9.1.7** (kernel **6.17.13-2-pve**) | ✅ Current | | **CPU** | 6 cores @ 1.60GHz | ⚠️ Older/slower | -| **Memory** | 125GB (75% used) | ⚠️ High usage | -| **Storage** | 907GB (26% used) | ✅ Good | -| **VMs** | 34 containers | ⚠️ Overloaded | +| **Memory** | ~63 GiB reported (`maxmem`); ~3.5% used | ✅ Low scheduling load | +| **Cluster guests** | **0** LXC/QEMU (**2026-05-09**) | ✅ Idle compute | -**Recommendations:** -- Consider migrating some VMs to r630-01/r630-02 -- Monitor memory usage closely -- CPU is slower - better suited for lightweight workloads +**Recommendations:** Prefer **r630-*** nodes for new workloads; keep ml110 as quorum / lightweight only unless expanded. ### r630-01 (192.168.11.11) | Property | Value | Status | |----------|-------|--------| | **Hostname** | r630-01 | ✅ Migrated | -| **Proxmox Version** | 9.1.0 | ✅ Current | +| **Proxmox Version** | **9.1.7** | ✅ Current | | **CPU** | 32 cores @ 2.40GHz | ✅ Good | -| **Memory** | 503GB (1% used) | ✅ Excellent | -| **Storage** | 736GB available | ✅ Ready | -| **VMs** | 0 containers | ✅ Ready | +| **Memory** | ~125 GiB reported; **~66%** used | ⚠️ Monitor | +| **Cluster guests** | **57** | Primary density | -**Recommendations:** -- Ready for VM deployment -- Excellent resources available -- Can handle many VMs +**Recommendations:** Watch **thin1** / **data** pool utilization before large new disks. ### r630-02 (192.168.11.12) | Property | Value | Status | |----------|-------|--------| | **Hostname** | r630-02 | ✅ Migrated | -| **Proxmox Version** | 9.1.0 | ✅ Current | +| **Proxmox Version** | **9.1.7** | ✅ Current | | **CPU** | 56 cores @ 2.00GHz | ✅ Excellent | -| **Memory** | 251GB (2% used) | ✅ Excellent | -| **Storage** | 1.3TB+ available | ✅ Ready | -| **VMs** | Has VMs on thin4 | ⚠️ Need verification | +| **Memory** | ~125 GiB reported; **~62%** used | ⚠️ Monitor | +| **Cluster guests** | **41** | Infra / workloads | -**Recommendations:** -- Best CPU performance (56 cores) -- Has VMs on storage (need to verify) -- Ready for additional VMs +**Recommendations:** Strong CPU headroom; verify thin pool **%** on **`thin1-r630-02` … `thin6`** when provisioning. --- ## 🎯 Critical Recommendations -### 1. Verify Existing VMs on r630-02 ⚠️ HIGH PRIORITY +**Inventory note (2026-05-09):** Guests are **distributed** across the cluster; VMIDs listed below were **examples** from an older audit — confirm with [`ALL_VMIDS_ENDPOINTS.md`](../04-configuration/ALL_VMIDS_ENDPOINTS.md) / [`check-cluster-besu-inventory.sh`](../../scripts/verify/check-cluster-besu-inventory.sh). -**Issue:** Storage shows VMs exist (VMIDs: 100, 101, 102, 103, 104, 105, 130, 5000, 6200, 7800) +### 1. Verify workloads on r630-02 (periodic) -**Action Required:** +**Reference VMIDs (infra — verify live):** 100, 101, 102, 103, 104, 105, 130, 5000, 6200, 7800… ```bash ssh root@192.168.11.12 pct list @@ -211,7 +200,7 @@ pvecm nodes ### Performance Recommendations #### Workload Distribution -**Current:** All 34 VMs on ml110 (overloaded) +**Current (2026-05-09):** **136** running guests cluster-wide; **ml110** **0**; see [PROXMOX_CLUSTER_ARCHITECTURE.md](../02-architecture/PROXMOX_CLUSTER_ARCHITECTURE.md). **Recommended Distribution:** - **ml110:** Keep 10-15 lightweight/management VMs @@ -336,9 +325,13 @@ pvesh get /nodes//status | Host | CPU Cores | Memory | Storage Available | VMs | Status | |------|-----------|--------|-------------------|-----|--------| -| ml110 | 6 (slow) | 125GB (75% used) | 907GB | 34 | ⚠️ Overloaded | -| r630-01 | 32 | 503GB (1% used) | 736GB | 0 | ✅ Ready | -| r630-02 | 56 | 251GB (2% used) | 1.3TB+ | Has VMs | ✅ Ready | +| Host | CPU | Memory | Storage (summary) | Guests (2026-05-09) | Status | +|------|-----|--------|-------------------|----------------------|--------| +| ml110 | 6 (slow) | ~63 GiB reported (~3% used) | thin pools near empty | **0** | Quorum / light use | +| r630-01 | 32 | ~125 GiB (~66% used) | thin1/data — check % | **57** | Primary load | +| r630-02 | 56 | ~125 GiB (~62% used) | thin1-r630-02…6 | **41** | Heavy / infra | +| r630-03 | 32 | ~125 GiB (~62% used) | data + thin*-r630-03 | **19** | Expansion | +| r630-04 | 32 | ~125 GiB (~17% used) | data + mev-local-lvm | **19** | Capacity | | **Total** | **94** | **879GB** | **~2.4TB** | **34+** | ✅ **Ready** | --- diff --git a/docs/10-best-practices/PROXMOX_FINAL_RECOMMENDATIONS.md b/docs/10-best-practices/PROXMOX_FINAL_RECOMMENDATIONS.md index 1eb9d8d6..9d811ae9 100644 --- a/docs/10-best-practices/PROXMOX_FINAL_RECOMMENDATIONS.md +++ b/docs/10-best-practices/PROXMOX_FINAL_RECOMMENDATIONS.md @@ -1,11 +1,13 @@ # Proxmox VE Final Recommendations and Summary -**Last Updated:** 2026-01-31 +**Last Updated:** 2026-05-09 **Document Version:** 1.0 **Status:** Active Documentation --- +**Live cluster (2026-05-09):** [PROXMOX_CLUSTER_ARCHITECTURE.md](../02-architecture/PROXMOX_CLUSTER_ARCHITECTURE.md) — PVE **9.1.7**, **136** running guests, **ml110 0**; r630-01 **57**, r630-02 **41**, r630-03/04 **19** each. The sections below retain **historical** migration and audit notes; do not use them as the current placement source of truth. + **Date:** 2025-01-20 **Status:** Complete Review with Actionable Recommendations @@ -20,10 +22,8 @@ - /etc/hosts updated on both hosts ### 2. IP Address Audit - COMPLETE ✅ -- **Total VMs/Containers:** 34 with static IPs (all on ml110) -- **IP Conflicts:** 0 ✅ -- **Invalid IPs:** 0 ✅ -- **All IPs documented and verified** +- **Cluster (2026-05-09):** **136** running LXC/QEMU; static IP inventory: [ALL_VMIDS_ENDPOINTS.md](../04-configuration/ALL_VMIDS_ENDPOINTS.md) +- **IP Conflicts:** 0 ✅ (re-verify when adding CTs) ### 3. Proxmox Configuration Review - COMPLETE ✅ - All hosts reviewed @@ -58,35 +58,25 @@ sed -i 's/nodes pve2 /nodes r630-02 /' /etc/pve/storage.cfg ## 📊 Host Configuration Summary ### ml110 (192.168.11.10) -- **Status:** ✅ Operational -- **CPU:** 6 cores (older, slower) -- **Memory:** 125GB (75% used - high) -- **Storage:** local (94GB) + local-lvm (813GB, 26% used) -- **VMs:** 34 containers (all current VMs) -- **Recommendation:** Consider migrating some VMs to r630-01/r630-02 +- **Status:** ✅ Operational (cluster member) +- **CPU:** 6 cores +- **Memory:** ~63 GiB reported; ~3.5% used +- **Guests:** **0** (2026-05-09) +- **Recommendation:** Use **r630-*** for new workloads unless intentionally placing on ml110. ### r630-01 (192.168.11.11) - Previously "pve" - **Status:** ✅ Operational -- **CPU:** 32 cores @ 2.40GHz (good performance) -- **Memory:** 503GB (1% used - excellent) -- **Storage:** - - local: 536GB (0% used) - - local-lvm: Exists but needs activation - - thin1: 208GB thin pool exists -- **VMs:** 0 containers -- **Recommendation:** Enable storage, ready for VM deployment +- **CPU:** 32 cores @ 2.40GHz +- **Memory:** ~125 GiB reported; **~66%** used +- **Guests:** **57** +- **Recommendation:** Monitor **thin1**/**data** utilization before large disks. ### r630-02 (192.168.11.12) - Previously "pve2" - **Status:** ✅ Operational -- **CPU:** 56 cores @ 2.00GHz (excellent performance) -- **Memory:** 251GB (2% used - excellent) -- **Storage:** - - local: 220GB (0% used) - - thin1-thin6: 6 volume groups (~230GB each) - - **VMs Found:** VMIDs 100, 101, 102, 103, 104, 105, 130, 5000, 6200 on thin1 - - **VMs Found:** VMID 7800 on thin4 -- **VMs:** Has VMs on storage (need verification) -- **Recommendation:** Verify VMs are accessible, enable storage +- **CPU:** 56 cores @ 2.00GHz +- **Memory:** ~125 GiB reported; **~62%** used +- **Guests:** **41** (includes infra CTs such as NPMplus — see inventory docs) +- **Recommendation:** Watch **thin1-r630-02** … **thin6** pool **%**. --- @@ -365,36 +355,36 @@ qm list ## 📊 Resource Summary -| Host | CPU | Memory | Storage | VMs | Status | -|------|-----|--------|---------|-----|--------| -| ml110 | 6 cores (slow) | 125GB (75% used) | 907GB (26% used) | 34 | ⚠️ Overloaded | -| r630-01 | 32 cores | 503GB (1% used) | 536GB (0% used) | 0 | ✅ Ready | -| r630-02 | 56 cores | 251GB (2% used) | 1.4TB (thin pools) | Has VMs | ✅ Ready | +| Host | CPU | Memory (reported) | Guests (2026-05-09) | Notes | +|------|-----|-------------------|---------------------|-------| +| ml110 | 6 | ~63 GiB · ~3% used | **0** | Quorum / idle | +| r630-01 | 32 | ~125 GiB · ~66% used | **57** | Highest count | +| r630-02 | 56 | ~125 GiB · ~62% used | **41** | Thin pools active | +| r630-03 | 32 | ~125 GiB · ~62% used | **19** | | +| r630-04 | 32 | ~125 GiB · ~17% used | **19** | | -**Total Resources:** -- **CPU:** 94 cores total -- **Memory:** 879GB total -- **Storage:** ~2.8TB total -- **VMs:** 34+ (need to verify r630-02) +**Totals:** **158** logical cores in cluster; **~562 GiB** reported `maxmem` sum (API); **136** running guests. Storage is **node-local thin** — see `pvesm status` per host. --- ## 🎯 Priority Actions -### 🔴 CRITICAL (Do Before Starting VMs) -1. Enable storage on r630-01 -2. Enable storage on r630-02 -3. Verify existing VMs on r630-02 +### Historical (migration era) + +The checklist below applied when **r630** storage names and hostname references were being repaired. For **current** capacity actions, use [PROXMOX_CLUSTER_ARCHITECTURE.md](../02-architecture/PROXMOX_CLUSTER_ARCHITECTURE.md) and live `pvesm status`. + +### 🔴 CRITICAL (historical) +1. ~~Enable storage on r630-01~~ — validate pools if UI shows **disabled** stubs +2. ~~Enable storage on r630-02~~ — **thin** pools are active on **r630-02** (verify utilization) ### ⚠️ HIGH PRIORITY -1. Update cluster configuration -2. Verify all VMs are accessible -3. Test storage performance +1. Continue distributing new CTs across **r630-01** … **r630-04** using thin pool headroom +2. Reconcile any **disabled** storage rows that reference foreign node names (cosmetic in `pvesm`) ### 📋 RECOMMENDED -1. Distribute VMs across hosts -2. Implement monitoring -3. Plan VLAN migration +1. Monitor **thin pool %** on busy nodes (**r630-01**, **r630-02**) +2. Implement / maintain Prometheus/Grafana per operational stack +3. Keep [ALL_VMIDS_ENDPOINTS.md](../04-configuration/ALL_VMIDS_ENDPOINTS.md) updated when adding VMIDs --- diff --git a/docs/11-references/ALL_MAINNET_ROUTING_ENGINE.md b/docs/11-references/ALL_MAINNET_ROUTING_ENGINE.md index b93e1158..59411175 100644 --- a/docs/11-references/ALL_MAINNET_ROUTING_ENGINE.md +++ b/docs/11-references/ALL_MAINNET_ROUTING_ENGINE.md @@ -38,6 +38,9 @@ The Transaction Router + Policy Engine maps chain labels to IDs: | ALL_MAINNET | 651940 | | HUB_EVM | 1 | | TEZOS | 1729 | +| SOLANA | non-EVM (planned hops) | +| TRON | non-EVM (planned hops) | +| XRPL | non-EVM (planned hops) | ## Contract References @@ -46,8 +49,18 @@ The Transaction Router + Policy Engine maps chain labels to IDs: ## API Endpoints -- **POST /v1/routes/chain138-to-usdtz** (multi-chain-execution): Accepts `source_chain_id=651940` -- **POST /api/v1/routes/chain138-to-usdtz** (dbis_core): Same +Structural route plans (multi-hop through Ethereum hub) are implemented in **multi-chain-execution** and mirrored in **dbis_core** under `/api/v1/routes`. + +| Method | Path | Service | Purpose | +|--------|------|---------|---------| +| POST | `/v1/routes/chain138-to-usdtz` | multi-chain-execution | Tezos USDtz path; `source_chain_id` **138** or **651940** | +| GET | `/v1/routes/non-evm-families` | multi-chain-execution | Lists `tezos_usdtz`, `solana_usdc`, `tron_usdt`, `xrpl_usdc` metadata | +| POST | `/v1/routes/plan-non-evm` | multi-chain-execution | Non-EVM multi-hop plan + `quote_hints`; body `destination_family` + `destination_address` | +| POST | `/api/v1/routes/chain138-to-usdtz` | dbis_core | Same as above with optional `async_quotes` (CCIP / Plenty enrichment) | +| GET | `/api/v1/routes/non-evm-families` | dbis_core | Same listing as multi-chain-execution | +| POST | `/api/v1/routes/plan-non-evm` | dbis_core | Same planner + protocol **allowlist**; `async_quotes` supported for `tezos_usdtz` only | + +Canonical planner source: `multi-chain-execution/src/lib/non-evm-route-planner.ts` — keep `dbis_core/src/core/defi/tezos-usdtz/non-evm-route-planner.ts` in sync when editing hops. ## Related Docs diff --git a/docs/11-references/CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION.md b/docs/11-references/CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION.md index 5f8bca0c..85925c6a 100644 --- a/docs/11-references/CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION.md +++ b/docs/11-references/CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION.md @@ -1,6 +1,6 @@ # Chains & Protocols: Bridges, Ledger, and Integrations -**Last Updated:** 2026-01-29 +**Last Updated:** 2026-05-09 **Status:** Authoritative reference for bridge chains and protocol acceptance --- @@ -92,6 +92,16 @@ To move Gnosis (100), Celo (42220), and Wemix (1111) from **Config ready** to ** --- +### 1.5 Cosmos ecosystem (optional — IBC / Noble / Osmosis / CosmWasm) + +**Status:** Not a live default bridge in this matrix. Chain 138 is EVM; Cosmos connectivity is **optional** and requires explicit bridges, registry rows, and ops. + +**Canonical runbook (streams A–E, checklists, completion criteria):** [COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md](COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md) — templates: [`config/cosmos-chain138-optional/README.md`](../../config/cosmos-chain138-optional/README.md) — **gaps audit:** [COSMOS_CHAIN138_GAPS_AND_INCONSISTENCIES.md](COSMOS_CHAIN138_GAPS_AND_INCONSISTENCIES.md) + +**On-chain design hook:** `ChainRegistry` supports `ChainType.Cosmos`. Historical stub: `smom-dbis-138/archive/solidity/contracts/bridge/adapters/non-evm/CosmosAdapter.sol` (not production IBC). + +--- + ## Part 2: Protocols That Have Accepted ChainID 138 ### 2.1 Ledger App-Ethereum @@ -222,6 +232,7 @@ Ledger Live and other clients can discover ChainID 138 RPCs via Chainlist. | **CCIP** | ✅ Custom implementation | ❌ | | **Li.Fi** | ❌ | ❌ | | **Bridge Vault** | — | — | +| **Cosmos / IBC (optional)** | ⚠️ Not live by default | — | --- diff --git a/docs/11-references/CONTRACT_ADDRESSES_REFERENCE.md b/docs/11-references/CONTRACT_ADDRESSES_REFERENCE.md index bacc465c..e485064d 100644 --- a/docs/11-references/CONTRACT_ADDRESSES_REFERENCE.md +++ b/docs/11-references/CONTRACT_ADDRESSES_REFERENCE.md @@ -102,7 +102,7 @@ Contracts deployed after chain initialization: | Role | Address | Explorer (Chain 138 only) | |------|---------|---------------------------| -| **Deployer / Admin** | `0x4A666F96fC8764181194447A7dFdb7d471b301C8` | [explorer.d-bis.org/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8](https://explorer.d-bis.org/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8) | +| **Deployer / Admin** | `0x4A666F96fC8764181194447A7dFdb7d471b301C8` | [explorer.d-bis.org/addresses/0x4A666F96fC8764181194447A7dFdb7d471b301C8](https://explorer.d-bis.org/addresses/0x4A666F96fC8764181194447A7dFdb7d471b301C8) | - **Chain 138:** use the link above. **Other chains (e.g. mainnet):** use [blockscan.com](https://blockscan.com/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8) to check balances on those chains. See [EXPLORER_AND_BLOCKSCAN_REFERENCE](EXPLORER_AND_BLOCKSCAN_REFERENCE.md). @@ -167,7 +167,7 @@ Use these addresses in config and .env. **smom-dbis-138/.env** has been reconcil | UNIVERSAL_CCIP_BRIDGE | `0xCd42e8eD79Dc50599535d1de48d3dAFa0BE156F8` | | BRIDGE_ORCHESTRATOR | `0x89aB428c437f23bAB9781ff8Db8D3848e27EeD6c` | -**Multicall / Oracle Aggregator (operator to confirm):** Address `0x99b3511a2d315a497c8112c1fdd8d508d4b1e506` is documented as both. **Action:** Open [explorer](https://explorer.d-bis.org/address/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506), confirm which contract is at this slot (Multicall vs Oracle Aggregator), and document the result here (e.g. "Verified: Multicall" or "Verified: Oracle Aggregator"). See [CONTRACT_NEXT_STEPS_LIST](CONTRACT_NEXT_STEPS_LIST.md) and [OPERATOR_ACTIONS](OPERATOR_ACTIONS.md). +**Multicall / Oracle Aggregator (operator to confirm):** Address `0x99b3511a2d315a497c8112c1fdd8d508d4b1e506` is documented as both. **Action:** Open [explorer](https://explorer.d-bis.org/addresses/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506), confirm which contract is at this slot (Multicall vs Oracle Aggregator), and document the result here (e.g. "Verified: Multicall" or "Verified: Oracle Aggregator"). See [CONTRACT_NEXT_STEPS_LIST](CONTRACT_NEXT_STEPS_LIST.md) and [OPERATOR_ACTIONS](OPERATOR_ACTIONS.md). **Reconcile .env:** ~~Copy the canonical block from `.env.example` into `.env`.~~ **Done 2026-02-11.** One entry per variable; matches this table. @@ -192,7 +192,7 @@ Chain 138 WETH9 bridges (LINK fee and native ETH fee) have mainnet destination s 2. **Oracle Proxy** address is the primary address for MetaMask price feeds 3. **CCIP Router** is required for cross-chain communication 4. All addresses are on ChainID 138 -5. **On-chain confirmation:** Verify each contract at https://explorer.d-bis.org/address/
and use [BLOCKSCOUT_VERIFICATION_GUIDE](../08-monitoring/BLOCKSCOUT_VERIFICATION_GUIDE.md) for source verification. +5. **On-chain confirmation:** Verify each contract at https://explorer.d-bis.org/addresses/
and use [BLOCKSCOUT_VERIFICATION_GUIDE](../08-monitoring/BLOCKSCOUT_VERIFICATION_GUIDE.md) for source verification. 6. **.env:** Reconciled to single source of truth (one entry per variable). When adding new contract vars, keep one entry and align with this table and `.env.example`. --- diff --git a/docs/11-references/CONTRACT_NEXT_STEPS_AND_RECOMMENDATIONS_COMPLETE.md b/docs/11-references/CONTRACT_NEXT_STEPS_AND_RECOMMENDATIONS_COMPLETE.md index 4cccdaa2..91090039 100644 --- a/docs/11-references/CONTRACT_NEXT_STEPS_AND_RECOMMENDATIONS_COMPLETE.md +++ b/docs/11-references/CONTRACT_NEXT_STEPS_AND_RECOMMENDATIONS_COMPLETE.md @@ -94,7 +94,7 @@ source smom-dbis-138/.env 2>/dev/null ./scripts/verify/run-contract-verification-with-proxy.sh ``` -Manual verification: https://explorer.d-bis.org/address/
#verify-contract +Manual verification: https://explorer.d-bis.org/addresses/
#verify-contract --- @@ -163,7 +163,7 @@ Manual verification: https://explorer.d-bis.org/address/
#verify-contrac | **Deploy all phases (138)** | `cd smom-dbis-138 && ./scripts/deployment/deploy-all-phases.sh` — Skips when env set; `--all` run every phase; `--phase N` one phase; `--dry-run` preview. | | **Run all commands on Proxmox via SSH** | `./scripts/run-on-proxmox-via-ssh.sh` (optionally `--sync` to rsync repo first). Runs: on-chain check, deploy-all-phases, phoenix-deploy-api install, Blockscout verification. Set PROXMOX_HOST, PROXMOX_REPO_PATH if needed. | | Single contract bytecode check | `cast code
--rpc-url https://rpc-core.d-bis.org` | -| Explorer link | https://explorer.d-bis.org/address/
| +| Explorer link | https://explorer.d-bis.org/addresses/
| --- diff --git a/docs/11-references/CONTRACT_NEXT_STEPS_LIST.md b/docs/11-references/CONTRACT_NEXT_STEPS_LIST.md index 875f733c..ab27ce5f 100644 --- a/docs/11-references/CONTRACT_NEXT_STEPS_LIST.md +++ b/docs/11-references/CONTRACT_NEXT_STEPS_LIST.md @@ -48,7 +48,7 @@ **Last run summary:** [OPERATOR_RUN_SUMMARY](OPERATOR_RUN_SUMMARY.md) (2026-02-11). From a host without LAN/VPN: on-chain check and Blockscout were run but RPC/Blockscout unreachable; .env verified reconciled; Multicall/Oracle not confirmed (explorer timeout). - [x] **On-chain check:** Run `./scripts/verify/check-contracts-on-chain-138.sh` — **Done 2026-02-11** (26/26 OK). Re-run when new contracts are deployed. -- [ ] **Blockscout verification:** From host that can reach Blockscout: `source smom-dbis-138/.env 2>/dev/null; ./scripts/verify/run-contract-verification-with-proxy.sh`. Or verify each contract at https://explorer.d-bis.org/address/
#verify-contract. +- [ ] **Blockscout verification:** From host that can reach Blockscout: `source smom-dbis-138/.env 2>/dev/null; ./scripts/verify/run-contract-verification-with-proxy.sh`. Or verify each contract at https://explorer.d-bis.org/addresses/
#verify-contract. - [x] **Reconcile .env:** Verified 2026-02-11: `smom-dbis-138/.env` has one entry per variable and matches [CONTRACT_ADDRESSES_REFERENCE § Canonical](CONTRACT_ADDRESSES_REFERENCE.md#-canonical-source-of-truth-chain-138). No change needed. - [x] **Multicall vs Oracle at 0x99b3...:** **Done 2026-02-11.** Confirmed via RPC (`latestRoundData()` returns data, `getBlockNumber()` reverts): **Oracle Aggregator**. Documented in [CONTRACT_ADDRESSES_REFERENCE](CONTRACT_ADDRESSES_REFERENCE.md). diff --git a/docs/11-references/COSMOS_CHAIN138_GAPS_AND_INCONSISTENCIES.md b/docs/11-references/COSMOS_CHAIN138_GAPS_AND_INCONSISTENCIES.md new file mode 100644 index 00000000..a3c5ff1e --- /dev/null +++ b/docs/11-references/COSMOS_CHAIN138_GAPS_AND_INCONSISTENCIES.md @@ -0,0 +1,90 @@ +# Cosmos ↔ Chain 138 — Gaps and Inconsistencies (full inventory) + +**Last Updated:** 2026-05-09 +**Companion:** [COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md](COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md), [`config/cosmos-chain138-optional/STATUS.md`](../../config/cosmos-chain138-optional/STATUS.md) +**Purpose:** Single list of **missing implementation**, **documentation conflicts**, and **operator/config drift** relevant to the optional Cosmos program and adjacent Chain 138 surfaces. Use this for remediation planning; do not treat archive paths as live authority. + +--- + +## 1. Cosmos program — implementation gaps + +| ID | Gap | Impact | +|----|-----|--------| +| C-01 | **No production IBC path** on Chain 138 (EVM only). No Noble, Axelar, Wormhole, or native light-client consumer contracts in the active `smom-dbis-138/contracts/` compile graph for Cosmos. | Streams A–D cannot go “live” without new contracts + vendor stack. | +| C-02 | **`CosmosAdapter` is archived** (`smom-dbis-138/archive/solidity/.../CosmosAdapter.sol`), not deployed as production IBC; oracle-lock pattern only. | Any doc implying “Cosmos adapter shipped” is wrong unless scoped to archive/history. | +| C-03 | **No `forge` / bash deploy script** in repo for registering a Cosmos chain in `ChainRegistry` or deploying a revived Cosmos adapter. | Operators have no copy-paste deploy for stream A4. | +| C-04 | **No CI gate** for `config/cosmos-chain138-optional/*` (optional JSON/YAML schema validation). | Typos in future real configs may slip until manual review. | +| C-05 | **Route planner / `dbis_core`** has no checked-in Cosmos leg (Osmosis quotes, IBC denom parser) wired to the runbook’s stream B/C. | Stream B2–B4 are spec-only. | +| C-06 | **`NON_EVM_NETWORK_HEALTH_AND_LANE_STATUS`** covers Solana / Tron / XRPL only — not Cosmos Hub, Noble, or Osmosis RPC health. | Stream E3 observability gap for Cosmos legs. | +| C-07 | **Token lists / token-aggregation** have no ICS-20 denom schema or allowlist file consumed in CI (only human templates under `config/cosmos-chain138-optional/`). | Stream C governance is out-of-band. | +| C-08 | **No Keplr / Leap / Snap integration** in this monorepo for Cosmos UX. | Stream E2 remains documentation-only. | +| C-09 | **Hyperledger Phase 4** (Cacti/Fabric/Indy) still partially open per `MULTI_CHAIN_DEPLOYMENT_GUIDE.md`; CosmWasm stream D4 depends on an explicit decision. | D4 may be N/A or blocked on unrelated work. | + +--- + +## 2. Cosmos program — documentation and naming inconsistencies + +| ID | Inconsistency | Resolution hint | +|----|----------------|-----------------| +| D-01 | **Archive** `COMPLETE_MULTI_CHAIN_DEPLOYMENT.md` marks **CosmosAdapter** as “Complete” while **current** `MULTI_CHAIN_DEPLOYMENT_GUIDE.md` leaves Cosmos as unchecked Phase 3 and points to the optional runbook. | Treat archive as historical; add banner in archive file or link to runbook (optional cleanup). | +| D-02 | Runbook **§7 E6** says “adopted subset of **A–D**” for closure; streams are now **A–E** (E is closure). | Wording: “product streams A–D + stream E closure” to avoid implying E is optional when anything is live. | +| D-03 | `config/cosmos-chain138-optional/cross-cutting.example.md` table still says “Streams adopted (subset of **A–D**)”. | Align to A–D product + E closure. | +| D-04 | **`.cursor/rules/project-doc-and-deployment-refs.mdc`** description line still says deployment phases **A–D** only (Cosmos is optional, not a phase letter). | Cosmetic; optional edit to “A–D + optional Cosmos runbook”. | +| D-05 | **GALATIC_SUMMARY** §1.2 table still lists **DODOPMMIntegration** at `0x5BDc62f1…` while workspace canonical token/PMM rule and on-chain probe narrative use **live traded** `0x86ADA6Ef…` (parallel stack `0x5BDc62f1…` documented as seeded / different role). | **Critical:** Reconcile `GALATIC_SUMMARY`, `POOL_ACCESS_DASHBOARD_API_MCP`, token-aggregation env examples, and `CONTRACT_ADDRESSES_REFERENCE` with [EXPLORER_TOKEN_LIST_CROSSCHECK.md](EXPLORER_TOKEN_LIST_CROSSCHECK.md) section 8 and [ADDRESS_MATRIX_AND_STATUS.md](ADDRESS_MATRIX_AND_STATUS.md). | + +--- + +## 3. Chain 138 global gaps (affects operator trust, not only Cosmos) + +| ID | Issue | Evidence / notes | +|----|--------|------------------| +| G-01 | **`config/smart-contracts-master.json`** is referenced as the master JSON in `CONTRACT_ADDRESSES_REFERENCE.md` and `load-project-env.sh`, but the file is **often absent** from checkout (see `reports/inventory/deployed-contracts-by-network.md`). | Single-source-of-truth claim in docs is weaker than reality; `.env` drives most scripts. | +| G-02 | **`CCIP_SENDER` address drift:** canonical doc lists **`0x105F8A15b819948a89153505762444Ee9f324684`**; `smom-dbis-138/.env` may set **`CCIP_SENDER=0x958Ae39Fb387A3653402a7eDA6847629b6F6F195`**. Verification then skips CCIPSender (“no bytecode” at 958…). | Reconcile `.env` with `CONTRACT_ADDRESSES_REFERENCE.md` and `scripts/verify-contracts-blockscout.sh` defaults. | +| G-03 | **`CCIP_ROUTER`:** `CONTRACT_ADDRESSES_REFERENCE` lists **`0x42DAb7b888Dd382bD5Adcf9E038dBF1fD03b4817`**; canonical `.env` reconcile output (completable step) has shown **`0x89EC6574eeAC72Ed1b93DfCa4DB43547C8367FF0`**. | Same as G-02: pick canonical row and align docs + env. | +| G-04 | **`CCIPWETH9Bridge` on 138:** doc line `0xcacfd227…` vs reconcile snippet **`0x9cba0D04…`** (from operator env templates). | Confirm on explorer which address holds live WETH9 bridge; fix the other surface. | +| G-05 | **`CONTRACT_ADDRESSES_REFERENCE`** notes **Multicall** and **Oracle Aggregator** share **`0x99b3511a2d315a497c8112c1fdd8d508d4b1e506`** with a warning — operator confusion risk. | Explorer slot verification + doc clarification. | +| G-06 | **`OPERATOR_READY_CHECKLIST`** historical note: WETH10 “not a smart contract” vs later verification success. | May be stale; refresh or date-stamp. | +| G-07 | **61-address on-chain check** includes a fixed list; it does **not** assert consistency between that list and `CONTRACT_ADDRESSES_REFERENCE` / `.env` for every CCIP variant. | Add optional cross-check script or extend check script. | + +--- + +## 4. In-repo contradictions (PMM “canonical” stack) + +Two different “canonical” narratives coexist: + +- **Narrative A (many deployment docs, GALATIC, CONTRACT_ADDRESSES, thirdweb insight examples):** `DODOPMMIntegration = 0x5BDc62f1ae7D630c37A8B363a1d49845356Ee72d` as corrected stack; pools such as `0xff8d3b8f…`. +- **Narrative B (workspace rules, EXPLORER_TOKEN_LIST_CROSSCHECK section 8 on-chain table, cursor `chain138-tokens-and-pmm.mdc`):** **Live traded** integration **`0x86ADA6Ef91A3B450F89f2b751e93B1b7A3218895`** with listed pools; **`0x5BDc62f1…`** described as parallel / different immutables / not for dApp wiring. + +Until reconciled, **any automation** (PMM mesh, liquidity scripts, MCP pool indexer env) may point at the wrong integration. + +--- + +## 5. Optional Cosmos templates — limitations + +| ID | Limitation | +|----|------------| +| T-01 | All `*.example.*` files use **TODO** placeholders — intentionally not production. | +| T-02 | No JSON Schema (`.schema.json`) for `denom-trace.example.json` — validation is manual. | +| T-03 | **STATUS.md** “Doc complete” for A–E means **runbook + templates**, not mainnet IBC. | + +--- + +## 6. Suggested remediation order (repo hygiene) + +1. **Resolve PMM dual-canon (section 4)** in `CONTRACT_ADDRESSES_REFERENCE`, `GALATIC_SUMMARY`, env examples, and aggregation docs — align with `EXPLORER_TOKEN_LIST_CROSSCHECK.md` section 8 after one on-chain proof export. +2. **Reconcile CCIP** env vars vs `CONTRACT_ADDRESSES_REFERENCE` (G-02–G-04). +3. **Restore or retire** `config/smart-contracts-master.json` claim (G-01). +4. **Fix wording** D-02, D-03, optional D-01 archive banner. +5. **Cosmos implementation** only after (1)–(3): deploy path, then C-03 script, then C-04 CI. + +--- + +## 7. Related documents + +- [COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md](COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md) +- [CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION.md](CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION.md) +- [CONTRACT_ADDRESSES_REFERENCE.md](CONTRACT_ADDRESSES_REFERENCE.md) +- [ADDRESS_MATRIX_AND_STATUS.md](ADDRESS_MATRIX_AND_STATUS.md) +- [EXPLORER_TOKEN_LIST_CROSSCHECK.md](EXPLORER_TOKEN_LIST_CROSSCHECK.md) +- [NON_EVM_NETWORK_HEALTH_AND_LANE_STATUS.md](NON_EVM_NETWORK_HEALTH_AND_LANE_STATUS.md) +- [`config/cosmos-chain138-optional/STATUS.md`](../../config/cosmos-chain138-optional/STATUS.md) diff --git a/docs/11-references/COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md b/docs/11-references/COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md new file mode 100644 index 00000000..bd0ca8c9 --- /dev/null +++ b/docs/11-references/COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md @@ -0,0 +1,154 @@ +# Cosmos Ecosystem and Chain 138 — Optional Integrations Runbook + +**Last Updated:** 2026-05-09 +**Status:** Optional program — streams **A–E** fully documented; live bridges remain operator-adopted (see `config/cosmos-chain138-optional/STATUS.md`) +**Audience:** Architecture, bridge operators, compliance, routing/backend owners + +--- + +## 1. Scope and facts + +- **Chain 138** is an **EVM (Besu)** network. It is **not** a Cosmos SDK zone and does **not** run the Cosmos stack natively. +- **“Full Cosmos integration”** therefore means **bridges, attestations, routing, wallets, and ops** that connect **IBC-capable chains** to **138 contracts and policies**, not enabling IBC on 138 itself. +- **Canonical Chain 138 tokens and PMM** remain per [EXPLORER_TOKEN_LIST_CROSSCHECK.md](EXPLORER_TOKEN_LIST_CROSSCHECK.md) (sections 5 and 8) and [ADDRESS_MATRIX_AND_STATUS.md](ADDRESS_MATRIX_AND_STATUS.md). Any Cosmos-side asset must map to those policies explicitly. +- **Known cross-repo conflicts** (PMM addresses, CCIP env drift, missing `smart-contracts-master.json`): [COSMOS_CHAIN138_GAPS_AND_INCONSISTENCIES.md](COSMOS_CHAIN138_GAPS_AND_INCONSISTENCIES.md) (section 3–4 are global, not Cosmos-only). +- **On-chain today:** `ChainRegistry` includes `ChainType.Cosmos` for metadata registration. A historical **`CosmosAdapter`** stub lives under `smom-dbis-138/archive/solidity/contracts/bridge/adapters/non-evm/CosmosAdapter.sol` (oracle-confirmed lock pattern; **not** production IBC). See [MASTER_CONTRACTS_AND_INFRASTRUCTURE_LIST.md](../../smom-dbis-138/docs/MASTER_CONTRACTS_AND_INFRASTRUCTURE_LIST.md). + +--- + +## 2. Preconditions (all streams) + +Complete before treating any stream as “in progress”: + +| # | Gate | Owner | +|---|------|--------| +| P1 | **Bridge class chosen** per corridor: trust-minimized (e.g. general-message bridge vendor), CCTP-style where applicable, or oracle/custodial (closest to archived adapter) | Architecture + risk | +| P2 | **Allowlist policy** for Cosmos denoms and bridge contracts (who may mint/list on 138) | Compliance + engineering | +| P3 | **Relayer / vendor SLA** or explicit “public relayer only” acceptance | Ops | +| P4 | **Incident playbooks** (stuck IBC packet, bridge pause, oracle disagreement) | Ops | +| P5 | **Explorer / indexer** plan: Blockscout covers 138 EVM only; Cosmos legs need Mintscan (or equivalent) and/or internal indexer | Platform | + +--- + +## 3. Stream A — Noble (and similar) native Cosmos stable corridor + +**Objective:** Recognized native Cosmos stablecoin (e.g. Noble-issued USDC) maps cleanly to **138 canonical stables** or wrapped representations, with auditability. + +| Step | Task | Done when | +|------|------|-----------| +| A1 | Select **source chain** (e.g. Noble) and **bridge stack** (vendor contracts + supported routes) | Written decision + vendor docs linked in internal wiki | +| A2 | Define **138-side mint/burn or lock/mint** contract(s) and roles (admin, pauser, oracle or light-client consumer) | Spec reviewed; addresses reserved in deployment tracker | +| A3 | Map **denom → 138 token address** (single canonical mapping table; no duplicate “explorer” addresses) | Row in repo config or registry doc + CI check if applicable | +| A4 | Register **non-EVM metadata** in `ChainRegistry` (`ChainType.Cosmos`, `chainIdentifier`, `adapter`, `additionalData` for channel/bridge IDs) | On-chain tx executed; [CONTRACT_ADDRESSES_REFERENCE.md](CONTRACT_ADDRESSES_REFERENCE.md) updated | +| A5 | **Liquidity** plan: seed PMM/vault or rely on external DEX only | Funding signed off | +| A6 | **E2E dry path**: small amount round-trip (Cosmos → 138 → Cosmos or to declared sink) | Tx hashes recorded; monitoring dashboards live | +| A7 | Update [CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION.md](CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION.md) and [GALATIC_SUMMARY.md](../GALATIC_SUMMARY.md) | PR merged | + +--- + +## 4. Stream B — Osmosis (and other Cosmos DEX) liquidity and routing + +**Objective:** Quotes and optional **execution legs** on Cosmos DEXes appear in the same **route-planning** discipline as other non-EVM legs (compare [TEZOS_USDTZ_INTEGRATION.md](TEZOS_USDTZ_INTEGRATION.md): route classes, not native 138 DEX). + +| Step | Task | Done when | +|------|------|-----------| +| B1 | Choose **read-only vs executable** integration (quotes only vs signed swaps on Osmosis) | Flag in route engine config | +| B2 | **Denom/path parser** for pool routes and IBC hops | Unit tests + sample traces | +| B3 | Integrate **slippage, timeout, and failure** handling for IBC + DEX latency | Documented limits in API/runbook | +| B4 | If using **aggregator stack**: align with existing Axelar/Squid-class patterns where applicable ([BRIDGE_CHAINS_IMPLEMENTATION_COMPLETE.md](BRIDGE_CHAINS_IMPLEMENTATION_COMPLETE.md)) | Code path reviewed | +| B5 | **Security review** for any server-side signing or custody | Sign-off recorded | + +--- + +## 5. Stream C — Generic IBC token (ICS-20 / app tokens) + +**Objective:** Any **allowlisted** IBC-traced asset can be registered and routed, not only USDC. + +| Step | Task | Done when | +|------|------|-----------| +| C1 | Define **IBC hash / full trace** schema stored in `ChainMetadata.additionalData` (or off-chain registry mirrored on-chain) | Schema doc + one example on testnet | +| C2 | **Tiering:** Tier-1 (manual governance allowlist) vs Tier-2 (automated with stricter caps) | Policy published | +| C3 | Per-token: **bridge + DEX + 138 contract** triple documented | Matrix row per denom | +| C4 | **User-facing warnings** for non-Noble / long trace paths | UI/API copy merged | +| C5 | Align token list / explorer publication with [EXPLORER_TOKEN_LIST_CROSSCHECK.md](EXPLORER_TOKEN_LIST_CROSSCHECK.md) | No conflicting canonical rows | + +--- + +## 6. Stream D — CosmWasm application ↔ Chain 138 + +**Objective:** One or more **named CosmWasm contracts** (e.g. DEX, lending, ID) integrate via explicit messages, not “all of CosmWasm.” + +| Step | Task | Done when | +|------|------|-----------| +| D1 | Pin **contract address(es), code ID, migrate policy** | Version doc | +| D2 | Choose **control path**: ICA/ICQ, general-message bridge to EVM, or off-chain coordinator + oracle | Sequence diagrams stored | +| D3 | **138-side counterpart** (vault, adapter, settlement hook) deployed or extended | Addresses in CONTRACT_ADDRESSES_REFERENCE | +| D4 | **Hyperledger** path only if required: Cacti/Firefly alignment per [MULTI_CHAIN_DEPLOYMENT_GUIDE.md](../../smom-dbis-138/docs/deployment/MULTI_CHAIN_DEPLOYMENT_GUIDE.md) Phase 4 | Explicit decision recorded | +| D5 | Wasm **CI/build** and upgrade runbook (outside or inside monorepo) | Maintainer sign-off | + +--- + +## 7. Stream E — Cross-cutting layers and program closure + +**Objective:** Relayers, wallets, indexing, audits, rate limits, and **formal program closure** so Cosmos legs are operable and governable, not only contract code. + +| Step | Task | Done when | +|------|------|-----------| +| E1 | **IBC relayers:** channels, fees, redundancy, key custody documented | Table in ops doc or [`config/cosmos-chain138-optional/cross-cutting.example.md`](../../config/cosmos-chain138-optional/cross-cutting.example.md) filled for live | +| E2 | **Wallets:** Keplr / Leap / MetaMask Snaps (if used) user flows documented | Links in cross-cutting doc | +| E3 | **Indexing:** Cosmos txs and IBC events in SIEM or internal logs | Dashboard or log path recorded | +| E4 | **Audits:** per bridge deployment and per new token class | Report URLs stored | +| E5 | **Rate limits:** API and on-chain caps for mint/burn | Limits published to integrators | +| E6 | **Program closure:** P1–P5 signed; adopted **product streams A–D** named; stream **E** (E1–E6) complete for adopted scope; `CHAINS_AND_PROTOCOLS` + `GALATIC_SUMMARY` updated for **live only**; annual review owner | [`config/cosmos-chain138-optional/STATUS.md`](../../config/cosmos-chain138-optional/STATUS.md) reflects live vs doc-only | + +**Templates (copy before live use):** [`config/cosmos-chain138-optional/README.md`](../../config/cosmos-chain138-optional/README.md) + +--- + +## 8. Suggested implementation order (still optional) + +1. Preconditions (section 2) +2. Stream A (one stable corridor end-to-end) +3. Stream C on top of the same bridge (allowlist + schema) +4. Stream B (quotes, then optional execution) +5. Stream D (first CosmWasm app as reference) +6. Stream E (relayers through program closure) — typically **last**, but E1–E3 planning can start in parallel + +--- + +## 9. Definition of “complete” for this program + +The **program** is complete when: + +- Every stream (**A–E**) that the organization **chooses to adopt** has all its rows in sections 3–7 marked done, with **on-chain or operational evidence** (tx hashes, run URLs, audit PDFs) referenced from [ADDRESS_MATRIX_AND_STATUS.md](ADDRESS_MATRIX_AND_STATUS.md) or a linked ops doc; and +- [CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION.md](CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION.md) reflects **live** vs **not used** for Cosmos-related bridges; and +- Deferred streams are explicitly listed as **out of scope** in [`config/cosmos-chain138-optional/STATUS.md`](../../config/cosmos-chain138-optional/STATUS.md) or in [PHASE_D_OPTIONAL_CHECKLIST.md](../03-deployment/PHASE_D_OPTIONAL_CHECKLIST.md). + +**Repo doc-and-template completion (no live bridge implied):** Streams A–E are **fully specified** in this runbook plus `config/cosmos-chain138-optional/*`; see `STATUS.md` for doc vs live matrix. + +Streams explicitly **not** adopted remain **optional**; no further work is required. + +--- + +## 10. Repo map (quick) + +| Item | Location | +|------|----------| +| Templates + status matrix | `config/cosmos-chain138-optional/` | +| `ChainType.Cosmos` | `smom-dbis-138/contracts/registry/ChainRegistry.sol` | +| Historical Cosmos adapter | `smom-dbis-138/archive/solidity/contracts/bridge/adapters/non-evm/CosmosAdapter.sol` | +| Multi-chain Phase 3 checklist | `smom-dbis-138/docs/deployment/MULTI_CHAIN_DEPLOYMENT_GUIDE.md` | +| Non-EVM adapter backlog | `smom-dbis-138/docs/deployment/REMAINING_TASKS_COMPLETE_LIST.md` (`BRG-DEP-005`) | +| Bridge reference (EVM-focused) | This directory, `CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION.md` | +| Tezos route pattern (analogy) | `TEZOS_USDTZ_INTEGRATION.md` | + +--- + +## 11. Related documents + +- [CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION.md](CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION.md) +- [TEZOS_USDTZ_INTEGRATION.md](TEZOS_USDTZ_INTEGRATION.md) +- [BRIDGE_CHAINS_IMPLEMENTATION_COMPLETE.md](BRIDGE_CHAINS_IMPLEMENTATION_COMPLETE.md) +- [NON_EVM_NETWORK_HEALTH_AND_LANE_STATUS.md](NON_EVM_NETWORK_HEALTH_AND_LANE_STATUS.md) +- [EXPLORER_TOKEN_LIST_CROSSCHECK.md](EXPLORER_TOKEN_LIST_CROSSCHECK.md) +- [COSMOS_CHAIN138_GAPS_AND_INCONSISTENCIES.md](COSMOS_CHAIN138_GAPS_AND_INCONSISTENCIES.md) — gaps, doc conflicts, env drift, PMM dual-canon diff --git a/docs/11-references/CW_TOKENS_AND_NETWORKS.md b/docs/11-references/CW_TOKENS_AND_NETWORKS.md index efcde856..943fbc9d 100644 --- a/docs/11-references/CW_TOKENS_AND_NETWORKS.md +++ b/docs/11-references/CW_TOKENS_AND_NETWORKS.md @@ -2,20 +2,20 @@ **Purpose:** Single reference for all compliant wrapped tokens (cW*) and the networks where they are defined or deployed. -**Source of truth:** `config/token-mapping-multichain.json` (`cToCwSymbolMapping`), `smom-dbis-138/script/deploy/DeployCWTokens.s.sol`, and `smom-dbis-138/.env` (for recorded addresses). +**Source of truth:** `config/token-mapping-multichain.json` (`cToCwSymbolMapping`), `cross-chain-pmm-lps/config/deployment-status.json`, `reports/status/cw-mesh-deployment-matrix-latest.json`, `smom-dbis-138/services/token-aggregation/src/config/canonical-tokens.ts`, `smom-dbis-138/script/deploy/DeployCWTokens.s.sol`, and `smom-dbis-138/.env` (for recorded addresses). --- ## 1. All cW* tokens and deployability -All 12 cW* tokens are deployable via `DeployCWTokens.s.sol` (env flags `DEPLOY_CWUSDT=1`, `DEPLOY_CWEURC=1`, etc.; default all 1). Set `DEPLOY_*=0` to skip a token. +All 12 primary GRU cW* tokens are deployable via `DeployCWTokens.s.sol` (env flags `DEPLOY_CWUSDT=1`, `DEPLOY_CWEURC=1`, etc.; default all 1). Set `DEPLOY_*=0` to skip a token. | cW* Token | Deploy script | Networks deployable (script) | Deployed on (addresses in .env) | |-----------|---------------|------------------------------|----------------------------------| -| **cWUSDT** | Yes | 1, 25, 56, 137, 100, 43114, 8453, 42161, 10 | All 9 chains (Mainnet, Cronos, BSC, Polygon, Gnosis, Avalanche, Base, Arbitrum, Optimism) | -| **cWUSDC** | Yes | 1, 25, 56, 137, 100, 43114, 8453, 42161, 10 | All 9 chains | -| **cWEURC** | Yes | 1, 25, 56, 137, 100, 43114, 8453, 42161, 10 | BSC, Polygon, Gnosis, Avalanche, Base, Optimism (Mainnet/Cronos/Arbitrum: deploy failed nonce/gas) | -| **cWEURT** | Yes | Same | Same as cWEURC | +| **cWUSDT** | Yes | 1, 10, 56, 100, 137, 8453, 42161, 42220, 43114 (+ optional tooling for `25`) | Active on nine promoted public mesh chains (excludes Cronos `25`) | +| **cWUSDC** | Yes | Same | Same | +| **cWEURC** | Yes | Same | Same | +| **cWEURT** | Yes | Same | Same | | **cWGBPC** | Yes | Same | Same | | **cWGBPT** | Yes | Same | Same | | **cWAUDC** | Yes | Same | Same | @@ -37,11 +37,12 @@ All 12 cW* tokens are deployable via `DeployCWTokens.s.sol` (env flags `DEPLOY_C | 56 | BSC (BNB Chain) | | 100 | Gnosis Chain | | 137 | Polygon | +| 42220 | Celo | | 43114 | Avalanche C-Chain | | 8453 | Base | | 42161 | Arbitrum One | -Chains **42220** (Celo) and **1111** (Wemix) are in the token-mapping file for c*→cW* mapping but are not in the current `deploy-tokens-and-weth-all-chains-skip-canonical.sh` chain list for cW* deployment. **651940** (ALL Mainnet) is env-validation only; no cW* deploy from this repo. +Chain **1111** (Wemix) is in the token-mapping file and has gas-scaffold entries, but it is not promoted as a live cW settlement surface because the current Chain 138 CCIP router still rejects the Wemix selector. **651940** (ALL Mainnet) is a separate Alltra corridor: bidirectional bridge inventory is published, but it is not a public cW mesh destination in the current canonical registry. --- @@ -49,7 +50,7 @@ Chains **42220** (Celo) and **1111** (Wemix) are in the token-mapping file for c | Chain(s) | Bridge / receiver | Bridge code mints cW*? | Notes | |----------|-------------------|------------------------|-------| -| All (1, 25, 56, 137, 100, 43114, 8453, 42161, 10) | CCIPRelayBridge (Mainnet), CCIPWETH9_BRIDGE_* (others) | **No** | Current suite is WETH-only; `ccipReceive` only transfers the received token. Granting MINTER/BURNER to these addresses allows DeployCWTokens to run but does not enable cross-chain mint until the receiver is extended or a dedicated cW* receiver (e.g. TwoWayTokenBridgeL2) is deployed. See [CW_BRIDGE_APPROACH.md](../07-ccip/CW_BRIDGE_APPROACH.md) and [CW_BRIDGE_TASK_LIST.md](CW_BRIDGE_TASK_LIST.md). | +| Active cW mesh chains (nine-chain promoted surface: 1, 10, 56, 100, 137, 8453, 42161, 42220, 43114) | CCIPRelayBridge (Mainnet), CCIPWETH9_BRIDGE_* (others) | **No, for cW mint/burn semantics** | Current bridge suite is WETH-oriented; `ccipReceive` only transfers the received token. cW token contracts, roles, PMM pools, and Uniswap V2 mesh evidence are active on the nine-chain set, but do not claim full c*<->cW* mint/burn E2E until dedicated cW receivers are deployed and proof transfers pass. Cronos `25` may appear in legacy wave-1 artifacts; it is **not** part of the promoted nine-chain public `cW*` count. See [CW_BRIDGE_APPROACH.md](../07-ccip/CW_BRIDGE_APPROACH.md) and [CW_BRIDGE_TASK_LIST.md](CW_BRIDGE_TASK_LIST.md). | | After Phase B or C | Extended bridge or TwoWayTokenBridgeL2 / CCIPReceiverCW | **Yes** (when implemented) | Per [CW_BRIDGE_APPROACH.md](../07-ccip/CW_BRIDGE_APPROACH.md), Option 2 (dedicated receiver) is chosen; deploy TwoWayTokenBridgeL2 or equivalent per chain and point `CW_BRIDGE_` to it for cW* mint/burn. | --- @@ -72,7 +73,7 @@ Chains **42220** (Celo) and **1111** (Wemix) are in the token-mapping file for c ## 4. References -- [C_TO_CW_MAPPER_MAPPING.md](../04-configuration/C_TO_CW_MAPPER_MAPPING.md) — c*→cW* symbol and address mapping for the mapper. -- [TOKENS_DEPLOYER_DEPLOYED_ON_OTHER_CHAINS.md](TOKENS_DEPLOYER_DEPLOYED_ON_OTHER_CHAINS.md) — Deployer tokens on other chains; cW* deploy path. -- [config/token-mapping-multichain.json](../../config/token-mapping-multichain.json) — `cToCwSymbolMapping` and per-pair `_cW` tokens. -- **[CW_BRIDGE_TASK_LIST.md](../00-meta/CW_BRIDGE_TASK_LIST.md)** — Detailed task list: note review, checks performed, and phases (bridge extension vs dedicated receiver, deploy cW*, wire config, E2E). +- [C_TO_CW_MAPPER_MAPPING.md](../04-configuration/C_TO_CW_MAPPER_MAPPING.md) - c* to cW* symbol and address mapping for the mapper. +- [TOKENS_DEPLOYER_DEPLOYED_ON_OTHER_CHAINS.md](TOKENS_DEPLOYER_DEPLOYED_ON_OTHER_CHAINS.md) - Deployer tokens on other chains; cW* deploy path. +- [config/token-mapping-multichain.json](../../config/token-mapping-multichain.json) - `cToCwSymbolMapping` and per-pair `_cW` tokens. +- **[CW_BRIDGE_TASK_LIST.md](../00-meta/CW_BRIDGE_TASK_LIST.md)** - Detailed task list: note review, checks performed, and phases (bridge extension vs dedicated receiver, deploy cW*, wire config, E2E). diff --git a/docs/11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md b/docs/11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md new file mode 100644 index 00000000..d69c21b7 --- /dev/null +++ b/docs/11-references/DEFILLAMA_DFIO_META_MAIN_DODO_ADAPTER_TVL.md @@ -0,0 +1,58 @@ +# DefiLlama DODO adapter — `dfio_meta_main` TVL breakdown + +**Purpose:** Explain why the DODO protocol TVL **per-chain breakdown** can show **`dfio_meta_main` (Chain 138, SDK key `dfio_meta_main`) as `0.00`** while other chains (BSC, Ethereum, Avax, …) show non-zero balances that still sum to a plausible **total** (for example ~12M when meta-main is missing from the priced subtotal). + +**Operator replay (full procedure):** [../00-meta/METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md](../00-meta/METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md) (section 3 — DefiLlama fork, test, PR tracking, post-merge updates). + +**Upstream tracking (open PR):** [DefiLlama/DefiLlama-Adapters#19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198) — *Add dfio_meta_main (chain 138) to DODO adapter + token mappings* (head: `Defi-Oracle-Meta-Blockchain:feat/chain-138-dfio-meta-main-dodo`). **GitHub `state` was verified `OPEN` on 2026-05-10** — keep using the PR URL as source of truth; update this note when it merges or is replaced. + +**Production `defillama.com` picks this up when that PR merges** (plus their usual indexer lag). + +**Canonical DefiLlama ↔ Chain 138 map:** [../04-configuration/defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md](../04-configuration/defillama/CHAIN138_DEFILLAMA_ECOSYSTEM_MAP.md). + +**Provider matrix row (DeFiLlama):** [../04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md](../04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md) (external repos table, DeFiLlama row). + +## Symptom + +Example breakdown shape: + +- `bsc`, `ethereum`, `avax`, … — non-zero. +- **`dfio_meta_main` — `0.00`.** +- **`total`** — equals the sum of the priced chains only (e.g. ~12.36M), so it **looks** as if meta-main contributes nothing. + +That pattern usually means **Chain 138 was not fully wired into the DODO TVL path** (missing chain key, missing hub-stable **`fixBalancesTokens`** entries, or no DVM factory/`fromBlock` slice for 138), so **`dfio_meta_main` USD attribution is wrong or absent** — not that DODO necessarily has zero inventory on 138. + +**CI / comment tables:** [PR #19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198) notes that workflow summary tables can **abbreviate** per-chain USD; a small slice next to a large total may show as **`0.00`** in that summary even when the full log or a local run shows a non-zero **`dfio_meta_main`** line — use the full workflow log or local test below when auditing. + +## Root cause (adapter) + +Until **`dfio_meta_main`** is integrated end-to-end in [DefiLlama-Adapters](https://github.com/DefiLlama/DefiLlama-Adapters), DODO TVL on Chain 138 does not flow through the same **chain registry + token price/fix-up + pool discovery** path as the other networks. [PR #19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198) adds, per its description: + +- `projects/helper/chains.json` — **`dfio_meta_main`** +- `projects/helper/tokenMapping.js` — **`fixBalancesTokens.dfio_meta_main`** (canonical **cUSDT** / **cUSDC** → priced IDs for USD valuation) +- `projects/dodo/index.js` — DODO V2 **DVM** on **`dfio_meta_main`** (factory `0xc93870594C7f83A0aE076c2e30b494Efc526b68E`, **`fromBlock` `3510162`**) + +The exact balance helper (`sumTokens` vs `sumTokens2`, etc.) is whatever lands in **`master`** after review; the important operator signal is **merge [PR #19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198)** for integrated 138 coverage. + +## Fix (maintainer / fork) + +Track and land **[DefiLlama/DefiLlama-Adapters#19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198)** (or an equivalent maintainer commit that carries the same three files). Fork for pushes: [Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters](https://github.com/Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters). + +After the change is in your clone, a local check should show a dedicated **`dfio_meta_main`** line (magnitude depends on live on-chain DODO custody and Llama pricing): + +```bash +cd DefiLlama-Adapters && node test.js projects/dodo/index.js +``` + +Look for `--- dfio_meta_main ---` in the output. A **very large** USD number can occur if large hub stable balances sit in counted contracts and price feeds resolve. + +## “Resolved” for operators + +| Surface | Meaning | +|--------|---------| +| **Fork / PR branch** matching [#19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198) | **Resolved** for local adapter output; **`dfio_meta_main` should not be missing** from the integrated DODO path for that reason. | +| **defillama.com** public UI | **Resolved** when [#19198](https://github.com/DefiLlama/DefiLlama-Adapters/pull/19198) is **merged** into [DefiLlama/DefiLlama-Adapters](https://github.com/DefiLlama/DefiLlama-Adapters) `master` (or whatever branch production indexes), then after their deploy lag. | + +## Machine-readable touchpoints + +[`config/defillama-chain138-touchpoints.json`](../../config/defillama-chain138-touchpoints.json) — includes **`upstreamPullRequest`**, **`upstreamPullRequestState`**, **`upstreamPullRequestStateAsOf`**, **`upstreamPullRequestLastGhCheckUtc`**, and under **`docs`**: **`dodoDfioMetaMainTvlNote`**, **`masterReference`**, **`repositoriesAndPrs`** (update when GitHub state or paths change). diff --git a/docs/11-references/DEPLOYED_TOKENS_BRIDGES_LPS_AND_ROUTING_STATUS.md b/docs/11-references/DEPLOYED_TOKENS_BRIDGES_LPS_AND_ROUTING_STATUS.md index 035d2348..85f9ab42 100644 --- a/docs/11-references/DEPLOYED_TOKENS_BRIDGES_LPS_AND_ROUTING_STATUS.md +++ b/docs/11-references/DEPLOYED_TOKENS_BRIDGES_LPS_AND_ROUTING_STATUS.md @@ -1,6 +1,6 @@ # Deployed Tokens, Bridges, DODO/Uniswap LPs — Status & Complete Routing Map -**Last Updated:** 2026-04-21 +**Last Updated:** 2026-05-09 **Purpose:** Single reference for (1) deployed tokens/coins and bridges per destination network, (2) DODO and Uniswap LPs with addresses, and (3) a complete mapping of all possible routes for routing **to** and **from** each chain. > Audit note (2026-04-19): this doc was synced against the current machine-readable deployment graph and generated mesh reports. When this doc disagrees with older narrative runbooks, prefer `cross-chain-pmm-lps/config/deployment-status.json`, `reports/status/cw-mesh-deployment-matrix-latest.json`, and `reports/extraction/promod-uniswap-v2-phase2-wave1-completion-status-latest.json`. See `reports/status/MULTI_NETWORK_DEPLOYMENT_AUDIT_20260419.md`. @@ -14,8 +14,8 @@ | **Chain 138 tokens** | ✅ Live | Chain 138 inventory is broader than older summaries implied: cUSDT, cUSDC, WETH, WETH10, LINK, the 10 compliant fiat additions, cXAUC/cXAUT, and `cAUSDT` are all evidenced in current repo-backed inventories. | | **Chain 138 DODO PMM** | ✅ Live | DODOPMMIntegration + 6 public pools are live on Chain 138: stable pairs plus the three public XAU pools. Official mirror `USDT/USDC` pools were corrected and funded locally. DODOPMMProvider remains deployed; routing cUSDT↔cUSDC and the live local direct/ XAU paths are active. | | **Chain 138 → destination bridges** | ✅ CCIP + Alltra with one blocked lane | CCIP WETH9/WETH10 routing is evidenced to Ethereum, BSC, Polygon, Arbitrum, Optimism, Avalanche, Cronos, Gnosis, and Celo; AlltraAdapter 138↔651940 is live. Wemix contracts are deployed and wired, but the current Chain 138 CCIP router still rejects the Wemix selector (`CCIPRouter: chain not supported`), so the lane is not sendable yet. | -| **Destination tokens (cW\*)** | ✅ Active on 10 public chains, scaffold-only on Wemix | `deployment-status.json` records active public-chain `cW*` inventory on `1, 10, 25, 56, 100, 137, 8453, 42161, 42220, 43114`; `1111` currently remains `planned_gas_scaffold` with `bridgeAvailable=false`, not a promoted live settlement surface. | -| **Destination DODO/Uniswap LPs** | ⚠️ Mixed by venue and confidence | Public-chain deployment evidence is no longer design-only: `deployment-status.json` records PMM pool inventories on the active public chains, and generated Uniswap V2 reports confirm a complete wave-1 wrapped-mesh rollout on `1, 10, 25, 56, 100, 137, 8453, 42161, 42220, 43114`. Explorer publication is still blocked in some cases. | +| **Destination tokens (cW\*)** | ✅ Active on 9 public chains, scaffold-only on Wemix | `deployment-status.json` records active public-chain `cW*` inventory on `1, 10, 56, 100, 137, 8453, 42161, 42220, 43114` (Cronos `25` is **not** counted here: no promoted live `cW*` surface); `1111` currently remains `planned_gas_scaffold` with `bridgeAvailable=false`, not a promoted live settlement surface. | +| **Destination DODO/Uniswap LPs** | ⚠️ Mixed by venue and confidence | Public-chain deployment evidence is no longer design-only: `deployment-status.json` records PMM pool inventories on the active public chains, and generated Uniswap V2 reports confirm wave-1 wrapped-mesh rollout on the same nine-chain `cW*` set (excluding Cronos). Explorer publication is still blocked in some cases. | | **Uniswap on 138** | ❌ | No Uniswap V2/V3 factory on Chain 138. | | **Uniswap on ALL Mainnet (651940)** | ⚠️ Documentation only | HYDX token and other Alltra ecosystem tokens are present on-chain, but Uniswap V2/V3, DODO, HYDX router/factory, and pool inventory are still not committed in canonical deployment status. | @@ -41,7 +41,7 @@ | Chain ID | Name | Tokens / bridges | |----------|------|-------------------| | **1** | Ethereum Mainnet | WETH, USDT, USDC, DAI; CCIP WETH9/WETH10 bridges; relay router. cW* deployable. | -| **25** | Cronos | Active `cW*` inventory plus wave-1 wrapped-mesh completion evidence. | +| **25** | Cronos | CCIP WETH9/WETH10; **no** promoted public `cW*` inventory (excluded from the nine-chain `cW*` count). | | **56** | BSC | Active `cW*` inventory plus wave-1 wrapped-mesh completion evidence. | | **100** | Gnosis | Active `cW*` inventory plus wave-1 wrapped-mesh completion evidence. | | **137** | Polygon | Active `cW*` inventory plus wave-1 wrapped-mesh completion evidence. | @@ -118,7 +118,7 @@ | DODO PMM | ⚠️ Env | `CHAIN_651940_DODO_POOL_MANAGER`, `_DODO_VENDING_MACHINE` | | HYDX | Present | Token `0x0d9793861AEB9244AD1B34375a83A6730F6AdD38`; no pool addresses in repo. | -### 4.3 Public chains (1, 56, 137, 10, 100, 25, 42161, 8453, 43114, 42220, 1111) — cW* edge pools +### 4.3 Public chains (1, 56, 137, 10, 100, 42161, 8453, 43114, 42220, 1111) — cW* edge pools **Designed (pool-matrix.json):** Per chain, first-tier pools: cWUSDT/USDC or cWUSDT/USDT, cWUSDC/USDC or cWUSDC/USDT, plus cWAUSDT, cWEURC, cWEURT, cWUSDW vs hub stable. Optional: cW*/USDT, cW*/DAI, cW*/BUSD, cW*/mUSD. @@ -184,7 +184,7 @@ | 138 | DeFi Oracle | cUSDT, cUSDC, cEURT, cXAUC, cXAUT, WETH, WETH10, LINK, official mirrors | — | — | DODO: 6 public pools live + 3 private XAU pools live | | 1 | Ethereum | WETH, USDT, USDC, DAI | ✅ CCIP WETH9/10 | ✅ CCIP relay | Native DEX; cW* design | | 651940 | ALL Mainnet | AUSDT, USDC, WETH, WALL | ✅ AlltraAdapter | ✅ AlltraAdapter | Documentation-only same-chain surface; no canonical router/factory/pool inventory committed yet | -| 25 | Cronos | cW* active | ✅ CCIP | ✅ CCIP | Wave-1 wrapped-mesh complete; explorer publication still partial | +| 25 | Cronos | No promoted `cW*` | ✅ CCIP | ✅ CCIP | WETH CCIP live; `cW*` not counted in nine-chain public surface | | 56, 100, 137, 10, 42161, 8453, 43114 | BSC, Gnosis, Polygon, Optimism, Arbitrum, Base, Avalanche | cW* active | ✅ CCIP | ✅ CCIP | Active cW* inventory plus generated wrapped-mesh rollout evidence | | 42220 | Celo | cW* active | ✅ CCIP | ✅ CCIP | Active cW* inventory plus generated wrapped-mesh rollout evidence | | 1111 | Wemix | planned_gas_scaffold | ⏳ | ⏳ | Contracts exist and destinations are wired, but the current Chain 138 CCIP router still rejects the Wemix selector, so proof transfer/promotion cannot proceed yet | diff --git a/docs/11-references/DEPLOYER_CONTRACTS_INVENTORY_AND_VERIFICATION_STATUS.md b/docs/11-references/DEPLOYER_CONTRACTS_INVENTORY_AND_VERIFICATION_STATUS.md index bd43912b..6ef30e9a 100644 --- a/docs/11-references/DEPLOYER_CONTRACTS_INVENTORY_AND_VERIFICATION_STATUS.md +++ b/docs/11-references/DEPLOYER_CONTRACTS_INVENTORY_AND_VERIFICATION_STATUS.md @@ -171,7 +171,7 @@ Files that contain smart contract addresses or deployment configuration: 1. **Chain 138:** From a host that can reach Blockscout (e.g. LAN), run: - `./scripts/verify/run-contract-verification-with-proxy.sh` to submit verification for contracts in the verification config. - - Open https://explorer.d-bis.org/address/
for each contract and confirm “Contract source code verified” (or equivalent). + - Open https://explorer.d-bis.org/addresses/
for each contract and confirm “Contract source code verified” (or equivalent). 2. **This doc:** Set **Verified** to **Yes** or **No** for each contract after checking. Leave **Unknown** until checked. --- diff --git a/docs/11-references/DEPLOYER_WALLET_FUNDING_PLAN_PMM_POOLS.md b/docs/11-references/DEPLOYER_WALLET_FUNDING_PLAN_PMM_POOLS.md index 941f1351..02f98fd9 100644 --- a/docs/11-references/DEPLOYER_WALLET_FUNDING_PLAN_PMM_POOLS.md +++ b/docs/11-references/DEPLOYER_WALLET_FUNDING_PLAN_PMM_POOLS.md @@ -12,7 +12,7 @@ | Item | Value | |------|--------| | **Address** | `0x4A666F96fC8764181194447A7dFdb7d471b301C8` | -| **Chain 138 Explorer** | https://explorer.d-bis.org/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8 | +| **Chain 138 Explorer** | https://explorer.d-bis.org/addresses/0x4A666F96fC8764181194447A7dFdb7d471b301C8 | | **Other chains (e.g. Mainnet)** | https://blockscan.com/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8 | This address is the **documented deployer/admin** for Chain 138 (contracts, pools, and operations). The same address may hold assets or be used for deployments on other networks. diff --git a/docs/11-references/EXPLORER_AND_BLOCKSCAN_REFERENCE.md b/docs/11-references/EXPLORER_AND_BLOCKSCAN_REFERENCE.md index 1ff47579..5f414a43 100644 --- a/docs/11-references/EXPLORER_AND_BLOCKSCAN_REFERENCE.md +++ b/docs/11-references/EXPLORER_AND_BLOCKSCAN_REFERENCE.md @@ -11,7 +11,7 @@ | Chain | Explorer | Example (deployer address) | |-------|----------|----------------------------| | **Ethereum mainnet, etc.** | **https://blockscan.com** | [blockscan.com/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8](https://blockscan.com/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8) — use to check balances on mainnet and other supported chains | -| **Chain 138 (SMOM-DBIS-138)** | **https://explorer.d-bis.org** | [explorer.d-bis.org/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8](https://explorer.d-bis.org/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8) — use for Chain 138 only | +| **Chain 138 (SMOM-DBIS-138)** | **https://explorer.d-bis.org** | [explorer.d-bis.org/addresses/0x4A666F96fC8764181194447A7dFdb7d471b301C8](https://explorer.d-bis.org/addresses/0x4A666F96fC8764181194447A7dFdb7d471b301C8) — use for Chain 138 only | - **To check balances on other chains:** Use **blockscan.com** (or etherscan.io) and select the chain; same address, different chain = different balance. - **To check balances on Chain 138:** Use **explorer.d-bis.org** only; Blockscan does not show Chain 138. @@ -21,7 +21,7 @@ ## Deployer / Admin address - **Address:** `0x4A666F96fC8764181194447A7dFdb7d471b301C8` -- **Chain 138 (balance, txs, contracts):** https://explorer.d-bis.org/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8 +- **Chain 138 (balance, txs, contracts):** https://explorer.d-bis.org/addresses/0x4A666F96fC8764181194447A7dFdb7d471b301C8 - **Other chains (e.g. Ethereum mainnet):** https://blockscan.com/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8 - **RPC (Chain 138):** https://rpc-core.d-bis.org or http://192.168.11.211:8545 diff --git a/docs/11-references/LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE.md b/docs/11-references/LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE.md index 2463df4f..f0d2c978 100644 --- a/docs/11-references/LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE.md +++ b/docs/11-references/LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE.md @@ -49,7 +49,7 @@ defi_oracle_meta_mainnet: { ethereumLikeInfo: { chainId: 138 }, explorerViews: [ { - address: "https://explorer.d-bis.org/address/$address", + address: "https://explorer.d-bis.org/addresses/$address", tx: "https://explorer.d-bis.org/tx/$hash", token: "https://explorer.d-bis.org/token/$contractAddress?a=$address", }, diff --git a/docs/11-references/OPERATOR_OPTIONAL_CHECKLIST.md b/docs/11-references/OPERATOR_OPTIONAL_CHECKLIST.md index d5ff54d8..d0d285d5 100644 --- a/docs/11-references/OPERATOR_OPTIONAL_CHECKLIST.md +++ b/docs/11-references/OPERATOR_OPTIONAL_CHECKLIST.md @@ -32,7 +32,7 @@ forge verify-contract 0x105F8A15b819948a89153505762444Ee9f324684 \ - Other contracts and full manual commands: [BLOCKSCOUT_VERIFICATION_GUIDE](../08-monitoring/BLOCKSCOUT_VERIFICATION_GUIDE.md) and script `scripts/verify-contracts-blockscout.sh` (addresses/paths). **Option C — Manual UI:** -Open https://explorer.d-bis.org/address/
#verify-contract and use "Verify & Publish" with Standard JSON or flattened source. +Open https://explorer.d-bis.org/addresses/
#verify-contract and use "Verify & Publish" with Standard JSON or flattened source. --- diff --git a/docs/11-references/OPERATOR_RUN_SUMMARY.md b/docs/11-references/OPERATOR_RUN_SUMMARY.md index 77981033..af814d8a 100644 --- a/docs/11-references/OPERATOR_RUN_SUMMARY.md +++ b/docs/11-references/OPERATOR_RUN_SUMMARY.md @@ -31,7 +31,7 @@ **Result:** Proxy runs and submits to Blockscout. Submission for CCIPSender can fail with `Invalid JSON, result=None` (Blockscout API response). When Blockscout is unreachable from the host, run times out. -**To complete:** Run from a host that can reach Blockscout. If submission returns Invalid JSON, try manual verification at https://explorer.d-bis.org/address/<ADDRESS>#verify-contract or check [BLOCKSCOUT_VERIFICATION_GUIDE](../08-monitoring/BLOCKSCOUT_VERIFICATION_GUIDE.md). +**To complete:** Run from a host that can reach Blockscout. If submission returns Invalid JSON, try manual verification at https://explorer.d-bis.org/addresses/<ADDRESS>#verify-contract or check [BLOCKSCOUT_VERIFICATION_GUIDE](../08-monitoring/BLOCKSCOUT_VERIFICATION_GUIDE.md). --- @@ -45,7 +45,7 @@ ## 4. Multicall vs Oracle at 0x99b3... -**Action:** Attempted to fetch the contract page at https://explorer.d-bis.org/address/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506 to determine which contract (Multicall or Oracle Aggregator) is deployed at that slot. +**Action:** Attempted to fetch the contract page at https://explorer.d-bis.org/addresses/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506 to determine which contract (Multicall or Oracle Aggregator) is deployed at that slot. **Result:** **Done 2026-02-11.** Confirmed via RPC: `latestRoundData()` returns data, `getBlockNumber()` reverts — **Oracle Aggregator**. Documented in [CONTRACT_ADDRESSES_REFERENCE](CONTRACT_ADDRESSES_REFERENCE.md). diff --git a/docs/11-references/SMART_CONTRACTS_INVENTORY_SIMPLE.md b/docs/11-references/SMART_CONTRACTS_INVENTORY_SIMPLE.md index 149153d6..57f08831 100644 --- a/docs/11-references/SMART_CONTRACTS_INVENTORY_SIMPLE.md +++ b/docs/11-references/SMART_CONTRACTS_INVENTORY_SIMPLE.md @@ -42,7 +42,7 @@ **Full next steps, operator checklist, and all recommendations:** [CONTRACT_NEXT_STEPS_AND_RECOMMENDATIONS_COMPLETE](CONTRACT_NEXT_STEPS_AND_RECOMMENDATIONS_COMPLETE.md). -1. **Confirm on-chain** — Run `./scripts/verify/check-contracts-on-chain-138.sh` (36 addresses) or open each address at https://explorer.d-bis.org/address/
. +1. **Confirm on-chain** — Run `./scripts/verify/check-contracts-on-chain-138.sh` (36 addresses) or open each address at https://explorer.d-bis.org/addresses/
. 2. **Verify source on Blockscout** — Use [BLOCKSCOUT_VERIFICATION_GUIDE](../08-monitoring/BLOCKSCOUT_VERIFICATION_GUIDE.md) and `scripts/verify/run-contract-verification-with-proxy.sh`. 3. **Do not use deprecated bridge** — Use **CCIPWETH9Bridge** at `0xcacfd227A040002e49e2e01626363071324f820a` only. 4. **If deploying more contracts** — Use **`--with-gas-price 1000000000`** for all `forge script`/`forge create` on Chain 138. Phased core: [CONTRACTS_TO_DEPLOY](CONTRACTS_TO_DEPLOY.md), [CONTRACT_DEPLOYMENT_RUNBOOK](../03-deployment/CONTRACT_DEPLOYMENT_RUNBOOK.md). RPC: Set `RPC_URL_138` in `smom-dbis-138/.env` (e.g. `http://192.168.11.211:8545` or `https://rpc-core.d-bis.org`). Set `PRIVATE_KEY` there too. diff --git a/docs/12-quick-reference/QUICK_REFERENCE.md b/docs/12-quick-reference/QUICK_REFERENCE.md index 9df21dc3..9756ec0e 100644 --- a/docs/12-quick-reference/QUICK_REFERENCE.md +++ b/docs/12-quick-reference/QUICK_REFERENCE.md @@ -200,6 +200,6 @@ bash ProxmoxVE/ct/AppName.sh -u - [ ] Use `msg_*` functions for messages - [ ] Use `$STD` for command execution - [ ] Quote all variables -- [ ] Test on Proxmox VE 8.4+ or 9.0+ +- [ ] Test on Proxmox VE **9.x** (live cluster **9.1.7**, kernel **6.17.13-2-pve**, **2026-05-09**) - [ ] Implement update function (if applicable) - [ ] Update documentation (if needed) diff --git a/docs/GALATIC_SUMMARY.md b/docs/GALATIC_SUMMARY.md index e2509c17..df310e90 100644 --- a/docs/GALATIC_SUMMARY.md +++ b/docs/GALATIC_SUMMARY.md @@ -1,7 +1,7 @@ # Galatic Summary — Chain 138 Ecosystem, Routing, and Integrations **Audience:** Galatic -**Last Updated:** 2026-03-26 +**Last Updated:** 2026-05-09 **Purpose:** Single reference for what is built, all available routing (DEX and aggregators), On-Ramp/Off-Ramp, Fireblocks, and other integrations. --- @@ -193,6 +193,7 @@ | **Bridge Vault** | Multi-chain stablecoin (Ethereum, Polygon, BNB) | cUSDT, cUSDC | Vault: `0x31884f84555210FFB36a19D2471b8eBc7372d0A8` | | **Truth Network** | Ethereum ↔ Truth (Substrate); adapter on 138 | TRUU (lift/burn) | TruthNetworkAdapter on 138; Truth registered in ChainRegistry | | **AlltraAdapter** | ALL Mainnet (651940) | Custom | CCIP/Li.Fi not supported on 651940 | +| **Cosmos / IBC (optional)** | Cosmos Hub, Noble, Osmosis, app chains | Policy-dependent | Not live by default; streams A–E + templates: [COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK](11-references/COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md), [`config/cosmos-chain138-optional/`](../config/cosmos-chain138-optional/README.md) | **References:** - [CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION](11-references/CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION.md) @@ -228,7 +229,7 @@ | Quote API | token-aggregation: GET /api/v1/quote, POST /api/bridge/quote | | On/off-ramp | MAINNET_RAMP_USER_FLOWS, INTEGRATIONS_QUICK_REFERENCE | | Fireblocks | FIREBLOCKS_WEB3_INTEGRATION, RPC_ENDPOINTS_MASTER | -| Bridges & chains | CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION, CCIP_CHAIN_SELECTORS | +| Bridges & chains | CHAINS_AND_PROTOCOLS_BRIDGE_INTEGRATION, CCIP_CHAIN_SELECTORS, [COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK](11-references/COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md) | | Integrations list | INTEGRATIONS_QUICK_REFERENCE (root) | --- diff --git a/docs/api/openapi-multi-chain-execution.yaml b/docs/api/openapi-multi-chain-execution.yaml index c47aab0f..95401b13 100644 --- a/docs/api/openapi-multi-chain-execution.yaml +++ b/docs/api/openapi-multi-chain-execution.yaml @@ -29,6 +29,41 @@ paths: '400': description: Validation error + /v1/routes/non-evm-families: + get: + summary: List non-EVM route families (Tezos, Solana, Tron, XRPL) and supported source chains + responses: + '200': + description: family metadata and supported_source_chain_ids (138, 651940) + + /v1/routes/plan-non-evm: + post: + summary: Multi-hop route plan from Chain 138 or ALL Mainnet through Ethereum hub to a non-EVM destination + requestBody: + content: + application/json: + schema: + type: object + required: [destination_family, destination_address] + properties: + destination_family: + type: string + enum: [tezos_usdtz, solana_usdc, tron_usdt, xrpl_usdc] + destination_address: + type: string + description: Destination address validated per family (Tezos tz1… / Solana base58 / Tron T… / XRPL r…) + source_chain_id: { type: integer, default: 138 } + source_asset: { type: string, description: Defaults to cUSDC on 138 or AUSDC on 651940 when omitted } + source_amount: { type: string } + async_quotes: + type: boolean + description: Reserved for future enrichment (Tezos path uses chain138-to-usdtz with async_quotes) + responses: + '200': + description: Route plan with hops, optional quote_hints (aggregator / bridge entry points), destination_family + '400': + description: Validation error + /v1/intents: post: summary: Create intent diff --git a/docs/archive/completion/ALI_INFRASTRUCTURE_COMPLETE.md b/docs/archive/completion/ALI_INFRASTRUCTURE_COMPLETE.md index 8a82dc57..568507d2 100644 --- a/docs/archive/completion/ALI_INFRASTRUCTURE_COMPLETE.md +++ b/docs/archive/completion/ALI_INFRASTRUCTURE_COMPLETE.md @@ -95,9 +95,9 @@ These contracts were pre-deployed when ChainID 138 was initialized: | **Multicall** | `0x99b3511a2d315a497c8112c1fdd8d508d4b1e506` | ✅ Pre-deployed | Batch contract calls | **Explorer Links:** -- [WETH9](https://explorer.d-bis.org/address/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) -- [WETH10](https://explorer.d-bis.org/address/0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f) -- [Multicall](https://explorer.d-bis.org/address/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506) +- [WETH9](https://explorer.d-bis.org/addresses/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) +- [WETH10](https://explorer.d-bis.org/addresses/0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f) +- [Multicall](https://explorer.d-bis.org/addresses/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506) --- @@ -112,9 +112,9 @@ Price feed and oracle infrastructure: | **Price Feed Keeper** | `0xD3AD6831aacB5386B8A25BB8D8176a6C8a026f04` | ✅ Deployed | Automated price updates | **Explorer Links:** -- [Oracle Proxy](https://explorer.d-bis.org/address/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6) -- [Oracle Aggregator](https://explorer.d-bis.org/address/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506) -- [Price Feed Keeper](https://explorer.d-bis.org/address/0xD3AD6831aacB5386B8A25BB8D8176a6C8a026f04) +- [Oracle Proxy](https://explorer.d-bis.org/addresses/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6) +- [Oracle Aggregator](https://explorer.d-bis.org/addresses/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506) +- [Price Feed Keeper](https://explorer.d-bis.org/addresses/0xD3AD6831aacB5386B8A25BB8D8176a6C8a026f04) **Note:** The Oracle Proxy address (`0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6`) is the primary address used by MetaMask for price feeds. @@ -130,8 +130,8 @@ Cross-Chain Interoperability Protocol contracts: | **CCIP Sender** | `0x105F8A15b819948a89153505762444Ee9f324684` | ✅ Deployed | Cross-chain message sender | **Explorer Links:** -- [CCIP Router](https://explorer.d-bis.org/address/0x8078A09637e47Fa5Ed34F626046Ea2094a5CDE5e) -- [CCIP Sender](https://explorer.d-bis.org/address/0x105F8A15b819948a89153505762444Ee9f324684) +- [CCIP Router](https://explorer.d-bis.org/addresses/0x8078A09637e47Fa5Ed34F626046Ea2094a5CDE5e) +- [CCIP Sender](https://explorer.d-bis.org/addresses/0x105F8A15b819948a89153505762444Ee9f324684) --- @@ -145,8 +145,8 @@ Cross-chain bridge contracts for WETH tokens: | **CCIPWETH10Bridge** | `0xe0E93247376aa097dB308B92e6Ba36bA015535D0` | ✅ Deployed | Bridge for WETH10 | **Explorer Links:** -- [CCIPWETH9Bridge](https://explorer.d-bis.org/address/0x89dd12025bfCD38A168455A44B400e913ED33BE2) -- [CCIPWETH10Bridge](https://explorer.d-bis.org/address/0xe0E93247376aa097dB308B92e6Ba36bA015535D0) +- [CCIPWETH9Bridge](https://explorer.d-bis.org/addresses/0x89dd12025bfCD38A168455A44B400e913ED33BE2) +- [CCIPWETH10Bridge](https://explorer.d-bis.org/addresses/0xe0E93247376aa097dB308B92e6Ba36bA015535D0) --- @@ -164,12 +164,12 @@ Core eMoney infrastructure contracts: | **eMoneyToken Implementation** | `0x0059e237973179146237aB49f1322E8197c22b21` | 10,088 bytes | ✅ Deployed | eMoney token implementation | **Explorer Links:** -- [TokenFactory138](https://explorer.d-bis.org/address/0xEBFb5C60dE5f7C4baae180CA328D3BB39E1a5133) -- [BridgeVault138](https://explorer.d-bis.org/address/0x31884f84555210FFB36a19D2471b8eBc7372d0A8) -- [ComplianceRegistry](https://explorer.d-bis.org/address/0xbc54fe2b6fda157c59d59826bcfdbcc654ec9ea1) -- [DebtRegistry](https://explorer.d-bis.org/address/0x95BC4A997c0670d5DAC64d55cDf3769B53B63C28) -- [PolicyManager](https://explorer.d-bis.org/address/0x0C4FD27018130A00762a802f91a72D6a64a60F14) -- [eMoneyToken Implementation](https://explorer.d-bis.org/address/0x0059e237973179146237aB49f1322E8197c22b21) +- [TokenFactory138](https://explorer.d-bis.org/addresses/0xEBFb5C60dE5f7C4baae180CA328D3BB39E1a5133) +- [BridgeVault138](https://explorer.d-bis.org/addresses/0x31884f84555210FFB36a19D2471b8eBc7372d0A8) +- [ComplianceRegistry](https://explorer.d-bis.org/addresses/0xbc54fe2b6fda157c59d59826bcfdbcc654ec9ea1) +- [DebtRegistry](https://explorer.d-bis.org/addresses/0x95BC4A997c0670d5DAC64d55cDf3769B53B63C28) +- [PolicyManager](https://explorer.d-bis.org/addresses/0x0C4FD27018130A00762a802f91a72D6a64a60F14) +- [eMoneyToken Implementation](https://explorer.d-bis.org/addresses/0x0059e237973179146237aB49f1322E8197c22b21) --- @@ -185,10 +185,10 @@ Compliance and token management contracts: | **FeeCollector** | `0xF78246eB94c6CB14018E507E60661314E5f4C53f` | 5,084 bytes | ✅ Deployed | Fee collection | **Explorer Links:** -- [CompliantUSDT](https://explorer.d-bis.org/address/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22) -- [CompliantUSDC](https://explorer.d-bis.org/address/0xf22258f57794CC8E06237084b353Ab30fFfa640b) -- [TokenRegistry](https://explorer.d-bis.org/address/0x91Efe92229dbf7C5B38D422621300956B55870Fa) -- [FeeCollector](https://explorer.d-bis.org/address/0xF78246eB94c6CB14018E507E60661314E5f4C53f) +- [CompliantUSDT](https://explorer.d-bis.org/addresses/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22) +- [CompliantUSDC](https://explorer.d-bis.org/addresses/0xf22258f57794CC8E06237084b353Ab30fFfa640b) +- [TokenRegistry](https://explorer.d-bis.org/addresses/0x91Efe92229dbf7C5B38D422621300956B55870Fa) +- [FeeCollector](https://explorer.d-bis.org/addresses/0xF78246eB94c6CB14018E507E60661314E5f4C53f) --- @@ -913,7 +913,7 @@ curl -X POST http://192.168.11.253:8545 \ **Check Contract on Explorer:** ```bash # Open contract in explorer -xdg-open "https://explorer.d-bis.org/address/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6" +xdg-open "https://explorer.d-bis.org/addresses/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6" ``` ### Service Scripts diff --git a/docs/archive/completion/ALL_NEXT_ACTIONS_COMPLETE.md b/docs/archive/completion/ALL_NEXT_ACTIONS_COMPLETE.md index f30994da..670c95ec 100644 --- a/docs/archive/completion/ALL_NEXT_ACTIONS_COMPLETE.md +++ b/docs/archive/completion/ALL_NEXT_ACTIONS_COMPLETE.md @@ -133,7 +133,7 @@ cd /home/intlc/projects/proxmox 3. Foundry to be properly configured **Alternative**: Manual verification via Blockscout UI: -1. Navigate to contract: `https://explorer.d-bis.org/address/
` +1. Navigate to contract: `https://explorer.d-bis.org/addresses/
` 2. Click "Verify & Publish" tab 3. Upload source code and metadata 4. Submit for verification diff --git a/docs/archive/completion/ALL_REMAINING_ACTIONS_COMPLETE.md b/docs/archive/completion/ALL_REMAINING_ACTIONS_COMPLETE.md index 9315c4ce..58596663 100644 --- a/docs/archive/completion/ALL_REMAINING_ACTIONS_COMPLETE.md +++ b/docs/archive/completion/ALL_REMAINING_ACTIONS_COMPLETE.md @@ -120,7 +120,7 @@ cd /home/intlc/projects/proxmox **Manual Verification**: - See `docs/BLOCKSCOUT_VERIFICATION_GUIDE.md` for detailed instructions -- Navigate to contract on Blockscout: `https://explorer.d-bis.org/address/
` +- Navigate to contract on Blockscout: `https://explorer.d-bis.org/addresses/
` - Click "Verify & Publish" tab - Upload source code and metadata diff --git a/docs/archive/completion/BLOCKSCOUT_PARAMETERS_COMPLETE_GUIDE.md b/docs/archive/completion/BLOCKSCOUT_PARAMETERS_COMPLETE_GUIDE.md index 591d3e9d..6d385c00 100644 --- a/docs/archive/completion/BLOCKSCOUT_PARAMETERS_COMPLETE_GUIDE.md +++ b/docs/archive/completion/BLOCKSCOUT_PARAMETERS_COMPLETE_GUIDE.md @@ -264,7 +264,7 @@ docker exec blockscout /app/bin/blockscout version Once you have specific block numbers or addresses, try: ``` https://explorer.d-bis.org/block/ -https://explorer.d-bis.org/address/
+https://explorer.d-bis.org/addresses/
``` These routes may work even if root path doesn't. diff --git a/docs/archive/completion/BRIDGE_CONFIGURATION_COMPLETE.md b/docs/archive/completion/BRIDGE_CONFIGURATION_COMPLETE.md index 1e07702a..b82f69e0 100644 --- a/docs/archive/completion/BRIDGE_CONFIGURATION_COMPLETE.md +++ b/docs/archive/completion/BRIDGE_CONFIGURATION_COMPLETE.md @@ -79,8 +79,8 @@ The Chain 138 bridges are fully configured to receive from Ethereum Mainnet. For ### Blockscout Links -- **CCIPWETH9Bridge (Chain 138)**: https://explorer.d-bis.org/address/0x89dd12025bfcd38a168455a44b400e913ed33be2 -- **CCIPWETH10Bridge (Chain 138)**: https://explorer.d-bis.org/address/0xe0e93247376aa097db308b92e6ba36ba015535d0 +- **CCIPWETH9Bridge (Chain 138)**: https://explorer.d-bis.org/addresses/0x89dd12025bfcd38a168455a44b400e913ed33be2 +- **CCIPWETH10Bridge (Chain 138)**: https://explorer.d-bis.org/addresses/0xe0e93247376aa097db308b92e6ba36ba015535d0 ### Verification Status diff --git a/docs/archive/completion/FINAL_VALIDATION_REPORT.md b/docs/archive/completion/FINAL_VALIDATION_REPORT.md index ce7135b1..1888558d 100644 --- a/docs/archive/completion/FINAL_VALIDATION_REPORT.md +++ b/docs/archive/completion/FINAL_VALIDATION_REPORT.md @@ -60,13 +60,13 @@ | Contract | Verified | Blockscout Link | |----------|----------|----------------| -| Oracle Proxy | ⏳ Pending | https://explorer.d-bis.org/address/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6 | -| Oracle Aggregator | ⏳ Pending | https://explorer.d-bis.org/address/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506 | -| CCIP Router | ⏳ Pending | https://explorer.d-bis.org/address/0x8078A09637e47Fa5Ed34F626046Ea2094a5CDE5e | -| CCIP Sender | ⏳ Pending | https://explorer.d-bis.org/address/0x105F8A15b819948a89153505762444Ee9f324684 | -| CCIPWETH9Bridge | ⏳ Pending | https://explorer.d-bis.org/address/0x89dd12025bfCD38A168455A44B400e913ED33BE2 | -| CCIPWETH10Bridge | ⏳ Pending | https://explorer.d-bis.org/address/0xe0E93247376aa097dB308B92e6Ba36bA015535D0 | -| Price Feed Keeper | ⏳ Pending | https://explorer.d-bis.org/address/0xD3AD6831aacB5386B8A25BB8D8176a6C8a026f04 | +| Oracle Proxy | ⏳ Pending | https://explorer.d-bis.org/addresses/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6 | +| Oracle Aggregator | ⏳ Pending | https://explorer.d-bis.org/addresses/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506 | +| CCIP Router | ⏳ Pending | https://explorer.d-bis.org/addresses/0x8078A09637e47Fa5Ed34F626046Ea2094a5CDE5e | +| CCIP Sender | ⏳ Pending | https://explorer.d-bis.org/addresses/0x105F8A15b819948a89153505762444Ee9f324684 | +| CCIPWETH9Bridge | ⏳ Pending | https://explorer.d-bis.org/addresses/0x89dd12025bfCD38A168455A44B400e913ED33BE2 | +| CCIPWETH10Bridge | ⏳ Pending | https://explorer.d-bis.org/addresses/0xe0E93247376aa097dB308B92e6Ba36bA015535D0 | +| Price Feed Keeper | ⏳ Pending | https://explorer.d-bis.org/addresses/0xD3AD6831aacB5386B8A25BB8D8176a6C8a026f04 | **Status**: ⏳ All contracts pending verification on Blockscout. diff --git a/docs/archive/completion/NEXT_ACTIONS_COMPLETED.md b/docs/archive/completion/NEXT_ACTIONS_COMPLETED.md index c1288aff..f9724688 100644 --- a/docs/archive/completion/NEXT_ACTIONS_COMPLETED.md +++ b/docs/archive/completion/NEXT_ACTIONS_COMPLETED.md @@ -98,7 +98,7 @@ cd /home/intlc/projects/proxmox If automated verification fails, verify contracts manually: 1. Navigate to contract on Blockscout: - - Example: `https://explorer.d-bis.org/address/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6` + - Example: `https://explorer.d-bis.org/addresses/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6` 2. Click "Verify & Publish" tab diff --git a/docs/archive/fixes/ALL_ISSUES_FIXED_SUMMARY.md b/docs/archive/fixes/ALL_ISSUES_FIXED_SUMMARY.md index 3e79aa6b..4e195b4f 100644 --- a/docs/archive/fixes/ALL_ISSUES_FIXED_SUMMARY.md +++ b/docs/archive/fixes/ALL_ISSUES_FIXED_SUMMARY.md @@ -135,8 +135,8 @@ - CCIPWETH10Bridge: `0xe0E93247376aa097dB308B92e6Ba36bA015535D0` **Links**: -- https://explorer.d-bis.org/address/0x89dd12025bfcd38a168455a44b400e913ed33be2 -- https://explorer.d-bis.org/address/0xe0e93247376aa097db308b92e6ba36ba015535d0 +- https://explorer.d-bis.org/addresses/0x89dd12025bfcd38a168455a44b400e913ed33be2 +- https://explorer.d-bis.org/addresses/0xe0e93247376aa097db308b92e6ba36ba015535d0 ### 2. Monitor Bridge Transactions diff --git a/docs/archive/historical/ALL_BRIDGE_ADDRESSES_AND_ROUTES.md b/docs/archive/historical/ALL_BRIDGE_ADDRESSES_AND_ROUTES.md index 65fd09e6..535a8351 100644 --- a/docs/archive/historical/ALL_BRIDGE_ADDRESSES_AND_ROUTES.md +++ b/docs/archive/historical/ALL_BRIDGE_ADDRESSES_AND_ROUTES.md @@ -130,11 +130,11 @@ ### Bridge Contracts to Verify 1. **CCIPWETH9Bridge**: `0x89dd12025bfCD38A168455A44B400e913ED33BE2` - - **Blockscout**: https://explorer.d-bis.org/address/0x89dd12025bfcd38a168455a44b400e913ed33be2 + - **Blockscout**: https://explorer.d-bis.org/addresses/0x89dd12025bfcd38a168455a44b400e913ed33be2 - **Status**: ⏳ Needs verification 2. **CCIPWETH10Bridge**: `0xe0E93247376aa097dB308B92e6Ba36bA015535D0` - - **Blockscout**: https://explorer.d-bis.org/address/0xe0e93247376aa097db308b92e6ba36ba015535d0 + - **Blockscout**: https://explorer.d-bis.org/addresses/0xe0e93247376aa097db308b92e6ba36ba015535d0 - **Status**: ⏳ Needs verification ### Verification Commands diff --git a/docs/archive/historical/BLOCKSCOUT_BRIDGE_ADDRESSES_UPDATE.md b/docs/archive/historical/BLOCKSCOUT_BRIDGE_ADDRESSES_UPDATE.md index 579a7878..b138eb6c 100644 --- a/docs/archive/historical/BLOCKSCOUT_BRIDGE_ADDRESSES_UPDATE.md +++ b/docs/archive/historical/BLOCKSCOUT_BRIDGE_ADDRESSES_UPDATE.md @@ -12,8 +12,8 @@ | Contract | Address | Blockscout Link | Status | |----------|---------|-----------------|--------| -| **CCIPWETH9Bridge** | `0x89dd12025bfCD38A168455A44B400e913ED33BE2` | [View](https://explorer.d-bis.org/address/0x89dd12025bfcd38a168455a44b400e913ed33be2) | ✅ Deployed | -| **CCIPWETH10Bridge** | `0xe0E93247376aa097dB308B92e6Ba36bA015535D0` | [View](https://explorer.d-bis.org/address/0xe0e93247376aa097db308b92e6ba36ba015535d0) | ✅ Deployed | +| **CCIPWETH9Bridge** | `0x89dd12025bfCD38A168455A44B400e913ED33BE2` | [View](https://explorer.d-bis.org/addresses/0x89dd12025bfcd38a168455a44b400e913ed33be2) | ✅ Deployed | +| **CCIPWETH10Bridge** | `0xe0E93247376aa097dB308B92e6Ba36bA015535D0` | [View](https://explorer.d-bis.org/addresses/0xe0e93247376aa097db308b92e6ba36ba015535d0) | ✅ Deployed | ### Constructor Arguments @@ -102,8 +102,8 @@ ### Step 1: Navigate to Contract -1. **CCIPWETH9Bridge**: https://explorer.d-bis.org/address/0x89dd12025bfcd38a168455a44b400e913ed33be2 -2. **CCIPWETH10Bridge**: https://explorer.d-bis.org/address/0xe0e93247376aa097db308b92e6ba36ba015535d0 +1. **CCIPWETH9Bridge**: https://explorer.d-bis.org/addresses/0x89dd12025bfcd38a168455a44b400e913ed33be2 +2. **CCIPWETH10Bridge**: https://explorer.d-bis.org/addresses/0xe0e93247376aa097db308b92e6ba36ba015535d0 ### Step 2: Click "Verify & Publish" diff --git a/docs/archive/historical/BLOCKSCOUT_PARAMETERS_AND_ENDPOINTS.md b/docs/archive/historical/BLOCKSCOUT_PARAMETERS_AND_ENDPOINTS.md index eb077cfc..d2573e0c 100644 --- a/docs/archive/historical/BLOCKSCOUT_PARAMETERS_AND_ENDPOINTS.md +++ b/docs/archive/historical/BLOCKSCOUT_PARAMETERS_AND_ENDPOINTS.md @@ -213,8 +213,8 @@ Once the web interface is fully operational, these routes will be available: - `https://explorer.d-bis.org/tx/` - Specific transaction ### Address Routes -- `https://explorer.d-bis.org/address/
` - Address details -- `https://explorer.d-bis.org/address/
/transactions` - Address transactions +- `https://explorer.d-bis.org/addresses/
` - Address details +- `https://explorer.d-bis.org/addresses/
/transactions` - Address transactions ### Token Routes - `https://explorer.d-bis.org/tokens` - All tokens diff --git a/docs/archive/historical/CHAINID_138_BLOCKSCOUT_INTEGRATION.md b/docs/archive/historical/CHAINID_138_BLOCKSCOUT_INTEGRATION.md index 1aadbc4d..d4317922 100644 --- a/docs/archive/historical/CHAINID_138_BLOCKSCOUT_INTEGRATION.md +++ b/docs/archive/historical/CHAINID_138_BLOCKSCOUT_INTEGRATION.md @@ -119,7 +119,7 @@ forge verify-contract \ ### View Contract on Explorer -- **Contract Page**: `https://explorer.d-bis.org/address/` +- **Contract Page**: `https://explorer.d-bis.org/addresses/` - **Manual Verification**: Navigate to contract → "Verify & Publish" tab ### Blockscout API Endpoints diff --git a/docs/archive/root-cleanup-20260220/TOKEN_ADDRESS_VERIFICATION_REPORT.md b/docs/archive/root-cleanup-20260220/TOKEN_ADDRESS_VERIFICATION_REPORT.md index d69abc8e..17319687 100644 --- a/docs/archive/root-cleanup-20260220/TOKEN_ADDRESS_VERIFICATION_REPORT.md +++ b/docs/archive/root-cleanup-20260220/TOKEN_ADDRESS_VERIFICATION_REPORT.md @@ -44,7 +44,7 @@ 4. **Deployment Records:** - ✅ Deployed by: `0x4A666F96fC8764181194447A7dFdb7d471b301C8` - ✅ Code Size: 6,806 bytes - - ✅ Explorer: https://explorer.d-bis.org/address/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22 + - ✅ Explorer: https://explorer.d-bis.org/addresses/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22 5. **Integration Documentation:** - ✅ `metamask-integration/EXTENSIBILITY_COMPLETE_SUMMARY.md` - Listed as active @@ -82,7 +82,7 @@ 4. **Deployment Records:** - ✅ Deployed by: `0x4A666F96fC8764181194447A7dFdb7d471b301C8` - ✅ Code Size: 6,806 bytes - - ✅ Explorer: https://explorer.d-bis.org/address/0xf22258f57794CC8E06237084b353Ab30fFfa640b + - ✅ Explorer: https://explorer.d-bis.org/addresses/0xf22258f57794CC8E06237084b353Ab30fFfa640b 5. **Integration Documentation:** - ✅ `metamask-integration/EXTENSIBILITY_COMPLETE_SUMMARY.md` - Listed as active @@ -171,7 +171,7 @@ cast code 0xf22258f57794CC8E06237084b353Ab30fFfa640b \ | **Decimals** | 6 | | **Chain ID** | 138 | | **Network** | DeFi Oracle Meta Mainnet | -| **Explorer** | https://explorer.d-bis.org/address/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22 | +| **Explorer** | https://explorer.d-bis.org/addresses/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22 | | **Status** | ✅ Deployed and Verified | | **Deployer** | `0x4A666F96fC8764181194447A7dFdb7d471b301C8` | | **Code Size** | 6,806 bytes | @@ -186,7 +186,7 @@ cast code 0xf22258f57794CC8E06237084b353Ab30fFfa640b \ | **Decimals** | 6 | | **Chain ID** | 138 | | **Network** | DeFi Oracle Meta Mainnet | -| **Explorer** | https://explorer.d-bis.org/address/0xf22258f57794CC8E06237084b353Ab30fFfa640b | +| **Explorer** | https://explorer.d-bis.org/addresses/0xf22258f57794CC8E06237084b353Ab30fFfa640b | | **Status** | ✅ Deployed and Verified | | **Deployer** | `0x4A666F96fC8764181194447A7dFdb7d471b301C8` | | **Code Size** | 6,806 bytes | diff --git a/docs/archive/root-status-reports/BRIDGE_EXECUTION_COMPLETE.md b/docs/archive/root-status-reports/BRIDGE_EXECUTION_COMPLETE.md index 99745bcd..1c5492dc 100644 --- a/docs/archive/root-status-reports/BRIDGE_EXECUTION_COMPLETE.md +++ b/docs/archive/root-status-reports/BRIDGE_EXECUTION_COMPLETE.md @@ -115,7 +115,7 @@ cast call 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 \ ``` 2. **Check transaction on explorer**: - - Visit: `https://explorer.d-bis.org/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8` + - Visit: `https://explorer.d-bis.org/addresses/0x4A666F96fC8764181194447A7dFdb7d471b301C8` - Look for recent transactions 3. **Check nonce**: diff --git a/docs/archive/root-status-reports/BRIDGE_EXECUTION_STATUS.md b/docs/archive/root-status-reports/BRIDGE_EXECUTION_STATUS.md index f15ce9d9..f5b058f6 100644 --- a/docs/archive/root-status-reports/BRIDGE_EXECUTION_STATUS.md +++ b/docs/archive/root-status-reports/BRIDGE_EXECUTION_STATUS.md @@ -137,7 +137,7 @@ cast call 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 \ 1. **Check if transaction was sent**: ```bash # Check recent transactions on explorer - # https://explorer.d-bis.org/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8 + # https://explorer.d-bis.org/addresses/0x4A666F96fC8764181194447A7dFdb7d471b301C8 ``` 2. **Try with higher gas price**: diff --git a/docs/archive/root-status-reports/BRIDGE_MANUAL_EXECUTION.md b/docs/archive/root-status-reports/BRIDGE_MANUAL_EXECUTION.md index cad93d93..a454b8bd 100644 --- a/docs/archive/root-status-reports/BRIDGE_MANUAL_EXECUTION.md +++ b/docs/archive/root-status-reports/BRIDGE_MANUAL_EXECUTION.md @@ -164,7 +164,7 @@ fi 1. **Check if transaction was sent**: - Look for transaction hash in output - - Check explorer: `https://explorer.d-bis.org/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8` + - Check explorer: `https://explorer.d-bis.org/addresses/0x4A666F96fC8764181194447A7dFdb7d471b301C8` 2. **Try with higher gas price**: ```bash diff --git a/docs/archive/root-status-reports/BRIDGE_START_STATUS.md b/docs/archive/root-status-reports/BRIDGE_START_STATUS.md index 34aa72b6..afd5a141 100644 --- a/docs/archive/root-status-reports/BRIDGE_START_STATUS.md +++ b/docs/archive/root-status-reports/BRIDGE_START_STATUS.md @@ -78,7 +78,7 @@ cast send 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 \ cast tx --rpc-url http://192.168.11.211:8545 # Or check on explorer -# https://explorer.d-bis.org/address/0x4A666F96fC8764181194447A7dFdb7d471b301C8 +# https://explorer.d-bis.org/addresses/0x4A666F96fC8764181194447A7dFdb7d471b301C8 ``` --- diff --git a/docs/archive/status/BLOCKSCOUT_STATUS_AND_VERIFICATION.md b/docs/archive/status/BLOCKSCOUT_STATUS_AND_VERIFICATION.md index 7abaf5c7..7dae98c4 100644 --- a/docs/archive/status/BLOCKSCOUT_STATUS_AND_VERIFICATION.md +++ b/docs/archive/status/BLOCKSCOUT_STATUS_AND_VERIFICATION.md @@ -137,7 +137,7 @@ cd /home/intlc/projects/proxmox ### Manual Verification 1. Navigate to contract on Blockscout: - - Example: https://explorer.d-bis.org/address/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6 + - Example: https://explorer.d-bis.org/addresses/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6 2. Click "Code" tab, then "Verify & Publish" diff --git a/docs/archive/status/BRIDGE_VERIFICATION_FINAL_STATUS.md b/docs/archive/status/BRIDGE_VERIFICATION_FINAL_STATUS.md index 685a069b..00319273 100644 --- a/docs/archive/status/BRIDGE_VERIFICATION_FINAL_STATUS.md +++ b/docs/archive/status/BRIDGE_VERIFICATION_FINAL_STATUS.md @@ -160,7 +160,7 @@ **Alternative Verification Methods**: 1. Use different RPC endpoint (if available) -2. Check Blockscout explorer: `https://explorer.d-bis.org/address/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2` +2. Check Blockscout explorer: `https://explorer.d-bis.org/addresses/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2` 3. Use internal RPC if accessible: `http://192.168.11.250:8545` ### If On-Chain Verification Passes diff --git a/docs/archive/status/CONTRACT_VALIDATION_STATUS_REPORT.md b/docs/archive/status/CONTRACT_VALIDATION_STATUS_REPORT.md index 9698b1b9..d79c2e06 100644 --- a/docs/archive/status/CONTRACT_VALIDATION_STATUS_REPORT.md +++ b/docs/archive/status/CONTRACT_VALIDATION_STATUS_REPORT.md @@ -42,13 +42,13 @@ All 7 core contracts have been deployed and have bytecode on-chain: | Contract | Address | Verification Status | Blockscout Link | |----------|---------|---------------------|-----------------| -| Oracle Proxy | `0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6` | ⏳ Not Verified | [View](https://explorer.d-bis.org/address/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6) | -| Oracle Aggregator | `0x99b3511a2d315a497c8112c1fdd8d508d4b1e506` | ⏳ Not Verified | [View](https://explorer.d-bis.org/address/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506) | -| CCIP Router | `0x8078A09637e47Fa5Ed34F626046Ea2094a5CDE5e` | ⏳ Not Verified | [View](https://explorer.d-bis.org/address/0x8078A09637e47Fa5Ed34F626046Ea2094a5CDE5e) | -| CCIP Sender | `0x105F8A15b819948a89153505762444Ee9f324684` | ⏳ Not Verified | [View](https://explorer.d-bis.org/address/0x105F8A15b819948a89153505762444Ee9f324684) | -| CCIPWETH9Bridge | `0x89dd12025bfCD38A168455A44B400e913ED33BE2` | ⏳ Not Verified | [View](https://explorer.d-bis.org/address/0x89dd12025bfCD38A168455A44B400e913ED33BE2) | -| CCIPWETH10Bridge | `0xe0E93247376aa097dB308B92e6Ba36bA015535D0` | ⏳ Not Verified | [View](https://explorer.d-bis.org/address/0xe0E93247376aa097dB308B92e6Ba36bA015535D0) | -| Price Feed Keeper | `0xD3AD6831aacB5386B8A25BB8D8176a6C8a026f04` | ⏳ Not Verified | [View](https://explorer.d-bis.org/address/0xD3AD6831aacB5386B8A25BB8D8176a6C8a026f04) | +| Oracle Proxy | `0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6` | ⏳ Not Verified | [View](https://explorer.d-bis.org/addresses/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6) | +| Oracle Aggregator | `0x99b3511a2d315a497c8112c1fdd8d508d4b1e506` | ⏳ Not Verified | [View](https://explorer.d-bis.org/addresses/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506) | +| CCIP Router | `0x8078A09637e47Fa5Ed34F626046Ea2094a5CDE5e` | ⏳ Not Verified | [View](https://explorer.d-bis.org/addresses/0x8078A09637e47Fa5Ed34F626046Ea2094a5CDE5e) | +| CCIP Sender | `0x105F8A15b819948a89153505762444Ee9f324684` | ⏳ Not Verified | [View](https://explorer.d-bis.org/addresses/0x105F8A15b819948a89153505762444Ee9f324684) | +| CCIPWETH9Bridge | `0x89dd12025bfCD38A168455A44B400e913ED33BE2` | ⏳ Not Verified | [View](https://explorer.d-bis.org/addresses/0x89dd12025bfCD38A168455A44B400e913ED33BE2) | +| CCIPWETH10Bridge | `0xe0E93247376aa097dB308B92e6Ba36bA015535D0` | ⏳ Not Verified | [View](https://explorer.d-bis.org/addresses/0xe0E93247376aa097dB308B92e6Ba36bA015535D0) | +| Price Feed Keeper | `0xD3AD6831aacB5386B8A25BB8D8176a6C8a026f04` | ⏳ Not Verified | [View](https://explorer.d-bis.org/addresses/0xD3AD6831aacB5386B8A25BB8D8176a6C8a026f04) | **Action Required**: Verify all contracts on Blockscout using `./scripts/verify-all-contracts.sh` diff --git a/docs/archive/status/CONTRACT_VERIFICATION_STATUS.md b/docs/archive/status/CONTRACT_VERIFICATION_STATUS.md index 8f81235a..dcb9c764 100644 --- a/docs/archive/status/CONTRACT_VERIFICATION_STATUS.md +++ b/docs/archive/status/CONTRACT_VERIFICATION_STATUS.md @@ -163,7 +163,7 @@ forge verify-contract \ ### Via Blockscout Web UI -1. Navigate to contract address: `https://explorer.d-bis.org/address/
` +1. Navigate to contract address: `https://explorer.d-bis.org/addresses/
` 2. Look for "Contract" tab 3. Check if "Contract Source Code" is visible (indicates verification) diff --git a/docs/archive/tests/CONTRACT_VALIDATION_CHECKLIST.md b/docs/archive/tests/CONTRACT_VALIDATION_CHECKLIST.md index a8c19214..2cc524ec 100644 --- a/docs/archive/tests/CONTRACT_VALIDATION_CHECKLIST.md +++ b/docs/archive/tests/CONTRACT_VALIDATION_CHECKLIST.md @@ -13,7 +13,7 @@ #### Oracle Proxy (`0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6`) - [ ] **Verification Status** - - [ ] Verified on Blockscout: `https://explorer.d-bis.org/address/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6` + - [ ] Verified on Blockscout: `https://explorer.d-bis.org/addresses/0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6` - [ ] Source code visible and matches deployment - [ ] Compiler version matches deployment diff --git a/docs/archive/tests/REMAINING_STEPS_AND_VALIDATION.md b/docs/archive/tests/REMAINING_STEPS_AND_VALIDATION.md index f93168a9..c8680cbe 100644 --- a/docs/archive/tests/REMAINING_STEPS_AND_VALIDATION.md +++ b/docs/archive/tests/REMAINING_STEPS_AND_VALIDATION.md @@ -83,7 +83,7 @@ These contracts were pre-deployed in genesis: **Tools Available**: - Automated script: `./scripts/verify-all-contracts.sh` - Manual verification: See `docs/BLOCKSCOUT_VERIFICATION_GUIDE.md` -- Blockscout UI: `https://explorer.d-bis.org/address/
` +- Blockscout UI: `https://explorer.d-bis.org/addresses/
` ### Priority 2: Contract Validation & Testing diff --git a/multi-chain-execution/package.json b/multi-chain-execution/package.json index a2adfd13..c48c0ef3 100644 --- a/multi-chain-execution/package.json +++ b/multi-chain-execution/package.json @@ -7,7 +7,7 @@ "scripts": { "build": "tsc", "start": "node dist/main.js", - "test": "node --test dist/**/*.test.js 2>/dev/null || true" + "test": "npm run build && node --test dist/lib/non-evm-route-planner.test.js" }, "dependencies": { "ethers": "^6.16.0", diff --git a/multi-chain-execution/src/api/route-routes.ts b/multi-chain-execution/src/api/route-routes.ts index bc9bcda0..4551a01c 100644 --- a/multi-chain-execution/src/api/route-routes.ts +++ b/multi-chain-execution/src/api/route-routes.ts @@ -1,18 +1,26 @@ /** - * Chain138 -> Tezos USDtz route planning API + * Chain138 / ALL Mainnet → hub EVM → non-EVM route planning API */ import { Router, Request, Response } from 'express'; +import { + NON_EVM_FAMILY_META, + defaultSourceAssetForChain, + planNonEvmRoute, + supportedSourceChainIds, + validateDestinationAddress, + type NonEvmFamily, +} from '../lib/non-evm-route-planner.js'; -const TEZOS_REGEX = /^(tz[1-4]|KT1)[1-9A-HJ-NP-Za-km-z]{33}$/; -const CHAIN_138 = 138; -const CHAIN_ALL_MAINNET = 651940; -const CUSDC = '0xf22258f57794CC8E06237084b353Ab30fFfa640b'; -const AUSDC = '0xa95EeD79f84E6A0151eaEb9d441F9Ffd50e8e881'; -/** Active ETH→Tezos bridge (from bridge-capability-matrix) */ -const ETH_TO_TEZOS_PROVIDER = 'Wrap Protocol'; +const router: Router = Router(); -interface RoutePlanRequest { +const NON_EVM_FAMILIES = Object.keys(NON_EVM_FAMILY_META) as NonEvmFamily[]; + +function isNonEvmFamily(s: string): s is NonEvmFamily { + return NON_EVM_FAMILIES.includes(s as NonEvmFamily); +} + +interface Chain138ToUsdtzBody { source_chain_id?: number; source_asset?: string; source_amount?: string; @@ -22,65 +30,90 @@ interface RoutePlanRequest { prefer_non_custodial?: boolean; } -interface RouteHop { - chain: string; - action: string; - protocol: string; - asset_in: string; - amount_in: string; - asset_out: string; - min_amount_out: string; - estimated_fees: string; +interface PlanNonEvmBody { + destination_family: string; + source_chain_id?: number; + source_asset?: string; + source_amount?: string; + destination_address: string; } -interface RoutePlan { - route_id: string; - hops: RouteHop[]; - totalEstimatedFees: string; - estimatedTimeSeconds: number; -} - -const router: Router = Router(); - +/** @deprecated Prefer POST /v1/routes/plan-non-evm with destination_family=tezos_usdtz */ router.post('/v1/routes/chain138-to-usdtz', (req: Request, res: Response) => { try { - const body = req.body as RoutePlanRequest; + const body = req.body as Chain138ToUsdtzBody; const sourceChainId = body.source_chain_id ?? 138; - const sourceAsset = body.source_asset ?? CUSDC; + const sourceAsset = body.source_asset ?? defaultSourceAssetForChain(sourceChainId); const sourceAmount = body.source_amount ?? '0'; const destAddr = body.destination_tezos_address ?? ''; - if (sourceChainId !== CHAIN_138 && sourceChainId !== CHAIN_ALL_MAINNET) { - return res.status(400).json({ valid: false, error: 'Only source_chain_id=138 (Chain138) or 651940 (ALL Mainnet) is supported' }); - } - if (!destAddr.trim() || !TEZOS_REGEX.test(destAddr.trim())) { - return res.status(400).json({ valid: false, error: 'Invalid destination_tezos_address' }); - } - const amount = BigInt(sourceAmount); - if (amount <= 0n) { - return res.status(400).json({ valid: false, error: 'source_amount must be > 0' }); + const destOk = validateDestinationAddress('tezos_usdtz', destAddr); + if (!destOk.ok) { + return res.status(400).json({ valid: false, error: destOk.error }); } - const routeId = `route-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; - const isChain138 = sourceChainId === CHAIN_138; - const sourceLabel = isChain138 ? 'CHAIN138' : 'ALL_MAINNET'; - const stableOut = isChain138 ? CUSDC : AUSDC; - const srcBridge = isChain138 ? 'CCIP' : 'AlltraAdapter'; - const hops: RouteHop[] = [ - { chain: sourceLabel, action: 'SWAP', protocol: isChain138 ? 'EnhancedSwapRouter' : 'AlltraDEX', asset_in: sourceAsset, amount_in: sourceAmount, asset_out: stableOut, min_amount_out: sourceAmount, estimated_fees: '0' }, - { chain: sourceLabel, action: 'BRIDGE', protocol: srcBridge, asset_in: stableOut, amount_in: sourceAmount, asset_out: 'USDC', min_amount_out: sourceAmount, estimated_fees: '0' }, - { chain: 'HUB_EVM', action: 'BRIDGE', protocol: ETH_TO_TEZOS_PROVIDER, asset_in: 'USDC', amount_in: sourceAmount, asset_out: 'USDC', min_amount_out: sourceAmount, estimated_fees: '0' }, - { chain: 'TEZOS', action: 'SWAP', protocol: 'Plenty', asset_in: 'USDC', amount_in: sourceAmount, asset_out: 'USDtz', min_amount_out: sourceAmount, estimated_fees: '0' }, - ]; + const planned = planNonEvmRoute({ + family: 'tezos_usdtz', + source_chain_id: sourceChainId, + source_asset: sourceAsset, + source_amount: sourceAmount, + }); + if (!planned.ok) { + return res.status(400).json({ valid: false, error: planned.error }); + } - const plan: RoutePlan = { - route_id: routeId, - hops, - totalEstimatedFees: '0', - estimatedTimeSeconds: 1800, - }; + res.status(200).json({ valid: true, routes: [planned.plan] }); + } catch (e) { + res.status(500).json({ valid: false, error: e instanceof Error ? e.message : 'Internal error' }); + } +}); - res.status(200).json({ valid: true, routes: [plan] }); +router.get('/v1/routes/non-evm-families', (_req: Request, res: Response) => { + res.status(200).json({ + valid: true, + supported_source_chain_ids: supportedSourceChainIds(), + families: NON_EVM_FAMILY_META, + family_ids: NON_EVM_FAMILIES, + }); +}); + +router.post('/v1/routes/plan-non-evm', (req: Request, res: Response) => { + try { + const body = req.body as PlanNonEvmBody; + const familyRaw = body.destination_family ?? ''; + if (!familyRaw || !isNonEvmFamily(familyRaw)) { + return res.status(400).json({ + valid: false, + error: `destination_family must be one of: ${NON_EVM_FAMILIES.join(', ')}`, + }); + } + + const destAddr = body.destination_address ?? ''; + const destOk = validateDestinationAddress(familyRaw, destAddr); + if (!destOk.ok) { + return res.status(400).json({ valid: false, error: destOk.error }); + } + + const sourceChainId = body.source_chain_id ?? 138; + const sourceAsset = body.source_asset ?? defaultSourceAssetForChain(sourceChainId); + const sourceAmount = body.source_amount ?? '0'; + + const planned = planNonEvmRoute({ + family: familyRaw, + source_chain_id: sourceChainId, + source_asset: sourceAsset, + source_amount: sourceAmount, + }); + if (!planned.ok) { + return res.status(400).json({ valid: false, error: planned.error }); + } + + res.status(200).json({ + valid: true, + destination_family: familyRaw, + destination_address: destAddr.trim(), + routes: [planned.plan], + }); } catch (e) { res.status(500).json({ valid: false, error: e instanceof Error ? e.message : 'Internal error' }); } diff --git a/multi-chain-execution/src/index.ts b/multi-chain-execution/src/index.ts index d7e9acff..166dc7b4 100644 --- a/multi-chain-execution/src/index.ts +++ b/multi-chain-execution/src/index.ts @@ -1,5 +1,16 @@ export * from './chain-adapters/index.js'; export * from './intent/types.js'; +export { + NON_EVM_FAMILY_META, + defaultSourceAssetForChain, + planNonEvmRoute, + supportedSourceChainIds, + validateDestinationAddress, + type NonEvmFamily, + type QuoteHints, + type RouteHop, + type RoutePlan, +} from './lib/non-evm-route-planner.js'; export { createIntent, getIntent, executeIntent, getExecution, getExecutionByIntent } from './eo/execution-orchestrator.js'; export { validateAndPlan } from './trpe/trpe.js'; export { nonceService } from './nonce-service/nonce-service.js'; diff --git a/multi-chain-execution/src/lib/non-evm-route-planner.test.ts b/multi-chain-execution/src/lib/non-evm-route-planner.test.ts new file mode 100644 index 00000000..ef4875ee --- /dev/null +++ b/multi-chain-execution/src/lib/non-evm-route-planner.test.ts @@ -0,0 +1,45 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { + defaultSourceAssetForChain, + planNonEvmRoute, + validateDestinationAddress, +} from './non-evm-route-planner.js'; + +test('defaultSourceAssetForChain uses AUSDC on ALL Mainnet', () => { + assert.equal( + defaultSourceAssetForChain(651940).toLowerCase(), + '0xa95eed79f84e6a0151eaeb9d441f9ffd50e8e881' + ); +}); + +test('validateDestinationAddress rejects empty Solana', () => { + const r = validateDestinationAddress('solana_usdc', ''); + assert.equal(r.ok, false); +}); + +test('planNonEvmRoute returns quote_hints and TRANSFER when already on stable', () => { + const r = planNonEvmRoute({ + family: 'solana_usdc', + source_chain_id: 138, + source_asset: '0xf22258f57794CC8E06237084b353Ab30fFfa640b', + source_amount: '1000000', + }); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.ok(r.plan.quote_hints?.swap_quote); + assert.equal(r.plan.hops[0]!.action, 'TRANSFER'); + assert.equal(r.plan.hops[0]!.protocol, 'Direct'); +}); + +test('planNonEvmRoute uses SWAP when source differs from stable on 138', () => { + const r = planNonEvmRoute({ + family: 'xrpl_usdc', + source_chain_id: 138, + source_asset: '0x93E66202A11B1772E55407B32B44e5Cd8eda7f22', + source_amount: '1000000', + }); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.equal(r.plan.hops[0]!.action, 'SWAP'); +}); diff --git a/multi-chain-execution/src/lib/non-evm-route-planner.ts b/multi-chain-execution/src/lib/non-evm-route-planner.ts new file mode 100644 index 00000000..4ed3d298 --- /dev/null +++ b/multi-chain-execution/src/lib/non-evm-route-planner.ts @@ -0,0 +1,386 @@ +/** + * Multi-hop route plans from Chain 138 / ALL Mainnet through Ethereum hub to non-EVM networks. + * Plans are structural (protocol labels identify bridge families); execution picks a live venue. + * + * Canonical copy — edit here first, then sync `dbis_core/src/core/defi/tezos-usdtz/non-evm-route-planner.ts`. + */ + +export interface RouteHop { + chain: string; + action: string; + protocol: string; + asset_in: string; + amount_in: string; + asset_out: string; + min_amount_out: string; + estimated_fees: string; + /** Optional operator hint for bridge selection or compliance. */ + notes?: string; +} + +/** Optional aggregator / bridge quote entry points for execution layers (LiFi, Jumper, native APIs). */ +export interface QuoteHints { + /** Same-chain swap on source (138 / ALL Mainnet) when the first hop is a SWAP. */ + swap_quote?: string; + /** CCIP / AlltraAdapter fee or quote surfaces for source → Ethereum. */ + source_bridge_quote?: string; + /** Ethereum → destination chain / ledger bridge (Wormhole, CCTP, Axelar, etc.). */ + hub_to_destination_quote?: string; + /** Stable conversion on Ethereum when bridge delivers USDT but the next hop expects USDC (or vice versa). */ + ethereum_stable_conversion?: string; + reference_urls?: string[]; +} + +export interface RoutePlan { + route_id: string; + hops: RouteHop[]; + totalEstimatedFees: string; + estimatedTimeSeconds: number; + destination_family: NonEvmFamily; + quote_hints?: QuoteHints; +} + +export type NonEvmFamily = 'tezos_usdtz' | 'solana_usdc' | 'tron_usdt' | 'xrpl_usdc'; + +const CHAIN_138 = 138; +const CHAIN_ALL_MAINNET = 651940; + +const CUSDC = '0xf22258f57794CC8E06237084b353Ab30fFfa640b'; +const AUSDC = '0xa95EeD79f84E6A0151eaEb9d441F9Ffd50e8e881'; +const AUSDT = '0x015B1897Ed5279930bC2Be46F661894d219292A6'; + +const TEZOS_REGEX = /^(tz[1-4]|KT1)[1-9A-HJ-NP-Za-km-z]{33}$/; +/** Base58 Solana pubkey — length varies slightly by encoding; exclude leading zeros edge cases. */ +const SOLANA_REGEX = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/; +const TRON_REGEX = /^T[1-9A-HJ-NP-Za-km-z]{33}$/; +const XRPL_REGEX = /^r[1-9A-HJ-NP-Za-km-z]{24,34}$/; + +const ETH_TO_TEZOS_PROVIDER = 'Wrap Protocol'; + +export function validateDestinationAddress(family: NonEvmFamily, addr: string): { ok: true } | { ok: false; error: string } { + const trimmed = addr.trim(); + if (!trimmed) { + return { ok: false, error: 'destination_address is required' }; + } + switch (family) { + case 'tezos_usdtz': + return TEZOS_REGEX.test(trimmed) ? { ok: true } : { ok: false, error: 'Invalid Tezos destination address' }; + case 'solana_usdc': + return SOLANA_REGEX.test(trimmed) ? { ok: true } : { ok: false, error: 'Invalid Solana base58 address' }; + case 'tron_usdt': + return TRON_REGEX.test(trimmed) ? { ok: true } : { ok: false, error: 'Invalid Tron base58check address' }; + case 'xrpl_usdc': + return XRPL_REGEX.test(trimmed) ? { ok: true } : { ok: false, error: 'Invalid XRPL classic address' }; + default: { + const _exhaustive: never = family; + return { ok: false, error: `Unknown family ${_exhaustive as string}` }; + } + } +} + +export function supportedSourceChainIds(): number[] { + return [CHAIN_138, CHAIN_ALL_MAINNET]; +} + +/** Default stable-like asset for routing when the client omits `source_asset`. */ +export function defaultSourceAssetForChain(source_chain_id: number): string { + return source_chain_id === CHAIN_ALL_MAINNET ? AUSDC : CUSDC; +} + +function assertPositiveAmount(source_amount: string): bigint | null { + try { + const amount = BigInt(source_amount); + return amount > 0n ? amount : null; + } catch { + return null; + } +} + +function newRouteId(): string { + return `route-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; +} + +function resolveStableOutAllMainnet(sourceAsset: string): string { + const lower = sourceAsset.toLowerCase(); + if (lower === AUSDT.toLowerCase() || lower === AUSDC.toLowerCase()) return sourceAsset; + return AUSDC; +} + +/** Human ticker after bridging to Ethereum mainnet for subsequent hub hops. */ +function hubStableTicker(stableAddress: string): 'USDC' | 'USDT' { + return stableAddress.toLowerCase() === AUSDT.toLowerCase() ? 'USDT' : 'USDC'; +} + +function evmStableBridgeSegment( + sourceChainId: number, + sourceAsset: string, + _sourceAmount: string +): { sourceLabel: string; stableOut: string; srcBridge: string; isChain138: boolean } | null { + if (sourceChainId !== CHAIN_138 && sourceChainId !== CHAIN_ALL_MAINNET) { + return null; + } + const isChain138 = sourceChainId === CHAIN_138; + const stableOut = isChain138 ? CUSDC : resolveStableOutAllMainnet(sourceAsset); + return { + sourceLabel: isChain138 ? 'CHAIN138' : 'ALL_MAINNET', + stableOut, + srcBridge: isChain138 ? 'CCIP' : 'AlltraAdapter', + isChain138, + }; +} + +/** + * Build hops from source EVM chain → Ethereum stable at hub (same pattern as Tezos USDtz path). + */ +function buildEvmToHubHops( + sourceLabel: string, + isChain138: boolean, + stableOut: string, + srcBridge: string, + sourceAsset: string, + sourceAmount: string +): RouteHop[] { + const needsSwap = sourceAsset.toLowerCase() !== stableOut.toLowerCase(); + const bridgeOutTicker = hubStableTicker(stableOut); + + return [ + { + chain: sourceLabel, + action: needsSwap ? 'SWAP' : 'TRANSFER', + protocol: needsSwap ? (isChain138 ? 'EnhancedSwapRouter' : 'AlltraDEX') : 'Direct', + asset_in: sourceAsset, + amount_in: sourceAmount, + asset_out: stableOut, + min_amount_out: sourceAmount, + estimated_fees: '0', + }, + { + chain: sourceLabel, + action: 'BRIDGE', + protocol: srcBridge, + asset_in: stableOut, + amount_in: sourceAmount, + asset_out: bridgeOutTicker, + min_amount_out: sourceAmount, + estimated_fees: '0', + }, + ]; +} + +function buildQuoteHints(family: NonEvmFamily, stableOut: string, isChain138: boolean): QuoteHints { + const hints: QuoteHints = { + swap_quote: isChain138 + ? 'token-aggregation / EnhancedSwapRouter quote on Chain 138' + : 'AlltraDEX or configured router on ALL Mainnet', + source_bridge_quote: isChain138 ? 'CCIP fee / delivery quote (LINK)' : 'AlltraAdapter bridge fee quote', + hub_to_destination_quote: '', + reference_urls: ['https://docs.llama.fi/', 'https://jumper.exchange/', 'https://portalbridge.com/'], + }; + + if (stableOut.toLowerCase() === AUSDT.toLowerCase()) { + hints.ethereum_stable_conversion = + 'If the next hub hop expects USDC, swap USDT→USDC on Ethereum before the bridge to Tezos or non-EVM.'; + } + + switch (family) { + case 'tezos_usdtz': + hints.hub_to_destination_quote = `${ETH_TO_TEZOS_PROVIDER} + Tezos Plenty USDtz quote`; + break; + case 'solana_usdc': + hints.hub_to_destination_quote = 'Wormhole / Portal / Circle CCTP quote (Ethereum → Solana USDC)'; + break; + case 'tron_usdt': + hints.hub_to_destination_quote = 'TRON↔Ethereum bridge or aggregator quote (USDT leg)'; + break; + case 'xrpl_usdc': + hints.hub_to_destination_quote = 'Axelar / Chainflip / XRPL bridge quote'; + break; + } + + return hints; +} + +export function planNonEvmRoute(params: { + family: NonEvmFamily; + source_chain_id: number; + source_asset: string; + source_amount: string; +}): { ok: true; plan: RoutePlan } | { ok: false; error: string } { + const amount = assertPositiveAmount(params.source_amount); + if (amount === null) { + return { ok: false, error: 'source_amount must be a positive integer string' }; + } + + const seg = evmStableBridgeSegment(params.source_chain_id, params.source_asset, params.source_amount); + if (!seg) { + return { ok: false, error: `Only source_chain_id=${CHAIN_138} (Chain138) or ${CHAIN_ALL_MAINNET} (ALL Mainnet) is supported` }; + } + + const { sourceLabel, stableOut, srcBridge, isChain138 } = seg; + const prefix = buildEvmToHubHops(sourceLabel, isChain138, stableOut, srcBridge, params.source_asset, params.source_amount); + + const crossBoundaryNote = + 'EVM→non-EVM boundary: choose a live bridge with inventory (Wormhole/Portal/CCTP, Axelar, TRON official, etc.).'; + + let tail: RouteHop[]; + let estimatedTimeSeconds: number; + + switch (params.family) { + case 'tezos_usdtz': { + const hubStableForWrap: 'USDC' | 'USDT' = + stableOut.toLowerCase() === AUSDT.toLowerCase() ? 'USDT' : 'USDC'; + tail = [ + { + chain: 'HUB_EVM', + action: 'BRIDGE', + protocol: ETH_TO_TEZOS_PROVIDER, + asset_in: hubStableForWrap, + amount_in: params.source_amount, + asset_out: hubStableForWrap, + min_amount_out: params.source_amount, + estimated_fees: '0', + notes: + hubStableForWrap === 'USDT' + ? 'Confirm Wrap path accepts USDT from Ethereum; otherwise swap to USDC on Ethereum first.' + : undefined, + }, + { + chain: 'TEZOS', + action: 'SWAP', + protocol: 'Plenty', + asset_in: hubStableForWrap, + amount_in: params.source_amount, + asset_out: 'USDtz', + min_amount_out: params.source_amount, + estimated_fees: '0', + }, + ]; + estimatedTimeSeconds = 1800; + break; + } + case 'solana_usdc': + tail = [ + { + chain: 'HUB_EVM', + action: 'BRIDGE', + protocol: 'USDC→Solana (Wormhole / Portal / Circle CCTP)', + asset_in: 'USDC', + amount_in: params.source_amount, + asset_out: 'USDC', + min_amount_out: params.source_amount, + estimated_fees: '0', + notes: + stableOut.toLowerCase() === AUSDT.toLowerCase() + ? `${crossBoundaryNote} Swap USDT→USDC on Ethereum before Solana bridge when the venue requires USDC.` + : crossBoundaryNote, + }, + { + chain: 'SOLANA', + action: 'SETTLE', + protocol: 'SPL (USDC)', + asset_in: 'USDC', + amount_in: params.source_amount, + asset_out: 'USDC', + min_amount_out: params.source_amount, + estimated_fees: '0', + notes: 'Destination mint is venue-specific (native USDC vs portal-wrapped); reconcile off-chain.', + }, + ]; + estimatedTimeSeconds = 1200; + break; + case 'tron_usdt': + tail = [ + { + chain: 'HUB_EVM', + action: 'BRIDGE', + protocol: 'USDT→Tron (TRON-Ethereum bridge / Synapse / Multichain-class)', + asset_in: 'USDC', + amount_in: params.source_amount, + asset_out: 'USDT', + min_amount_out: params.source_amount, + estimated_fees: '0', + notes: `${crossBoundaryNote} Often swap USDC→USDT on Ethereum before Tron bridge when required.`, + }, + { + chain: 'TRON', + action: 'SETTLE', + protocol: 'TRC-20 (USDT)', + asset_in: 'USDT', + amount_in: params.source_amount, + asset_out: 'USDT', + min_amount_out: params.source_amount, + estimated_fees: '0', + }, + ]; + estimatedTimeSeconds = 2400; + break; + case 'xrpl_usdc': + tail = [ + { + chain: 'HUB_EVM', + action: 'BRIDGE', + protocol: 'USDC→XRPL (Axelar / Chainflip / XRPL-Axo — IOU vs stable)', + asset_in: 'USDC', + amount_in: params.source_amount, + asset_out: 'USDC', + min_amount_out: params.source_amount, + estimated_fees: '0', + notes: `${crossBoundaryNote} Confirm trust lines / destination tags with issuer policy.`, + }, + { + chain: 'XRPL', + action: 'SETTLE', + protocol: 'Issued stable / RLUSD-class', + asset_in: 'USDC', + amount_in: params.source_amount, + asset_out: 'USDC', + min_amount_out: params.source_amount, + estimated_fees: '0', + notes: 'Map to live XRPL USD stable issuer per ops policy.', + }, + ]; + estimatedTimeSeconds = 1800; + break; + default: { + const _exhaustive: never = params.family; + return { ok: false, error: `Unknown family ${_exhaustive as string}` }; + } + } + + const plan: RoutePlan = { + route_id: newRouteId(), + hops: [...prefix, ...tail], + totalEstimatedFees: '0', + estimatedTimeSeconds, + destination_family: params.family, + quote_hints: buildQuoteHints(params.family, stableOut, isChain138), + }; + + return { ok: true, plan }; +} + +export const NON_EVM_FAMILY_META: Record< + NonEvmFamily, + { label: string; destinationAsset: string; hubBridgeHint: string } +> = { + tezos_usdtz: { + label: 'Tezos USDtz', + destinationAsset: 'USDtz', + hubBridgeHint: ETH_TO_TEZOS_PROVIDER, + }, + solana_usdc: { + label: 'Solana USDC (SPL)', + destinationAsset: 'USDC', + hubBridgeHint: 'Wormhole / Portal / CCTP', + }, + tron_usdt: { + label: 'Tron USDT (TRC-20)', + destinationAsset: 'USDT', + hubBridgeHint: 'TRON-Ethereum bridge / aggregator-class routes', + }, + xrpl_usdc: { + label: 'XRPL USD stable', + destinationAsset: 'USDC', + hubBridgeHint: 'Axelar / Chainflip / XRPL hooks', + }, +}; diff --git a/package.json b/package.json index bff279df..11331b7d 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "test:basic": "cd mcp-proxmox && node test-basic-tools.js", "test:workflows": "cd mcp-proxmox && node test-workflows.js", "verify:ws-chain138": "node scripts/verify-ws-rpc-chain138.mjs", + "multi-chain:test": "pnpm --filter @proxmox/multi-chain-execution test", "ura:validate": "node scripts/validate/validate-universal-resource-activation.mjs", "ura:validate-profiles": "node scripts/validate/validate-ura-policy-profiles.mjs", "ura:validate-closure": "node scripts/ura/validate-manifest-closure.mjs", @@ -46,6 +47,23 @@ "mission-control:test": "pnpm --filter mission-control test", "token-lists:validate-metadata": "node scripts/validation/validate-token-list-metadata.mjs", "pool-matrix:validate": "node scripts/validation/validate-pool-creation-matrix.mjs", + "cw:full-readiness": "bash scripts/verify/check-cw-full-operational-readiness.sh", + "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-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", + "cwusdc:role-audit": "python3 scripts/verify/audit-cwusdc-mainnet-roles.py", + "cwusdc:role-appendix": "python3 scripts/verify/build-cwusdc-role-deployment-appendix.py", + "cwusdc:doc-links": "python3 scripts/verify/check-cwusdc-institutional-doc-links.py", + "cwusdc:submission-prefill": "python3 scripts/verify/build-cwusdc-provider-submission-prefill.py", + "cwusdc:provider-monitor": "bash scripts/verify/run-cwusdc-provider-monitoring-snapshot.sh", + "cwusdc:evidence-bundle": "bash scripts/verify/build-cwusdc-institutional-evidence-bundle.sh", + "provider:cmc-sanity": "python3 scripts/verify/check-cmc-provider-report-sanity.py", + "provider:submission-index": "python3 scripts/verify/build-external-submission-packet-index.py", + "ecosystem:cmc-top10": "python3 scripts/verify/build-cmc-top10-ecosystem-coverage.py", + "non-evm:requirements": "python3 scripts/verify/build-non-evm-requirement-stubs.py", "all-mainnet:readiness": "node scripts/status/generate-all-mainnet-readiness.mjs", "all-mainnet:pool-balances": "node scripts/status/check-all-mainnet-required-pool-balances.mjs", "all-mainnet:canary-preflight": "node scripts/status/preflight-all-mainnet-canaries.mjs", @@ -63,6 +81,7 @@ "engine-x:canary-preflight": "bash scripts/verify/preflight-dbis-engine-x-virtual-batch-canary.sh", "engine-x:v2-deploy": "EXECUTE=0 bash scripts/deployment/deploy-engine-x-v2-mainnet.sh", "engine-x:borrow-vault-deploy": "EXECUTE=0 bash scripts/deployment/deploy-engine-x-xaut-usdc-borrow-vault-mainnet.sh", + "engine-x:single-sided-dodo-deploy": "EXECUTE=0 bash scripts/deployment/deploy-engine-x-single-sided-dodo-cwusdc-vault-mainnet.sh", "engine-x:indexed-vault-deploy": "EXECUTE=0 bash scripts/deployment/deploy-engine-x-indexed-liquidity-vault-mainnet.sh", "engine-x:indexed-lp-migration": "EXECUTE=0 bash scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh", "engine-x:legacy-retirement": "EXECUTE=0 bash scripts/deployment/retire-engine-x-legacy-vault.sh", @@ -70,6 +89,9 @@ "engine-x:univ3-swap-proof": "EXECUTE=0 bash scripts/deployment/run-engine-x-univ3-public-swap-proof.sh", "engine-x:indexed-proof-record": "EXECUTE=0 bash scripts/deployment/record-engine-x-indexed-liquidity-proof.sh", "engine-x:public-readiness": "bash scripts/verify/check-engine-x-public-indexed-readiness.sh", + "engine-x:mev-defense": "bash scripts/verify/check-engine-x-mev-defense-readiness.sh", + "engine-x:automation-advisor": "bash scripts/verify/plan-engine-x-automated-liquidity-advisor.sh", + "engine-x:weth-liquidity-eval": "bash scripts/verify/evaluate-mainnet-cwusdc-weth-liquidity-surfaces.sh", "engine-x:audit-manifest": "bash scripts/verify/build-engine-x-audit-manifest.sh" }, "keywords": [ diff --git a/pr-workspace/ledger-chain138-integration/step-01-currency/currencies.chain138.ts b/pr-workspace/ledger-chain138-integration/step-01-currency/currencies.chain138.ts index 39277818..78e0f88c 100644 --- a/pr-workspace/ledger-chain138-integration/step-01-currency/currencies.chain138.ts +++ b/pr-workspace/ledger-chain138-integration/step-01-currency/currencies.chain138.ts @@ -27,7 +27,7 @@ export const defiOracleMetaMainnetCurrency = { // disableCountervalue: true, // uncomment if fiat should not be shown explorerViews: [ { - address: "https://explorer.d-bis.org/address/$address", + address: "https://explorer.d-bis.org/addresses/$address", tx: "https://explorer.d-bis.org/tx/$hash", token: "https://explorer.d-bis.org/token/$contractAddress?a=$address", }, @@ -54,7 +54,7 @@ export const defiOracleMetaMainnetCurrencyRaw = { blockAvgTime: 2, explorerViews: [ { - address: "https://explorer.d-bis.org/address/$address", + address: "https://explorer.d-bis.org/addresses/$address", tx: "https://explorer.d-bis.org/tx/$hash", token: "https://explorer.d-bis.org/token/$contractAddress?a=$address", }, diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 00000000..e13e7f99 --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,66 @@ +{ + "include": [ + "scripts", + "mcp-proxmox", + "docs/scripts", + "smom-dbis-138/scripts", + "smom-dbis-138/services", + "smom-dbis-138/connectors", + "smom-dbis-138/orchestration", + "dbis_core/sdk/python", + "ai-mcp-pmm-controller", + "metaverseDubai/scripts", + "gru-docs/scripts", + "pr-workspace/app-ethereum" + ], + "exclude": [ + "ProxmoxVE", + "explorer-monorepo", + "MEV_Bot", + "miracles_in_motion", + "OMNIS", + "alltra-lifi-settlement", + "amex-high-value-rails", + "arromis-monorepo", + "atomic-swap-dapp", + "cross-chain-pmm-lps", + "cross-chain-pmm-lps-publish", + "tmp", + ".tmp", + ".codex-artifacts", + ".devin", + ".venv-checkjson", + "third-party", + "vendor", + "pr-workspace/app-ethereum/glyphs", + "**/.*", + "**/node_modules", + "**/__pycache__", + "**/.git", + "**/.pnpm-store", + "**/.pnpm", + "**/dist", + "**/build", + "**/.next", + "**/out", + "**/coverage", + "**/.mypy_cache", + "**/.pytest_cache", + "**/.ruff_cache", + "**/.tox", + "**/.turbo", + "**/.parcel-cache", + "**/.nx", + "**/.vite", + "**/.cache", + "**/forge-cache", + "**/broadcast", + "**/artifacts", + "logs", + "reports", + "backups", + "output", + "venv", + "home" + ] +} diff --git a/reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json b/reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json index f04981ab..8b69f39d 100644 --- a/reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json +++ b/reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json @@ -1,6 +1,6 @@ { - "generated_at": "2026-04-22T04:59:28Z", - "write_discovered": false, + "generated_at": "2026-05-09T05:43:24Z", + "write_discovered": true, "discovered_live_pair_count": 19, "healthy_live_pair_count": 10, "writes_applied": [], @@ -20,14 +20,14 @@ "poolAddress": "0xC28706F899266b36BC43cc072b3a921BDf2C48D9", "live": true, "health": { - "baseReserveRaw": "6267", - "quoteReserveRaw": "6267", - "baseReserveUnits": "0.006267", - "quoteReserveUnits": "0.006267", - "priceQuotePerBase": "1", - "deviationBps": "0", + "baseReserveRaw": "1267071063797", + "quoteReserveRaw": "2167762", + "baseReserveUnits": "1267071.063797", + "quoteReserveUnits": "2.167762", + "priceQuotePerBase": "0.000001710844846779092339761738687", + "deviationBps": "9999.982891551532209076602383", "depthOk": false, - "parityOk": true, + "parityOk": false, "healthy": false } }, @@ -43,12 +43,12 @@ "poolAddress": "0x422608c5dDff909675ac2C5F872fD42f16B9287A", "live": true, "health": { - "baseReserveRaw": "9898542641851", - "quoteReserveRaw": "9898540782562", - "baseReserveUnits": "9898542.641851", - "quoteReserveUnits": "9898540.782562", - "priceQuotePerBase": "0.9999998121653795641660116571", - "deviationBps": "0.0018783462043583398834290000", + "baseReserveRaw": "9900803423129", + "quoteReserveRaw": "9900803423131", + "baseReserveUnits": "9900803.423129", + "quoteReserveUnits": "9900803.423131", + "priceQuotePerBase": "1.000000000000202003808633131", + "deviationBps": "2.020038086331310000E-9", "depthOk": true, "parityOk": true, "healthy": true diff --git a/reports/extraction/promod-uniswap-v2-phase1-funding-actions-latest.json b/reports/extraction/promod-uniswap-v2-phase1-funding-actions-latest.json index ef07d570..938b2925 100644 --- a/reports/extraction/promod-uniswap-v2-phase1-funding-actions-latest.json +++ b/reports/extraction/promod-uniswap-v2-phase1-funding-actions-latest.json @@ -1,5 +1,5 @@ { - "generated_at": "2026-04-17T18:13:05Z", + "generated_at": "2026-05-09T21:07:05Z", "program_name": "Mr. Promod Uniswap V2 phase 1 funding actions", "purpose": "Strict phase-1 funding actions: seed-now, mint-then-seed, and bridge-possible notes for each target chain.", "signer": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", @@ -8,15 +8,24 @@ "chain_id": 1, "network": "Ethereum Mainnet", "phase_1_pair": "cWUSDT/cWUSDC", - "action_type": "seed_now", - "tokens_missing": [], + "action_type": "mint_destination_then_seed", + "tokens_missing": [ + "cWUSDC" + ], "minimum_gas_issue": false, "gas_topup_note": "No minimum gas top-up issue from the latest preflight snapshot.", - "recommended_seed_human": "8888511.867466", + "recommended_seed_human": "1000", "bridge_possible": true, "bridge_note": "Bridge path is structurally available for chain `1` via `CW_BRIDGE_MAINNET` and `bridgeAvailable=true`, but the repo-native executable path today is destination-side cW minting. Cross-chain c* -> cW delivery still follows `docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md` and `docs/07-ccip/CW_BRIDGE_APPROACH.md` rather than a single helper script.", - "mint_steps": [], - "exact_post_funding_deploy_block": "source smom-dbis-138/scripts/load-env.sh >/dev/null\nexport RPC_URL=\"${ETHEREUM_MAINNET_RPC}\"\nexport FACTORY=\"${CHAIN_1_UNISWAP_V2_FACTORY}\"\nexport ROUTER=\"${CHAIN_1_UNISWAP_V2_ROUTER}\"\nexport CWUSDT=\"0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE\"\nexport CWUSDC=\"0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a\"\nexport SIGNER=\"$(cast wallet address --private-key \"$PRIVATE_KEY\")\"\nexport AMOUNT_RAW=\"8888511867466\"\nexport DEADLINE=\"$(( $(date +%s) + 3600 ))\"\n\nPAIR=\"$(cast call \"$FACTORY\" 'getPair(address,address)(address)' \"$CWUSDT\" \"$CWUSDC\" --rpc-url \"$RPC_URL\")\"\nif [[ \"$PAIR\" == \"0x0000000000000000000000000000000000000000\" ]]; then\n cast send \"$FACTORY\" 'createPair(address,address)(address)' \"$CWUSDT\" \"$CWUSDC\" \\\n --private-key \"$PRIVATE_KEY\" --rpc-url \"$RPC_URL\"\nfi\n\ncast send \"$CWUSDT\" 'approve(address,uint256)(bool)' \"$ROUTER\" \"$AMOUNT_RAW\" \\\n --private-key \"$PRIVATE_KEY\" --rpc-url \"$RPC_URL\"\n\ncast send \"$CWUSDC\" 'approve(address,uint256)(bool)' \"$ROUTER\" \"$AMOUNT_RAW\" \\\n --private-key \"$PRIVATE_KEY\" --rpc-url \"$RPC_URL\"\n\ncast send \"$ROUTER\" \\\n 'addLiquidity(address,address,uint256,uint256,uint256,uint256,address,uint256)' \\\n \"$CWUSDT\" \"$CWUSDC\" \"$AMOUNT_RAW\" \"$AMOUNT_RAW\" \"$AMOUNT_RAW\" \"$AMOUNT_RAW\" \"$SIGNER\" \"$DEADLINE\" \\\n --private-key \"$PRIVATE_KEY\" --rpc-url \"$RPC_URL\"\n\npython3 scripts/lib/promod_uniswap_v2_live_pair_discovery.py --write-discovered\nbash scripts/verify/build-promod-uniswap-v2-promotion-gates.sh\nnode cross-chain-pmm-lps/scripts/validate-deployment-status.cjs cross-chain-pmm-lps/config/deployment-status.json" + "mint_steps": [ + { + "token": "cWUSDC", + "amount_human": "1000", + "amount_raw": 1000000000, + "exact_mint_block": "source smom-dbis-138/scripts/load-env.sh >/dev/null\ncast send \"0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a\" 'mint(address,uint256)' \"$(cast wallet address --private-key \"$PRIVATE_KEY\")\" \"1000000000\" \\\n --rpc-url \"${ETHEREUM_MAINNET_RPC}\" --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 100000" + } + ], + "exact_post_funding_deploy_block": null }, { "chain_id": 10, @@ -27,7 +36,7 @@ "cWUSDT", "cWUSDC" ], - "minimum_gas_issue": true, + "minimum_gas_issue": false, "gas_topup_note": "Top up native gas on Optimism before minting or seeding; current balance is below the 0.001 safety threshold.", "recommended_seed_human": "1000", "bridge_possible": true, @@ -53,29 +62,13 @@ "network": "Cronos", "phase_1_pair": "cWUSDT/cWUSDC", "action_type": "mint_destination_then_seed", - "tokens_missing": [ - "cWUSDT", - "cWUSDC" - ], + "tokens_missing": [], "minimum_gas_issue": false, "gas_topup_note": "No minimum gas top-up issue from the latest preflight snapshot.", "recommended_seed_human": "1000", "bridge_possible": true, "bridge_note": "Bridge path is structurally available for chain `25` via `CW_BRIDGE_CRONOS` and `bridgeAvailable=true`, but the repo-native executable path today is destination-side cW minting. Cross-chain c* -> cW delivery still follows `docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md` and `docs/07-ccip/CW_BRIDGE_APPROACH.md` rather than a single helper script.", - "mint_steps": [ - { - "token": "cWUSDT", - "amount_human": "1000", - "amount_raw": 1000000000, - "exact_mint_block": "source smom-dbis-138/scripts/load-env.sh >/dev/null\ncast send \"0x72948a7a813B60b37Cd0c920C4657DbFF54312b8\" 'mint(address,uint256)' \"$(cast wallet address --private-key \"$PRIVATE_KEY\")\" \"1000000000\" \\\n --rpc-url \"${CRONOS_RPC_URL}\" --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 100000" - }, - { - "token": "cWUSDC", - "amount_human": "1000", - "amount_raw": 1000000000, - "exact_mint_block": "source smom-dbis-138/scripts/load-env.sh >/dev/null\ncast send \"0x932566E5bB6BEBF6B035B94f3DE1f75f126304Ec\" 'mint(address,uint256)' \"$(cast wallet address --private-key \"$PRIVATE_KEY\")\" \"1000000000\" \\\n --rpc-url \"${CRONOS_RPC_URL}\" --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 100000" - } - ], + "mint_steps": [], "exact_post_funding_deploy_block": null }, { @@ -83,29 +76,13 @@ "network": "BSC", "phase_1_pair": "cWUSDT/cWUSDC", "action_type": "mint_destination_then_seed", - "tokens_missing": [ - "cWUSDT", - "cWUSDC" - ], + "tokens_missing": [], "minimum_gas_issue": false, "gas_topup_note": "No minimum gas top-up issue from the latest preflight snapshot.", "recommended_seed_human": "1000", "bridge_possible": true, "bridge_note": "Bridge path is structurally available for chain `56` via `CW_BRIDGE_BSC` and `bridgeAvailable=true`, but the repo-native executable path today is destination-side cW minting. Cross-chain c* -> cW delivery still follows `docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md` and `docs/07-ccip/CW_BRIDGE_APPROACH.md` rather than a single helper script.", - "mint_steps": [ - { - "token": "cWUSDT", - "amount_human": "1000", - "amount_raw": 1000000000, - "exact_mint_block": "source smom-dbis-138/scripts/load-env.sh >/dev/null\ncast send \"0x9a1D0dBEE997929ED02fD19E0E199704d20914dB\" 'mint(address,uint256)' \"$(cast wallet address --private-key \"$PRIVATE_KEY\")\" \"1000000000\" \\\n --rpc-url \"${BSC_RPC_URL}\" --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 100000" - }, - { - "token": "cWUSDC", - "amount_human": "1000", - "amount_raw": 1000000000, - "exact_mint_block": "source smom-dbis-138/scripts/load-env.sh >/dev/null\ncast send \"0x5355148C4740fcc3D7a96F05EdD89AB14851206b\" 'mint(address,uint256)' \"$(cast wallet address --private-key \"$PRIVATE_KEY\")\" \"1000000000\" \\\n --rpc-url \"${BSC_RPC_URL}\" --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 100000" - } - ], + "mint_steps": [], "exact_post_funding_deploy_block": null }, { @@ -142,15 +119,24 @@ "chain_id": 137, "network": "Polygon", "phase_1_pair": "cWUSDT/cWUSDC", - "action_type": "seed_now", - "tokens_missing": [], + "action_type": "mint_destination_then_seed", + "tokens_missing": [ + "cWUSDC" + ], "minimum_gas_issue": false, "gas_topup_note": "No minimum gas top-up issue from the latest preflight snapshot.", - "recommended_seed_human": "996.297636", + "recommended_seed_human": "1000", "bridge_possible": true, "bridge_note": "Bridge path is structurally available for chain `137` via `CW_BRIDGE_POLYGON` and `bridgeAvailable=true`, but the repo-native executable path today is destination-side cW minting. Cross-chain c* -> cW delivery still follows `docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md` and `docs/07-ccip/CW_BRIDGE_APPROACH.md` rather than a single helper script.", - "mint_steps": [], - "exact_post_funding_deploy_block": "source smom-dbis-138/scripts/load-env.sh >/dev/null\nexport RPC_URL=\"${POLYGON_MAINNET_RPC}\"\nexport FACTORY=\"${CHAIN_137_UNISWAP_V2_FACTORY}\"\nexport ROUTER=\"${CHAIN_137_UNISWAP_V2_ROUTER}\"\nexport CWUSDT=\"0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF\"\nexport CWUSDC=\"0xd6969bC19b53f866C64f2148aE271B2Dae0C58E4\"\nexport SIGNER=\"$(cast wallet address --private-key \"$PRIVATE_KEY\")\"\nexport AMOUNT_RAW=\"996297636\"\nexport DEADLINE=\"$(( $(date +%s) + 3600 ))\"\n\nPAIR=\"$(cast call \"$FACTORY\" 'getPair(address,address)(address)' \"$CWUSDT\" \"$CWUSDC\" --rpc-url \"$RPC_URL\")\"\nif [[ \"$PAIR\" == \"0x0000000000000000000000000000000000000000\" ]]; then\n cast send \"$FACTORY\" 'createPair(address,address)(address)' \"$CWUSDT\" \"$CWUSDC\" \\\n --private-key \"$PRIVATE_KEY\" --rpc-url \"$RPC_URL\"\nfi\n\ncast send \"$CWUSDT\" 'approve(address,uint256)(bool)' \"$ROUTER\" \"$AMOUNT_RAW\" \\\n --private-key \"$PRIVATE_KEY\" --rpc-url \"$RPC_URL\"\n\ncast send \"$CWUSDC\" 'approve(address,uint256)(bool)' \"$ROUTER\" \"$AMOUNT_RAW\" \\\n --private-key \"$PRIVATE_KEY\" --rpc-url \"$RPC_URL\"\n\ncast send \"$ROUTER\" \\\n 'addLiquidity(address,address,uint256,uint256,uint256,uint256,address,uint256)' \\\n \"$CWUSDT\" \"$CWUSDC\" \"$AMOUNT_RAW\" \"$AMOUNT_RAW\" \"$AMOUNT_RAW\" \"$AMOUNT_RAW\" \"$SIGNER\" \"$DEADLINE\" \\\n --private-key \"$PRIVATE_KEY\" --rpc-url \"$RPC_URL\"\n\npython3 scripts/lib/promod_uniswap_v2_live_pair_discovery.py --write-discovered\nbash scripts/verify/build-promod-uniswap-v2-promotion-gates.sh\nnode cross-chain-pmm-lps/scripts/validate-deployment-status.cjs cross-chain-pmm-lps/config/deployment-status.json" + "mint_steps": [ + { + "token": "cWUSDC", + "amount_human": "1000", + "amount_raw": 1000000000, + "exact_mint_block": "source smom-dbis-138/scripts/load-env.sh >/dev/null\ncast send \"0xd6969bC19b53f866C64f2148aE271B2Dae0C58E4\" 'mint(address,uint256)' \"$(cast wallet address --private-key \"$PRIVATE_KEY\")\" \"1000000000\" \\\n --rpc-url \"${POLYGON_MAINNET_RPC}\" --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 100000" + } + ], + "exact_post_funding_deploy_block": null }, { "chain_id": 8453, @@ -161,7 +147,7 @@ "cWUSDT", "cWUSDC" ], - "minimum_gas_issue": true, + "minimum_gas_issue": false, "gas_topup_note": "Top up native gas on Base before minting or seeding; current balance is below the 0.001 safety threshold.", "recommended_seed_human": "1000", "bridge_possible": true, @@ -246,21 +232,28 @@ "chain_id": 43114, "network": "Avalanche", "phase_1_pair": "cWUSDT/cWUSDC", - "action_type": "mint_missing_side_then_seed", + "action_type": "mint_destination_then_seed", "tokens_missing": [ + "cWUSDT", "cWUSDC" ], "minimum_gas_issue": false, "gas_topup_note": "No minimum gas top-up issue from the latest preflight snapshot.", - "recommended_seed_human": "0.8", + "recommended_seed_human": "1000", "bridge_possible": true, "bridge_note": "Bridge path is structurally available for chain `43114` via `CW_BRIDGE_AVALANCHE` and `bridgeAvailable=true`, but the repo-native executable path today is destination-side cW minting. Cross-chain c* -> cW delivery still follows `docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md` and `docs/07-ccip/CW_BRIDGE_APPROACH.md` rather than a single helper script.", "mint_steps": [ + { + "token": "cWUSDT", + "amount_human": "1000", + "amount_raw": 1000000000, + "exact_mint_block": "source smom-dbis-138/scripts/load-env.sh >/dev/null\ncast send \"0x8142BA530B08f3950128601F00DaaA678213DFdf\" 'mint(address,uint256)' \"$(cast wallet address --private-key \"$PRIVATE_KEY\")\" \"1000000000\" \\\n --rpc-url \"${AVALANCHE_RPC_URL}\" --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 100000" + }, { "token": "cWUSDC", - "amount_human": "0.8", - "amount_raw": 800000, - "exact_mint_block": "source smom-dbis-138/scripts/load-env.sh >/dev/null\ncast send \"0x0C242b513008Cd49C89078F5aFb237A3112251EB\" 'mint(address,uint256)' \"$(cast wallet address --private-key \"$PRIVATE_KEY\")\" \"800000\" \\\n --rpc-url \"${AVALANCHE_RPC_URL}\" --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 100000" + "amount_human": "1000", + "amount_raw": 1000000000, + "exact_mint_block": "source smom-dbis-138/scripts/load-env.sh >/dev/null\ncast send \"0x0C242b513008Cd49C89078F5aFb237A3112251EB\" 'mint(address,uint256)' \"$(cast wallet address --private-key \"$PRIVATE_KEY\")\" \"1000000000\" \\\n --rpc-url \"${AVALANCHE_RPC_URL}\" --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 100000" } ], "exact_post_funding_deploy_block": null diff --git a/reports/extraction/promod-uniswap-v2-phase1-funding-readiness-latest.json b/reports/extraction/promod-uniswap-v2-phase1-funding-readiness-latest.json index 677a4551..e094f133 100644 --- a/reports/extraction/promod-uniswap-v2-phase1-funding-readiness-latest.json +++ b/reports/extraction/promod-uniswap-v2-phase1-funding-readiness-latest.json @@ -1,5 +1,5 @@ { - "generated_at": "2026-04-17T20:05:54Z", + "generated_at": "2026-05-09T20:53:57Z", "program_name": "Mr. Promod Uniswap V2 phase 1 funding readiness", "purpose": "Live deployer-wallet funding view for seeding cWUSDT/cWUSDC phase-1 pools chain by chain.", "signer": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", @@ -30,30 +30,28 @@ "pair_address": "0x422608c5dDff909675ac2C5F872fD42f16B9287A", "pair_exists": true, "pair_seeded_live": true, - "native_balance": "0.028982898459925766", + "native_balance": "0.002397937659907006", "native_balance_ok_for_ops": true, "cwusdt": { "address": "0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE", "decimals": 6, - "balance_raw": "397197013", - "balance_human": "397.197013", + "balance_raw": "8523829088635", + "balance_human": "8523829.088635", "allowance_raw": "0" }, "cwusdc": { "address": "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a", "decimals": 6, - "balance_raw": "0", - "balance_human": "0", - "allowance_raw": "0" + "balance_raw": "56705345129226", + "balance_human": "56705345.129226", + "allowance_raw": "300011896" }, - "max_equal_seed_human": "0", - "reserve0_raw": "8888511867466", - "reserve1_raw": "[8.888e12]", + "max_equal_seed_human": "8523829.088635", + "reserve0_raw": "9900803423131", + "reserve1_raw": "[9.9e12]", "execution_status": "completed", "funding_ready_now": false, - "funding_blockers": [ - "cWUSDC balance is zero" - ] + "funding_blockers": [] }, { "chain_id": 10, @@ -66,30 +64,29 @@ "pair_address": "0xe28BFf306442a8A512d2441847c27211a7C4C613", "pair_exists": true, "pair_seeded_live": true, - "native_balance": "0.001980670026642148", - "native_balance_ok_for_ops": true, + "native_balance": "0.000430532335030017", + "native_balance_ok_for_ops": false, "cwusdt": { "address": "0x04B2AE3c3bb3d70Df506FAd8717b0FBFC78ED7E6", "decimals": 6, - "balance_raw": "0", - "balance_human": "0", - "allowance_raw": "0" + "balance_raw": "3000984101", + "balance_human": "3000.984101", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, "cwusdc": { "address": "0x377a5FaA3162b3Fc6f4e267301A3c817bAd18105", "decimals": 6, - "balance_raw": "0", - "balance_human": "0", - "allowance_raw": "0" + "balance_raw": "3000986900", + "balance_human": "3000.9869", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, - "max_equal_seed_human": "0", + "max_equal_seed_human": "3000.984101", "reserve0_raw": "3000000000", "reserve1_raw": "[3e9]", "execution_status": "completed", "funding_ready_now": false, "funding_blockers": [ - "cWUSDT balance is zero", - "cWUSDC balance is zero" + "native gas below 0.001" ] }, { @@ -103,23 +100,23 @@ "pair_address": "0x438d8E1a8E311d2ae4b75a38E0044675fD324133", "pair_exists": true, "pair_seeded_live": true, - "native_balance": "22.401143346977489259", + "native_balance": "19.154691909389182692", "native_balance_ok_for_ops": true, "cwusdt": { "address": "0x72948a7a813B60b37Cd0c920C4657DbFF54312b8", "decimals": 6, - "balance_raw": "1000000000", - "balance_human": "1000", - "allowance_raw": "0" + "balance_raw": "999991800", + "balance_human": "999.9918", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, "cwusdc": { "address": "0x932566E5bB6BEBF6B035B94f3DE1f75f126304Ec", "decimals": 6, - "balance_raw": "1000000000", - "balance_human": "1000", - "allowance_raw": "0" + "balance_raw": "999993800", + "balance_human": "999.9938", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, - "max_equal_seed_human": "1000", + "max_equal_seed_human": "999.9918", "reserve0_raw": "3000000000", "reserve1_raw": "[3e9]", "execution_status": "completed", @@ -137,23 +134,23 @@ "pair_address": "0x7e308c12bd609607DF9C4137E30235D5A9Da2A64", "pair_exists": true, "pair_seeded_live": true, - "native_balance": "0.010626271367072709", + "native_balance": "0.003681967051288879", "native_balance_ok_for_ops": true, "cwusdt": { "address": "0x9a1D0dBEE997929ED02fD19E0E199704d20914dB", "decimals": 6, - "balance_raw": "1000000000", - "balance_human": "1000", - "allowance_raw": "0" + "balance_raw": "997995900", + "balance_human": "997.9959", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, "cwusdc": { "address": "0x5355148C4740fcc3D7a96F05EdD89AB14851206b", "decimals": 6, - "balance_raw": "1000000000", - "balance_human": "1000", - "allowance_raw": "0" + "balance_raw": "997995900", + "balance_human": "997.9959", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, - "max_equal_seed_human": "1000", + "max_equal_seed_human": "997.9959", "reserve0_raw": "3000000000", "reserve1_raw": "[3e9]", "execution_status": "completed", @@ -171,31 +168,28 @@ "pair_address": "0x064d782Be0113Cb427f3Af0De9335C9F34A1de34", "pair_exists": true, "pair_seeded_live": true, - "native_balance": "2.407080479781350729", + "native_balance": "0.551078988224567304", "native_balance_ok_for_ops": true, "cwusdt": { "address": "0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF", "decimals": 6, - "balance_raw": "0", - "balance_human": "0", - "allowance_raw": "0" + "balance_raw": "999995800", + "balance_human": "999.9958", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, "cwusdc": { "address": "0xd6969bC19b53f866C64f2148aE271B2Dae0C58E4", "decimals": 6, - "balance_raw": "0", - "balance_human": "0", - "allowance_raw": "0" + "balance_raw": "998995900", + "balance_human": "998.9959", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, - "max_equal_seed_human": "0", + "max_equal_seed_human": "998.9959", "reserve0_raw": "4000000000", "reserve1_raw": "[4e9]", "execution_status": "completed", "funding_ready_now": false, - "funding_blockers": [ - "cWUSDT balance is zero", - "cWUSDC balance is zero" - ] + "funding_blockers": [] }, { "chain_id": 137, @@ -208,30 +202,28 @@ "pair_address": "0x3411A20C39773d1A18cb53864893b236f41f1e99", "pair_exists": true, "pair_seeded_live": true, - "native_balance": "24.318080574425380349", + "native_balance": "15.030270707053166727", "native_balance_ok_for_ops": true, "cwusdt": { "address": "0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF", "decimals": 6, - "balance_raw": "2686028", - "balance_human": "2.686028", - "allowance_raw": "0" + "balance_raw": "3001992967", + "balance_human": "3001.992967", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, "cwusdc": { "address": "0xd6969bC19b53f866C64f2148aE271B2Dae0C58E4", "decimals": 6, - "balance_raw": "0", - "balance_human": "0", - "allowance_raw": "0" + "balance_raw": "2999903173", + "balance_human": "2999.903173", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, - "max_equal_seed_human": "0", - "reserve0_raw": "996297636", - "reserve1_raw": "[9.962e8]", + "max_equal_seed_human": "2999.903173", + "reserve0_raw": "10994302668", + "reserve1_raw": "[1.099e10]", "execution_status": "completed", "funding_ready_now": false, - "funding_blockers": [ - "cWUSDC balance is zero" - ] + "funding_blockers": [] }, { "chain_id": 8453, @@ -244,31 +236,28 @@ "pair_address": "0x56eb93f747D3B8251d43849cC72B39c1899fcaca", "pair_exists": true, "pair_seeded_live": true, - "native_balance": "0.002877934067617928", + "native_balance": "0.004958123781375043", "native_balance_ok_for_ops": true, "cwusdt": { "address": "0x04B2AE3c3bb3d70Df506FAd8717b0FBFC78ED7E6", "decimals": 6, - "balance_raw": "0", - "balance_human": "0", - "allowance_raw": "0" + "balance_raw": "5998983900", + "balance_human": "5998.9839", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, "cwusdc": { "address": "0x377a5FaA3162b3Fc6f4e267301A3c817bAd18105", "decimals": 6, - "balance_raw": "0", - "balance_human": "0", - "allowance_raw": "0" + "balance_raw": "5998985900", + "balance_human": "5998.9859", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, - "max_equal_seed_human": "0", + "max_equal_seed_human": "5998.9839", "reserve0_raw": "1000000000", "reserve1_raw": "[1e9]", "execution_status": "completed", "funding_ready_now": false, - "funding_blockers": [ - "cWUSDT balance is zero", - "cWUSDC balance is zero" - ] + "funding_blockers": [] }, { "chain_id": 42161, @@ -281,30 +270,29 @@ "pair_address": "0x2b2ea2EA9e7617de09FCb5063BEfafa01A9ef2b4", "pair_exists": true, "pair_seeded_live": true, - "native_balance": "0.001055113904178255", - "native_balance_ok_for_ops": true, + "native_balance": "0.000971971100341626", + "native_balance_ok_for_ops": false, "cwusdt": { "address": "0x73ADaF7dBa95221c080db5631466d2bC54f6a76B", "decimals": 6, - "balance_raw": "0", - "balance_human": "0", - "allowance_raw": "0" + "balance_raw": "1999884800", + "balance_human": "1999.8848", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, "cwusdc": { "address": "0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF", "decimals": 6, - "balance_raw": "0", - "balance_human": "0", - "allowance_raw": "0" + "balance_raw": "999884800", + "balance_human": "999.8848", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, - "max_equal_seed_human": "0", + "max_equal_seed_human": "999.8848", "reserve0_raw": "3000000000", "reserve1_raw": "[3e9]", "execution_status": "completed", "funding_ready_now": false, "funding_blockers": [ - "cWUSDT balance is zero", - "cWUSDC balance is zero" + "native gas below 0.001" ] }, { @@ -318,31 +306,28 @@ "pair_address": "0x6F97dE8AB68c722DcBC02cEA0cE6B587b8210052", "pair_exists": true, "pair_seeded_live": true, - "native_balance": "8.537307308649465530", + "native_balance": "5.373727945247599766", "native_balance_ok_for_ops": true, "cwusdt": { "address": "0x73376eB92c16977B126dB9112936A20Fa0De3442", "decimals": 6, - "balance_raw": "0", - "balance_human": "0", - "allowance_raw": "0" + "balance_raw": "6999988900", + "balance_human": "6999.9889", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, "cwusdc": { "address": "0x4C38F9A5ed68A04cd28a72E8c68C459Ec34576f3", "decimals": 6, - "balance_raw": "0", - "balance_human": "0", - "allowance_raw": "0" + "balance_raw": "5999988900", + "balance_human": "5999.9889", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, - "max_equal_seed_human": "0", + "max_equal_seed_human": "5999.9889", "reserve0_raw": "1000000000", "reserve1_raw": "[1e9]", "execution_status": "completed", "funding_ready_now": false, - "funding_blockers": [ - "cWUSDT balance is zero", - "cWUSDC balance is zero" - ] + "funding_blockers": [] }, { "chain_id": 43114, @@ -355,31 +340,28 @@ "pair_address": "0x79c8eA153e77BC69b989f59F69BfA44c466D5DEE", "pair_exists": true, "pair_seeded_live": true, - "native_balance": "0.446784013286210977", + "native_balance": "0.014891739291454170", "native_balance_ok_for_ops": true, "cwusdt": { "address": "0x8142BA530B08f3950128601F00DaaA678213DFdf", "decimals": 6, - "balance_raw": "0", - "balance_human": "0", - "allowance_raw": "0" + "balance_raw": "998985900", + "balance_human": "998.9859", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, "cwusdc": { "address": "0x0C242b513008Cd49C89078F5aFb237A3112251EB", "decimals": 6, - "balance_raw": "0", - "balance_human": "0", - "allowance_raw": "0" + "balance_raw": "998985900", + "balance_human": "998.9859", + "allowance_raw": "115792089237316195423570985008687907853269984665640564039457584007913129639935" }, - "max_equal_seed_human": "0", - "reserve0_raw": "800000", - "reserve1_raw": "[8e5]", + "max_equal_seed_human": "998.9859", + "reserve0_raw": "10000800000", + "reserve1_raw": "[1e10]", "execution_status": "completed", "funding_ready_now": false, - "funding_blockers": [ - "cWUSDT balance is zero", - "cWUSDC balance is zero" - ] + "funding_blockers": [] } ], "source_artifacts": [ diff --git a/reports/status/cusdc-chain138-explorer-decimal-normalization-fix-20260511.md b/reports/status/cusdc-chain138-explorer-decimal-normalization-fix-20260511.md new file mode 100644 index 00000000..d1e04a38 --- /dev/null +++ b/reports/status/cusdc-chain138-explorer-decimal-normalization-fix-20260511.md @@ -0,0 +1,90 @@ +# cUSDC Chain 138 Explorer Decimal Normalization Fix + +Generated: 2026-05-11T05:00:00Z + +## Issue + +The Chain 138 Explorer token page for native `cUSDC` showed visible liquidity as: + +`$5,180,095,723,066,127` + +The same token uses 6 decimals. The Etherscan wrapped `cWUSDC` page demonstrates the correct interpretation for the wrapped transport asset: raw base units must be divided by `10^6` before display. + +## Correct Interpretation + +| Asset | Surface | Contract | Decimals | +|---|---|---|---:| +| `cUSDC` | Chain 138 Explorer native / hub-side asset | `0xf22258f57794CC8E06237084b353Ab30fFfa640b` | `6` | +| `cWUSDC` | Etherscan wrapped Mainnet transport asset | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | `6` | + +The problematic Chain 138 Explorer liquidity value normalizes as: + +```text +5,180,095,723,066,127 / 10^6 = 5,180,095,723.066127 +``` + +So the display error was a 6-decimal raw-unit normalization issue, not evidence of quadrillion-dollar liquidity. + +The `/tokens` list showed the same pattern for the stable-token cards: + +| Token | Raw/stale visible liquidity | Corrected 6-decimal display | +|---|---:|---:| +| `cUSDT` | `$2,270,037,545,569` | `$2,270,037.545569` | +| `cUSDC` | `$5,180,095,723,066,127` | `$5,180,095,723.066127` | + +The gold tokens in the same screenshot (`cXAUC`, `cXAUT`) did not exhibit this raw-unit symptom because their displayed liquidity was already below the token's normalized supply value at the current canonical XAU price. + +## Code Completed + +Updated: + +- `smom-dbis-138/services/token-aggregation/src/api/routes/tokens.ts` +- `smom-dbis-138/services/token-aggregation/src/api/routes/tokens.test.ts` +- `explorer-monorepo/frontend/src/services/api/tokenAggregation.ts` +- `explorer-monorepo/frontend/src/services/api/tokens.test.ts` + +Behavior added: + +- If token market liquidity is larger than the token's normalized supply value, and dividing by `10 ** decimals` produces a plausible value, the token API returns the normalized liquidity value. +- This preserves ordinary market rows while correcting stale/raw liquidity rows for 6-decimal assets such as `cUSDC`. +- The Explorer frontend applies the same defensive normalization to token aggregation snapshots so `/tokens` cannot display stale raw-unit liquidity while a backend deploy is pending. + +## Verification + +Commands run: + +```bash +cd smom-dbis-138/services/token-aggregation +npm test -- --runTestsByPath src/api/routes/tokens.test.ts src/services/chain138-dodo-liquidity.test.ts +npm run build + +cd ../../../explorer-monorepo/frontend +npm run test:unit -- src/services/api/tokens.test.ts +``` + +Result: + +- `13` focused tests passed. +- TypeScript build passed. +- Frontend token API unit test passed. + +## Deployment Verification + +Completed: 2026-05-11T05:13:26Z + +Patched services were deployed to the live Chain 138 Explorer host and restarted on VMID 5000: + +- `solacescanscout-frontend`: active +- `token-aggregation`: active + +Public API output now returns normalized liquidity: + +| Token | Decimals | Live API liquidity | +|---|---:|---:| +| `cUSDT` | 6 | `2270037.545568842` | +| `cUSDC` | 6 | `5180095723.066127` | + +The live `/tokens` page now hydrates with the corrected visible values: + +- `cUSDT`: `$2,270,038` +- `cUSDC`: `$5,180,095,723` diff --git a/reports/status/cusdc-token-page-review-20260511.png b/reports/status/cusdc-token-page-review-20260511.png new file mode 100644 index 00000000..4c8fa39b Binary files /dev/null and b/reports/status/cusdc-token-page-review-20260511.png differ diff --git a/reports/status/cw-mesh-full-operational-live-readiness-20260509.md b/reports/status/cw-mesh-full-operational-live-readiness-20260509.md new file mode 100644 index 00000000..2dff69d6 --- /dev/null +++ b/reports/status/cw-mesh-full-operational-live-readiness-20260509.md @@ -0,0 +1,67 @@ +# cW Mesh Full Operational Live Readiness + +Generated: `2026-05-09` + +## Executive Status + +Chain 138 is the canonical source of truth for the GRU c* asset family and public cW* transport mirrors. The active public cW mesh is broad, but the system should not yet be described as fully implemented, operational, and live with every optional add-on until the remaining live execution gates below are closed. + +## Complete / Active + +| Area | Status | Evidence | +|---|---|---| +| Chain 138 source of truth | Complete | `cross-chain-pmm-lps/config/deployment-status.json` has `homeChainId=138`; `config/token-mapping-multichain.json` has `cToCwSymbolMapping`. | +| Canonical token registry | Updated | `smom-dbis-138/services/token-aggregation/src/config/canonical-tokens.ts` now includes the active nine-chain promoted GRU cW set and the broader 12-token wrapped fiat/commodity family. | +| Active cW public mesh chains | Active | Nine-chain promoted surface: `1, 10, 56, 100, 137, 8453, 42161, 42220, 43114` (Cronos `25` excluded from this public count; may still appear in legacy tooling/reports). | +| cWUSDC / cWUSDT mesh | Active | `reports/status/cw-mesh-deployment-matrix-latest.json` reports cWUSDC and cWUSDT on the active public chains with UniV2 pair discovery and cWUSDT/cWUSDC live/healthy. | +| Broader cW fiat/commodity family | Registry active | cWEURC, cWEURT, cWGBPC, cWGBPT, cWAUDC, cWJPYC, cWCHFC, cWCADC, cWXAUC, and cWXAUT are now represented in canonical token aggregation for the active public mesh. | +| Mainnet cWUSDC technical packet | Complete | `reports/status/mainnet-cwusdc-technical-completion-20260508.json`; Etherscan profile packet and logo assets under `docs/04-configuration/etherscan/`. | +| Branding/tracker packets | Complete internally | Etherscan, CoinGecko/CMC, logo, E2E, and branding docs exist under `docs/04-configuration/etherscan/` and `docs/04-configuration/coingecko/`. | +| Automated readiness gate | Added | `pnpm cw:full-readiness` writes `reports/status/cw-full-operational-readiness-latest.{json,md}`. Current in-repo source-of-truth status is green; live bridge E2E and external tracker approvals remain blocked. | +| Bridge evidence gate | Added | `pnpm cw:bridge-e2e-readiness` writes `reports/status/cw-multitoken-bridge-e2e-latest.{json,md}`. Latest evidence shows Chain 138, Ethereum, and Avalanche passing; Optimism, Cronos, BSC, Gnosis, Polygon, Base, Arbitrum, and Celo still need CWMultiToken L2 receiver/mapping remediation. | +| External tracker evidence gate | Added | `pnpm cwusdc:external-trackers` writes `reports/status/cwusdc-external-trackers-live-latest.{json,md}`. Latest evidence shows Etherscan, CMC DEX, and GeckoTerminal reachable; CoinGecko listing and DexScreener pair API payloads remain blocked. | + +## Not Yet Fully Complete + +| Gate | Current state | Required to call it fully live | +|---|---|---| +| Dedicated cW mint/burn receivers | Pending | Deploy or wire `CWMultiTokenBridgeL1` on Chain 138 and `CWMultiTokenBridgeL2` per active destination, grant minter/burner roles, and prove c* -> cW* -> c* transfers. | +| Wemix `1111` | Scaffold only | Chain 138 CCIP router must support the Wemix selector; bridge proof transfer must pass; then promote from `planned_gas_scaffold`. | +| ALL Mainnet `651940` | Bridge corridor live, not cW mesh | Decide whether ALL Mainnet is a cW destination or only an Alltra corridor; if cW destination, deploy cW inventory and same-chain routing; otherwise document as non-cW optional add-on. | +| Official-USDC public liquidity depth | Capital-gated | Fund official Mainnet USDC, rerun repeg plan, repair public cWUSDC/USDC liquidity, and sustain policy-floor depth. | +| Etherscan token profile | External pending | Submit profile with `@d-bis.org` email and 32x32 SVG; capture approval evidence. | +| CoinGecko / CMC listing | External pending | Submit tracker packets; wait for public listing and circulating-supply acceptance. | +| DexScreener / GeckoTerminal profile metadata | External pending | Submit/update token and pool profile metadata. | +| Wallet/token-list distribution | Pending | Publish updated token lists/logos, then submit to wallet ecosystems where applicable. | +| Oracle/direct price feed | Not live for cWUSDC | Keep valuation precedence documented or deploy/obtain approved direct feed. | +| E2E monitoring | Needs production gate | Add recurring checks for cW receiver roles, bridge proof transfers, LP depth, tracker metadata, and public endpoint health. | + +## Optional Add-Ons Required For "All Optional Add-Ons" + +1. **Public tracker profiles:** Etherscan, CoinGecko, CoinMarketCap, DexScreener, GeckoTerminal. +2. **Wallet recognition:** token-list exports, logo hosting, Trust Wallet / MetaMask / wallet import packages where applicable. +3. **Bridge proof suite:** live c* lock/burn/mint/release proofs for each active destination chain. +4. **Liquidity policy:** per-chain minimum depth policy, automated top-up/repeg reports, and public LP repair execution evidence. +5. **Oracle policy:** direct cWUSDC feed or documented valuation precedence with periodic review. +6. **Monitoring:** daily/CI reports for contract code, roles, balances, LP depth, price deviation, tracker status, and bridge sendability. +7. **Wemix promotion:** only after router support and proof transfer succeeds. +8. **ALL Mainnet decision:** promote as cW destination or keep as separate Alltra corridor with its own docs. + +## Recommended Execution Order + +1. Keep the canonical registry and docs reconciled with the deployment graph. +2. Deploy/wire dedicated cW receivers for active public mesh chains. +3. Run one small c* -> cW* -> c* proof transfer per active chain. +4. Fund official-USDC liquidity gaps and execute public liquidity repair. +5. Submit/complete Etherscan profile, then mirror the same facts to CoinGecko, CMC, DexScreener, and GeckoTerminal. +6. Publish wallet-ready token-list/logo updates. +7. Add recurring monitoring and a daily readiness report. +8. Promote Wemix and/or ALL Mainnet only after their respective gates pass. + +## Validation Performed + +- `pnpm --dir smom-dbis-138/services/token-aggregation test -- --runInBand canonical-tokens.test.ts` +- `pnpm token-lists:validate-metadata` +- `pnpm cw:full-readiness` +- `pnpm cw:bridge-e2e-readiness` +- `pnpm cwusdc:external-trackers` diff --git a/reports/status/cw-multitoken-l2-receiver-remediation-20260509.md b/reports/status/cw-multitoken-l2-receiver-remediation-20260509.md new file mode 100644 index 00000000..ee9811ca --- /dev/null +++ b/reports/status/cw-multitoken-l2-receiver-remediation-20260509.md @@ -0,0 +1,81 @@ +# CWMultiToken L2 Receiver Remediation - 2026-05-09 + +## Scope + +Completed live deployment/configuration for the eight chains that previously failed CWMultiToken bridge E2E readiness: + +| Chain | Receiver | +| --- | --- | +| Optimism `10` | `0x0b92EFd17f5FFA8E5941fdF7E8Ce1E670C3BD67f` | +| Cronos `25` | `0xc4e2ce800Ec5441a0D0B5787010F7363BF175B3E` | +| BSC `56` | `0x0909Fc58D8014FA8e8DB778F6bC913B923694889` | +| Gnosis `100` | `0xE3a1dA8Eb0fbE0968250656C0cC38411aC187CBa` | +| Polygon `137` | `0x69b0D30789E403ea8e69A4Fe95a66d358EF7D510` | +| Base `8453` | `0x159c8E779731625Ba4fE92E3AD18F9D217Ba79fb` | +| Arbitrum `42161` | `0x6627b125e4E8e0f274b6aF876737f6dB7526f26C` | +| Celo `42220` | `0x32aD687F24F77bF8C86605c202c829163Ac5Ab36` | + +## Completed Configuration + +- Deployed or reused a `CWMultiTokenBridgeL2` receiver on each failed chain. +- Updated `smom-dbis-138/.env` `CW_BRIDGE_*` source-of-truth entries for the receiver addresses. +- Configured each L2 receiver destination back to Chain 138 L1 bridge `0x152eD3e9912161b76BDFd368D0C84B7C31C10dE7`. +- Configured Chain 138 L1 destinations for each remote chain selector. +- Configured the core token pairs on each receiver: + - `Compliant_USDT_cW` + - `Compliant_USDC_cW` +- Granted each receiver `MINTER_ROLE` and `BURNER_ROLE` on its remote cWUSDT and cWUSDC mirrors. + +## Deployer Balance Check + +Deployer: `0x4A666F96fC8764181194447A7dFdb7d471b301C8` + +Post-remediation balances were still non-zero on all eight remediated chains: + +| Chain | Balance Wei | +| --- | ---: | +| Optimism | `430532335030017` | +| Cronos | `19154691909389182692` | +| BSC | `3681967051288879` | +| Gnosis | `551078988224567304` | +| Polygon | `15030270707053166727` | +| Base | `4958123781375043` | +| Arbitrum | `971971100341626` | +| Celo | `5373727945247599766` | + +## Verification + +Passed: + +- `pnpm cw:bridge-e2e-readiness` + - `readyForProduction: true` + - `allActiveChainsPassed: true` + - `activeChainCount: 10` + - `failedChainIds: []` +- `pnpm cw:full-readiness` + - `inRepoSourceOfTruthConfigured: true` + - `cw_mint_burn_bridge_live_e2e: pass` + - Remaining blocked gate: `external_trackers_live` +- `pnpm token-lists:validate-metadata` +- `pnpm --dir smom-dbis-138/services/token-aggregation test -- --runInBand canonical-tokens.test.ts` + +Evidence: + +- `reports/status/cw-multitoken-bridge-e2e-latest.json` +- `reports/status/cw-multitoken-bridge-e2e-latest.md` +- `reports/status/cw-full-operational-readiness-latest.json` +- `reports/status/cw-full-operational-readiness-latest.md` +- `reports/status/cw-multitoken-l2-remediation-20260509T150147Z.jsonl` +- `reports/status/cw-multitoken-l2-remediation-20260509T150513Z.jsonl` +- `reports/status/cw-multitoken-l2-remediation-20260509T150647Z.jsonl` + +## Remaining Blocker + +The CWMultiToken bridge blocker is resolved. The remaining `fullyOperationalAndLive: false` status is external tracker approval/indexing, not L2 receiver configuration: + +- `external_trackers_live` +- failed required tracker IDs: + - `coingecko_token_price_api` + - `dexscreener_v3_pool` + - `dexscreener_v2_pool` + diff --git a/reports/status/cwusdc-etherscan-token-page-review-20260511.md b/reports/status/cwusdc-etherscan-token-page-review-20260511.md new file mode 100644 index 00000000..8401f274 --- /dev/null +++ b/reports/status/cwusdc-etherscan-token-page-review-20260511.md @@ -0,0 +1,85 @@ +# cWUSDC Etherscan Token Page Review + +Generated: 2026-05-11T04:52:44Z + +Reviewed page: + +`https://etherscan.io/token/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a` + +## Summary + +Etherscan resolves the Ethereum Mainnet cWUSDC contract and shows the ERC-20 supply, holder count, verified source, and contract ABI. The token is still not profile-approved as `Wrapped cUSDC` / `cWUSDC` in Etherscan's primary display layer, and Etherscan is not yet showing USD market value fields. + +## Chain 138 Explorer Relationship + +The Etherscan link is the Ethereum Mainnet wrapped representation of the Chain 138 Explorer source asset relationship: + +| Surface | Role | Contract | +|---|---|---| +| Chain 138 Explorer | Native / hub-side `cUSDC` settlement asset | `0xf22258f57794CC8E06237084b353Ab30fFfa640b` | +| Etherscan | Ethereum Mainnet wrapped transport `cWUSDC` | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | + +Reviewers should treat the Etherscan token as the wrapped off-hub transport version. Chain 138 Explorer evidence should be used to explain the source-asset and system-of-record relationship, but Etherscan profile, supply, transfer, holder, and market-data submissions must remain scoped to the Ethereum Mainnet wrapped contract. + +## Decimal Normalization Relevance + +The Etherscan page explains the Chain 138 Explorer display issue: both the wrapped Mainnet `cWUSDC` and the Chain 138 hub-side `cUSDC` use 6 decimals. Etherscan displays the wrapped supply as `10,451,316,981.309788`, applying the 6-decimal divisor to raw units. + +The Chain 138 Explorer token-aggregation response was exposing `liquidityUsd: 5180095723066127` for `cUSDC`. That value is consistent with a raw 6-decimal unit value being displayed as dollars. Applying the same 6-decimal normalization yields: + +| Raw displayed value | Decimals | Corrected display value | +|---:|---:|---:| +| `5,180,095,723,066,127` | `6` | `5,180,095,723.066127` | + +Code update: `smom-dbis-138/services/token-aggregation/src/api/routes/tokens.ts` now normalizes clearly raw-unit liquidity rows before returning token market data. The regression test is in `smom-dbis-138/services/token-aggregation/src/api/routes/tokens.test.ts`. + +## Observed Page State + +| Signal | Current result | +|---|---| +| Page title / social metadata | `ERC-20 Token | Address: 0x2de5f116...c24b9284a | Etherscan` | +| Token reputation metadata | `Unknown` | +| Header token label | Generic `Token`; rendered symbol surface shows `ERC20 ***` | +| Tooltip / contract metadata | `Wrapped cUSDC (cWUSDC)` is present as a tooltip string | +| Max total supply | `10,451,316,981.309788 ERC20 ***` | +| Holders | `3,783` | +| Token contract | `0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a`, 6 decimals | +| Onchain market cap | blank | +| Circulating supply market cap | blank | +| Exchange data source section | `Coingecko`, but no accepted cWUSDC market row is visible | +| Contract verification | Source code verified as similar match to `0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE` | +| Contract name | `CompliantWrappedToken` | +| Compiler | `v0.8.20+commit.a1b79de6` | +| Audit surface | No contract security audit submitted | + +## Findings + +1. Etherscan profile approval is still the first blocker. + The page does not yet show the canonical `Wrapped cUSDC` / `cWUSDC` branding in the main token title, social metadata, and token display rows. It still exposes a generic `ERC20 ***` surface. + +2. Etherscan USD value is still blocked by upstream price acceptance. + The contract supply is visible, but both Etherscan market-cap fields are blank. This remains consistent with the current provider monitor result: CoinGecko does not yet return a positive USD price for the exact Ethereum Mainnet cWUSDC contract. + +3. The supply field is visible and can be used as submission evidence. + Etherscan reports `10,451,316,981.309788` units with 6 decimals. This matches the repo-side supply/circulating-supply attestation workstream and should be attached to Etherscan, CoinGecko, and CMC submissions. + +4. Contract verification is not the blocker. + The source/ABI surface is available and identifies `CompliantWrappedToken`. The missing pieces are profile metadata, token image/reputation, market-data acceptance, and optional audit evidence. + +5. The Etherscan page already exposes the update path. + The page includes `Update Token Info`, `Update Name Tag or Label`, `Submit Burn Details`, `Report/Flag Address`, and `Submit Audit` actions. The next operator action is authenticated Etherscan token-profile submission using the existing packet. + +## Required Next Actions + +| Priority | Action | Packet / evidence | +|---:|---|---| +| 1 | Submit or re-submit the Etherscan token profile update. | `docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md` | +| 2 | Use the canonical name, symbol, website, email, and 32x32 logo. | `Wrapped cUSDC`, `cWUSDC`, `https://d-bis.org/`, `submissions@d-bis.org`, `https://d-bis.org/tokens/cwusdc.svg` | +| 3 | Attach supply/circulating-supply proof. | `reports/status/cwusdc-supply-circulating-attestation-latest.*` | +| 4 | Submit CoinGecko and CMC updates for the exact Mainnet contract. | `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md` | +| 5 | Re-run propagation monitoring after every external response. | `python3 scripts/verify/monitor-cwusdc-etherscan-value-propagation.py` | +| 6 | Consider submitting a contract audit link if available. | Etherscan `Submit Audit` route | + +## Operator Note + +Do not mix Chain 138 `cUSDC` transfer volume or market activity into Ethereum Mainnet `cWUSDC` submissions. Etherscan should receive Ethereum Mainnet `cWUSDC` identity, supply, profile, and price evidence only, with Chain 138 `cUSDC` referenced only as the canonical source-asset relationship. diff --git a/reports/status/cwusdc-external-tracker-remediation-20260509.md b/reports/status/cwusdc-external-tracker-remediation-20260509.md new file mode 100644 index 00000000..adaa8657 --- /dev/null +++ b/reports/status/cwusdc-external-tracker-remediation-20260509.md @@ -0,0 +1,45 @@ +# cWUSDC External Tracker Remediation - 2026-05-09 + +## Result + +Repo-side remediation is complete, but the `external_trackers_live` gate cannot honestly be marked live yet because CoinGecko and DexScreener still require third-party acceptance/indexing. + +## Completed + +- Rechecked CoinGecko, DexScreener, GeckoTerminal, CoinMarketCap DEX, and Etherscan public surfaces. +- Confirmed GeckoTerminal V3 pool evidence is live: + - `https://www.geckoterminal.com/eth/pools/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3` + - GeckoTerminal identifies it as `cWUSDC/USDC` on Uniswap V3 with a low-liquidity warning. +- Updated `scripts/verify/check-cwusdc-external-trackers-live.py` to probe DexScreener's current `token-pairs/v1` and `tokens/v1` endpoints instead of relying only on legacy pair endpoints. +- Corrected stale submission-packet wording that previously described DexScreener as indexed. +- Added a CoinGecko listing request payload: + - `docs/04-configuration/coingecko/submissions/cwusdc-coingecko-listing-request-20260509.json` +- Added a DexScreener indexing/profile packet: + - `docs/04-configuration/dexscreener/CWUSDC_DEXSCREENER_INDEXING_AND_PROFILE_PACKET_20260509.md` + +## Live Blockers + +| Surface | Current status | Required completion evidence | +|---|---|---| +| CoinGecko token price API | Empty response for cWUSDC contract | API returns cWUSDC under `simple/token_price/ethereum` | +| DexScreener token pairs API | Empty array for cWUSDC | `token-pairs/v1/ethereum/` returns at least one pool | +| DexScreener profile/order | No approved order | `orders/v1/ethereum/` shows approved profile order or token page metadata appears | + +## On-Chain Funding Boundary + +The smallest honest public UniV2 repair canary is currently blocked by official Mainnet USDC funding: + +| Item | Amount | +|---|---:| +| Wallet USDC | `0.343655 USDC` | +| UniV2 repair canary requirement | `1,657.650239 USDC` | +| UniV2 repair canary shortfall | `1,657.306584 USDC` | +| Public policy-floor quote-side top-up | `2,497.832239 USDC` | +| Public policy-floor shortfall | `2,497.488584 USDC` | + +## Verification Commands + +```bash +pnpm cwusdc:external-trackers +pnpm cw:full-readiness +``` diff --git a/reports/status/cwusdc-institutional-hardening-completion-20260511.md b/reports/status/cwusdc-institutional-hardening-completion-20260511.md new file mode 100644 index 00000000..9d354415 --- /dev/null +++ b/reports/status/cwusdc-institutional-hardening-completion-20260511.md @@ -0,0 +1,58 @@ +# cWUSDC Institutional Hardening Completion + +Generated: `2026-05-11` + +## Completed Now + +The following repo-controlled institutional hardening items have been completed: + +| Item | Status | Artifact | +|---|---|---| +| Evidence bundle index | Complete | `docs/04-configuration/etherscan/CWUSDC_EVIDENCE_BUNDLE_INDEX.md` | +| Supply/circulating methodology | Complete | `docs/04-configuration/etherscan/CWUSDC_SUPPLY_AND_CIRCULATING_METHODOLOGY.md` | +| Security/audit disclosure | Complete | `docs/04-configuration/etherscan/CWUSDC_SECURITY_AND_AUDIT_DISCLOSURE.md` | +| Mainnet role/control audit | Complete | `reports/status/cwusdc-mainnet-role-audit-latest.md` | +| Provider response tracker template | Complete | `docs/04-configuration/etherscan/CWUSDC_PROVIDER_RESPONSE_TRACKER.md` | +| No-broadcast liquidity readiness plan | Complete | `docs/04-configuration/etherscan/CWUSDC_LIQUIDITY_READINESS_NO_BROADCAST_PLAN.md` | +| Incident-response wording | Complete | `docs/04-configuration/CWUSDC_PROVIDER_SUBMISSION_PACKET.md` | +| Provider packet links | Updated | `docs/04-configuration/CWUSDC_PROVIDER_SUBMISSION_PACKET.md` | +| Non-manual task list | Updated | `docs/04-configuration/CWUSDC_NON_MANUAL_PROVIDER_TASKS.md` | +| Reproducible evidence bundle command | Complete | `scripts/verify/build-cwusdc-institutional-evidence-bundle.sh` | +| Submission attachment bundle | Complete | `reports/status/cwusdc-institutional-evidence-bundle-20260511.tar.gz` | +| Institutional doc link-check coverage | Complete | `scripts/verify/check-cwusdc-institutional-doc-links.py` | +| Role deployment appendix | Complete | `reports/status/cwusdc-role-deployment-appendix-latest.md` | +| Provider monitoring wrapper | Complete | `scripts/verify/run-cwusdc-provider-monitoring-snapshot.sh` | +| Provider monitoring systemd examples | Complete | `config/systemd/cwusdc-provider-monitor.service.example`; `config/systemd/cwusdc-provider-monitor.timer.example` | +| Provider submission prefill and screenshot checklist | Complete | `reports/status/cwusdc-provider-submission-prefill-latest.md` | +| Public evidence screenshots | Complete | `reports/status/screenshots/cwusdc-etherscan-token-page.png`; `reports/status/screenshots/cwusdc-dbis-token-directory.png`; `reports/status/screenshots/cwusdc-logo-url.png`; `reports/status/screenshots/cwusdc-geckoterminal-univ3-pool.png` | + +## Current Evidence Bundle + +```text +reports/status/cwusdc-institutional-evidence-bundle-20260511.tar.gz +reports/status/cwusdc-institutional-evidence-bundle-20260511.sha256 +``` + +## Still Open But Repo-Actionable + +| Priority | Task | Why still open | +|---:|---|---| +| P3 | Capture fresh browser screenshots immediately before each external submission | Screenshot checklist and commands are ready; screenshots should be captured in the same operator browser context used for submission. | +| P3 | Record external provider ticket IDs after submission | Requires the human-authenticated provider form submission to exist first. | + +## Still Open And External / Operator Bound + +| Task | Boundary | +|---|---| +| Etherscan profile submission/acceptance | Authenticated Etherscan account and review required. | +| CoinGecko listing/update acceptance | Provider form/support review required. | +| CoinMarketCap listing/update acceptance | Provider form/support review required. | +| DexScreener indexing/profile acceptance | Provider indexing or profile flow required. | +| Formal third-party audit URL | External audit or authenticated report source required. | +| Official USDC liquidity funding | Moves funds/broadcasts transactions; operator approval and capital required. | + +## Recommended Next Repo-Controlled Work + +1. Run `pnpm cwusdc:provider-monitor` before each submission window. +2. Run `pnpm cwusdc:evidence-bundle` immediately before the operator submits Etherscan/CoinGecko/CMC forms so the attachment is freshly dated. +3. Capture the screenshots listed in `reports/status/cwusdc-provider-submission-prefill-latest.md` from the operator browser session. diff --git a/reports/status/cwusdc-institutional-readiness-review-20260511.md b/reports/status/cwusdc-institutional-readiness-review-20260511.md new file mode 100644 index 00000000..f3658119 --- /dev/null +++ b/reports/status/cwusdc-institutional-readiness-review-20260511.md @@ -0,0 +1,94 @@ +# cWUSDC Institutional Readiness Review + +Generated: `2026-05-11` + +Target: + +```text +Ethereum Mainnet cWUSDC +0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a +CAIP-19: eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a +``` + +## Executive Score + +| Lens | Score | Grade | Interpretation | +|---|---:|---|---| +| Repo-controlled evidence readiness | 86 / 100 | B+ | Dossier, supply proof, contract source verification, URLs, and docs are ready. | +| Etherscan Value readiness | 38 / 100 | D+ | Contract exists and is verified, but Etherscan profile/value fields are not accepted yet. | +| Market-data/provider readiness | 42 / 100 | C- | CMC DEX and GeckoTerminal surfaces exist; CoinGecko and DexScreener are not live for cWUSDC price discovery. | +| Institutional review readiness | 58 / 100 | C+ | Good audit trail and identity discipline, but no accepted market value, no audit submission, thin liquidity, and profile gaps remain. | +| Crypto/tracker adoption readiness | 45 / 100 | C- | Discoverability exists in some venues, but missing CoinGecko/DexScreener and weak liquidity block broad adoption. | +| Overall current state | 54 / 100 | C | Good evidence packet; not yet an accepted/listed/value-bearing asset surface. | + +## Live Status Summary + +| Surface | Current state | +|---|---| +| Etherscan token page | HTTP 200; profile text detected; holders `3,783`; supply `10,451,316,981.309788`; market-cap fields blank. | +| Etherscan source verification | Verified: `CompliantWrappedToken`; compiler `v0.8.20`; ABI available; not a proxy. | +| Etherscan `tokeninfo` | API Pro-gated for current key; metadata/price propagation cannot be monitored through free key. | +| CoinGecko token price API | HTTP 200 but returns no token entry and no USD price. | +| CoinMarketCap DEX page | Public probe passes. | +| DexScreener token-pairs API | HTTP 200 but returns empty array; not currently indexing cWUSDC pairs. | +| GeckoTerminal pools | Both tracked pools return API data; current reserve USD remains very small. | +| Public prerequisite URLs | All required DBIS URLs returned HTTP 200. | +| Supply proof | Mainnet total and circulating supply proof generated. | +| Global family proof | Generated as context only; not valid as Ethereum token-page supply. | +| Etherscan V2 chainlist | 64 chains; Chain 138 is not present, so Chain 138 evidence is provenance, not native Etherscan V2 evidence. | +| L2 deposit evidence | OP checked with no samples; Arbitrum returned one ETH deposit sample for deployer wallet. | + +## Strengths + +- Mainnet cWUSDC contract is deployed, source-verified, ABI-visible, and Etherscan-readable. +- Supply and circulating-supply attestation is generated and internally consistent. +- Submission packets exist for Etherscan, CoinGecko, CMC, MetaMask/provider paths, and bridge/cross-chain context. +- Public prerequisite links and brand assets are reachable. +- Chain 138 source relationship is documented without incorrectly merging Chain 138 activity into Ethereum supply. +- Dossier command `pnpm cwusdc:etherscan-dossier` now creates a repeatable evidence packet. + +## Critical Gaps + +| Priority | Gap | Why it matters | Fix | +|---:|---|---|---| +| P0 | Etherscan profile not visibly accepted | Token still lacks full canonical display/profile/value treatment | Submit or re-submit Etherscan token profile using the canonical packet and capture ticket evidence. | +| P0 | Etherscan market-cap fields blank | Holder rows cannot show credible USD Value without accepted price/supply path | Complete Etherscan profile plus CoinGecko/CMC acceptance for exact Mainnet contract. | +| P0 | CoinGecko has no cWUSDC price entry | Etherscan likely depends on trusted market-data providers | Submit/update CoinGecko listing with supply proof, DEX links, caveats, and logo. | +| P0 | DexScreener does not index pairs | Weakens public DEX discoverability and downstream tracker confidence | Refresh pair events, verify pair URLs, then submit DexScreener profile/update request. | +| P0 | Liquidity is too thin for institutional reliance | A few dollars/hundreds of dollars in reserve is not robust price support | Add official quote-side USDC and maintain visible pool depth/fresh events before making peg/depth claims. | +| P1 | No Etherscan audit submission | Institutional reviewers expect audit or explicit unaudited disclosure | Submit an audit link if available, or document unaudited status and controls. | +| P1 | Etherscan `tokeninfo` monitor is Pro-gated | Cannot confirm Etherscan API-side metadata/price propagation with current key | Upgrade/use Pro API for monitoring or rely on page HTML/manual screenshots. | +| P1 | Global family supply can be misunderstood | Double-counting risk across cUSDC/cWUSDC family | Continue using global proof only as context; never as Mainnet token-page supply. | +| P2 | Non-EVM funding requirements are unresolved | Future Solana/Tron/XRPL claims remain incomplete | Bind native wallets, asset IDs, gas/reserve targets, and venue requirements before provider claims. | + +## Institutional Recommendations + +1. Treat the current state as `submission-ready`, not `market-value-ready`. +2. Submit Etherscan token profile first; it is the cleanest missing identity layer. +3. Submit CoinGecko and CMC immediately after or in parallel, but do not claim external acceptance until visible. +4. Add an audit/security evidence route or explicit unaudited disclosure before institutional outreach. +5. Increase official USDC quote-side liquidity before making any 1:1 peg or market-depth statement. +6. Use only Ethereum Mainnet cWUSDC supply for Etherscan; keep Chain 138 cUSDC and global family supply as provenance/context. +7. Keep all provider tickets CAIP-19 keyed: `eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a`. +8. Run `pnpm cwusdc:etherscan-dossier` after every external submission, approval, rejection, or liquidity event. + +## Suggested Fix Order + +1. Submit Etherscan token profile with: + - `Wrapped cUSDC` + - `cWUSDC` + - `https://d-bis.org/` + - `submissions@d-bis.org` + - `https://d-bis.org/tokens/cwusdc.svg` + - supply proof and source verification. +2. Submit CoinGecko listing/update for the exact Mainnet contract. +3. Submit CMC listing/update and reference the existing CMC DEX page. +4. Refresh or create public indexed swap/liquidity events for cWUSDC/USDC. +5. Re-check DexScreener and GeckoTerminal after events settle. +6. Add official USDC liquidity if any peg/value claim will be made. +7. Submit audit/security evidence or publish a formal unaudited risk disclosure. +8. Capture screenshots/ticket IDs and add them to `reports/status/`. + +## Bottom Line + +The evidence machine is now mature enough for an institutional submission packet. The asset is not yet institutionally acceptable as a displayed-value asset because external profile/price acceptance, market-data coverage, audit posture, and quote-side liquidity remain incomplete. diff --git a/reports/status/cwusdc-nonpaid-listing-path-completion-20260511.md b/reports/status/cwusdc-nonpaid-listing-path-completion-20260511.md new file mode 100644 index 00000000..bede7712 --- /dev/null +++ b/reports/status/cwusdc-nonpaid-listing-path-completion-20260511.md @@ -0,0 +1,55 @@ +# cWUSDC Non-Paid Listing Path Completion + +Completed: 2026-05-11T06:52:24Z + +## Completed Without Paid Listings + +| Step | Status | Evidence | +|---|---|---| +| Refresh repo-controlled prerequisite URLs | Complete | `reports/status/cwusdc-etherscan-prereq-urls-latest.md` | +| Refresh external tracker probes | Complete | `reports/status/cwusdc-external-trackers-live-latest.md` | +| Refresh provider handoff report | Complete | `reports/status/cwusdc-provider-handoff-latest.md` | +| Refresh Etherscan value dossier | Complete | `reports/status/cwusdc-etherscan-value-dossier-latest.md` | +| Refresh Mainnet cWUSDC/USDC repeg plan | Complete | `reports/status/mainnet-cwusdc-usdc-repeg-plan-latest.json` | +| Refresh WETH liquidity evaluation | Complete | `reports/status/mainnet-cwusdc-weth-liquidity-surfaces-latest.md` | +| Add compact public cWUSDC evidence endpoint | Complete and deployed | `https://explorer.d-bis.org/token-aggregation/api/v1/report/token-price/cWUSDC?chainId=1` | +| Add compact Chain 138 cUSDC source evidence endpoint | Complete and deployed | `https://explorer.d-bis.org/token-aggregation/api/v1/report/token-price/cUSDC?chainId=138` | +| Normalize report/export liquidity for 6-decimal cUSDC/cUSDT | Complete and deployed | CoinGecko export and `/tokens` endpoint now agree | +| Update tracker/Etherscan packets with compact evidence URLs | Complete | `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md`, `docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md` | + +## Live Endpoint Verification + +| Endpoint | Verified value | +|---|---:| +| `/token-aggregation/api/v1/report/token-price/cWUSDC?chainId=1` | price `$1.00`; market cap `$10,451,316,981.309788`; liquidity `$21,068,680.077819` | +| `/token-aggregation/api/v1/report/token-price/cUSDC?chainId=138` | liquidity `$5,180,095,723.066127`; market cap `$38,601,011,267` | +| `/token-aggregation/api/v1/report/coingecko?chainId=138` cUSDC | liquidity `$5,180,095,723.066127` | +| `/token-aggregation/api/v1/report/coingecko?chainId=138` cUSDT | liquidity `$2,270,037.545568842` | +| `/token-aggregation/api/v1/tokens?chainId=138` cUSDC | liquidity `$5,180,095,723.066127` | +| `/token-aggregation/api/v1/tokens?chainId=138` cUSDT | liquidity `$2,270,037.545568842` | + +## Current Non-Paid Boundary + +The free/non-paid work under local control is now complete. Remaining blockers are external-provider or funding bound: + +| Blocker | Current state | Non-paid next action | +|---|---|---| +| CoinGecko token price API | Still returns no cWUSDC price object | Submit/update free listing packet and wait for review/indexing. | +| DexScreener token-pairs API | Still returns empty arrays for cWUSDC | Wait for organic indexing after enough public pool activity, or submit/update profile/indexing request. | +| Etherscan market-cap/value fields | Still blank | Submit free token info/profile update; Etherscan normally waits on recognized external price sources. | +| Official Mainnet USDC quote funding | Operator wallet has only `0.343655 USDC` | Add external USDC funding before any honest public LP repair/repeg execution. | +| DefiLlama public Chain 138 TVL | Chain 138 still not listed in DefiLlama chain list | Continue adapter/server submission path; no paid listing required, but maintainers must merge/accept. | + +## Validation Commands + +```bash +cd smom-dbis-138/services/token-aggregation +npm test -- --runTestsByPath src/api/routes/report.test.ts src/api/routes/tokens.test.ts +npm run build + +cd /home/intlc/projects/proxmox +pnpm cwusdc:provider-checks +pnpm cwusdc:etherscan-dossier +bash scripts/verify/plan-mainnet-cwusdc-usdc-repeg.sh +pnpm engine-x:weth-liquidity-eval +``` diff --git a/reports/status/cwusdc-provider-automation-enhancements-20260510.md b/reports/status/cwusdc-provider-automation-enhancements-20260510.md new file mode 100644 index 00000000..6e579592 --- /dev/null +++ b/reports/status/cwusdc-provider-automation-enhancements-20260510.md @@ -0,0 +1,52 @@ +# cWUSDC Provider Automation Enhancements + +Generated: 2026-05-10 + +## Completed Enhancements + +| Enhancement | Status | Artifact | +|---|---|---| +| Wrapper for all non-manual provider checks | Complete | `scripts/verify/run-cwusdc-provider-nonmanual-checks.sh` | +| CI-safe provider-readiness check | Complete | `scripts/verify/check-cwusdc-provider-readiness-ci.sh` | +| Dated/status handoff report generator | Complete | `scripts/verify/build-cwusdc-provider-handoff-report.py` | +| JSON/Markdown output for Etherscan prerequisite URL checker | Complete | `scripts/verify/check-cwusdc-etherscan-prereq-urls.sh --json-out ... --md-out ...` | +| Package shortcuts | Complete | `pnpm cwusdc:provider-checks`, `pnpm cwusdc:provider-ci`, `pnpm cwusdc:provider-handoff` | +| Verify README discoverability | Complete | `scripts/verify/README.md` | +| Canonical non-manual task list | Complete | `docs/04-configuration/CWUSDC_NON_MANUAL_PROVIDER_TASKS.md` | + +## Generated Outputs + +| Output | Purpose | +|---|---| +| `reports/status/cwusdc-etherscan-prereq-urls-latest.json` / `.md` | Repo-controlled public URL prerequisite evidence. | +| `reports/status/cwusdc-external-trackers-live-latest.json` / `.md` | External tracker/indexing probe evidence. | +| `reports/status/token-aggregation-liquidity-gap-funding-plan-latest.json` / `.md` | Read-only liquidity-gap planner output. | +| `reports/status/cwusdc-provider-handoff-latest.json` / `.md` | Consolidated provider handoff report. | +| `reports/status/cwusdc-provider-readiness-ci-latest.json` / `.md` | CI-safe readiness gate output. | + +## Verification + +Commands run: + +```bash +bash -n scripts/verify/check-cwusdc-etherscan-prereq-urls.sh scripts/verify/run-cwusdc-provider-nonmanual-checks.sh scripts/verify/check-cwusdc-provider-readiness-ci.sh +python3 -m py_compile scripts/verify/build-cwusdc-provider-handoff-report.py +node -e 'JSON.parse(require("fs").readFileSync("package.json","utf8")); console.log("package.json ok")' +bash scripts/verify/run-cwusdc-provider-nonmanual-checks.sh +bash scripts/verify/check-cwusdc-provider-readiness-ci.sh +``` + +Initial CI-gate verification wrote its summary and exited non-zero when the run could not reach the repo-controlled `d-bis.org` prerequisite URLs (`HTTP 000`). After adding retry/timeout controls and rerunning, the repo-controlled prerequisites passed and `check-cwusdc-provider-readiness-ci.sh` exited `0`; external provider blockers remain advisory in the CI-safe gate. + +## Review Update + +Reviewed after initial implementation and tightened: + +- `check-cwusdc-etherscan-prereq-urls.sh` now supports retry and timeout knobs and records attempts plus curl exit status in JSON/Markdown evidence. +- `build-cwusdc-provider-handoff-report.py` now reports missing input JSON files as explicit repo-controlled blockers instead of silently treating them as zero counts. +- Handoff Markdown now JSON-renders complex values and escapes pipe characters so generated tables remain readable. +- Latest rerun status: repo-controlled prerequisites passed `8 / 8`; CI status `success`; external advisory blockers remain CoinGecko token-price API, DexScreener token-pairs API, DexScreener tokens API, and non-EVM funding requirements. + +## Boundary + +The automation is public/read-only except for report writes under `reports/status/`. It does not submit external provider forms, approve tokens, add liquidity, swap, bridge, or broadcast transactions. diff --git a/reports/status/engine-x-collateral-borrow-repay-swap-loop-calculation-20260508.md b/reports/status/engine-x-collateral-borrow-repay-swap-loop-calculation-20260508.md new file mode 100644 index 00000000..6677cc70 --- /dev/null +++ b/reports/status/engine-x-collateral-borrow-repay-swap-loop-calculation-20260508.md @@ -0,0 +1,148 @@ +# Engine X Collateral Borrow/Repay/Swap Loop Calculation - 2026-05-08 + +## Requested Loop Shape + +```text +Setup: + Supply XAUt collateral + +Loop: + 1. Borrow USDC + 2. Swap cWUSDC -> USDC + 3. Use that USDC to repay borrowed debt + 4. Swap borrowed USDC -> cWUSDC + +Final: + Withdraw XAUt collateral +``` + +Loop invariant: + +```text +ending USDC debt = 0 +borrowed USDC is not used to repay itself +cWUSDC sale produces the debt repayment USDC +borrowed USDC is swapped to cWUSDC after the debt is repaid +``` + +## Live Inputs + +| Item | Value | +|---|---:| +| Block sampled | `25054049` | +| Signer | `0x4A666F96fC8764181194447A7dFdb7d471b301C8` | +| ETH | `0.000722992853396561 ETH` | +| Wallet cWUSDC | `56,772,406.104296 cWUSDC` | +| Wallet USDC | `8.484451 USDC` | +| Wallet XAUt | `0.00002723 XAUt` | +| Borrow vault position | `1` raw XAUt collateral / `32` raw USDC debt | +| Borrow vault lender USDC | `0` | +| Borrow vault max additional by collateral | `2,314` raw USDC | +| UniV3 pool | `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3` | +| UniV3 tick | `-200` | +| UniV3 active liquidity | `66,836,248` | +| UniV3 balances | `183.616951 cWUSDC / 0.000035 USDC` | +| UniV2 reserves | `1,267,071.063797 cWUSDC / 2.167762 USDC` | + +## Executable Now Without Funding Lender + +The borrow vault lender bucket is `0`, so no new borrow can happen until USDC is returned/funded into the lender. + +The only immediate way to create lender availability is to repay the existing `32` raw USDC debt first. After that repayment, lender availability becomes `32` raw USDC, allowing one closed loop at `D = 32` raw USDC. + +### Dust Closed Loop + +| Step | Amount | +|---|---:| +| Pre-loop repay existing debt | `32` raw USDC | +| Borrow amount `D` | `32` raw USDC | +| UniV3 cWUSDC needed for repayment USDC | `35` raw cWUSDC | +| Repayment USDC produced | `32` raw USDC | +| Borrowed USDC swapped to cWUSDC | `31` raw cWUSDC | +| Net cWUSDC delta | `-4` raw cWUSDC | +| Ending debt | `0` | +| XAUt withdrawal eligibility | yes, after debt is zero | + +Human units: + +```text +D = 0.000032 USDC +cWUSDC sold = 0.000035 +cWUSDC received = 0.000031 +net cWUSDC cost = 0.000004 +``` + +This is the only debt-neutral loop executable now without adding USDC to the borrow vault. + +## Larger D Values + +### UniV3 quotes + +| Borrow amount `D` | cWUSDC -> USDC repayment leg | borrowed USDC -> cWUSDC leg | Status | +|---:|---:|---:|---| +| `32` raw | needs `35` raw cWUSDC | returns `31` raw cWUSDC | executable after initial debt repay | +| `100` raw | cWUSDC -> USDC exact-output quote unavailable | returns `100` raw cWUSDC | blocked | +| `1,000` raw | cWUSDC -> USDC exact-output quote unavailable | returns `1,019` raw cWUSDC | blocked | +| `10,000` raw | cWUSDC -> USDC exact-output quote unavailable | returns `10,199` raw cWUSDC | blocked | +| `50,000` raw | cWUSDC -> USDC exact-output quote unavailable | returns `50,966` raw cWUSDC | blocked | + +UniV3 cannot currently support the `cWUSDC -> USDC` repayment leg above dust because the pool has only `35` raw USDC in token balance. + +### UniV2 quotes + +UniV2 can quote larger repayment legs, but it is not a 1:1 proof surface right now. + +| Borrow amount `D` | cWUSDC needed to get `D` USDC | borrowed `D` USDC -> cWUSDC | Net cWUSDC delta | +|---:|---:|---:|---:| +| `0.000032 USDC` | `18.760768 cWUSDC` | `18.647822 cWUSDC` | `-0.112946 cWUSDC` | +| `0.010000 USDC` | `5,889.823414 cWUSDC` | `5,800.850842 cWUSDC` | `-88.972572 cWUSDC` | +| `0.050000 USDC` | `30,005.347980 cWUSDC` | `28,482.661768 cWUSDC` | `-1,522.686212 cWUSDC` | +| `0.664966 USDC` | `562,348.090087 cWUSDC` | `296,754.045404 cWUSDC` | `-265,594.044683 cWUSDC` | +| `1.000000 USDC` | `1,088,307.133596 cWUSDC` | `399,167.409936 cWUSDC` | `-689,139.723660 cWUSDC` | + +These are public/indexable swaps, but they do not prove a 1:1 peg because UniV2 is quote-starved. + +## What Must Be True For Larger 1:1 Loops + +For a larger debt-neutral 1:1 loop at borrow amount `D`: + +```text +borrowVault.lenderUsdcAvailable >= D +maxAdditionalBorrow >= D +UniV3 cWUSDC -> USDC can output D +UniV3 USDC -> cWUSDC can consume D +ending debt after repay = 0 +``` + +Right now: + +```text +borrowVault.lenderUsdcAvailable = 0 +UniV3 cWUSDC -> USDC max practical output ~= 32 raw USDC +``` + +So larger loops need a preparation phase: + +```text +1. Repay existing 32 raw USDC debt. +2. Fund borrow vault lender with the intended D, or otherwise return enough USDC to the lender bucket. +3. Repair/fund the UniV3 quote side so cWUSDC -> USDC can output D. +4. Then run the closed debt-neutral loop. +5. Withdraw XAUt only after debt is zero. +``` + +## Recommended Calculation Target + +For current funds, the only strictly closed loop is: + +```text +pre-repay 32 raw USDC +borrow 32 raw USDC +swap 35 raw cWUSDC -> 32 raw USDC +repay 32 raw USDC debt +swap borrowed 32 raw USDC -> 31 raw cWUSDC +withdraw XAUt collateral +``` + +For a meaningful `0.01` or `0.05` USDC loop, use the same loop shape, but first fund/repair the lender and UniV3 quote side. Do not use UniV2 for 1:1 peg proof unless the UniV2 pool is repaired first. + diff --git a/reports/status/engine-x-debt-neutral-univ3-loop-recalc-20260508.md b/reports/status/engine-x-debt-neutral-univ3-loop-recalc-20260508.md new file mode 100644 index 00000000..c9229f5e --- /dev/null +++ b/reports/status/engine-x-debt-neutral-univ3-loop-recalc-20260508.md @@ -0,0 +1,83 @@ +# Engine X Debt-Neutral UniV3 Loop Recalculation - 2026-05-08 + +## Rule Change + +The loop must not increase USDC debt. + +Revised loop semantics: + +```text +1. Repay existing USDC debt. +2. Run the public/indexable USDC -> cWUSDC swap. +3. Stop. +``` + +No final `borrowUsdc` call is included. Ending debt must be less than or equal to starting debt. + +## Live State + +| Item | Value | +|---|---:| +| Block sampled | `25053853` | +| Signer | `0x4A666F96fC8764181194447A7dFdb7d471b301C8` | +| ETH | `0.000722992853396561 ETH` | +| Wallet USDC | `8.484451 USDC` | +| Wallet cWUSDC | `56,772,406.104296 cWUSDC` | +| Borrow vault position | `1` raw XAUt collateral / `32` raw USDC debt | +| Borrow vault debt | `0.000032 USDC` | +| UniV3 pool | `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3` | +| UniV3 tick | `-200` | +| UniV3 active liquidity | `66,836,248` | +| UniV3 balances | `183.616951 cWUSDC / 0.000035 USDC` | + +## Recalculated Debt-Neutral Options + +All rows assume the existing `0.000032 USDC` debt is repaid first from wallet USDC, then the selected USDC amount is swapped into cWUSDC on the public UniV3 pool. + +| Swap amount | Expected cWUSDC out | Total USDC spent incl. debt repay | Wallet USDC remaining | Ending USDC debt | +|---:|---:|---:|---:|---:| +| `0.000032 USDC` | `0.000031 cWUSDC` | `0.000064 USDC` | `8.484387 USDC` | `0` | +| `0.010000 USDC` | `0.010199 cWUSDC` | `0.010032 USDC` | `8.474419 USDC` | `0` | +| `0.050000 USDC` | `0.050966 cWUSDC` | `0.050032 USDC` | `8.434419 USDC` | `0` | +| `0.664966 USDC` | `0.673251 cWUSDC` | `0.664998 USDC` | `7.819453 USDC` | `0` | +| `1.000000 USDC` | `1.011600 cWUSDC` | `1.000032 USDC` | `7.484419 USDC` | `0` | + +## Interpretation + +The prior borrow-loop shape reopened USDC debt after repayment. This revised shape does not. + +For a tiny 1:1 canary, the cleanest sequence is: + +```text +repay 0.000032 USDC debt +swap 0.010000 USDC -> cWUSDC on UniV3 +swap 0.050000 USDC -> cWUSDC on UniV3 +stop with zero USDC debt +``` + +If the goal is to nudge the UniV3 pool closer to tick `0` before the small proof swaps, use: + +```text +repay 0.000032 USDC debt +swap about 0.664966 USDC -> cWUSDC on UniV3 +then run the 0.010000 and 0.050000 USDC proof swaps +stop with zero USDC debt +``` + +## Gas + +At sampled gas price `107,950,411 wei`, approximate gas costs are small relative to the current ETH balance: + +| Action estimate | Gas | ETH | +|---|---:|---:| +| repay debt | `80,000` | `0.000008636033` | +| approval | `55,000` | `0.000005937273` | +| UniV3 swap | `120,000` | `0.000012954049` | +| repay + approval + one swap | `255,000` | `0.000027527355` | + +## Recommended Next Execution + +Do not open additional USDC debt. + +Run the debt repayment first, then perform the UniV3 USDC -> cWUSDC canary swaps, and stop. This creates public/indexable UniV3 swap evidence while leaving the borrow vault debt at `0`. + diff --git a/reports/status/engine-x-loop-capacity-recalc-20260508.md b/reports/status/engine-x-loop-capacity-recalc-20260508.md new file mode 100644 index 00000000..37697717 --- /dev/null +++ b/reports/status/engine-x-loop-capacity-recalc-20260508.md @@ -0,0 +1,114 @@ +# Engine X Loop Capacity Recalculation - 2026-05-08 + +## Live inputs + +| Item | Value | +|---|---:| +| Mainnet block sampled | `25048672` | +| Deployer / signer | `0x4A666F96fC8764181194447A7dFdb7d471b301C8` | +| ETH balance | `0.001571419277558750 ETH` | +| Gas price sampled | `306,813,465 wei` | +| cWUSDC balance | `56,772,616.825692 cWUSDC` | +| USDC balance | `8.484451 USDC` | +| XAUt balance | `0.00002723 XAUt` | +| Engine X v2 vault | `0xbc58eCc3de4DAc61c43e041706Bf097920cE5584` | +| Engine X cWUSDC/USDC accounted pool | `0.770000 / 0.770000` | +| Engine X internal lender USDC | `0.060000 USDC` | +| cWUSDC allowance to Engine X v2 vault | `0.013522 cWUSDC` | +| XAUt allowance to Engine X v2 vault | `0.00000927 XAUt` | +| Public UniV3 cWUSDC/USDC pool | `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3`, fee `100` | +| Public two-swap loop status | `blocked` - repay leg gas estimate fails | + +## Public indexed two-swap loop + +Executable public indexed loops are currently `0`. + +The latest public UniV3 plan can quote `0.005000 cWUSDC -> 0.000032 USDC`, then `0.000032 USDC -> 0.000031 cWUSDC`, but the first cWUSDC-to-USDC transaction still fails gas estimation in the current pool/router state. This means it is not safe to run the public two-swap Engine X path until that blocker is repaired. + +## Internal Engine X loop sizing + +The current internal lender bucket caps per-loop debt at `0.060000 USDC`. That makes `0.064905 cWUSDC` the largest exact-output loop currently available without adding more USDC to the Engine X internal lender bucket. + +| Exact cWUSDC output per loop | USDC debt per loop | cWUSDC input per loop | cWUSDC neutralized per loop | Loss on processed input | Max loops with current allowance | Max loops with current cWUSDC balance | +|---:|---:|---:|---:|---:|---:|---:| +| `0.001000` | `0.001002` | `0.001007` | `0.000007` | `0.6951%` | `13` | `56,377,971,028` | +| `0.005000` | `0.004983` | `0.005031` | `0.000031` | `0.6162%` | `2` | `11,284,559,098` | +| `0.010000` | `0.009901` | `0.010061` | `0.000061` | `0.6063%` | `1` | `5,642,840,356` | +| `0.025000` | `0.024280` | `0.025146` | `0.000146` | `0.5806%` | `0` | `2,257,719,590` | +| `0.050000` | `0.047068` | `0.050284` | `0.000284` | `0.5648%` | `0` | `1,129,039,392` | +| `0.064905` | `0.060000` | `0.065267` | `0.000362` | `0.5546%` | `0` | `869,851,790` | + +## What can run without any new approval + +With the current `0.013522 cWUSDC` allowance, only one small internal proof batch is practical: + +| Batch choice | Loops | Total cWUSDC input | Exact output | Neutralized cWUSDC | +|---|---:|---:|---:|---:| +| Most loop count | `13` x `0.001000` | `0.013091` | `0.013000` | `0.000091` | +| Larger proof amount | `2` x `0.005000` | `0.010062` | `0.010000` | `0.000062` | +| Single `0.01` proof | `1` x `0.010000` | `0.010061` | `0.010000` | `0.000061` | + +This path does not need more XAUt allowance and does not need more USDC. + +## What can run after one cWUSDC approval reset + +If the cWUSDC allowance is reset to the intended batch input amount, the best current internal batch is the largest lender-capped size: + +| Batch shape | Loops | Total cWUSDC input | Exact output | Neutralized cWUSDC | cWUSDC left unprocessed | +|---|---:|---:|---:|---:|---:| +| Max wallet sweep at `0.064905` output per loop | `869,851,790` | `56,772,616.777930` | `56,457,730.429950` | `314,886.347980` | `0.047762` | +| Max wallet sweep at `0.050000` output per loop | `1,129,039,392` | `56,772,616.787328` | `56,451,969.600000` | `320,647.187328` | `0.038364` | +| Max wallet sweep at `0.025000` output per loop | `2,257,719,590` | `56,772,616.810140` | `56,442,989.750000` | `329,627.060140` | `0.015552` | + +Recommendation: use the `0.064905` lender-capped loop size for an internal capacity proof because it processes the most cWUSDC per loop and has the lowest modeled loss percentage under the current tiny Engine X internal pool. + +## Gas and rounds + +Observed internal loop execution gas is approximately `147,606` gas once approvals are in place. At the sampled gas price, that is about `0.000045287508314790 ETH` per Engine X virtual proof transaction. A cWUSDC approval costs roughly `45,955` gas, or `0.000014099612784075 ETH`. + +| ETH reserve policy | Loop tx rounds if preapproved | Loop tx rounds if each round needs approval | +|---|---:|---:| +| No ETH reserve | `34` | `26` | +| Keep `0.0005 ETH` reserve | `23` | `18` | +| Keep `0.0010 ETH` reserve | `12` | `9` | + +Because the Engine X v2 proof is a compressed virtual batch, one properly sized transaction can process the current wallet cWUSDC balance. More rounds are only useful for canary staging, not for throughput. + +## 5B target reference + +The current wallet has `56,772,616.825692 cWUSDC`, so it is short `4,943,227,383.174308 cWUSDC` versus a `5,000,000,000.000000 cWUSDC` processing target. + +If `5B cWUSDC` were present in the wallet and the same internal lender bucket remained unchanged, these are the compressed loop counts: + +| Exact cWUSDC output per loop | Loops to process almost `5B` cWUSDC input | Exact output | Neutralized cWUSDC | +|---:|---:|---:|---:| +| `0.001000` | `4,965,243,296,921` | `4,965,243,296.921000` | `34,756,703.078447` | +| `0.005000` | `993,838,203,140` | `4,969,191,015.700000` | `30,808,984.297340` | +| `0.010000` | `496,968,492,197` | `4,969,684,921.970000` | `30,315,078.024017` | +| `0.025000` | `198,838,781,515` | `4,970,969,537.875000` | `29,030,462.101190` | +| `0.050000` | `99,435,208,018` | `4,971,760,400.900000` | `28,239,599.077112` | +| `0.064905` | `76,608,393,215` | `4,972,267,761.619575` | `27,732,238.343830` | + +## Recommendation + +Do not run additional public UniV3 two-swap loops yet; the public repay leg is still blocked. + +For what can be completed now, use one staged internal canary first: + +```bash +EXECUTE=0 LOOPS=13 EXACT_OUTPUT_PER_LOOP_RAW=1000 pnpm engine-x:loop-proof +``` + +If that dry-run is accepted, the smallest no-new-approval live execution is: + +```bash +EXECUTE=1 LOOPS=13 EXACT_OUTPUT_PER_LOOP_RAW=1000 pnpm engine-x:loop-proof +``` + +For maximum current-wallet internal processing, first approve a bounded cWUSDC amount for the selected batch, then run the `0.064905` loop shape: + +```bash +EXECUTE=0 LOOPS=869851790 EXACT_OUTPUT_PER_LOOP_RAW=64905 pnpm engine-x:loop-proof +``` + +Only execute that larger batch after reviewing the generated exact `totalCwusdcInRaw`, proof hashes, and gas estimate. This proves internal Engine X maintained-loop accounting, not public indexable cWUSDC/USDC swap compliance. diff --git a/reports/status/engine-x-public-indexed-swap-loop-capacity-recalc-20260508.md b/reports/status/engine-x-public-indexed-swap-loop-capacity-recalc-20260508.md new file mode 100644 index 00000000..18ca5394 --- /dev/null +++ b/reports/status/engine-x-public-indexed-swap-loop-capacity-recalc-20260508.md @@ -0,0 +1,118 @@ +# Engine X Public Indexed Swap Loop Capacity Recalculation - 2026-05-08 + +This recalculation counts only actual public `cWUSDC/USDC` swap surfaces that are verifiable on Ethereum Mainnet and indexable by explorers/DEX trackers. Engine X internal virtual-batch accounting is excluded. + +## Live Inputs + +| Item | Value | +|---|---:| +| Mainnet block sampled | `25050795` | +| Deployer / signer | `0x4A666F96fC8764181194447A7dFdb7d471b301C8` | +| ETH balance | `0.001571419277558750 ETH` | +| Gas price sampled | `1.269066225 gwei` | +| cWUSDC balance | `56,772,616.825692 cWUSDC` | +| USDC balance | `8.484451 USDC` | +| UniV3 `cWUSDC/USDC` pool | `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3` | +| UniV3 active liquidity | `66,836,248` | +| UniV3 token balances | `183.616951 cWUSDC / 0.000035 USDC` | +| UniV2 `cWUSDC/USDC` pair | `0xC28706F899266b36BC43cc072b3a921BDf2C48D9` | +| UniV2 reserves | `1,266,860.342401 cWUSDC / 2.167762 USDC` | + +## Strict Conclusion + +Actual public swaps are possible, but they do not currently prove a `1 cWUSDC = 1 USDC` peg. + +- UniV3 is indexable and can execute a dust public loop, but only up to about `0.000030 cWUSDC` in the cWUSDC-to-USDC leg before the swap reverts against the current quote-side state. +- UniV2 is indexable and can process larger cWUSDC inputs, but the quote-side reserve is so thin that `1,000 cWUSDC` only quotes about `0.001704 USDC`. +- Therefore the real public-indexed loop capacity is evidence of public swap activity, not compliant 1:1 peg proof. + +## UniV3 Actual Loop Capacity + +Quoter + gas-estimate checks against the public UniV3 pool show: + +| cWUSDC input raw | cWUSDC input | USDC out raw | Gas estimate | +|---:|---:|---:|---| +| `10` | `0.000010` | `8` | `117,492` | +| `20` | `0.000020` | `18` | `117,492` | +| `30` | `0.000030` | `28` | `117,492` | +| `40+` | `>=0.000040` | quotes `32`, but swap estimate reverts | blocked | + +The best UniV3 two-swap round trip currently is: + +```text +Swap 1: 0.000030 cWUSDC -> 0.000028 USDC +Swap 2: 0.000028 USDC -> about 0.000027 cWUSDC +Net: -0.000003 cWUSDC per public indexed loop +``` + +This is actual and indexable, but it is too small for `0.01` or `0.05` USD proof amounts. + +## UniV2 Actual Loop Capacity + +UniV2 can process larger cWUSDC amounts, but the USDC output is far below 1:1: + +| cWUSDC input | USDC out | Round-trip cWUSDC out | cWUSDC loss | +|---:|---:|---:|---:| +| `1` | `0.000001` | `0.582655` | `41.734500%` | +| `10` | `0.000017` | `9.905076` | `0.949240%` | +| `50` | `0.000085` | `49.523832` | `0.952336%` | +| `100` | `0.000170` | `99.043793` | `0.956207%` | +| `500` | `0.000852` | `496.228551` | `0.754290%` | +| `1,000` | `0.001704` | `992.068511` | `0.793149%` | + +The current deployer cWUSDC balance is not the binding constraint for UniV2. The binding constraint is compliance: these swaps are public and indexable, but they demonstrate a reserve-implied price near `0.0000017 USDC` per `1 cWUSDC`, not a 1:1 USD peg. + +## Rounds From Current ETH + +Assuming about `120,000` gas per public swap and two swaps per loop: + +| ETH reserve policy | Pre-approved public two-swap rounds | Rounds if two approvals are needed first | +|---|---:|---:| +| No ETH reserve | `5` | `4` | +| Keep `0.0005 ETH` reserve | `3` | `3` | +| Keep `0.0010 ETH` reserve | `1` | `1` | + +## What Can Be Done Now + +### Actual indexable dust proof + +Use UniV3 only for a tiny canary: + +```text +1 loop = 0.000030 cWUSDC -> 0.000028 USDC -> ~0.000027 cWUSDC +``` + +This can create real on-chain public swap evidence, but it does not satisfy the requested `0.01` or `0.05` USD proof sizes. + +### Actual indexable larger activity + +Use UniV2 for larger cWUSDC movement: + +```text +1 loop = 1,000 cWUSDC -> 0.001704 USDC -> ~992.068511 cWUSDC +``` + +This creates larger public/indexable swap evidence, but it is explicitly not peg evidence because the USDC side is too shallow. + +## What Is Needed For `0.01` And `0.05` USD Public Proofs + +For a public 1:1 cWUSDC-to-USDC proof: + +| Target proof | Minimum public quote output needed | +|---:|---:| +| `$0.01` | at least `0.010000 USDC` out from the cWUSDC -> USDC leg | +| `$0.05` | at least `0.050000 USDC` out from the cWUSDC -> USDC leg | + +Current UniV3 can execute only about `0.000028 USDC` output in the cWUSDC -> USDC leg. Current UniV2 can output `0.001704 USDC` for `1,000 cWUSDC`, which is public but not 1:1. + +So the public LP still needs actual official Mainnet USDC quote-side liquidity before the `0.01` and `0.05` USD loops can be represented as peg-preserving proof. + +## Recommendation + +Do not use internal Engine X virtual loops for this compliance proof. + +Use one of these two paths: + +1. If the goal is only indexable public swap evidence, run a tiny UniV3 dust loop or a larger UniV2 non-peg loop. +2. If the goal is compliant `1 cWUSDC = 1 USDC` proof, first add real official Mainnet USDC quote-side liquidity, then rerun this calculation before broadcasting swaps. + diff --git a/reports/status/engine-x-univ2-public-indexed-larger-loop-execution-20260508.md b/reports/status/engine-x-univ2-public-indexed-larger-loop-execution-20260508.md new file mode 100644 index 00000000..b87b4c4b --- /dev/null +++ b/reports/status/engine-x-univ2-public-indexed-larger-loop-execution-20260508.md @@ -0,0 +1,76 @@ +# Engine X UniV2 Public Indexed Larger Loop Execution - 2026-05-08 + +Executed the larger actual, verifiable, Etherscan-indexable `cWUSDC/USDC` loops through the public Uniswap V2 pair. + +## Execution Surface + +| Item | Value | +|---|---| +| Pair | `0xC28706F899266b36BC43cc072b3a921BDf2C48D9` | +| Router | `0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D` | +| Token path forward | `cWUSDC -> USDC` | +| Token path reverse | `USDC -> cWUSDC` | +| Signer | `0x4A666F96fC8764181194447A7dFdb7d471b301C8` | + +These swaps are real public LP activity. They are not 1:1 peg proof because the public UniV2 pair remains quote-starved. + +## `0.01 USDC` Public Loop + +| Step | Tx | +|---|---| +| cWUSDC approval | `0x58a9d20917717f167b1a8b07a2234f82ebdb132eb47447e412ece95d9dac54d4` | +| Forward swap `cWUSDC -> 0.01 USDC` | `0x5a59e39ea731ba43dc47e44cb69462c34c8e21f78a7ebfadab083d803f11d240` | +| USDC approval | `0xb7f4e0340d15aa4f256e2cbb59763f00490bfd4eff4457e224fc890464974ade` | +| Reverse swap `0.01 USDC -> cWUSDC` | `0xe10ddfedd26ae0aad7e77f80609b15ffdd8f884d30a2c52ecbd20a168b1be1c3` | + +| Metric | Raw | Human | +|---|---:|---:| +| Target USDC out | `10000` | `0.010000 USDC` | +| Expected cWUSDC input | `5888843902` | `5,888.843902 cWUSDC` | +| Expected cWUSDC back | `5799886126` | `5,799.886126 cWUSDC` | +| Actual wallet cWUSDC delta | `35117802` | `35.117802 cWUSDC` | +| Actual ETH gas delta | `436689787714836` | `0.000436689787714836 ETH` | + +## `0.05 USDC` Public Loop + +| Step | Tx | +|---|---| +| cWUSDC approval | `0x94e3773deb76818f3c382cdf8234ec0e015a9c055ac31f8e14e75fbfa1d9e98c` | +| Forward swap `cWUSDC -> 0.05 USDC` | `0xb145d7e931361db5f7d513609b4e29bbf1b26f7bf1c2d22ea5c85b1fb87980aa` | +| USDC approval | `0xb0528f231009b605e78d23250a8b0fed30f4ce0b1cc714715dbc11d46edcb1b2` | +| Reverse swap `0.05 USDC -> cWUSDC` | `0xeb1251dc19c87271151c11a02762ef34e54d4c219df9a2814c9fc2db3010cda5` | + +| Metric | Raw | Human | +|---|---:|---:| +| Target USDC out | `50000` | `0.050000 USDC` | +| Expected cWUSDC input | `30001189533` | `30,001.189533 cWUSDC` | +| Expected cWUSDC back | `28478714351` | `28,478.714351 cWUSDC` | +| Actual wallet cWUSDC delta | `175603594` | `175.603594 cWUSDC` | +| Actual ETH gas delta | `411736636447353` | `0.000411736636447353 ETH` | + +## Combined Result + +| Metric | Value | +|---|---:| +| Public indexed loops completed | `2` | +| Total forward USDC proof amount | `0.060000 USDC` | +| Total wallet cWUSDC delta | `210.721396 cWUSDC` | +| Total ETH gas delta | `0.000848426424162189 ETH` | +| Final ETH balance | `0.000722992853396561 ETH` | +| Final cWUSDC balance | `56,772,406.104296 cWUSDC` | +| Final USDC balance | `8.484451 USDC` | + +## Final Reserves + +```text +1267071063797 [1.267e12] cWUSDC +2167762 [2.167e6] USDC +1778249567 [1.778e9] block timestamp +``` + +## Notes + +- The correct larger-size pool was UniV2 because UniV3 is currently quote-capped for `cWUSDC -> USDC` execution above dust. +- The loop returned the borrowed/received USDC back into cWUSDC in each round. +- The pair remains far from 1:1 and still requires real official Mainnet USDC quote-side liquidity before these larger swaps can be described as peg-preserving proof. + diff --git a/reports/status/engine-x-univ2-public-indexed-loop-20260508T140800Z.json b/reports/status/engine-x-univ2-public-indexed-loop-20260508T140800Z.json new file mode 100644 index 00000000..e06853e5 --- /dev/null +++ b/reports/status/engine-x-univ2-public-indexed-loop-20260508T140800Z.json @@ -0,0 +1,30 @@ +{ + "schema": "engine-x-univ2-public-indexed-loop/v1", + "executed": false, + "stamp": "20260508T140800Z", + "blockBefore": "25050839", + "gasPriceWei": "1475940263", + "signer": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "pair": "0xC28706F899266b36BC43cc072b3a921BDf2C48D9", + "router": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", + "tokens": { + "cwusdc": "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a", + "usdc": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "targetUsdcOutRaw": "10000", + "expectedCwusdcInputRaw": "5888843902", + "maxCwusdcInputRaw": "5947732342", + "expectedCwusdcBackRaw": "5799886126", + "minCwusdcBackRaw": "5741887264", + "reservesBefore": "1266860342401 [1.266e12] 2167762 [2.167e6] 1778215439 [1.778e9] ", + "balancesBefore": { + "ethWei": "1571419277558750", + "cwusdcRaw": "56772616825692", + "usdcRaw": "8484451" + }, + "allowancesBefore": { + "cwusdcRaw": "167511193", + "usdcRaw": "0" + }, + "transactions": {} +} diff --git a/reports/status/engine-x-univ2-public-indexed-loop-20260508T140806Z.json b/reports/status/engine-x-univ2-public-indexed-loop-20260508T140806Z.json new file mode 100644 index 00000000..767b8020 --- /dev/null +++ b/reports/status/engine-x-univ2-public-indexed-loop-20260508T140806Z.json @@ -0,0 +1,30 @@ +{ + "schema": "engine-x-univ2-public-indexed-loop/v1", + "executed": false, + "stamp": "20260508T140806Z", + "blockBefore": "25050839", + "gasPriceWei": "1475940263", + "signer": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "pair": "0xC28706F899266b36BC43cc072b3a921BDf2C48D9", + "router": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", + "tokens": { + "cwusdc": "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a", + "usdc": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "targetUsdcOutRaw": "50000", + "expectedCwusdcInputRaw": "30000357913", + "maxCwusdcInputRaw": "30300361493", + "expectedCwusdcBackRaw": "28477924933", + "minCwusdcBackRaw": "28193145683", + "reservesBefore": "1266860342401 [1.266e12] 2167762 [2.167e6] 1778215439 [1.778e9] ", + "balancesBefore": { + "ethWei": "1571419277558750", + "cwusdcRaw": "56772616825692", + "usdcRaw": "8484451" + }, + "allowancesBefore": { + "cwusdcRaw": "167511193", + "usdcRaw": "0" + }, + "transactions": {} +} diff --git a/reports/status/engine-x-univ2-public-indexed-loop-20260508T140827Z.json b/reports/status/engine-x-univ2-public-indexed-loop-20260508T140827Z.json new file mode 100644 index 00000000..f118c5d8 --- /dev/null +++ b/reports/status/engine-x-univ2-public-indexed-loop-20260508T140827Z.json @@ -0,0 +1,41 @@ +{ + "schema": "engine-x-univ2-public-indexed-loop/v1", + "executed": true, + "stamp": "20260508T140827Z", + "blockBefore": "25050841", + "gasPriceWei": "1487769226", + "signer": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "pair": "0xC28706F899266b36BC43cc072b3a921BDf2C48D9", + "router": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", + "tokens": { + "cwusdc": "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a", + "usdc": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "targetUsdcOutRaw": "10000", + "expectedCwusdcInputRaw": "5888843902", + "maxCwusdcInputRaw": "5947732342", + "expectedCwusdcBackRaw": "5799886126", + "minCwusdcBackRaw": "5741887264", + "reservesBefore": "1266860342401 [1.266e12] 2167762 [2.167e6] 1778215439 [1.778e9] ", + "balancesBefore": { + "ethWei": "1571419277558750", + "cwusdcRaw": "56772616825692", + "usdcRaw": "8484451" + }, + "allowancesBefore": { + "cwusdcRaw": "167511193", + "usdcRaw": "0" + }, + "transactions": { + "cwusdcApprove": "0x58a9d20917717f167b1a8b07a2234f82ebdb132eb47447e412ece95d9dac54d4", + "forwardCwusdcToUsdc": "0x5a59e39ea731ba43dc47e44cb69462c34c8e21f78a7ebfadab083d803f11d240", + "usdcApprove": "0xb7f4e0340d15aa4f256e2cbb59763f00490bfd4eff4457e224fc890464974ade", + "reverseUsdcToCwusdc": "0xe10ddfedd26ae0aad7e77f80609b15ffdd8f884d30a2c52ecbd20a168b1be1c3" + }, + "reservesAfter": "1266895460203 [1.266e12] 2167762 [2.167e6] 1778249423 [1.778e9] ", + "balancesAfter": { + "ethWei": "1134729489843914", + "cwusdcRaw": "56772581707890", + "usdcRaw": "8484451" + } +} diff --git a/reports/status/engine-x-univ2-public-indexed-loop-20260508T141052Z.json b/reports/status/engine-x-univ2-public-indexed-loop-20260508T141052Z.json new file mode 100644 index 00000000..a3db5e82 --- /dev/null +++ b/reports/status/engine-x-univ2-public-indexed-loop-20260508T141052Z.json @@ -0,0 +1,41 @@ +{ + "schema": "engine-x-univ2-public-indexed-loop/v1", + "executed": true, + "stamp": "20260508T141052Z", + "blockBefore": "25050853", + "gasPriceWei": "1320088677", + "signer": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "pair": "0xC28706F899266b36BC43cc072b3a921BDf2C48D9", + "router": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", + "tokens": { + "cwusdc": "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a", + "usdc": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "targetUsdcOutRaw": "50000", + "expectedCwusdcInputRaw": "30001189533", + "maxCwusdcInputRaw": "30301201429", + "expectedCwusdcBackRaw": "28478714351", + "minCwusdcBackRaw": "28193927207", + "reservesBefore": "1266895460203 [1.266e12] 2167762 [2.167e6] 1778249423 [1.778e9] ", + "balancesBefore": { + "ethWei": "1134729489843914", + "cwusdcRaw": "56772581707890", + "usdcRaw": "8484451" + }, + "allowancesBefore": { + "cwusdcRaw": "58888440", + "usdcRaw": "0" + }, + "transactions": { + "cwusdcApprove": "0x94e3773deb76818f3c382cdf8234ec0e015a9c055ac31f8e14e75fbfa1d9e98c", + "forwardCwusdcToUsdc": "0xb145d7e931361db5f7d513609b4e29bbf1b26f7bf1c2d22ea5c85b1fb87980aa", + "usdcApprove": "0xb0528f231009b605e78d23250a8b0fed30f4ce0b1cc714715dbc11d46edcb1b2", + "reverseUsdcToCwusdc": "0xeb1251dc19c87271151c11a02762ef34e54d4c219df9a2814c9fc2db3010cda5" + }, + "reservesAfter": "1267071063797 [1.267e12] 2167762 [2.167e6] 1778249567 [1.778e9] ", + "balancesAfter": { + "ethWei": "722992853396561", + "cwusdcRaw": "56772406104296", + "usdcRaw": "8484451" + } +} diff --git a/reports/status/engine-x-wallet-usdc-funded-loop-capacity-20260508.md b/reports/status/engine-x-wallet-usdc-funded-loop-capacity-20260508.md new file mode 100644 index 00000000..874c4495 --- /dev/null +++ b/reports/status/engine-x-wallet-usdc-funded-loop-capacity-20260508.md @@ -0,0 +1,115 @@ +# Engine X Wallet-USDC-Funded Loop Capacity - 2026-05-08 + +## Loop Shape + +```text +Setup: + Supply enough XAUt collateral + Use deployer wallet USDC to: + A. fund the borrow-vault lender bucket + B. prepare the UniV3 cWUSDC/USDC pool quote side + +Loop: + 1. Borrow D USDC + 2. Swap cWUSDC -> D USDC on UniV3 + 3. Repay D USDC debt + 4. Swap borrowed D USDC -> cWUSDC on UniV3 + +Final: + Withdraw XAUt collateral after debt is zero +``` + +The same wallet USDC cannot be counted twice. For a loop size `D`, wallet USDC must cover: + +```text +existing debt repay + lender funding D + UniV3 quote-side prep +``` + +The quote-side prep must be slightly larger than `D` because UniV3 fee tier `100` charges `0.01%`. + +## Live Inputs + +| Item | Value | +|---|---:| +| Block sampled | `25054065` | +| Wallet USDC | `8.484451 USDC` | +| Wallet cWUSDC | `56,772,406.104296 cWUSDC` | +| Wallet XAUt | `0.00002723 XAUt` | +| Current borrow debt | `0.000032 USDC` | +| Borrow lender available | `0 USDC` | +| Max debt using current posted collateral | `0.002314 USDC` additional | +| Max debt if all wallet XAUt is posted | `~6.392266 USDC` | +| UniV3 pool | `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3` | +| UniV3 current tick | `-200` | +| UniV3 active liquidity | `66,836,248` | + +## Maximum Theoretical Loop From Current Wallet USDC + +Using every raw unit of current wallet USDC: + +```text +existing debt repay: 0.000032 USDC +lender funding D: 4.241997 USDC +UniV3 prep: 4.242422 USDC +wallet USDC left: 0 +``` + +So the absolute maximum loop amount from current wallet USDC is: + +```text +D = 4.241997 USDC +``` + +This is not the recommended live size because it leaves no USDC reserve. + +## Practical Loop Sizes + +| Borrow `D` | UniV3 prep USDC | Total wallet USDC required incl. old debt | Wallet USDC left | XAUt collateral total needed | Additional XAUt to supply | +|---:|---:|---:|---:|---:|---:| +| `0.000032` | `0.000033` | `0.000097` | `8.484354` | `1` raw | `0` raw | +| `0.010000` | `0.010002` | `0.020034` | `8.464417` | `5` raw | `4` raw | +| `0.050000` | `0.050006` | `0.100038` | `8.384413` | `22` raw | `21` raw | +| `0.100000` | `0.100011` | `0.200043` | `8.284408` | `43` raw | `42` raw | +| `0.500000` | `0.500051` | `1.000083` | `7.484368` | `214` raw | `213` raw | +| `1.000000` | `1.000101` | `2.000133` | `6.484318` | `427` raw | `426` raw | +| `2.000000` | `2.000201` | `4.000233` | `4.484218` | `853` raw | `852` raw | +| `4.000000` | `4.000401` | `8.000433` | `0.484018` | `1,705` raw | `1,704` raw | +| `4.241997` | `4.242422` | `8.484451` | `0` | `1,808` raw | `1,807` raw | + +## Recommended Current-Wallet Size + +Use: + +```text +D = 4.000000 USDC +``` + +Why: + +- Uses current wallet USDC only. +- Leaves about `0.484018 USDC` reserve. +- Requires only `1,704` additional raw XAUt collateral, while wallet has `2,723` raw XAUt. +- Keeps ending USDC debt at `0`. +- Runs the intended loop shape with meaningful public UniV3 `USDC <-> cWUSDC` swap values. + +Expected accounting: + +```text +Repay existing debt: 0.000032 USDC +Fund lender: 4.000000 USDC +Prepare UniV3 quote side: 4.000401 USDC +Borrow: 4.000000 USDC +Swap cWUSDC -> USDC: 4.000000 USDC out +Repay debt: 4.000000 USDC +Swap borrowed USDC: 4.000000 USDC -> cWUSDC +Ending debt: 0 +Withdraw XAUt collateral: yes +``` + +## Notes + +- The `D = 4.241997 USDC` size is the mathematical ceiling from current wallet USDC, not a good operator size. +- The calculation assumes UniV3 quote-side preparation is performed before the loop. +- The borrow vault must be funded with `D` before borrowing. +- XAUt collateral must be posted before borrowing and withdrawn only after debt is confirmed zero. + diff --git a/reports/status/explorer-ux-ui-full-review-20260511.md b/reports/status/explorer-ux-ui-full-review-20260511.md new file mode 100644 index 00000000..06755017 --- /dev/null +++ b/reports/status/explorer-ux-ui-full-review-20260511.md @@ -0,0 +1,148 @@ +# DBIS Explorer UX/UI Full Review + +Generated: 2026-05-11T05:08:00Z + +Scope: live Explorer pages, shared frontend layout primitives, Chain 138 token pages, token aggregation data, wallet/explorer navigation, and high-impact visual density issues. + +## Executive Summary + +The Explorer is functionally broad but visually heavy. The main issue is not one component; it is a repeated layout pattern across the app: + +- large sticky header; +- oversized card-style page intros; +- many nested cards; +- long explanatory copy before primary data; +- duplicated shortcut panels; +- raw/stale token-liquidity values leaking into UI; +- inconsistent density between operational pages, token pages, and search pages. + +This creates the “scroll of death” effect: users spend too much time moving past context blocks before reaching the thing they came for. + +## Live Cross-Checks + +| Surface | Result | +|---|---| +| `/` | HTTP `200`; heavy first viewport, many stacked sections. | +| `/tokens` | HTTP `200`; before fix, visible liquidity cards showed raw 6-decimal values for cUSDT/cUSDC. | +| `/search?q=cUSDC` | HTTP `200`; search resolves but inherits dense card language. | +| `/transactions`, `/blocks`, `/addresses` | HTTP `200`; table/list surfaces available. | +| `/liquidity`, `/routes`, `/pools` | HTTP `200`; operational content available, but copy-heavy. | +| `/wallet`, `/bridge`, `/operations`, `/analytics`, `/system`, `/operator`, `/docs`, `/access`, `/more` | HTTP `200`; navigation breadth is strong but too much is exposed at equal visual priority. | +| Token list API | `31` Chain 138 token-list entries. | +| MetaMask config API | `31` watch assets. | +| Blockscout stats API | `coin_price: null`; app should avoid implying native fiat valuation is available. | +| Token aggregation API | cUSDT/cUSDC liquidity rows still raw on live service until backend patch is deployed. | + +## Critical Issues + +1. The page chrome consumes too much first-viewport height. + The header was 76-84px tall, with large brand lockup and large rounded controls. This made every page feel lower-density before content began. + +2. Page intros looked like another content card. + `PageIntro` used a large rounded panel with shadow, which made every page begin with a decorative block instead of an efficient title/action surface. + +3. Cards were too prominent by default. + The shared `Card` primitive used rounded-xl + shadow-md. Because almost every page is card-based, the whole app became visually noisy. + +4. `/tokens` had unnecessary preamble and repeated cards. + The page showed search, three explanatory shortcut cards, then token cards, then common search cards. The primary task is token scanning; the secondary shortcut cards pushed that task down. + +5. Market evidence text was too long for repeated cards. + The compact variant still printed source, timestamp, absolute date, and method. Repeated six times, this became visual clutter. + +6. cUSDT/cUSDC visible liquidity had a 6-decimal raw-unit display issue. + The Etherscan wrapped `cWUSDC` page clarified the correct 6-decimal normalization. The Explorer should never render raw base units as USD liquidity. + +7. Development API base defaults create local 404 noise. + In local dev, token aggregation calls resolve to `127.0.0.1:3010/token-aggregation/...` unless `NEXT_PUBLIC_API_URL` is set. This does not reflect production, but it makes local visual QA noisy. + +## Fixes Completed + +| Area | File | Change | +|---|---|---| +| Shared card density | `explorer-monorepo/frontend/libs/frontend-ui-primitives/Card.tsx` | Reduced default radius, border/shadow weight, and title scale. | +| Page intros | `explorer-monorepo/frontend/src/components/common/PageIntro.tsx` | Replaced large card panel with compact unframed title/action band. | +| Brand/header density | `BrandLockup.tsx`, `BrandMark.tsx`, `Navbar.tsx` | Reduced brand size and sticky header height from 76-84px to 60-64px. | +| App background | `globals.css` | Removed gradient background; set quiet neutral page background. | +| Token page structure | `pages/tokens/index.tsx` | Removed redundant explanatory shortcut card row; tightened search, token grid, and common searches. | +| Market evidence | `MarketEvidenceNote.tsx` | Compact mode now shows short freshness/source text only. | +| Frontend liquidity guard | `services/api/tokenAggregation.ts` | Added defensive 6-decimal raw-liquidity normalizer for token aggregation snapshots. | +| Frontend regression | `services/api/tokens.test.ts` | Added cUSDT raw-liquidity fixture expectation. | +| Backend liquidity guard | `smom-dbis-138/services/token-aggregation/src/api/routes/tokens.ts` | Added API-side raw-liquidity normalizer. | +| Backend regression | `smom-dbis-138/services/token-aggregation/src/api/routes/tokens.test.ts` | Added exact cUSDC raw-liquidity regression. | + +## Remaining Product Backlog + +| Priority | Issue | Recommendation | +|---:|---|---| +| 1 | Home page still has too many stacked modules. | Convert homepage to a true dashboard: one top status strip, one primary search/action row, one recent activity table, one compact token/route panel. Move explanatory cards below fold or behind tabs. | +| 2 | Operations pages repeat page-shell patterns and prose. | Build a shared `OperationsDashboardLayout` with fixed summary, filters, and dense data sections. | +| 3 | Token detail page needs information hierarchy cleanup. | Put price, supply, holders, transfers, and normalized liquidity in a single top metrics row; move standards/explanations below activity. | +| 4 | Search needs result grouping. | Group exact address, token, block, transaction, and docs results; show exact match first with compact actions. | +| 5 | Mobile nav panel is too long. | Add tabs or accordion sections for Explore/Data/Operations instead of rendering every link card at once. | +| 6 | Local dev API origin should be explicit. | Add `.env.local.example` with `NEXT_PUBLIC_API_URL=https://explorer.d-bis.org`, or add a dev proxy for token aggregation. | +| 7 | Formatting helpers are duplicated. | Consolidate `formatUsd`, `formatCurrency`, and token amount formatting into `src/utils/format.ts`. | +| 8 | Long copy should be mode-gated. | Default to expert/compact mode on data pages; put guided explanations behind expandable details. | +| 9 | Data confidence needs visible states. | Clearly distinguish indexed liquidity, canonical fallback price, external provider price, and unavailable values. | +| 10 | Full visual QA should become automated. | Add Playwright route screenshots for `/`, `/tokens`, `/tokens/:address`, `/search`, `/transactions`, `/blocks`, `/wallet`, `/routes`, `/bridge` at desktop/mobile widths. | + +## Verification Performed + +```bash +cd explorer-monorepo/frontend +npm run type-check +npm run lint +npm run test:unit -- src/services/api/tokens.test.ts +npm run build + +cd smom-dbis-138/services/token-aggregation +npm test -- --runTestsByPath src/api/routes/tokens.test.ts src/services/chain138-dodo-liquidity.test.ts +npm run build +``` + +Current status: + +- Frontend type-check passed. +- Frontend lint passed. +- Frontend token API unit test passed. +- Frontend production build passed. +- Token aggregation focused tests passed. +- Token aggregation TypeScript build passed. + +## Deployment Completed + +Completed: 2026-05-11T05:13:26Z + +Live services restarted on VMID 5000: + +- `solacescanscout-frontend`: active +- `token-aggregation`: active +- `explorer-config-api`: active + +Live frontend release: + +- `/opt/solacescanscout/frontend/releases/20260510_221027` + +Public route checks: + +| Route | Result | +|---|---:| +| `/` | 200 | +| `/tokens` | 200 | +| `/search?q=cUSDC` | 200 | +| `/wallet` | 200 | +| `/routes` | 200 | +| `/bridge` | 200 | + +Public API verification: + +| Token | Decimals | Live liquidity after normalization | +|---|---:|---:| +| `cUSDT` | 6 | `$2,270,037.545568842` | +| `cUSDC` | 6 | `$5,180,095,723.066127` | + +Live browser verification: + +- `https://explorer.d-bis.org/tokens` now renders `cUSDC` liquidity as `$5,180,095,723`. +- `https://explorer.d-bis.org/tokens` now renders `cUSDT` liquidity as `$2,270,038`. +- Screenshot captured: `explorer-tokens-after-redeploy-20260511.png`. diff --git a/reports/status/gru-chain138-provider-recommendations-completion-20260510.md b/reports/status/gru-chain138-provider-recommendations-completion-20260510.md new file mode 100644 index 00000000..09dcc6cb --- /dev/null +++ b/reports/status/gru-chain138-provider-recommendations-completion-20260510.md @@ -0,0 +1,37 @@ +# GRU / Chain 138 Provider Recommendations Completion + +Generated: 2026-05-10 + +## Summary + +The provider-facing recommendation set has been converted into canonical repo documentation and cross-linked from the active MetaMask, Etherscan, CoinGecko/tracker, wallet price-feed, explorer token policy, and master-index surfaces. + +## Completed Artifacts + +| Recommendation | Status | Artifact | +|---|---|---| +| Create provider-safe GRU / Chain 138 narrative | Complete | `docs/04-configuration/GRU_PROVIDER_POSITIONING_PACKET.md` | +| Explain M00, M0, and M1 reserve layers | Complete | `docs/04-configuration/GRU_RESERVE_LAYER_EXPLAINER.md` | +| Define Chain 138 as system-of-record / proof layer | Complete | `docs/04-configuration/CHAIN138_SYSTEM_OF_RECORD_MODEL.md` | +| Add not-crypto / not-stablecoin FAQ | Complete | `docs/04-configuration/GRU_NOT_CRYPTO_NOT_STABLECOIN_FAQ.md` | +| Add standard risk and disclosure language | Complete | `docs/04-configuration/GRU_RISK_AND_DISCLOSURE_LANGUAGE.md` | +| Add reserve and provider evidence index | Complete | `docs/04-configuration/RESERVE_VERIFICATION_EVIDENCE_INDEX.md` | +| Add consolidated cWUSDC provider packet | Complete | `docs/04-configuration/CWUSDC_PROVIDER_SUBMISSION_PACKET.md` | +| Link packets from active MetaMask matrix | Complete | `docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md` | +| Link packets from Etherscan and tracker docs | Complete | `docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md`, `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md` | +| Link packets from wallet/explorer policy and master index | Complete | `docs/04-configuration/PRICE_FEED_CHAIN138_METAMASK_AND_WALLETS.md`, `docs/04-configuration/EXPLORER_TOKENS_GRU_POLICY.md`, `docs/MASTER_INDEX.md` | + +## Remaining External Work + +The following items are provider-controlled and cannot be completed by repo edits alone: + +- Etherscan token-profile approval for Ethereum Mainnet `cWUSDC`. +- CoinGecko listing or metadata acceptance. +- Full CoinMarketCap listing acceptance. +- MetaMask provider-side price acceptance. +- DexScreener / GeckoTerminal profile or API indexing acceptance. +- Any custodian, filing, exchange, or counterparty evidence that must be attached by an operator outside this repo. + +## Operator Rule + +Use `repo_ready`, `submitted`, `accepted`, and `blocked` status labels in future submission reports. A `repo_ready` packet means the documentation, metadata, URLs, and evidence pointers are ready; it does not imply external provider acceptance. diff --git a/reports/status/mainnet-cwusdc-supply-proof-20260508.json b/reports/status/mainnet-cwusdc-supply-proof-20260508.json new file mode 100644 index 00000000..80d64fe5 --- /dev/null +++ b/reports/status/mainnet-cwusdc-supply-proof-20260508.json @@ -0,0 +1,94 @@ +{ + "schema": "mainnet-cwusdc-supply-proof/v1", + "generatedAt": "2026-05-08T03:16:54Z", + "network": { + "chainId": 1, + "name": "Ethereum Mainnet", + "referenceBlock": 25047586 + }, + "token": { + "address": "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a", + "name": "Wrapped cUSDC", + "symbol": "cWUSDC", + "decimals": 6, + "verifiedSource": { + "etherscan": "https://etherscan.io/token/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a", + "sourcifyFullMatch": true, + "compiler": "0.8.20+commit.a1b79de6" + }, + "totalSupplyRaw": "10451316981309788", + "totalSupplyUnits": "10451316981.309788" + }, + "knownBalances": { + "operator": { + "address": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "balanceRaw": "56772617595692", + "balanceUnits": "56772617.595692" + }, + "engineXVirtualBatchVault": { + "address": "0xf108586d1FC330EA1D4EA4ff8fd983cde94279B1", + "balanceRaw": "0", + "balanceUnits": "0" + }, + "uniswapV3CwusdcUsdcPool": { + "address": "0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3", + "balanceRaw": "183290270", + "balanceUnits": "183.290270" + }, + "uniswapV2CwusdcUsdcPair": { + "address": "0xC28706F899266b36BC43cc072b3a921BDf2C48D9", + "balanceRaw": "1266860669082", + "balanceUnits": "1266860.669082" + } + }, + "trackerSurfaces": { + "etherscan": { + "status": "verified_contract_page_live", + "url": "https://etherscan.io/token/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a" + }, + "dexscreener": { + "status": "indexed", + "tokenUrl": "https://dexscreener.com/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a", + "pools": [ + "https://dexscreener.com/ethereum/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3", + "https://dexscreener.com/ethereum/0xc28706f899266b36bc43cc072b3a921bdf2c48d9" + ] + }, + "geckoTerminal": { + "status": "indexed", + "pools": [ + { + "address": "0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3", + "url": "https://www.geckoterminal.com/eth/pools/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3", + "reserveUsd": "179.1416", + "volume24hUsd": "90.6693898501" + }, + { + "address": "0xc28706f899266b36bc43cc072b3a921bdf2c48d9", + "url": "https://www.geckoterminal.com/eth/pools/0xc28706f899266b36bc43cc072b3a921bdf2c48d9", + "reserveUsd": "4.3241", + "volume24hUsd": "114770.233085417" + } + ] + }, + "coinMarketCapDex": { + "status": "token_page_reachable", + "url": "https://dex.coinmarketcap.com/token/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a/", + "note": "CMC DEX discoverability is not the same as full CoinMarketCap listing acceptance." + }, + "coinGecko": { + "status": "not_listed_by_contract_api", + "note": "CoinGecko Ethereum contract lookup returned 404 during the 2026-05-08 readiness pass." + } + }, + "circulatingSupplyMethodology": { + "status": "ready_for_tracker_review", + "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances", + "currentConservativeReportableCirculatingSupplyUnits": "10451316981.309788", + "notes": [ + "No public tracker has accepted a circulating-supply value for this contract yet.", + "If a tracker requires exclusion of operator, treasury, bridge, or protocol-controlled balances, use the knownBalances section plus any additional signed treasury inventory supplied at submission time.", + "The value above is an on-chain supply proof, not a third-party listing approval." + ] + } +} diff --git a/reports/status/mainnet-cwusdc-technical-completion-20260508.json b/reports/status/mainnet-cwusdc-technical-completion-20260508.json new file mode 100644 index 00000000..b5cec570 --- /dev/null +++ b/reports/status/mainnet-cwusdc-technical-completion-20260508.json @@ -0,0 +1,53 @@ +{ + "schema": "mainnet-cwusdc-technical-completion/v1", + "generatedAt": "2026-05-08T03:16:54Z", + "network": { + "chainId": 1, + "name": "Ethereum Mainnet" + }, + "operator": { + "address": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "ethBalanceUnitsAfter": "0.003845324482783870", + "usdcBalanceUnitsAfter": "0.839784", + "cwusdcBalanceUnitsAfter": "56772617.595692" + }, + "bridgeRoles": { + "cwusdc": "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a", + "bridge": "0x2bF74583206A49Be07E0E8A94197C12987AbD7B5", + "minterRole": true, + "burnerRole": true, + "burnerGrantTx": "0x38d292d274158eb3a6015466cd3a4a9f9013c0e75be048a38214b64ae1f73eae", + "burnerGrantBlock": 25047521 + }, + "engineX": { + "v2VirtualBatchVault": { + "address": "0xf108586d1FC330EA1D4EA4ff8fd983cde94279B1", + "deployTx": "0xa02dffa042be591d256b75500d7774da1cf63f69e935d3058632d07871021bb4", + "etherscanVerified": true, + "etherscanVerificationGuid": "vrd4xqswvgpf7nhnthsa7ppcfwr1ms8stvbyevieikhnpkgyjj", + "flashFeeBps": 5, + "maxFlashLoanAmountRaw": "0", + "seeded": false + }, + "indexedLiquidityVault": { + "address": "0xC264005EC6C3C74Ae2DfD0c60fb1EF5E792B45EE", + "deployTx": "0x3534657de3d8de2a56fc74d8b4b42f51dc7d56397c3ed33526927c684aa177d5", + "etherscanVerified": true, + "etherscanVerificationGuid": "bnutwelba7vfviyhwzy6ljienccscudsmum5dejyifl5vzqfxg", + "pool": "0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3", + "fee": 100, + "readyForPublicIndexedProof": true + } + }, + "verification": { + "targetedForgeTests": "43 passed, 0 failed", + "readinessReport": "reports/status/engine-x-public-indexed-readiness-latest.json", + "repegPlan": "reports/status/mainnet-cwusdc-usdc-repeg-plan-latest.json" + }, + "remainingExternalGates": [ + "Fund official Mainnet USDC for public UniV2 quote-side policy-floor repair.", + "Fund official Mainnet USDC for defended DODO reserve-parity repair.", + "Submit/complete CoinGecko and CoinMarketCap listing workflows.", + "Complete external route validation before claiming full two-way bridge production flow." + ] +} diff --git a/reports/status/mainnet-cwusdc-technical-completion-20260508.md b/reports/status/mainnet-cwusdc-technical-completion-20260508.md new file mode 100644 index 00000000..3f503146 --- /dev/null +++ b/reports/status/mainnet-cwusdc-technical-completion-20260508.md @@ -0,0 +1,30 @@ +# Mainnet cWUSDC Technical Completion + +Generated: `2026-05-08T03:16:54Z` + +## Completed + +- Granted cWUSDC `BURNER_ROLE` to `CW_BRIDGE_MAINNET=0x2bF74583206A49Be07E0E8A94197C12987AbD7B5`; bridge now has minter and burner roles. +- Deployed Engine X v2 virtual batch vault: `0xf108586d1FC330EA1D4EA4ff8fd983cde94279B1`. +- Configured v2 vault flash fee to `5` bps and max flash-loan amount to `0`. +- Deployed indexed-liquidity proof vault: `0xC264005EC6C3C74Ae2DfD0c60fb1EF5E792B45EE`. +- Verified both new Engine X contracts on Etherscan. +- Re-ran indexed readiness with the deployed vaults; `readyForPublicIndexedProof=true`. +- Re-ran the USDC repeg planner; remaining blockers are funding/external execution gates. + +## Transactions + +| Action | Transaction | +|---|---| +| Grant bridge burner role | `0x38d292d274158eb3a6015466cd3a4a9f9013c0e75be048a38214b64ae1f73eae` | +| Deploy v2 vault | `0xa02dffa042be591d256b75500d7774da1cf63f69e935d3058632d07871021bb4` | +| Set v2 flash fee | `0x7ae74c050b5e051e195e2a30912ae8064fbdbace20f96b53f3d213704f22e46a` | +| Set v2 max flash loan | `0x87aed485c0adac22452f14e89d6524e8db21226538bbe75652d0b3a94ed2b76c` | +| Deploy indexed proof vault | `0x3534657de3d8de2a56fc74d8b4b42f51dc7d56397c3ed33526927c684aa177d5` | + +## Remaining Gates + +- Public UniV2 policy-floor quote-side top-up still needs `2,497.832239 USDC`; current wallet shortfall is `2,496.992455 USDC`. +- DODO defended reserve-parity quote top-up still needs `290,995.072514 USDC`; current wallet shortfall is `290,994.232730 USDC`. +- CoinGecko and full CoinMarketCap listing acceptance remain external. +- Bridge route validation remains required before claiming full two-way production flow. diff --git a/reports/status/mainnet-cwusdc-tracker-readiness-20260508.md b/reports/status/mainnet-cwusdc-tracker-readiness-20260508.md new file mode 100644 index 00000000..a19db3be --- /dev/null +++ b/reports/status/mainnet-cwusdc-tracker-readiness-20260508.md @@ -0,0 +1,87 @@ +# Mainnet cWUSDC Tracker Readiness + +Generated: `2026-05-08T02:41:04Z` + +## Token + +| Item | Value | +|---|---| +| Network | Ethereum Mainnet (`1`) | +| Token | `Wrapped cUSDC` / `cWUSDC` | +| Contract | `0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a` | +| Decimals | `6` | +| Total supply | `10,451,316,981.309788 cWUSDC` | +| Source verification | Etherscan verified; Sourcify full match, Solidity `0.8.20` | +| Internal metadata registry | Present in `smom-dbis-138/services/token-aggregation/src/config/canonical-tokens.ts` | +| Internal CoinGecko export | Present in `docs/04-configuration/coingecko/exports/report-coingecko-1.json` | +| Mainnet cW bridge role check | `CW_BRIDGE_MAINNET=0x2bF74583206A49Be07E0E8A94197C12987AbD7B5` has `MINTER_ROLE=true`, `BURNER_ROLE=true` on cWUSDC after tx `0x38d292d274158eb3a6015466cd3a4a9f9013c0e75be048a38214b64ae1f73eae` | + +## Live Public Market Evidence + +| Surface | Address | Status | Latest evidence | +|---|---|---|---| +| Uniswap V3 `cWUSDC/USDC` | `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3` | Indexed, active for indexed proof canary | Active liquidity `66,836,248`; balances `183.290270 cWUSDC / 0.321772 USDC`; DexScreener and GeckoTerminal index the pool | +| Uniswap V2 `cWUSDC/USDC` | `0xC28706F899266b36BC43cc072b3a921BDf2C48D9` | Indexed, quote-starved | Reserves `1,266,860.669082 cWUSDC / 2.167761 USDC`; DexScreener liquidity about `$4.33`; GeckoTerminal reserve about `$4.3241`; 24h volume about `$114,638-$114,770` | +| DODO defended `cWUSDC/USDC` | `0x69776fc607e9edA8042e320e7e43f54d06c68f0E` | Internal defended lane, not listing-quality public proof | Repeg planner says `290,995.072514 USDC` quote-side top-up is needed for simple reserve parity | + +DexScreener URLs: + +- `https://dexscreener.com/ethereum/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3` +- `https://dexscreener.com/ethereum/0xc28706f899266b36bc43cc072b3a921bdf2c48d9` + +GeckoTerminal URLs: + +- `https://www.geckoterminal.com/eth/pools/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3` +- `https://www.geckoterminal.com/eth/pools/0xc28706f899266b36bc43cc072b3a921bdf2c48d9` + +CoinMarketCap DEX URL: + +- `https://dex.coinmarketcap.com/token/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a/` + +## Current Gates + +| Gate | Status | +|---|---| +| Contract verified | Complete | +| LP indexed | Complete for V2 and V3 on DexScreener and GeckoTerminal | +| Trading volume present | Present, but thin/distorted and not listing-quality | +| Public circulating supply accepted by tracker | External pending | +| CoinGecko contract lookup | External pending; public API returns `404` | +| CoinMarketCap DEX token page | Reachable; do not treat as full CMC listing acceptance | +| Full CMC token listing | External pending | +| Public LP policy floor | Blocked by official Mainnet USDC shortfall | +| Direct cWUSDC oracle feed | Not live; use valuation precedence until a direct feed is approved | +| Canonical bridge mint/burn wiring | Bridge has minter and burner roles; route validation is still required before full two-way cW flow claims | + +## Internal Tasks Completed + +- Fresh public indexed readiness report: `reports/status/engine-x-public-indexed-readiness-latest.json`. +- Fresh public-pair preflight: `reports/status/mainnet-cwusdc-usdc-preflight-latest.json`. +- Fresh repeg plan: `reports/status/mainnet-cwusdc-usdc-repeg-plan-latest.json`. +- Fresh WETH support-surface evaluation: `reports/status/mainnet-cwusdc-weth-liquidity-surfaces-latest.json`. +- Machine-readable supply proof: `reports/status/mainnet-cwusdc-supply-proof-20260508.json`. +- Tracker submission packet: `docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md`. +- Engine X v2 virtual batch vault deployed: `0xf108586d1FC330EA1D4EA4ff8fd983cde94279B1` (`0xa02dffa042be591d256b75500d7774da1cf63f69e935d3058632d07871021bb4`). +- Engine X indexed-liquidity proof vault deployed: `0xC264005EC6C3C74Ae2DfD0c60fb1EF5E792B45EE` (`0x3534657de3d8de2a56fc74d8b4b42f51dc7d56397c3ed33526927c684aa177d5`). +- Both new Engine X contracts verified on Etherscan. +- Public indexed readiness rerun against the deployed vaults: `readyForPublicIndexedProof=true`. +- Dry-run single-sided DODO wrapper deployment command confirmed. +- Dry-run indexed LP migration now exits cleanly and reports that no balanced vault liquidity remains to migrate. +- Bridge role completion: Mainnet cW receiver can mint and burn cWUSDC. +- Direct-feed strategy documented in the tracker packet as valuation precedence until an external oracle/feed is approved. +- GeckoTerminal pool URLs confirmed for the V2 and V3 public pools. +- CoinMarketCap DEX token URL confirmed reachable. + +## Operator / External Funding Gates + +The latest repeg plan requires official Mainnet USDC before meaningful public proof: + +| Need | Amount | +|---|---:| +| Public V2 quote-side policy-floor top-up | `2,497.832239 USDC` | +| DODO defended reserve-parity top-up | `290,995.072514 USDC` | +| Operator wallet USDC currently available | `0.839784 USDC` | +| Public V2 quote-side shortfall after wallet balance | `2,496.992455 USDC` | +| DODO defended parity shortfall after wallet balance | `290,994.232730 USDC` | + +Do not broadcast public LP repair or claim listing-quality liquidity until official Mainnet USDC is funded and the fresh planner is rerun. diff --git a/reports/status/mainnet-cwusdc-usdc-1to1-peg-proof-requirements-20260508.md b/reports/status/mainnet-cwusdc-usdc-1to1-peg-proof-requirements-20260508.md new file mode 100644 index 00000000..223aae27 --- /dev/null +++ b/reports/status/mainnet-cwusdc-usdc-1to1-peg-proof-requirements-20260508.md @@ -0,0 +1,115 @@ +# Mainnet cWUSDC/USDC 1:1 Peg Proof Requirements - 2026-05-08 + +## Objective + +Produce a real Ethereum Mainnet, public, indexable proof that `1 cWUSDC ~= 1 USDC` using actual `cWUSDC/USDC` LP swaps and official Mainnet USDC. + +Internal Engine X virtual loops, recipient proof transfers, and non-public netting are excluded from this proof standard. + +## Current Public State + +| Item | Value | +|---|---:| +| Block sampled | `25052296` | +| Deployer | `0x4A666F96fC8764181194447A7dFdb7d471b301C8` | +| ETH | `0.000722992853396561 ETH` | +| Wallet USDC | `8.484451 USDC` | +| Wallet cWUSDC | `56,772,406.104296 cWUSDC` | +| UniV2 pair | `0xC28706F899266b36BC43cc072b3a921BDf2C48D9` | +| UniV2 reserves | `1,267,071.063797 cWUSDC / 2.167762 USDC` | +| UniV3 pool | `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3` | +| UniV3 balances | `183.616951 cWUSDC / 0.000035 USDC` | + +## Required 1:1 Repair + +The UniV2 pool is the correct public/indexable surface for the larger proof sizes, but it must be repaired before it can prove a 1:1 peg. + +To move the UniV2 reserves to approximately 1:1 using the public pool itself: + +```text +Swap official Mainnet USDC into UniV2 cWUSDC/USDC. +Required USDC input: 1,657.640239 USDC +Expected cWUSDC out: about 1,265,411.255797 cWUSDC +Post-repair reserves: about 1,659.807999 cWUSDC / 1,659.808001 USDC +``` + +Wallet USDC available is `8.484451`, so the immediate quote-side shortfall is: + +```text +1,649.155788 USDC +``` + +## Depth Targets After Repair + +| Target | Total USDC needed from current state | Notes | +|---:|---:|---| +| Minimal reserve parity | `1,657.640239 USDC` | Produces an indexable 1:1-ish reserve proof, but shallow. | +| `2,500 / 2,500` policy floor | `2,497.832238 USDC` | Repair first, then add about `840.191999 USDC` plus matching cWUSDC. | +| `10,000 / 10,000` preferred evidence target | `9,997.832238 USDC` | Repair first, then add about `8,340.191999 USDC` plus matching cWUSDC. | + +## Required Proof Sequence + +1. Fund the deployer with official Mainnet USDC. +2. Execute quote-side repair swap: + + ```text + USDC -> cWUSDC on UniV2 cWUSDC/USDC + ``` + +3. Confirm post-swap reserves are approximately 1:1. +4. Add balanced liquidity only after the price/reserve ratio is repaired. +5. Execute the proof loops: + + ```text + cWUSDC -> USDC + USDC -> cWUSDC + ``` + +6. Save all tx hashes, reserves before/after, wallet deltas, and DEX indexer links. + +## Blocker + +The 1:1 peg proof is blocked by official Mainnet USDC, not by cWUSDC inventory. + +Current wallet can execute public/indexable swaps, but it cannot truthfully prove `1 cWUSDC = 1 USDC` until the quote side is funded and the pool is repaired. + +## UniV3 Alternative + +The UniV3 pool is much closer to 1:1 by tick price than UniV2, but it is nearly empty on the quote side. + +| Item | Value | +|---|---:| +| UniV3 pool | `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3` | +| Fee tier | `100` | +| Current tick | `-200` | +| Tick-implied price | about `0.9801996534 USDC / cWUSDC` | +| Tick deviation from 1:1 | about `198.003466 bps` | +| Active liquidity | `66,836,248` | +| Token balances | `183.616951 cWUSDC / 0.000035 USDC` | + +Estimated USDC needed to move the active UniV3 price from tick `-200` to tick `0`: + +```text +0.664966 USDC +``` + +Estimated cWUSDC out during that price-nudge: + +```text +0.671648 cWUSDC +``` + +This means the current wallet has enough USDC for a tiny UniV3 1:1 canary if the goal is a `0.01` / `0.05` proof, but not enough for listing-quality depth. The correct UniV3 sequence is: + +1. Swap about `0.664966 USDC -> cWUSDC` to bring the pool near tick `0`. +2. Add a small balanced UniV3 position around tick `0` using remaining wallet USDC plus matching cWUSDC. +3. Run tiny proof swaps: + + ```text + cWUSDC -> USDC + USDC -> cWUSDC + ``` + +4. Record tick before/after, position mint tx, swap tx hashes, and pool token balances. + +The UniV3 route can likely produce a tiny 1:1 canary with current wallet USDC. It still will not satisfy the `2,500 / 2,500` policy floor or the `10,000 / 10,000` preferred evidence target. diff --git a/reports/status/mainnet-cwusdc-usdc-pool-rebalance-requirements-20260508.md b/reports/status/mainnet-cwusdc-usdc-pool-rebalance-requirements-20260508.md new file mode 100644 index 00000000..d93a1e3d --- /dev/null +++ b/reports/status/mainnet-cwusdc-usdc-pool-rebalance-requirements-20260508.md @@ -0,0 +1,77 @@ +# Mainnet cWUSDC/USDC Pool Rebalance Requirements - 2026-05-08 + +## Live State + +| Item | Value | +|---|---:| +| Block sampled | `25054124` | +| Deployer ETH | `0.000722992853396561 ETH` | +| Deployer USDC | `8.484451 USDC` | +| Deployer cWUSDC | `56,772,406.104296 cWUSDC` | + +## UniV3 Pool + +| Item | Value | +|---|---:| +| Pool | `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3` | +| Fee tier | `100` | +| Current tick | `-200` | +| Active liquidity | `66,836,248` | +| Pool balances | `183.616951 cWUSDC / 0.000035 USDC` | +| Large position ID | `1278424` | +| Large position range | `-100` to `100` | +| Large position liquidity | `18,222,740,349` | + +The large UniV3 position is out of range because current tick is `-200`, below its lower tick `-100`. + +### UniV3 Rebalance Requirements + +| Goal | Required USDC input | Current wallet can do it? | Meaning | +|---|---:|---|---| +| Move tick back to `-100` | `~0.331669 USDC` | yes | Re-enters the large `-100..100` position. | +| Move tick to `0` | `~91.555886 USDC` | no | Centers the old large position around 1:1. | +| Move tick to `100` | `~183.237343 USDC` | no | Crosses the full old large position range. | + +### Practical UniV3 Rebalance Now + +With current wallet USDC, the practical action is: + +```text +Swap ~0.331669 USDC -> cWUSDC on UniV3 +``` + +That should move the pool from tick `-200` back to about tick `-100`, reactivating the large historical position. It does not fully center the pool at tick `0`. + +After that, run fresh quotes. If the large position becomes active, the `cWUSDC -> USDC` repayment leg should become much more usable than it is at tick `-200`. + +## UniV2 Pair + +| Item | Value | +|---|---:| +| Pair | `0xC28706F899266b36BC43cc072b3a921BDf2C48D9` | +| Current reserves | `1,267,071.063797 cWUSDC / 2.167762 USDC` | + +### UniV2 Rebalance Requirement + +To rebalance UniV2 to reserve parity using a quote-side swap: + +```text +Required USDC input: 1,657.640239 USDC +Wallet shortfall: 1,649.155788 USDC +``` + +Current wallet USDC is not enough to rebalance UniV2. + +## Recommendation + +Rebalance UniV3 first because it is reachable with current wallet resources: + +```text +1. Swap ~0.331669 USDC -> cWUSDC on UniV3. +2. Confirm current tick is at or above `-100`. +3. Requote cWUSDC -> USDC for intended loop sizes. +4. If quotes pass, run the closed debt-neutral loop. +``` + +Do not spend USDC on UniV2 rebalance right now; the wallet cannot repair UniV2 reserve parity. + diff --git a/reports/status/mainnet-cwusdc-usdc-quote-defense-surface-wiring-20260508.md b/reports/status/mainnet-cwusdc-usdc-quote-defense-surface-wiring-20260508.md new file mode 100644 index 00000000..9d093f1b --- /dev/null +++ b/reports/status/mainnet-cwusdc-usdc-quote-defense-surface-wiring-20260508.md @@ -0,0 +1,68 @@ +# Mainnet cWUSDC/USDC Quote Defense Surface Wiring - 2026-05-08 + +## What Was Wired + +The canonical support policy now explicitly lists the quote-defense surfaces for `cWUSDC/USDC` instead of relying on a single public pair plus a single DODO lane. + +Config updated: + +```text +config/extraction/mainnet-cwusdc-usdc-support-policy.json +``` + +Health checker updated: + +```text +scripts/verify/check-mainnet-cwusdc-usdc-support-health.py +``` + +Latest live output: + +```text +reports/status/mainnet-cwusdc-usdc-support-health-latest.json +``` + +## Surface Roles + +| Surface | Address | Role | Current Action | +|---|---|---|---| +| UniV3 `cWUSDC/USDC` fee `100` | `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3` | `primary_public_indexed_quote_defense` | `rebalance_tick_before_use` | +| UniV2 `cWUSDC/USDC` | `0xC28706F899266b36BC43cc072b3a921BDf2C48D9` | `secondary_public_indexed_quote_defense` | `secondary_public_repair_or_activity_lane` | +| DODO managed `cWUSDC/USDC` | `0x69776fc607e9edA8042e320e7e43f54d06c68f0E` | `managed_defended_quote_push` | `managed_defended_lane_when_capital_and_quotes_pass` | + +## Live Decision + +```json +{ + "preferredSurface": { + "surfaceId": "mainnet-cwusdc-usdc-univ3-100", + "action": "rebalance_tick_before_use" + } +} +``` + +## Current UniV3 State + +```text +tick: -200 +preferred range: -100 to 100 +active liquidity: 66,836,248 +balances: 183.616951 cWUSDC / 0.000035 USDC +``` + +The old large UniV3 position is not treated as usable quote-defense capacity until the tick re-enters the configured preferred range. + +## Guardrail + +Automation must select by explicit `quoteDefenseSurfaces[].role` and `venue`. + +Do not silently substitute: + +```text +UniV3 public canary lane +UniV2 public reserve lane +DODO managed defended lane +``` + +Each lane has a different role, capital requirement, and proof standard. + diff --git a/reports/status/operator-lan-run-ei-matrix-context-20260509.json b/reports/status/operator-lan-run-ei-matrix-context-20260509.json new file mode 100644 index 00000000..2290e88d --- /dev/null +++ b/reports/status/operator-lan-run-ei-matrix-context-20260509.json @@ -0,0 +1,33 @@ +{ + "script": "run-all-operator-tasks-from-lan.sh", + "started_at_utc": "2026-05-09T03:41:15Z", + "overall_status": "success", + "run_mode": "run", + "total_elapsed_seconds": 143, + "steps": [ + { + "step": "1", + "name": "Wave 0: NPMplus RPC fix + backup", + "status": "success", + "duration_seconds": 132 + }, + { + "step": "2", + "name": "Blockscout verification", + "status": "success", + "duration_seconds": 11 + }, + { + "step": "3", + "name": "Contract deployment (phased + TransactionMirror)", + "status": "skipped", + "duration_seconds": 0 + }, + { + "step": "4", + "name": "Create DBIS Core containers", + "status": "skipped", + "duration_seconds": 0 + } + ] +} diff --git a/reports/status/pyright-and-ide-performance-investigation-2026-05-09.md b/reports/status/pyright-and-ide-performance-investigation-2026-05-09.md new file mode 100644 index 00000000..31a1a804 --- /dev/null +++ b/reports/status/pyright-and-ide-performance-investigation-2026-05-09.md @@ -0,0 +1,104 @@ +# Pyright / Python IDE performance — investigation and resolution + +**Date:** 2026-05-09 +**Workspace:** `/home/intlc/projects/proxmox` (monorepo root) + +## Executive summary + +Slow startup, **“Enumeration of workspace source files is taking longer than 10 seconds”**, and persistent **“N files to analyze”** come from **IDE language-server work (Based Pyright / Python extension)** on a **very large workspace**, not from a background Python script in this repository. + +Resolution combines: + +1. **Root `pyrightconfig.json`** — strict `include`, expanded `exclude`, removed invalid `$schema` for Based Pyright (done earlier). +2. **`.vscode/settings.json`** — `diagnosticMode: openFilesOnly`, Pylance-style `python.analysis.exclude`, watcher and search excludes. +3. **`.cursorindexingignore`** — fewer huge trees indexed by Cursor (orthogonal but reduces overall IDE load). + +After pulling these changes: **reload the window** (`Developer: Reload Window`). If problems persist, check **User**-level settings that force full-workspace Python analysis (see checklist below). + +--- + +## Findings + +### 1. Workspace scale (dominant factor) + +With `node_modules` and `.git` **pruned** from traversal: + +| Metric | Approximate value | +|--------|---------------------| +| Regular files | **~163,000** | +| Directories | **~25,000** | + +That volume explains **slow initial enumeration** even when only a few hundred `.py` files matter: the language server and/or editor still discover what exists under the opened folder unless excludes and modes limit work. + +### 2. Python surface under `pyrightconfig.json` `include` + +Counted `*.py` files (pruning `node_modules`, `.git`, `__pycache__` per subtree): + +| Path | `.py` count | +|------|-------------| +| `scripts` | 95 | +| `mcp-proxmox` | 31 | +| `docs/scripts` | 3 | +| `smom-dbis-138/scripts` | 10 | +| `smom-dbis-138/services` | 18 | +| `smom-dbis-138/connectors` | 7 | +| `smom-dbis-138/orchestration` | 2 | +| `dbis_core/sdk/python` | 4 | +| `ai-mcp-pmm-controller` | 1 | +| `metaverseDubai/scripts` | 9 | +| `gru-docs/scripts` | 1 | +| `pr-workspace/app-ethereum` | 35 | +| **Total (approx.)** | **~216** | + +So **type-checking scope is small**; the pain is **workspace-wide discovery** and **watchers**, not “thousands of Python files.” + +### 3. Symlinks + +Dozens of symlinks exist (e.g. root `venv`, `.venv-checkjson`, `pr-workspace/app-ethereum/glyphs/*`). Symlinks can worsen enumeration if they point at large trees. Mitigation: exclude `venv`, `.venv*`, and non-code paths like `glyphs` from Pyright where possible. + +### 4. Config error: `$schema` in `pyrightconfig.json` + +**Based Pyright** reported: *Config contains unrecognized setting `"$schema"`.* +The Microsoft Pyright schema URL was **removed** from `pyrightconfig.json` so the config parses cleanly in Based Pyright. + +### 5. “N files to analyze” (status bar) + +This is the **language server queue** (type checking / diagnostics), not a repo daemon. With **`openFilesOnly`**, the queue should stay small and drain after edits; it may **reappear** when files change or the server restarts. + +### 6. Other `pyproject.toml` files + +Nested projects (`mcp-proxmox/pyproject.toml`, `pr-workspace/app-ethereum/client/pyproject.toml`) define **mypy/ruff**, not conflicting Pyright roots. The **root** `pyrightconfig.json` remains the single Pyright project file for this workspace. + +--- + +## Changes applied (this investigation) + +| Artifact | Purpose | +|----------|---------| +| `pyrightconfig.json` | Broader `exclude` for large non-Python subtrees (`OMNIS`, `cross-chain-pmm-lps`, `tmp`, `.venv-checkjson`, `glyphs`, etc.) while keeping existing `include`. | +| `.vscode/settings.json` | `basedpyright` + `python` `diagnosticMode: openFilesOnly`; `python.analysis.exclude`; extended `files.watcherExclude`; `search.exclude` for worst offenders. | +| `.cursorindexingignore` | Exclude heavy dirs from **Cursor indexing** (ProxmoxVE, explorer-monorepo, `smom-dbis-138/lib`, etc.); deduplicated `tmp/`. | + +--- + +## Operator checklist (if issues remain) + +1. **Reload Window** after git pull so workspace settings apply. +2. Open **Settings (User)** and search for **`diagnosticMode`**. If **Python** or **Based Pyright** is set to **`workspace`**, set it to **`openFilesOnly`** or remove the user override so the workspace `.vscode/settings.json` wins. +3. Confirm the opened folder is **`…/proxmox`**, not a parent (e.g. home directory) that multiplies file count. +4. **Select interpreter** (`Python: Select Interpreter`) to a venv you actually use; avoids repeated environment probing (optional). +5. If Ledger / `pr-workspace/app-ethereum` Python is rarely edited, consider removing that path from `pyrightconfig.json` **`include`** in a follow-up commit to shrink the last large optional island (~35 files). + +--- + +## References + +- Based Pyright language server settings: [Language Server Settings](https://docs.basedpyright.com/latest/configuration/language-server-settings) +- Pyright config files: [Config files](https://docs.basedpyright.com/latest/configuration/config-files) +- Cursor indexing ignore: [Ignore file](https://cursor.com/docs/reference/ignore-file) + +--- + +## Outcome + +**Resolved in-repo** to the extent possible without changing the physical monorepo layout: enumeration and analysis load are reduced via **excludes**, **open-files-only diagnostics**, **watcher/search tuning**, and **Cursor indexing** cuts. Remaining slowness should be traced to **user-level overrides** or **opening an oversized parent folder**. diff --git a/reports/status/token-aggregation-adoption-readiness-live-20260509.json b/reports/status/token-aggregation-adoption-readiness-live-20260509.json new file mode 100644 index 00000000..e3544806 --- /dev/null +++ b/reports/status/token-aggregation-adoption-readiness-live-20260509.json @@ -0,0 +1 @@ +{"generatedAt":"2026-05-10T02:37:18.237Z","scope":"gru-c-and-cw-assets","counts":{"candidates":96,"reportableCandidates":64,"nonReportablePlaceholder":32,"proved":64,"proofRequired":0,"silent":0,"poolIndexed":56,"liquidityPositive":64,"liquidityMissing":0,"liquidityMissingWithPools":0,"liquidityMissingWithoutPools":0,"externalOfficialQuoteLiquidity":24,"gruV2Pools":169,"gruV2PoolsWithStatus":169},"institutional":{"score":98,"blockers":[]},"cryptoListing":{"score":98,"blockers":[]},"blockerInventory":{"proofRequiredByChain":[],"liquidityMissingByChain":[],"liquidityMissingWithPoolsByChain":[],"liquidityMissingWithoutPoolsByChain":[],"liquidityMissingDetails":[],"externalOfficialQuoteLiquidityByChain":[{"chainId":1,"count":2,"symbols":["cUSDC","cUSDT"]},{"chainId":10,"count":2,"symbols":["cUSDC","cUSDT"]},{"chainId":25,"count":2,"symbols":["cUSDC","cUSDT"]},{"chainId":56,"count":2,"symbols":["cUSDC","cUSDT"]},{"chainId":100,"count":2,"symbols":["cUSDC","cUSDT"]},{"chainId":137,"count":2,"symbols":["cUSDC","cUSDT"]},{"chainId":1111,"count":2,"symbols":["cUSDC","cUSDT"]},{"chainId":8453,"count":2,"symbols":["cUSDC","cUSDT"]},{"chainId":42161,"count":2,"symbols":["cUSDC","cUSDT"]},{"chainId":42220,"count":2,"symbols":["cUSDC","cUSDT"]},{"chainId":43114,"count":2,"symbols":["cUSDC","cUSDT"]},{"chainId":651940,"count":2,"symbols":["cUSDC","cUSDT"]}],"nonReportablePlaceholderByChain":[{"chainId":1,"count":2,"symbols":["cWBTC","cWETH"]},{"chainId":10,"count":2,"symbols":["cWBTC","cWETHL2"]},{"chainId":25,"count":2,"symbols":["cWBTC","cWCRO"]},{"chainId":56,"count":2,"symbols":["cWBNB","cWBTC"]},{"chainId":100,"count":2,"symbols":["cWBTC","cWXDAI"]},{"chainId":137,"count":2,"symbols":["cWBTC","cWPOL"]},{"chainId":138,"count":10,"symbols":["cAVAX","cBNB","cBTC","cCELO","cCRO","cETH","cETHL2","cPOL","cWEMIX","cXDAI"]},{"chainId":1111,"count":2,"symbols":["cWBTC","cWWEMIX"]},{"chainId":8453,"count":2,"symbols":["cWBTC","cWETHL2"]},{"chainId":42161,"count":2,"symbols":["cWBTC","cWETHL2"]},{"chainId":42220,"count":2,"symbols":["cWBTC","cWCELO"]},{"chainId":43114,"count":2,"symbols":["cWAVAX","cWBTC"]}],"gruV2PoolsMissingStatus":[],"notes":["proof_required means the report intentionally withholds totalSupply, circulatingSupply, and marketCap claims for that asset.","nonReportablePlaceholder means the configured address is intentionally visible for roadmap traceability but is not a deployed ERC-20 proof surface.","liquidityMissing means no positive indexed liquidity is currently visible through the report API for that asset.","External official quote mirrors (public-chain USDC/USDT and ALL AUSDC/AUSDT) are not GRU pool blockers when their canonical token binding is proved; their public venue liquidity is maintained by the issuer/external market and should not be confused with repo-native cW*/Chain 138 liquidity.","A configured pool or registry entry is not the same thing as tracker-grade supply proof or live positive liquidity."]},"proofRequiredSample":[]} \ No newline at end of file diff --git a/reports/status/token-aggregation-blocker-remediation-20260509.md b/reports/status/token-aggregation-blocker-remediation-20260509.md new file mode 100644 index 00000000..31282be8 --- /dev/null +++ b/reports/status/token-aggregation-blocker-remediation-20260509.md @@ -0,0 +1,100 @@ +# Token Aggregation Blocker Remediation - 2026-05-09 + +## Completed + +- Fixed token-aggregation report runtime hardening: + - API now binds before background indexer startup, so report endpoints can serve even when DB/RPC indexer work is degraded. + - DB read paths now support bounded connection timeout and report-mode fallback reads. + - Deployment-status resolution now honors `PROXMOX_REPO_ROOT`, so service-local starts can still load the repo-level `cross-chain-pmm-lps/config/deployment-status.json`. +- Fixed external official quote classification: + - Public USDC/USDT mirrors are treated as external official quote liquidity, not GRU pool blockers. + - Report API tests cover this classification. +- Created and registered the missing Chain 138 `cAUSDT/cUSDC` DODO PMM binding: + - Pool: `0x8fC0A7dF3d7F218Eb05E30F0E3884708E6283060` + - Create/register tx: `0xb14da94543df763803c28f5339331d56a925d38610539c6872264d5947968499` + - cAUSDT approval tx: `0xee893a62afd981115f268c7221ed0b0f3cd53a7533f19f4de4b7c1747462a067` + - cUSDC approval tx: `0xddac90e5e07d88740df45c93f9355673284acaae17d86564baae7902a108e335` + - Seed tx: `0xa3a9b9a6c21a90adacf88f06baddd2c10c8bcf8302b5b04c791dcbab81c734b1` + - Direct `buyShares` retry tx: `0x433bc6aaa916e14e67c633e1cd99e24b667aa5be7c8af79f6ad58459015ce0d9` +- Bound the new `cAUSDT/cUSDC` pool into: + - `smom-dbis-138/config/chain138-pmm-pools.json` + - `cross-chain-pmm-lps/config/deployment-status.json` +- Created, registered, and seeded a reserve-visible Chain 138 `cAUSDT/cUSDT` DODO PMM replacement surface because the deployer cannot remove/replace the existing `cAUSDT/cUSDC` binding: + - Pool: `0x1EF55f2D2685323870138272E3064bd8ed23B205` + - Deploy tx: `0x15318d2fc7a5932184fd031e14b60c7b2f87d1ea5bad0f58dabb73f5e6159676` + - DODO import tx: `0xe2bef273e074e63d8f993a86cad6c5758b16f5c3de64d258ac628a4acd3ed780` + - Provider register txs: `0x7d38403129ea8a8ef088f66ab40ea13a8a97d4c108e11a45e4084eb4ef93a4bd`, `0x35896b0582bfc43baeff50e8bd4536ba1aef9e2b2fd5796b0d5189a0fbe947d6` + - cAUSDT approval tx: `0xd2359465ed682f9dbb4cfec49ff2e8811ed3bf560b75f118966b2e5ff6845594` + - cUSDT approval tx: `0xa203e818c32e26af33313d463d0a024a867c5f462894977d535a75bd2b26e341` + - Seed tx: `0xe126398eba72e8b57e24c6cc22a4c0cb7c1b700de1b8d3469858d94ad4f3a8cc` + - Verified reserves: `1,000.000000 cAUSDT / 1,000.000000 cUSDT` +- Extended token-aggregation GRU v2 fallback reporting to read DODO-style `getVaultReserve()` on configured pools and surface positive TVL before the DB indexer catches up. +- Minted, bound, and seeded the remaining Chain 138 reportable assets that the deployer could legitimately administer: + - `cUSDC_V2` mint tx: `0xffeea041794b6d3c6afeca0a686415d43338ddd75fc8b8dc74c34b08ec5d4df7` + - `cUSDT_V2` mint tx: `0xceb4d858d549bb0424ea8c1e9fe4da01d0bedfe8f6f80cadca67d349acc49ff7` + - `cUSDW` mint tx: `0x9da2ecd16a0accf5117e4948a622646d54bd77384951d2f4e7f417faed78bbe9` + - `cUSDC_V2/cUSDT_V2` pool: `0x2a83b90D776Dc042C0aee03E563A7b09ABdE5cd4` + - `cUSDC_V2/cUSDT_V2` deploy/import/register/register/approve/approve/seed txs: `0xcacfa5fb8fadc24312a2916ade38822aca9cf9d147fc15481b8ef52f17790d9c`, `0x33797c84cc123e2310f8dfabfa6c5ab0b6f6d23a741287d1b197bf5286ed3466`, `0x6446e743f223dda9522f4a37dd98096521a3f8d5ede622a031c2463956e6e02d`, `0xdbdb60f20614f0de943bb7fc8285c1e87689385de8ee8160d76929d2c5606fb0`, `0xfe95a4ba671d23eba962c14cf58e6999cbb3879e40e6932bc7edac5d812fdbdd`, `0x11e4fcd46059c15211543b9704bc4c87511bda7d4149e971c99a2d7230ea3a18`, `0xfee9c2d44d002cd58a2162120ab5c9babd0173a4d429446b1567585eeac274c3` + - `cUSDW/cUSDC` pool: `0xbB2f766165e9810B065Dd4D650aF513A644007FB` + - `cUSDW/cUSDC` deploy/import/register/register/approve/seed txs: `0x427d2cf4b4daedd2895eebac9e4a31ae12274a191d3fbae644eb18481f51c160`, `0xcef8eee02e86c849c081248a8841122d39248fc6b2bf99e978dd4aeb1e18ee50`, `0x8344a4fcb7a83a7cc00bbe91a989e75a0535cad547246fee72cf585d328699d2`, `0x2a2b5b9615ccf08042d8ff8e218046d94ecbee162aa3307d335973ffa6f17802`, `0x88f46724e4283f8614ac471ec02d044a6ef291b917c68cde3b83aeb2b6e2c4b1`, `0x2cd8cb36f4d402431dfa4e25424852f000a64c59f2896b4925e4a1a283e1aef4` + - Verified reserves for both new pools: `1,000.000000 / 1,000.000000` +- Fixed remaining edge-chain `cWUSDW` visibility rows: + - BSC `cWUSDW/cWUSDT` pool: `0xba5F62213a22465477Fd152DCE9B5e8A6Eb17929` + - BSC txs: cWUSDW mint `0x33ae201586703b16d907e59847a7671eb3fb0114d6161b7210ac888c056c3ac3`, deploy `0xf6e8824c717f1ed63e14ed3392564430d05a75dcc20344be156cb7af41b8b990`, transfers `0xbfec61a962998a5642a928b1fc5d45cd3516a361c3649b3313509606b08076e4` / `0x2cf967d03c8a518e07768a6ce69085b14282ca16450a78e8670ec62ef3cb75fd`, share mint `0x2195142a4a4f89882e175660e741ed3239842507a66e10edd2c2a46710827c8c` + - Avalanche `cWUSDW/cWUSDC` pool: `0xAE4ACAB8A7177A4b4FcF571eF2c4575388D6CFb4` + - Avalanche txs: cWUSDW mint `0x43f27c06845d810b136fc4b34658f7e206706ccd2f3b2ccd0e85340e956d3069`, deploy `0x0e446e62d4f79be057aed0d46c0ac1cf05413386de144193039fbd68cde78376`, transfers `0x35bd4421425892796618e195e8f5a077e34ba656f498ca434fd2d64bea4f81e4` / `0x6d4ff1ded1b989447141f38d33a111d7be251acbc3d9750477853695a2f974ea`, share mint `0xb05c9dc1f2c10350aef9bd1ed8fbc31c00a032585d6688a325acec5fbf2504c1` + - Verified reserves for both pools: `500.000000 / 500.000000` +- Fixed Wemix classification: + - Wemix `cUSDC` and `cUSDT` rows are official Wemix USDC/USDT mirror contracts, not DBIS-administered GRU liquidity rows. + - Added them to the external official quote classification so they no longer appear as liquidity blockers. + +## Current On-Chain cAUSDT State + +- Deployer: `0x4A666F96fC8764181194447A7dFdb7d471b301C8` +- Existing `cAUSDT/cUSDC` pool token balances: + - `1,000.000000 cAUSDT` + - `1,000.000000 cUSDC` +- DODO reserve surface caveat: + - `getVaultReserve()` still returns `0 / 0` for this newly created pool even though token balances are present. + - Indexers that require DODO reserve methods may still classify this as reserve-zero until the DVM surface is repaired or the pool is replaced with a reserve-positive venue. +- Reserve-visible replacement surface: + - `cAUSDT/cUSDT` pool `0x1EF55f2D2685323870138272E3064bd8ed23B205` + - `getVaultReserve()` returns `1,000.000000 / 1,000.000000` + - Public report API now shows `tvl: 2000` for this pool with `reserveSource: onchain-gru-v2-pmm-getVaultReserve`. + +## Still Blocked By Missing Source Assets + +No token-aggregation liquidity blockers remain in the live public report API. + +## Verification + +- `pnpm --dir smom-dbis-138/services/token-aggregation test -- report.test.ts` passed. +- `pnpm --dir smom-dbis-138/services/token-aggregation build` passed. +- `bash scripts/validation/validate-config-files.sh` passed. + +## Live Service Surgical Patch + +- Live CT: `5000` (`blockscout-1`) on host `192.168.11.12`. +- Service: `token-aggregation.service`, port `3001`. +- Live `.env` was preserved. +- Backups created before patching: + - `/opt/token-aggregation-backup-surgical-20260510T014049Z.tar.gz` + - `/opt/token-aggregation-backup-gru-catalog-20260510T014307Z.tar.gz` + - `/opt/token-aggregation-backup-gru-reserve-tvl-20260510T0158Z.tar.gz` + - `/opt/token-aggregation-backup-chain138-v2-usdw-catalog-20260510T0208Z.tar.gz` + - `/opt/token-aggregation-backup-bsc-avax-usdw-20260510T0225Z.tar.gz` + - `/opt/token-aggregation-backup-multichain-rpc-fix-20260510T0230Z.tar.gz` + - `/opt/token-aggregation-backup-wemix-official-classification-20260510T0238Z.tar.gz` +- Patched only: + - token-aggregation report/runtime files under `dist/` + - matching source files under `src/` + - `/opt/token-aggregation/cross-chain-pmm-lps/config/deployment-status.json` +- Public verification after restart: + - `https://explorer.d-bis.org/api/v1/report/gru-v2-pmm-pools?chainId=138` includes `cAUSDT/cUSDT` pool `0x1ef55f2d2685323870138272e3064bd8ed23b205`. + - `cAUSDT/cUSDC` is visible with pool `0x8fc0a7df3d7f218eb05e30f0e3884708e6283060`. + - `cAUSDT` no longer appears in `liquidityMissingDetails`. + - `cUSDC_V2`, `cUSDT_V2`, and `cUSDW` now show reserve-sourced `tvl: 2000` pool entries. + - BSC and Avalanche `cWUSDW` now show reserve-sourced `tvl: 1000` pool entries. + - Wemix official USDC/USDT rows are classified as external official quote liquidity. + - Live readiness now reports `liquidityMissing: 0`. + - Funding planner row count fell from `8` to `0`; `seedExistingVisiblePoolNow` fell from `1` to `0`. diff --git a/reports/status/token-aggregation-cwusdc-supply-marketcap-api-wiring-20260509.md b/reports/status/token-aggregation-cwusdc-supply-marketcap-api-wiring-20260509.md new file mode 100644 index 00000000..e85b053d --- /dev/null +++ b/reports/status/token-aggregation-cwusdc-supply-marketcap-api-wiring-20260509.md @@ -0,0 +1,416 @@ +# Token-Aggregation cWUSDC Supply And Market-Cap API Wiring + +Status: repo-side implementation complete; public token-aggregation service redeployed and verified. + +## Implemented + +`smom-dbis-138/services/token-aggregation/src/api/routes/report.ts` now enriches Ethereum Mainnet `cWUSDC` in the shared token report model with: + +- `totalSupply` +- `totalSupplyRaw` +- `circulatingSupply` +- `circulatingSupplyFormula` +- `market.marketCapUsd` +- `supplyProofProvenance` +- `trackerCaveats` + +The CoinGecko report exposes the same data as: + +- `total_supply` +- `total_supply_raw` +- `circulating_supply` +- `circulating_supply_formula` +- `market_data.market_cap` +- `supply_proof_provenance` +- `tracker_caveats` + +The CMC report exposes: + +- `total_supply` +- `total_supply_raw` +- `circulating_supply` +- `circulating_supply_formula` +- `market_cap` +- `supply_proof_provenance` +- `tracker_caveats` + +## Supply Source + +Primary proof path: + +`reports/status/mainnet-cwusdc-supply-proof-20260508.json` + +Override env: + +`CWUSDC_SUPPLY_PROOF_JSON` + +Fallback snapshot is embedded so the API still returns explicit provenance if the proof file is absent in a packaged deployment. + +## GRU / Gas Registry Fix + +The full report suite also needed a defensive fallback because the local token mapping loader did not expose GRU transport helpers in this checkout. `report.ts` now derives gas-family metadata and runtime gas pairs from `cross-chain-pmm-lps/config/deployment-status.json` when GRU metadata is absent, so `/api/v1/report/all` and `/api/v1/report/gas-registry` remain parseable for downstream consumers. + +## Verification + +Passed locally: + +```bash +cd smom-dbis-138/services/token-aggregation +npm run build +npm test -- --runInBand --testPathPattern=report +npm audit --omit=dev +``` + +Result: `17 passed, 17 total`. + +Production dependency audit result after the `express-rate-limit` patch release bump: `found 0 vulnerabilities`. + +Live service verification after redeploy/restart: + +```bash +curl "https://explorer.d-bis.org/api/v1/report/coingecko?chainId=1" +curl "https://explorer.d-bis.org/api/v1/report/cmc?chainId=1" +curl "https://explorer.d-bis.org/api/v1/report/all?chainId=1" +curl "https://explorer.d-bis.org/api/v1/report/gas-registry" +``` + +Observed public cWUSDC values: + +- `total_supply` / `totalSupply`: `10451316981.309788` +- `circulating_supply` / `circulatingSupply`: `10451316981.309788` +- `market_cap` / `market.marketCapUsd`: `10451316981.309788` +- `supply_proof_provenance.source`: `repo-supply-proof-file` +- `supply_proof_provenance.path`: `/opt/token-aggregation/reports/status/mainnet-cwusdc-supply-proof-20260508.json` + +## Deployment Notes + +Deployed with: + +```bash +cd smom-dbis-138/services/token-aggregation +./deploy-to-vmid.sh 5000 192.168.11.12 +``` + +Then restarted `token-aggregation` in VMID `5000` and copied the supply proof artifact to `/opt/token-aggregation/reports/status/mainnet-cwusdc-supply-proof-20260508.json` so live provenance is file-backed instead of embedded fallback-backed. + +The first production install reported two moderate advisories from `express-rate-limit` -> `ip-address`. The dependency was updated to the patched `express-rate-limit` release, redeployed, and verified in VMID `5000` with `npm audit --omit=dev --audit-level=moderate`: `found 0 vulnerabilities`. + +## GRU / GRU v2 Catalog Generalization + +Follow-up completion: + +- Added `smom-dbis-138/services/token-aggregation/config/supply-proof-catalog.json` as the service-local supply proof catalog. +- Generalized report enrichment from a cWUSDC-only proof loader to a catalog loader keyed by `chainId:tokenAddress`. +- Preserved `cWUSDC` as the only currently proved token in the catalog. +- Added explicit `missing-supply-proof` / `proof_required` provenance and tracker caveats for proofless GRU `c*` and `cW*` assets instead of leaving supply fields silent. +- Updated `deploy-to-vmid.sh` so deploy bundles include `config/`, `public/`, `docs/`, and `frontend/` alongside `dist/`, `src/`, scripts, and package manifests. This keeps token logo references, static pages, docs/page info, and proof catalog content in the VM package. + +Verification after redeploy: + +```bash +cd smom-dbis-138/services/token-aggregation +npm run build +npm test -- --runInBand --testPathPattern=report +npm audit --omit=dev +./deploy-to-vmid.sh 5000 192.168.11.12 +``` + +Result: report suite `20 passed, 20 total`; production audit `found 0 vulnerabilities`. + +Public endpoint count from `https://explorer.d-bis.org/api/v1/report/all`: + +- c*/cW* candidates: `96` +- proved by catalog: `1` +- explicitly proof-gated: `95` +- silent supply-proof state: `0` + +Packaging verification on VMID `5000` confirmed: + +- `/opt/token-aggregation/config/supply-proof-catalog.json` +- `/opt/token-aggregation/public/omnl-dashboard.html` +- `/opt/token-aggregation/docs/REST_API_REFERENCE.md` +- `/opt/token-aggregation/frontend/index.html` + +## Priority Fix Completion Pass + +Completed the review priority list in order: + +1. Reconciled GRU v2 deployment-status pools into tracker-facing token reports. `cWUSDC` now exposes its configured GRU v2 PMM surfaces in `/report/coingecko`, `/report/cmc`, and `/report/all` even when the indexed DB pool repository has no live TVL row. +2. Added lifecycle status and status reasons to GRU v2 PMM pool rows. Live public verification: `135 / 135` GRU v2 pools have non-null `status`. +3. Kept cWUSDC as the only catalog-proved asset and made the remaining c*/cW* assets explicitly `proof_required`. Live public verification: `96` candidates, `1` proved, `95` proof-gated, `0` silent. +4. Replaced generic local SVG placeholders with controlled DBIS-style GRU token SVGs for fiat/commodity logos and packaged them under `public/token-logos/gru/`. +5. Added public report-owned logo serving via extensionless `/api/v1/report/logo/:symbol`, avoiding public edge rules that returned 404 for direct `.svg` asset paths. +6. Added `/api/v1/report/adoption-readiness` with institutional and crypto-listing scoring plus blocker summaries. + +Final live verification: + +```bash +curl "https://explorer.d-bis.org/api/v1/report/logo/cUSDC" +curl "https://explorer.d-bis.org/api/v1/report/token-list?chainId=1" +curl "https://explorer.d-bis.org/api/v1/report/coingecko?chainId=1" +curl "https://explorer.d-bis.org/api/v1/report/gru-v2-pmm-pools?chainId=1" +curl "https://explorer.d-bis.org/api/v1/report/adoption-readiness" +``` + +Current readiness scores from live `/report/adoption-readiness`: + +- Institutional: `52 / 100` +- Crypto listing: `12 / 100` + +Remaining blockers are now explicit rather than hidden: + +- `95` assets still require token-specific supply proof artifacts. +- Not every c*/cW* asset has positive indexed liquidity in the report API. + +## Final Packaging / Public Edge Completion Pass + +Completed a second pass so the live service is no longer cWUSDC-only: + +- Added `scripts/generate-supply-proof-catalog.ts` and `npm run generate:supply-proof-catalog`. +- Generated and packaged `59` supply-proof artifacts in `config/supply-proof-catalog.json`. +- Preserved the richer Ethereum Mainnet `cWUSDC` proof from `reports/status/mainnet-cwusdc-supply-proof-20260508.json` instead of overwriting it with a generic ERC-20 probe. The packaged cWUSDC proof still carries `knownBalances`, tracker surfaces, and `ready_for_tracker_review` methodology. +- Kept unresolved probes visible as `proofFailures` in the catalog generator output. Current unresolved count: `37`, mostly non-contract staged cW gas/monetary placeholders and public RPC/auth failures. +- Redeployed VMID `5000` on `192.168.11.12`, restarted `token-aggregation`, and verified `/opt/token-aggregation/config/supply-proof-catalog.json` is live. +- Verified extensionless logo routes work through the public edge, e.g. `https://explorer.d-bis.org/api/v1/report/logo/cUSDC` returns the packaged DBIS GRU SVG. + +Final verification: + +```bash +cd smom-dbis-138/services/token-aggregation +npm run build +npm test -- --runInBand --testPathPattern=report +npm audit --omit=dev +./deploy-to-vmid.sh 5000 192.168.11.12 +``` + +Result: + +- Build: passed. +- Report API tests: `23 passed, 23 total`. +- Production dependency audit: `found 0 vulnerabilities`. +- VMID `5000` service: `active`. + +Live `/api/v1/report/adoption-readiness` after the catalog deployment: + +- Candidates: `96` +- Proved: `57` +- Proof-required: `39` +- Silent proof state: `0` +- Pool-indexed: `34` +- Positive indexed liquidity: `12` +- GRU v2 pools with lifecycle status: `135 / 135` +- Institutional score: `73 / 100` +- Crypto-listing score: `38 / 100` + +Live Ethereum Mainnet cWUSDC report state: + +- `total_supply`: `10451316981.309788` +- `circulating_supply`: `10451316981.309788` +- `market_data.market_cap`: `10451316981.309788` +- `supply_proof_provenance.source`: `repo-supply-proof-catalog` +- `supply_proof_provenance.schema`: `mainnet-cwusdc-supply-proof/v1` +- `supply_proof_provenance.status`: `ready_for_tracker_review` +- `liquidity_pools` includes the configured cWUSDC/USDC defense PMM plus cWUSDC/USDT, cWUSDT/cWUSDC, and TRUU surfaces from deployment status. + +Remaining blockers: + +- `39` c*/cW* assets still require successful token-specific supply-proof artifacts before supply/market-cap claims should be submitted. +- Only `12` c*/cW* candidates currently show positive indexed liquidity in the report API. +- Several remaining proof failures are expected until staged placeholder addresses become deployed ERC-20 contracts or RPC endpoints are replaced with authenticated/working providers. + +## Blocker Completion Pass + +Completed additional live fixes after the first blocker inventory: + +- Replaced the stale Polygon RPC default/fallback list with working unauthenticated endpoints: + - `https://polygon-bor-rpc.publicnode.com` + - `https://1rpc.io/matic` + - `https://polygon.drpc.org` +- Regenerated `config/supply-proof-catalog.json`. +- Polygon proof coverage improved from zero current proofs to five current proofs: + - `cUSDC` + - `cUSDT` + - `cWAUSDT` + - `cWUSDC` + - `cWUSDT` +- Added `blockerInventory` directly to live `/api/v1/report/adoption-readiness` so downstream consumers can fetch the complete proof/liquidity blocker list instead of relying on a truncated sample. +- Saved the live public readiness payload to `reports/status/token-aggregation-adoption-readiness-live-20260509.json`. + +Validation: + +```bash +cd smom-dbis-138/services/token-aggregation +npm run build +npm test -- --runInBand --testPathPattern=report +npm audit --omit=dev +./deploy-to-vmid.sh 5000 192.168.11.12 +``` + +Result: + +- Build: passed. +- Report API tests: `23 passed, 23 total`. +- Production dependency audit: `found 0 vulnerabilities`. +- VMID `5000` service: `active`. + +Final live `/api/v1/report/adoption-readiness`: + +- Candidates: `96` +- Proved: `62` +- Proof-required: `34` +- Silent proof state: `0` +- Pool-indexed: `34` +- Positive indexed liquidity: `12` +- GRU v2 pools with lifecycle status: `135 / 135` +- Institutional score: `75 / 100` +- Crypto-listing score: `41 / 100` + +Current proof blockers by chain: + +| Chain ID | Count | Symbols | +|---:|---:|---| +| `1` | `2` | `cWBTC`, `cWETH` | +| `10` | `2` | `cWBTC`, `cWETHL2` | +| `25` | `2` | `cWBTC`, `cWCRO` | +| `56` | `2` | `cWBNB`, `cWBTC` | +| `100` | `2` | `cWBTC`, `cWXDAI` | +| `137` | `2` | `cWBTC`, `cWPOL` | +| `138` | `12` | `cAVAX`, `cBNB`, `cBTC`, `cCELO`, `cCRO`, `cETH`, `cETHL2`, `cPOL`, `cUSDC_V2`, `cUSDT_V2`, `cWEMIX`, `cXDAI` | +| `1111` | `2` | `cWBTC`, `cWWEMIX` | +| `8453` | `2` | `cWBTC`, `cWETHL2` | +| `42161` | `2` | `cWBTC`, `cWETHL2` | +| `42220` | `2` | `cWBTC`, `cWCELO` | +| `43114` | `2` | `cWAVAX`, `cWBTC` | + +Current liquidity blockers by chain: + +| Chain ID | Count | Symbols | +|---:|---:|---| +| `1` | `6` | `cUSDC`, `cUSDT`, `cWBTC`, `cWETH`, `cWUSDC`, `cWUSDT` | +| `10` | `6` | `cUSDC`, `cUSDT`, `cWBTC`, `cWETHL2`, `cWUSDC`, `cWUSDT` | +| `25` | `4` | `cUSDC`, `cUSDT`, `cWBTC`, `cWCRO` | +| `56` | `8` | `cUSDC`, `cUSDT`, `cWAUSDT`, `cWBNB`, `cWBTC`, `cWUSDC`, `cWUSDT`, `cWUSDW` | +| `100` | `6` | `cUSDC`, `cUSDT`, `cWBTC`, `cWUSDC`, `cWUSDT`, `cWXDAI` | +| `137` | `7` | `cUSDC`, `cUSDT`, `cWAUSDT`, `cWBTC`, `cWPOL`, `cWUSDC`, `cWUSDT` | +| `138` | `14` | `cAUSDT`, `cAVAX`, `cBNB`, `cBTC`, `cCELO`, `cCRO`, `cETH`, `cETHL2`, `cPOL`, `cUSDC_V2`, `cUSDT_V2`, `cUSDW`, `cWEMIX`, `cXDAI` | +| `1111` | `4` | `cUSDC`, `cUSDT`, `cWBTC`, `cWWEMIX` | +| `8453` | `6` | `cUSDC`, `cUSDT`, `cWBTC`, `cWETHL2`, `cWUSDC`, `cWUSDT` | +| `42161` | `6` | `cUSDC`, `cUSDT`, `cWBTC`, `cWETHL2`, `cWUSDC`, `cWUSDT` | +| `42220` | `7` | `cUSDC`, `cUSDT`, `cWAUSDT`, `cWBTC`, `cWCELO`, `cWUSDC`, `cWUSDT` | +| `43114` | `8` | `cUSDC`, `cUSDT`, `cWAUSDT`, `cWAVAX`, `cWBTC`, `cWUSDC`, `cWUSDT`, `cWUSDW` | +| `651940` | `2` | `cUSDC`, `cUSDT` | + +Remaining blocker classes: + +- Staged/non-contract cW gas or cW monetary placeholders still return `0x` for ERC-20 `totalSupply`; they cannot be converted into supply proofs until real ERC-20 deployments or corrected contract bindings exist. +- Chain 138 v2 stable entries `cUSDC_V2` and `cUSDT_V2` still require proof artifacts or corrected canonical bindings. +- Liquidity reporting remains limited by the pool indexer and live TVL inputs; configured pools are visible, but positive indexed liquidity is still not present for every c*/cW* candidate. + +## V2 Binding And Placeholder Classification Completion + +Completed the next blocker pass: + +- Corrected the live VMID `5000` `.env` overrides for Chain 138 V2 stable bindings: + - `CUSDC_V2_ADDRESS_138=0x219522c60e83dEe01FC5b0329d6fA8fD84b9D13d` + - `CUSDT_V2_ADDRESS_138=0x9FBfab33882Efe0038DAa608185718b772EE5660` +- Updated `smom-dbis-138/services/token-aggregation/.env.example` to match the canonical V2 addresses. +- Restarted the live service and verified `cUSDC_V2` / `cUSDT_V2` are no longer proof blockers. +- Added deterministic placeholder classification to report enrichment: + - Placeholder bindings such as `0xcb7c0000...`, `0xce7e0000...`, and similar gas/monetary roadmap addresses now emit `supplyProofProvenance.source = deterministic-placeholder-address`. + - Placeholder status is `non_reportable_until_erc20_deployed`. + - The API no longer mislabels deterministic placeholder addresses as tracker-submittable missing proof artifacts. +- Updated `/api/v1/report/adoption-readiness` scoring to separate: + - `reportableCandidates` + - `nonReportablePlaceholder` + - `proofRequired` + - `liquidityMissing` + +Validation: + +```bash +cd smom-dbis-138/services/token-aggregation +npm run build +npm test -- --runInBand --testPathPattern=report +npm audit --omit=dev +./deploy-to-vmid.sh 5000 192.168.11.12 +``` + +Result: + +- Build: passed. +- Report API tests: `23 passed, 23 total`. +- Production dependency audit: `found 0 vulnerabilities`. +- VMID `5000` service: `active`. + +Final live readiness after this pass: + +- Candidates: `96` +- Reportable candidates: `64` +- Non-reportable deterministic placeholders: `32` +- Proved reportable assets: `64` +- Proof-required reportable assets: `0` +- Silent proof state: `0` +- Pool-indexed: `34` +- Positive indexed liquidity: `12` +- GRU v2 pools with lifecycle status: `135 / 135` +- Institutional score: `91 / 100` +- Crypto-listing score: `62 / 100` + +Proof blocker status: + +- No reportable c*/cW* asset remains proof-blocked. +- `32` deterministic placeholders remain visible for roadmap traceability only; they are not valid tracker/listing proof surfaces until deployed ERC-20 bindings replace them. + +Only remaining live blocker: + +- `52` reportable assets lack positive indexed liquidity in the token-aggregation API. +- This cannot be honestly fixed by metadata alone. It requires one of: + - real positive-liquidity pools, + - indexer coverage for already-funded pools, + - or a policy decision to remove a token from reportable listing scope until liquidity exists. + +## Liquidity Blocker Split Deployed + +Completed the next API pass and redeployed VMID `5000`. + +`/api/v1/report/adoption-readiness` now splits the remaining liquidity blocker into two operator-actionable classes: + +- `liquidityMissingWithPools`: reportable assets with configured or indexed pools but zero positive indexed TVL. +- `liquidityMissingWithoutPools`: reportable assets with no visible pool binding. + +Live counts: + +- `liquidityMissing`: `52` +- `liquidityMissingWithPools`: `22` +- `liquidityMissingWithoutPools`: `30` + +This confirms the supply-proof work is closed for reportable assets. The remaining crypto-listing gap is now liquidity execution and pool binding, not report metadata. + +Detailed component inventory: + +- `reports/status/token-aggregation-engine-x-open-components-20260509.md` + +## Live UniV2 Indexing Pass + +Completed the next execution focus that could be closed safely from repo artifacts and live reserve reads: + +- Refreshed live UniV2 pair discovery. +- Generated and packaged `config/live-uniswap-v2-pool-catalog.json`. +- Wired the catalog into report API pool enrichment. +- Redeployed VMID `5000` and restarted `token-aggregation`. +- Saved the refreshed public readiness payload to `reports/status/token-aggregation-adoption-readiness-live-20260509.json`. + +Public readiness moved from: + +- `poolIndexed`: `34` -> `50` +- `liquidityPositive`: `12` -> `35` +- `liquidityMissing`: `52` -> `29` +- `liquidityMissingWithPools`: `22` -> `15` +- `liquidityMissingWithoutPools`: `30` -> `14` +- crypto-listing score: `62 / 100` -> `80 / 100` + +The remaining `29` rows are now covered by the read-only funding plan: + +- `reports/status/token-aggregation-liquidity-gap-funding-plan-latest.md` + +The funding plan now also includes non-EVM funding requirements for Solana, Tron, XRPL, and other major non-EVM expansions. Those rows are explicitly marked `TBD` until canonical non-EVM custody wallets, mint/asset IDs, trustlines/program IDs, and minimum native gas/rent/reserve targets are bound. diff --git a/reports/status/token-aggregation-engine-x-open-components-20260509.md b/reports/status/token-aggregation-engine-x-open-components-20260509.md new file mode 100644 index 00000000..d69dc4d6 --- /dev/null +++ b/reports/status/token-aggregation-engine-x-open-components-20260509.md @@ -0,0 +1,238 @@ +# Token-Aggregation And Engine X Open Components - 2026-05-09 + +Status: repo-side report API fixes are deployed; remaining work is liquidity, pool binding, external proof, and policy/ownership closure. + +## Live API State + +Public endpoint checked: + +```bash +curl -fsS "https://explorer.d-bis.org/api/v1/report/adoption-readiness" +``` + +Snapshot saved: + +- `reports/status/token-aggregation-adoption-readiness-live-20260509.json` + +Current live counts: + +| Metric | Count | +|---|---:| +| Candidate c*/cW* assets | `96` | +| Reportable candidates | `64` | +| Deterministic placeholders, non-reportable until ERC-20 deployment | `32` | +| Proved reportable assets | `64` | +| Proof-required reportable assets | `0` | +| Silent proof state | `0` | +| Pool-indexed assets | `34` | +| Assets with positive indexed liquidity | `12` | +| Reportable assets missing positive indexed liquidity | `52` | +| Missing liquidity with configured/indexed zero-TVL pools | `22` | +| Missing liquidity with no visible pool binding | `30` | +| GRU v2 pools with lifecycle status | `135 / 135` | + +Readiness scores: + +| Surface | Score | Blocker | +|---|---:|---| +| Institutional report API | `91 / 100` | None from the live adoption-readiness endpoint | +| Crypto listing / tracker readiness | `62 / 100` | Not every reportable c*/cW* asset has positive indexed liquidity | + +## What Is Complete + +- `/api/v1/report/coingecko`, `/api/v1/report/cmc`, and `/api/v1/report/all` expose enriched supply and market-cap metadata for reportable c*/cW* assets. +- `cWUSDC` and the broader reportable GRU / GRU v2 c*/cW* set now emit: + - `totalSupply` + - `circulatingSupply` + - `marketCapUsd` + - `supplyProofProvenance` + - tracker caveats + - professional DBIS logo URIs where packaged assets exist +- Chain 138 V2 stable bindings were corrected in live VMID `5000` config: + - `CUSDC_V2_ADDRESS_138=0x219522c60e83dEe01FC5b0329d6fA8fD84b9D13d` + - `CUSDT_V2_ADDRESS_138=0x9FBfab33882Efe0038DAa608185718b772EE5660` +- Deterministic placeholder addresses are no longer treated as tracker-ready proof blockers. They are classified as `non_reportable_until_erc20_deployed`. +- `/api/v1/report/adoption-readiness` now separates: + - reportable candidates + - deterministic placeholders + - supply-proof blockers + - liquidity blockers with configured/indexed zero-TVL pools + - liquidity blockers with no visible pool binding +- The live service was redeployed to VMID `5000` and restarted; `systemctl is-active token-aggregation` returned `active`. + +## Remaining Components + +### 1. Existing Pools Need Positive Indexed Liquidity + +These `22` reportable assets have configured or indexed pools, but the report API sees zero positive TVL. The next fix is not metadata; it is funding, pool indexer ingestion, or pool status correction. + +| Chain ID | Count | Symbols | +|---:|---:|---| +| `1` | `4` | `cUSDC`, `cUSDT`, `cWUSDC`, `cWUSDT` | +| `10` | `4` | `cUSDC`, `cUSDT`, `cWUSDC`, `cWUSDT` | +| `25` | `2` | `cUSDC`, `cUSDT` | +| `56` | `1` | `cUSDT` | +| `100` | `1` | `cUSDC` | +| `137` | `4` | `cUSDC`, `cUSDT`, `cWUSDC`, `cWUSDT` | +| `1111` | `1` | `cUSDC` | +| `8453` | `1` | `cUSDC` | +| `42161` | `1` | `cUSDC` | +| `42220` | `1` | `cUSDC` | +| `43114` | `1` | `cUSDC` | +| `651940` | `1` | `cUSDC` | + +Recommended closure: + +1. For each row, confirm the configured pool address exists and is the intended venue. +2. If the pool is funded on-chain but API TVL is zero, fix pool-indexer reads or chain RPC coverage. +3. If the pool is unfunded, fund a minimal canonical pool or mark the asset out of reportable listing scope until funded. + +### 2. Missing Pool Bindings + +These `30` reportable assets have no visible pool binding in the live report. The next fix is pool creation, pool discovery, or config binding. + +| Chain ID | Count | Symbols | +|---:|---:|---| +| `56` | `5` | `cUSDC`, `cWAUSDT`, `cWUSDC`, `cWUSDT`, `cWUSDW` | +| `100` | `3` | `cUSDT`, `cWUSDC`, `cWUSDT` | +| `137` | `1` | `cWAUSDT` | +| `138` | `4` | `cAUSDT`, `cUSDC_V2`, `cUSDT_V2`, `cUSDW` | +| `1111` | `1` | `cUSDT` | +| `8453` | `3` | `cUSDT`, `cWUSDC`, `cWUSDT` | +| `42161` | `3` | `cUSDT`, `cWUSDC`, `cWUSDT` | +| `42220` | `4` | `cUSDT`, `cWAUSDT`, `cWUSDC`, `cWUSDT` | +| `43114` | `5` | `cUSDT`, `cWAUSDT`, `cWUSDC`, `cWUSDT`, `cWUSDW` | +| `651940` | `1` | `cUSDT` | + +Recommended closure: + +1. Run or refresh live pair discovery for each affected chain. +2. Add discovered pool bindings to the GRU v2 deployment pool config. +3. For chains without deployable pool evidence, create the minimum canonical pool before marking the asset reportable to trackers. + +### 3. Deterministic Placeholders Are Not Tracker Assets + +The `32` deterministic placeholder entries remain intentionally visible for roadmap and catalog traceability. They should not be submitted to CoinGecko, CMC, Etherscan, or exchange listing workflows as deployed ERC-20 assets. + +Required closure: + +- deploy real ERC-20 contracts for those symbols, or +- replace the placeholder bindings with already deployed canonical token addresses, then regenerate the supply proof catalog. + +### 4. Engine X Public Proof Boundary + +Engine X internal accounting proofs are separate from public DEX peg evidence. + +Current repo evidence supports: + +- internal Engine X accounting proof records, +- recipient proof hashes, +- ISO-style evidence envelopes, +- token-aggregation supply, market-cap, and caveat reporting. + +It does not, by itself, prove: + +- public `1 cWUSDC : 1 USDC` depth across large sizes, +- regulatory approval, +- external audit opinion, +- AML/KYC clearance, +- exchange acceptance, +- public DEX volume from internal accounting loops. + +Required closure: + +1. Use real, indexable `cWUSDC/USDC` pools for public peg proofs. +2. Use actual official USDC in the swap path. +3. Keep internal Engine X proofs and public DEX proofs as separate evidence classes. +4. Avoid claiming market-price or regulatory conclusions from virtual accounting activity. + +### 5. HYBX / Settlement Ownership Decisions + +The HYBX sidecar boundary still needs explicit owner decisions before it can be treated as fully production-owned: + +1. Canonical settlement orchestration owner. +2. Final posting responsibility versus suggested-entry generation. +3. Canonical event consumed by FireFly and on-chain settlement. +4. System-of-record versus adapter versus evidence-generator classification. + +These are policy and architecture decisions, not code-only fixes. + +## Next Best Execution Order + +1. Keep the current report API deployment live; do not roll back the supply-proof and adoption-readiness split. +2. Work the `22` zero-TVL configured/indexed pools first because their missing component is narrower: funding/indexing/status. +3. Work the `30` no-binding rows second by pair discovery, pool creation, or scope removal. +4. Replace deterministic placeholders only when real ERC-20 deployments or canonical bindings are available. +5. For Engine X cWUSDC public proof, route only through actual indexable `cWUSDC/USDC` pools with official USDC and protected execution where available. +6. Treat regulatory, exchange, and audit claims as external approvals until signed third-party evidence exists. + +## Verification Commands + +```bash +cd smom-dbis-138/services/token-aggregation +npm run build +npm test -- --runInBand --testPathPattern=report +npm audit --omit=dev +``` + +Live checks: + +```bash +curl -fsS "https://explorer.d-bis.org/api/v1/report/adoption-readiness" | jq . +curl -fsS "https://explorer.d-bis.org/api/v1/report/all?chainId=1" | jq . +``` + +## Execution Pass - Live UniV2 Indexing And Funding Preflight + +Completed after the first open-components inventory: + +- Refreshed live Uniswap V2 pair discovery and recorded `23` configured UniV2 pool bindings in `cross-chain-pmm-lps/config/deployment-status.json`. +- Generated `smom-dbis-138/services/token-aggregation/config/live-uniswap-v2-pool-catalog.json` from the live pair-discovery artifact. +- Wired the live UniV2 catalog into token-aggregation report responses as `source = live-uniswap-v2-pair-discovery`. +- Redeployed VMID `5000` and restarted `token-aggregation`. +- Generated read-only deployer funding preflight: + - `reports/status/token-aggregation-liquidity-gap-funding-plan-latest.json` + - `reports/status/token-aggregation-liquidity-gap-funding-plan-latest.md` + +Live readiness after deployment: + +| Metric | Before | After | +|---|---:|---:| +| Pool-indexed reportable assets | `34` | `50` | +| Positive-liquidity reportable assets | `12` | `35` | +| Reportable assets missing positive liquidity | `52` | `29` | +| Missing liquidity with configured/indexed zero-TVL pools | `22` | `15` | +| Missing liquidity with no visible pool binding | `30` | `14` | +| Crypto-listing score | `62 / 100` | `80 / 100` | + +Live UniV2 catalog: + +| Metric | Count | +|---|---:| +| Live UniV2 pools with positive reserves | `19` | +| Healthy by existing depth/parity rule | `10` | +| Live but marked repair-needed | `9` | + +Funding preflight from the deployer wallet `0x4A666F96fC8764181194447A7dFdb7d471b301C8`: + +| Status | Count | +|---|---:| +| `fundable_token_balance_present` | `9` | +| `pool_binding_gated` | `7` | +| `token_balance_gated` | `6` | +| `pool_binding_and_token_balance_gated` | `7` | + +Non-EVM funding requirements were added to the same funding scope: + +| Network | Native gas | Current amount | Required amount | Status | +|---|---|---|---|---| +| Solana | `SOL` | not checked by EVM planner | `TBD` | canonical wallet, SPL mint inventory, rent/gas targets required | +| Tron | `TRX` | not checked by EVM planner | `TBD` | native Tron wallet, energy/bandwidth, TRC-20 inventory requirements needed | +| XRPL | `XRP` | not checked by EVM planner | `TBD` | XRPL account reserve, trustline/issuer, wXRP bridge inventory requirements needed | +| Other non-EVM majors | varies | not supported by current planner | `TBD` | per-network adapter, wallet, asset ID, and venue requirements needed | + +No value-moving transaction was broadcast in this pass. The remaining rows require one of: + +- enough token inventory on the target chain, +- pool creation/binding first, +- or a policy decision to remove that asset from reportable listing scope until liquidity exists. diff --git a/scripts/archive/consolidated/fix/fix-blockscout-verification.sh b/scripts/archive/consolidated/fix/fix-blockscout-verification.sh index 2ac288d1..0cc47880 100755 --- a/scripts/archive/consolidated/fix/fix-blockscout-verification.sh +++ b/scripts/archive/consolidated/fix/fix-blockscout-verification.sh @@ -82,7 +82,7 @@ if [ "$WETH9_VERIFIED" != "verified" ]; then log_success "✓ CCIPWETH9Bridge verification submitted!" else log_warn "⚠️ Automated verification failed - manual verification required" - log_info "Manual verification URL: https://explorer.d-bis.org/address/$WETH9_BRIDGE_138#verify" + log_info "Manual verification URL: https://explorer.d-bis.org/addresses/$WETH9_BRIDGE_138#verify" log_info "See docs/BLOCKSCOUT_BRIDGE_ADDRESSES_UPDATE.md for instructions" fi fi @@ -109,7 +109,7 @@ if [ "$WETH10_VERIFIED" != "verified" ]; then log_success "✓ CCIPWETH10Bridge verification submitted!" else log_warn "⚠️ Automated verification failed - manual verification required" - log_info "Manual verification URL: https://explorer.d-bis.org/address/$WETH10_BRIDGE_138#verify" + log_info "Manual verification URL: https://explorer.d-bis.org/addresses/$WETH10_BRIDGE_138#verify" log_info "See docs/BLOCKSCOUT_BRIDGE_ADDRESSES_UPDATE.md for instructions" fi fi @@ -120,7 +120,7 @@ log_success "Verification Attempt Complete" log_success "=========================================" log_info "" log_info "Blockscout Links:" -log_info " CCIPWETH9Bridge: https://explorer.d-bis.org/address/$WETH9_BRIDGE_138" -log_info " CCIPWETH10Bridge: https://explorer.d-bis.org/address/$WETH10_BRIDGE_138" +log_info " CCIPWETH9Bridge: https://explorer.d-bis.org/addresses/$WETH9_BRIDGE_138" +log_info " CCIPWETH10Bridge: https://explorer.d-bis.org/addresses/$WETH10_BRIDGE_138" log_info "" diff --git a/scripts/archive/consolidated/fix/fix-blockscout-web-interface-complete.sh b/scripts/archive/consolidated/fix/fix-blockscout-web-interface-complete.sh index 15a7c330..72a7f46f 100755 --- a/scripts/archive/consolidated/fix/fix-blockscout-web-interface-complete.sh +++ b/scripts/archive/consolidated/fix/fix-blockscout-web-interface-complete.sh @@ -147,7 +147,7 @@ echo "" echo "✅ If you have transaction hashes or addresses:" echo " https://explorer.d-bis.org/tx/" -echo " https://explorer.d-bis.org/address/
" +echo " https://explorer.d-bis.org/addresses/
" echo "" log_info "Note: Some Blockscout versions serve the web interface" diff --git a/scripts/archive/consolidated/fix/fix-blockscout-web-interface.sh b/scripts/archive/consolidated/fix/fix-blockscout-web-interface.sh index 6ce76170..d7c4954d 100755 --- a/scripts/archive/consolidated/fix/fix-blockscout-web-interface.sh +++ b/scripts/archive/consolidated/fix/fix-blockscout-web-interface.sh @@ -150,7 +150,7 @@ echo "⚠️ WEB INTERFACE ROUTES (May return 404 until more data):" echo " - https://explorer.d-bis.org/ (root)" echo " - https://explorer.d-bis.org/blocks" echo " - https://explorer.d-bis.org/transactions" -echo " - https://explorer.d-bis.org/address/
" +echo " - https://explorer.d-bis.org/addresses/
" echo "" log_info "The web interface may need more indexed data or time to fully initialize" echo "" diff --git a/scripts/archive/consolidated/verify/verify-all-contracts.sh b/scripts/archive/consolidated/verify/verify-all-contracts.sh index 141f1d65..a32a7610 100755 --- a/scripts/archive/consolidated/verify/verify-all-contracts.sh +++ b/scripts/archive/consolidated/verify/verify-all-contracts.sh @@ -213,6 +213,6 @@ done echo "" log_info "View contracts on Blockscout:" -log_info " https://explorer.d-bis.org/address/" +log_info " https://explorer.d-bis.org/addresses/" echo "" diff --git a/scripts/archive/consolidated/verify/verify-chain138-bridges-blockscout.sh b/scripts/archive/consolidated/verify/verify-chain138-bridges-blockscout.sh index 8937e4b9..bd498676 100755 --- a/scripts/archive/consolidated/verify/verify-chain138-bridges-blockscout.sh +++ b/scripts/archive/consolidated/verify/verify-chain138-bridges-blockscout.sh @@ -65,7 +65,7 @@ if forge verify-contract \ --via-ir \ 2>&1 | tee /tmp/weth9-bridge-138-verify.log; then log_success "✓ CCIPWETH9Bridge verification submitted!" - log_info "View on Blockscout: https://explorer.d-bis.org/address/$WETH9_BRIDGE_138" + log_info "View on Blockscout: https://explorer.d-bis.org/addresses/$WETH9_BRIDGE_138" else log_warn "⚠️ CCIPWETH9Bridge verification failed or already verified" log_info "Check /tmp/weth9-bridge-138-verify.log for details" @@ -90,7 +90,7 @@ if forge verify-contract \ --via-ir \ 2>&1 | tee /tmp/weth10-bridge-138-verify.log; then log_success "✓ CCIPWETH10Bridge verification submitted!" - log_info "View on Blockscout: https://explorer.d-bis.org/address/$WETH10_BRIDGE_138" + log_info "View on Blockscout: https://explorer.d-bis.org/addresses/$WETH10_BRIDGE_138" else log_warn "⚠️ CCIPWETH10Bridge verification failed or already verified" log_info "Check /tmp/weth10-bridge-138-verify.log for details" @@ -102,7 +102,7 @@ log_success "Verification Complete!" log_success "=========================================" log_info "" log_info "Blockscout Links:" -log_info " CCIPWETH9Bridge: https://explorer.d-bis.org/address/$WETH9_BRIDGE_138" -log_info " CCIPWETH10Bridge: https://explorer.d-bis.org/address/$WETH10_BRIDGE_138" +log_info " CCIPWETH9Bridge: https://explorer.d-bis.org/addresses/$WETH9_BRIDGE_138" +log_info " CCIPWETH10Bridge: https://explorer.d-bis.org/addresses/$WETH10_BRIDGE_138" log_info "" diff --git a/scripts/deployment/continue-cwusdc-ei-matrix-wallets.sh b/scripts/deployment/continue-cwusdc-ei-matrix-wallets.sh new file mode 100755 index 00000000..4a607d6e --- /dev/null +++ b/scripts/deployment/continue-cwusdc-ei-matrix-wallets.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# Resume mainnet cWUSDC EI matrix transfers from ei-matrix-cwusdc-send-last-idx.txt + 1. +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +exec "$SCRIPT_DIR/send-cwusdc-ei-matrix-wallets.sh" --resume-next "$@" diff --git a/scripts/deployment/continue-mint-cwusdc-ei-matrix-wallets.sh b/scripts/deployment/continue-mint-cwusdc-ei-matrix-wallets.sh old mode 100644 new mode 100755 diff --git a/scripts/deployment/deploy-configure-cw-multitoken-l2-receivers.sh b/scripts/deployment/deploy-configure-cw-multitoken-l2-receivers.sh new file mode 100755 index 00000000..4b125402 --- /dev/null +++ b/scripts/deployment/deploy-configure-cw-multitoken-l2-receivers.sh @@ -0,0 +1,391 @@ +#!/usr/bin/env bash +# Deploy/configure CWMultiTokenBridgeL2 receivers for active public cW chains. +# Defaults to dry-run. Use --apply to broadcast. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +SMOM_ROOT="$PROJECT_ROOT/smom-dbis-138" +ENV_FILE="$SMOM_ROOT/.env" +REPORT_DIR="$PROJECT_ROOT/reports/status" +APPLY=false +FULL_FAMILY=false +CHAIN_FILTER="" +TS="$(date -u +%Y%m%dT%H%M%SZ)" +MANIFEST="$REPORT_DIR/cw-multitoken-l2-remediation-${TS}.jsonl" + +usage() { + cat <] + +Without --apply this prints actions only. With --apply it broadcasts deployments +and configuration transactions, then updates smom-dbis-138/.env CW_BRIDGE_*. +USAGE +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --apply) APPLY=true ;; + --full-family) FULL_FAMILY=true ;; + --chain) CHAIN_FILTER="${2:-}"; shift ;; + --chain=*) CHAIN_FILTER="${1#*=}" ;; + -h|--help) usage; exit 0 ;; + *) echo "Unknown arg: $1" >&2; usage >&2; exit 1 ;; + esac + shift +done + +# shellcheck disable=SC1091 +source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" >/dev/null 2>&1 || true + +[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY is required" >&2; exit 1; } +command -v cast >/dev/null 2>&1 || { echo "cast is required" >&2; exit 1; } +command -v forge >/dev/null 2>&1 || { echo "forge is required" >&2; exit 1; } +command -v jq >/dev/null 2>&1 || { echo "jq is required" >&2; exit 1; } + +mkdir -p "$REPORT_DIR" +: > "$MANIFEST" + +DEPLOYER="${DEPLOYER_ADDRESS:-$(cast wallet address "$PRIVATE_KEY")}" +L1_BRIDGE="${CW_L1_BRIDGE_CHAIN138:-}" +RPC_138="${RPC_URL_138:-${CHAIN138_RPC:-${CHAIN138_RPC_URL:-}}}" +[[ -n "$L1_BRIDGE" && "$L1_BRIDGE" != "0x0000000000000000000000000000000000000000" ]] || { echo "CW_L1_BRIDGE_CHAIN138 is required" >&2; exit 1; } +[[ -n "$RPC_138" ]] || { echo "RPC_URL_138/CHAIN138_RPC is required" >&2; exit 1; } + +MINTER_ROLE="$(cast keccak "MINTER_ROLE")" +BURNER_ROLE="$(cast keccak "BURNER_ROLE")" + +declare -A CHAIN_NAME=( + [10]="Optimism" + [25]="Cronos" + [56]="BSC" + [100]="Gnosis" + [137]="Polygon" + [8453]="Base" + [42161]="Arbitrum" + [42220]="Celo" +) +declare -A CHAIN_SUFFIX=( + [10]="OPTIMISM" + [25]="CRONOS" + [56]="BSC" + [100]="GNOSIS" + [137]="POLYGON" + [8453]="BASE" + [42161]="ARBITRUM" + [42220]="CELO" +) +declare -A CHAIN_SELECTOR=( + [10]="3734403246176062136" + [25]="1456215246176062136" + [56]="11344663589394136015" + [100]="465200170687744372" + [137]="4051577828743386545" + [8453]="15971525489660198786" + [42161]="4949039107694359620" + [42220]="1346049177634351622" +) + +set_env_value() { + local file="$1" key="$2" value="$3" + if grep -qE "^${key}=" "$file"; then + sed -i "s|^${key}=.*|${key}=${value}|" "$file" + else + printf '\n%s=%s\n' "$key" "$value" >> "$file" + fi +} + +var_value() { + local key="$1" + printf '%s' "${!key:-}" +} + +first_env() { + local key value + for key in "$@"; do + value="$(var_value "$key")" + if [[ -n "$value" ]]; then + printf '%s\n' "$value" + return 0 + fi + done + return 1 +} + +rpc_for_chain() { + local suffix="$1" + case "$suffix" in + OPTIMISM) first_env OPTIMISM_RPC_URL OPTIMISM_MAINNET_RPC ;; + CRONOS) first_env CRONOS_RPC_URL CRONOS_RPC CRONOS_MAINNET_RPC ;; + BSC) first_env BSC_RPC_URL BSC_MAINNET_RPC ;; + GNOSIS) first_env GNOSIS_RPC_URL GNOSIS_MAINNET_RPC GNOSIS_RPC ;; + POLYGON) first_env POLYGON_RPC_URL POLYGON_MAINNET_RPC ;; + BASE) first_env BASE_RPC_URL BASE_MAINNET_RPC ;; + ARBITRUM) first_env ARBITRUM_RPC_URL ARBITRUM_MAINNET_RPC ;; + CELO) first_env CELO_RPC_URL CELO_MAINNET_RPC CELO_RPC ;; + esac +} + +old_bridge_for_suffix() { + local suffix="$1" key="CW_BRIDGE_${suffix}" + var_value "$key" +} + +old_bridge_call() { + local bridge="$1" rpc="$2" sig="$3" + [[ -n "$bridge" && -n "$rpc" ]] || return 1 + cast call "$bridge" "$sig" --rpc-url "$rpc" 2>/dev/null | awk 'NF{print $1; exit}' +} + +send_router_for_chain() { + local suffix="$1" old_bridge="$2" rpc="$3" value + value="$(first_env "CCIP_${suffix}_ROUTER" "CCIP_ROUTER_${suffix}" 2>/dev/null || true)" + if [[ -n "$value" ]]; then printf '%s\n' "$value"; return; fi + old_bridge_call "$old_bridge" "$rpc" "ccipRouter()(address)" +} + +receive_router_for_chain() { + local suffix="$1" send_router="$2" value + value="$(first_env "CCIP_RELAY_ROUTER_${suffix}_CW" "CCIP_RELAY_ROUTER_${suffix}" 2>/dev/null || true)" + if [[ -n "$value" ]]; then printf '%s\n' "$value"; return; fi + printf '%s\n' "$send_router" +} + +fee_token_for_chain() { + local suffix="$1" old_bridge="$2" rpc="$3" value + value="$(first_env "CCIP_${suffix}_LINK_TOKEN" "LINK_TOKEN_${suffix}" "LINK_${suffix}" 2>/dev/null || true)" + if [[ -n "$value" ]]; then printf '%s\n' "$value"; return; fi + old_bridge_call "$old_bridge" "$rpc" "feeToken()(address)" || printf '0x0000000000000000000000000000000000000000\n' +} + +json_log() { + python3 - "$MANIFEST" "$@" <<'PY' +import json, sys +path = sys.argv[1] +items = dict(arg.split("=", 1) for arg in sys.argv[2:]) +with open(path, "a", encoding="utf-8") as fh: + fh.write(json.dumps(items, sort_keys=True) + "\n") +PY +} + +token_rows_for_chain() { + local chain_id="$1" + jq -r --argjson cid "$chain_id" --argjson full "$($FULL_FAMILY && echo true || echo false)" ' + .pairs[] + | select(.fromChainId == 138 and .toChainId == $cid) + | .tokens[] + | select((.key == "Compliant_USDT_cW") or (.key == "Compliant_USDC_cW") or ($full and (.key | endswith("_cW")))) + | select(.addressFrom and .addressTo and .addressTo != "0x0000000000000000000000000000000000000000") + | [.key, .addressFrom, .addressTo] | @tsv + ' "$PROJECT_ROOT/config/token-mapping-multichain.json" +} + +send_tx() { + local rpc="$1"; shift + local base_cmd=(cast send --rpc-url "$rpc" --private-key "$PRIVATE_KEY" --gas-limit 500000 "$@") + if ! $APPLY; then + printf '[dry-run] ' + printf '%q ' "${base_cmd[@]/$PRIVATE_KEY/\$PRIVATE_KEY}" + printf '\n' + return 0 + fi + local attempt mode out rc + for mode in legacy eip1559; do + local cmd=("${base_cmd[@]}") + if [[ "$mode" == "legacy" ]]; then + cmd=(cast send --rpc-url "$rpc" --private-key "$PRIVATE_KEY" --legacy --gas-limit 500000 "$@") + fi + for attempt in 1 2 3; do + set +e + out="$("${cmd[@]}" 2>&1)" + rc=$? + set -e + printf '%s\n' "$out" + if [[ "$rc" -eq 0 ]]; then + sleep 3 + return 0 + fi + if [[ "$mode" == "legacy" && ( "$out" == *"Invalid params"* || "$out" == *"legacy"* || "$out" == *"transaction type"* ) ]]; then + echo " legacy tx rejected; retrying with EIP-1559 tx params" + break + fi + if [[ "$out" == *"max fee per gas less than block base fee"* ]]; then + local gas_price + gas_price="$(cast gas-price --rpc-url "$rpc" 2>/dev/null || echo 25000000)" + gas_price="$((gas_price * 2))" + echo " gas price below base fee; retrying with --gas-price $gas_price" + cmd=(cast send --rpc-url "$rpc" --private-key "$PRIVATE_KEY" --gas-limit 500000 --gas-price "$gas_price" "$@") + sleep $((attempt * 3)) + continue + fi + if [[ "$out" == *"replacement transaction underpriced"* || "$out" == *"nonce too low"* || "$out" == *"invalid nonce"* || "$out" == *"invalid sequence"* || "$out" == *"already known"* ]]; then + echo " retryable tx error (attempt $attempt); waiting before retry" + sleep $((attempt * 10)) + continue + fi + return "$rc" + done + done + return "$rc" +} + +forge_create_l2() { + local rpc="$1" chain_id="$2" send_router="$3" receive_router="$4" fee_token="$5" + local mode out rc + for mode in legacy eip1559; do + set +e + if [[ "$mode" == "legacy" ]]; then + out="$( + cd "$SMOM_ROOT" && + forge create contracts/bridge/CWMultiTokenBridgeL2.sol:CWMultiTokenBridgeL2 \ + --rpc-url "$rpc" --chain-id "$chain_id" --broadcast --private-key "$PRIVATE_KEY" --legacy \ + --constructor-args "$send_router" "$receive_router" "$fee_token" 2>&1 + )" + else + out="$( + cd "$SMOM_ROOT" && + forge create contracts/bridge/CWMultiTokenBridgeL2.sol:CWMultiTokenBridgeL2 \ + --rpc-url "$rpc" --chain-id "$chain_id" --broadcast --private-key "$PRIVATE_KEY" \ + --constructor-args "$send_router" "$receive_router" "$fee_token" 2>&1 + )" + fi + rc=$? + set -e + printf '%s\n' "$out" + if [[ "$rc" -eq 0 ]]; then + return 0 + fi + if [[ "$mode" == "legacy" && ( "$out" == *"Invalid params"* || "$out" == *"legacy"* || "$out" == *"transaction type"* ) ]]; then + echo " legacy deploy rejected; retrying with EIP-1559 tx params" + continue + fi + return "$rc" + done + return "$rc" +} + +ensure_token_pair() { + local rpc="$1" bridge="$2" key="$3" canonical="$4" mirrored="$5" + local current + current="$(cast call "$bridge" "canonicalToMirrored(address)(address)" "$canonical" --rpc-url "$rpc" 2>/dev/null || true)" + if [[ "${current,,}" == "${mirrored,,}" ]]; then + echo " pair ok $key" + return + fi + echo " configure pair $key" + send_tx "$rpc" "$bridge" "configureTokenPair(address,address)" "$canonical" "$mirrored" +} + +ensure_l2_destination() { + local rpc="$1" bridge="$2" + local current + current="$(cast call "$bridge" "destinations(uint64)((address,bool))" 138 --rpc-url "$rpc" 2>/dev/null || true)" + if [[ "${current,,}" == *"${L1_BRIDGE,,}"* && "${current,,}" == *"true"* ]]; then + echo " L2 destination 138 ok" + return + fi + echo " configure L2 destination 138" + send_tx "$rpc" "$bridge" "configureDestination(uint64,address,bool)" 138 "$L1_BRIDGE" true +} + +ensure_l1_destination() { + local selector="$1" key="$2" canonical="$3" bridge="$4" + local current + current="$(cast call "$L1_BRIDGE" "destinations(address,uint64)((address,bool))" "$canonical" "$selector" --rpc-url "$RPC_138" 2>/dev/null || true)" + if [[ "${current,,}" == *"${bridge,,}"* && "${current,,}" == *"true"* ]]; then + echo " L1 destination ok $key" + return + fi + echo " configure L1 destination $key selector=$selector" + send_tx "$RPC_138" "$L1_BRIDGE" "configureDestination(address,uint64,address,bool)" "$canonical" "$selector" "$bridge" true +} + +ensure_role() { + local rpc="$1" token="$2" role_name="$3" role="$4" holder="$5" + local current + current="$(cast call "$token" "hasRole(bytes32,address)(bool)" "$role" "$holder" --rpc-url "$rpc" 2>/dev/null || echo false)" + if [[ "$current" == "true" ]]; then + echo " role ok $role_name $token" + return + fi + echo " grant $role_name on $token" + send_tx "$rpc" "$token" "grantRole(bytes32,address)" "$role" "$holder" +} + +deploy_l2() { + local chain_id="$1" suffix="$2" rpc="$3" send_router="$4" receive_router="$5" fee_token="$6" + echo " deploying CWMultiTokenBridgeL2 send=$send_router receive=$receive_router fee=$fee_token" + if ! $APPLY; then + echo " [dry-run] forge script DeployCWMultiTokenBridgeL2" + printf '0x000000000000000000000000000000000000%04x\n' "$chain_id" + return + fi + local out addr + out="$(forge_create_l2 "$rpc" "$chain_id" "$send_router" "$receive_router" "$fee_token")" + printf '%s\n' "$out" + addr="$(printf '%s\n' "$out" | sed -nE 's/.*Deployed to:[[:space:]]*(0x[a-fA-F0-9]{40}).*/\1/p; s/.*CWMultiTokenBridgeL2:[[:space:]]*(0x[a-fA-F0-9]{40}).*/\1/p' | tail -1)" + [[ -n "$addr" ]] || { echo "Could not parse deployed bridge address for $suffix" >&2; return 1; } + printf '%s\n' "$addr" +} + +is_cw_multitoken_l2() { + local rpc="$1" bridge="$2" + [[ -n "$bridge" ]] || return 1 + cast call "$bridge" "sendRouter()(address)" --rpc-url "$rpc" >/dev/null 2>&1 && + cast call "$bridge" "receiveRouter()(address)" --rpc-url "$rpc" >/dev/null 2>&1 && + cast call "$bridge" "canonicalToMirrored(address)(address)" "0x93E66202A11B1772E55407B32B44e5Cd8eda7f22" --rpc-url "$rpc" >/dev/null 2>&1 +} + +echo "CWMultiToken L2 remediation" +echo "Apply: $APPLY" +echo "Full family: $FULL_FAMILY" +echo "Deployer: $DEPLOYER" +echo "Manifest: $MANIFEST" +echo + +for chain_id in 10 25 56 100 137 8453 42161 42220; do + [[ -n "$CHAIN_FILTER" && "$CHAIN_FILTER" != "$chain_id" ]] && continue + suffix="${CHAIN_SUFFIX[$chain_id]}" + rpc="$(rpc_for_chain "$suffix" || true)" + old_bridge="$(old_bridge_for_suffix "$suffix")" + selector="${CHAIN_SELECTOR[$chain_id]}" + echo "=== $chain_id ${CHAIN_NAME[$chain_id]} ($suffix) ===" + if [[ -z "$rpc" ]]; then + echo " skip: missing RPC" + json_log chainId="$chain_id" suffix="$suffix" status="skipped" reason="missing_rpc" + continue + fi + send_router="$(send_router_for_chain "$suffix" "$old_bridge" "$rpc" || true)" + receive_router="$(receive_router_for_chain "$suffix" "$send_router")" + fee_token="$(fee_token_for_chain "$suffix" "$old_bridge" "$rpc")" + if [[ -z "$send_router" || -z "$receive_router" || -z "$fee_token" ]]; then + echo " skip: missing router or fee token" + json_log chainId="$chain_id" suffix="$suffix" status="skipped" reason="missing_router_or_fee" + continue + fi + + current_bridge="$(old_bridge_for_suffix "$suffix")" + if is_cw_multitoken_l2 "$rpc" "$current_bridge"; then + bridge="$current_bridge" + echo " using existing CWMultiTokenBridgeL2: $bridge" + else + bridge="$(deploy_l2 "$chain_id" "$suffix" "$rpc" "$send_router" "$receive_router" "$fee_token" | tail -1)" + echo " new bridge: $bridge" + fi + if $APPLY; then + set_env_value "$ENV_FILE" "CW_BRIDGE_${suffix}" "$bridge" + fi + + ensure_l2_destination "$rpc" "$bridge" + while IFS=$'\t' read -r key canonical mirrored; do + ensure_token_pair "$rpc" "$bridge" "$key" "$canonical" "$mirrored" + ensure_l1_destination "$selector" "$key" "$canonical" "$bridge" + ensure_role "$rpc" "$mirrored" "MINTER_ROLE" "$MINTER_ROLE" "$bridge" + ensure_role "$rpc" "$mirrored" "BURNER_ROLE" "$BURNER_ROLE" "$bridge" + done < <(token_rows_for_chain "$chain_id") + json_log chainId="$chain_id" suffix="$suffix" status="configured" bridge="$bridge" sendRouter="$send_router" receiveRouter="$receive_router" feeToken="$fee_token" +done + +echo +echo "Done. Manifest: $MANIFEST" +echo "Next: pnpm cw:bridge-e2e-readiness && pnpm cw:full-readiness" diff --git a/scripts/deployment/deploy-engine-x-single-sided-dodo-cwusdc-vault-mainnet.sh b/scripts/deployment/deploy-engine-x-single-sided-dodo-cwusdc-vault-mainnet.sh new file mode 100755 index 00000000..495a1630 --- /dev/null +++ b/scripts/deployment/deploy-engine-x-single-sided-dodo-cwusdc-vault-mainnet.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" + +: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}" + +CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}" +QUOTE_TOKEN="${ENGINE_X_SINGLE_SIDED_DODO_QUOTE_TOKEN:-${WETH9_MAINNET:-0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2}}" +DODO_INTEGRATION="${ENGINE_X_SINGLE_SIDED_DODO_INTEGRATION:-${DODO_PMM_INTEGRATION_MAINNET:-${CHAIN_1_DODO_PMM_INTEGRATION:-}}}" +VERIFY="${VERIFY:-1}" +EXECUTE="${EXECUTE:-0}" + +OWNER="${ENGINE_X_SINGLE_SIDED_DODO_OWNER:-${DEPLOYER_ADDRESS:-}}" +if [[ -n "${PRIVATE_KEY:-}" ]]; then + OWNER="$(cast wallet address --private-key "${PRIVATE_KEY}")" +fi + +if [[ -z "${OWNER}" ]]; then + echo "Set PRIVATE_KEY, DEPLOYER_ADDRESS, or ENGINE_X_SINGLE_SIDED_DODO_OWNER" >&2 + exit 1 +fi +if [[ -z "${DODO_INTEGRATION}" ]]; then + echo "Set DODO_PMM_INTEGRATION_MAINNET or ENGINE_X_SINGLE_SIDED_DODO_INTEGRATION" >&2 + exit 1 +fi +if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then + echo "PRIVATE_KEY is required when EXECUTE=1" >&2 + exit 1 +fi + +VERIFY_ARGS=() +if [[ "${VERIFY}" == "1" ]]; then + VERIFY_ARGS+=(--verify) +fi + +CREATE_CMD_EXEC=( + forge create + --broadcast + --rpc-url "${ETHEREUM_MAINNET_RPC}" + --private-key "${PRIVATE_KEY:-}" + "${VERIFY_ARGS[@]}" + contracts/flash/DBISEngineXSingleSidedDodoCwusdcVault.sol:DBISEngineXSingleSidedDodoCwusdcVault + --constructor-args + "${CWUSDC}" "${QUOTE_TOKEN}" "${DODO_INTEGRATION}" "${OWNER}" +) + +cat </dev/null +DEPLOY_OUT="$("${CREATE_CMD_EXEC[@]}")" +popd >/dev/null +printf '%s\n' "${DEPLOY_OUT}" + +VAULT="$(printf '%s\n' "${DEPLOY_OUT}" | grep -oE 'Deployed to: 0x[a-fA-F0-9]{40}' | awk '{print $3}' | tail -1)" +if [[ -z "${VAULT}" ]]; then + echo "Could not parse deployed single-sided DODO cWUSDC wrapper address" >&2 + exit 1 +fi + +echo "DBIS_ENGINE_X_SINGLE_SIDED_DODO_CWUSDC_VAULT=${VAULT}" diff --git a/scripts/deployment/deploy-sankofa-studio-lxc.sh b/scripts/deployment/deploy-sankofa-studio-lxc.sh index fb96e73d..6aa0194d 100755 --- a/scripts/deployment/deploy-sankofa-studio-lxc.sh +++ b/scripts/deployment/deploy-sankofa-studio-lxc.sh @@ -3,7 +3,8 @@ # Usage: ./scripts/deployment/deploy-sankofa-studio-lxc.sh [--dry-run] [--skip-create] # --dry-run Print commands only. # --skip-create Use existing container 7805 (only install Docker / compose / deploy app). -# Env: PROXMOX_HOST, NODE, VMID, HOSTNAME, IP_SANKOFA_STUDIO, REPO_URL or REPO_PATH, ENV_FILE. +# Env: PROXMOX_HOST (defaults from VMID), NODE, VMID, HOSTNAME, IP_SANKOFA_STUDIO, REPO_URL or REPO_PATH, ENV_FILE. +# DEPLOY_PCT_ON_LOCAL_PVE=1 — only on a Proxmox node: run pct locally (no SSH). # See: docs/03-deployment/SANKOFA_STUDIO_DEPLOYMENT.md set -euo pipefail @@ -45,6 +46,11 @@ for a in "$@"; do [[ "$a" == "--skip-create" ]] && SKIP_CREATE=true done +PROXMOX_MONOREPO_ROOT="$PROXMOX_ROOT" +# shellcheck disable=SC1091 +source "$PROXMOX_ROOT/scripts/lib/require-proxmox-ssh-for-pct.sh" +require_proxmox_ssh_for_pct || exit 1 + run_cmd() { if [[ -n "$PROXMOX_HOST" ]]; then ssh $SSH_OPTS root@"$PROXMOX_HOST" "$@" @@ -72,17 +78,6 @@ echo "URL: https://studio.sankofa.nexus → http://${IP}:8000" echo "IP: $IP | Memory: ${MEMORY_MB}MB | Cores: $CORES | Disk: ${DISK_GB}G" echo "" -# pct runs only on Proxmox hosts; from another machine set PROXMOX_HOST to SSH there -if ! $DRY_RUN && [[ -z "${PROXMOX_HOST:-}" ]] && ! command -v pct &>/dev/null; then - echo "ERROR: 'pct' not found. This script must run on a Proxmox host or with PROXMOX_HOST set." - echo "" - echo "From your current machine, run:" - echo " PROXMOX_HOST=192.168.11.11 REPO_URL='https://gitea.d-bis.org/d-bis/FusionAI-Creator.git' $0" - echo "" - echo "Or SSH to the Proxmox host and run the script there (with REPO_URL set)." - exit 1 -fi - if ! $SKIP_CREATE; then if $DRY_RUN; then echo "[DRY-RUN] Would create LXC $VMID with hostname=$HOSTNAME, ip=$IP/24 (Docker + FusionAI Creator)" diff --git a/scripts/deployment/execute-coffee-money-gas-topups.mjs b/scripts/deployment/execute-coffee-money-gas-topups.mjs new file mode 100644 index 00000000..998feea2 --- /dev/null +++ b/scripts/deployment/execute-coffee-money-gas-topups.mjs @@ -0,0 +1,172 @@ +#!/usr/bin/env node +/** + * Execute the coffee-money gas top-up packet generated by + * scripts/deployment/plan-coffee-money-gas-topups.mjs. + * + * Default mode is dry-run. Pass --execute to approve exact source-token allowance + * and submit the bounded LiFi bridge transactions. + */ + +import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { Contract, JsonRpcProvider, Wallet } from "ethers"; + +const repoRoot = resolve(new URL("../..", import.meta.url).pathname); +const planPath = resolve(repoRoot, "reports/status/coffee-money-gas-topup-plan-latest.json"); +const outJson = resolve(repoRoot, "reports/status/coffee-money-gas-topup-execution-latest.json"); +const outMd = resolve(repoRoot, "reports/status/coffee-money-gas-topup-execution-latest.md"); +const execute = process.argv.includes("--execute"); +const confirmations = Number(process.env.COFFEE_MONEY_CONFIRMATIONS || "1"); +const rpcUrl = process.env.ETHEREUM_MAINNET_RPC || process.env.RPC_URL_1 || process.env.RPC_URL_MAINNET || "https://ethereum.publicnode.com"; +const privateKey = process.env.PRIVATE_KEY; +const erc20Abi = [ + "function allowance(address owner,address spender) view returns (uint256)", + "function approve(address spender,uint256 amount) returns (bool)", + "function balanceOf(address owner) view returns (uint256)", +]; + +const plan = JSON.parse(readFileSync(planPath, "utf8")); + +if (execute && !privateKey) { + console.error("PRIVATE_KEY is required for --execute"); + process.exit(1); +} + +if (!plan.readiness?.sourceTokenSufficient && !plan.readiness?.sourceUsdcSufficient) { + console.error("Plan source token balance is not sufficient. Re-run the planner and inspect readiness."); + process.exit(1); +} + +if (!plan.readiness?.mainnetEthGasSufficientForQuotedBridgeTxs || !plan.readiness?.allQuotesOk) { + console.error("Plan is not ready for execution. Re-run the planner and inspect readiness."); + process.exit(1); +} + +const provider = new JsonRpcProvider(rpcUrl, 1); +const wallet = execute ? new Wallet(privateKey, provider) : null; +const operator = execute ? await wallet.getAddress() : plan.deployer; + +if (operator.toLowerCase() !== String(plan.deployer).toLowerCase()) { + console.error(`PRIVATE_KEY resolves to ${operator}, expected ${plan.deployer}`); + process.exit(1); +} + +const sourceTokenAddress = plan.source?.tokenAddress; +const sourceTokenSymbol = plan.source?.token || "source token"; +if (!sourceTokenAddress) { + console.error("Plan is missing source.tokenAddress. Re-run the planner with the updated planner script."); + process.exit(1); +} + +const token = new Contract(sourceTokenAddress, erc20Abi, execute ? wallet : provider); +const execution = { + generatedAt: new Date().toISOString(), + mode: execute ? "execute_broadcast" : "dry_run_no_broadcast", + deployer: plan.deployer, + plan: "reports/status/coffee-money-gas-topup-plan-latest.json", + approvals: [], + transactions: [], + totals: plan.totals, +}; + +for (const allowancePlan of plan.allowances ?? []) { + const spender = allowancePlan.spender; + const required = BigInt(allowancePlan.requiredRaw); + const current = await token.allowance(plan.deployer, spender); + const approvalRecord = { + spender, + requiredRaw: required.toString(), + currentRaw: current.toString(), + action: current >= required ? "skip_allowance_sufficient" : "approve_exact_required", + txHash: null, + status: "pending", + }; + if (execute && current < required) { + const tx = await token.approve(spender, required, { gasLimit: 100_000n }); + approvalRecord.txHash = tx.hash; + const receipt = await tx.wait(confirmations); + approvalRecord.status = receipt?.status === 1 ? "confirmed" : "failed"; + approvalRecord.blockNumber = receipt?.blockNumber ?? null; + if (receipt?.status !== 1) { + execution.approvals.push(approvalRecord); + throw new Error(`Approval failed: ${tx.hash}`); + } + } else { + approvalRecord.status = execute ? "confirmed" : "dry_run"; + } + execution.approvals.push(approvalRecord); +} + +for (const row of plan.rows) { + const request = row.transactionRequest; + const record = { + chainId: row.chainId, + nativeSymbol: row.nativeSymbol, + spend: row.spend, + spendUSDC: row.spendUSDC, + expectedOutNative: row.toAmountNative, + tool: row.tool, + to: request?.to ?? null, + value: request?.value ?? null, + gasLimit: request?.gasLimit ?? null, + gasPrice: request?.gasPrice ?? null, + txHash: null, + status: execute ? "pending" : "dry_run", + }; + if (execute) { + const tx = await wallet.sendTransaction({ + to: request.to, + data: request.data, + value: BigInt(request.value || "0"), + gasLimit: BigInt(request.gasLimit), + gasPrice: BigInt(request.gasPrice), + chainId: 1, + }); + record.txHash = tx.hash; + const receipt = await tx.wait(confirmations); + record.status = receipt?.status === 1 ? "confirmed" : "failed"; + record.blockNumber = receipt?.blockNumber ?? null; + if (receipt?.status !== 1) { + execution.transactions.push(record); + throw new Error(`Bridge tx failed: ${tx.hash}`); + } + } + execution.transactions.push(record); +} + +function table(headers, rows) { + return [ + `| ${headers.join(" | ")} |`, + `| ${headers.map(() => "---").join(" | ")} |`, + ...rows.map((row) => `| ${row.map((cell) => String(cell ?? "").replace(/\|/g, "\\|")).join(" | ")} |`), + ].join("\n"); +} + +const md = [ + "# Coffee-Money Gas Top-Up Execution", + "", + `- Generated: \`${execution.generatedAt}\``, + `- Mode: \`${execution.mode}\``, + `- Deployer: \`${execution.deployer}\``, + `- Planned spend: \`${execution.totals.spend || execution.totals.spendUSDC} ${sourceTokenSymbol}\``, + `- Mainnet gas estimate from plan: \`${execution.totals.mainnetGasCostETH} ETH\``, + "", + "## Approvals", + "", + table( + ["Spender", "Required raw", "Action", "Status", "Tx"], + execution.approvals.map((row) => [row.spender, row.requiredRaw, row.action, row.status, row.txHash]), + ), + "", + "## Bridge Transactions", + "", + table( + ["Destination", "Spend", "Expected out", "Tool", "Status", "Tx"], + execution.transactions.map((row) => [row.chainId, `${row.spend || row.spendUSDC} ${sourceTokenSymbol}`, `${row.expectedOutNative} ${row.nativeSymbol}`, row.tool, row.status, row.txHash]), + ), +].join("\n"); + +mkdirSync(resolve(repoRoot, "reports/status"), { recursive: true }); +writeFileSync(outJson, `${JSON.stringify(execution, null, 2)}\n`); +writeFileSync(outMd, `${md}\n`); +console.log(outJson); diff --git a/scripts/deployment/fix-dev-vm-srv-projects-ownership.sh b/scripts/deployment/fix-dev-vm-srv-projects-ownership.sh new file mode 100755 index 00000000..ba1be481 --- /dev/null +++ b/scripts/deployment/fix-dev-vm-srv-projects-ownership.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# Recursively chown /srv/projects inside Dev CT 5700 to the primary dev user. +# Use when rsync --delete-remote fails with Permission denied (root-owned files on VM). +# +# Requires: SSH as root to the Proxmox node that hosts VMID 5700 (default r630-04). +# +# Usage: +# ./scripts/deployment/fix-dev-vm-srv-projects-ownership.sh --dry-run +# ./scripts/deployment/fix-dev-vm-srv-projects-ownership.sh +# +# Env: +# DEV_VM_CTID — LXC ID (default 5700) +# DEV_VM_USER — owning user inside CT (default dev1) +# DEV_VM_PVE_HOST — override Proxmox node IP/hostname (default: get_host_for_vmid + R630_04 fallback) +# Do not use generic PROXMOX_HOST here; it may point at the wrong node. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +export PROJECT_ROOT +# shellcheck source=/dev/null +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true + +DRY_RUN=0 +while [[ $# -gt 0 ]]; do + case "$1" in + --dry-run) DRY_RUN=1; shift ;; + --help|-h) + sed -n '1,22p' "$0" | tail -n +2 + exit 0 + ;; + *) + echo "ERROR: unknown argument: $1 (try --help)" >&2 + exit 1 + ;; + esac +done + +CTID="${DEV_VM_CTID:-5700}" +OWNER="${DEV_VM_USER:-dev1}" +if [[ -n "${DEV_VM_PVE_HOST:-}" ]]; then + NODE="$DEV_VM_PVE_HOST" +else + NODE="$(get_host_for_vmid "$CTID" 2>/dev/null || true)" +fi +NODE="${NODE:-${PROXMOX_HOST_R630_04:-192.168.11.14}}" + +REMOTE_CMD="pct exec $CTID -- chown -R ${OWNER}:${OWNER} /srv/projects" + +echo "=== Fix Dev VM /srv/projects ownership ===" +echo "Node: root@${NODE}" +echo "CT: $CTID" +echo "Owner: $OWNER" +echo "" + +if [[ "$DRY_RUN" == "1" ]]; then + echo "DRY-RUN: ssh root@${NODE} \"$REMOTE_CMD\"" + exit 0 +fi + +ssh -o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new "root@${NODE}" "$REMOTE_CMD" +echo "Done." diff --git a/scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh b/scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh index b08a8c0c..abb0fe01 100755 --- a/scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh +++ b/scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh @@ -6,6 +6,8 @@ PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" # shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/mev-protection.sh +source "${PROJECT_ROOT}/scripts/lib/mev-protection.sh" : "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}" @@ -103,8 +105,24 @@ available_usdc = vault_usdc if include_lender == "1" else max(vault_usdc - lende amount = min(pool_cw, pool_usdc, vault_cw, available_usdc) if override_amount: amount = int(override_amount) + +def units(raw: int) -> str: + return f"{Decimal(raw) / Decimal(10**6):f}" + +def emit(name, value): + print(f"{name}='{value}'") + +emit("POOL_CWUSDC_UNITS", units(pool_cw)) +emit("POOL_USDC_UNITS", units(pool_usdc)) +emit("LENDER_USDC_UNITS", units(lender_usdc)) +emit("VAULT_CWUSDC_UNITS", units(vault_cw)) +emit("VAULT_USDC_UNITS", units(vault_usdc)) +emit("POOL_USDC_AVAILABLE_FOR_MIGRATION_RAW", available_usdc) +emit("POOL_USDC_AVAILABLE_FOR_MIGRATION_UNITS", units(available_usdc)) if amount <= 0: - raise SystemExit("no balanced vault liquidity is available to migrate") + emit("NO_MIGRATION", "1") + emit("NO_MIGRATION_REASON", "no balanced vault liquidity is available to migrate") + raise SystemExit(0) if amount > vault_cw: raise SystemExit("migration amount exceeds vault cWUSDC balance") if amount > vault_usdc: @@ -117,28 +135,40 @@ token0, token1 = addrs amount0 = amount if token0 == cwusdc.lower() else amount amount1 = amount if token1 == usdc.lower() else amount -def units(raw: int) -> str: - return f"{Decimal(raw) / Decimal(10**6):f}" - -def emit(name, value): - print(f"{name}='{value}'") - +emit("NO_MIGRATION", "0") emit("TOKEN0", token0) emit("TOKEN1", token1) emit("AMOUNT0_RAW", amount0) emit("AMOUNT1_RAW", amount1) emit("MIGRATE_RAW", amount) emit("MIGRATE_UNITS", units(amount)) -emit("POOL_CWUSDC_UNITS", units(pool_cw)) -emit("POOL_USDC_UNITS", units(pool_usdc)) -emit("LENDER_USDC_UNITS", units(lender_usdc)) -emit("VAULT_CWUSDC_UNITS", units(vault_cw)) -emit("VAULT_USDC_UNITS", units(vault_usdc)) -emit("POOL_USDC_AVAILABLE_FOR_MIGRATION_RAW", available_usdc) -emit("POOL_USDC_AVAILABLE_FOR_MIGRATION_UNITS", units(available_usdc)) PY )" +if [[ "${NO_MIGRATION}" == "1" ]]; then + cat </dev/null || echo "$s_raw") - echo " idx=$_s_idx $s_addr raw=$s_raw (~$h cWUSDC)" - _s_idx=$((_s_idx + 1)) -done < <(head -3 "$ADDR_TMP") 3< <(head -3 "$AMOUNTS_TMP") -_s_idx=$((OFFSET + WALLET_COUNT - 3)) -while IFS= read -r s_addr && IFS= read -r s_raw <&3; do - h=$(python3 -c "d=int('$DECIMALS'); a=int('$s_raw'); print(f'{a / (10**d):.6f}')" 2>/dev/null || echo "$s_raw") - echo " idx=$_s_idx $s_addr raw=$s_raw (~$h cWUSDC)" - _s_idx=$((_s_idx + 1)) -done < <(tail -3 "$ADDR_TMP") 3< <(tail -3 "$AMOUNTS_TMP") +echo "Sample mints:" +if [[ "$WALLET_COUNT" -le 6 ]]; then + _s_idx=$OFFSET + while IFS=$'\t' read -r s_addr s_raw; do + h=$(python3 -c "d=int('$DECIMALS'); a=int('$s_raw'); print(f'{a / (10**d):.6f}')" 2>/dev/null || echo "$s_raw") + echo " idx=$_s_idx $s_addr raw=$s_raw (~$h cWUSDC)" + _s_idx=$((_s_idx + 1)) + done < <(paste -d $'\t' "$ADDR_TMP" "$AMOUNTS_TMP") +else + _s_idx=$OFFSET + while IFS=$'\t' read -r s_addr s_raw; do + h=$(python3 -c "d=int('$DECIMALS'); a=int('$s_raw'); print(f'{a / (10**d):.6f}')" 2>/dev/null || echo "$s_raw") + echo " idx=$_s_idx $s_addr raw=$s_raw (~$h cWUSDC)" + _s_idx=$((_s_idx + 1)) + done < <(paste -d $'\t' "$ADDR_TMP" "$AMOUNTS_TMP" | head -3) + _s_idx=$((OFFSET + WALLET_COUNT - 3)) + while IFS=$'\t' read -r s_addr s_raw; do + h=$(python3 -c "d=int('$DECIMALS'); a=int('$s_raw'); print(f'{a / (10**d):.6f}')" 2>/dev/null || echo "$s_raw") + echo " idx=$_s_idx $s_addr raw=$s_raw (~$h cWUSDC)" + _s_idx=$((_s_idx + 1)) + done < <(paste -d $'\t' "$ADDR_TMP" "$AMOUNTS_TMP" | tail -3) +fi echo "" sent=0 diff --git a/scripts/deployment/mirror-github-fork-to-gitea.sh b/scripts/deployment/mirror-github-fork-to-gitea.sh new file mode 100755 index 00000000..65d79d0a --- /dev/null +++ b/scripts/deployment/mirror-github-fork-to-gitea.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Mirror a GitHub fork to Gitea (push --mirror). Run from operator LAN when Gitea is reachable. +# +# Required: +# GITEA_REMOTE — e.g. https://gitea.d-bis.org/ORG/DefiLlama-Adapters.git +# Optional: +# GITHUB_FORK — default https://github.com/Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters.git +# +# Usage: +# export GITEA_REMOTE='https://USER:TOKEN@gitea.d-bis.org/d-bis/DefiLlama-Adapters.git' +# ./scripts/deployment/mirror-github-fork-to-gitea.sh +# +# Or dry-run (fetch only): +# DRY_RUN=1 ./scripts/deployment/mirror-github-fork-to-gitea.sh +set -euo pipefail +GITHUB_FORK="${GITHUB_FORK:-https://github.com/Defi-Oracle-Meta-Blockchain/DefiLlama-Adapters.git}" +if [[ -z "${GITEA_REMOTE:-}" ]]; then + echo "Set GITEA_REMOTE to your Gitea repo URL (with credentials if needed)." >&2 + exit 1 +fi +TMP="${TMPDIR:-/tmp}/mirror-defillama-$$" +cleanup() { rm -rf "$TMP"; } +trap cleanup EXIT +git clone --mirror "$GITHUB_FORK" "$TMP" +if [[ "${DRY_RUN:-}" == "1" ]]; then + echo "DRY_RUN=1: mirror clone OK; skip push to GITEA_REMOTE" + exit 0 +fi +git -C "$TMP" push --mirror "$GITEA_REMOTE" +echo "Mirror push completed." diff --git a/scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh b/scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh old mode 100644 new mode 100755 index 91544794..3eb0a757 --- a/scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh +++ b/scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh @@ -49,17 +49,18 @@ if [[ "$CID" != "1" ]]; then exit 1 fi -ROLE=$(cast keccak "MINTER_ROLE()") -# AccessControl MINTER_ROLE is keccak256 of the string "MINTER_ROLE" for OZ — use cast keccak ROLE=$(cast keccak "MINTER_ROLE") -if HR=$(cast call "$CWUSDC" "hasRole(bytes32,address)(bool)" "$ROLE" "$FROM" --rpc-url "$RPC" 2>/dev/null); then - if [[ "${HR,,}" != *true* ]]; then - echo "[WARN] hasRole(MINTER_ROLE) returned false for signer — mints will likely revert." >&2 - else +if [[ "${SKIP_EI_MATRIX_MINT_PREFLIGHT:-}" == "1" ]]; then + echo "Preflight: skipped (SKIP_EI_MATRIX_MINT_PREFLIGHT=1)" +elif HR=$(cast call "$CWUSDC" "hasRole(bytes32,address)(bool)" "$ROLE" "$FROM" --rpc-url "$RPC" 2>/dev/null); then + HR_TR=$(echo "$HR" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]') + if [[ "$HR_TR" == *true* || "$HR_TR" == *0x0000000000000000000000000000000000000000000000000000000000000001* ]]; then echo "Preflight: MINTER_ROLE on cWUSDC for signer — OK" + else + echo "[WARN] hasRole(MINTER_ROLE) not true for signer (got: $HR) — mints may revert if minter is elsewhere." >&2 fi else - echo "[WARN] Could not call hasRole (ABI may differ) — continuing." >&2 + echo "[WARN] Could not call hasRole — continuing (token ABI may differ)." >&2 fi echo "" diff --git a/scripts/deployment/pipeline-ei-matrix-remediate-cwusdc-from-audit.sh b/scripts/deployment/pipeline-ei-matrix-remediate-cwusdc-from-audit.sh new file mode 100755 index 00000000..5bce6984 --- /dev/null +++ b/scripts/deployment/pipeline-ei-matrix-remediate-cwusdc-from-audit.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +# Run full-grid readiness audit (optional), then remediate mainnet cWUSDC gaps. +# +# Modes: +# Default: fixed --send-raw to each gap index (send-cwusdc-ei-matrix-targeted.sh). +# --multicall: per-wallet deficit TSV + Multicall3 batches (cheapest; see send-cwusdc-ei-matrix-multicall-batches.sh). +# +# Usage: +# ./scripts/deployment/pipeline-ei-matrix-remediate-cwusdc-from-audit.sh --dry-run --send-raw 5000000 +# ./scripts/deployment/pipeline-ei-matrix-remediate-cwusdc-from-audit.sh --dry-run --multicall +# ./scripts/deployment/pipeline-ei-matrix-remediate-cwusdc-from-audit.sh --execute --multicall +# SKIP_EI_MATRIX_REMEDIATE_AUDIT=1 ./scripts/deployment/pipeline-ei-matrix-remediate-cwusdc-from-audit.sh --multicall --execute +# +# Env: +# SKIP_EI_MATRIX_REMEDIATE_AUDIT=1 +# EI_MATRIX_REMEDIATE_MULTICALL=1 Same as --multicall +# EI_MATRIX_REMEDIATE_TOPUP_TSV=path Default: reports/status/ei-matrix-cwusdc-topup-amounts.tsv +# Rebuild TSV after audit: scripts/verify/build-ei-matrix-cwusdc-topup-tsv-from-audit-json.sh +# +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" + +DRY=(false) +SEND_RAW="" +INDICES_OVERRIDE="" +USE_MULTICALL=false +PASS=() +while [[ $# -gt 0 ]]; do + case "$1" in + --dry-run) DRY=(true); PASS+=(--dry-run); shift ;; + --send-raw) SEND_RAW="${2:?}"; shift 2 ;; + --indices-file) INDICES_OVERRIDE="${2:?}"; shift 2 ;; + --multicall) USE_MULTICALL=true; shift ;; + --) shift; PASS+=("$@"); break ;; + *) PASS+=("$1"); shift ;; + esac +done + +if [[ "${EI_MATRIX_REMEDIATE_MULTICALL:-}" == "1" ]]; then + USE_MULTICALL=true +fi + +AUDIT_SH="$PROJECT_ROOT/scripts/verify/run-ei-matrix-full-readiness-audit.sh" +TARGET_SH="$PROJECT_ROOT/scripts/deployment/send-cwusdc-ei-matrix-targeted.sh" +MULTICALL_SH="$PROJECT_ROOT/scripts/deployment/send-cwusdc-ei-matrix-multicall-batches.sh" +BUILD_TSV_SH="$PROJECT_ROOT/scripts/verify/build-ei-matrix-cwusdc-topup-tsv-from-audit-json.sh" + +if [[ "$USE_MULTICALL" == true ]]; then + if [[ -n "$SEND_RAW" ]]; then + echo "Do not combine --multicall with --send-raw (multicall uses per-row TSV amounts)." >&2 + exit 1 + fi +else + if [[ -z "$SEND_RAW" ]]; then + echo "Required: --send-raw R, or use --multicall with a top-up TSV." >&2 + exit 1 + fi +fi + +if [[ "${SKIP_EI_MATRIX_REMEDIATE_AUDIT:-}" != "1" ]]; then + echo "→ Step 1: full readiness audit (refreshes gap files; exit 1 means gaps below policy — OK)" + _audit_rc=0 + "$AUDIT_SH" || _audit_rc=$? + if [[ "$_audit_rc" -gt 1 ]]; then + echo "Audit failed with exit $_audit_rc (RPC/config?)." >&2 + exit "$_audit_rc" + fi + if [[ "$USE_MULTICALL" == true ]]; then + echo "→ Step 1b: rebuild top-up TSV from ei-matrix-readiness-audit-latest.json" + "$BUILD_TSV_SH" + fi +else + echo "→ Step 1: skipped (SKIP_EI_MATRIX_REMEDIATE_AUDIT=1)" +fi + +if [[ "$USE_MULTICALL" == true ]]; then + TSV="${EI_MATRIX_REMEDIATE_TOPUP_TSV:-$PROJECT_ROOT/reports/status/ei-matrix-cwusdc-topup-amounts.tsv}" + if [[ ! -f "$TSV" ]]; then + echo "Missing top-up TSV: $TSV — run $BUILD_TSV_SH or audit without SKIP." >&2 + exit 1 + fi + N=$(wc -l <"$TSV" | tr -d ' ') + echo "→ Step 2: Multicall3 batches ($N rows) $TSV" + if $DRY; then + exec "$MULTICALL_SH" --dry-run --tsv "$TSV" + else + exec "$MULTICALL_SH" --execute --tsv "$TSV" + fi +fi + +GAPS="${INDICES_OVERRIDE:-${EI_MATRIX_AUDIT_GAPS_MAINNET:-${PROJECT_ROOT}/reports/status/ei-matrix-readiness-gaps-mainnet-indices.txt}}" +if [[ ! -f "$GAPS" ]]; then + echo "Missing gap file: $GAPS" >&2 + exit 1 +fi +N=$(grep -cE '^[0-9]+$' "$GAPS" 2>/dev/null || echo 0) +if [[ "$N" -eq 0 ]]; then + echo "No mainnet gap indices in $GAPS — nothing to remediate." + exit 0 +fi +echo "→ Step 2: targeted send to $N indices ($GAPS) --send-raw $SEND_RAW" +exec "$TARGET_SH" "${PASS[@]}" --send-raw "$SEND_RAW" --indices-file "$GAPS" diff --git a/scripts/deployment/plan-coffee-money-gas-topups.mjs b/scripts/deployment/plan-coffee-money-gas-topups.mjs new file mode 100644 index 00000000..f1898dd9 --- /dev/null +++ b/scripts/deployment/plan-coffee-money-gas-topups.mjs @@ -0,0 +1,325 @@ +#!/usr/bin/env node +/** + * Read-only coffee-money gas top-up planner. + * + * Quotes a Mainnet source token -> destination native gas via LiFi for the chains that + * currently block token-aggregation stability work. It does not approve, + * bridge, sign, or broadcast transactions. + */ + +import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { resolve } from "node:path"; + +const repoRoot = resolve(new URL("../..", import.meta.url).pathname); +const fundingPlanPath = resolve(repoRoot, "reports/status/token-aggregation-liquidity-gap-funding-plan-latest.json"); +const jsonOut = resolve(repoRoot, "reports/status/coffee-money-gas-topup-plan-latest.json"); +const mdOut = resolve(repoRoot, "reports/status/coffee-money-gas-topup-plan-latest.md"); +const deployer = process.env.DEPLOYER_ADDRESS || "0x4A666F96fC8764181194447A7dFdb7d471b301C8"; +const sourceChain = 1; +const sourceToken = process.env.COFFEE_MONEY_SOURCE_SYMBOL || "USDC"; +const sourceTokenAddress = process.env.COFFEE_MONEY_SOURCE_TOKEN_ADDRESS || "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; +const sourceDecimals = Number(process.env.COFFEE_MONEY_SOURCE_DECIMALS || "6"); +const safetyMultiplierBps = BigInt(process.env.COFFEE_MONEY_TOPUP_SAFETY_BPS || "15000"); +const minSpendRaw = BigInt(process.env.COFFEE_MONEY_MIN_SPEND_USDC_RAW || "1000000"); +const maxSpendRaw = BigInt(process.env.COFFEE_MONEY_MAX_SPEND_USDC_RAW || "4000000"); +const targetChains = new Set( + (process.env.COFFEE_MONEY_TARGET_CHAINS || "10,56,137,42161") + .split(",") + .map((value) => Number(value.trim())) + .filter(Boolean), +); +const nativeTokenAddress = "0x0000000000000000000000000000000000000000"; +const nativeTokenOverrides = { + // LiFi models CELO as the canonical CELO token on Celo, not the zero address. + 42220: "0x471EcE3750Da237f93B8E339c536989b8978a438", +}; +const ethereumRpc = process.env.ETHEREUM_MAINNET_RPC || process.env.RPC_URL_1 || "https://ethereum.publicnode.com"; + +const fundingPlan = JSON.parse(readFileSync(fundingPlanPath, "utf8")); + +function parseUnitsDecimal(value, decimals = 18) { + const [whole, frac = ""] = String(value).split("."); + const padded = `${frac}${"0".repeat(decimals)}`.slice(0, decimals); + return BigInt(whole || "0") * 10n ** BigInt(decimals) + BigInt(padded || "0"); +} + +function decimalUnits(raw, decimals) { + const scale = 10n ** BigInt(decimals); + const whole = raw / scale; + const frac = raw % scale; + const fracText = frac.toString().padStart(decimals, "0").replace(/0+$/, ""); + return fracText ? `${whole}.${fracText}` : whole.toString(); +} + +function padAddress(address) { + return String(address).replace(/^0x/i, "").padStart(64, "0"); +} + +async function rpcCall(rpcUrl, method, params) { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 12_000); + try { + const response = await fetch(rpcUrl, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ jsonrpc: "2.0", method, params, id: 1 }), + signal: controller.signal, + }); + const json = await response.json(); + if (json.error) return { ok: false, error: json.error.message || JSON.stringify(json.error) }; + return { ok: true, result: json.result }; + } catch (error) { + return { ok: false, error: error.message }; + } finally { + clearTimeout(timeout); + } +} + +function bigintFromHex(hex) { + if (!hex || hex === "0x") return 0n; + return BigInt(hex); +} + +async function erc20Allowance(token, owner, spender) { + if (!spender) return { ok: false, raw: "0", units: "0", error: "missing_spender" }; + const selector = "0xdd62ed3e"; + const data = `${selector}${padAddress(owner)}${padAddress(spender)}`; + const result = await rpcCall(ethereumRpc, "eth_call", [{ to: token, data }, "latest"]); + const raw = result.ok ? bigintFromHex(result.result) : 0n; + return { + ok: result.ok, + raw: raw.toString(), + units: decimalUnits(raw, sourceDecimals), + error: result.ok ? null : result.error, + }; +} + +function spendForShortfall(shortfallNative, quote) { + const shortfallRaw = parseUnitsDecimal(shortfallNative, 18); + const toAmountRaw = BigInt(quote.estimate?.toAmount || "0"); + const fromAmountRaw = BigInt(quote.estimate?.fromAmount || "0"); + if (toAmountRaw === 0n || fromAmountRaw === 0n) return maxSpendRaw; + const desiredOutRaw = (shortfallRaw * safetyMultiplierBps + 9_999n) / 10_000n; + const spend = (desiredOutRaw * fromAmountRaw + toAmountRaw - 1n) / toAmountRaw; + if (spend < minSpendRaw) return minSpendRaw; + if (spend > maxSpendRaw) return maxSpendRaw; + return spend; +} + +async function lifiQuote(toChain, fromAmountRaw) { + const url = new URL("https://li.quest/v1/quote"); + url.searchParams.set("fromChain", String(sourceChain)); + url.searchParams.set("toChain", String(toChain)); + url.searchParams.set("fromToken", sourceTokenAddress); + url.searchParams.set("toToken", nativeTokenOverrides[toChain] || nativeTokenAddress); + url.searchParams.set("fromAmount", String(fromAmountRaw)); + url.searchParams.set("fromAddress", deployer); + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 20_000); + try { + const response = await fetch(url, { signal: controller.signal }); + const text = await response.text(); + let data = null; + try { + data = JSON.parse(text); + } catch { + data = { raw: text }; + } + return { + ok: response.ok, + statusCode: response.status, + url: url.toString(), + data, + error: response.ok ? null : text.slice(0, 500), + }; + } catch (error) { + return { ok: false, statusCode: 0, url: url.toString(), data: null, error: error.message }; + } finally { + clearTimeout(timeout); + } +} + +const shortfalls = fundingPlan.chainGasBudgetRows.filter((row) => ( + targetChains.has(Number(row.chainId)) && row.status === "chain_gas_budget_shortfall" +)); + +const sourceInventory = fundingPlan.ethereumSourceInventory?.tokens?.find((token) => ( + token.symbol === sourceToken || String(token.address).toLowerCase() === String(sourceTokenAddress).toLowerCase() +)); +const sourceBalanceRaw = BigInt(sourceInventory?.balanceRaw || "0"); +const rows = []; + +for (const shortfall of shortfalls) { + const oneDollarQuote = await lifiQuote(shortfall.chainId, minSpendRaw); + let spendRaw = minSpendRaw; + let finalQuote = oneDollarQuote; + if (oneDollarQuote.ok) { + spendRaw = spendForShortfall(shortfall.shortfallNative, oneDollarQuote.data); + if (spendRaw !== minSpendRaw) finalQuote = await lifiQuote(shortfall.chainId, spendRaw); + } + const estimate = finalQuote.data?.estimate ?? {}; + const tx = finalQuote.data?.transactionRequest ?? null; + rows.push({ + chainId: shortfall.chainId, + symbols: shortfall.symbols, + nativeSymbol: shortfall.nativeSymbol, + shortfallNative: shortfall.shortfallNative, + spendRaw: spendRaw.toString(), + spend: decimalUnits(spendRaw, sourceDecimals), + spendUSDC: sourceToken === "USDC" ? decimalUnits(spendRaw, sourceDecimals) : null, + quoteOk: finalQuote.ok, + quoteStatusCode: finalQuote.statusCode, + tool: finalQuote.data?.tool ?? estimate.tool ?? null, + toAmountRaw: estimate.toAmount ?? null, + toAmountMinRaw: estimate.toAmountMin ?? null, + toAmountNative: estimate.toAmount ? decimalUnits(BigInt(estimate.toAmount), 18) : null, + fromAmountUSD: estimate.fromAmountUSD ?? null, + toAmountUSD: estimate.toAmountUSD ?? null, + mainnetGasCostRaw: estimate.gasCosts?.[0]?.amount ?? null, + mainnetGasCostETH: estimate.gasCosts?.[0]?.amount ? decimalUnits(BigInt(estimate.gasCosts[0].amount), 18) : null, + approvalAddress: estimate.approvalAddress ?? null, + transactionTo: tx?.to ?? null, + transactionValue: tx?.value ?? null, + transactionGasLimit: tx?.gasLimit ?? null, + transactionDataPresent: Boolean(tx?.data), + transactionRequest: tx ? { + chainId: tx.chainId, + to: tx.to, + value: tx.value, + gasLimit: tx.gasLimit, + gasPrice: tx.gasPrice, + data: tx.data, + } : null, + quoteUrl: finalQuote.url, + blocker: finalQuote.ok ? null : finalQuote.error, + }); +} + +const allowanceBySpender = {}; +for (const spender of [...new Set(rows.map((row) => row.approvalAddress).filter(Boolean))]) { + const allowance = await erc20Allowance(sourceTokenAddress, deployer, spender); + const requiredRaw = rows + .filter((row) => row.approvalAddress === spender) + .reduce((sum, row) => sum + BigInt(row.spendRaw || "0"), 0n); + allowanceBySpender[spender] = { + spender, + requiredRaw: requiredRaw.toString(), + required: decimalUnits(requiredRaw, sourceDecimals), + requiredUSDC: sourceToken === "USDC" ? decimalUnits(requiredRaw, sourceDecimals) : null, + allowanceRaw: allowance.raw, + allowance: allowance.units, + allowanceUSDC: sourceToken === "USDC" ? allowance.units : null, + sufficient: BigInt(allowance.raw || "0") >= requiredRaw, + error: allowance.error, + }; +} + +const totalSpendRaw = rows.reduce((sum, row) => sum + BigInt(row.spendRaw || "0"), 0n); +const totalMainnetGasRaw = rows.reduce((sum, row) => sum + BigInt(row.mainnetGasCostRaw || "0"), 0n); +const mainnetEth = fundingPlan.ethereumSourceInventory?.native?.balanceRaw ? BigInt(fundingPlan.ethereumSourceInventory.native.balanceRaw) : 0n; + +const payload = { + generatedAt: new Date().toISOString(), + mode: "read_only_no_broadcast", + deployer, + source: { + chainId: sourceChain, + token: sourceToken, + tokenAddress: sourceTokenAddress, + decimals: sourceDecimals, + balanceRaw: sourceBalanceRaw.toString(), + balance: decimalUnits(sourceBalanceRaw, sourceDecimals), + balanceUSDC: sourceToken === "USDC" ? decimalUnits(sourceBalanceRaw, sourceDecimals) : null, + }, + policy: { + safetyMultiplierBps: Number(safetyMultiplierBps), + minSpend: decimalUnits(minSpendRaw, sourceDecimals), + maxSpend: decimalUnits(maxSpendRaw, sourceDecimals), + unit: sourceToken, + minSpendUSDC: sourceToken === "USDC" ? decimalUnits(minSpendRaw, sourceDecimals) : null, + maxSpendUSDC: sourceToken === "USDC" ? decimalUnits(maxSpendRaw, sourceDecimals) : null, + }, + totals: { + spendRaw: totalSpendRaw.toString(), + spend: decimalUnits(totalSpendRaw, sourceDecimals), + spendUSDC: sourceToken === "USDC" ? decimalUnits(totalSpendRaw, sourceDecimals) : null, + sourceBalanceAfterRaw: sourceBalanceRaw > totalSpendRaw ? (sourceBalanceRaw - totalSpendRaw).toString() : "0", + sourceBalanceAfter: decimalUnits(sourceBalanceRaw > totalSpendRaw ? sourceBalanceRaw - totalSpendRaw : 0n, sourceDecimals), + sourceBalanceAfterUSDC: sourceToken === "USDC" ? decimalUnits(sourceBalanceRaw > totalSpendRaw ? sourceBalanceRaw - totalSpendRaw : 0n, sourceDecimals) : null, + mainnetGasCostRaw: totalMainnetGasRaw.toString(), + mainnetGasCostETH: decimalUnits(totalMainnetGasRaw, 18), + mainnetEthBalanceRaw: mainnetEth.toString(), + mainnetEthBalance: decimalUnits(mainnetEth, 18), + mainnetEthAfterBridgeGasRaw: mainnetEth > totalMainnetGasRaw ? (mainnetEth - totalMainnetGasRaw).toString() : "0", + mainnetEthAfterBridgeGas: decimalUnits(mainnetEth > totalMainnetGasRaw ? mainnetEth - totalMainnetGasRaw : 0n, 18), + }, + readiness: { + sourceTokenSufficient: sourceBalanceRaw >= totalSpendRaw, + sourceUsdcSufficient: sourceToken === "USDC" ? sourceBalanceRaw >= totalSpendRaw : null, + mainnetEthGasSufficientForQuotedBridgeTxs: mainnetEth >= totalMainnetGasRaw, + allQuotesOk: rows.every((row) => row.quoteOk), + sourceAllowancesSufficient: Object.values(allowanceBySpender).every((row) => row.sufficient), + usdcAllowancesSufficient: sourceToken === "USDC" ? Object.values(allowanceBySpender).every((row) => row.sufficient) : null, + broadcastReady: false, + broadcastBoundary: "Quotes include transaction data, but this planner intentionally does not sign, approve, or broadcast.", + }, + allowances: Object.values(allowanceBySpender), + rows, +}; + +function table(headers, tableRows) { + return [ + `| ${headers.join(" | ")} |`, + `| ${headers.map(() => "---").join(" | ")} |`, + ...tableRows.map((row) => `| ${row.map((cell) => String(cell ?? "").replace(/\|/g, "\\|")).join(" | ")} |`), + ].join("\n"); +} + +const md = [ + "# Coffee-Money Gas Top-Up Plan", + "", + `- Generated: \`${payload.generatedAt}\``, + `- Mode: \`${payload.mode}\``, + `- Deployer: \`${deployer}\``, + `- Source: \`${payload.source.balance} ${sourceToken}\` on Ethereum Mainnet`, + `- Planned spend: \`${payload.totals.spend} ${sourceToken}\``, + `- Mainnet bridge gas estimate: \`${payload.totals.mainnetGasCostETH} ETH\``, + `- Mainnet ETH after quoted bridge gas: \`${payload.totals.mainnetEthAfterBridgeGas} ETH\``, + "", + table( + ["Check", "Value"], + Object.entries(payload.readiness).map(([key, value]) => [key, value]), + ), + "", + `## ${sourceToken} Allowances`, + "", + table( + ["Spender", "Required", "Allowance", "Sufficient"], + payload.allowances.map((row) => [row.spender, `${row.required} ${sourceToken}`, `${row.allowance} ${sourceToken}`, row.sufficient]), + ), + "", + "## Top-Up Quotes", + "", + table( + ["Chain", "Symbols", "Need", "Spend", "Tool", "Out", "Mainnet gas", "Tx data"], + rows.map((row) => [ + row.chainId, + row.symbols, + `${row.shortfallNative} ${row.nativeSymbol}`, + `${row.spend} ${sourceToken}`, + row.tool, + row.toAmountNative ? `${row.toAmountNative} ${row.nativeSymbol}` : "quote_failed", + row.mainnetGasCostETH ? `${row.mainnetGasCostETH} ETH` : "", + row.transactionDataPresent, + ]), + ), + "", + "## Execution Boundary", + "", + "This artifact proves route availability and bounded spend only. Before broadcast, each row still needs allowance/approval handling, final quote refresh, private-key signing, and tx submission.", +].join("\n"); + +mkdirSync(resolve(repoRoot, "reports/status"), { recursive: true }); +writeFileSync(jsonOut, `${JSON.stringify(payload, null, 2)}\n`); +writeFileSync(mdOut, `${md}\n`); +console.log(jsonOut); diff --git a/scripts/deployment/probe-dev-vm-ssh.sh b/scripts/deployment/probe-dev-vm-ssh.sh new file mode 100755 index 00000000..cd94b431 --- /dev/null +++ b/scripts/deployment/probe-dev-vm-ssh.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# Probe Dev VM SSH: LAN IP vs Cloudflare FQDN (tunnel + Access). +# See: docs/04-configuration/DEV_VM_SSH_REMOTE_ACCESS.md +# +# Usage: +# ./scripts/deployment/probe-dev-vm-ssh.sh +# DEV_VM_USER=dev1 DEV_VM_FQDN=ssh.dev.d-bis.org ./scripts/deployment/probe-dev-vm-ssh.sh + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +# shellcheck source=/dev/null +source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true + +USER_NAME="${DEV_VM_USER:-dev1}" +IP="${IP_DEV_VM:-192.168.11.59}" +FQDN="${DEV_VM_FQDN:-ssh.dev.d-bis.org}" + +echo "=== Dev VM SSH probes (user=$USER_NAME) ===" +echo "" + +echo "1) LAN: BatchMode SSH to $IP" +if ssh -o BatchMode=yes -o ConnectTimeout=8 -o StrictHostKeyChecking=accept-new "${USER_NAME}@${IP}" true 2>/dev/null; then + echo " OK ${USER_NAME}@${IP}" +else + echo " FAIL ${USER_NAME}@${IP} (no route, firewall, or key not accepted)" +fi +echo "" + +echo "2) DNS: $FQDN" +if command -v dig >/dev/null 2>&1; then + dig +short "$FQDN" A 2>/dev/null | head -3 | sed 's/^/ A: /' || true + dig +short "$FQDN" AAAA 2>/dev/null | head -2 | sed 's/^/ AAAA: /' || true +else + echo " (dig not installed; skip)" +fi +echo "" + +echo "3) Plain SSH to $FQDN:22 (usually FAILS behind Cloudflare — tunnel expects cloudflared client)" +set +e +out=$(ssh -4 -o BatchMode=yes -o ConnectTimeout=12 -o StrictHostKeyChecking=accept-new "${USER_NAME}@${FQDN}" true 2>&1) +code=$? +set -e +if [[ "$code" -eq 0 ]]; then + echo " OK (unexpected for CF tunnel host — you may be using port-forward / direct)" +else + echo " FAIL (expected for tunnel hostname): $out" +fi +echo "" + +echo "4) FQDN via cloudflared access ssh (needs cloudflared on PATH + Access policy / service token)" +PATH="$HOME/bin:$PATH" +if command -v cloudflared >/dev/null 2>&1; then + set +e + out=$(ssh -o BatchMode=yes -o ConnectTimeout=25 \ + -o ProxyCommand="cloudflared access ssh --hostname %h" \ + -o StrictHostKeyChecking=accept-new \ + "${USER_NAME}@${FQDN}" true 2>&1) + code=$? + set -e + if [[ "$code" -eq 0 ]]; then + echo " OK ProxyCommand → ${USER_NAME}@${FQDN}" + else + echo " FAIL: $out" + fi +else + echo " SKIP: cloudflared not in PATH" + echo " Install: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/" + echo " Then set CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET if using service tokens (see DEV_VM_SSH_REMOTE_ACCESS.md)." +fi +echo "" +echo "Done." diff --git a/scripts/deployment/repair-mainnet-cwusdc-usdc-univ2-canary.sh b/scripts/deployment/repair-mainnet-cwusdc-usdc-univ2-canary.sh index fb6ef617..b1b30214 100755 --- a/scripts/deployment/repair-mainnet-cwusdc-usdc-univ2-canary.sh +++ b/scripts/deployment/repair-mainnet-cwusdc-usdc-univ2-canary.sh @@ -6,6 +6,8 @@ PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" # shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/mev-protection.sh +source "${PROJECT_ROOT}/scripts/lib/mev-protection.sh" : "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}" : "${PRIVATE_KEY:?PRIVATE_KEY is required}" @@ -131,24 +133,21 @@ EOF exit 0 fi +mev_require_private_for_action "mainnet-cwusdc-usdc-univ2-canary-repair" + DEADLINE="$(( $(date +%s) + 1800 ))" -cast send "${USDC}" 'approve(address,uint256)(bool)' "${ROUTER}" "${QUOTE_IN_RAW}" \ - --private-key "${PRIVATE_KEY}" --rpc-url "${ETHEREUM_MAINNET_RPC}" +mev_cast_send "${USDC}" 'approve(address,uint256)(bool)' "${ROUTER}" "${QUOTE_IN_RAW}" -cast send "${ROUTER}" 'swapExactTokensForTokens(uint256,uint256,address[],address,uint256)' \ +mev_cast_send "${ROUTER}" 'swapExactTokensForTokens(uint256,uint256,address[],address,uint256)' \ "${QUOTE_IN_RAW}" "${MIN_BASE_OUT_RAW}" "[${USDC},${CWUSDC}]" "${SIGNER}" "${DEADLINE}" \ - --private-key "${PRIVATE_KEY}" --rpc-url "${ETHEREUM_MAINNET_RPC}" if [[ "${BALANCED_ADD_RAW}" != "0" ]]; then - cast send "${CWUSDC}" 'approve(address,uint256)(bool)' "${ROUTER}" "${BALANCED_ADD_RAW}" \ - --private-key "${PRIVATE_KEY}" --rpc-url "${ETHEREUM_MAINNET_RPC}" - cast send "${USDC}" 'approve(address,uint256)(bool)' "${ROUTER}" "${BALANCED_ADD_RAW}" \ - --private-key "${PRIVATE_KEY}" --rpc-url "${ETHEREUM_MAINNET_RPC}" - cast send "${ROUTER}" 'addLiquidity(address,address,uint256,uint256,uint256,uint256,address,uint256)' \ + mev_cast_send "${CWUSDC}" 'approve(address,uint256)(bool)' "${ROUTER}" "${BALANCED_ADD_RAW}" + mev_cast_send "${USDC}" 'approve(address,uint256)(bool)' "${ROUTER}" "${BALANCED_ADD_RAW}" + mev_cast_send "${ROUTER}" 'addLiquidity(address,address,uint256,uint256,uint256,uint256,address,uint256)' \ "${CWUSDC}" "${USDC}" "${BALANCED_ADD_RAW}" "${BALANCED_ADD_RAW}" \ "${MIN_BALANCED_ADD_RAW}" "${MIN_BALANCED_ADD_RAW}" "${SIGNER}" "${DEADLINE}" \ - --private-key "${PRIVATE_KEY}" --rpc-url "${ETHEREUM_MAINNET_RPC}" fi bash "${PROJECT_ROOT}/scripts/verify/snapshot-mainnet-cwusdc-usdc-preflight.sh" diff --git a/scripts/deployment/run-ei-matrix-cwusdc-targeted-chunked.sh b/scripts/deployment/run-ei-matrix-cwusdc-targeted-chunked.sh new file mode 100755 index 00000000..3cc90392 --- /dev/null +++ b/scripts/deployment/run-ei-matrix-cwusdc-targeted-chunked.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# Chunked continuation of send-cwusdc-ei-matrix-targeted.sh using the full topup TSV. +# Uses ei-matrix-cwusdc-targeted-last-idx.txt: next index is last+1 (TSV line last+2). +# Chooses chunk size from current ETH and gas price (never exceeds --max-chunk). +# Stops when TSV exhausted, chunk size < 1, or the targeted script exits non-zero. +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" + +MAX_CHUNK="${EI_MATRIX_TARGETED_MAX_CHUNK:-500}" +TSV="${EI_MATRIX_TARGETED_TSV:-${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-topup-amounts.tsv}" +LAST_FILE="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-targeted-last-idx.txt" +LOG="${EI_MATRIX_TARGETED_CHUNK_LOG:-${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-targeted-chunked.log}" + +# shellcheck disable=SC1091 +source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" + +[[ -f "$TSV" ]] || { echo "Missing TSV: $TSV" >&2; exit 1; } +[[ -f "$LAST_FILE" ]] || { echo "Missing progress file: $LAST_FILE" >&2; exit 1; } + +command -v cast &>/dev/null || { echo "cast required" >&2; exit 1; } + +RPC="${ETHEREUM_MAINNET_RPC:-${RPC_URL_1:-}}" +[[ -n "$RPC" ]] || { echo "ETHEREUM_MAINNET_RPC or RPC_URL_1 required" >&2; exit 1; } +[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY required" >&2; exit 1; } + +D=$(cast wallet address --private-key "$PRIVATE_KEY") +TOTAL_LINES=$(wc -l <"$TSV" | tr -d ' ') + +exec >>"$LOG" 2>&1 +echo "======== $(date -Is) chunked targeted cWUSDC start signer=$D max_chunk=$MAX_CHUNK tsv=$TSV" + +while true; do + LAST=$(tr -d '[:space:]' <"$LAST_FILE" || echo "-1") + START=$((LAST + 2)) + REM=$((TOTAL_LINES - START + 1)) + if [[ "$REM" -le 0 ]]; then + echo "======== $(date -Is) COMPLETE: all indices done (last_idx=$LAST lines=$TOTAL_LINES)" + exit 0 + fi + + ETH_WEI=$(cast balance "$D" --rpc-url "$RPC" | awk '{print $1}') + GP=$(cast gas-price --rpc-url "$RPC" | awk '{print $1}' | head -1) + CHUNK=$(python3 -c " +eth = int('${ETH_WEI}') +gp = int('${GP}') +rem = int('${REM}') +mx = int('${MAX_CHUNK}') +# ~72k gas per ERC-20 transfer + 20% headroom on fee +cost = max(1, 72000 * gp * 12 // 10) +# spend at most 85% of ETH on this chunk +n = eth * 85 // 100 // cost +chunk = max(0, min(n, rem, mx)) +print(chunk) +") + + if [[ "$CHUNK" -lt 1 ]]; then + echo "======== $(date -Is) STOP: not enough ETH for one transfer (rem=$REM wei=$ETH_WEI gp=$GP). Top up and re-run." + exit 2 + fi + + echo "-------- $(date -Is) last=$LAST start_line=$START chunk=$CHUNK rem=$REM eth_wei=$ETH_WEI gp=$GP" + + # Avoid tail|head under pipefail (SIGPIPE → exit 141). + _end=$((START + CHUNK - 1)) + sed -n "${START},${_end}p" "$TSV" >"${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-chunk.tsv" + awk '{print $1}' "${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-chunk.tsv" >"${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-chunk.idx" + + EI_MATRIX_SKIP_GAS_CHECK=1 \ + "$SCRIPT_DIR/send-cwusdc-ei-matrix-targeted.sh" \ + --amounts-tsv "${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-chunk.tsv" \ + --indices-file "${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-chunk.idx" +done diff --git a/scripts/deployment/run-engine-x-univ2-public-indexed-loop.sh b/scripts/deployment/run-engine-x-univ2-public-indexed-loop.sh new file mode 100755 index 00000000..9e851bce --- /dev/null +++ b/scripts/deployment/run-engine-x-univ2-public-indexed-loop.sh @@ -0,0 +1,169 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/mev-protection.sh +source "${PROJECT_ROOT}/scripts/lib/mev-protection.sh" + +: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}" + +CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}" +USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}" +PAIR="${MAINNET_CWUSDC_USDC_UNIV2_PAIR:-0xC28706F899266b36BC43cc072b3a921BDf2C48D9}" +ROUTER="${CHAIN_1_UNISWAP_V2_ROUTER:-0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D}" +TARGET_USDC_OUT_RAW="${TARGET_USDC_OUT_RAW:-10000}" +SLIPPAGE_BPS="${SLIPPAGE_BPS:-100}" +DEADLINE_SECONDS="${DEADLINE_SECONDS:-900}" +EXECUTE="${EXECUTE:-0}" +STAMP="${ENGINE_X_UNIV2_LOOP_STAMP:-$(date -u +%Y%m%dT%H%M%SZ)}" +OUT_JSON="${OUT_JSON:-reports/status/engine-x-univ2-public-indexed-loop-${STAMP}.json}" +LATEST_JSON="${LATEST_JSON:-reports/status/engine-x-univ2-public-indexed-loop-latest.json}" + +if [[ -n "${PRIVATE_KEY:-}" ]]; then + SIGNER="$(cast wallet address --private-key "${PRIVATE_KEY}")" +else + SIGNER="${DEPLOYER_ADDRESS:-}" +fi +if [[ -z "${SIGNER}" ]]; then + echo "Set PRIVATE_KEY or DEPLOYER_ADDRESS" >&2 + exit 1 +fi +if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then + echo "PRIVATE_KEY is required when EXECUTE=1" >&2 + exit 1 +fi + +TOKEN0="$(cast call "${PAIR}" 'token0()(address)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)" +TOKEN1="$(cast call "${PAIR}" 'token1()(address)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)" +if [[ "${TOKEN0,,}" != "${CWUSDC,,}" || "${TOKEN1,,}" != "${USDC,,}" ]]; then + echo "Configured pair is not token0=cWUSDC/token1=USDC: ${PAIR}" >&2 + exit 1 +fi + +RESERVES_BEFORE="$(cast call "${PAIR}" 'getReserves()(uint112,uint112,uint32)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | tr '\n' ' ')" +CW_BAL_BEFORE="$(cast call "${CWUSDC}" 'balanceOf(address)(uint256)' "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +USDC_BAL_BEFORE="$(cast call "${USDC}" 'balanceOf(address)(uint256)' "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +CW_ALLOW_BEFORE="$(cast call "${CWUSDC}" 'allowance(address,address)(uint256)' "${SIGNER}" "${ROUTER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +USDC_ALLOW_BEFORE="$(cast call "${USDC}" 'allowance(address,address)(uint256)' "${SIGNER}" "${ROUTER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +ETH_BEFORE="$(cast balance "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}")" +GAS_PRICE_WEI="$(cast gas-price --rpc-url "${ETHEREUM_MAINNET_RPC}")" +BLOCK_BEFORE="$(cast block-number --rpc-url "${ETHEREUM_MAINNET_RPC}")" + +CW_IN_RAW="$(cast call --json "${ROUTER}" 'getAmountsIn(uint256,address[])(uint256[])' "${TARGET_USDC_OUT_RAW}" "[${CWUSDC},${USDC}]" --rpc-url "${ETHEREUM_MAINNET_RPC}" | jq -r '.[0][0]')" +ROUNDTRIP_CW_OUT_RAW="$(cast call --json "${ROUTER}" 'getAmountsOut(uint256,address[])(uint256[])' "${TARGET_USDC_OUT_RAW}" "[${USDC},${CWUSDC}]" --rpc-url "${ETHEREUM_MAINNET_RPC}" | jq -r '.[0][-1]')" +MAX_CW_IN_RAW="$(( CW_IN_RAW * (10000 + SLIPPAGE_BPS) / 10000 + 1 ))" +MIN_CW_BACK_RAW="$(( ROUNDTRIP_CW_OUT_RAW * (10000 - SLIPPAGE_BPS) / 10000 ))" + +if (( CW_BAL_BEFORE < MAX_CW_IN_RAW )); then + echo "Insufficient cWUSDC: need ${MAX_CW_IN_RAW}, have ${CW_BAL_BEFORE}" >&2 + exit 1 +fi + +cat <"$LOCK_FILE" +if ! flock -n 9; then + echo "Another send-cwusdc-ei-matrix-multicall-batches.sh is running (lock: $LOCK_FILE)." >&2 + exit 1 +fi + +exec python3 "$PROJECT_ROOT/scripts/lib/ei_matrix_multicall3_cwusdc_batch.py" "$@" diff --git a/scripts/deployment/send-cwusdc-ei-matrix-targeted.sh b/scripts/deployment/send-cwusdc-ei-matrix-targeted.sh new file mode 100755 index 00000000..f70b56eb --- /dev/null +++ b/scripts/deployment/send-cwusdc-ei-matrix-targeted.sh @@ -0,0 +1,297 @@ +#!/usr/bin/env bash +# Targeted mainnet cWUSDC transfers to a subset of EI matrix wallets by linear index. +# Intended for triage: feed indices from run-ei-matrix-full-readiness-audit.sh gap files. +# +# Usage: +# ./scripts/deployment/send-cwusdc-ei-matrix-targeted.sh [--dry-run] --send-raw R \ +# [--indices-file PATH] [--amounts-tsv PATH] [--resume-next] [--quiet-dry-run] [--legacy] +# +# --indices-file Newline-separated linear indices (default: reports/status/ei-matrix-readiness-gaps-mainnet-indices.txt). +# Empty lines and # comments ignored. +# --send-raw R Amount (raw, 6 decimals) per wallet when not using --amounts-tsv. +# --amounts-tsv F Tab-separated: linearIndex amountRaw (must cover every index in indices file). +# --resume-next Continue from reports/status/ei-matrix-cwusdc-targeted-last-idx.txt + 1 +# (skips indices at or below last completed). +# +# Env: same as send-cwusdc-ei-matrix-wallets.sh (PRIVATE_KEY, ETHEREUM_MAINNET_RPC, CWUSDC_MAINNET, …) +# Lock: reports/status/ei-matrix-cwusdc-targeted-send.lock +# Progress: reports/status/ei-matrix-cwusdc-targeted-last-idx.txt +# Failures: reports/status/ei-matrix-cwusdc-targeted-failures.log +# +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" + +DRY_RUN=false +QUIET_DRY_RUN=false +CAST_LEGACY=false +RESUME_NEXT=false +INDICES_FILE="${EI_MATRIX_TARGETED_INDICES_FILE:-${PROJECT_ROOT}/reports/status/ei-matrix-readiness-gaps-mainnet-indices.txt}" +AMOUNTS_TSV="" +SEND_RAW="" +LAST_IDX_FILE="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-targeted-last-idx.txt" + +while [[ $# -gt 0 ]]; do + case "$1" in + --dry-run) DRY_RUN=true; shift ;; + --quiet-dry-run) QUIET_DRY_RUN=true; shift ;; + --legacy) CAST_LEGACY=true; shift ;; + --resume-next) RESUME_NEXT=true; shift ;; + --indices-file) INDICES_FILE="${2:?}"; shift 2 ;; + --amounts-tsv) AMOUNTS_TSV="${2:?}"; shift 2 ;; + --send-raw) SEND_RAW="${2:?}"; shift 2 ;; + *) echo "Unknown arg: $1" >&2; exit 1 ;; + esac +done + +if [[ -z "$SEND_RAW" && -z "$AMOUNTS_TSV" ]]; then + echo "Set --send-raw R or --amounts-tsv PATH." >&2 + exit 1 +fi +if [[ -n "$SEND_RAW" && -n "$AMOUNTS_TSV" ]]; then + echo "Use only one of --send-raw or --amounts-tsv." >&2 + exit 1 +fi + +# shellcheck disable=SC1091 +source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" + +CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}" +PUBLIC_ETH_RPC="${ETHEREUM_MAINNET_PUBLIC_RPC:-https://ethereum-rpc.publicnode.com}" +RPC="${ETHEREUM_MAINNET_RPC:-${RPC_URL_1:-${ETH_MAINNET_RPC_URL:-$PUBLIC_ETH_RPC}}}" +BALANCE_RPC="${EI_MATRIX_BALANCE_RPC:-$RPC}" +GRID="$PROJECT_ROOT/config/pmm-soak-wallet-grid.json" + +[[ -f "$INDICES_FILE" ]] || { echo "Missing indices file: $INDICES_FILE" >&2; exit 1; } +[[ -f "$GRID" ]] || { echo "Missing $GRID" >&2; exit 1; } +command -v cast &>/dev/null || { echo "cast required" >&2; exit 1; } +command -v jq &>/dev/null || { echo "jq required" >&2; exit 1; } +[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY not set" >&2; exit 1; } + +LOCK_FILE="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-targeted-send.lock" +mkdir -p "${PROJECT_ROOT}/reports/status" +exec 200>"$LOCK_FILE" +if ! flock -n 200; then + echo "Another send-cwusdc-ei-matrix-targeted.sh is already running (lock: $LOCK_FILE)." >&2 + exit 1 +fi + +FROM_ADDR=$(cast wallet address --private-key "$PRIVATE_KEY") +CHAIN_ID=$(cast chain-id --rpc-url "$RPC" 2>/dev/null | tr -d '[:space:]' || true) +[[ -n "$CHAIN_ID" ]] || CHAIN_ID="1" +if [[ "$CHAIN_ID" != "1" ]]; then + echo "[WARN] chain-id=$CHAIN_ID (expected 1)." >&2 +fi + +pending_nonce() { + local resp hex + resp=$(curl -sS -X POST "$RPC" -H "Content-Type: application/json" \ + -d "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionCount\",\"params\":[\"${FROM_ADDR}\",\"pending\"],\"id\":1}" 2>/dev/null) || return 1 + hex=$(echo "$resp" | jq -r '.result // empty') + [[ -n "$hex" ]] || return 1 + cast to-dec "$hex" +} + +token_decimals() { + cast call "$CWUSDC" 'decimals()(uint8)' --rpc-url "$BALANCE_RPC" 2>/dev/null | awk '{print $1}' +} + +token_balance_raw() { + cast call "$CWUSDC" "balanceOf(address)(uint256)" "$FROM_ADDR" --rpc-url "$BALANCE_RPC" 2>/dev/null | awk '{print $1}' +} + +ERR_LOG="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-targeted-failures.log" + +# Sorted unique indices from file (one index per line; # comments; whitespace tolerated) +IND_TMP=$(mktemp) +grep -v '^[[:space:]]*$' "$INDICES_FILE" | sed 's/#.*//' \ + | awk '{ gsub(/[[:space:]]/, "", $0); if ($0 ~ /^[0-9]+$/) print $0 }' \ + | sort -n -u >"$IND_TMP" || true + +if [[ ! -s "$IND_TMP" ]]; then + echo "No numeric indices in $INDICES_FILE — nothing to do." + rm -f "$IND_TMP" + exit 0 +fi + +if $RESUME_NEXT; then + [[ -f "$LAST_IDX_FILE" ]] || { echo "Missing $LAST_IDX_FILE for --resume-next" >&2; rm -f "$IND_TMP"; exit 1; } + _last="$(tr -d '[:space:]' < "$LAST_IDX_FILE" || echo "")" + [[ -n "$_last" ]] || { echo "Empty $LAST_IDX_FILE" >&2; rm -f "$IND_TMP"; exit 1; } + _filtered=$(mktemp) + while read -r line; do + [[ "$line" =~ ^[0-9]+$ ]] || continue + if [[ "$line" -gt "$_last" ]]; then + echo "$line" + fi + done <"$IND_TMP" >"$_filtered" + mv "$_filtered" "$IND_TMP" + echo "Resume-next: last completed idx=$_last; remaining indices: $(wc -l <"$IND_TMP" | tr -d ' ')" +fi + +N_IND=$(wc -l <"$IND_TMP" | tr -d ' ') +if [[ "$N_IND" -eq 0 ]]; then + echo "No indices to process after filters." + rm -f "$IND_TMP" + exit 0 +fi + +PAIR_TMP=$(mktemp) +cleanup() { + [[ -f "$IND_TMP" ]] && rm -f "$IND_TMP" + [[ -f "$PAIR_TMP" ]] && rm -f "$PAIR_TMP" +} +trap cleanup EXIT + +if [[ -n "$AMOUNTS_TSV" ]]; then + [[ -f "$AMOUNTS_TSV" ]] || { echo "Missing amounts TSV: $AMOUNTS_TSV" >&2; exit 1; } + # Build map idx->amount in Python for validation + join + python3 - "$IND_TMP" "$AMOUNTS_TSV" "$PAIR_TMP" <<'PY' +import sys +from pathlib import Path + +ind_path = Path(sys.argv[1]) +amt_path = Path(sys.argv[2]) +out_path = Path(sys.argv[3]) + +wanted = [int(x) for x in ind_path.read_text().split() if x.strip().isdigit()] +wanted_set = set(wanted) +amap: dict[int, int] = {} +for line in amt_path.read_text().splitlines(): + line = line.split("#", 1)[0].strip() + if not line: + continue + parts = line.split("\t") + if len(parts) < 2: + parts = line.split() + if len(parts) < 2: + print(f"Bad amounts line: {line!r}", file=sys.stderr) + sys.exit(1) + idx = int(parts[0].strip()) + raw = int(parts[1].strip()) + amap[idx] = raw + +missing = sorted(wanted_set - set(amap)) +if missing: + print(f"Amounts TSV missing {len(missing)} indices (first 20): {missing[:20]}", file=sys.stderr) + sys.exit(1) + +lines = [] +for idx in sorted(wanted_set): + lines.append(f"{idx}\t{amap[idx]}") +out_path.write_text("\n".join(lines) + "\n", encoding="utf-8") +PY + # PAIR_TMP now: idx \t raw per line sorted by idx + BUDGET_RAW=$(awk '{s+=$2} END {print s}' "$PAIR_TMP") +else + while read -r idx; do + echo -e "${idx}\t${SEND_RAW}" + done <"$IND_TMP" | sort -n -t $'\t' -k1,1 >"$PAIR_TMP" + BUDGET_RAW=$(python3 -c "print(int('$SEND_RAW') * int('$N_IND'))") +fi + +ADDR_AMT_TMP=$(mktemp) +cleanup() { + [[ -f "$IND_TMP" ]] && rm -f "$IND_TMP" + [[ -f "$PAIR_TMP" ]] && rm -f "$PAIR_TMP" + [[ -f "$ADDR_AMT_TMP" ]] && rm -f "$ADDR_AMT_TMP" +} +trap cleanup EXIT + +while IFS=$'\t' read -r idx raw_amt; do + addr=$(jq -r --argjson i "$idx" '.wallets[$i].address // empty' "$GRID") + [[ -n "$addr" && "$addr" != null ]] || { echo "No address for index $idx in grid" >&2; exit 1; } + echo -e "$addr\t$raw_amt\t$idx" +done <"$PAIR_TMP" >"$ADDR_AMT_TMP" + +DECIMALS=$(token_decimals || echo "6") +GAS_EST="${EI_MATRIX_SEND_GAS_EST:-70000}" +HEADROOM_BPS="${EI_MATRIX_GAS_HEADROOM_BPS:-10500}" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "EI matrix cWUSDC targeted transfer (mainnet)" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "RPC: $RPC" +echo "Token: $CWUSDC" +echo "Signer: $FROM_ADDR" +echo "Indices: $N_IND (from $INDICES_FILE)" +echo "Budget: $BUDGET_RAW raw total" +echo "Dry-run: $DRY_RUN" +echo "" + +ETH_WEI=$(cast balance "$FROM_ADDR" --rpc-url "$BALANCE_RPC" 2>/dev/null | awk '{print $1}' || echo "0") +TOKEN_BAL=$(token_balance_raw || echo "0") +echo "Signer ETH: $ETH_WEI wei" +echo "Signer cWUSDC (raw): $TOKEN_BAL" + +if ! $DRY_RUN && [[ "${EI_MATRIX_SKIP_BALANCE_CHECK:-}" != "1" ]]; then + if ! python3 -c "import sys; sys.exit(0 if int('$TOKEN_BAL') >= int('$BUDGET_RAW') else 1)"; then + echo "Insufficient cWUSDC for budget $BUDGET_RAW raw." >&2 + exit 1 + fi +fi + +if ! $DRY_RUN && [[ "${EI_MATRIX_SKIP_GAS_CHECK:-}" != "1" ]]; then + GAS_PRICE_WEI=$(cast gas-price --rpc-url "$RPC" 2>/dev/null | awk '{print $1}' | head -1) + [[ -n "$GAS_PRICE_WEI" ]] || GAS_PRICE_WEI=0 + MIN_WEI=$(python3 -c "c=int('$N_IND'); g=int('$GAS_EST'); p=int('$GAS_PRICE_WEI'); b=int('$HEADROOM_BPS'); print(c*g*p*b//10000)") + if ! python3 -c "import sys; sys.exit(0 if int('$ETH_WEI') >= int('$MIN_WEI') else 1)"; then + echo "Insufficient ETH for gas (need ≈ $MIN_WEI wei)." >&2 + exit 1 + fi +fi + +matrix_try_transfer() { + local addr="$1" raw_amt="$2" idx="$3" + local dec human out tx attempt=1 + dec="${DECIMALS:-6}" + [[ "$raw_amt" != "0" ]] || { echo "[skip] idx=$idx zero"; return 0; } + human=$(python3 -c "d=int('$dec'); a=int('$raw_amt'); print(f'{a / (10**d):.6f}')" 2>/dev/null || echo "$raw_amt") + if $DRY_RUN; then + if ! $QUIET_DRY_RUN; then + echo "[dry-run] idx=$idx $addr raw=$raw_amt (~$human)" + fi + return 0 + fi + local cast_extra=() + $CAST_LEGACY && cast_extra+=(--legacy) + while [[ "$attempt" -le 2 ]]; do + if out=$(cast send "$CWUSDC" "transfer(address,uint256)" "$addr" "$raw_amt" \ + --rpc-url "$RPC" --private-key "$PRIVATE_KEY" \ + --nonce "$NONCE" "${cast_extra[@]}" 2>&1); then + tx=$(echo "$out" | tail -n1) + echo "[ok] idx=$idx nonce=$NONCE $addr raw=$raw_amt tx=$tx" + sent=$((sent + 1)) + NONCE=$((NONCE + 1)) + echo "$idx" >"$LAST_IDX_FILE" + return 0 + fi + if [[ "$attempt" -eq 1 ]] && echo "$out" | grep -qi 'nonce too low'; then + NONCE=$(pending_nonce) || true + attempt=$((attempt + 1)) + continue + fi + echo "[fail] idx=$idx $out" | tee -a "$ERR_LOG" >&2 + failed=$((failed + 1)) + NONCE=$(pending_nonce) || true + return 0 + done +} + +sent=0 +failed=0 +NONCE=$(pending_nonce) || { echo "Could not read nonce" >&2; exit 1; } +echo "Starting nonce: $NONCE" +echo "" + +while IFS=$'\t' read -r addr raw_amt idx; do + matrix_try_transfer "$addr" "$raw_amt" "$idx" +done <"$ADDR_AMT_TMP" + +if $DRY_RUN; then + echo "Dry-run complete ($N_IND wallets)." +else + echo "Done. sent=$sent failed=$failed" +fi diff --git a/scripts/deployment/send-cwusdc-ei-matrix-wallets.sh b/scripts/deployment/send-cwusdc-ei-matrix-wallets.sh new file mode 100755 index 00000000..1be791e7 --- /dev/null +++ b/scripts/deployment/send-cwusdc-ei-matrix-wallets.sh @@ -0,0 +1,355 @@ +#!/usr/bin/env bash +# Transfer Ethereum mainnet cWUSDC from PRIVATE_KEY holder to each address in +# config/pmm-soak-wallet-grid.json (EI matrix slice). Uses ERC-20 transfer(address,uint256). +# +# Modes (exactly one): +# --send-raw R Same raw units sent to every wallet in the slice. +# --total-send-raw B Total sent across the slice, split with ±spread then renormalized to B. +# +# Usage: +# ./scripts/deployment/send-cwusdc-ei-matrix-wallets.sh [--dry-run] [--limit N] [--offset N|--resume-next] +# (--send-raw R | --total-send-raw B [--spread-pct S]) +# +# --quiet-dry-run With --dry-run, suppress per-wallet lines. +# --legacy Pass --legacy to cast send. +# +# Env: ETHEREUM_MAINNET_RPC, CWUSDC_MAINNET, PRIVATE_KEY, +# EI_MATRIX_SEND_GAS_EST (default 70000), EI_MATRIX_GAS_HEADROOM_BPS (default 10500), +# EI_MATRIX_SKIP_GAS_CHECK=1, EI_MATRIX_SKIP_BALANCE_CHECK=1 (operator risk). +# +# Progress: reports/status/ei-matrix-cwusdc-send-last-idx.txt +# Failures: reports/status/ei-matrix-cwusdc-send-failures.log +# Lock: reports/status/ei-matrix-cwusdc-send.lock +# +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" + +DRY_RUN=false +LIMIT="" +OFFSET="0" +OFFSET_EXPLICIT=false +RESUME_NEXT=false +SPREAD_PCT="${EI_MATRIX_SPREAD_PCT:-15}" +CAST_LEGACY=false +QUIET_DRY_RUN=false +SEND_RAW="" +TOTAL_SEND_RAW="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --dry-run) DRY_RUN=true; shift ;; + --quiet-dry-run) QUIET_DRY_RUN=true; shift ;; + --limit) LIMIT="${2:?}"; shift 2 ;; + --resume-next) RESUME_NEXT=true; shift ;; + --offset) OFFSET="${2:?}"; OFFSET_EXPLICIT=true; shift 2 ;; + --spread-pct) SPREAD_PCT="${2:?}"; shift 2 ;; + --send-raw) SEND_RAW="${2:?}"; shift 2 ;; + --total-send-raw) TOTAL_SEND_RAW="${2:?}"; shift 2 ;; + --legacy) CAST_LEGACY=true; shift ;; + *) echo "Unknown arg: $1" >&2; exit 1 ;; + esac +done + +LAST_IDX_FILE="${EI_MATRIX_CWUSDC_SEND_LAST_IDX_FILE:-${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-send-last-idx.txt}" +if $RESUME_NEXT && $OFFSET_EXPLICIT; then + echo "Use only one of --offset or --resume-next." >&2 + exit 1 +fi +if $RESUME_NEXT; then + [[ -f "$LAST_IDX_FILE" ]] || { echo "Missing last-index file for --resume-next: $LAST_IDX_FILE" >&2; exit 1; } + _last="$(tr -d '[:space:]' < "$LAST_IDX_FILE" || echo "")" + [[ -n "$_last" ]] || { echo "Empty $LAST_IDX_FILE" >&2; exit 1; } + OFFSET=$((_last + 1)) + echo "Resume-next (send): last completed idx=$_last → offset=$OFFSET" +fi + +if [[ -n "$SEND_RAW" && -n "$TOTAL_SEND_RAW" ]]; then + echo "Use only one of --send-raw or --total-send-raw." >&2 + exit 1 +fi +if [[ -z "$SEND_RAW" && -z "$TOTAL_SEND_RAW" ]]; then + echo "Set --send-raw or --total-send-raw." >&2 + exit 1 +fi + +# shellcheck disable=SC1091 +source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" + +CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}" +PUBLIC_ETH_RPC="${ETHEREUM_MAINNET_PUBLIC_RPC:-https://ethereum-rpc.publicnode.com}" +RPC="${ETHEREUM_MAINNET_RPC:-${RPC_URL_1:-${ETH_MAINNET_RPC_URL:-$PUBLIC_ETH_RPC}}}" +BALANCE_RPC="${EI_MATRIX_BALANCE_RPC:-$RPC}" + +LOCK_FILE="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-send.lock" +MANIFEST_DIR="${PROJECT_ROOT}/reports/status" +mkdir -p "$MANIFEST_DIR" +exec 200>"$LOCK_FILE" +if ! flock -n 200; then + echo "Another send-cwusdc-ei-matrix-wallets.sh is already running (lock: $LOCK_FILE)." >&2 + exit 1 +fi + +GRID="$PROJECT_ROOT/config/pmm-soak-wallet-grid.json" + +[[ -f "$GRID" ]] || { echo "Missing $GRID" >&2; exit 1; } +command -v cast &>/dev/null || { echo "cast required" >&2; exit 1; } +command -v jq &>/dev/null || { echo "jq required" >&2; exit 1; } +[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY not set" >&2; exit 1; } + +FROM_ADDR=$(cast wallet address --private-key "$PRIVATE_KEY") +CHAIN_ID=$(cast chain-id --rpc-url "$RPC" 2>/dev/null | tr -d '[:space:]' || true) +[[ -n "$CHAIN_ID" ]] || CHAIN_ID="1" +if [[ "$CHAIN_ID" != "1" ]]; then + echo "[WARN] chain-id=$CHAIN_ID (expected 1)." >&2 +fi + +pending_nonce() { + local resp hex + resp=$(curl -sS -X POST "$RPC" -H "Content-Type: application/json" \ + -d "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionCount\",\"params\":[\"${FROM_ADDR}\",\"pending\"],\"id\":1}" 2>/dev/null) || return 1 + hex=$(echo "$resp" | jq -r '.result // empty') + [[ -n "$hex" ]] || return 1 + cast to-dec "$hex" +} + +token_decimals() { + cast call "$CWUSDC" 'decimals()(uint8)' --rpc-url "$BALANCE_RPC" 2>/dev/null | awk '{print $1}' +} + +token_balance_raw() { + cast call "$CWUSDC" "balanceOf(address)(uint256)" "$FROM_ADDR" --rpc-url "$BALANCE_RPC" 2>/dev/null | awk '{print $1}' +} + +generate_spread_amounts_raw() { + local count="$1" budget="$2" spread="$3" + python3 - "$count" "$budget" "$spread" <<'PY' +import random +import sys +n = int(sys.argv[1]) +budget = int(sys.argv[2]) +spread = float(sys.argv[3]) +if n <= 0: + sys.exit("count must be positive") +if budget < 0: + sys.exit("budget must be non-negative") +if spread < 0 or spread > 100: + sys.exit("spread-pct must be in [0, 100]") +base = 10000 +low_w = max(1, (100 * base - int(spread * base)) // 100) +high_w = (100 * base + int(spread * base)) // 100 +w = [random.randint(low_w, high_w) for _ in range(n)] +s = sum(w) +raw = [(budget * wi) // s for wi in w] +rem = budget - sum(raw) +for i in range(rem): + raw[i % n] += 1 +for x in raw: + print(x) +PY +} + +stream_addresses() { + if [[ -n "${LIMIT:-}" ]]; then + jq -r --argjson o "$OFFSET" --argjson l "$LIMIT" '.wallets[$o:$o+$l][] | .address' "$GRID" + else + jq -r --argjson o "$OFFSET" '.wallets[$o:][] | .address' "$GRID" + fi +} + +ERR_LOG="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-send-failures.log" +LAST_IDX="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-send-last-idx.txt" + +matrix_try_transfer() { + local addr="$1" raw_amt="$2" idx="$3" + local dec human out tx attempt=1 + dec="${DECIMALS:-6}" + if [[ "$raw_amt" == "0" ]]; then + echo "[skip] idx=$idx $addr zero raw" + return 0 + fi + human=$(python3 -c "d=int('$dec'); a=int('$raw_amt'); print(f'{a / (10**d):.{min(d,8)}f}')" 2>/dev/null || echo "$raw_amt") + if $DRY_RUN; then + if ! $QUIET_DRY_RUN; then + echo "[dry-run] idx=$idx $addr raw=$raw_amt (~$human)" + fi + return 0 + fi + local cast_extra=() + $CAST_LEGACY && cast_extra+=(--legacy) + while [[ "$attempt" -le 2 ]]; do + if out=$(cast send "$CWUSDC" "transfer(address,uint256)" "$addr" "$raw_amt" \ + --rpc-url "$RPC" --private-key "$PRIVATE_KEY" \ + --nonce "$NONCE" "${cast_extra[@]}" 2>&1); then + tx=$(echo "$out" | tail -n1) + echo "[ok] idx=$idx nonce=$NONCE $addr raw=$raw_amt (~$human) tx=$tx" + sent=$((sent + 1)) + NONCE=$((NONCE + 1)) + echo "$idx" > "$LAST_IDX" + return 0 + fi + if [[ "$attempt" -eq 1 ]] && echo "$out" | grep -qi 'nonce too low'; then + NONCE=$(pending_nonce) || true + echo "[retry] idx=$idx nonce refreshed to $NONCE (nonce too low)" >&2 + attempt=$((attempt + 1)) + continue + fi + echo "[fail] idx=$idx nonce=$NONCE $addr $out" | tee -a "$ERR_LOG" >&2 + failed=$((failed + 1)) + NONCE=$(pending_nonce) || true + return 0 + done +} + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "EI matrix cWUSDC transfer (mainnet)" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "RPC: $RPC" +echo "Token: $CWUSDC" +echo "Signer: $FROM_ADDR" +echo "Grid: $GRID" +echo "Dry-run: $DRY_RUN Quiet: $QUIET_DRY_RUN" +echo "Offset: $OFFSET Limit: ${LIMIT:-all}" +if [[ -n "$SEND_RAW" ]]; then + echo "Mode: fixed --send-raw $SEND_RAW per wallet" +else + echo "Mode: --total-send-raw $TOTAL_SEND_RAW spread: ±${SPREAD_PCT}% normalized" +fi +echo "" + +DECIMALS=$(token_decimals || echo "6") + +ADDR_TMP=$(mktemp) +AMOUNTS_TMP=$(mktemp) +cleanup_tmp() { + [[ -f "$ADDR_TMP" ]] && rm -f "$ADDR_TMP" + [[ -f "$AMOUNTS_TMP" ]] && rm -f "$AMOUNTS_TMP" +} +trap cleanup_tmp EXIT + +stream_addresses > "$ADDR_TMP" +WALLET_COUNT=$(wc -l < "$ADDR_TMP" | tr -d '[:space:]') +if [[ -z "$WALLET_COUNT" || "$WALLET_COUNT" -eq 0 ]]; then + echo "No wallets in range (offset=$OFFSET limit=${LIMIT:-all})." >&2 + exit 1 +fi + +if [[ -n "$SEND_RAW" ]]; then + awk -v r="$SEND_RAW" '{print r}' "$ADDR_TMP" > "$AMOUNTS_TMP" + BUDGET_RAW=$((SEND_RAW * WALLET_COUNT)) +else + BUDGET_RAW="$TOTAL_SEND_RAW" + if [[ "$BUDGET_RAW" -le 0 ]]; then + echo "total-send-raw must be positive." >&2 + exit 1 + fi + generate_spread_amounts_raw "$WALLET_COUNT" "$BUDGET_RAW" "$SPREAD_PCT" > "$AMOUNTS_TMP" +fi + +SUM_CHECK=$(awk '{s+=$1} END {print s}' "$AMOUNTS_TMP") +if [[ "$SUM_CHECK" != "$BUDGET_RAW" ]]; then + echo "INTERNAL: amount sum $SUM_CHECK != budget $BUDGET_RAW" >&2 + exit 1 +fi + +AMOUNTS_SHA256=$(sha256sum "$AMOUNTS_TMP" | awk '{print $1}') +TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ") +MANIFEST="$MANIFEST_DIR/ei-matrix-cwusdc-send-manifest-${TS//:/-}.json" +cat >"$MANIFEST" </dev/null | awk '{print $1}' || echo "0") +ETH_HUMAN=$(python3 -c "print(f'{int(\"$ETH_WEI\") / 1e18:.6f}')" 2>/dev/null || echo "?") +echo "Signer ETH (gas): ${ETH_WEI} wei (~$ETH_HUMAN ETH)" + +TOKEN_BAL=$(token_balance_raw || echo "0") +echo "Signer cWUSDC balance (raw): $TOKEN_BAL (need >= $BUDGET_RAW for this slice)" + +if ! $DRY_RUN && [[ "${EI_MATRIX_SKIP_BALANCE_CHECK:-}" != "1" ]]; then + if python3 -c "import sys; sys.exit(0 if int('$TOKEN_BAL') >= int('$BUDGET_RAW') else 1)"; then + echo "Token preflight OK: balance covers budget $BUDGET_RAW raw." + else + echo "Insufficient cWUSDC: have $TOKEN_BAL raw, need $BUDGET_RAW raw." >&2 + echo "Set EI_MATRIX_SKIP_BALANCE_CHECK=1 to override (operator risk)." >&2 + exit 1 + fi +fi + +if ! $DRY_RUN && [[ "${EI_MATRIX_SKIP_GAS_CHECK:-}" != "1" ]]; then + GAS_PRICE_WEI=$(cast gas-price --rpc-url "$RPC" 2>/dev/null | awk '{print $1}' | head -1) + [[ -n "$GAS_PRICE_WEI" ]] || GAS_PRICE_WEI=0 + MIN_WEI=$(python3 -c "c=int('$WALLET_COUNT'); g=int('$GAS_EST'); p=int('$GAS_PRICE_WEI'); b=int('$HEADROOM_BPS'); print(c*g*p*b//10000)") + if python3 -c "import sys; sys.exit(0 if int('$ETH_WEI') >= int('$MIN_WEI') else 1)"; then + echo "Gas preflight OK: est ${GAS_EST} gas/tx × $WALLET_COUNT × gasPrice $GAS_PRICE_WEI × (${HEADROOM_BPS}/10000) ≈ $MIN_WEI wei." + else + echo "Insufficient ETH for gas preflight. Need ≈ $MIN_WEI wei." >&2 + echo "Set EI_MATRIX_SKIP_GAS_CHECK=1 to override (operator risk)." >&2 + exit 1 + fi +fi +echo "" + +echo "Sample transfers:" +if [[ "$WALLET_COUNT" -le 6 ]]; then + _s_idx=$OFFSET + while IFS=$'\t' read -r s_addr s_raw; do + h=$(python3 -c "d=int('$DECIMALS'); a=int('$s_raw'); print(f'{a / (10**d):.6f}')" 2>/dev/null || echo "$s_raw") + echo " idx=$_s_idx $s_addr raw=$s_raw (~$h cWUSDC)" + _s_idx=$((_s_idx + 1)) + done < <(paste -d $'\t' "$ADDR_TMP" "$AMOUNTS_TMP") +else + _s_idx=$OFFSET + while IFS=$'\t' read -r s_addr s_raw; do + h=$(python3 -c "d=int('$DECIMALS'); a=int('$s_raw'); print(f'{a / (10**d):.6f}')" 2>/dev/null || echo "$s_raw") + echo " idx=$_s_idx $s_addr raw=$s_raw (~$h cWUSDC)" + _s_idx=$((_s_idx + 1)) + done < <(paste -d $'\t' "$ADDR_TMP" "$AMOUNTS_TMP" | head -3) + _s_idx=$((OFFSET + WALLET_COUNT - 3)) + while IFS=$'\t' read -r s_addr s_raw; do + h=$(python3 -c "d=int('$DECIMALS'); a=int('$s_raw'); print(f'{a / (10**d):.6f}')" 2>/dev/null || echo "$s_raw") + echo " idx=$_s_idx $s_addr raw=$s_raw (~$h cWUSDC)" + _s_idx=$((_s_idx + 1)) + done < <(paste -d $'\t' "$ADDR_TMP" "$AMOUNTS_TMP" | tail -3) +fi +echo "" + +sent=0 +failed=0 +idx=$OFFSET +NONCE=$(pending_nonce) || { echo "Could not read pending nonce" >&2; exit 1; } +echo "Starting nonce (pending): $NONCE" +echo "" + +while IFS=$'\t' read -r addr raw_amt; do + matrix_try_transfer "$addr" "$raw_amt" "$idx" + idx=$((idx + 1)) +done < <(paste -d $'\t' "$ADDR_TMP" "$AMOUNTS_TMP") + +if $DRY_RUN; then + echo "Dry-run complete. Indices covered: $OFFSET..$((idx - 1))." +else + echo "Done. Transfer txs attempted: sent=$sent failed=$failed" +fi diff --git a/scripts/deployment/solana-transfer-native.py b/scripts/deployment/solana-transfer-native.py new file mode 100755 index 00000000..50a00676 --- /dev/null +++ b/scripts/deployment/solana-transfer-native.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +""" +Transfer native SOL (lamports) on Solana mainnet-beta (or any RPC you pass). + +Loads ``SOLANA_RPC_URL`` and ``SOLANA_KEYPAIR_PATH`` from the environment when +set (after ``source scripts/lib/load-project-env.sh``). Submits via +``solana_jsonrpc.send_transaction_wire`` (``scripts/lib/solana_jsonrpc.py``) so +RPCs that return only a signature string for ``sendTransaction`` do not hit +``solana-py``'s ``SendTransactionResp`` parser (which can panic on ``missing field 'data'``). + +Install (venv recommended):: + + pip install -r scripts/lib/requirements-solana-ops.txt +""" + +from __future__ import annotations + +import argparse +import base64 +import json +import os +import sys +from pathlib import Path + +# ``scripts/lib`` is not a Python package; load ``solana_jsonrpc`` by path. +_LIB = Path(__file__).resolve().parents[1] / "lib" +if str(_LIB) not in sys.path: + sys.path.insert(0, str(_LIB)) + +import solana_jsonrpc # noqa: E402 + + +def _load_keypair(path: Path): + with path.open() as f: + raw = json.load(f) + if not isinstance(raw, list) or len(raw) != 64: + raise SystemExit("keypair JSON must be a 64-element byte array (Solana CLI format)") + from solders.keypair import Keypair + + return Keypair.from_bytes(bytes(raw)) + + +def main() -> None: + p = argparse.ArgumentParser(description="Send native SOL via JSON-RPC (robust sendTransaction parsing).") + p.add_argument("--to", required=True, help="Destination base58 pubkey") + p.add_argument( + "--lamports", + type=int, + help="Amount to send (excludes fee; payer pays fee separately). Omit with --sweep-all", + ) + p.add_argument( + "--sweep-all", + action="store_true", + help="Send entire balance minus 5000 lamports legacy fee reserve", + ) + p.add_argument("--fee-lamports", type=int, default=5000, help="Reserved for fee when using --sweep-all") + p.add_argument("--rpc-url", default=os.environ.get("SOLANA_RPC_URL", "").strip()) + p.add_argument( + "--keypair", + type=Path, + default=Path(os.environ.get("SOLANA_KEYPAIR_PATH", "").strip() or "."), + help="Solana CLI JSON keypair path (default: SOLANA_KEYPAIR_PATH)", + ) + p.add_argument("--skip-preflight", action="store_true") + p.add_argument( + "--dry-run", + action="store_true", + help="Print base64 wire and exit without sending", + ) + p.add_argument( + "--no-wait", + action="store_true", + help="Do not poll getSignatureStatuses after send (default: wait up to 90s)", + ) + args = p.parse_args() + + try: + from solders.hash import Hash + from solders.pubkey import Pubkey + from solders.system_program import TransferParams, transfer + from solders.transaction import Transaction + except ImportError: + print( + "Missing dependency: install with\n" + " pip install -r scripts/lib/requirements-solana-ops.txt", + file=sys.stderr, + ) + raise SystemExit(2) from None + + if not args.rpc_url: + print("Set SOLANA_RPC_URL or pass --rpc-url", file=sys.stderr) + raise SystemExit(2) + if not args.keypair.is_file(): + print(f"Keypair not found: {args.keypair}", file=sys.stderr) + raise SystemExit(2) + + kp = _load_keypair(args.keypair) + dest = Pubkey.from_string(args.to) + src = kp.pubkey() + + if args.dry_run: + if args.sweep_all: + raise SystemExit("--dry-run requires explicit --lamports (no balance query)") + if args.lamports is None: + raise SystemExit("Pass --lamports N with --dry-run") + send_lamports = args.lamports + else: + bal = solana_jsonrpc.get_balance_lamports(args.rpc_url, str(src)) + if args.sweep_all: + send_lamports = bal - args.fee_lamports + if send_lamports <= 0: + raise SystemExit("Nothing to sweep after fee reserve") + elif args.lamports is not None: + send_lamports = args.lamports + if send_lamports <= 0: + raise SystemExit("--lamports must be positive") + if bal < send_lamports + args.fee_lamports: + raise SystemExit( + f"Insufficient balance: have {bal} lamports, need {send_lamports + args.fee_lamports}" + ) + else: + raise SystemExit("Pass --lamports N or --sweep-all") + + bh_str = solana_jsonrpc.get_latest_blockhash(args.rpc_url) + bh = Hash.from_string(bh_str) + ix = transfer(TransferParams(from_pubkey=src, to_pubkey=dest, lamports=send_lamports)) + tx = Transaction.new_signed_with_payer([ix], src, [kp], bh) + wire = bytes(tx) + + if args.dry_run: + print("blockhash", bh_str) + print("wire_b64", base64.b64encode(wire).decode("ascii")) + return + + sig = solana_jsonrpc.send_transaction_wire( + args.rpc_url, + wire, + skip_preflight=args.skip_preflight, + preflight_commitment="confirmed", + ) + print(sig) + if not args.no_wait: + st = solana_jsonrpc.wait_until_signature_confirmed(args.rpc_url, sig) + print("confirmationStatus", st.get("confirmationStatus"), "slot", st.get("slot"), file=sys.stderr) + + +if __name__ == "__main__": + main() diff --git a/scripts/deployment/sync-gov-portals-ct-7804-from-git.sh b/scripts/deployment/sync-gov-portals-ct-7804-from-git.sh new file mode 100755 index 00000000..70d3a8e0 --- /dev/null +++ b/scripts/deployment/sync-gov-portals-ct-7804-from-git.sh @@ -0,0 +1,127 @@ +#!/usr/bin/env bash +# Sync Gov Portals monorepo from Gitea to CT 7804 (gov-portals-dev), install deps, +# build DBIS + ICCC (and OMNL/XOM when they define a "build" script), restart systemd units. +# +# CT 7804 typically runs on r630-04 (192.168.11.14); tarball deploys omit .git, so +# in-container "git pull" is not enough — this script refreshes a local clone then +# streams the tree into the container. +# +# Usage (from proxmox repo root): +# export GITEA_TOKEN=... # or ensure it is in .env (see .env.master.example) +# bash scripts/deployment/sync-gov-portals-ct-7804-from-git.sh +# +# Options: +# --skip-fetch Use GOV_PORTALS_SOURCE as-is (no git fetch; no token required) +# --dry-run Print steps only +# +# Env: +# GOV_PORTALS_SOURCE Default: /home/intlc/projects/gov-portals-monorepo +# GOV_PORTALS_REPO_URL Default: https://gitea.d-bis.org/Gov_Web_Portals/gov-portals-monorepo.git +# GOV_PORTALS_REF Default: main +# PROXMOX_HOST / DBIS_PORTAL_PROXMOX_HOST / PROXMOX_HOST_GOV_PORTALS Default: 192.168.11.14 +# VMID_GOV_PORTALS Default: 7804 + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +# shellcheck disable=SC1090 +source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true +# shellcheck disable=SC1090 +[ -f "$PROJECT_ROOT/.env" ] && set +u && source "$PROJECT_ROOT/.env" 2>/dev/null || true && set -u + +GOV_PORTALS_SOURCE="${GOV_PORTALS_SOURCE:-/home/intlc/projects/gov-portals-monorepo}" +GOV_PORTALS_REPO_URL="${GOV_PORTALS_REPO_URL:-https://gitea.d-bis.org/Gov_Web_Portals/gov-portals-monorepo.git}" +GOV_PORTALS_REF="${GOV_PORTALS_REF:-main}" +VMID_GOV_PORTALS="${VMID_GOV_PORTALS:-7804}" +PROXMOX_HOST="${DBIS_PORTAL_PROXMOX_HOST:-${PROXMOX_HOST_GOV_PORTALS:-192.168.11.14}}" + +SKIP_FETCH=false +DRY_RUN=false +for arg in "$@"; do + [[ "$arg" == "--skip-fetch" ]] && SKIP_FETCH=true + [[ "$arg" == "--dry-run" ]] && DRY_RUN=true +done + +die() { echo "ERROR: $*" >&2; exit 1; } +log() { echo "[$(date +%H:%M:%S)] $*"; } + +[[ -d "$GOV_PORTALS_SOURCE" ]] || die "GOV_PORTALS_SOURCE is not a directory: $GOV_PORTALS_SOURCE" + +git_auth_args=() +if [[ -n "${GITEA_TOKEN:-}" ]]; then + git_auth_args=(-c "http.extraHeader=Authorization: token ${GITEA_TOKEN}") +fi + +if [[ "$SKIP_FETCH" != "true" ]]; then + [[ -d "$GOV_PORTALS_SOURCE/.git" ]] || die "Not a git clone: $GOV_PORTALS_SOURCE (use --skip-fetch to rsync only)" + if [[ ${#git_auth_args[@]} -eq 0 ]]; then + die "GITEA_TOKEN is unset. Add it to $PROJECT_ROOT/.env or run: export GITEA_TOKEN=... (Or use --skip-fetch.)" + fi + if [[ "$DRY_RUN" == "true" ]]; then + log "DRY: would git fetch $GOV_PORTALS_REF and submodule update in $GOV_PORTALS_SOURCE" + else + log "Fetching $GOV_PORTALS_REF and updating submodules in $GOV_PORTALS_SOURCE" + git -C "$GOV_PORTALS_SOURCE" "${git_auth_args[@]}" fetch origin + git -C "$GOV_PORTALS_SOURCE" reset --hard "origin/$GOV_PORTALS_REF" + git -C "$GOV_PORTALS_SOURCE" "${git_auth_args[@]}" submodule update --init --recursive --force + log "Monorepo HEAD: $(git -C "$GOV_PORTALS_SOURCE" log -1 --oneline)" + if [[ -e "$GOV_PORTALS_SOURCE/DBIS/.git" ]]; then + log "DBIS HEAD: $(git -C "$GOV_PORTALS_SOURCE/DBIS" log -1 --oneline)" + fi + fi +else + log "Skipping git fetch (--skip-fetch)" +fi + +SYNC_ID="gov-portals-ct-${VMID_GOV_PORTALS}-$(date +%s)" +REMOTE_SYNC="/tmp/$SYNC_ID" + +if [[ "$DRY_RUN" == "true" ]]; then + log "DRY: would rsync to root@$PROXMOX_HOST:$REMOTE_SYNC/ and tar into CT $VMID_GOV_PORTALS" + exit 0 +fi + +log "Rsync to $PROXMOX_HOST:$REMOTE_SYNC/" +rsync -az --delete \ + --exclude 'node_modules' --exclude '.next' --exclude '.git' \ + --exclude '*/node_modules' --exclude '*/.next' --exclude '*/.git' \ + "$GOV_PORTALS_SOURCE/" "root@$PROXMOX_HOST:$REMOTE_SYNC/" + +run_pve() { + ssh -o ConnectTimeout=20 -o StrictHostKeyChecking=accept-new "root@$PROXMOX_HOST" "$@" +} + +VMID="$VMID_GOV_PORTALS" + +run_pve "pct exec $VMID -- mkdir -p /srv/gov-portals /tmp/gov-env-7804" +for portal in DBIS ICCC; do + for f in .env .env.local .env.production; do + run_pve "pct exec $VMID -- bash -c '[ -f /srv/gov-portals/${portal}/${f} ] && cp -a /srv/gov-portals/${portal}/${f} /tmp/gov-env-7804/${portal}_${f} || true'" + done +done + +run_pve "pct exec $VMID -- bash -c 'if [ -d /srv/gov-portals ]; then find /srv/gov-portals -mindepth 1 -maxdepth 1 -exec rm -rf {} +; else mkdir -p /srv/gov-portals; fi'" + +run_pve "bash -c 'cd $REMOTE_SYNC && tar cf - . | pct exec $VMID -- tar xf - -C /srv/gov-portals'" + +for portal in DBIS ICCC; do + for f in .env .env.local .env.production; do + run_pve "pct exec $VMID -- bash -c '[ -f /tmp/gov-env-7804/${portal}_${f} ] && [ ! -f /srv/gov-portals/${portal}/${f} ] && cp -a /tmp/gov-env-7804/${portal}_${f} /srv/gov-portals/${portal}/${f} || true'" + done +done + +run_pve "pct exec $VMID -- bash -lc 'export PATH=/usr/local/bin:/usr/bin:/bin:\$PATH; cd /srv/gov-portals && (pnpm install --frozen-lockfile || pnpm install)'" + +run_pve "pct exec $VMID -- bash -lc 'export PATH=/usr/local/bin:/usr/bin:/bin:\$PATH; cd /srv/gov-portals/DBIS && pnpm run build && systemctl restart gov-portal-DBIS'" + +run_pve "pct exec $VMID -- bash -lc 'export PATH=/usr/local/bin:/usr/bin:/bin:\$PATH; cd /srv/gov-portals/ICCC && pnpm run build && systemctl restart gov-portal-ICCC'" + +run_pve "pct exec $VMID -- bash -lc 'export PATH=/usr/local/bin:/usr/bin:/bin:\$PATH; for p in OMNL XOM; do d=/srv/gov-portals/\$p; if [ -f \"\$d/package.json\" ] && grep -qF \"\\\"build\\\"\" \"\$d/package.json\" 2>/dev/null; then (cd \"\$d\" && pnpm run build && systemctl restart gov-portal-\$p) || true; fi; done'" + +run_pve "pct exec $VMID -- bash -lc 'systemctl is-active gov-portal-DBIS gov-portal-ICCC gov-portal-OMNL gov-portal-XOM || true; printf DBIS:; curl -s -o /dev/null -w %{http_code} http://127.0.0.1:3001/; echo; printf ICCC:; curl -s -o /dev/null -w %{http_code} http://127.0.0.1:3002/; echo'" + +run_pve "rm -rf $REMOTE_SYNC" +log "Removed $PROXMOX_HOST:$REMOTE_SYNC" + +log "Done. CT $VMID_GOV_PORTALS on $PROXMOX_HOST updated." diff --git a/scripts/deployment/sync-local-projects-to-dev-vm.sh b/scripts/deployment/sync-local-projects-to-dev-vm.sh new file mode 100755 index 00000000..217b63be --- /dev/null +++ b/scripts/deployment/sync-local-projects-to-dev-vm.sh @@ -0,0 +1,154 @@ +#!/usr/bin/env bash +# One-way sync: local workstation project tree → Dev VM (5700) /srv/projects +# for coordinated development over SSH (Cursor Remote-SSH, shared tree). +# +# Prerequisites: +# - CT 5700 exists, ssh dev1@IP_DEV_VM works, /srv/projects is writable (see setup-dev-vm-users-and-gitea.sh). +# - Run from a machine that has your local clone (default: ~/projects). +# +# Usage: +# ./scripts/deployment/sync-local-projects-to-dev-vm.sh --dry-run +# ./scripts/deployment/sync-local-projects-to-dev-vm.sh +# ./scripts/deployment/sync-local-projects-to-dev-vm.sh --delete-remote # mirror: remove remote files absent locally +# RSYNC_RSH='ssh -o ProxyCommand="cloudflared access ssh --hostname ssh.dev.d-bis.org"' \ +# DEV_VM_HOST=ssh.dev.d-bis.org ./scripts/deployment/sync-local-projects-to-dev-vm.sh +# +# Env: +# SOURCE — local directory (default: ~/projects) +# DEV_VM_USER — SSH user on dev VM (default: dev1) +# DEV_VM_HOST — override IP/hostname (default: IP_DEV_VM from config) +# DEV_VM_PROJECTS — remote path (default: /srv/projects) +# RSYNC_EXTRA_OPTS — extra rsync args (quoted string) +# RSYNC_RSH — passed to rsync as SSH transport (e.g. cloudflared ProxyCommand) +# +# If --delete-remote fails with rsync code 23 (Permission denied on delete), run: +# ./scripts/deployment/fix-dev-vm-srv-projects-ownership.sh +# See: docs/04-configuration/DEV_VM_WORKSTATION_MIGRATION_RUNBOOK.md + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +# shellcheck source=/dev/null +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true +# shellcheck source=/dev/null +source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true + +SOURCE="${SOURCE:-${HOME}/projects}" +DEV_VM_USER="${DEV_VM_USER:-dev1}" +DEV_VM_HOST="${DEV_VM_HOST:-${IP_DEV_VM:-192.168.11.59}}" +DEV_VM_PROJECTS="${DEV_VM_PROJECTS:-/srv/projects}" +RSYNC_EXTRA_OPTS="${RSYNC_EXTRA_OPTS:-}" + +DRY_RUN=() +DELETE_REMOTE=0 +while [[ $# -gt 0 ]]; do + case "$1" in + --dry-run) DRY_RUN=(-n); shift ;; + --delete-remote) DELETE_REMOTE=1; shift ;; + --help|-h) + sed -n '1,35p' "$0" | tail -n +2 + exit 0 + ;; + *) + echo "ERROR: unknown argument: $1 (try --help)" >&2 + exit 1 + ;; + esac +done + +if [[ ! -d "$SOURCE" ]]; then + echo "ERROR: SOURCE is not a directory: $SOURCE" >&2 + exit 1 +fi + +REMOTE="${DEV_VM_USER}@${DEV_VM_HOST}:${DEV_VM_PROJECTS}/" + +RSYNC_DELETE=() +if [[ "$DELETE_REMOTE" == "1" ]]; then + RSYNC_DELETE=(--delete-delay) + echo "WARNING: --delete-remote — files on Dev VM under DEST not present locally (after excludes) will be REMOVED." + echo "" +else + echo "Safe mode: no remote delete (omit files only on VM). Use --delete-remote to mirror (destructive)." + echo "" +fi + +echo "=== Sync local projects → Dev VM ===" +echo "SOURCE: $SOURCE" +echo "DEST: $REMOTE" +echo "Mode: ${DRY_RUN[*]:-live}" +if [[ -n "${RSYNC_RSH:-}" ]]; then + echo "RSYNC_RSH: set (custom SSH / cloudflared)" +fi +echo "" +echo "NOTE: Review secrets after sync — chmod 600 remote .env; share only with trusted dev users." +echo "" + +# Heavy / reproducible artifacts (keep .git; omit bulky caches) +RSYNC_EXCLUDES=( + --exclude=node_modules + --exclude=__pycache__ + --exclude=.pnpm-store + --exclude=.pnpm + --exclude=venv + --exclude=.venv + --exclude=.venv-* + --exclude=dist + --exclude=build + --exclude=.next + --exclude=out + --exclude=.turbo + --exclude=.cache + --exclude=.parcel-cache + --exclude=coverage + --exclude=.pytest_cache + --exclude=.mypy_cache + --exclude=.ruff_cache + --exclude=forge-cache + --exclude=artifacts + --exclude=broadcast + --exclude=tmp + --exclude=.tmp + --exclude=.codex-artifacts +) + +# Optional: skip known multi-GB trees (re-clone or sync later with --no-skip-large) +if [[ "${SKIP_LARGE_LOCAL_TREES:-1}" == "1" ]]; then + RSYNC_EXCLUDES+=( + --exclude=MEV_Bot + --exclude=the-order + ) + echo "SKIP_LARGE_LOCAL_TREES=1: excluding MEV_Bot, the-order (set SKIP_LARGE_LOCAL_TREES=0 to include)." + echo "" +fi + +if [[ -n "${RSYNC_RSH:-}" ]]; then + export RSYNC_RSH +else + unset RSYNC_RSH 2>/dev/null || true +fi + +set -x +# shellcheck disable=SC2086 +set +e +rsync -av "${DRY_RUN[@]}" "${RSYNC_DELETE[@]}" --omit-dir-times \ + "${RSYNC_EXCLUDES[@]}" \ + ${RSYNC_EXTRA_OPTS} \ + "${SOURCE}/" "${REMOTE}" +rsync_ec=$? +set -e +set +x + +if [[ "$rsync_ec" -ne 0 ]]; then + echo "" >&2 + echo "ERROR: rsync exited with code $rsync_ec." >&2 + if [[ "$rsync_ec" -eq 23 && "$DELETE_REMOTE" == "1" ]]; then + echo " Common cause: root-owned files on the VM block deletes. From repo root (Proxmox root SSH to the node that runs CT 5700):" >&2 + echo " ./scripts/deployment/fix-dev-vm-srv-projects-ownership.sh" >&2 + fi + exit "$rsync_ec" +fi + +echo "" +echo "Done. Next: SSH to dev VM, open /srv/projects/proxmox in Cursor (Remote-SSH), run pnpm/npm install where needed." diff --git a/scripts/dev-vm/act-runner-resource-snapshot.sh b/scripts/dev-vm/act-runner-resource-snapshot.sh new file mode 100755 index 00000000..1cc65b28 --- /dev/null +++ b/scripts/dev-vm/act-runner-resource-snapshot.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# Quick RAM / Docker snapshot for act_runner CTs (5700 + 5701). Host loadavg inside LXCs +# tracks the Proxmox host — use docker stats for job CPU when containers are running. +# +# Usage: +# bash scripts/dev-vm/act-runner-resource-snapshot.sh +# +# Env: +# PROXMOX_HOST_R630_04 — default 192.168.11.14 + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +[[ -f "${PROJECT_ROOT}/config/ip-addresses.conf" ]] && source "${PROJECT_ROOT}/config/ip-addresses.conf" + +PVE="${PROXMOX_HOST_R630_04:-192.168.11.14}" + +print_ct() { + local vmid="$1" + echo "=== CT ${vmid} ===" + ssh -o BatchMode=yes -o ConnectTimeout=12 "root@${PVE}" "pct exec ${vmid} -- bash -lc 'hostname; nproc; free -h | head -2; echo loadavg: \$(cat /proc/loadavg); systemctl show act-runner -p MemoryCurrent -p CPUUsageNSec --no-pager 2>/dev/null || true; docker stats --no-stream 2>/dev/null | head -12 || echo \"(no docker stats / no containers)\"'" + echo "" +} + +print_ct 5700 +print_ct 5701 diff --git a/scripts/dev-vm/apply-act-runner-config.sh b/scripts/dev-vm/apply-act-runner-config.sh new file mode 100755 index 00000000..bb5ce28d --- /dev/null +++ b/scripts/dev-vm/apply-act-runner-config.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Copy repo-managed act_runner YAML to CT 5700 / 5701 and restart services. +# Requires SSH to the Proxmox node that hosts both CTs (default r630-04). +# +# Usage (repo root): +# bash scripts/dev-vm/apply-act-runner-config.sh +# +# Env: +# PROXMOX_HOST_R630_04 — override Proxmox host IP (default 192.168.11.14) + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +[[ -f "${PROJECT_ROOT}/config/ip-addresses.conf" ]] && source "${PROJECT_ROOT}/config/ip-addresses.conf" + +PVE="${PROXMOX_HOST_R630_04:-192.168.11.14}" +CFG_DIR="${PROJECT_ROOT}/config/gitea-act-runner" + +ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PVE}" "pct exec 5700 -- mkdir -p /etc/act_runner" +ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PVE}" "pct exec 5701 -- mkdir -p /etc/act_runner" + +ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PVE}" "pct exec 5700 -- bash -c 'cat > /etc/act_runner/config.yaml'" < "${CFG_DIR}/config-5700-heavy.yaml" +ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PVE}" "pct exec 5701 -- bash -c 'cat > /etc/act_runner/config.yaml'" < "${CFG_DIR}/config-5701-standard.yaml" + +ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PVE}" \ + "pct exec 5700 -- systemctl restart act-runner && pct exec 5701 -- systemctl restart act-runner" + +echo "Applied templates and restarted act-runner on 5700 and 5701 (${PVE})." diff --git a/scripts/dev-vm/bootstrap-gitea-act-runner-secondary-lan.sh b/scripts/dev-vm/bootstrap-gitea-act-runner-secondary-lan.sh new file mode 100755 index 00000000..63e08062 --- /dev/null +++ b/scripts/dev-vm/bootstrap-gitea-act-runner-secondary-lan.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Standard runner on CT 5701 (gitea-runner-1): ubuntu-latest / 22.04 / 20.04 for org-wide jobs. +# Gitea HTTP is on dev-vm — use LAN URL from inside 5701. +# +# Usage (repo root with GITEA_TOKEN in .env): +# bash scripts/dev-vm/bootstrap-gitea-act-runner-secondary-lan.sh + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +[[ -f "${PROJECT_ROOT}/config/ip-addresses.conf" ]] && source "${PROJECT_ROOT}/config/ip-addresses.conf" + +IP_DEV="${IP_DEV_VM:-192.168.11.59}" + +export DEV_VM_VMID="${DEV_VM_VMID:-5701}" +export GITEA_RUNNER_INSTANCE="${GITEA_RUNNER_INSTANCE:-http://${IP_DEV}:3000}" +export RUNNER_LABELS="${RUNNER_LABELS:-ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest,ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04,ubuntu-20.04:docker://docker.gitea.com/runner-images:ubuntu-20.04}" + +exec bash "${SCRIPT_DIR}/bootstrap-gitea-act-runner.sh" diff --git a/scripts/dev-vm/bootstrap-gitea-act-runner-site-wide.sh b/scripts/dev-vm/bootstrap-gitea-act-runner-site-wide.sh index 46df3661..91f3d90e 100755 --- a/scripts/dev-vm/bootstrap-gitea-act-runner-site-wide.sh +++ b/scripts/dev-vm/bootstrap-gitea-act-runner-site-wide.sh @@ -1,70 +1,14 @@ #!/usr/bin/env bash -# Site-wide Gitea Actions runner: use admin GITEA_TOKEN from root .env to fetch the -# instance registration token, then register act_runner on dev-vm (5700) with ubuntu-latest. -# -# Requires: SSH to Proxmox (BatchMode), CT 5700 running Gitea + act_runner under /opt/act_runner. -# Env (from .env via load-project-env): GITEA_TOKEN, optional GITEA_URL, RUNNER_LABELS, -# RUNNER_FORCE_REREGISTER=1 to drop .runner and re-register, DEV_VM_VMID (default 5700). -# -# Usage (repo root): -# bash scripts/dev-vm/bootstrap-gitea-act-runner-site-wide.sh -# RUNNER_FORCE_REREGISTER=1 bash scripts/dev-vm/bootstrap-gitea-act-runner-site-wide.sh +# Heavy runner on CT 5700 (dev-vm): registers ubuntu-latest-heavy for monorepo / validation workflows. +# See scripts/dev-vm/bootstrap-gitea-act-runner.sh set -euo pipefail - SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" -# Load only root .env + IPs (avoid full load-project-env if another dotenv exits non-zero under set -e). -[[ -f "${PROJECT_ROOT}/.env" ]] && set -a && source "${PROJECT_ROOT}/.env" && set +a [[ -f "${PROJECT_ROOT}/config/ip-addresses.conf" ]] && source "${PROJECT_ROOT}/config/ip-addresses.conf" -PROXMOX_HOST_R630_01="${PROXMOX_R630_01:-${PROXMOX_HOST_R630_01:-192.168.11.11}}" -PROXMOX_HOST_R630_02="${PROXMOX_R630_02:-${PROXMOX_HOST_R630_02:-192.168.11.12}}" -PROXMOX_HOST_ML110="${PROXMOX_ML110:-${PROXMOX_HOST_ML110:-192.168.11.10}}" -get_host_for_vmid() { - case "$1" in - 5000|5700|7810|2201|2303|2401|6200|6201|10234|10237|5800|5801) echo "${PROXMOX_HOST_R630_02}";; - 5400|5401|5402|5403|5410|5411|5412|5413|5414|5415|5416|5417|5418|5419|5420|5421|5422|5423|5424|5425|5440|5441|5442|5443|5444|5445|5446|5447|5448|5449|5450|5451|5452|5453|5454|5455|5470|5471|5472|5473|5474|5475|5476) echo "${PROXMOX_HOST_R630_02}";; - 2101|10130|10150|10151|106|107|108|10000|10001|10020|10100|10101|10120|10233|10235) echo "${PROXMOX_HOST_R630_01}";; - 2301|2400|1504|2503|2504|2505) echo "${PROXMOX_HOST_ML110}";; - *) echo "${PROXMOX_HOST_R630_01}";; - esac -} -GITEA_URL="${GITEA_URL:-https://gitea.d-bis.org}" -GITEA_URL="${GITEA_URL%/}" -VMID="${DEV_VM_VMID:-5700}" -RUNNER_LABELS="${RUNNER_LABELS:-ubuntu-latest}" +export DEV_VM_VMID="${DEV_VM_VMID:-5700}" +export GITEA_RUNNER_INSTANCE="${GITEA_RUNNER_INSTANCE:-http://127.0.0.1:3000}" +export RUNNER_LABELS="${RUNNER_LABELS:-ubuntu-latest-heavy:docker://docker.gitea.com/runner-images:ubuntu-latest}" -if [[ -z "${GITEA_TOKEN:-}" ]]; then - echo "ERROR: GITEA_TOKEN not set (root .env)." >&2 - exit 1 -fi - -REG_JSON="$(curl -sS -H "Authorization: token ${GITEA_TOKEN}" \ - "${GITEA_URL}/api/v1/admin/runners/registration-token")" -REG_TOKEN="$(printf '%s' "$REG_JSON" | sed -n 's/.*"token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')" -if [[ -z "$REG_TOKEN" || "$REG_TOKEN" == "null" ]]; then - echo "ERROR: Could not get admin registration token. Response:" >&2 - printf '%s\n' "$REG_JSON" >&2 - echo "Ensure GITEA_TOKEN is an admin token with access to GET /api/v1/admin/runners/registration-token" >&2 - exit 1 -fi - -PROXMOX_HOST="$(get_host_for_vmid "$VMID")" -echo "Using Proxmox host ${PROXMOX_HOST} for VMID ${VMID}." - -if [[ "${RUNNER_FORCE_REREGISTER:-0}" == "1" ]]; then - ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PROXMOX_HOST}" \ - "pct exec ${VMID} -- bash -lc 'rm -f /opt/act_runner/.runner; systemctl stop act-runner 2>/dev/null || true'" -fi - -# Pass registration token into the container without embedding raw secret in ssh argv (still reversible from b64). -TB64="$(printf '%s' "$REG_TOKEN" | base64 | tr -d '\n')" -ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PROXMOX_HOST}" \ - "pct exec ${VMID} -- bash -c 'export GITEA_RUNNER_REGISTRATION_TOKEN=\$(printf %s \"${TB64}\" | base64 -d); export RUNNER_LABELS=\"${RUNNER_LABELS}\"; bash -s'" \ - < "${SCRIPT_DIR}/setup-act-runner.sh" - -ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PROXMOX_HOST}" \ - "pct exec ${VMID} -- bash -s" < "${SCRIPT_DIR}/install-act-runner-systemd.sh" - -echo "Done. Check Gitea Admin → Actions → Runners for an online runner with labels including: ${RUNNER_LABELS}" +exec bash "${SCRIPT_DIR}/bootstrap-gitea-act-runner.sh" diff --git a/scripts/dev-vm/bootstrap-gitea-act-runner.sh b/scripts/dev-vm/bootstrap-gitea-act-runner.sh new file mode 100755 index 00000000..0eb9c405 --- /dev/null +++ b/scripts/dev-vm/bootstrap-gitea-act-runner.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# Register/re-register Gitea act_runner on a Proxmox LXC (5700 heavy pool or 5701 standard pool). +# +# Env (required unless noted): +# GITEA_TOKEN — admin token (root .env) +# DEV_VM_VMID — default 5700 +# GITEA_RUNNER_INSTANCE — URL passed to act_runner register --instance (5700: http://127.0.0.1:3000) +# RUNNER_LABELS — comma-separated labels (docker image refs); default set by wrappers +# Optional: +# GITEA_URL — default https://gitea.d-bis.org +# RUNNER_FORCE_REREGISTER=1 — remove .runner before register +# +# Usage: +# bash scripts/dev-vm/bootstrap-gitea-act-runner-site-wide.sh +# bash scripts/dev-vm/bootstrap-gitea-act-runner-secondary-lan.sh + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +[[ -f "${PROJECT_ROOT}/.env" ]] && set -a && source "${PROJECT_ROOT}/.env" && set +a +[[ -f "${PROJECT_ROOT}/config/ip-addresses.conf" ]] && source "${PROJECT_ROOT}/config/ip-addresses.conf" + +PROXMOX_HOST_R630_04="${PROXMOX_HOST_R630_04:-192.168.11.14}" +PROXMOX_HOST_R630_01="${PROXMOX_R630_01:-${PROXMOX_HOST_R630_01:-192.168.11.11}}" +PROXMOX_HOST_R630_02="${PROXMOX_R630_02:-${PROXMOX_HOST_R630_02:-192.168.11.12}}" + +get_host_for_vmid() { + local vmid="$1" + case "$vmid" in + 5700|5701) echo "${PROXMOX_HOST_R630_04}";; + 5000|7810|2201|2303|2401|6200|6201|10234|10237|5800|5801) echo "${PROXMOX_HOST_R630_02}";; + *) echo "${PROXMOX_HOST_R630_01}";; + esac +} + +GITEA_URL="${GITEA_URL:-https://gitea.d-bis.org}" +GITEA_URL="${GITEA_URL%/}" +VMID="${DEV_VM_VMID:-5700}" +GITEA_RUNNER_INSTANCE="${GITEA_RUNNER_INSTANCE:-http://127.0.0.1:3000}" + +if [[ -z "${RUNNER_LABELS:-}" ]]; then + echo "ERROR: RUNNER_LABELS must be set (use a wrapper script or export explicitly)." >&2 + exit 1 +fi + +if [[ -z "${GITEA_TOKEN:-}" ]]; then + echo "ERROR: GITEA_TOKEN not set (root .env)." >&2 + exit 1 +fi + +REG_JSON="$(curl -sS -H "Authorization: token ${GITEA_TOKEN}" \ + "${GITEA_URL}/api/v1/admin/runners/registration-token")" +REG_TOKEN="$(printf '%s' "$REG_JSON" | sed -n 's/.*"token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')" +if [[ -z "$REG_TOKEN" || "$REG_TOKEN" == "null" ]]; then + echo "ERROR: Could not get admin registration token. Response:" >&2 + printf '%s\n' "$REG_JSON" >&2 + exit 1 +fi + +PROXMOX_HOST="$(get_host_for_vmid "$VMID")" +echo "Using Proxmox host ${PROXMOX_HOST} for VMID ${VMID}." + +TB64="$(printf '%s' "$REG_TOKEN" | base64 | tr -d '\n')" +LB64="$(printf '%s' "$RUNNER_LABELS" | base64 | tr -d '\n')" +IB64="$(printf '%s' "$GITEA_RUNNER_INSTANCE" | base64 | tr -d '\n')" + +if [[ "${RUNNER_FORCE_REREGISTER:-0}" == "1" ]]; then + ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PROXMOX_HOST}" \ + "pct exec ${VMID} -- bash -lc 'rm -f /opt/act_runner/.runner; systemctl stop act-runner 2>/dev/null || true'" +fi + +ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PROXMOX_HOST}" \ + "pct exec ${VMID} -- bash -c 'export GITEA_RUNNER_REGISTRATION_TOKEN=\$(printf %s \"${TB64}\" | base64 -d); export RUNNER_LABELS=\$(printf %s \"${LB64}\" | base64 -d); export INSTANCE=\$(printf %s \"${IB64}\" | base64 -d); bash -s'" \ + < "${SCRIPT_DIR}/setup-act-runner.sh" + +ACT_RUNNER_CONFIG="${ACT_RUNNER_CONFIG:-/etc/act_runner/config.yaml}" +ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PROXMOX_HOST}" \ + "pct exec ${VMID} -- env ACT_RUNNER_CONFIG=${ACT_RUNNER_CONFIG} GITEA_ACTION_URL=${GITEA_RUNNER_INSTANCE} bash -s" \ + < "${SCRIPT_DIR}/install-act-runner-systemd.sh" + +echo "Done. VMID ${VMID} — labels: ${RUNNER_LABELS}" diff --git a/scripts/dev-vm/delete-offline-gitea-actions-runners.sh b/scripts/dev-vm/delete-offline-gitea-actions-runners.sh new file mode 100755 index 00000000..2cb6b428 --- /dev/null +++ b/scripts/dev-vm/delete-offline-gitea-actions-runners.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# Delete Gitea Actions runners that are **offline** (stale rows after re-register). +# Uses Admin API — requires GITEA_TOKEN (admin) in repo root .env. +# +# Usage (repo root): +# bash scripts/dev-vm/delete-offline-gitea-actions-runners.sh --dry-run +# bash scripts/dev-vm/delete-offline-gitea-actions-runners.sh --apply +# +# Over SSH (from a host with this repo and .env): +# ssh user@workstation 'cd /path/to/proxmox && bash scripts/dev-vm/delete-offline-gitea-actions-runners.sh --apply' + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +[[ -f "${PROJECT_ROOT}/.env" ]] && set -a && source "${PROJECT_ROOT}/.env" && set +a + +GITEA_URL="${GITEA_URL:-https://gitea.d-bis.org}" +GITEA_URL="${GITEA_URL%/}" +MODE="${1:-}" + +if [[ -z "${GITEA_TOKEN:-}" ]]; then + echo "ERROR: GITEA_TOKEN not set (root .env)." >&2 + exit 1 +fi + +if [[ "$MODE" != "--dry-run" && "$MODE" != "--apply" ]]; then + echo "Usage: $0 --dry-run | --apply" >&2 + exit 1 +fi + +export GITEA_URL GITEA_TOKEN +export DELETE_MODE="$MODE" + +python3 <<'PY' +import json, os, sys, urllib.request + +base = os.environ["GITEA_URL"].rstrip("/") +token = os.environ["GITEA_TOKEN"] +mode = os.environ["DELETE_MODE"] + +req = urllib.request.Request( + f"{base}/api/v1/admin/actions/runners?limit=100", + headers={"Authorization": f"token {token}"}, +) +with urllib.request.urlopen(req, timeout=60) as resp: + data = json.loads(resp.read().decode()) + +runners = data.get("runners") or [] +offline = [r for r in runners if r.get("status") == "offline"] + +if not offline: + print("No offline runners.") + sys.exit(0) + +for r in offline: + print(f"offline id={r.get('id')} name={r.get('name')!r}") + +if mode == "--dry-run": + print("--dry-run: no DELETE issued.") + sys.exit(0) + +for r in offline: + rid = r["id"] + dreq = urllib.request.Request( + f"{base}/api/v1/admin/actions/runners/{rid}", + method="DELETE", + headers={"Authorization": f"token {token}"}, + ) + with urllib.request.urlopen(dreq, timeout=60) as resp: + print(f"DELETE runner id={rid} -> HTTP {resp.status}") +PY diff --git a/scripts/dev-vm/install-act-runner-systemd.sh b/scripts/dev-vm/install-act-runner-systemd.sh index e3b68158..5842d983 100755 --- a/scripts/dev-vm/install-act-runner-systemd.sh +++ b/scripts/dev-vm/install-act-runner-systemd.sh @@ -10,6 +10,8 @@ set -euo pipefail WORK_DIR="${WORK_DIR:-/opt/act_runner}" GITEA_ACTION_URL="${GITEA_ACTION_URL:-http://127.0.0.1:3000}" +ACT_RUNNER_CONFIG="${ACT_RUNNER_CONFIG:-/etc/act_runner/config.yaml}" +mkdir -p "$(dirname "${ACT_RUNNER_CONFIG}")" if [ ! -x "${WORK_DIR}/act_runner" ]; then echo "Missing ${WORK_DIR}/act_runner — run setup-act-runner.sh with GITEA_RUNNER_REGISTRATION_TOKEN first." @@ -30,7 +32,7 @@ After=network.target Type=simple User=root WorkingDirectory=${WORK_DIR} -ExecStart=${WORK_DIR}/act_runner daemon +ExecStart=${WORK_DIR}/act_runner daemon -c ${ACT_RUNNER_CONFIG} Restart=on-failure RestartSec=10 Environment=GITEA_ACTION_URL=${GITEA_ACTION_URL} diff --git a/scripts/dev-vm/setup-act-runner.sh b/scripts/dev-vm/setup-act-runner.sh index 34a0b20a..4c8b3599 100644 --- a/scripts/dev-vm/setup-act-runner.sh +++ b/scripts/dev-vm/setup-act-runner.sh @@ -7,10 +7,10 @@ set -euo pipefail ACT_RUNNER_VERSION="${ACT_RUNNER_VERSION:-0.2.13}" # Gitea root URL as seen from this host (same LXC as Gitea → 127.0.0.1) -INSTANCE="${INSTANCE:-http://127.0.0.1:3000}" +INSTANCE="${INSTANCE:-${GITEA_RUNNER_INSTANCE:-http://127.0.0.1:3000}}" WORK_DIR="${WORK_DIR:-/opt/act_runner}" TOKEN="${GITEA_RUNNER_REGISTRATION_TOKEN:-}" -# Workflows commonly use runs-on: ubuntu-latest; labels must match. +# Labels must match workflow runs-on (e.g. ubuntu-latest or ubuntu-latest-heavy); comma-separated. RUNNER_LABELS="${RUNNER_LABELS:-ubuntu-latest}" if [ -z "$TOKEN" ]; then diff --git a/scripts/fix-blockscout-forge-verification.sh b/scripts/fix-blockscout-forge-verification.sh index 5aea7f79..d5e801c5 100755 --- a/scripts/fix-blockscout-forge-verification.sh +++ b/scripts/fix-blockscout-forge-verification.sh @@ -44,6 +44,6 @@ ENDSSH echo "" echo "Verifier URL: http://${IP}/api/" echo "If forge still fails with 'module and action required', use manual verification:" -echo " https://explorer.d-bis.org/address/#verify-contract" +echo " https://explorer.d-bis.org/addresses/#verify-contract" echo "" echo "Test: source smom-dbis-138/.env && ./scripts/verify-contracts-blockscout.sh" diff --git a/scripts/health/check-rpc-vms-health.sh b/scripts/health/check-rpc-vms-health.sh old mode 100644 new mode 100755 diff --git a/scripts/lib/ei_matrix_multicall3_cwusdc_batch.py b/scripts/lib/ei_matrix_multicall3_cwusdc_batch.py new file mode 100644 index 00000000..240088fc --- /dev/null +++ b/scripts/lib/ei_matrix_multicall3_cwusdc_batch.py @@ -0,0 +1,246 @@ +#!/usr/bin/env python3 +""" +Batch mainnet cWUSDC to EI matrix wallets via canonical Multicall3 aggregate3. + +Each inner call is transferFrom(deployer, recipient, amount) on the token, so +msg.sender is Multicall3. Requires a prior approve(deployer -> Multicall3) for +at least the sum of amounts in this run (one tx before batches). + +Default Multicall3 (Ethereum): 0xcA11bde05977b3631167028862bE2a173976CA11 + +Examples: + python3 scripts/lib/ei_matrix_multicall3_cwusdc_batch.py --dry-run \\ + --tsv reports/status/ei-matrix-cwusdc-topup-amounts.tsv + + python3 scripts/lib/ei_matrix_multicall3_cwusdc_batch.py --execute \\ + --tsv reports/status/ei-matrix-cwusdc-topup-amounts.tsv + +Env: PRIVATE_KEY (or DEPLOYER_ADDRESS for dry-run calldata only), ETHEREUM_MAINNET_RPC, + CWUSDC_MAINNET (optional), MULTICALL3_MAINNET (optional), EI_MATRIX_MC_CHUNK (default 200). +""" +from __future__ import annotations + +import argparse +import json +import os +import subprocess +import sys +import time +from pathlib import Path + +MULTICALL3_MAINNET = "0xcA11bde05977b3631167028862bE2a173976CA11" +DEFAULT_CWUSDC = "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a" + + +def _sh(cmd: list[str]) -> str: + r = subprocess.run(cmd, capture_output=True, text=True, check=False) + if r.returncode != 0: + raise RuntimeError(f"command failed: {' '.join(cmd)}\n{(r.stderr or r.stdout).strip()}") + return (r.stdout or "").strip() + + +def _deployer(pk: str | None) -> str: + if pk: + return _sh(["cast", "wallet", "address", "--private-key", pk]) + env = (os.environ.get("DEPLOYER_ADDRESS") or os.environ.get("DEPLOYER") or "").strip() + if env: + return env + raise SystemExit("Set PRIVATE_KEY or DEPLOYER_ADDRESS for transferFrom(from=...)") + + +def _cast_calldata_transfer_from(from_addr: str, to_addr: str, amount: int) -> str: + out = _sh(["cast", "calldata", "transferFrom(address,address,uint256)", from_addr, to_addr, str(amount)]) + return out if out.startswith("0x") else "0x" + out + + +def _cast_calldata_aggregate3(calls_tuple_str: str) -> str: + out = _sh(["cast", "calldata", "aggregate3((address,bool,bytes)[])", calls_tuple_str]) + return out if out.startswith("0x") else "0x" + out + + +def _estimate_gas(from_addr: str, multicall: str, data: str, rpc_url: str) -> int: + payload = json.dumps({"from": from_addr, "to": multicall, "data": data}) + raw = _sh(["cast", "rpc", "eth_estimateGas", payload, "--rpc-url", rpc_url]) + return int(raw, 16) + + +def _allowance(token: str, owner: str, spender: str, rpc_url: str) -> int: + out = _sh(["cast", "call", token, "allowance(address,address)(uint256)", owner, spender, "--rpc-url", rpc_url]) + return int(out.split()[0], 0) + + +def _send_cast_send(to: str, sig: str, args: list[str], rpc_url: str, pk: str, gas_limit: str | None) -> None: + cmd = ["cast", "send", to, sig, *args, "--rpc-url", rpc_url, "--private-key", pk] + if gas_limit: + cmd.extend(["--gas-limit", gas_limit]) + print("→", " ".join(cmd[:8]), "…", file=sys.stderr) + r = subprocess.run(cmd, env={**os.environ}) + if r.returncode != 0: + sys.exit(r.returncode) + + +def _send_raw_calldata(to: str, data: str, rpc_url: str, pk: str, gas_limit: str) -> None: + cmd = ["cast", "send", to, data, "--rpc-url", rpc_url, "--private-key", pk, "--gas-limit", gas_limit] + print("→ cast send", to[:10] + "…", "--gas-limit", gas_limit, file=sys.stderr) + r = subprocess.run(cmd, env={**os.environ}) + if r.returncode != 0: + sys.exit(r.returncode) + + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("--tsv", required=True, help="linearIndex TAB amountRaw") + ap.add_argument("--grid", default="config/pmm-soak-wallet-grid.json") + ap.add_argument("--chunk-size", type=int, default=int(os.environ.get("EI_MATRIX_MC_CHUNK", "200"))) + ap.add_argument("--multicall", default=os.environ.get("MULTICALL3_MAINNET", MULTICALL3_MAINNET)) + ap.add_argument("--token", default=os.environ.get("CWUSDC_MAINNET", DEFAULT_CWUSDC)) + ap.add_argument("--rpc-url", default=os.environ.get("ETHEREUM_MAINNET_RPC") or os.environ.get("RPC_URL_1") or "") + ap.add_argument("--dry-run", action="store_true") + ap.add_argument("--execute", action="store_true") + ap.add_argument("--gas-headroom-bps", type=int, default=13000) + ap.add_argument("--min-gas-per-batch", type=int, default=500_000) + ap.add_argument("--start-batch", type=int, default=0) + ap.add_argument("--max-batches", type=int, default=0, help="0 = all remaining") + ap.add_argument("--progress-file", default="reports/status/ei-matrix-multicall3-batch-progress.txt") + args = ap.parse_args() + + if not args.rpc_url: + print("Need --rpc-url or ETHEREUM_MAINNET_RPC / RPC_URL_1", file=sys.stderr) + return 2 + if args.dry_run == args.execute: + print("Specify exactly one of --dry-run or --execute", file=sys.stderr) + return 2 + + repo = Path(__file__).resolve().parents[2] + grid_path = repo / args.grid if not os.path.isabs(args.grid) else Path(args.grid) + tsv_path = repo / args.tsv if not os.path.isabs(args.tsv) else Path(args.tsv) + + wallets = json.loads(grid_path.read_text(encoding="utf-8"))["wallets"] + rows: list[tuple[str, int]] = [] + for line in tsv_path.read_text(encoding="utf-8").splitlines(): + line = line.split("#", 1)[0].strip() + if not line: + continue + parts = line.split("\t") + if len(parts) < 2: + parts = line.split() + if len(parts) < 2: + continue + idx = int(parts[0]) + amt = int(parts[1]) + if amt <= 0: + continue + addr = wallets[idx]["address"] + rows.append((addr, amt)) + + if not rows: + print("No positive-amount rows in TSV.", file=sys.stderr) + return 0 + + pk = os.environ.get("PRIVATE_KEY", "").strip() or None + if args.execute and not pk: + print("PRIVATE_KEY required for --execute", file=sys.stderr) + return 2 + + deployer = _deployer(pk) + + mc = args.multicall + token = args.token + + all_chunks: list[list[tuple[str, int]]] = [] + for i in range(0, len(rows), args.chunk_size): + all_chunks.append(rows[i : i + args.chunk_size]) + + start_b = max(0, args.start_batch) + if args.max_batches > 0: + end_b = min(len(all_chunks), start_b + args.max_batches) + else: + end_b = len(all_chunks) + chunks = all_chunks[start_b:end_b] + budget_raw = sum(amt for c in chunks for _, amt in c) + + if not chunks: + print("No batches in range.", file=sys.stderr) + return 0 + + print( + f"batches {start_b}..{end_b - 1} of {len(all_chunks)} transfers={sum(len(c) for c in chunks)} " + f"budget_raw={budget_raw}", + file=sys.stderr, + ) + + if args.dry_run: + try: + allow = _allowance(token, deployer, mc, args.rpc_url) + except Exception: + allow = 0 + print(f"# allowance Multicall3: {allow} budget_this_run: {budget_raw}", file=sys.stderr) + if allow < budget_raw: + print( + f"cast send {token} \"approve(address,uint256)\" {mc} {budget_raw} \\\n" + f" --rpc-url \"$ETHEREUM_MAINNET_RPC\" --private-key \"$PRIVATE_KEY\" --gas-limit 120000", + file=sys.stderr, + ) + chunk = chunks[0] + parts = [] + for addr, amt in chunk: + data = _cast_calldata_transfer_from(deployer, addr, amt) + parts.append(f"({token},false,{data})") + tuple_str = "[" + ",".join(parts) + "]" + calldata = _cast_calldata_aggregate3(tuple_str) + gl = args.min_gas_per_batch + 65_000 * len(chunk) + sample_hex = repo / "reports/status/ei-matrix-multicall3-dryrun-sample-batch.hex" + sample_hex.write_text(calldata + "\n", encoding="utf-8") + rel = os.path.relpath(str(sample_hex), str(repo)) + print(f"\n# sample batch 0 n={len(chunk)} gas_limit~{gl}", file=sys.stderr) + print(f"# calldata written: {rel}", file=sys.stderr) + print( + f"cast send {mc} $(cat {rel}) --rpc-url \"$ETHEREUM_MAINNET_RPC\" \\\n" + f" --private-key \"$PRIVATE_KEY\" --gas-limit {gl}" + ) + print(f"\n# … {len(chunks)} batches total (chunk_size={args.chunk_size})", file=sys.stderr) + return 0 + + assert pk is not None + allow = _allowance(token, deployer, mc, args.rpc_url) + if allow < budget_raw: + print(f"Approving Multicall3 for {budget_raw} raw (was {allow})", file=sys.stderr) + _send_cast_send(token, "approve(address,uint256)", [mc, str(budget_raw)], args.rpc_url, pk, "120000") + time.sleep(2) + allow2 = _allowance(token, deployer, mc, args.rpc_url) + if allow2 < budget_raw: + print(f"Allowance insufficient: {allow2} < {budget_raw}", file=sys.stderr) + return 1 + + progress_path = repo / args.progress_file + progress_path.parent.mkdir(parents=True, exist_ok=True) + + for bi, chunk in enumerate(chunks): + global_batch_idx = start_b + bi + parts = [] + for addr, amt in chunk: + data = _cast_calldata_transfer_from(deployer, addr, amt) + parts.append(f"({token},false,{data})") + tuple_str = "[" + ",".join(parts) + "]" + calldata = _cast_calldata_aggregate3(tuple_str) + + gas_est = args.min_gas_per_batch + try: + gas_est = _estimate_gas(deployer, mc, calldata, args.rpc_url) + except Exception as e: + print(f"[warn] estimateGas failed, fallback: {e}", file=sys.stderr) + gas_est = 70_000 * len(chunk) + 400_000 + + gas_with_headroom = max(args.min_gas_per_batch, (gas_est * args.gas_headroom_bps + 9999) // 10000) + print(f"Batch {global_batch_idx}: n={len(chunk)} estimate={gas_est} limit={gas_with_headroom}", file=sys.stderr) + + _send_raw_calldata(mc, calldata, args.rpc_url, pk, str(gas_with_headroom)) + progress_path.write_text(f"{global_batch_idx}\n", encoding="utf-8") + time.sleep(1) + + print("Done.", file=sys.stderr) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/lib/ei_matrix_onchain_readiness_audit.py b/scripts/lib/ei_matrix_onchain_readiness_audit.py new file mode 100644 index 00000000..0c397573 --- /dev/null +++ b/scripts/lib/ei_matrix_onchain_readiness_audit.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python3 +""" +On-chain readiness audit for EI matrix wallets (config/pmm-soak-wallet-grid.json). + +Queries ERC-20 balanceOf for each address on one or both chains: + - Ethereum mainnet cWUSDC (default from env CWUSDC_MAINNET) + - Chain 138 cUSDC (default canonical CompliantUSDC) + +Use for strength profiling: segment by class/lpbca via --report-by-class, find gaps vs thresholds. + +Environment (optional defaults for thresholds): + EI_MATRIX_AUDIT_MIN_MAINNET_RAW, EI_MATRIX_AUDIT_MIN_138_RAW, EI_MATRIX_AUDIT_WORKERS + +Examples: + python3 scripts/lib/ei_matrix_onchain_readiness_audit.py --mainnet-only --min-mainnet-raw 1 + python3 scripts/lib/ei_matrix_onchain_readiness_audit.py --both \\ + --shard-size 400 --min-mainnet-raw 12000000 --min-138-raw 0 --workers 3 \\ + --report-by-class --json-out reports/status/ei-matrix-readiness-audit-latest.json +""" +from __future__ import annotations + +import argparse +import json +import os +import sys +import urllib.error +import urllib.request +from concurrent.futures import ThreadPoolExecutor, as_completed +from pathlib import Path + +# balanceOf(address) selector +BALANCE_OF = bytes.fromhex("70a08231") +ADDR_PAD = 12 * b"\x00" + + +def encode_balance_of_call(addr: str) -> str: + a = addr.lower().removeprefix("0x") + if len(a) != 40: + raise ValueError(f"bad address {addr}") + data = BALANCE_OF + ADDR_PAD + bytes.fromhex(a) + return "0x" + data.hex() + + +def rpc_eth_call(to: str, data: str, rpc_url: str, timeout: float = 30.0) -> str: + body = json.dumps( + { + "jsonrpc": "2.0", + "id": 1, + "method": "eth_call", + "params": [{"to": to, "data": data}, "latest"], + } + ).encode() + req = urllib.request.Request(rpc_url, data=body, headers={"Content-Type": "application/json"}, method="POST") + with urllib.request.urlopen(req, timeout=timeout) as r: + j = json.loads(r.read().decode()) + if "error" in j: + raise RuntimeError(str(j["error"])) + return j.get("result") or "0x0" + + +def hex_to_int(h: str) -> int: + h = h.strip() + if not h or h == "0x": + return 0 + return int(h, 16) + + +def collect_rows_for_slice( + slice_items: list[tuple[int, dict]], + *, + do_main: bool, + do_138: bool, + mainnet_rpc: str, + chain138_rpc: str, + mainnet_token: str, + chain138_cusdc: str, + workers: int, +) -> list[dict]: + def fetch_one(item: tuple[int, dict]) -> tuple[int, dict, int, int]: + idx, w = item + addr = w["address"] + mbal, bbal = 0, 0 + if do_main: + calldata = encode_balance_of_call(addr) + res = rpc_eth_call(mainnet_token.lower(), calldata, mainnet_rpc) + mbal = hex_to_int(res) + if do_138: + calldata = encode_balance_of_call(addr) + res = rpc_eth_call(chain138_cusdc.lower(), calldata, chain138_rpc) + bbal = hex_to_int(res) + return idx, w, mbal, bbal + + rows: list[dict] = [] + with ThreadPoolExecutor(max_workers=max(1, workers)) as ex: + futs = [ex.submit(fetch_one, it) for it in slice_items] + for fut in as_completed(futs): + idx, w, mbal, bbal = fut.result() + cls = int(w.get("class", 0)) + row = { + "linearIndex": idx, + "address": w["address"], + "cellId": w.get("cellId"), + "class": cls, + "mainnetCwusdcRaw": mbal if do_main else None, + "chain138CusdcRaw": bbal if do_138 else None, + } + rows.append(row) + return rows + + +def write_indices(path: Path, indices: list[int]) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text("\n".join(str(i) for i in indices) + ("\n" if indices else ""), encoding="utf-8") + + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("--grid", default="config/pmm-soak-wallet-grid.json") + ap.add_argument("--offset", type=int, default=0) + ap.add_argument("--limit", type=int, default=0, help="0 = all from offset to grid end") + ap.add_argument( + "--shard-size", + type=int, + default=int(os.environ.get("EI_MATRIX_AUDIT_SHARD_SIZE", "0")), + help="If >0, query in sequential shards of this size (eases RPC load). 0 = single batch.", + ) + ap.add_argument("--workers", type=int, default=int(os.environ.get("EI_MATRIX_AUDIT_WORKERS", "4"))) + ap.add_argument("--mainnet-only", action="store_true") + ap.add_argument("--chain138-only", action="store_true") + ap.add_argument("--both", action="store_true") + ap.add_argument("--mainnet-rpc", default=os.environ.get("ETHEREUM_MAINNET_RPC") or os.environ.get("RPC_URL_1") or "") + ap.add_argument("--chain138-rpc", default=os.environ.get("RPC_URL_138") or os.environ.get("CHAIN138_PUBLIC_RPC_URL") or "") + ap.add_argument("--mainnet-token", default=os.environ.get("CWUSDC_MAINNET", "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a")) + ap.add_argument( + "--chain138-cusdc", + default=os.environ.get("CUSDC_CHAIN138", "0xf22258f57794CC8E06237084b353Ab30fFfa640b"), + ) + ap.add_argument( + "--min-mainnet-raw", + type=int, + default=int(os.environ.get("EI_MATRIX_AUDIT_MIN_MAINNET_RAW", "0")), + help="fail wallets strictly below this (mainnet); env EI_MATRIX_AUDIT_MIN_MAINNET_RAW", + ) + ap.add_argument( + "--min-138-raw", + type=int, + default=int(os.environ.get("EI_MATRIX_AUDIT_MIN_138_RAW", "0")), + help="fail wallets strictly below this (138); env EI_MATRIX_AUDIT_MIN_138_RAW", + ) + ap.add_argument("--report-by-class", action="store_true", help="aggregate counts by matrix class 0..5") + ap.add_argument("--json-out", default="", help="write full per-wallet rows + summary") + ap.add_argument( + "--gaps-mainnet-out", + default="", + help="write newline-separated linear indices below mainnet minimum (only if mainnet queried)", + ) + ap.add_argument( + "--gaps-138-out", + default="", + help="write newline-separated linear indices below 138 minimum (only if 138 queried)", + ) + ap.add_argument("--max-list", type=int, default=200, help="max gap indices to print on stderr") + args = ap.parse_args() + + repo = Path(__file__).resolve().parents[2] + grid_path = repo / args.grid if not os.path.isabs(args.grid) else Path(args.grid) + data = json.loads(grid_path.read_text(encoding="utf-8")) + wallets: list[dict] = data["wallets"] + n = len(wallets) + scan_end = n if args.limit <= 0 else min(n, args.offset + args.limit) + scan_start = args.offset + if scan_start < 0 or scan_start > n: + print("Invalid --offset", file=sys.stderr) + return 2 + if scan_end < scan_start: + print("Invalid --limit / range", file=sys.stderr) + return 2 + + do_main = args.mainnet_only or args.both + do_138 = args.chain138_only or args.both + if not do_main and not do_138: + print("Specify --mainnet-only, --chain138-only, or --both", file=sys.stderr) + return 2 + if do_main and not args.mainnet_rpc: + print("Need --mainnet-rpc or ETHEREUM_MAINNET_RPC / RPC_URL_1", file=sys.stderr) + return 2 + if do_138 and not args.chain138_rpc: + print("Need --chain138-rpc or RPC_URL_138", file=sys.stderr) + return 2 + + shard = max(0, args.shard_size) + rows: list[dict] = [] + if shard <= 0: + slice_items = list(enumerate(wallets[scan_start:scan_end], start=scan_start)) + rows = collect_rows_for_slice( + slice_items, + do_main=do_main, + do_138=do_138, + mainnet_rpc=args.mainnet_rpc, + chain138_rpc=args.chain138_rpc, + mainnet_token=args.mainnet_token, + chain138_cusdc=args.chain138_cusdc, + workers=args.workers, + ) + else: + for start in range(scan_start, scan_end, shard): + chunk_end = min(scan_end, start + shard) + slice_items = list(enumerate(wallets[start:chunk_end], start=start)) + print(f"Shard {start}..{chunk_end} ({len(slice_items)} wallets)", file=sys.stderr) + rows.extend( + collect_rows_for_slice( + slice_items, + do_main=do_main, + do_138=do_138, + mainnet_rpc=args.mainnet_rpc, + chain138_rpc=args.chain138_rpc, + mainnet_token=args.mainnet_token, + chain138_cusdc=args.chain138_cusdc, + workers=args.workers, + ) + ) + + rows.sort(key=lambda r: r["linearIndex"]) + + by_class: dict[int, dict] = {i: {"n": 0, "mainnet_below": 0, "138_below": 0} for i in range(6)} + if args.report_by_class: + for r in rows: + cls = int(r.get("class", 0)) + if cls not in by_class: + continue + by_class[cls]["n"] += 1 + if do_main and r["mainnetCwusdcRaw"] < args.min_mainnet_raw: + by_class[cls]["mainnet_below"] += 1 + if do_138 and r["chain138CusdcRaw"] < args.min_138_raw: + by_class[cls]["138_below"] += 1 + + gaps_main: list[int] = [] + gaps_138: list[int] = [] + for r in rows: + if do_main and r["mainnetCwusdcRaw"] < args.min_mainnet_raw: + gaps_main.append(r["linearIndex"]) + if do_138 and r["chain138CusdcRaw"] < args.min_138_raw: + gaps_138.append(r["linearIndex"]) + + summary = { + "gridPath": str(grid_path), + "slice": {"offset": scan_start, "endExclusive": scan_end, "count": len(rows)}, + "shardSize": shard if shard > 0 else None, + "mainnet": { + "token": args.mainnet_token if do_main else None, + "rpc": args.mainnet_rpc[:48] + "…" if do_main and len(args.mainnet_rpc) > 48 else args.mainnet_rpc, + "minRaw": args.min_mainnet_raw, + "belowMin": len(gaps_main), + }, + "chain138": { + "token": args.chain138_cusdc if do_138 else None, + "minRaw": args.min_138_raw, + "belowMin": len(gaps_138), + }, + "byClass": by_class if args.report_by_class else None, + } + + print(json.dumps(summary, indent=2)) + if gaps_main: + print( + f"\nMainnet cWUSDC below min ({args.min_mainnet_raw}) — {len(gaps_main)} wallets " + f"(first {args.max_list} indices):", + file=sys.stderr, + ) + print(", ".join(str(x) for x in gaps_main[: args.max_list]), file=sys.stderr) + if gaps_138: + print( + f"\nChain 138 cUSDC below min ({args.min_138_raw}) — {len(gaps_138)} wallets " + f"(first {args.max_list} indices):", + file=sys.stderr, + ) + print(", ".join(str(x) for x in gaps_138[: args.max_list]), file=sys.stderr) + + if args.json_out: + outp = repo / args.json_out if not os.path.isabs(args.json_out) else Path(args.json_out) + outp.parent.mkdir(parents=True, exist_ok=True) + outp.write_text(json.dumps({"summary": summary, "rows": rows}, indent=2), encoding="utf-8") + print(f"\nWrote {outp}", file=sys.stderr) + + if do_main and args.gaps_mainnet_out: + gp = repo / args.gaps_mainnet_out if not os.path.isabs(args.gaps_mainnet_out) else Path(args.gaps_mainnet_out) + write_indices(gp, gaps_main) + print(f"Wrote mainnet gap indices ({len(gaps_main)}): {gp}", file=sys.stderr) + if do_138 and args.gaps_138_out: + gp = repo / args.gaps_138_out if not os.path.isabs(args.gaps_138_out) else Path(args.gaps_138_out) + write_indices(gp, gaps_138) + print(f"Wrote 138 gap indices ({len(gaps_138)}): {gp}", file=sys.stderr) + + fail = bool(gaps_main or gaps_138) + return 1 if fail else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/lib/find-repo-files.sh b/scripts/lib/find-repo-files.sh new file mode 100755 index 00000000..8eaacf9d --- /dev/null +++ b/scripts/lib/find-repo-files.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# List files under the repo root without descending into node_modules or .git +# (avoids hanging on huge dependency trees). +# +# Usage: +# scripts/lib/find-repo-files.sh +# scripts/lib/find-repo-files.sh -name '*.md' +# scripts/lib/find-repo-files.sh \( -name '*.ts' -o -name '*.tsx' \) +# +# Example — search text without scanning node_modules (prefer narrowing extensions; +# piping every file to grep can still be slow on very large trees): +# scripts/lib/find-repo-files.sh -name '*.md' | xargs grep -l 'pattern' 2>/dev/null +# scripts/lib/find-repo-files.sh \( -name '*.md' -o -name '*.sh' -o -name '*.ts' -o -name '*.json' \) \\ +# | xargs grep -l 'pattern' 2>/dev/null + +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" + +# shellcheck disable=SC2086 +exec find "$ROOT" \ + \( -name node_modules -o -name .git \) -prune -o \ + -type f ${1+"$@"} -print diff --git a/scripts/lib/load-project-env.sh b/scripts/lib/load-project-env.sh index 9c6ed25a..430eb73f 100644 --- a/scripts/lib/load-project-env.sh +++ b/scripts/lib/load-project-env.sh @@ -172,12 +172,13 @@ export DBIS_CORE_DIR="${DBIS_CORE_DIR:-${PROJECT_ROOT}/dbis_core}" get_host_for_vmid() { local vmid="$1" case "$vmid" in - 7800|7801|7802|7803|7804|7805|7806) echo "${PROXMOX_HOST_R630_01}";; + 7800|7801|7802|7803|7805|7806) echo "${PROXMOX_HOST_R630_01}";; + 7804) echo "${PROXMOX_HOST_R630_04:-192.168.11.14}";; 10130|10150|10151|106|107|108|10000|10001|10020|10100|10101|10120|10203|10233|10235) echo "${PROXMOX_HOST_R630_01}";; 1000|1001|1002|1500|1501|1502|2101|2103) echo "${PROXMOX_HOST_R630_01}";; 1003|1004|1503|1504|1505|1506|1507|1509|1510|2102|2301|2304|2400|2402|2403) echo "${PROXMOX_HOST_R630_03}";; 1508) echo "${PROXMOX_HOST_R630_04}";; - 5700) echo "${PROXMOX_HOST_R630_04}";; + 5700|5701) echo "${PROXMOX_HOST_R630_04:-192.168.11.14}";; 5000|7810|2201|2303|2305|2306|2307|2308|2401|6200|6201|6202|6203|6204|6205|10234|10237|5800|5801) echo "${PROXMOX_HOST_R630_02}";; 2420|2430|2440|2460|2470|2480) echo "${PROXMOX_HOST_R630_01}";; 5400|5401|5402|5403|5410|5411|5412|5413|5414|5415|5416|5417|5418|5419|5420|5421|5422|5423|5424|5425|5440|5441|5442|5443|5444|5445|5446|5447|5448|5449|5450|5451|5452|5453|5454|5455|5470|5471|5472|5473|5474|5475|5476) echo "${PROXMOX_HOST_R630_02}";; diff --git a/scripts/lib/mev-protection.sh b/scripts/lib/mev-protection.sh new file mode 100755 index 00000000..b3e78bff --- /dev/null +++ b/scripts/lib/mev-protection.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +# Shared protected-broadcast helpers for Engine X Mainnet actions. +# +# Source after scripts/lib/load-project-env.sh. Reads use the normal public RPC; +# sensitive writes should go through mev_cast_send so operators cannot +# accidentally broadcast quote-defense swaps through the public mempool. + +mev_private_rpc_key() { + local key value + for key in ENGINE_X_PRIVATE_TX_RPC MEV_BLOCKER_RPC_URL FLASHBOTS_RPC_URL BLOXROUTE_RPC_URL BLINK_RPC_URL; do + value="${!key-}" + if [[ -n "${value}" ]]; then + printf '%s\n' "${key}" + return 0 + fi + done + return 1 +} + +mev_has_private_rpc() { + mev_private_rpc_key >/dev/null 2>&1 +} + +mev_write_rpc_label() { + local key + if key="$(mev_private_rpc_key)"; then + case "${key}" in + ENGINE_X_PRIVATE_TX_RPC) printf '%s\n' "${ENGINE_X_PRIVATE_TX_RPC_LABEL:-engine-x-private-tx-rpc}" ;; + MEV_BLOCKER_RPC_URL) printf '%s\n' "mev-blocker" ;; + FLASHBOTS_RPC_URL) printf '%s\n' "flashbots" ;; + BLOXROUTE_RPC_URL) printf '%s\n' "bloxroute" ;; + BLINK_RPC_URL) printf '%s\n' "blink" ;; + *) printf '%s\n' "${key}" ;; + esac + return 0 + fi + printf '%s\n' "public-mainnet-rpc" +} + +mev_write_rpc_url() { + local key + if key="$(mev_private_rpc_key)"; then + printf '%s\n' "${!key}" + return 0 + fi + + if [[ "${ENGINE_X_MEV_PROTECTION:-1}" == "1" && "${ENGINE_X_ALLOW_PUBLIC_BROADCAST:-0}" != "1" ]]; then + return 1 + fi + + if [[ -z "${ETHEREUM_MAINNET_RPC:-}" ]]; then + return 1 + fi + printf '%s\n' "${ETHEREUM_MAINNET_RPC}" +} + +mev_require_private_for_action() { + local action="${1:-engine-x-sensitive-action}" + if [[ "${ENGINE_X_MEV_PROTECTION:-1}" != "1" ]]; then + echo "WARN: MEV protection disabled for ${action} (ENGINE_X_MEV_PROTECTION=0)." >&2 + return 0 + fi + if mev_has_private_rpc; then + return 0 + fi + if [[ "${ENGINE_X_ALLOW_PUBLIC_BROADCAST:-0}" == "1" ]]; then + echo "WARN: public broadcast explicitly allowed for ${action} (ENGINE_X_ALLOW_PUBLIC_BROADCAST=1)." >&2 + return 0 + fi + + cat >&2 <&2 + return 1 + fi + cast send "${target}" "$@" --private-key "${PRIVATE_KEY:?PRIVATE_KEY is required}" --rpc-url "${rpc}" +} diff --git a/scripts/lib/require-proxmox-ssh-for-pct.sh b/scripts/lib/require-proxmox-ssh-for-pct.sh new file mode 100644 index 00000000..f562cb2b --- /dev/null +++ b/scripts/lib/require-proxmox-ssh-for-pct.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# Resolve PROXMOX_HOST for pct-over-SSH so operator workstations do not run pct by mistake. +# +# Usage (after VMID is set): +# PROXMOX_MONOREPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" # proxmox repo root +# source "${PROXMOX_MONOREPO_ROOT}/scripts/lib/require-proxmox-ssh-for-pct.sh" +# require_proxmox_ssh_for_pct +# +# Env: +# PROXMOX_HOST If set, use this host (ssh root@$PROXMOX_HOST … pct …). +# VMID Used with get_host_for_vmid when PROXMOX_HOST is unset. +# PROXMOX_MONOREPO_ROOT Proxmox monorepo root (directory containing scripts/lib/load-project-env.sh). +# PROJECT_ROOT Alternative to PROXMOX_MONOREPO_ROOT when sourcing load-project-env. +# DEPLOY_PCT_ON_LOCAL_PVE Set to 1 only on a real Proxmox node (/etc/pve/.members) to run local pct +# without SSH (hypervisor shell only). + +require_proxmox_ssh_for_pct() { + local vmid="${VMID:-}" + + if [[ "${DEPLOY_PCT_ON_LOCAL_PVE:-0}" == "1" ]]; then + if [[ -r /etc/pve/.members ]]; then + export PROXMOX_HOST="" + echo "NOTE: DEPLOY_PCT_ON_LOCAL_PVE=1 — using pct on this Proxmox node (no SSH)." >&2 + return 0 + fi + echo "ERROR: DEPLOY_PCT_ON_LOCAL_PVE=1 but this host is not a Proxmox cluster member (/etc/pve/.members missing)." >&2 + return 1 + fi + + if [[ -n "${PROXMOX_HOST:-}" ]]; then + echo "Using Proxmox target: ssh root@${PROXMOX_HOST} (VMID ${vmid:-n/a})" >&2 + return 0 + fi + + if [[ -z "$vmid" ]]; then + echo "ERROR: PROXMOX_HOST is unset and VMID is empty — cannot choose a Proxmox host." >&2 + echo " Set PROXMOX_HOST (e.g. 192.168.11.12) or VMID, or run on a PVE node with DEPLOY_PCT_ON_LOCAL_PVE=1." >&2 + return 1 + fi + + local root="${PROXMOX_MONOREPO_ROOT:-${PROJECT_ROOT:-}}" + if [[ -z "$root" || ! -f "$root/scripts/lib/load-project-env.sh" ]]; then + echo "ERROR: Proxmox monorepo root not found (expected scripts/lib/load-project-env.sh under PROXMOX_MONOREPO_ROOT or PROJECT_ROOT)." >&2 + return 1 + fi + + # shellcheck disable=SC1090 + PROJECT_ROOT="$root" source "$root/scripts/lib/load-project-env.sh" + + local chosen + chosen="$(get_host_for_vmid "$vmid")" + if [[ -z "$chosen" ]]; then + echo "ERROR: get_host_for_vmid returned empty for VMID=$vmid" >&2 + return 1 + fi + export PROXMOX_HOST="$chosen" + echo "Auto-selected Proxmox host from VMID ${vmid}: ssh root@${PROXMOX_HOST}" >&2 + return 0 +} diff --git a/scripts/lib/requirements-solana-ops.txt b/scripts/lib/requirements-solana-ops.txt new file mode 100644 index 00000000..d4e99374 --- /dev/null +++ b/scripts/lib/requirements-solana-ops.txt @@ -0,0 +1,3 @@ +# Used by scripts/deployment/solana-transfer-native.py (sign + serialize only). +# RPC calls use stdlib in scripts/lib/solana_jsonrpc.py (avoids solana-py sendTransaction parse panics on some hosts). +solders>=0.21.0,<0.26 diff --git a/scripts/lib/solana_jsonrpc.py b/scripts/lib/solana_jsonrpc.py new file mode 100644 index 00000000..a2fd4106 --- /dev/null +++ b/scripts/lib/solana_jsonrpc.py @@ -0,0 +1,188 @@ +""" +Minimal Solana JSON-RPC over HTTP (stdlib only). + +Some public RPCs return a bare string for ``sendTransaction`` ``result`` without +extra fields that ``solana-py``'s ``SendTransactionResp`` expects, which makes +``Client.send_raw_transaction`` panic while deserializing (missing JSON field +``data``). Use :func:`send_transaction_wire` for submission; keep ``solders`` +(or ``solana-py``) only for signing and local serialization. +""" + +from __future__ import annotations + +import base64 +import json +import time +import urllib.error +import urllib.request +from typing import Any + + +DEFAULT_USER_AGENT = "proxmox-scripts/solana-jsonrpc/1.0" + + +class SolanaJsonRpcError(RuntimeError): + """JSON-RPC error object or unexpected HTTP / parse failure.""" + + def __init__(self, message: str, *, payload: dict[str, Any] | None = None) -> None: + super().__init__(message) + self.payload = payload + + +def post_json_rpc( + rpc_url: str, + method: str, + params: list[Any], + *, + request_id: int = 1, + timeout_s: float = 90.0, + user_agent: str = DEFAULT_USER_AGENT, +) -> dict[str, Any]: + body = json.dumps( + {"jsonrpc": "2.0", "id": request_id, "method": method, "params": params} + ).encode("utf-8") + req = urllib.request.Request( + rpc_url, + data=body, + headers={"Content-Type": "application/json", "User-Agent": user_agent}, + method="POST", + ) + try: + with urllib.request.urlopen(req, timeout=timeout_s) as resp: + raw = resp.read().decode("utf-8") + except urllib.error.HTTPError as e: + try: + detail = e.read().decode("utf-8", errors="replace") + except Exception: + detail = str(e) + raise SolanaJsonRpcError(f"HTTP {e.code}: {detail}") from e + + try: + out: dict[str, Any] = json.loads(raw) + except json.JSONDecodeError as e: + raise SolanaJsonRpcError(f"invalid JSON from RPC: {raw[:500]!r}") from e + + err = out.get("error") + if err: + raise SolanaJsonRpcError(f"RPC error: {err}", payload=out) + return out + + +def get_latest_blockhash( + rpc_url: str, *, commitment: str = "confirmed", timeout_s: float = 30.0 +) -> str: + out = post_json_rpc( + rpc_url, + "getLatestBlockhash", + [{"commitment": commitment}], + timeout_s=timeout_s, + ) + try: + return str(out["result"]["value"]["blockhash"]) + except (KeyError, TypeError) as e: + raise SolanaJsonRpcError(f"unexpected getLatestBlockhash shape: {out!r}") from e + + +def get_balance_lamports( + rpc_url: str, pubkey_b58: str, *, commitment: str = "confirmed" +) -> int: + out = post_json_rpc( + rpc_url, + "getBalance", + [pubkey_b58, {"commitment": commitment}], + ) + try: + return int(out["result"]["value"]) + except (KeyError, TypeError, ValueError) as e: + raise SolanaJsonRpcError(f"unexpected getBalance shape: {out!r}") from e + + +def send_transaction_wire( + rpc_url: str, + signed_wire: bytes, + *, + skip_preflight: bool = False, + preflight_commitment: str = "confirmed", + max_retries: int | None = None, + timeout_s: float = 90.0, +) -> str: + """ + Submit a fully signed legacy or versioned transaction (wire bytes). + + Returns base58 transaction signature string from ``result``. + """ + opts: dict[str, Any] = { + "encoding": "base64", + "skipPreflight": skip_preflight, + "preflightCommitment": preflight_commitment, + } + if max_retries is not None: + opts["maxRetries"] = max_retries + + params: list[Any] = [base64.b64encode(signed_wire).decode("ascii"), opts] + out = post_json_rpc(rpc_url, "sendTransaction", params, timeout_s=timeout_s) + result = out.get("result") + if not isinstance(result, str): + raise SolanaJsonRpcError(f"unexpected sendTransaction result: {out!r}") + return result + + +def get_signature_statuses( + rpc_url: str, + signatures: list[str], + *, + search_transaction_history: bool = False, +) -> list[dict[str, Any] | None]: + """Return one status object (or null) per signature, same order as input.""" + if search_transaction_history: + params: list[Any] = [signatures, {"searchTransactionHistory": True}] + else: + params = [signatures] + out = post_json_rpc(rpc_url, "getSignatureStatuses", params) + try: + val = out["result"]["value"] + except (KeyError, TypeError) as e: + raise SolanaJsonRpcError(f"unexpected getSignatureStatuses shape: {out!r}") from e + if not isinstance(val, list): + raise SolanaJsonRpcError(f"unexpected getSignatureStatuses value: {val!r}") + out_list: list[dict[str, Any] | None] = [] + for item in val: + if item is None: + out_list.append(None) + elif isinstance(item, dict): + out_list.append(item) + else: + raise SolanaJsonRpcError(f"unexpected status entry: {item!r}") + return out_list + + +def wait_until_signature_confirmed( + rpc_url: str, + signature: str, + *, + timeout_s: float = 90.0, + poll_interval_s: float = 1.0, +) -> dict[str, Any]: + """ + Poll ``getSignatureStatuses`` until the signature has a terminal ``err`` or + reaches ``confirmationStatus`` of ``confirmed`` / ``finalized``. + """ + deadline = time.monotonic() + timeout_s + last: dict[str, Any] | None = None + while time.monotonic() < deadline: + statuses = get_signature_statuses(rpc_url, [signature]) + st = statuses[0] if statuses else None + last = st + if st is None: + time.sleep(poll_interval_s) + continue + err = st.get("err") + if err: + raise SolanaJsonRpcError(f"transaction failed: err={err!r}", payload=st) + conf = st.get("confirmationStatus") + if conf in ("confirmed", "finalized"): + return st + time.sleep(poll_interval_s) + raise SolanaJsonRpcError( + f"timeout waiting for confirmation of {signature!r}; last={last!r}" + ) diff --git a/scripts/maintenance/npmplus-cluster-placement-status.sh b/scripts/maintenance/npmplus-cluster-placement-status.sh new file mode 100755 index 00000000..1cba57ec --- /dev/null +++ b/scripts/maintenance/npmplus-cluster-placement-status.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# Show which Proxmox node hosts NPMplus CTs 10233–10236 (live SSH). +# Usage: bash scripts/maintenance/npmplus-cluster-placement-status.sh +# Requires: SSH BatchMode to cluster nodes (root@192.168.11.11, .12); extend HOSTS if needed. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +# shellcheck source=/dev/null +[[ -f "$PROJECT_ROOT/config/ip-addresses.conf" ]] && source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true + +SSH_OPTS=(-o BatchMode=yes -o ConnectTimeout=12 -o StrictHostKeyChecking=no) + +HOSTS=( + "${PROXMOX_HOST_R630_01:-192.168.11.11}:r630-01" + "${PROXMOX_HOST_R630_02:-192.168.11.12}:r630-02" + "${PROXMOX_HOST_R630_03:-192.168.11.13}:r630-03" + "${PROXMOX_HOST_R630_04:-192.168.11.14}:r630-04" +) + +echo "=== NPMplus CT placement (VMIDs 10233–10236) ===" +echo "" + +for entry in "${HOSTS[@]}"; do + ip="${entry%%:*}" + label="${entry##*:}" + echo "--- $label ($ip) ---" + if ssh "${SSH_OPTS[@]}" "root@$ip" "pct list | grep -E '^10233|^10234|^10235|^10236' || true" 2>/dev/null; then + : + else + echo "(SSH skipped or failed — check key access to $ip)" + fi + echo "" +done + +echo "=== Quick HTTP :81 (LAN, optional) ===" +for ip in 167 168 169 170; do + code=$(curl -sS -m 4 -o /dev/null -w "%{http_code}" "http://192.168.11.$ip:81/" 2>/dev/null || echo "fail") + echo "192.168.11.$ip:81 -> $code" +done + +echo "" +echo "Target distribution: docs/04-configuration/NPMPLUS_MISSION_CRITICAL_DISTRIBUTION_AND_HA_PLAN.md" diff --git a/scripts/maintenance/stop-local-repo-dev-servers.sh b/scripts/maintenance/stop-local-repo-dev-servers.sh new file mode 100755 index 00000000..94c8a2b9 --- /dev/null +++ b/scripts/maintenance/stop-local-repo-dev-servers.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Stop dev helpers started from this repo (not IDE/Cursor, not system DNS). +# Safe to run before leaving a workstation; does not touch Besu/RPC/NPM on LAN. +# +# Usage: bash scripts/maintenance/stop-local-repo-dev-servers.sh + +set -euo pipefail + +_stopped=0 +for sig in TERM KILL; do + if pgrep -f 'forge-verification-proxy/server\.js' >/dev/null 2>&1; then + pkill -"$sig" -f 'forge-verification-proxy/server\.js' 2>/dev/null || true + _stopped=1 + fi + if pgrep -f 'serve_explorer_spa\.py' >/dev/null 2>&1; then + pkill -"$sig" -f 'serve_explorer_spa\.py' 2>/dev/null || true + _stopped=1 + fi +done + +if [[ "$_stopped" == 1 ]]; then + echo "Stopped forge-verification-proxy and/or serve_explorer_spa.py (if running)." +else + echo "No forge-verification-proxy or serve_explorer_spa.py processes found." +fi diff --git a/scripts/run-wave0-from-lan.sh b/scripts/run-wave0-from-lan.sh old mode 100644 new mode 100755 diff --git a/scripts/verify-contracts-blockscout.sh b/scripts/verify-contracts-blockscout.sh index de1e4466..41162f1a 100755 --- a/scripts/verify-contracts-blockscout.sh +++ b/scripts/verify-contracts-blockscout.sh @@ -222,6 +222,25 @@ if [[ -n "$ADDR_TX_MIRROR" ]] && should_verify TransactionMirror; then verify_one_explicit "$ADDR_TX_MIRROR" "contracts/mirror/TransactionMirror.sol:TransactionMirror" "TransactionMirror" "${enc:-}" fi +# Optional: alternate CCIPWETH9Bridge deployment (must run after verify_one_explicit is defined). +# Constructor args read from chain. Enable: VERIFY_ALTERNATE_CCIPWETH9_BRIDGE=1 ./scripts/verify/run-contract-verification-with-proxy.sh +ADDR_CCIPWETH9_ALT="${VERIFY_CCIPWETH9_ALT_ADDRESS:-0x9cba0D04Ae5f6f16e3C599025aB97a05c4A593d5}" +if [[ "${VERIFY_ALTERNATE_CCIPWETH9_BRIDGE:-0}" == "1" ]] && should_verify CCIPWETH9BridgeAlt; then + if has_contract_bytecode "$ADDR_CCIPWETH9_ALT"; then + r=$(cast call "$ADDR_CCIPWETH9_ALT" "ccipRouter()(address)" --rpc-url "$RPC" 2>/dev/null | tr -d '\n\r \t') || r="" + w=$(cast call "$ADDR_CCIPWETH9_ALT" "weth9()(address)" --rpc-url "$RPC" 2>/dev/null | tr -d '\n\r \t') || w="" + f=$(cast call "$ADDR_CCIPWETH9_ALT" "feeToken()(address)" --rpc-url "$RPC" 2>/dev/null | tr -d '\n\r \t') || f="" + if [[ -n "$r" && -n "$w" && -n "$f" ]]; then + enc=$(cast abi-encode 'constructor(address,address,address)' "$r" "$w" "$f" 2>/dev/null | tr -d '\n\r \t') || enc="" + verify_one_explicit "$ADDR_CCIPWETH9_ALT" "contracts/ccip/CCIPWETH9Bridge.sol:CCIPWETH9Bridge" "CCIPWETH9Bridge (alternate $ADDR_CCIPWETH9_ALT)" "${enc:-}" + else + echo "CCIPWETH9Bridge (alternate): skip — could not read immutables from $ADDR_CCIPWETH9_ALT" + fi + else + echo "CCIPWETH9Bridge (alternate): skip — no bytecode at $ADDR_CCIPWETH9_ALT" + fi +fi + # CompliantFiatToken: one deployment per currency with distinct constructor args — verify per token in the Blockscout UI or add scripted entries when addresses are enumerated in env. echo "" diff --git a/scripts/verify/README.md b/scripts/verify/README.md index d2122a9c..4dd4a608 100644 --- a/scripts/verify/README.md +++ b/scripts/verify/README.md @@ -27,6 +27,10 @@ One-line install (Debian/Ubuntu): `sudo apt install -y sshpass rsync dnsutils ip ## Scripts +- `run-cwusdc-provider-nonmanual-checks.sh` - Run all public/read-only cWUSDC provider checks and write `reports/status/cwusdc-provider-handoff-latest.{json,md}`. Does not submit forms, approve tokens, add liquidity, swap, bridge, or broadcast transactions. +- `check-cwusdc-provider-readiness-ci.sh` - CI-safe cWUSDC provider gate: fails only when repo-controlled URL prerequisites fail; reports external provider blockers as advisory. +- `build-cwusdc-provider-handoff-report.py` - Build a concise cWUSDC provider handoff report from latest JSON probe outputs. +- `check-cwusdc-etherscan-prereq-urls.sh` - Refresh public URL prerequisite evidence for Etherscan profile submission; supports `--json-out`, `--md-out`, `--timeout`, and `--retries` (or env `CWUSDC_PROVIDER_URL_TIMEOUT` / `CWUSDC_PROVIDER_URL_RETRIES`). - `backup-npmplus.sh` - Full NPMplus backup (database, API exports, certificates) - `check-contracts-on-chain-138.sh` - Check that Chain 138 deployed contracts have bytecode on-chain (`cast code` for 31 addresses; requires `cast` and RPC access). Use `[RPC_URL]` or env `RPC_URL_138`; `--dry-run` lists addresses only (no RPC calls); `SKIP_EXIT=1` to exit 0 when RPC unreachable. - `check-non-evm-network-health.sh` - Read-only live check for the public Solana, Tron, and XRPL endpoints used in repo docs. Prints a concise status table and can also write `reports/status/non-evm-network-health-latest.json`. diff --git a/scripts/verify/audit-cusdc-cwusdc-etherscan-feeds.py b/scripts/verify/audit-cusdc-cwusdc-etherscan-feeds.py new file mode 100755 index 00000000..5f8a3989 --- /dev/null +++ b/scripts/verify/audit-cusdc-cwusdc-etherscan-feeds.py @@ -0,0 +1,310 @@ +#!/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()) diff --git a/scripts/verify/audit-cwusdc-mainnet-roles.py b/scripts/verify/audit-cwusdc-mainnet-roles.py new file mode 100644 index 00000000..17420b12 --- /dev/null +++ b/scripts/verify/audit-cwusdc-mainnet-roles.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 +"""Read-only role/control audit for Ethereum Mainnet cWUSDC.""" + +from __future__ import annotations + +import argparse +import datetime as dt +import json +import os +import subprocess +import urllib.parse +import urllib.request +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +REPORT_JSON = ROOT / "reports" / "status" / "cwusdc-mainnet-role-audit-latest.json" +REPORT_MD = ROOT / "reports" / "status" / "cwusdc-mainnet-role-audit-latest.md" +CWUSDC = "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a" +DEPLOYER = "0x4A666F96fC8764181194447A7dFdb7d471b301C8" +CW_BRIDGE_MAINNET_FALLBACK = "0x2bF74583206A49Be07E0E8A94197C12987AbD7B5" +ETHERSCAN_V2_API = "https://api.etherscan.io/v2/api" + + +def load_dotenv(path: Path) -> None: + if not path.exists(): + return + for line in path.read_text().splitlines(): + line = line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + key, value = line.split("=", 1) + key = key.strip() + value = value.strip().strip('"').strip("'") + if key and key not in os.environ: + os.environ[key] = value + + +def cast_call(contract: str, signature: str, *args: str, rpc_url: str) -> str: + command = ["cast", "call", contract, signature, *args, "--rpc-url", rpc_url] + proc = subprocess.run(command, cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False) + if proc.returncode != 0: + raise RuntimeError(proc.stderr.strip() or proc.stdout.strip()) + return proc.stdout.strip() + + +def cast_keccak(signature: str) -> str: + proc = subprocess.run(["cast", "keccak", signature], cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False) + if proc.returncode != 0: + raise RuntimeError(proc.stderr.strip() or proc.stdout.strip()) + return proc.stdout.strip() + + +def fetch_json(url: str) -> Any: + req = urllib.request.Request(url, headers={"User-Agent": "dbis-cwusdc-role-audit/1.0"}) + with urllib.request.urlopen(req, timeout=30) as response: + return json.loads(response.read().decode("utf-8")) + + +def etherscan_logs(api_key: str, address: str, topic0: str) -> list[dict[str, Any]]: + if not api_key: + return [] + query = { + "chainid": "1", + "module": "logs", + "action": "getLogs", + "fromBlock": "0", + "toBlock": "latest", + "address": address, + "topic0": topic0, + "apikey": api_key, + } + payload = fetch_json(f"{ETHERSCAN_V2_API}?{urllib.parse.urlencode(query)}") + result = payload.get("result") if isinstance(payload, dict) else None + return result if isinstance(result, list) else [] + + +def topic_to_address(topic: str) -> str: + return "0x" + topic[-40:] + + +def bool_from_cast(value: str) -> bool: + return value.strip().lower() in {"true", "1"} + + +def candidate_addresses() -> dict[str, str]: + candidates = { + "deployer": os.environ.get("DEPLOYER_ADDRESS") or DEPLOYER, + "cwBridgeMainnet": os.environ.get("CW_BRIDGE_MAINNET", "") or CW_BRIDGE_MAINNET_FALLBACK, + "ccipRelayBridgeMainnet": os.environ.get("CCIP_RELAY_BRIDGE_MAINNET", ""), + "mainnetCcipWeth9Bridge": os.environ.get("MAINNET_CCIP_WETH9_BRIDGE", ""), + "mainnetCcipWeth10Bridge": os.environ.get("MAINNET_CCIP_WETH10_BRIDGE", ""), + "ccipEthRouter": os.environ.get("CCIP_ETH_ROUTER", ""), + "uniswapV3CwusdcUsdcPool": "0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3", + "uniswapV2CwusdcUsdcPair": "0xC28706F899266b36BC43cc072b3a921BDf2C48D9", + "engineXVirtualBatchVault": "0xf108586d1FC330EA1D4EA4ff8fd983cde94279B1", + } + return {label: address for label, address in candidates.items() if address and address.startswith("0x") and len(address) == 42} + + +def build(args: argparse.Namespace) -> dict[str, Any]: + load_dotenv(ROOT / ".env") + load_dotenv(ROOT / "smom-dbis-138" / ".env") + rpc_url = args.rpc_url or os.environ.get("ETHEREUM_MAINNET_RPC") or os.environ.get("MAINNET_RPC_URL") + if not rpc_url: + raise SystemExit("ETHEREUM_MAINNET_RPC or --rpc-url is required") + + roles = { + "DEFAULT_ADMIN_ROLE": cast_call(args.token, "DEFAULT_ADMIN_ROLE()(bytes32)", rpc_url=rpc_url), + "MINTER_ROLE": cast_call(args.token, "MINTER_ROLE()(bytes32)", rpc_url=rpc_url), + "BURNER_ROLE": cast_call(args.token, "BURNER_ROLE()(bytes32)", rpc_url=rpc_url), + } + role_admins = { + role_name: cast_call(args.token, "getRoleAdmin(bytes32)(bytes32)", role_id, rpc_url=rpc_url) + for role_name, role_id in roles.items() + } + + candidates = candidate_addresses() + checks: dict[str, Any] = {} + for label, address in candidates.items(): + checks[label] = {"address": address, "roles": {}} + for role_name, role_id in roles.items(): + checks[label]["roles"][role_name] = bool_from_cast( + cast_call(args.token, "hasRole(bytes32,address)(bool)", role_id, address, rpc_url=rpc_url) + ) + + privileged = [ + { + "label": label, + "address": data["address"], + "roles": [role for role, has_role in data["roles"].items() if has_role], + } + for label, data in checks.items() + if any(data["roles"].values()) + ] + + api_key = os.environ.get("ETHERSCAN_API_KEY", "") + event_topics = { + "RoleGranted": cast_keccak("RoleGranted(bytes32,address,address)"), + "RoleRevoked": cast_keccak("RoleRevoked(bytes32,address,address)"), + } + events: list[dict[str, Any]] = [] + if api_key: + for event_name, topic0 in event_topics.items(): + for item in etherscan_logs(api_key, args.token, topic0): + topics = item.get("topics") or [] + if len(topics) < 4: + continue + role_id = topics[1] + account = topic_to_address(topics[2]) + sender = topic_to_address(topics[3]) + role_name = next((name for name, value in roles.items() if value.lower() == role_id.lower()), role_id) + events.append( + { + "event": event_name, + "role": role_name, + "roleId": role_id, + "account": account, + "sender": sender, + "blockNumber": int(str(item.get("blockNumber", "0")), 16) if str(item.get("blockNumber", "")).startswith("0x") else item.get("blockNumber"), + "transactionHash": item.get("transactionHash"), + "logIndex": item.get("logIndex"), + } + ) + effective_from_events: dict[str, set[str]] = {role: set() for role in roles} + for item in sorted(events, key=lambda row: (int(row.get("blockNumber") or 0), int(str(row.get("logIndex") or "0x0"), 16) if str(row.get("logIndex", "")).startswith("0x") else 0)): + role = item["role"] + if role not in effective_from_events: + continue + if item["event"] == "RoleGranted": + effective_from_events[role].add(item["account"]) + elif item["event"] == "RoleRevoked": + effective_from_events[role].discard(item["account"]) + + return { + "schema": "cwusdc-mainnet-role-audit/v1", + "generatedAt": dt.datetime.now(dt.UTC).isoformat().replace("+00:00", "Z"), + "network": {"chainId": 1, "name": "Ethereum Mainnet"}, + "token": {"address": args.token, "symbol": "cWUSDC", "name": "Wrapped cUSDC"}, + "roles": roles, + "roleAdmins": role_admins, + "candidateChecks": checks, + "privilegedCandidates": privileged, + "eventLogReview": { + "checked": bool(api_key), + "topics": event_topics, + "eventCount": len(events), + "events": events, + "effectiveMembersFromEvents": {role: sorted(values) for role, values in effective_from_events.items()}, + }, + "limitations": [ + "This audit checks known candidate addresses only.", + "Event-log reconstruction is included when ETHERSCAN_API_KEY is available, but provider log limits or pruned responses can still require manual verification.", + "This is a read-only control snapshot, not a formal third-party audit.", + ], + } + + +def write_md(payload: dict[str, Any], path: Path) -> None: + lines = [ + "# cWUSDC Mainnet Role Audit", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Token: `{payload['token']['address']}`", + "", + "## Role IDs", + "", + "| Role | ID | Admin role ID |", + "|---|---|---|", + ] + for role, role_id in payload["roles"].items(): + lines.append(f"| `{role}` | `{role_id}` | `{payload['roleAdmins'][role]}` |") + lines.extend(["", "## Candidate Role Checks", "", "| Label | Address | Admin | Minter | Burner |", "|---|---|---:|---:|---:|"]) + for label, data in payload["candidateChecks"].items(): + roles = data["roles"] + lines.append( + f"| `{label}` | `{data['address']}` | `{roles['DEFAULT_ADMIN_ROLE']}` | `{roles['MINTER_ROLE']}` | `{roles['BURNER_ROLE']}` |" + ) + lines.extend(["", "## Event-Log Role Reconstruction", "", f"- Checked: `{payload['eventLogReview']['checked']}`", f"- Event count: `{payload['eventLogReview']['eventCount']}`", "", "| Role | Effective members from events |", "|---|---|"]) + for role, members in payload["eventLogReview"]["effectiveMembersFromEvents"].items(): + lines.append(f"| `{role}` | `{', '.join(members) if members else 'none observed'}` |") + lines.extend(["", "## Limitations", ""]) + lines.extend(f"- {item}" for item in payload["limitations"]) + path.write_text("\n".join(lines) + "\n") + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--token", default=CWUSDC) + parser.add_argument("--rpc-url", default="") + parser.add_argument("--json-out", type=Path, default=REPORT_JSON) + parser.add_argument("--md-out", type=Path, default=REPORT_MD) + args = parser.parse_args() + payload = build(args) + 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) + print(f"Wrote {args.json_out.relative_to(ROOT)}") + print(f"Wrote {args.md_out.relative_to(ROOT)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/audit-ei-matrix-onchain-readiness.sh b/scripts/verify/audit-ei-matrix-onchain-readiness.sh new file mode 100755 index 00000000..03a8f12a --- /dev/null +++ b/scripts/verify/audit-ei-matrix-onchain-readiness.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# On-chain readiness audit: EI matrix vs mainnet cWUSDC and/or Chain 138 cUSDC. +# Loads scripts/lib/load-project-env.sh for RPCs and token defaults. +# +# Usage: +# ./scripts/verify/audit-ei-matrix-onchain-readiness.sh --mainnet-only --min-mainnet-raw 12000000 +# ./scripts/verify/audit-ei-matrix-onchain-readiness.sh --both --min-mainnet-raw 1 --min-138-raw 1 --workers 6 --report-by-class +# Optional leading "--" is stripped (for shells that pass it through). +# Exit 1 if any wallet is below configured minima (CI gate). Use min 0 to only report. +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" +# shellcheck disable=SC1091 +source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" +[[ "${1:-}" == "--" ]] && shift +exec python3 "$PROJECT_ROOT/scripts/lib/ei_matrix_onchain_readiness_audit.py" "$@" diff --git a/scripts/verify/build-cmc-top10-ecosystem-coverage.py b/scripts/verify/build-cmc-top10-ecosystem-coverage.py new file mode 100755 index 00000000..db834f6d --- /dev/null +++ b/scripts/verify/build-cmc-top10-ecosystem-coverage.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python3 +"""Build a repo-side CMC top-10 ecosystem coverage matrix.""" + +from __future__ import annotations + +import json +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +OUT_JSON = ROOT / "reports/status/cmc-top10-ecosystem-coverage-latest.json" +OUT_MD = ROOT / "docs/04-configuration/CMC_TOP10_ECOSYSTEM_ACCESSIBILITY_MATRIX.md" + +SOURCE = { + "name": "CoinMarketCap coins page", + "url": "https://coinmarketcap.com/coins/", + "observedAt": "2026-05-11", + "note": "Ranks are volatile; rerun or update this snapshot before external outreach.", +} + +TOKENS: list[dict[str, Any]] = [ + { + "rank": 1, + "symbol": "BTC", + "name": "Bitcoin", + "accessibility": "non_evm_wrapped_lane_required", + "dbisTouchpoint": "cWBTC / BTC reserve-or-wrapper evidence lane", + "timeframe": "1-2 weeks repo evidence after wallet/venue binding", + "repoDoableNext": [ + "Bind canonical BTC custody/address evidence fields.", + "Add BTC venue and wrapped-asset evidence placeholders.", + "Keep claims as provenance-only until custody and liquidity are independently evidenced.", + ], + "requiresExternalHuman": False, + }, + { + "rank": 2, + "symbol": "ETH", + "name": "Ethereum", + "accessibility": "native_evm_core_surface", + "dbisTouchpoint": "WETH/cWETH, Ethereum Mainnet cWUSDC, gas/quote evidence", + "timeframe": "1-3 days repo hardening", + "repoDoableNext": [ + "Refresh Ethereum pool and quote-side evidence.", + "Add CMC/Dex/Gecko sanity checks for ETH-paired surfaces.", + ], + "requiresExternalHuman": False, + }, + { + "rank": 3, + "symbol": "USDT", + "name": "Tether USDt", + "accessibility": "evm_quote_asset_and_wrapped_transport", + "dbisTouchpoint": "cUSDT / cWUSDT", + "timeframe": "1-3 days repo-side; provider acceptance external", + "repoDoableNext": [ + "Refresh cUSDT/cWUSDT provider packet fields.", + "Validate official USDT quote addresses per chain.", + ], + "requiresExternalHuman": False, + }, + { + "rank": 4, + "symbol": "XRP", + "name": "XRP", + "accessibility": "xrpl_lane_required", + "dbisTouchpoint": "XRPLAdapter / wXRP / MintBurnController", + "timeframe": "1-2 weeks after wallet/trustline binding", + "repoDoableNext": [ + "Bind XRPL account and destination tag policy placeholders.", + "Document XRP reserve, trustline, and issuer requirements.", + ], + "requiresExternalHuman": False, + }, + { + "rank": 5, + "symbol": "BNB", + "name": "BNB", + "accessibility": "evm_compatible_bsc_lane", + "dbisTouchpoint": "BSC cW* routing and gas surface", + "timeframe": "2-5 days repo-side", + "repoDoableNext": [ + "Refresh BSC cW* pool and official quote evidence.", + "Check BNB gas budget and CMC report values.", + ], + "requiresExternalHuman": False, + }, + { + "rank": 6, + "symbol": "USDC", + "name": "USD Coin", + "accessibility": "primary_focus_ready_for_submission", + "dbisTouchpoint": "cUSDC / cWUSDC", + "timeframe": "submission-ready now; price/listing acceptance external", + "repoDoableNext": [ + "Keep Etherscan/CoinGecko/CMC/DexScreener packets current.", + "Maintain exact CAIP-19 discipline for Mainnet cWUSDC.", + ], + "requiresExternalHuman": False, + }, + { + "rank": 7, + "symbol": "SOL", + "name": "Solana", + "accessibility": "solana_spl_lane_required", + "dbisTouchpoint": "SolanaAdapter and config/solana-gru-bridge-lineup.json", + "timeframe": "3-7 days repo-side if mints are bound", + "repoDoableNext": [ + "Bind SPL mint placeholders and minimum rent/gas targets.", + "Separate confirmed Chain 138 adapter evidence from native Solana liquidity claims.", + ], + "requiresExternalHuman": False, + }, + { + "rank": 8, + "symbol": "TRX", + "name": "TRON", + "accessibility": "tron_wallet_and_energy_lane_required", + "dbisTouchpoint": "TronAdapter and derived/canonical Tron wallet evidence", + "timeframe": "3-7 days after address confirmation", + "repoDoableNext": [ + "Bind canonical Tron address policy placeholder.", + "Document TRX energy/bandwidth and TRC-20 inventory requirements.", + ], + "requiresExternalHuman": False, + }, + { + "rank": 9, + "symbol": "DOGE", + "name": "Dogecoin", + "accessibility": "new_non_evm_adapter_or_custody_lane", + "dbisTouchpoint": "future DOGE wrapper/custody evidence lane", + "timeframe": "1-3 weeks for serious repo evidence", + "repoDoableNext": [ + "Create DOGE custody and bridge evidence stub.", + "Keep DOGE out of provider claims until wallet, reserve, and venue evidence exist.", + ], + "requiresExternalHuman": False, + }, + { + "rank": 10, + "symbol": "HYPE", + "name": "Hyperliquid", + "accessibility": "new_chain_or_venue_research_required", + "dbisTouchpoint": "future Hyperliquid venue/asset touchpoint", + "timeframe": "1-3 weeks for discovery/evidence", + "repoDoableNext": [ + "Open a research stub for chain/asset identifiers and supported custody paths.", + "Do not include HYPE in liquidity or settlement claims until identifiers are bound.", + ], + "requiresExternalHuman": False, + }, +] + + +def table(headers: list[str], rows: list[list[Any]]) -> str: + def cell(value: Any) -> str: + if isinstance(value, list): + value = "
".join(str(item) for item in value) + return str(value).replace("|", "\\|").replace("\n", "
") + + return "\n".join( + [ + f"| {' | '.join(cell(header) for header in headers)} |", + f"| {' | '.join('---' for _ in headers)} |", + *[f"| {' | '.join(cell(value) for value in row)} |" for row in rows], + ] + ) + + +def main() -> int: + payload = { + "schema": "cmc-top10-ecosystem-coverage/v1", + "generatedAt": datetime.now(timezone.utc).isoformat(), + "source": SOURCE, + "summary": { + "tokenCount": len(TOKENS), + "repoDoableWithoutOutsideHumanCount": sum(1 for token in TOKENS if not token["requiresExternalHuman"]), + "externalAcceptanceStillRequired": [ + "CMC/CoinGecko/DexScreener/Etherscan listing and price acceptance", + "Any custody, bank, exchange, or provider-side manual review", + ], + }, + "tokens": TOKENS, + } + OUT_JSON.parent.mkdir(parents=True, exist_ok=True) + OUT_JSON.write_text(json.dumps(payload, indent=2) + "\n") + + lines = [ + "# CMC Top 10 Ecosystem Accessibility Matrix", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Source: [{SOURCE['name']}]({SOURCE['url']})", + f"- Observed: `{SOURCE['observedAt']}`", + f"- Boundary: {SOURCE['note']}", + "", + table( + ["Rank", "Token", "Accessibility", "DBIS touchpoint", "Repo-side timeframe", "Repo-doable next work"], + [ + [ + token["rank"], + f"{token['symbol']} ({token['name']})", + token["accessibility"], + token["dbisTouchpoint"], + token["timeframe"], + token["repoDoableNext"], + ] + for token in TOKENS + ], + ), + "", + "## Operating Rule", + "", + "This matrix is a repo-side planning artifact. It improves DBIS coverage discipline, but it does not imply that any external tracker, wallet, exchange, custodian, or market-data provider has accepted a token.", + ] + OUT_MD.parent.mkdir(parents=True, exist_ok=True) + OUT_MD.write_text("\n".join(lines) + "\n") + print(f"Wrote {OUT_JSON.relative_to(ROOT)}") + print(f"Wrote {OUT_MD.relative_to(ROOT)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/build-cwusdc-etherscan-value-dossier.py b/scripts/verify/build-cwusdc-etherscan-value-dossier.py new file mode 100755 index 00000000..8529d203 --- /dev/null +++ b/scripts/verify/build-cwusdc-etherscan-value-dossier.py @@ -0,0 +1,528 @@ +#!/usr/bin/env python3 +"""Build a submission-ready cWUSDC Etherscan Value dossier. + +The dossier intentionally separates Ethereum Mainnet cWUSDC evidence from +global cUSDC/cWUSDC family context. It is read-only: it runs monitors and proof +generators, then summarizes what can be submitted and what remains externally +blocked. +""" + +from __future__ import annotations + +import argparse +import datetime as dt +import json +import os +import subprocess +import urllib.parse +import urllib.request +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +REPORT_JSON = ROOT / "reports" / "status" / "cwusdc-etherscan-value-dossier-latest.json" +REPORT_MD = ROOT / "reports" / "status" / "cwusdc-etherscan-value-dossier-latest.md" + +CWUSDC = "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a" +ETHERSCAN_CHAINLIST_URL = "https://api.etherscan.io/v2/chainlist" +ETHERSCAN_V2_API = "https://api.etherscan.io/v2/api" +DEPLOYER_FALLBACK = "0x4A666F96fC8764181194447A7dFdb7d471b301C8" +L2_DEPOSIT_CHAINS = { + "10": "OP Mainnet", + "42161": "Arbitrum One Mainnet", +} +ARTIFACTS = { + "mainnetSupply": ROOT / "reports" / "status" / "cwusdc-supply-circulating-attestation-latest.json", + "globalFamilySupply": ROOT / "reports" / "status" / "global-cusdc-cwusdc-family-supply-proof-latest.json", + "feedAudit": ROOT / "reports" / "status" / "cusdc-cwusdc-etherscan-feed-audit-latest.json", + "propagation": ROOT / "reports" / "status" / "cwusdc-etherscan-value-propagation-latest.json", +} +DOCS = { + "executionPlan": ROOT / "docs" / "04-configuration" / "etherscan" / "CWUSDC_ETHERSCAN_VALUE_EXECUTION_PLAN.md", + "bridgeLayerMap": ROOT / "docs" / "04-configuration" / "etherscan" / "CWUSDC_ETHERSCAN_BRIDGE_CROSSCHAIN_LAYER_MAP.md", + "profilePacket": ROOT / "docs" / "04-configuration" / "etherscan" / "CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md", + "e2eRecommendations": ROOT / "docs" / "04-configuration" / "etherscan" / "CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md", + "trackerPacket": ROOT / "docs" / "04-configuration" / "coingecko" / "CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md", +} + + +def load_dotenv(path: Path, env: dict[str, str]) -> dict[str, str]: + if not path.exists(): + return env + merged = dict(env) + for line in path.read_text().splitlines(): + line = line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + key, value = line.split("=", 1) + key = key.strip() + value = value.strip().strip('"').strip("'") + if key and key not in merged: + merged[key] = value + return merged + + +def run_command(command: list[str], env: dict[str, str]) -> dict[str, Any]: + proc = subprocess.run( + command, + cwd=ROOT, + env=env, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=False, + ) + return { + "command": command, + "returncode": proc.returncode, + "stdout": proc.stdout.strip(), + "stderr": proc.stderr.strip(), + "ok": proc.returncode == 0, + } + + +def read_json(path: Path) -> Any | None: + if not path.exists(): + return None + return json.loads(path.read_text()) + + +def fetch_json_url(url: str, timeout: int = 30) -> Any: + req = urllib.request.Request(url, headers={"User-Agent": "dbis-cwusdc-etherscan-dossier/1.0"}) + with urllib.request.urlopen(req, timeout=timeout) as response: + return json.loads(response.read().decode("utf-8")) + + +def load_etherscan_chainlist() -> dict[str, Any]: + try: + payload = fetch_json_url(ETHERSCAN_CHAINLIST_URL) + except Exception as exc: # noqa: BLE001 - dossier should capture diagnostics instead of crashing + return { + "url": ETHERSCAN_CHAINLIST_URL, + "available": False, + "error": str(exc), + "totalcount": None, + "supportedChainIds": [], + "statusByChainId": {}, + } + + result = payload.get("result") if isinstance(payload, dict) else None + chains = result if isinstance(result, list) else [] + status_by_chain_id = { + str(item.get("chainid")): { + "chainname": item.get("chainname"), + "blockexplorer": item.get("blockexplorer"), + "apiurl": item.get("apiurl"), + "status": item.get("status"), + "comment": item.get("comment"), + } + for item in chains + if isinstance(item, dict) and item.get("chainid") is not None + } + return { + "url": ETHERSCAN_CHAINLIST_URL, + "available": True, + "comments": payload.get("comments") if isinstance(payload, dict) else None, + "totalcount": payload.get("totalcount") if isinstance(payload, dict) else len(chains), + "supportedChainIds": sorted(status_by_chain_id, key=lambda value: int(value) if value.isdigit() else value), + "statusByChainId": status_by_chain_id, + } + + +def human_token_value(raw: Any, token_address: str | None) -> str | None: + try: + raw_int = int(str(raw)) + except (TypeError, ValueError): + return None + decimals = 18 if token_address == "ETH" else 6 if token_address and token_address.lower() == CWUSDC.lower() else 18 + whole = raw_int // (10**decimals) + frac = str(raw_int % (10**decimals)).rjust(decimals, "0").rstrip("0") + return f"{whole}" + (f".{frac}" if frac else "") + + +def human_wei(raw: Any) -> str | None: + try: + raw_int = int(str(raw)) + except (TypeError, ValueError): + return None + whole = raw_int // (10**18) + frac = str(raw_int % (10**18)).rjust(18, "0").rstrip("0") + return f"{whole}" + (f".{frac}" if frac else "") + + +def normalize_deposit_row(row: dict[str, Any]) -> dict[str, Any]: + token_address = row.get("tokenAddress") + return { + "hash": row.get("hash"), + "l1TransactionHash": row.get("L1transactionhash"), + "timeStamp": row.get("timeStamp"), + "from": row.get("from"), + "to": row.get("to"), + "valueRaw": row.get("value"), + "valueEth": human_wei(row.get("value")), + "tokenAddress": token_address, + "tokenValueRaw": row.get("tokenValue"), + "tokenValueUnits": human_token_value(row.get("tokenValue"), token_address), + "txreceiptStatus": row.get("txreceipt_status"), + "isError": row.get("isError"), + } + + +def etherscan_v2_call(params: dict[str, str], api_key: str) -> dict[str, Any]: + query = {**params, "apikey": api_key} + url = f"{ETHERSCAN_V2_API}?{urllib.parse.urlencode(query)}" + redacted_query = {**params, "apikey": "REDACTED"} + redacted_url = f"{ETHERSCAN_V2_API}?{urllib.parse.urlencode(redacted_query)}" + try: + payload = fetch_json_url(url) + except Exception as exc: # noqa: BLE001 - capture diagnostics instead of crashing the dossier + return {"url": redacted_url, "ok": False, "error": str(exc), "status": None, "message": None, "result": None} + status = str(payload.get("status")) if isinstance(payload, dict) else None + message = payload.get("message") if isinstance(payload, dict) else None + result = payload.get("result") if isinstance(payload, dict) else None + no_rows = isinstance(result, str) and "No transactions found" in result + return { + "url": redacted_url, + "ok": status == "1" or no_rows, + "status": status, + "message": message, + "result": [] if no_rows else result, + "error": None, + } + + +def load_l2_deposit_evidence(api_key: str, chainlist: dict[str, Any], address: str) -> dict[str, Any]: + if not api_key: + return { + "checked": False, + "reason": "ETHERSCAN_API_KEY is not set.", + "address": address, + "chains": {}, + } + + supported = set(chainlist.get("statusByChainId", {})) + chains: dict[str, Any] = {} + for chain_id, chain_name in L2_DEPOSIT_CHAINS.items(): + if chain_id not in supported: + chains[chain_id] = { + "chainName": chain_name, + "checked": False, + "reason": "chain is not present in Etherscan V2 chainlist", + } + continue + response = etherscan_v2_call( + { + "chainid": chain_id, + "module": "account", + "action": "getdeposittxs", + "address": address, + "page": "1", + "offset": "10", + "sort": "desc", + }, + api_key, + ) + result = response.get("result") + rows = result if isinstance(result, list) else [] + chains[chain_id] = { + "chainName": chain_name, + "checked": True, + "ok": response.get("ok"), + "status": response.get("status"), + "message": response.get("message"), + "sampleCount": len(rows), + "latest": normalize_deposit_row(rows[0]) if rows else None, + "url": response.get("url"), + "error": response.get("error"), + } + + return { + "checked": True, + "address": address, + "scope": "Etherscan-indexed L2 deposits by address. This is bridge provenance only and does not set Mainnet cWUSDC USD Value.", + "rawUnitNote": "tokenValue is returned as raw token units. ETH uses 18 decimals; ERC-20 rows must be normalized with that token contract's decimals.", + "chains": chains, + } + + +def load_contract_source_verification(api_key: str, address: str) -> dict[str, Any]: + if not api_key: + return { + "checked": False, + "reason": "ETHERSCAN_API_KEY is not set.", + "address": address, + "verified": False, + } + response = etherscan_v2_call( + { + "chainid": "1", + "module": "contract", + "action": "getsourcecode", + "address": address, + }, + api_key, + ) + result = response.get("result") + entry = result[0] if isinstance(result, list) and result and isinstance(result[0], dict) else {} + source_code = str(entry.get("SourceCode") or "") + abi = str(entry.get("ABI") or "") + contract_name = str(entry.get("ContractName") or "") + return { + "checked": True, + "address": address, + "ok": response.get("ok"), + "status": response.get("status"), + "message": response.get("message"), + "verified": bool(source_code and contract_name and abi and abi != "Contract source code not verified"), + "contractName": contract_name or None, + "compilerVersion": entry.get("CompilerVersion") or None, + "optimizationUsed": entry.get("OptimizationUsed") or None, + "runs": entry.get("Runs") or None, + "constructorArgumentsPresent": bool(entry.get("ConstructorArguments")), + "evmVersion": entry.get("EVMVersion") or None, + "licenseType": entry.get("LicenseType") or None, + "proxy": entry.get("Proxy") or None, + "implementation": entry.get("Implementation") or None, + "sourceCodeBytes": len(source_code), + "abiAvailable": bool(abi and abi != "Contract source code not verified"), + "url": response.get("url"), + "error": response.get("error"), + } + + +def rel(path: Path) -> str: + return str(path.relative_to(ROOT)) + + +def build(args: argparse.Namespace) -> dict[str, Any]: + env = load_dotenv(ROOT / ".env", dict(os.environ)) + etherscan_api_key = env.get("ETHERSCAN_API_KEY", "") + l2_deposit_address = args.l2_deposit_address or env.get("DEPLOYER_ADDRESS") or DEPLOYER_FALLBACK + commands: list[dict[str, Any]] = [] + if args.refresh: + commands = [ + run_command(["python3", "scripts/verify/generate-cwusdc-supply-circulating-attestation.py"], env), + run_command(["python3", "scripts/verify/generate-global-cusdc-cwusdc-family-supply-proof.py"], env), + run_command(["python3", "scripts/verify/audit-cusdc-cwusdc-etherscan-feeds.py"], env), + run_command(["python3", "scripts/verify/monitor-cwusdc-etherscan-value-propagation.py"], env), + run_command(["bash", "scripts/verify/check-cwusdc-etherscan-prereq-urls.sh"], env), + ] + + artifacts = {key: read_json(path) for key, path in ARTIFACTS.items()} + propagation = artifacts["propagation"] or {} + supply = artifacts["mainnetSupply"] or {} + global_family = artifacts["globalFamilySupply"] or {} + feed_audit = artifacts["feedAudit"] or {} + chainlist = load_etherscan_chainlist() + l2_deposits = load_l2_deposit_evidence(etherscan_api_key, chainlist, l2_deposit_address) + contract_source = load_contract_source_verification(etherscan_api_key, CWUSDC) + family_chain_ids = sorted( + {str(item.get("chainId")) for item in global_family.get("entries", []) if isinstance(item, dict) and item.get("chainId") is not None}, + key=lambda value: int(value) if value.isdigit() else value, + ) + supported_family_chain_ids = [chain_id for chain_id in family_chain_ids if chain_id in chainlist.get("statusByChainId", {})] + unsupported_family_chain_ids = [chain_id for chain_id in family_chain_ids if chain_id not in chainlist.get("statusByChainId", {})] + + blockers = list(((propagation.get("summary") or {}).get("blockers") or [])) + command_failures = [item for item in commands if not item["ok"]] + for item in command_failures: + blockers.append("Command failed: " + " ".join(item["command"])) + + ready_evidence = { + "mainnetSupplyAttestation": bool(supply.get("supply")), + "globalFamilySupplyContext": bool(global_family.get("summary")), + "chain138MainnetFeedAudit": bool(feed_audit.get("canonicalRelationship")), + "mainnetContractSourceVerified": bool(contract_source.get("verified")), + "propagationMonitor": bool(propagation.get("checks")), + "publicPrereqUrls": not any( + item["command"] == ["bash", "scripts/verify/check-cwusdc-etherscan-prereq-urls.sh"] and not item["ok"] + for item in commands + ) + if commands + else None, + "documentationPacket": {key: path.exists() for key, path in DOCS.items()}, + } + + next_actions = [] + if blockers: + next_actions.extend( + [ + "Submit/update Etherscan token profile for the exact Ethereum Mainnet cWUSDC contract.", + "Submit/update CoinGecko and CoinMarketCap listings with Mainnet supply proof, DEX evidence, and bridge-family context.", + "Use the global family supply proof only as context; use the Ethereum Mainnet cWUSDC attestation as the token-page supply basis.", + "Re-run this dossier after each external approval or tracker response.", + ] + ) + else: + next_actions.append("No blockers detected by local monitors; capture Etherscan screenshots and continue propagation monitoring.") + + return { + "schema": "cwusdc-etherscan-value-dossier/v1", + "generatedAt": dt.datetime.now(dt.UTC).isoformat().replace("+00:00", "Z"), + "purpose": "Single submission and monitoring packet for making Etherscan show USD value for Ethereum Mainnet cWUSDC.", + "target": { + "network": "Ethereum Mainnet", + "chainId": 1, + "contract": CWUSDC, + "caip19": f"eip155:1/erc20:{CWUSDC}", + "name": "Wrapped cUSDC", + "symbol": "cWUSDC", + "decimals": 6, + }, + "readiness": { + "readyForExternalSubmission": ready_evidence["mainnetSupplyAttestation"] + and ready_evidence["chain138MainnetFeedAudit"] + and ready_evidence["mainnetContractSourceVerified"] + and ready_evidence["propagationMonitor"], + "etherscanValueReady": (propagation.get("summary") or {}).get("etherscanValueReady"), + "coinGeckoPriceReady": (propagation.get("summary") or {}).get("coingeckoPriceReady"), + "blockers": blockers, + }, + "evidence": { + "artifacts": {key: rel(path) for key, path in ARTIFACTS.items()}, + "docs": {key: rel(path) for key, path in DOCS.items()}, + "readyEvidence": ready_evidence, + "mainnetContractSourceVerification": contract_source, + "mainnetSupply": supply.get("supply"), + "globalFamilyWarning": (global_family.get("caveats") or ["Global family supply is context only; do not use it as Ethereum Etherscan token-page supply."])[0], + "globalFamilySummary": global_family.get("summary"), + "feedRelationship": feed_audit.get("canonicalRelationship"), + "etherscanChainlist": { + **chainlist, + "familyChainIds": family_chain_ids, + "etherscanSupportedFamilyChainIds": supported_family_chain_ids, + "notEtherscanSupportedFamilyChainIds": unsupported_family_chain_ids, + "chain138SupportedByEtherscanV2": "138" in chainlist.get("statusByChainId", {}), + "interpretation": "Only chains present in Etherscan V2 chainlist should be described as first-class Etherscan-family API evidence. Chain 138 remains provenance/context evidence unless Etherscan adds chainid 138.", + }, + "l2DepositTransactions": l2_deposits, + }, + "commands": commands, + "nextActions": next_actions, + } + + +def write_md(payload: dict[str, Any], path: Path) -> None: + readiness = payload["readiness"] + evidence = payload["evidence"] + target = payload["target"] + lines = [ + "# cWUSDC Etherscan Value Dossier", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Target: `{target['contract']}`", + f"- CAIP-19: `{target['caip19']}`", + f"- Ready for external submission: `{readiness['readyForExternalSubmission']}`", + f"- Etherscan value ready: `{readiness['etherscanValueReady']}`", + f"- CoinGecko price ready: `{readiness['coinGeckoPriceReady']}`", + "", + "## Blockers", + "", + ] + if readiness["blockers"]: + lines.extend(f"- {item}" for item in readiness["blockers"]) + else: + lines.append("- None detected by this dossier.") + + lines.extend( + [ + "", + "## Evidence Artifacts", + "", + "| Artifact | Path |", + "|---|---|", + ] + ) + for key, value in evidence["artifacts"].items(): + lines.append(f"| `{key}` | `{value}` |") + + lines.extend(["", "## Documentation Packet", "", "| Document | Path |", "|---|---|"]) + for key, value in evidence["docs"].items(): + lines.append(f"| `{key}` | `{value}` |") + + lines.extend( + [ + "", + "## Mainnet Contract Verification", + "", + f"- Checked: `{(evidence['mainnetContractSourceVerification'] or {}).get('checked')}`", + f"- Verified: `{(evidence['mainnetContractSourceVerification'] or {}).get('verified')}`", + f"- Contract name: `{(evidence['mainnetContractSourceVerification'] or {}).get('contractName')}`", + f"- Compiler: `{(evidence['mainnetContractSourceVerification'] or {}).get('compilerVersion')}`", + f"- License: `{(evidence['mainnetContractSourceVerification'] or {}).get('licenseType')}`", + f"- Proxy: `{(evidence['mainnetContractSourceVerification'] or {}).get('proxy')}`", + "", + "## Supply Boundary", + "", + f"- Ethereum Mainnet cWUSDC supply basis: `{(evidence['mainnetSupply'] or {}).get('totalSupplyUnits')}`", + f"- Circulating supply basis: `{(evidence['mainnetSupply'] or {}).get('circulatingSupplyUnits')}`", + f"- Global family warning: {evidence['globalFamilyWarning']}", + "", + "## Etherscan Chainlist Boundary", + "", + f"- Etherscan V2 chainlist total: `{(evidence['etherscanChainlist'] or {}).get('totalcount')}`", + f"- Family chain IDs: `{', '.join((evidence['etherscanChainlist'] or {}).get('familyChainIds') or [])}`", + f"- Etherscan-supported family chain IDs: `{', '.join((evidence['etherscanChainlist'] or {}).get('etherscanSupportedFamilyChainIds') or [])}`", + f"- Not Etherscan-supported family chain IDs: `{', '.join((evidence['etherscanChainlist'] or {}).get('notEtherscanSupportedFamilyChainIds') or [])}`", + f"- Chain 138 supported by Etherscan V2: `{(evidence['etherscanChainlist'] or {}).get('chain138SupportedByEtherscanV2')}`", + "", + "## L2 Deposit Transaction Boundary", + "", + f"- Address checked: `{(evidence['l2DepositTransactions'] or {}).get('address')}`", + f"- Checked: `{(evidence['l2DepositTransactions'] or {}).get('checked')}`", + "- Scope: Etherscan-indexed OP/Arbitrum deposit provenance only; it does not set Mainnet cWUSDC USD Value.", + "- Unit note: `value` is raw wei; `tokenValue` is raw token units. `1195403000000000` in `value` is `0.001195403 ETH`; `598200000000000` with `tokenAddress=ETH` is `0.0005982 ETH`.", + "", + "| Chain | Checked | Sample deposits | Latest tx | Native value | Token value |", + "|---|---:|---:|---|---:|---:|", + ] + ) + for chain_id, item in (evidence["l2DepositTransactions"].get("chains") or {}).items(): + latest = item.get("latest") or {} + lines.append( + f"| `{chain_id}` {item.get('chainName')} | `{item.get('checked')}` | `{item.get('sampleCount', 0)}` | `{latest.get('hash')}` | `{latest.get('valueEth')}` ETH | `{latest.get('tokenValueUnits')}` {latest.get('tokenAddress') or ''} |" + ) + lines.extend( + [ + "", + "## Next Actions", + "", + ] + ) + lines.extend(f"- {item}" for item in payload["nextActions"]) + + if payload["commands"]: + lines.extend(["", "## Command Results", "", "| Command | Exit |", "|---|---:|"]) + for item in payload["commands"]: + lines.append(f"| `{' '.join(item['command'])}` | `{item['returncode']}` |") + + path.write_text("\n".join(lines) + "\n") + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--no-refresh", action="store_true", help="Only aggregate existing reports; do not rerun checks.") + parser.add_argument("--json-out", type=Path, default=REPORT_JSON) + parser.add_argument("--md-out", type=Path, default=REPORT_MD) + parser.add_argument("--l2-deposit-address", default="", help="Address to check with Etherscan getdeposittxs.") + parser.add_argument("--strict", action="store_true") + args = parser.parse_args() + args.refresh = not args.no_refresh + + payload = build(args) + 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) + print(f"Wrote {args.json_out.relative_to(ROOT)}") + print(f"Wrote {args.md_out.relative_to(ROOT)}") + print(f"readyForExternalSubmission={payload['readiness']['readyForExternalSubmission']}") + if payload["readiness"]["blockers"]: + print("Blockers: " + "; ".join(payload["readiness"]["blockers"])) + if args.strict and payload["readiness"]["blockers"]: + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/build-cwusdc-institutional-evidence-bundle.sh b/scripts/verify/build-cwusdc-institutional-evidence-bundle.sh new file mode 100755 index 00000000..b79024ef --- /dev/null +++ b/scripts/verify/build-cwusdc-institutional-evidence-bundle.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +set -euo pipefail + +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$PROJECT_ROOT" + +DATE_TAG="${CWUSDC_EVIDENCE_BUNDLE_DATE:-$(date -u +%Y%m%d)}" +OUT_DIR="reports/status" +BUNDLE="${OUT_DIR}/cwusdc-institutional-evidence-bundle-${DATE_TAG}.tar.gz" +CHECKSUM="${OUT_DIR}/cwusdc-institutional-evidence-bundle-${DATE_TAG}.sha256" + +FILES=( + "docs/04-configuration/etherscan/CWUSDC_EVIDENCE_BUNDLE_INDEX.md" + "docs/04-configuration/etherscan/CWUSDC_SUPPLY_AND_CIRCULATING_METHODOLOGY.md" + "docs/04-configuration/etherscan/CWUSDC_SECURITY_AND_AUDIT_DISCLOSURE.md" + "docs/04-configuration/etherscan/CWUSDC_PROVIDER_RESPONSE_TRACKER.md" + "docs/04-configuration/etherscan/CWUSDC_LIQUIDITY_READINESS_NO_BROADCAST_PLAN.md" + "docs/04-configuration/CWUSDC_PROVIDER_SUBMISSION_PACKET.md" + "docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md" + "docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md" + "docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_VALUE_EXECUTION_PLAN.md" + "docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_BRIDGE_CROSSCHAIN_LAYER_MAP.md" + "docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md" + "docs/04-configuration/dexscreener/CWUSDC_DEXSCREENER_INDEXING_AND_PROFILE_PACKET_20260509.md" + "docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md" + "reports/status/cwusdc-etherscan-value-dossier-latest.md" + "reports/status/cwusdc-etherscan-value-dossier-latest.json" + "reports/status/cwusdc-supply-circulating-attestation-latest.md" + "reports/status/cwusdc-supply-circulating-attestation-latest.json" + "reports/status/global-cusdc-cwusdc-family-supply-proof-latest.md" + "reports/status/global-cusdc-cwusdc-family-supply-proof-latest.json" + "reports/status/cusdc-cwusdc-etherscan-feed-audit-latest.md" + "reports/status/cusdc-cwusdc-etherscan-feed-audit-latest.json" + "reports/status/cwusdc-mainnet-role-audit-latest.md" + "reports/status/cwusdc-mainnet-role-audit-latest.json" + "reports/status/cwusdc-role-deployment-appendix-latest.md" + "reports/status/cwusdc-role-deployment-appendix-latest.json" + "reports/status/cwusdc-institutional-doc-link-check-latest.md" + "reports/status/cwusdc-institutional-doc-link-check-latest.json" + "reports/status/cwusdc-provider-submission-prefill-latest.md" + "reports/status/cwusdc-provider-submission-prefill-latest.json" + "reports/status/cwusdc-provider-handoff-latest.md" + "reports/status/cwusdc-provider-handoff-latest.json" + "reports/status/cwusdc-external-trackers-live-latest.md" + "reports/status/cwusdc-external-trackers-live-latest.json" + "reports/status/cwusdc-institutional-readiness-review-20260511.md" + "reports/status/cwusdc-institutional-hardening-completion-20260511.md" +) + +OPTIONAL_FILES=( + "reports/status/cwusdc-provider-monitoring-snapshot-latest.md" + "reports/status/cwusdc-provider-monitoring-snapshot-latest.json" + "reports/status/screenshots/cwusdc-etherscan-token-page.png" + "reports/status/screenshots/cwusdc-dbis-token-directory.png" + "reports/status/screenshots/cwusdc-logo-url.png" + "reports/status/screenshots/cwusdc-geckoterminal-univ3-pool.png" +) + +missing=() +present=() +for file in "${FILES[@]}"; do + if [[ -f "$file" ]]; then + present+=("$file") + else + missing+=("$file") + fi +done +for file in "${OPTIONAL_FILES[@]}"; do + if [[ -f "$file" ]]; then + present+=("$file") + fi +done + +if ((${#missing[@]} > 0)); then + printf 'Missing required evidence files:\n' >&2 + printf ' - %s\n' "${missing[@]}" >&2 + exit 1 +fi + +mkdir -p "$OUT_DIR" +tar -czf "$BUNDLE" "${present[@]}" +sha256sum "$BUNDLE" > "$CHECKSUM" + +printf 'Wrote %s\n' "$BUNDLE" +printf 'Wrote %s\n' "$CHECKSUM" +cat "$CHECKSUM" diff --git a/scripts/verify/build-cwusdc-provider-handoff-report.py b/scripts/verify/build-cwusdc-provider-handoff-report.py new file mode 100755 index 00000000..38dfdbf7 --- /dev/null +++ b/scripts/verify/build-cwusdc-provider-handoff-report.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python3 +"""Build a concise cWUSDC provider handoff report from latest probe JSON.""" +from __future__ import annotations + +import argparse +import json +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +DEFAULT_PREREQ_JSON = ROOT / "reports/status/cwusdc-etherscan-prereq-urls-latest.json" +DEFAULT_TRACKERS_JSON = ROOT / "reports/status/cwusdc-external-trackers-live-latest.json" +DEFAULT_LIQUIDITY_JSON = ROOT / "reports/status/token-aggregation-liquidity-gap-funding-plan-latest.json" +DEFAULT_CMC_SANITY_JSON = ROOT / "reports/status/cmc-provider-report-sanity-latest.json" +DEFAULT_MD = ROOT / "reports/status/cwusdc-provider-handoff-latest.md" +DEFAULT_JSON = ROOT / "reports/status/cwusdc-provider-handoff-latest.json" + + +def read_json(path: Path) -> Any | None: + if not path.exists(): + return None + return json.loads(path.read_text()) + + +def rel(path: Path) -> str: + try: + return str(path.relative_to(ROOT)) + except ValueError: + return str(path) + + +def first(obj: dict[str, Any] | None, path: list[str], default: Any = None) -> Any: + cur: Any = obj + for part in path: + if not isinstance(cur, dict): + return default + cur = cur.get(part) + return cur if cur is not None else default + + +def table(headers: list[str], rows: list[list[Any]]) -> str: + def cell(value: Any) -> str: + if isinstance(value, (dict, list)): + value = json.dumps(value, sort_keys=True) + text = str(value) + return text.replace("|", "\\|").replace("\n", "
") + + return "\n".join([ + f"| {' | '.join(cell(header) for header in headers)} |", + f"| {' | '.join('---' for _ in headers)} |", + *[f"| {' | '.join(cell(value) for value in row)} |" for row in rows], + ]) + + +def build_payload( + prereq: Any, + trackers: Any, + liquidity: Any, + cmc_sanity: Any, + prereq_path: Path, + trackers_path: Path, + liquidity_path: Path, + cmc_sanity_path: Path, +) -> dict[str, Any]: + tracker_summary = first(trackers, ["summary"], {}) + failed_required = tracker_summary.get("failedRequiredIds") if isinstance(tracker_summary, dict) else [] + liquidity_summary = first(liquidity, ["summary"], {}) + blockers = [] + + if prereq is None: + blockers.append({ + "id": "missing_prereq_url_evidence", + "type": "repo_controlled", + "status": "blocked", + "nextAction": "Run check-cwusdc-etherscan-prereq-urls.sh with JSON output.", + }) + if trackers is None: + blockers.append({ + "id": "missing_external_tracker_evidence", + "type": "repo_controlled", + "status": "blocked", + "nextAction": "Run check-cwusdc-external-trackers-live.sh with JSON output.", + }) + if liquidity is None: + blockers.append({ + "id": "missing_liquidity_planner_evidence", + "type": "repo_controlled", + "status": "blocked", + "nextAction": "Run plan-token-aggregation-liquidity-gap-funding.mjs.", + }) + if cmc_sanity is None: + blockers.append({ + "id": "missing_cmc_report_sanity_evidence", + "type": "repo_controlled", + "status": "blocked", + "nextAction": "Run check-cmc-provider-report-sanity.py.", + }) + + if prereq is not None and not first(prereq, ["summary", "allPassed"], False): + blockers.append({ + "id": "repo_public_urls", + "type": "repo_controlled", + "status": "blocked", + "nextAction": "Fix failing d-bis.org prerequisite URLs before external profile submission.", + }) + + for failed in failed_required or []: + blockers.append({ + "id": failed, + "type": "external_provider", + "status": "blocked", + "nextAction": "Submit/update provider packet or wait for provider indexing, then rerun tracker probe.", + }) + + if first(liquidity, ["summary", "nonEvmFundingRequirementRows"], 0): + blockers.append({ + "id": "non_evm_funding_requirements", + "type": "operator_bound", + "status": "open", + "nextAction": "Bind non-EVM wallets, asset IDs, and minimum funding targets before making non-EVM liquidity claims.", + }) + if first(cmc_sanity, ["summary", "warningCount"], 0): + blockers.append({ + "id": "cmc_report_sanity_warnings", + "type": "repo_advisory", + "status": "open", + "nextAction": "Review CMC-shaped report warnings before using CMC fields as listing-quality liquidity or quote-asset evidence.", + }) + + repo_ready = bool(first(prereq, ["summary", "allPassed"], False)) + ready_for_etherscan_value = bool(first(trackers, ["summary", "readyForEtherscanUsdValue"], False)) + + return { + "schema": "cwusdc-provider-handoff/v1", + "generatedAt": datetime.now(timezone.utc).isoformat(), + "inputs": { + "prereq": rel(prereq_path), + "trackers": rel(trackers_path), + "liquidity": rel(liquidity_path), + "cmcSanity": rel(cmc_sanity_path), + }, + "summary": { + "repoControlledPrereqsPassed": repo_ready, + "externalTrackersAllLive": bool(first(trackers, ["summary", "allTrackersLive"], False)), + "readyForEtherscanUsdValue": ready_for_etherscan_value, + "externalRequiredPassed": first(trackers, ["summary", "requiredPassedCount"], None), + "externalRequiredCount": first(trackers, ["summary", "requiredCount"], None), + "liquidityRows": first(liquidity, ["summary", "rows"], None), + "nonEvmFundingRequirementRows": first(liquidity, ["summary", "nonEvmFundingRequirementRows"], None), + "cmcSanityWarningCount": first(cmc_sanity, ["summary", "warningCount"], None), + "cmcPromotedTokenCount": first(cmc_sanity, ["summary", "promotedTokenCount"], None), + "blockerCount": len(blockers), + }, + "blockers": blockers, + } + + +def write_markdown(payload: dict[str, Any], prereq: Any, trackers: Any, liquidity: Any, cmc_sanity: Any, path: Path) -> None: + prereq_checks = first(prereq, ["checks"], []) or [] + tracker_checks = first(trackers, ["checks"], []) or [] + liquidity_summary = first(liquidity, ["summary"], {}) or {} + cmc_summary = first(cmc_sanity, ["summary"], {}) or {} + cmc_warnings = first(cmc_sanity, ["warnings"], []) or [] + + lines = [ + "# cWUSDC Provider Handoff Report", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Repo-controlled prerequisites passed: `{payload['summary']['repoControlledPrereqsPassed']}`", + f"- External trackers all live: `{payload['summary']['externalTrackersAllLive']}`", + f"- Ready for Etherscan USD Value path: `{payload['summary']['readyForEtherscanUsdValue']}`", + "", + "## Inputs", + "", + table(["Input", "Path"], [[key, value] for key, value in payload["inputs"].items()]), + "", + "## Repo-Controlled URL Prerequisites", + "", + table( + ["URL", "Passed", "HTTP", "Attempts", "curl status"], + [[ + c.get("url"), + f"`{c.get('passed')}`", + f"`{c.get('status')}`", + f"`{c.get('attempts', '-')}`", + f"`{c.get('curlStatus', '-')}`", + ] for c in prereq_checks], + ) if prereq_checks else "No prerequisite URL JSON found.", + "", + "## External Tracker State", + "", + table( + ["Surface", "Passed", "HTTP", "Details"], + [[ + c.get("id"), + f"`{c.get('passed')}`", + f"`{c.get('status')}`", + "; ".join(c.get("details") or []) or c.get("error") or "-", + ] for c in tracker_checks], + ) if tracker_checks else "No external tracker JSON found.", + "", + "## Liquidity Planner Summary", + "", + table(["Metric", "Value"], [[key, value] for key, value in liquidity_summary.items()]), + "", + "## CMC Report Sanity", + "", + table(["Metric", "Value"], [[key, value] for key, value in cmc_summary.items()]) if cmc_summary else "No CMC sanity JSON found.", + "", + table( + ["ID", "Symbol", "Severity", "Message"], + [[w.get("id"), w.get("symbol", "-"), w.get("severity"), w.get("message")] for w in cmc_warnings], + ) if cmc_warnings else "No CMC sanity warnings.", + "", + "## Blockers", + "", + table( + ["ID", "Type", "Status", "Next action"], + [[b["id"], b["type"], b["status"], b["nextAction"]] for b in payload["blockers"]], + ) if payload["blockers"] else "No current blockers detected.", + "", + "## Submission Boundary", + "", + "This report is generated from public/read-only repo checks. It does not submit forms, approve tokens, add liquidity, swap, bridge, or broadcast transactions.", + ] + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text("\n".join(lines) + "\n") + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--prereq-json", type=Path, default=DEFAULT_PREREQ_JSON) + parser.add_argument("--trackers-json", type=Path, default=DEFAULT_TRACKERS_JSON) + parser.add_argument("--liquidity-json", type=Path, default=DEFAULT_LIQUIDITY_JSON) + parser.add_argument("--cmc-sanity-json", type=Path, default=DEFAULT_CMC_SANITY_JSON) + parser.add_argument("--json-out", type=Path, default=DEFAULT_JSON) + parser.add_argument("--md-out", type=Path, default=DEFAULT_MD) + args = parser.parse_args() + + prereq = read_json(args.prereq_json) + trackers = read_json(args.trackers_json) + liquidity = read_json(args.liquidity_json) + cmc_sanity = read_json(args.cmc_sanity_json) + payload = build_payload( + prereq, + trackers, + liquidity, + cmc_sanity, + args.prereq_json, + args.trackers_json, + args.liquidity_json, + args.cmc_sanity_json, + ) + + args.json_out.parent.mkdir(parents=True, exist_ok=True) + args.json_out.write_text(json.dumps(payload, indent=2) + "\n") + write_markdown(payload, prereq, trackers, liquidity, cmc_sanity, args.md_out) + print(f"Wrote {rel(args.json_out)}") + print(f"Wrote {rel(args.md_out)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/build-cwusdc-provider-submission-prefill.py b/scripts/verify/build-cwusdc-provider-submission-prefill.py new file mode 100644 index 00000000..2f2f85ce --- /dev/null +++ b/scripts/verify/build-cwusdc-provider-submission-prefill.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +"""Generate prefilled provider submission packets and screenshot checklist for cWUSDC.""" + +from __future__ import annotations + +import datetime as dt +import json +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +REPORT_JSON = ROOT / "reports" / "status" / "cwusdc-provider-submission-prefill-latest.json" +REPORT_MD = ROOT / "reports" / "status" / "cwusdc-provider-submission-prefill-latest.md" + +ASSET = { + "network": "Ethereum Mainnet", + "chainId": 1, + "caip19": "eip155:1/erc20:0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a", + "contract": "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a", + "name": "Wrapped cUSDC", + "symbol": "cWUSDC", + "decimals": 6, + "website": "https://d-bis.org/", + "tokenDirectory": "https://d-bis.org/gru/tokens", + "logo": "https://d-bis.org/tokens/cwusdc.svg", + "contactEmail": "submissions@d-bis.org", + "supportEmail": "support@d-bis.org", + "securityUrl": "https://d-bis.org/security", + "nonAffiliation": "cWUSDC is not Circle-issued USDC and should not be represented as an official Circle asset.", +} + +DESCRIPTION = ( + "cWUSDC is the Ethereum Mainnet compliant wrapped transport representation of Chain 138 cUSDC " + "in the DBIS GRU asset family. It is used for public-network mirrored settlement, proof, and " + "interoperability workflows. cWUSDC is a DBIS/GRU transport asset and is not Circle-issued USDC." +) + +PROVIDERS = { + "etherscan": { + "objective": "Token profile/logo/value evidence submission", + "fields": { + "contract": ASSET["contract"], + "website": ASSET["website"], + "email": ASSET["contactEmail"], + "logo": ASSET["logo"], + "description": DESCRIPTION, + }, + "attachment": "docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md", + }, + "coingecko": { + "objective": "Token listing/update with supply and liquidity caveats", + "fields": { + "chain": ASSET["network"], + "contract": ASSET["contract"], + "symbol": ASSET["symbol"], + "website": ASSET["website"], + "logo": ASSET["logo"], + "description": DESCRIPTION, + }, + "attachment": "docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md", + }, + "coinmarketcap": { + "objective": "Token listing/update with DEX discoverability and supply proof", + "fields": { + "chain": ASSET["network"], + "contract": ASSET["contract"], + "symbol": ASSET["symbol"], + "website": ASSET["website"], + "logo": ASSET["logo"], + "description": DESCRIPTION, + }, + "attachment": "docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md", + }, + "dexscreener": { + "objective": "Token profile/indexing support packet", + "fields": { + "chain": "ethereum", + "tokenAddress": ASSET["contract"], + "website": ASSET["website"], + "logo": ASSET["logo"], + "description": DESCRIPTION, + }, + "attachment": "docs/04-configuration/dexscreener/CWUSDC_DEXSCREENER_INDEXING_AND_PROFILE_PACKET_20260509.md", + }, + "metamask": { + "objective": "Wallet metadata/price-provider support evidence", + "fields": { + "assetId": ASSET["caip19"], + "chainId": ASSET["chainId"], + "address": ASSET["contract"], + "symbol": ASSET["symbol"], + "logoURI": ASSET["logo"], + "description": DESCRIPTION, + }, + "attachment": "docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md", + }, +} + +SCREENSHOTS = [ + { + "id": "etherscan-token-page", + "url": f"https://etherscan.io/token/{ASSET['contract']}", + "target": "reports/status/screenshots/cwusdc-etherscan-token-page.png", + "reason": "Shows verified Mainnet token page and current value/market-cap state.", + }, + { + "id": "dbis-token-directory", + "url": ASSET["tokenDirectory"], + "target": "reports/status/screenshots/cwusdc-dbis-token-directory.png", + "reason": "Shows official website token context.", + }, + { + "id": "dbis-logo-url", + "url": ASSET["logo"], + "target": "reports/status/screenshots/cwusdc-logo-url.png", + "reason": "Shows hosted token logo asset.", + }, + { + "id": "geckoterminal-univ3-pool", + "url": "https://www.geckoterminal.com/eth/pools/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3", + "target": "reports/status/screenshots/cwusdc-geckoterminal-univ3-pool.png", + "reason": "Shows indexed public cWUSDC/USDC pool evidence.", + }, +] + + +def load_optional(path: str) -> Any: + full = ROOT / path + if not full.exists(): + return None + if full.suffix == ".json": + return json.loads(full.read_text()) + return full.read_text() + + +def build() -> dict[str, Any]: + screenshots = [] + for item in SCREENSHOTS: + entry = dict(item) + target = ROOT / entry["target"] + entry["captured"] = target.exists() + entry["sizeBytes"] = target.stat().st_size if target.exists() else 0 + screenshots.append(entry) + return { + "schema": "cwusdc-provider-submission-prefill/v1", + "generatedAt": dt.datetime.now(dt.UTC).isoformat().replace("+00:00", "Z"), + "asset": ASSET, + "description": DESCRIPTION, + "providers": PROVIDERS, + "screenshotChecklist": screenshots, + "currentReadiness": { + "dossier": load_optional("reports/status/cwusdc-etherscan-value-dossier-latest.json"), + "providerCi": load_optional("reports/status/cwusdc-provider-readiness-ci-latest.json"), + "supply": load_optional("reports/status/cwusdc-supply-circulating-attestation-latest.json"), + }, + "submissionBoundary": [ + "This packet is prefilled evidence only; it does not prove provider acceptance.", + "Screenshots should be captured immediately before submission and after any provider response.", + "Do not remove the non-Circle disclosure from provider forms.", + ], + } + + +def write_md(payload: dict[str, Any], path: Path) -> None: + lines = [ + "# cWUSDC Provider Submission Prefill", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Contract: `{ASSET['contract']}`", + f"- CAIP-19: `{ASSET['caip19']}`", + "", + "## Description", + "", + DESCRIPTION, + "", + "## Provider Prefill", + "", + ] + for provider, data in payload["providers"].items(): + lines.extend([f"### {provider}", "", f"- Objective: {data['objective']}", f"- Attachment: `{data['attachment']}`", "", "| Field | Value |", "|---|---|"]) + for key, value in data["fields"].items(): + lines.append(f"| `{key}` | `{value}` |") + lines.append("") + lines.extend(["## Screenshot Checklist", "", "| ID | URL | Target | Captured | Reason |", "|---|---|---|---:|---|"]) + for item in payload["screenshotChecklist"]: + lines.append(f"| `{item['id']}` | {item['url']} | `{item['target']}` | `{item['captured']}` | {item['reason']} |") + lines.extend(["", "## Screenshot Capture Commands", "", "```bash"]) + for item in payload["screenshotChecklist"]: + lines.append(f"pnpm exec playwright screenshot --timeout=60000 {item['url']} {item['target']}") + lines.extend(["```", "", "## Boundaries", ""]) + lines.extend(f"- {item}" for item in payload["submissionBoundary"]) + path.write_text("\n".join(lines) + "\n") + + +def main() -> int: + payload = build() + REPORT_JSON.parent.mkdir(parents=True, exist_ok=True) + REPORT_JSON.write_text(json.dumps(payload, indent=2) + "\n") + write_md(payload, REPORT_MD) + print(f"Wrote {REPORT_JSON.relative_to(ROOT)}") + print(f"Wrote {REPORT_MD.relative_to(ROOT)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/build-cwusdc-role-deployment-appendix.py b/scripts/verify/build-cwusdc-role-deployment-appendix.py new file mode 100644 index 00000000..f05415fe --- /dev/null +++ b/scripts/verify/build-cwusdc-role-deployment-appendix.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +"""Build a formal role-event/deployment-record appendix for Mainnet cWUSDC.""" + +from __future__ import annotations + +import datetime as dt +import json +import re +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +ROLE_AUDIT = ROOT / "reports" / "status" / "cwusdc-mainnet-role-audit-latest.json" +REPORT_JSON = ROOT / "reports" / "status" / "cwusdc-role-deployment-appendix-latest.json" +REPORT_MD = ROOT / "reports" / "status" / "cwusdc-role-deployment-appendix-latest.md" + +SEARCH_ROOTS = [ + ROOT / "docs", + ROOT / "reports" / "status", + ROOT / "config", + ROOT / "scripts", +] + + +def load_role_audit() -> dict[str, Any]: + if not ROLE_AUDIT.exists(): + raise SystemExit(f"Missing role audit: {ROLE_AUDIT.relative_to(ROOT)}") + return json.loads(ROLE_AUDIT.read_text()) + + +def read_text(path: Path) -> str: + try: + return path.read_text(errors="ignore") + except Exception: + return "" + + +def candidate_files() -> list[Path]: + files: list[Path] = [] + for root in SEARCH_ROOTS: + if not root.exists(): + continue + for path in root.rglob("*"): + if path.is_file() and path.suffix.lower() in {".md", ".json", ".jsonl", ".sh", ".py", ".env", ".txt"}: + files.append(path) + return files + + +def find_mentions(needles: list[str]) -> list[dict[str, Any]]: + lowered = [(needle, needle.lower()) for needle in needles if needle] + findings: list[dict[str, Any]] = [] + for path in candidate_files(): + rel = str(path.relative_to(ROOT)) + if rel.endswith("cwusdc-role-deployment-appendix-latest.json"): + continue + text = read_text(path) + if not text: + continue + text_lower = text.lower() + matches = [needle for needle, low in lowered if low in text_lower] + if not matches: + continue + lines = [] + for index, line in enumerate(text.splitlines(), start=1): + low_line = line.lower() + if any(low in low_line for _, low in lowered): + lines.append({"line": index, "text": line[:240]}) + if len(lines) >= 8: + break + findings.append({"path": rel, "matches": sorted(set(matches)), "sampleLines": lines}) + return sorted(findings, key=lambda item: item["path"]) + + +def tx_url(tx_hash: str | None) -> str: + return f"https://etherscan.io/tx/{tx_hash}" if tx_hash else "" + + +def build() -> dict[str, Any]: + role_audit = load_role_audit() + events = role_audit.get("eventLogReview", {}).get("events", []) + tx_hashes = sorted({event.get("transactionHash") for event in events if event.get("transactionHash")}) + addresses = sorted( + { + role_audit.get("token", {}).get("address", ""), + *[candidate.get("address", "") for candidate in role_audit.get("candidateChecks", {}).values()], + *[event.get("account", "") for event in events], + *[event.get("sender", "") for event in events], + } + ) + addresses = [address for address in addresses if re.fullmatch(r"0x[a-fA-F0-9]{40}", address or "")] + needles = [role_audit.get("token", {}).get("address", ""), *tx_hashes, *addresses] + mentions = find_mentions(needles) + deployment_record_candidates = [ + item + for item in mentions + if any(token in item["path"].lower() for token in ["deploy", "tracker", "technical", "completion", "readiness", "runbook", "bridge"]) + ] + return { + "schema": "cwusdc-role-deployment-appendix/v1", + "generatedAt": dt.datetime.now(dt.UTC).isoformat().replace("+00:00", "Z"), + "roleAudit": str(ROLE_AUDIT.relative_to(ROOT)), + "token": role_audit.get("token", {}), + "eventCount": len(events), + "transactionHashes": tx_hashes, + "effectiveMembersFromEvents": role_audit.get("eventLogReview", {}).get("effectiveMembersFromEvents", {}), + "privilegedCandidates": role_audit.get("privilegedCandidates", []), + "deploymentRecordCandidates": deployment_record_candidates, + "allMentions": mentions, + "limitations": [ + "This appendix reconciles on-chain role events with repository records discoverable by local text search.", + "It is not a substitute for a signed third-party audit or a provider-side ownership verification flow.", + "Operator notebooks, private emails, and provider form submissions are outside this local repository scan unless committed as evidence reports.", + ], + } + + +def write_md(payload: dict[str, Any], path: Path) -> None: + lines = [ + "# cWUSDC Role Deployment Appendix", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Token: `{payload['token'].get('address')}`", + f"- Source role audit: `{payload['roleAudit']}`", + f"- On-chain role event count: `{payload['eventCount']}`", + "", + "## Effective Members From Events", + "", + "| Role | Members |", + "|---|---|", + ] + for role, members in payload["effectiveMembersFromEvents"].items(): + lines.append(f"| `{role}` | `{', '.join(members) if members else 'none observed'}` |") + lines.extend(["", "## Privileged Candidates", "", "| Label | Address | Roles |", "|---|---|---|"]) + for item in payload["privilegedCandidates"]: + lines.append(f"| `{item['label']}` | `{item['address']}` | `{', '.join(item['roles'])}` |") + lines.extend(["", "## Role Event Transactions", "", "| Transaction | Etherscan |", "|---|---|"]) + for tx_hash in payload["transactionHashes"]: + lines.append(f"| `{tx_hash}` | {tx_url(tx_hash)} |") + lines.extend(["", "## Deployment Record Candidates", "", "| Path | Matches | Sample |", "|---|---|---|"]) + for item in payload["deploymentRecordCandidates"]: + sample = "; ".join(f"L{line['line']}: {line['text']}" for line in item["sampleLines"][:3]) + lines.append(f"| `{item['path']}` | `{', '.join(item['matches'][:4])}` | {sample} |") + lines.extend(["", "## Limitations", ""]) + lines.extend(f"- {item}" for item in payload["limitations"]) + path.write_text("\n".join(lines) + "\n") + + +def main() -> int: + payload = build() + REPORT_JSON.parent.mkdir(parents=True, exist_ok=True) + REPORT_JSON.write_text(json.dumps(payload, indent=2) + "\n") + write_md(payload, REPORT_MD) + print(f"Wrote {REPORT_JSON.relative_to(ROOT)}") + print(f"Wrote {REPORT_MD.relative_to(ROOT)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/build-ei-matrix-cwusdc-topup-tsv-from-audit-json.sh b/scripts/verify/build-ei-matrix-cwusdc-topup-tsv-from-audit-json.sh new file mode 100755 index 00000000..d0889b95 --- /dev/null +++ b/scripts/verify/build-ei-matrix-cwusdc-topup-tsv-from-audit-json.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# Build ei-matrix-cwusdc-topup-indices.txt + ei-matrix-cwusdc-topup-amounts.tsv from +# reports/status/ei-matrix-readiness-audit-latest.json (rows with mainnetCwusdcRaw < TARGET). +# +# Usage (repo root): +# EI_MATRIX_TOPUP_TARGET_RAW=12000000 ./scripts/verify/build-ei-matrix-cwusdc-topup-tsv-from-audit-json.sh +# ./scripts/verify/build-ei-matrix-cwusdc-topup-tsv-from-audit-json.sh /path/to/audit.json +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +JSON="${1:-$PROJECT_ROOT/reports/status/ei-matrix-readiness-audit-latest.json}" +TARGET="${EI_MATRIX_TOPUP_TARGET_RAW:-12000000}" +OUT_IDX="$PROJECT_ROOT/reports/status/ei-matrix-cwusdc-topup-indices.txt" +OUT_TSV="$PROJECT_ROOT/reports/status/ei-matrix-cwusdc-topup-amounts.tsv" +[[ -f "$JSON" ]] || { echo "Missing $JSON" >&2; exit 1; } +python3 - "$JSON" "$TARGET" "$OUT_IDX" "$OUT_TSV" <<'PY' +import json +import sys +from pathlib import Path + +p, target, out_idx, out_tsv = Path(sys.argv[1]), int(sys.argv[2]), Path(sys.argv[3]), Path(sys.argv[4]) +data = json.loads(p.read_text(encoding="utf-8")) +rows = data["rows"] +gaps = [] +total = 0 +for r in rows: + cur = int(r.get("mainnetCwusdcRaw") or 0) + if cur < target: + need = target - cur + idx = int(r["linearIndex"]) + gaps.append((idx, need)) + total += need +out_idx.parent.mkdir(parents=True, exist_ok=True) +out_idx.write_text("\n".join(str(i) for i, _ in gaps) + "\n", encoding="utf-8") +out_tsv.write_text("\n".join(f"{i}\t{n}" for i, n in gaps) + "\n", encoding="utf-8") +print(f"wrote {out_idx} + {out_tsv} gap_wallets={len(gaps)} total_topup_raw={total}") +PY diff --git a/scripts/verify/build-external-submission-packet-index.py b/scripts/verify/build-external-submission-packet-index.py new file mode 100755 index 00000000..39f6c3f8 --- /dev/null +++ b/scripts/verify/build-external-submission-packet-index.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +"""Build an index of external submission packets and current probe artifacts.""" + +from __future__ import annotations + +import json +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +OUT_JSON = ROOT / "reports/status/external-submission-packet-index-latest.json" +OUT_MD = ROOT / "docs/04-configuration/EXTERNAL_SUBMISSION_PACKET_INDEX.md" + +PACKETS: list[dict[str, Any]] = [ + { + "provider": "Etherscan", + "status": "repo_ready_external_acceptance_pending", + "primaryPacket": "docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md", + "supporting": [ + "docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md", + "docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_VALUE_EXECUTION_PLAN.md", + "reports/status/cwusdc-etherscan-value-dossier-latest.json", + ], + "nextRepoAction": "Refresh dossier and capture post-submit response evidence.", + }, + { + "provider": "CoinGecko", + "status": "repo_ready_external_price_entry_missing", + "primaryPacket": "docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md", + "supporting": [ + "docs/04-configuration/coingecko/CWUSDC_MAINNET_EXTERNAL_SUBMISSION_CHECKLIST.md", + "docs/04-configuration/coingecko/submissions/cwusdc-coingecko-listing-request-20260509.json", + "reports/status/cwusdc-external-trackers-live-latest.json", + ], + "nextRepoAction": "Keep token-price API blocker visible and attach current supply/liquidity caveats.", + }, + { + "provider": "CoinMarketCap", + "status": "dex_page_visible_full_value_acceptance_pending", + "primaryPacket": "docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md", + "supporting": [ + "reports/status/token-aggregation-cmc-report-chain1-latest.json", + "reports/status/cmc-provider-report-sanity-latest.json", + "reports/status/cmc-top10-ecosystem-coverage-latest.json", + ], + "nextRepoAction": "Use CMC sanity report to avoid overclaiming liquidity or quote-asset identity.", + }, + { + "provider": "DexScreener", + "status": "api_not_indexing_pairs", + "primaryPacket": "docs/04-configuration/dexscreener/CWUSDC_DEXSCREENER_INDEXING_AND_PROFILE_PACKET_20260509.md", + "supporting": [ + "reports/status/cwusdc-external-trackers-live-latest.json", + "reports/status/cwusdc-provider-handoff-latest.md", + ], + "nextRepoAction": "Keep pair/profile request evidence updated after fresh public swap/liquidity events.", + }, + { + "provider": "GeckoTerminal", + "status": "pool_api_visible_low_reserve", + "primaryPacket": "docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md", + "supporting": [ + "reports/status/cwusdc-external-trackers-live-latest.json", + "reports/status/cmc-provider-report-sanity-latest.json", + ], + "nextRepoAction": "Track reserve USD and 24h volume separately from listing acceptance.", + }, + { + "provider": "MetaMask", + "status": "metadata_path_ready_price_provider_external", + "primaryPacket": "docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md", + "supporting": [ + "docs/04-configuration/metamask/METAMASK_EIP747_CONTRACT_METADATA_REFERENCE_PACKET.md", + "docs/04-configuration/metamask/METAMASK_CWUSDC_API_FEED_SPIDER_WEB_RESEARCH.md", + "reports/status/cwusdc-provider-readiness-ci-latest.json", + ], + "nextRepoAction": "Keep CAIP-19, EIP-747, logo URL, and external price-provider blockers aligned.", + }, +] + + +def exists(path: str) -> bool: + return (ROOT / path).exists() + + +def table(headers: list[str], rows: list[list[Any]]) -> str: + def cell(value: Any) -> str: + if isinstance(value, list): + value = "
".join(str(item) for item in value) + return str(value).replace("|", "\\|").replace("\n", "
") + + return "\n".join( + [ + f"| {' | '.join(cell(header) for header in headers)} |", + f"| {' | '.join('---' for _ in headers)} |", + *[f"| {' | '.join(cell(value) for value in row)} |" for row in rows], + ] + ) + + +def main() -> int: + generated_at = datetime.now(timezone.utc).isoformat() + packets = [] + for packet in PACKETS: + row = dict(packet) + row["primaryExists"] = exists(row["primaryPacket"]) + row["supportingExists"] = [{"path": path, "exists": exists(path)} for path in row["supporting"]] + row["allArtifactsPresent"] = row["primaryExists"] and all(item["exists"] for item in row["supportingExists"]) + packets.append(row) + payload = { + "schema": "external-submission-packet-index/v1", + "generatedAt": generated_at, + "summary": { + "providerCount": len(packets), + "allArtifactsPresent": all(row["allArtifactsPresent"] for row in packets), + "missingArtifactCount": sum(1 for row in packets if not row["allArtifactsPresent"]), + }, + "packets": packets, + } + OUT_JSON.parent.mkdir(parents=True, exist_ok=True) + OUT_JSON.write_text(json.dumps(payload, indent=2) + "\n") + + lines = [ + "# External Submission Packet Index", + "", + f"- Generated: `{generated_at}`", + f"- All artifacts present: `{payload['summary']['allArtifactsPresent']}`", + "", + table( + ["Provider", "Status", "Primary packet", "Supporting artifacts", "Next repo action"], + [ + [ + row["provider"], + row["status"], + f"`{row['primaryPacket']}` ({row['primaryExists']})", + [f"`{item['path']}` ({item['exists']})" for item in row["supportingExists"]], + row["nextRepoAction"], + ] + for row in packets + ], + ), + "", + "## Boundary", + "", + "This index tracks repo-side evidence availability only. Provider submission, review, acceptance, and price propagation remain external states.", + ] + OUT_MD.parent.mkdir(parents=True, exist_ok=True) + OUT_MD.write_text("\n".join(lines) + "\n") + print(f"Wrote {OUT_JSON.relative_to(ROOT)}") + print(f"Wrote {OUT_MD.relative_to(ROOT)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/build-non-evm-requirement-stubs.py b/scripts/verify/build-non-evm-requirement-stubs.py new file mode 100755 index 00000000..612649de --- /dev/null +++ b/scripts/verify/build-non-evm-requirement-stubs.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +"""Write repo-side non-EVM funding and identity requirement stubs.""" + +from __future__ import annotations + +import json +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +CONFIG_OUT = ROOT / "config/non-evm-lane-requirements.json" +REPORT_JSON = ROOT / "reports/status/non-evm-lane-requirements-latest.json" +REPORT_MD = ROOT / "reports/status/non-evm-lane-requirements-latest.md" + +LANES: list[dict[str, Any]] = [ + { + "network": "solana", + "nativeAsset": "SOL", + "walletStatus": "bound_from_SOLANA_KEYPAIR_PATH_public_key", + "canonicalWallet": "9b4ebHVimuhMqbiCh6tUMMY2S48VyEHpqg5nxMMFe5Pf", + "requiredBindings": ["splMintAddresses", "rentReserveTarget", "venueMinimumLiquidity"], + "minimumFundingTarget": "TBD", + "claimBoundary": "Do not claim native Solana liquidity until SPL mints, rent/gas, and venue inventory are bound.", + }, + { + "network": "tron", + "nativeAsset": "TRX", + "walletStatus": "derived_wallet_needs_canonical_confirmation", + "canonicalWallet": "TGkbidE5LfVJZ3QGj6DaPqzCTcTe9tJDxm", + "requiredBindings": ["canonicalWalletApproval", "energyBandwidthTarget", "trc20Inventory"], + "minimumFundingTarget": "TBD", + "claimBoundary": "Do not claim native Tron liquidity until the canonical wallet and TRC-20 inventory are confirmed.", + }, + { + "network": "xrpl", + "nativeAsset": "XRP", + "walletStatus": "missing", + "canonicalWallet": None, + "requiredBindings": ["xrplAccount", "destinationTagPolicy", "trustlineIssuerPolicy", "xrpReserveTarget"], + "minimumFundingTarget": "TBD", + "claimBoundary": "Do not claim XRPL corridor readiness until account reserve, tags, trustlines, and wXRP controller evidence are closed.", + }, + { + "network": "bitcoin", + "nativeAsset": "BTC", + "walletStatus": "missing", + "canonicalWallet": None, + "requiredBindings": ["btcCustodyAddress", "proofOfReservesPolicy", "wrappedAssetMapping", "venueTarget"], + "minimumFundingTarget": "TBD", + "claimBoundary": "Use BTC as a planning lane only until custody/reserve evidence and wrapping policy are bound.", + }, + { + "network": "dogecoin", + "nativeAsset": "DOGE", + "walletStatus": "missing", + "canonicalWallet": None, + "requiredBindings": ["dogeCustodyAddress", "bridgeOrCustodyModel", "venueTarget"], + "minimumFundingTarget": "TBD", + "claimBoundary": "Use DOGE as a planning lane only until native custody and bridge model are bound.", + }, + { + "network": "hyperliquid", + "nativeAsset": "HYPE", + "walletStatus": "research_required", + "canonicalWallet": None, + "requiredBindings": ["chainIdentifier", "assetIdentifier", "custodyPath", "venueOrApiEvidence"], + "minimumFundingTarget": "TBD", + "claimBoundary": "Use HYPE only as a market-cap watch item until identifiers and custody path are verified.", + }, +] + + +def table(headers: list[str], rows: list[list[Any]]) -> str: + def cell(value: Any) -> str: + if isinstance(value, list): + value = "
".join(str(item) for item in value) + if value is None: + value = "TBD" + return str(value).replace("|", "\\|").replace("\n", "
") + + return "\n".join( + [ + f"| {' | '.join(cell(header) for header in headers)} |", + f"| {' | '.join('---' for _ in headers)} |", + *[f"| {' | '.join(cell(value) for value in row)} |" for row in rows], + ] + ) + + +def main() -> int: + generated_at = datetime.now(timezone.utc).isoformat() + payload = { + "schema": "non-evm-lane-requirements/v1", + "generatedAt": generated_at, + "status": "stubs_bound_repo_side", + "lanes": LANES, + "validationRule": "A lane is claimable only after canonicalWallet, asset IDs, native gas/reserve target, venue target, and evidence source are non-TBD.", + } + for path in (CONFIG_OUT, REPORT_JSON): + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload, indent=2) + "\n") + + lines = [ + "# Non-EVM Lane Requirement Stubs", + "", + f"- Generated: `{generated_at}`", + f"- Config source: `{CONFIG_OUT.relative_to(ROOT)}`", + "", + table( + ["Network", "Native", "Wallet status", "Canonical wallet", "Required bindings", "Minimum target", "Claim boundary"], + [ + [ + lane["network"], + lane["nativeAsset"], + lane["walletStatus"], + lane["canonicalWallet"], + lane["requiredBindings"], + lane["minimumFundingTarget"], + lane["claimBoundary"], + ] + for lane in LANES + ], + ), + ] + REPORT_MD.parent.mkdir(parents=True, exist_ok=True) + REPORT_MD.write_text("\n".join(lines) + "\n") + print(f"Wrote {CONFIG_OUT.relative_to(ROOT)}") + print(f"Wrote {REPORT_JSON.relative_to(ROOT)}") + print(f"Wrote {REPORT_MD.relative_to(ROOT)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/check-cmc-provider-report-sanity.py b/scripts/verify/check-cmc-provider-report-sanity.py new file mode 100755 index 00000000..b347373f --- /dev/null +++ b/scripts/verify/check-cmc-provider-report-sanity.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +"""Advisory sanity checks for the repo CMC-shaped Mainnet report.""" + +from __future__ import annotations + +import json +from datetime import datetime, timezone +from decimal import Decimal, InvalidOperation +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +CMC_REPORT = ROOT / "reports/status/token-aggregation-cmc-report-chain1-latest.json" +TRACKERS = ROOT / "reports/status/cwusdc-external-trackers-live-latest.json" +OUT_JSON = ROOT / "reports/status/cmc-provider-report-sanity-latest.json" +OUT_MD = ROOT / "reports/status/cmc-provider-report-sanity-latest.md" + +OFFICIAL_QUOTES = { + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": "USDC", + "0xdac17f958d2ee523a2206206994597c13d831ec7": "USDT", +} +PROMOTED = {"cWUSDC", "cWUSDT", "cWXAUC", "cWXAUT", "cWBTC", "cWETH"} + + +def load(path: Path) -> dict[str, Any]: + return json.loads(path.read_text()) if path.exists() else {} + + +def dec(value: Any) -> Decimal: + try: + return Decimal(str(value or "0")) + except (InvalidOperation, ValueError): + return Decimal(0) + + +def table(headers: list[str], rows: list[list[Any]]) -> str: + def cell(value: Any) -> str: + if isinstance(value, list): + value = "
".join(str(item) for item in value) + return str(value).replace("|", "\\|").replace("\n", "
") + + return "\n".join( + [ + f"| {' | '.join(cell(header) for header in headers)} |", + f"| {' | '.join('---' for _ in headers)} |", + *[f"| {' | '.join(cell(value) for value in row)} |" for row in rows], + ] + ) + + +def main() -> int: + cmc = load(CMC_REPORT) + trackers = load(TRACKERS) + tokens = cmc.get("tokens", []) + warnings: list[dict[str, Any]] = [] + promoted_rows = [] + + for token in tokens: + symbol = token.get("symbol") + address = str(token.get("contract_address", "")).lower() + liquidity = dec(token.get("liquidity_usd")) + 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.", + } + ) + if symbol in PROMOTED: + promoted_rows.append( + { + "symbol": symbol, + "address": address, + "liquidityUsd": str(liquidity), + "volume24hUsd": str(volume), + "pairCount": len(pairs), + } + ) + if liquidity <= 0: + warnings.append( + { + "id": "zero_reported_liquidity", + "symbol": symbol, + "address": address, + "severity": "warning", + "message": "CMC-shaped report shows zero liquidity_usd; do not use it as listing-quality liquidity evidence.", + } + ) + + gecko_reserves = [] + for check in trackers.get("checks", []): + if not str(check.get("id", "")).startswith("geckoterminal"): + continue + attrs = (((check.get("jsonPreview") or {}).get("data") or {}).get("attributes") or {}) + gecko_reserves.append( + { + "id": check.get("id"), + "pool": attrs.get("address"), + "name": attrs.get("name"), + "reserveUsd": attrs.get("reserve_in_usd"), + "volume24hUsd": (attrs.get("volume_usd") or {}).get("h24"), + } + ) + + payload = { + "schema": "cmc-provider-report-sanity/v1", + "generatedAt": datetime.now(timezone.utc).isoformat(), + "inputs": { + "cmcReport": str(CMC_REPORT.relative_to(ROOT)), + "trackerReport": str(TRACKERS.relative_to(ROOT)) if TRACKERS.exists() else None, + }, + "summary": { + "tokenCount": len(tokens), + "promotedTokenCount": len(promoted_rows), + "warningCount": len(warnings), + "geckoReserveEvidenceCount": len(gecko_reserves), + }, + "promotedTokens": promoted_rows, + "geckoReserveEvidence": gecko_reserves, + "warnings": warnings, + } + OUT_JSON.parent.mkdir(parents=True, exist_ok=True) + OUT_JSON.write_text(json.dumps(payload, indent=2) + "\n") + + lines = [ + "# CMC Provider Report Sanity", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Warnings: `{len(warnings)}`", + "", + "## Promoted Mainnet Rows", + "", + table( + ["Symbol", "Address", "Liquidity USD", "24h volume USD", "Pairs"], + [[row["symbol"], row["address"], row["liquidityUsd"], row["volume24hUsd"], row["pairCount"]] for row in promoted_rows], + ), + "", + "## GeckoTerminal Reserve Cross-Check", + "", + table( + ["Check", "Pool", "Name", "Reserve USD", "24h volume USD"], + [[row["id"], row["pool"], row["name"], row["reserveUsd"], row["volume24hUsd"]] for row in gecko_reserves], + ) if gecko_reserves else "No GeckoTerminal tracker reserve evidence found.", + "", + "## Advisory Warnings", + "", + table( + ["ID", "Symbol", "Address", "Severity", "Message"], + [[w["id"], w.get("symbol", "-"), w.get("address", "-"), w["severity"], w["message"]] for w in warnings], + ) if warnings else "No warnings.", + ] + OUT_MD.write_text("\n".join(lines) + "\n") + print(f"Wrote {OUT_JSON.relative_to(ROOT)}") + print(f"Wrote {OUT_MD.relative_to(ROOT)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/check-contracts-on-chain-138.sh b/scripts/verify/check-contracts-on-chain-138.sh index 9ca2a4bb..6609bd85 100755 --- a/scripts/verify/check-contracts-on-chain-138.sh +++ b/scripts/verify/check-contracts-on-chain-138.sh @@ -157,7 +157,7 @@ for addr in "${ADDRESSES[@]}"; do done echo "" -echo "Total: $OK present, $MISS missing/empty (${#ADDRESSES[@]} addresses). Explorer: https://explorer.d-bis.org/address/" +echo "Total: $OK present, $MISS missing/empty (${#ADDRESSES[@]} addresses). Explorer: https://explorer.d-bis.org/addresses/" if [[ $MISS -gt 0 && -z "$rpc_reachable" ]]; then echo " → RPC was unreachable from this host; see WARN above. Run from LAN/VPN or pass a reachable RPC URL." >&2 fi diff --git a/scripts/verify/check-cw-full-operational-readiness.py b/scripts/verify/check-cw-full-operational-readiness.py new file mode 100755 index 00000000..ef971bce --- /dev/null +++ b/scripts/verify/check-cw-full-operational-readiness.py @@ -0,0 +1,536 @@ +#!/usr/bin/env python3 +"""Build a read-only cW mesh operational readiness report. + +This checker intentionally does not call RPC. It validates the source-of-truth +files that the deployment, bridge, liquidity, token aggregation, and tracker +runbooks consume, then records the remaining live/operator and external gates. +""" +from __future__ import annotations + +import argparse +import json +import re +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] + +DEPLOYMENT_STATUS = ROOT / "cross-chain-pmm-lps" / "config" / "deployment-status.json" +MESH_MATRIX = ROOT / "reports" / "status" / "cw-mesh-deployment-matrix-latest.json" +TOKEN_MAPPING = ROOT / "config" / "token-mapping-multichain.json" +CANONICAL_TOKENS = ROOT / "smom-dbis-138" / "services" / "token-aggregation" / "src" / "config" / "canonical-tokens.ts" +ENGINE_X_READINESS = ROOT / "reports" / "status" / "engine-x-public-indexed-readiness-latest.json" +ETHERSCAN_PACKET = ROOT / "docs" / "04-configuration" / "etherscan" / "CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md" +COINGECKO_CHECKLIST = ROOT / "docs" / "04-configuration" / "coingecko" / "CWUSDC_MAINNET_EXTERNAL_SUBMISSION_CHECKLIST.md" +CW_L1_CONTRACT = ROOT / "smom-dbis-138" / "contracts" / "bridge" / "CWMultiTokenBridgeL1.sol" +CW_L2_CONTRACT = ROOT / "smom-dbis-138" / "contracts" / "bridge" / "CWMultiTokenBridgeL2.sol" +CW_L1_DEPLOY = ROOT / "smom-dbis-138" / "script" / "DeployCWMultiTokenBridgeL1.s.sol" +CW_L2_DEPLOY = ROOT / "smom-dbis-138" / "script" / "DeployCWMultiTokenBridgeL2.s.sol" +CW_ROUTE_BOOTSTRAP = ROOT / "smom-dbis-138" / "scripts" / "deployment" / "cw-l1-bootstrap-gru-v2-ccip-routes.sh" + +BRIDGE_EVIDENCE_CANDIDATES = [ + ROOT / "reports" / "status" / "cw-multitoken-bridge-e2e-latest.json", + ROOT / "reports" / "status" / "cw-bridge-live-e2e-latest.json", +] +TRACKER_EVIDENCE_CANDIDATES = [ + ROOT / "reports" / "status" / "cwusdc-tracker-profile-approval-latest.json", + ROOT / "reports" / "status" / "cwusdc-external-trackers-live-latest.json", +] + +ACTIVE_CHAIN_IDS = [1, 10, 56, 100, 137, 8453, 42161, 42220, 43114] +PLANNED_OR_NON_CW_CHAIN_IDS = [1111, 651940] + +REQUIRED_CW_SYMBOLS = [ + "cWUSDT", + "cWUSDC", + "cWEURC", + "cWEURT", + "cWGBPC", + "cWGBPT", + "cWAUDC", + "cWJPYC", + "cWCHFC", + "cWCADC", + "cWXAUC", + "cWXAUT", +] + +OPTIONAL_ADDON_SYMBOLS = ["cWBTC"] + +TOKEN_MAPPING_KEYS = { + "cWUSDT": "Compliant_USDT_cW", + "cWUSDC": "Compliant_USDC_cW", + "cWEURC": "Compliant_EURC_cW", + "cWEURT": "Compliant_EURT_cW", + "cWGBPC": "Compliant_GBPC_cW", + "cWGBPT": "Compliant_GBPT_cW", + "cWAUDC": "Compliant_AUDC_cW", + "cWJPYC": "Compliant_JPYC_cW", + "cWCHFC": "Compliant_CHFC_cW", + "cWCADC": "Compliant_CADC_cW", + "cWXAUC": "Compliant_XAUC_cW", + "cWXAUT": "Compliant_XAUT_cW", +} + + +def load_json(path: Path, default: Any = None) -> Any: + if not path.exists(): + return default + return json.loads(path.read_text()) + + +def rel(path: Path) -> str: + try: + return str(path.relative_to(ROOT)) + except ValueError: + return str(path) + + +def is_zero_address(value: str | None) -> bool: + if not value: + return True + return value.lower() == "0x0000000000000000000000000000000000000000" + + +def ok_gate(gate_id: str, title: str, details: list[str] | None = None, evidence: list[str] | None = None) -> dict[str, Any]: + return { + "id": gate_id, + "title": title, + "status": "pass", + "details": details or [], + "evidence": evidence or [], + } + + +def warn_gate(gate_id: str, title: str, details: list[str], evidence: list[str] | None = None) -> dict[str, Any]: + return { + "id": gate_id, + "title": title, + "status": "warn", + "details": details, + "evidence": evidence or [], + } + + +def fail_gate(gate_id: str, title: str, details: list[str], evidence: list[str] | None = None) -> dict[str, Any]: + return { + "id": gate_id, + "title": title, + "status": "blocked", + "details": details, + "evidence": evidence or [], + } + + +def check_source_of_truth(dep: dict[str, Any]) -> dict[str, Any]: + issues: list[str] = [] + if dep.get("homeChainId") != 138: + issues.append(f"deployment-status homeChainId is `{dep.get('homeChainId')}`, expected `138`.") + if "138" not in (dep.get("chains") or {}): + issues.append("deployment-status is missing Chain 138.") + for chain_id in ACTIVE_CHAIN_IDS: + if str(chain_id) not in (dep.get("chains") or {}): + issues.append(f"deployment-status is missing active chain `{chain_id}`.") + + evidence = [rel(DEPLOYMENT_STATUS)] + if issues: + return fail_gate("source_of_truth_chain_138", "Chain 138 source of truth", issues, evidence) + return ok_gate( + "source_of_truth_chain_138", + "Chain 138 source of truth", + ["Chain 138 is the home chain and all active public mesh chains are represented."], + evidence, + ) + + +def check_deployment_coverage(dep: dict[str, Any]) -> dict[str, Any]: + chains = dep.get("chains") or {} + issues: list[str] = [] + optional_missing: list[str] = [] + + for chain_id in ACTIVE_CHAIN_IDS: + info = chains.get(str(chain_id)) or {} + cw_tokens = info.get("cwTokens") or {} + for symbol in REQUIRED_CW_SYMBOLS: + if is_zero_address(cw_tokens.get(symbol)): + issues.append(f"chain `{chain_id}` missing `{symbol}` in deployment-status cwTokens.") + for symbol in OPTIONAL_ADDON_SYMBOLS: + if is_zero_address(cw_tokens.get(symbol)): + optional_missing.append(f"chain `{chain_id}` missing optional add-on `{symbol}`.") + + evidence = [rel(DEPLOYMENT_STATUS)] + if issues: + return fail_gate("active_cw_token_coverage", "Active cW token coverage", issues, evidence) + if optional_missing: + return warn_gate( + "active_cw_token_coverage", + "Active cW token coverage", + ["All required cW fiat/commodity symbols are present.", *optional_missing], + evidence, + ) + return ok_gate( + "active_cw_token_coverage", + "Active cW token coverage", + ["All required cW fiat/commodity symbols and optional cWBTC add-on are present on active mesh chains."], + evidence, + ) + + +def check_token_mapping(dep: dict[str, Any], mapping: dict[str, Any]) -> dict[str, Any]: + chains = dep.get("chains") or {} + pairs = mapping.get("pairs") or [] + pair_by_to = {int(p.get("toChainId")): p for p in pairs if p.get("fromChainId") == 138 and p.get("toChainId") is not None} + issues: list[str] = [] + + for chain_id in ACTIVE_CHAIN_IDS: + pair = pair_by_to.get(chain_id) + if not pair: + issues.append(f"token-mapping missing 138 -> `{chain_id}` pair.") + continue + tokens = {t.get("key"): t.get("addressTo") for t in pair.get("tokens") or []} + cw_tokens = (chains.get(str(chain_id)) or {}).get("cwTokens") or {} + for symbol, key in TOKEN_MAPPING_KEYS.items(): + mapped = tokens.get(key) + expected = cw_tokens.get(symbol) + if is_zero_address(mapped): + issues.append(f"138 -> `{chain_id}` token-mapping has empty `{key}`.") + elif expected and mapped.lower() != expected.lower(): + issues.append(f"138 -> `{chain_id}` `{key}` is `{mapped}`, deployment-status has `{expected}`.") + + evidence = [rel(TOKEN_MAPPING), rel(DEPLOYMENT_STATUS)] + if issues: + return fail_gate("token_mapping_mesh", "138 to public-chain token mapping", issues, evidence) + return ok_gate( + "token_mapping_mesh", + "138 to public-chain token mapping", + ["All required cW mapping entries are non-zero and match deployment-status for active mesh chains."], + evidence, + ) + + +def parse_gru_chain_ids(text: str) -> list[int]: + match = re.search(r"const\s+GRU_CW_CHAIN_IDS\s*=\s*\[([^\]]+)\]", text) + if not match: + return [] + return [int(x) for x in re.findall(r"\d+", match.group(1))] + + +def fallback_symbol_has_chain(text: str, symbol: str, chain_id: int) -> bool: + match = re.search(rf"\n\s+{re.escape(symbol)}:\s*\{{(?P.*?)\n\s+\}},", text, flags=re.S) + if not match: + return False + body = match.group("body") + return re.search(rf"\[\s*{chain_id}\s*\]\s*:\s*['\"]0x[a-fA-F0-9]{{40}}['\"]", body) is not None + + +def check_token_aggregation() -> dict[str, Any]: + if not CANONICAL_TOKENS.exists(): + return fail_gate("token_aggregation_registry", "Token aggregation canonical registry", ["canonical-tokens.ts is missing."], [rel(CANONICAL_TOKENS)]) + + text = CANONICAL_TOKENS.read_text() + issues: list[str] = [] + chain_ids = parse_gru_chain_ids(text) + for chain_id in ACTIVE_CHAIN_IDS: + if chain_id not in chain_ids: + issues.append(f"GRU_CW_CHAIN_IDS missing active chain `{chain_id}`.") + for symbol in REQUIRED_CW_SYMBOLS: + if f"symbol: '{symbol}'" not in text and f'symbol: "{symbol}"' not in text: + issues.append(f"CANONICAL_TOKENS missing first-class `{symbol}` entry.") + for chain_id in ACTIVE_CHAIN_IDS: + if not fallback_symbol_has_chain(text, symbol, chain_id): + issues.append(f"FALLBACK_ADDRESSES `{symbol}` missing chain `{chain_id}`.") + + evidence = [rel(CANONICAL_TOKENS), "smom-dbis-138/services/token-aggregation/src/config/canonical-tokens.test.ts"] + if issues: + return fail_gate("token_aggregation_registry", "Token aggregation canonical registry", issues, evidence) + return ok_gate( + "token_aggregation_registry", + "Token aggregation canonical registry", + ["Canonical token aggregation includes the active nine-chain promoted cW mesh and the full required wrapped family."], + evidence, + ) + + +def check_liquidity_and_indexing(matrix: dict[str, Any], dep: dict[str, Any]) -> dict[str, Any]: + rows = {int(r.get("chainId")): r for r in matrix.get("rows") or []} + chains = dep.get("chains") or {} + issues: list[str] = [] + missing_rails: list[str] = [] + unseeded_by_chain: dict[int, int] = {} + unseeded_examples: dict[int, list[str]] = {} + + for chain_id in ACTIVE_CHAIN_IDS: + row = rows.get(chain_id) + if not row: + issues.append(f"mesh matrix missing chain `{chain_id}`.") + continue + if row.get("uniswapV2CWUSDTvsCWUSDCLive") is not True: + issues.append(f"chain `{chain_id}` cWUSDT/cWUSDC UniV2 pair is not live in latest matrix.") + if row.get("uniswapV2CWUSDTvsCWUSDCHealthy") is not True: + issues.append(f"chain `{chain_id}` cWUSDT/cWUSDC UniV2 pair is not healthy in latest matrix.") + rails = set(row.get("pmmSettlementRails") or []) + if not any(x in rails for x in ("cWUSDC/USDC", "cWUSDC/USDT")): + missing_rails.append(f"chain `{chain_id}` lacks cWUSDC stable settlement PMM rail") + if not any(x in rails for x in ("cWUSDT/USDT", "cWUSDT/USDC")): + missing_rails.append(f"chain `{chain_id}` lacks cWUSDT stable settlement PMM rail") + + for pool in (chains.get(str(chain_id)) or {}).get("pmmPools") or []: + notes = pool.get("notes") or [] + if any("unseeded_pending" in str(note) for note in notes): + unseeded_by_chain[chain_id] = unseeded_by_chain.get(chain_id, 0) + 1 + unseeded_examples.setdefault(chain_id, []) + if len(unseeded_examples[chain_id]) < 3: + unseeded_examples[chain_id].append(f"{pool.get('base')}/{pool.get('quote')} {pool.get('poolAddress')}") + + evidence = [rel(MESH_MATRIX), rel(DEPLOYMENT_STATUS)] + warnings: list[str] = [] + if missing_rails: + warnings.append( + "Stable settlement PMM rail gaps: " + + "; ".join(missing_rails) + + ". Core cWUSDT/cWUSDC pair indexing remains healthy." + ) + if unseeded_by_chain: + summary = ", ".join(f"{chain_id}: {count}" for chain_id, count in sorted(unseeded_by_chain.items())) + warnings.append(f"Unseeded pending PMM pools by chain: {summary}.") + for chain_id in sorted(unseeded_examples): + warnings.append(f"chain `{chain_id}` examples: " + "; ".join(unseeded_examples[chain_id])) + if issues: + return fail_gate("liquidity_and_indexing", "LP indexing and settlement rails", issues + warnings, evidence) + if warnings: + return warn_gate( + "liquidity_and_indexing", + "LP indexing and settlement rails", + ["Core cWUSDT/cWUSDC pair indexing is present and healthy.", *warnings], + evidence, + ) + return ok_gate( + "liquidity_and_indexing", + "LP indexing and settlement rails", + ["Core cWUSDT/cWUSDC pair indexing and stable settlement rails are present with no unseeded PMM warnings."], + evidence, + ) + + +def check_bridge_implementation() -> dict[str, Any]: + required = [CW_L1_CONTRACT, CW_L2_CONTRACT, CW_L1_DEPLOY, CW_L2_DEPLOY, CW_ROUTE_BOOTSTRAP] + missing = [rel(path) for path in required if not path.exists()] + evidence = [rel(path) for path in required] + if missing: + return fail_gate("cw_mint_burn_bridge_implementation", "cW mint/burn bridge implementation", [f"missing `{path}`" for path in missing], evidence) + return ok_gate( + "cw_mint_burn_bridge_implementation", + "cW mint/burn bridge implementation", + ["CWMultiTokenBridgeL1/L2 contracts and deployment/bootstrap scripts are present."], + evidence, + ) + + +def evidence_file_status(paths: list[Path], success_keys: list[str]) -> tuple[bool, str | None, list[str]]: + for path in paths: + if not path.exists(): + continue + data = load_json(path, {}) + if not isinstance(data, dict): + continue + for key in success_keys: + value: Any = data + for part in key.split("."): + value = value.get(part) if isinstance(value, dict) else None + if value is True: + return True, rel(path), [] + details = [f"`{rel(path)}` exists but does not expose a true success key: {', '.join(success_keys)}."] + summary = data.get("summary") if isinstance(data, dict) else None + if isinstance(summary, dict): + for key in ("failedChainIds", "failedRequiredIds", "blockedGateIds"): + values = summary.get(key) + if values: + details.append(f"{key}: {', '.join(str(x) for x in values)}.") + for key in ("passedChainCount", "activeChainCount", "requiredPassedCount", "requiredCount"): + if key in summary: + details.append(f"{key}: {summary[key]}.") + return False, rel(path), details + return False, None, [f"No evidence file found. Expected one of: {', '.join(rel(p) for p in paths)}."] + + +def check_bridge_live_e2e() -> dict[str, Any]: + ok, evidence, details = evidence_file_status( + BRIDGE_EVIDENCE_CANDIDATES, + ["summary.allActiveChainsPassed", "summary.readyForProduction", "allActiveChainsPassed"], + ) + ev = [evidence] if evidence else [rel(path) for path in BRIDGE_EVIDENCE_CANDIDATES] + if ok: + return ok_gate("cw_mint_burn_bridge_live_e2e", "cW mint/burn bridge live E2E", ["Live E2E evidence reports all active chains passed."], ev) + return fail_gate( + "cw_mint_burn_bridge_live_e2e", + "cW mint/burn bridge live E2E", + [ + "Dedicated cW mint/burn bridge code is implemented, but live receiver deployment, role wiring, route bootstrap, and E2E bridge proof evidence are still required.", + *details, + ], + ev, + ) + + +def check_engine_x_readiness() -> dict[str, Any]: + data = load_json(ENGINE_X_READINESS, {}) + ready = (((data or {}).get("summary") or {}).get("readyForPublicIndexedProof") is True) + blockers = (((data or {}).get("summary") or {}).get("blockers") or []) + if ready and not blockers: + return ok_gate("engine_x_public_indexed_readiness", "Engine X cWUSDC public indexed readiness", ["Engine X readiness report is green."], [rel(ENGINE_X_READINESS)]) + return fail_gate( + "engine_x_public_indexed_readiness", + "Engine X cWUSDC public indexed readiness", + ["Engine X readiness report is not green.", *[str(x) for x in blockers]], + [rel(ENGINE_X_READINESS)], + ) + + +def check_external_tracker_packet() -> dict[str, Any]: + missing = [rel(path) for path in [ETHERSCAN_PACKET, COINGECKO_CHECKLIST] if not path.exists()] + if missing: + return fail_gate("external_tracker_submission_packet", "External tracker submission packet", [f"missing `{path}`" for path in missing], [rel(ETHERSCAN_PACKET), rel(COINGECKO_CHECKLIST)]) + return ok_gate( + "external_tracker_submission_packet", + "External tracker submission packet", + ["Etherscan profile packet and CoinGecko/external tracker checklist are present."], + [rel(ETHERSCAN_PACKET), rel(COINGECKO_CHECKLIST)], + ) + + +def check_external_tracker_live() -> dict[str, Any]: + ok, evidence, details = evidence_file_status( + TRACKER_EVIDENCE_CANDIDATES, + ["summary.allTrackersLive", "summary.readyForEtherscanUsdValue", "allTrackersLive"], + ) + ev = [evidence] if evidence else [rel(path) for path in TRACKER_EVIDENCE_CANDIDATES] + if ok: + return ok_gate("external_trackers_live", "External trackers live", ["External tracker evidence reports live indexing/profile approval."], ev) + return fail_gate( + "external_trackers_live", + "External trackers live", + [ + "Explorer/tracker packet is prepared, but Etherscan/CoinGecko/CMC/DexScreener approval or live tracker evidence is external and still pending.", + *details, + ], + ev, + ) + + +def check_planned_chains(dep: dict[str, Any]) -> dict[str, Any]: + chains = dep.get("chains") or {} + details: list[str] = [] + for chain_id in PLANNED_OR_NON_CW_CHAIN_IDS: + info = chains.get(str(chain_id)) or {} + state = info.get("activationState") or "not_active_cw_mesh" + details.append(f"chain `{chain_id}` `{info.get('name', '')}` is `{state}` and is not counted as active cW mesh coverage.") + return warn_gate("planned_or_non_cw_chains", "Planned or non-cW chain scope", details, [rel(DEPLOYMENT_STATUS)]) + + +def build_report() -> dict[str, Any]: + dep = load_json(DEPLOYMENT_STATUS, {}) + matrix = load_json(MESH_MATRIX, {"rows": []}) + mapping = load_json(TOKEN_MAPPING, {}) + + gates = [ + check_source_of_truth(dep), + check_deployment_coverage(dep), + check_token_mapping(dep, mapping), + check_token_aggregation(), + check_liquidity_and_indexing(matrix, dep), + check_bridge_implementation(), + check_bridge_live_e2e(), + check_engine_x_readiness(), + check_external_tracker_packet(), + check_external_tracker_live(), + check_planned_chains(dep), + ] + blocked = [g for g in gates if g["status"] == "blocked"] + warnings = [g for g in gates if g["status"] == "warn"] + in_repo_blockers = [ + g for g in blocked + if g["id"] not in {"cw_mint_burn_bridge_live_e2e", "external_trackers_live"} + ] + + return { + "schema": "cw-full-operational-readiness/v1", + "generatedAt": datetime.now(timezone.utc).isoformat(), + "activeChainIds": ACTIVE_CHAIN_IDS, + "requiredCwSymbols": REQUIRED_CW_SYMBOLS, + "summary": { + "inRepoSourceOfTruthConfigured": len(in_repo_blockers) == 0, + "fullyOperationalAndLive": len(blocked) == 0, + "blockedGateCount": len(blocked), + "warningGateCount": len(warnings), + "blockedGateIds": [g["id"] for g in blocked], + "warningGateIds": [g["id"] for g in warnings], + }, + "gates": gates, + "nextActions": [ + "Deploy and configure CWMultiTokenBridgeL1 on Chain 138 and CWMultiTokenBridgeL2 on each active public mesh chain.", + "Grant MINTER_ROLE and BURNER_ROLE for each cW* token to the active CWMultiTokenBridgeL2 receiver on its chain.", + "Run cW route bootstrap/configuration and capture live E2E evidence to reports/status/cw-multitoken-bridge-e2e-latest.json.", + "Submit/confirm Etherscan, CoinGecko, CMC, DexScreener tracker approvals and capture live evidence.", + "Seed any PMM pools still marked unseeded_pending in deployment-status, prioritizing stable quote rails with tracker-visible volume.", + ], + } + + +def write_markdown(report: dict[str, Any], path: Path) -> None: + lines: list[str] = [] + summary = report["summary"] + lines.append("# cW Full Operational Readiness") + lines.append("") + lines.append(f"- Generated: `{report['generatedAt']}`") + lines.append(f"- Active chains: `{', '.join(str(x) for x in report['activeChainIds'])}`") + lines.append(f"- In-repo source of truth configured: `{summary['inRepoSourceOfTruthConfigured']}`") + lines.append(f"- Fully operational and live: `{summary['fullyOperationalAndLive']}`") + lines.append(f"- Blocked gates: `{summary['blockedGateCount']}`") + lines.append(f"- Warning gates: `{summary['warningGateCount']}`") + lines.append("") + lines.append("## Gates") + lines.append("") + lines.append("| Gate | Status | Details | Evidence |") + lines.append("|---|---:|---|---|") + for gate in report["gates"]: + details = "
".join(gate["details"]) if gate["details"] else "-" + evidence = "
".join(f"`{x}`" for x in gate["evidence"]) if gate["evidence"] else "-" + lines.append(f"| {gate['title']} | `{gate['status']}` | {details} | {evidence} |") + lines.append("") + lines.append("## Next Actions") + lines.append("") + for action in report["nextActions"]: + lines.append(f"- {action}") + lines.append("") + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text("\n".join(lines) + "\n") + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--json-out", type=Path, default=ROOT / "reports" / "status" / "cw-full-operational-readiness-latest.json") + parser.add_argument("--md-out", type=Path, default=ROOT / "reports" / "status" / "cw-full-operational-readiness-latest.md") + parser.add_argument("--strict", action="store_true", help="Exit non-zero if any gate is blocked.") + args = parser.parse_args() + + report = build_report() + args.json_out.parent.mkdir(parents=True, exist_ok=True) + args.json_out.write_text(json.dumps(report, indent=2) + "\n") + write_markdown(report, args.md_out) + + print(f"Wrote {rel(args.json_out)}") + print(f"Wrote {rel(args.md_out)}") + print(f"In-repo configured: {report['summary']['inRepoSourceOfTruthConfigured']}") + print(f"Fully operational/live: {report['summary']['fullyOperationalAndLive']}") + if report["summary"]["blockedGateIds"]: + print("Blocked gates: " + ", ".join(report["summary"]["blockedGateIds"])) + + if args.strict and report["summary"]["blockedGateCount"]: + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/check-cw-full-operational-readiness.sh b/scripts/verify/check-cw-full-operational-readiness.sh new file mode 100755 index 00000000..f3c35fde --- /dev/null +++ b/scripts/verify/check-cw-full-operational-readiness.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Read-only readiness gate for cW source-of-truth, liquidity/indexing, bridge, and tracker status. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$ROOT" + +exec python3 scripts/verify/check-cw-full-operational-readiness.py "$@" diff --git a/scripts/verify/check-cw-multitoken-bridge-e2e-readiness.py b/scripts/verify/check-cw-multitoken-bridge-e2e-readiness.py new file mode 100755 index 00000000..59db2ce2 --- /dev/null +++ b/scripts/verify/check-cw-multitoken-bridge-e2e-readiness.py @@ -0,0 +1,364 @@ +#!/usr/bin/env python3 +"""Read-only CWMultiToken bridge readiness evidence. + +The script checks deployed bridge contracts, configured routes, canonical-to- +mirrored token mappings, and cW token MINTER/BURNER roles. It does not send +transactions or trigger bridge transfers. +""" +from __future__ import annotations + +import argparse +import json +import os +import subprocess +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +TOKEN_MAPPING = ROOT / "config" / "token-mapping-multichain.json" +DEFAULT_JSON = ROOT / "reports" / "status" / "cw-multitoken-bridge-e2e-latest.json" +DEFAULT_MD = ROOT / "reports" / "status" / "cw-multitoken-bridge-e2e-latest.md" + +ACTIVE_CHAINS = [ + (1, "Ethereum Mainnet", "MAINNET", "5009297550715157269"), + (10, "Optimism", "OPTIMISM", "3734403246176062136"), + (25, "Cronos", "CRONOS", "1456215246176062136"), + (56, "BSC", "BSC", "11344663589394136015"), + (100, "Gnosis", "GNOSIS", "465200170687744372"), + (137, "Polygon", "POLYGON", "4051577828743386545"), + (8453, "Base", "BASE", "15971525489660198786"), + (42161, "Arbitrum", "ARBITRUM", "4949039107694359620"), + (42220, "Celo", "CELO", "1346049177634351622"), + (43114, "Avalanche", "AVALANCHE", "6433500567565415381"), +] + +RPC_CANDIDATES = { + 1: ["ETHEREUM_MAINNET_RPC", "ETH_MAINNET_RPC_URL", "MAINNET_RPC_URL", "RPC_URL_MAINNET"], + 10: ["OPTIMISM_RPC_URL", "OPTIMISM_MAINNET_RPC"], + 25: ["CRONOS_RPC_URL", "CRONOS_MAINNET_RPC", "CRONOS_RPC"], + 56: ["BSC_RPC_URL", "BSC_MAINNET_RPC"], + 100: ["GNOSIS_RPC_URL", "GNOSIS_MAINNET_RPC", "GNOSIS_RPC"], + 137: ["POLYGON_RPC_URL", "POLYGON_MAINNET_RPC"], + 8453: ["BASE_RPC_URL", "BASE_MAINNET_RPC"], + 42161: ["ARBITRUM_RPC_URL", "ARBITRUM_MAINNET_RPC"], + 42220: ["CELO_RPC_URL", "CELO_MAINNET_RPC", "CELO_RPC"], + 43114: ["AVALANCHE_RPC_URL", "AVALANCHE_MAINNET_RPC", "AVALANCHE_RPC"], +} + +CORE_KEYS = {"Compliant_USDT_cW", "Compliant_USDC_cW"} + + +def rel(path: Path) -> str: + try: + return str(path.relative_to(ROOT)) + except ValueError: + return str(path) + + +def env_first(keys: list[str]) -> str: + for key in keys: + value = os.environ.get(key, "").strip() + if value: + return value + return "" + + +def is_address(value: str) -> bool: + return value.startswith("0x") and len(value) == 42 + + +def run_cast(args: list[str], timeout: int = 18) -> tuple[bool, str]: + try: + proc = subprocess.run(["cast", *args], cwd=ROOT, text=True, capture_output=True, timeout=timeout, check=False) + except Exception as exc: # noqa: BLE001 - evidence should capture any runner failure + return False, str(exc) + out = (proc.stdout or proc.stderr or "").strip() + return proc.returncode == 0, out + + +def cast_call(address: str, signature: str, params: list[str], rpc: str) -> tuple[bool, str]: + return run_cast(["call", address, signature, *params, "--rpc-url", rpc]) + + +def cast_code(address: str, rpc: str) -> tuple[bool, str]: + return run_cast(["code", address, "--rpc-url", rpc]) + + +def cast_keccak(value: str) -> str: + ok, out = run_cast(["keccak", value], timeout=5) + if not ok: + raise RuntimeError(f"cast keccak failed for {value}: {out}") + return out.split()[0] + + +def bool_from_cast(value: str) -> bool: + return value.strip().lower().splitlines()[-1:] == ["true"] + + +def address_in_cast(value: str, expected: str) -> bool: + return expected.lower() in value.lower() + + +def load_token_rows(full_family: bool) -> dict[int, list[dict[str, str]]]: + data = json.loads(TOKEN_MAPPING.read_text()) + out: dict[int, list[dict[str, str]]] = {} + for pair in data.get("pairs") or []: + if pair.get("fromChainId") != 138: + continue + chain_id = int(pair.get("toChainId")) + rows: list[dict[str, str]] = [] + for token in pair.get("tokens") or []: + key = token.get("key", "") + if not key.endswith("_cW") and key not in CORE_KEYS: + continue + if not full_family and key not in CORE_KEYS: + continue + address_from = token.get("addressFrom", "") + address_to = token.get("addressTo", "") + if is_address(address_from) and is_address(address_to) and int(address_to, 16) != 0: + rows.append( + { + "key": key, + "name": token.get("name", key), + "canonical": address_from, + "mirrored": address_to, + } + ) + if rows: + out[chain_id] = rows + return out + + +def check_l1(l1_bridge: str, rpc: str, token_rows: dict[int, list[dict[str, str]]]) -> dict[str, Any]: + result: dict[str, Any] = { + "address": l1_bridge, + "rpcConfigured": bool(rpc), + "hasCode": False, + "sendRouterReadable": False, + "receiveRouterReadable": False, + "destinationChecks": [], + "passed": False, + "errors": [], + } + if not is_address(l1_bridge): + result["errors"].append("CW_L1_BRIDGE_CHAIN138 is unset or invalid.") + return result + if not rpc: + result["errors"].append("RPC_URL_138/CHAIN138_RPC is unset.") + return result + + ok, code = cast_code(l1_bridge, rpc) + result["hasCode"] = ok and code not in ("", "0x") + for field in ["sendRouter", "receiveRouter"]: + ok, out = cast_call(l1_bridge, f"{field}()(address)", [], rpc) + result[f"{field}Readable"] = ok and is_address(out.splitlines()[-1].strip()) + result[f"{field}"] = out.splitlines()[-1].strip() if ok and out else "" + + for chain_id, _, _, selector in ACTIVE_CHAINS: + rows = token_rows.get(chain_id) or [] + for token in rows: + ok, out = cast_call( + l1_bridge, + "destinations(address,uint64)((address,bool))", + [token["canonical"], selector], + rpc, + ) + result["destinationChecks"].append( + { + "chainId": chain_id, + "selector": selector, + "token": token["key"], + "canonical": token["canonical"], + "raw": out, + "configured": ok and "true" in out.lower(), + } + ) + + result["passed"] = ( + result["hasCode"] + and result["sendRouterReadable"] + and result["receiveRouterReadable"] + and all(x["configured"] for x in result["destinationChecks"]) + ) + return result + + +def check_chain( + chain_id: int, + name: str, + suffix: str, + selector: str, + token_rows: list[dict[str, str]], + minter_role: str, + burner_role: str, +) -> dict[str, Any]: + rpc = env_first(RPC_CANDIDATES[chain_id]) + bridge = os.environ.get(f"CW_BRIDGE_{suffix}", "").strip() + result: dict[str, Any] = { + "chainId": chain_id, + "network": name, + "selector": selector, + "bridge": bridge, + "rpcConfigured": bool(rpc), + "hasCode": False, + "sendRouterReadable": False, + "receiveRouterReadable": False, + "feeTokenReadable": False, + "tokenPairChecks": [], + "roleChecks": [], + "destination138": {}, + "passed": False, + "errors": [], + } + if not rpc: + result["errors"].append("RPC unset.") + return result + if not is_address(bridge): + result["errors"].append(f"CW_BRIDGE_{suffix} is unset or invalid.") + return result + + ok, code = cast_code(bridge, rpc) + result["hasCode"] = ok and code not in ("", "0x") + for field in ["sendRouter", "receiveRouter", "feeToken"]: + ok, out = cast_call(bridge, f"{field}()(address)", [], rpc) + result[f"{field}Readable"] = ok and is_address(out.splitlines()[-1].strip()) + result[field] = out.splitlines()[-1].strip() if ok and out else "" + + ok, out = cast_call(bridge, "destinations(uint64)((address,bool))", ["138"], rpc) + result["destination138"] = {"raw": out, "configured": ok and "true" in out.lower()} + + for token in token_rows: + ok, out = cast_call(bridge, "canonicalToMirrored(address)(address)", [token["canonical"]], rpc) + mapped = ok and address_in_cast(out, token["mirrored"]) + result["tokenPairChecks"].append( + { + "token": token["key"], + "canonical": token["canonical"], + "expectedMirrored": token["mirrored"], + "raw": out, + "configured": mapped, + } + ) + for role_name, role in [("MINTER_ROLE", minter_role), ("BURNER_ROLE", burner_role)]: + ok_role, out_role = cast_call( + token["mirrored"], + "hasRole(bytes32,address)(bool)", + [role, bridge], + rpc, + ) + result["roleChecks"].append( + { + "token": token["key"], + "mirrored": token["mirrored"], + "role": role_name, + "holder": bridge, + "granted": ok_role and bool_from_cast(out_role), + "raw": out_role, + } + ) + + result["passed"] = ( + result["hasCode"] + and result["sendRouterReadable"] + and result["receiveRouterReadable"] + and result["destination138"]["configured"] + and all(x["configured"] for x in result["tokenPairChecks"]) + and all(x["granted"] for x in result["roleChecks"]) + ) + return result + + +def write_markdown(payload: dict[str, Any], path: Path) -> None: + lines = [ + "# cW MultiToken Bridge E2E Readiness", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Scope: `{payload['scope']}`", + f"- All active chains passed: `{payload['summary']['allActiveChainsPassed']}`", + f"- L1 passed: `{payload['summary']['l1Passed']}`", + f"- Chain pass count: `{payload['summary']['passedChainCount']} / {payload['summary']['activeChainCount']}`", + "", + "## Chain Status", + "", + "| Chain | Network | Passed | Bridge | Notes |", + "|---:|---|---:|---|---|", + ] + for row in payload["chains"]: + notes = [] + if row["errors"]: + notes.extend(row["errors"]) + failed_pairs = [x["token"] for x in row["tokenPairChecks"] if not x["configured"]] + failed_roles = [f"{x['token']} {x['role']}" for x in row["roleChecks"] if not x["granted"]] + if failed_pairs: + notes.append("missing token pairs: " + ", ".join(failed_pairs[:6])) + if failed_roles: + notes.append("missing roles: " + ", ".join(failed_roles[:6])) + if not row["destination138"].get("configured"): + notes.append("destination 138 not configured") + lines.append( + f"| {row['chainId']} | {row['network']} | `{row['passed']}` | `{row['bridge'] or ''}` | {'; '.join(notes) or 'ok'} |" + ) + lines.extend(["", "## L1", ""]) + l1 = payload["l1"] + lines.append(f"- Bridge: `{l1['address']}`") + lines.append(f"- Passed: `{l1['passed']}`") + if l1["errors"]: + lines.append(f"- Errors: `{'; '.join(l1['errors'])}`") + missing = [f"{x['chainId']} {x['token']}" for x in l1["destinationChecks"] if not x["configured"]] + lines.append(f"- Missing destination checks: `{', '.join(missing[:30]) if missing else 'none'}`") + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text("\n".join(lines) + "\n") + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--full-family", action="store_true", help="Check every cW mapping, not only cWUSDT/cWUSDC canary routes.") + parser.add_argument("--json-out", type=Path, default=DEFAULT_JSON) + parser.add_argument("--md-out", type=Path, default=DEFAULT_MD) + parser.add_argument("--strict", action="store_true") + args = parser.parse_args() + + token_rows = load_token_rows(args.full_family) + minter_role = cast_keccak("MINTER_ROLE") + burner_role = cast_keccak("BURNER_ROLE") + l1_bridge = os.environ.get("CW_L1_BRIDGE_CHAIN138", "").strip() + rpc_138 = env_first(["RPC_URL_138", "CHAIN138_RPC", "CHAIN138_RPC_URL", "RPC_URL"]) + l1 = check_l1(l1_bridge, rpc_138, token_rows) + chains = [ + check_chain(chain_id, name, suffix, selector, token_rows.get(chain_id) or [], minter_role, burner_role) + for chain_id, name, suffix, selector in ACTIVE_CHAINS + ] + passed = [row for row in chains if row["passed"]] + payload = { + "schema": "cw-multitoken-bridge-e2e-readiness/v1", + "generatedAt": datetime.now(timezone.utc).isoformat(), + "scope": "full-family" if args.full_family else "core-cwusdt-cwusdc", + "summary": { + "readyForProduction": l1["passed"] and len(passed) == len(chains), + "allActiveChainsPassed": l1["passed"] and len(passed) == len(chains), + "l1Passed": l1["passed"], + "activeChainCount": len(chains), + "passedChainCount": len(passed), + "failedChainIds": [row["chainId"] for row in chains if not row["passed"]], + }, + "roles": {"MINTER_ROLE": minter_role, "BURNER_ROLE": burner_role}, + "l1": l1, + "chains": chains, + } + args.json_out.parent.mkdir(parents=True, exist_ok=True) + args.json_out.write_text(json.dumps(payload, indent=2) + "\n") + write_markdown(payload, args.md_out) + print(f"Wrote {rel(args.json_out)}") + print(f"Wrote {rel(args.md_out)}") + print(f"All active chains passed: {payload['summary']['allActiveChainsPassed']}") + if payload["summary"]["failedChainIds"]: + print("Failed chains: " + ", ".join(str(x) for x in payload["summary"]["failedChainIds"])) + if args.strict and not payload["summary"]["allActiveChainsPassed"]: + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/check-cw-multitoken-bridge-e2e-readiness.sh b/scripts/verify/check-cw-multitoken-bridge-e2e-readiness.sh new file mode 100755 index 00000000..7c594e4b --- /dev/null +++ b/scripts/verify/check-cw-multitoken-bridge-e2e-readiness.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Read-only evidence producer for CWMultiTokenBridgeL1/L2 route, role, and config readiness. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$ROOT" + +# shellcheck disable=SC1091 +source scripts/lib/load-project-env.sh >/dev/null 2>&1 || true + +exec python3 scripts/verify/check-cw-multitoken-bridge-e2e-readiness.py "$@" diff --git a/scripts/verify/check-cwusdc-etherscan-prereq-urls.sh b/scripts/verify/check-cwusdc-etherscan-prereq-urls.sh new file mode 100755 index 00000000..96d07da1 --- /dev/null +++ b/scripts/verify/check-cwusdc-etherscan-prereq-urls.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +# Public URL prereq checks for cWUSDC Etherscan token profile (d-bis.org surfaces). +# See: docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md +# Usage: bash scripts/verify/check-cwusdc-etherscan-prereq-urls.sh [--json-out PATH] [--md-out PATH] [--timeout SEC] [--retries N] +# Exit: 0 if every URL returns HTTP 200; 1 otherwise. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" + +JSON_OUT="" +MD_OUT="" +TIMEOUT="${CWUSDC_PROVIDER_URL_TIMEOUT:-15}" +RETRIES="${CWUSDC_PROVIDER_URL_RETRIES:-1}" +while [[ $# -gt 0 ]]; do + case "$1" in + --json-out) + [[ $# -ge 2 ]] || { echo "Missing value for --json-out" >&2; exit 1; } + JSON_OUT="$2" + shift 2 + ;; + --md-out) + [[ $# -ge 2 ]] || { echo "Missing value for --md-out" >&2; exit 1; } + MD_OUT="$2" + shift 2 + ;; + --timeout) + [[ $# -ge 2 ]] || { echo "Missing value for --timeout" >&2; exit 1; } + TIMEOUT="$2" + shift 2 + ;; + --retries) + [[ $# -ge 2 ]] || { echo "Missing value for --retries" >&2; exit 1; } + RETRIES="$2" + shift 2 + ;; + -h|--help) + sed -n '1,5p' "$0" + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + exit 1 + ;; + esac +done + +TMP_TSV="$(mktemp)" +trap 'rm -f "$TMP_TSV"' EXIT + +FAIL=0 +while IFS= read -r url; do + [[ -z "$url" || "$url" =~ ^# ]] && continue + code="000" + curl_status=0 + attempts=0 + max_attempts=$((RETRIES + 1)) + while [[ "$attempts" -lt "$max_attempts" ]]; do + attempts=$((attempts + 1)) + curl_status=0 + code=$(curl -L --max-time "$TIMEOUT" -o /dev/null -s -w "%{http_code}" "$url") || curl_status=$? + [[ -n "$code" ]] || code="000" + [[ "$code" == "200" ]] && break + [[ "$attempts" -lt "$max_attempts" ]] && sleep 1 + done + if [[ "$code" != "200" ]]; then + echo "FAIL $code $url (attempts=$attempts curl_status=$curl_status)" >&2 + FAIL=1 + printf '%s\t%s\t%s\t%s\t%s\n' "$url" "$code" "false" "$attempts" "$curl_status" >> "$TMP_TSV" + else + echo "OK 200 $url (attempts=$attempts)" + printf '%s\t%s\t%s\t%s\t%s\n' "$url" "$code" "true" "$attempts" "$curl_status" >> "$TMP_TSV" + fi +done <<'URLS' +https://d-bis.org/ +https://d-bis.org/contact +https://d-bis.org/leadership +https://d-bis.org/gru/tokens +https://d-bis.org/security +https://d-bis.org/.well-known/trust.json +https://d-bis.org/brand-assets +https://d-bis.org/tokens/cwusdc.svg +URLS + +if [[ -n "$JSON_OUT" || -n "$MD_OUT" ]]; then + python3 - "$TMP_TSV" "$JSON_OUT" "$MD_OUT" <<'PY' +import json +import sys +from datetime import datetime, timezone +from pathlib import Path + +tsv = Path(sys.argv[1]) +json_out = Path(sys.argv[2]) if sys.argv[2] else None +md_out = Path(sys.argv[3]) if sys.argv[3] else None + +checks = [] +for line in tsv.read_text().splitlines(): + url, status, passed, attempts, curl_status = line.split("\t") + checks.append({ + "url": url, + "status": int(status) if status.isdigit() else status, + "passed": passed == "true", + "attempts": int(attempts), + "curlStatus": int(curl_status), + }) + +payload = { + "schema": "cwusdc-etherscan-prereq-urls/v1", + "generatedAt": datetime.now(timezone.utc).isoformat(), + "summary": { + "allPassed": all(check["passed"] for check in checks), + "requiredCount": len(checks), + "passedCount": sum(1 for check in checks if check["passed"]), + "failedUrls": [check["url"] for check in checks if not check["passed"]], + }, + "checks": checks, +} + +if json_out: + json_out.parent.mkdir(parents=True, exist_ok=True) + json_out.write_text(json.dumps(payload, indent=2) + "\n") + print(f"Wrote {json_out}") + +if md_out: + md_out.parent.mkdir(parents=True, exist_ok=True) + lines = [ + "# cWUSDC Etherscan Prerequisite URL Evidence", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- All passed: `{payload['summary']['allPassed']}`", + f"- Passed: `{payload['summary']['passedCount']} / {payload['summary']['requiredCount']}`", + "", + "| URL | Passed | HTTP | Attempts | curl status |", + "|---|---:|---:|---:|---:|", + ] + for check in checks: + lines.append(f"| {check['url']} | `{check['passed']}` | `{check['status']}` | `{check['attempts']}` | `{check['curlStatus']}` |") + md_out.write_text("\n".join(lines) + "\n") + print(f"Wrote {md_out}") +PY +fi + +exit "$FAIL" diff --git a/scripts/verify/check-cwusdc-external-trackers-live.py b/scripts/verify/check-cwusdc-external-trackers-live.py new file mode 100755 index 00000000..015e504f --- /dev/null +++ b/scripts/verify/check-cwusdc-external-trackers-live.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python3 +"""Probe cWUSDC public tracker/indexing surfaces and write evidence JSON.""" +from __future__ import annotations + +import argparse +import json +import re +import time +import urllib.error +import urllib.request +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +DEFAULT_JSON = ROOT / "reports" / "status" / "cwusdc-external-trackers-live-latest.json" +DEFAULT_MD = ROOT / "reports" / "status" / "cwusdc-external-trackers-live-latest.md" + +CWUSDC = "0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a" +POOLS = [ + "0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3", + "0xc28706f899266b36bc43cc072b3a921bdf2c48d9", +] + +URLS = [ + { + "id": "etherscan_token_page", + "kind": "explorer", + "url": f"https://etherscan.io/token/{CWUSDC}", + "required": True, + "mustContain": ["cWUSDC", "Contract"], + }, + { + "id": "coingecko_token_price_api", + "kind": "listing_api", + "url": f"https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses={CWUSDC}&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true", + "required": True, + "jsonTokenKey": CWUSDC, + }, + { + "id": "coinmarketcap_dex_token", + "kind": "dex_index", + "url": f"https://dex.coinmarketcap.com/token/ethereum/{CWUSDC}/", + "required": True, + "mustContain": ["cWUSDC", "ethereum"], + }, + { + "id": "dexscreener_token_pairs_v1", + "kind": "dex_index", + "url": f"https://api.dexscreener.com/token-pairs/v1/ethereum/{CWUSDC}", + "required": True, + "jsonRootMinLength": 1, + }, + { + "id": "dexscreener_tokens_v1", + "kind": "dex_index", + "url": f"https://api.dexscreener.com/tokens/v1/ethereum/{CWUSDC}", + "required": True, + "jsonRootMinLength": 1, + }, + { + "id": "dexscreener_v3_pair_api_legacy", + "kind": "dex_index", + "url": f"https://api.dexscreener.com/latest/dex/pairs/ethereum/{POOLS[0]}", + "required": False, + "jsonPathPresent": ["pairs"], + }, + { + "id": "dexscreener_v2_pair_api_legacy", + "kind": "dex_index", + "url": f"https://api.dexscreener.com/latest/dex/pairs/ethereum/{POOLS[1]}", + "required": False, + "jsonPathPresent": ["pairs"], + }, + { + "id": "dexscreener_orders_profile", + "kind": "dex_profile", + "url": f"https://api.dexscreener.com/orders/v1/ethereum/{CWUSDC}", + "required": False, + "jsonPathPresent": ["orders"], + }, + { + "id": "geckoterminal_v3_pool", + "kind": "dex_index", + "url": f"https://api.geckoterminal.com/api/v2/networks/eth/pools/{POOLS[0]}", + "required": True, + "jsonPathPresent": ["data"], + }, + { + "id": "geckoterminal_v2_pool", + "kind": "dex_index", + "url": f"https://api.geckoterminal.com/api/v2/networks/eth/pools/{POOLS[1]}", + "required": True, + "jsonPathPresent": ["data"], + }, +] + + +def rel(path: Path) -> str: + try: + return str(path.relative_to(ROOT)) + except ValueError: + return str(path) + + +def fetch(url: str, timeout: int) -> dict[str, Any]: + req = urllib.request.Request( + url, + headers={ + "User-Agent": "Mozilla/5.0 DBIS-readiness-check/1.0", + "Accept": "application/json,text/html;q=0.9,*/*;q=0.8", + }, + ) + started = time.time() + try: + with urllib.request.urlopen(req, timeout=timeout) as resp: + body = resp.read(512_000) + text = body.decode("utf-8", errors="replace") + return { + "ok": 200 <= resp.status < 300, + "status": resp.status, + "elapsedMs": int((time.time() - started) * 1000), + "contentType": resp.headers.get("content-type", ""), + "text": text, + "error": "", + } + except urllib.error.HTTPError as exc: + text = exc.read(64_000).decode("utf-8", errors="replace") if exc.fp else "" + return { + "ok": False, + "status": exc.code, + "elapsedMs": int((time.time() - started) * 1000), + "contentType": exc.headers.get("content-type", "") if exc.headers else "", + "text": text, + "error": str(exc), + } + except Exception as exc: # noqa: BLE001 - evidence should capture network errors + return { + "ok": False, + "status": None, + "elapsedMs": int((time.time() - started) * 1000), + "contentType": "", + "text": "", + "error": str(exc), + } + + +def json_path_present(data: Any, path: list[str]) -> bool: + cur = data + for part in path: + if isinstance(cur, dict): + cur = cur.get(part) + else: + return False + if isinstance(cur, list): + return len(cur) > 0 + return cur is not None + + +def evaluate(spec: dict[str, Any], timeout: int) -> dict[str, Any]: + raw = fetch(spec["url"], timeout) + text = raw.pop("text") + evidence: dict[str, Any] = { + "id": spec["id"], + "kind": spec["kind"], + "url": spec["url"], + "required": spec["required"], + "httpOk": raw["ok"], + "status": raw["status"], + "elapsedMs": raw["elapsedMs"], + "contentType": raw["contentType"], + "passed": False, + "error": raw["error"], + "details": [], + } + data: Any = None + if "json" in raw["contentType"] or text.strip().startswith(("{", "[")): + try: + data = json.loads(text) + evidence["jsonPreview"] = data if len(text) < 5000 else "json-too-large" + except json.JSONDecodeError as exc: + evidence["details"].append(f"JSON parse failed: {exc}") + + passed = raw["ok"] + for needle in spec.get("mustContain") or []: + found = re.search(re.escape(needle), text, flags=re.I) is not None + evidence["details"].append(f"contains `{needle}`: {found}") + passed = passed and found + if spec.get("jsonTokenKey"): + token_key = spec["jsonTokenKey"].lower() + found = isinstance(data, dict) and token_key in {str(k).lower(): v for k, v in data.items()} + evidence["details"].append(f"json token key `{token_key}` present: {found}") + passed = passed and found + for path in spec.get("jsonPathPresent") or []: + present = json_path_present(data, path if isinstance(path, list) else [path]) + evidence["details"].append(f"json path `{'.'.join(path) if isinstance(path, list) else path}` present: {present}") + passed = passed and present + if spec.get("jsonRootMinLength") is not None: + min_len = int(spec["jsonRootMinLength"]) + found = isinstance(data, list) and len(data) >= min_len + evidence["details"].append(f"json root array length >= {min_len}: {found}") + passed = passed and found + + evidence["passed"] = bool(passed) + return evidence + + +def write_markdown(payload: dict[str, Any], path: Path) -> None: + lines = [ + "# cWUSDC External Trackers Live Evidence", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- All trackers live: `{payload['summary']['allTrackersLive']}`", + f"- Required passed: `{payload['summary']['requiredPassedCount']} / {payload['summary']['requiredCount']}`", + "", + "| Surface | Passed | HTTP | URL | Details |", + "|---|---:|---:|---|---|", + ] + for check in payload["checks"]: + details = "; ".join(check["details"]) or check["error"] or "-" + 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") + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + 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("--strict", action="store_true") + args = parser.parse_args() + + checks = [evaluate(spec, args.timeout) for spec in URLS] + required = [c for c in checks if c["required"]] + required_passed = [c for c in required if c["passed"]] + payload = { + "schema": "cwusdc-external-trackers-live/v1", + "generatedAt": datetime.now(timezone.utc).isoformat(), + "token": {"chainId": 1, "network": "ethereum", "address": CWUSDC, "symbol": "cWUSDC"}, + "summary": { + "allTrackersLive": len(required_passed) == len(required), + "readyForEtherscanUsdValue": len(required_passed) == len(required), + "requiredCount": len(required), + "requiredPassedCount": len(required_passed), + "failedRequiredIds": [c["id"] for c in required if not c["passed"]], + }, + "checks": checks, + } + args.json_out.parent.mkdir(parents=True, exist_ok=True) + args.json_out.write_text(json.dumps(payload, indent=2) + "\n") + write_markdown(payload, args.md_out) + print(f"Wrote {rel(args.json_out)}") + print(f"Wrote {rel(args.md_out)}") + print(f"All trackers live: {payload['summary']['allTrackersLive']}") + if payload["summary"]["failedRequiredIds"]: + print("Failed required trackers: " + ", ".join(payload["summary"]["failedRequiredIds"])) + if args.strict and not payload["summary"]["allTrackersLive"]: + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/check-cwusdc-external-trackers-live.sh b/scripts/verify/check-cwusdc-external-trackers-live.sh new file mode 100755 index 00000000..6ffac2b7 --- /dev/null +++ b/scripts/verify/check-cwusdc-external-trackers-live.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Probe public cWUSDC tracker/indexing surfaces and write readiness evidence. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$ROOT" + +exec python3 scripts/verify/check-cwusdc-external-trackers-live.py "$@" diff --git a/scripts/verify/check-cwusdc-institutional-doc-links.py b/scripts/verify/check-cwusdc-institutional-doc-links.py new file mode 100644 index 00000000..6f503d72 --- /dev/null +++ b/scripts/verify/check-cwusdc-institutional-doc-links.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +"""Lightweight link check for the cWUSDC institutional evidence packet.""" + +from __future__ import annotations + +import argparse +import datetime as dt +import json +import re +import urllib.request +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +REPORT_JSON = ROOT / "reports" / "status" / "cwusdc-institutional-doc-link-check-latest.json" +REPORT_MD = ROOT / "reports" / "status" / "cwusdc-institutional-doc-link-check-latest.md" + +DOCS = [ + "docs/04-configuration/CWUSDC_PROVIDER_SUBMISSION_PACKET.md", + "docs/04-configuration/etherscan/CWUSDC_EVIDENCE_BUNDLE_INDEX.md", + "docs/04-configuration/etherscan/CWUSDC_SUPPLY_AND_CIRCULATING_METHODOLOGY.md", + "docs/04-configuration/etherscan/CWUSDC_SECURITY_AND_AUDIT_DISCLOSURE.md", + "docs/04-configuration/etherscan/CWUSDC_PROVIDER_RESPONSE_TRACKER.md", + "docs/04-configuration/etherscan/CWUSDC_LIQUIDITY_READINESS_NO_BROADCAST_PLAN.md", + "docs/04-configuration/etherscan/CWUSDC_MAINNET_ETHERSCAN_PROFILE_PACKET.md", + "docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md", + "docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_VALUE_EXECUTION_PLAN.md", + "docs/04-configuration/etherscan/CWUSDC_ETHERSCAN_BRIDGE_CROSSCHAIN_LAYER_MAP.md", + "docs/04-configuration/coingecko/CWUSDC_MAINNET_TRACKER_SUBMISSION_PACKET.md", + "docs/04-configuration/dexscreener/CWUSDC_DEXSCREENER_INDEXING_AND_PROFILE_PACKET_20260509.md", + "docs/04-configuration/metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md", +] + +LINK_RE = re.compile(r"(? str: + return target.split("#", 1)[0] + + +def is_skipped(target: str) -> bool: + return ( + not target + or target.startswith("#") + or target.startswith("mailto:") + or target.startswith("app://") + or target.startswith("plugin://") + ) + + +def resolve_local(source: Path, target: str) -> Path: + target = urllib.request.url2pathname(strip_fragment(target)) + if target.startswith("/"): + return ROOT / target.lstrip("/") + candidate = (source.parent / target).resolve() + try: + candidate.relative_to(ROOT) + except ValueError: + return ROOT / target + return candidate + + +def http_status(url: str, timeout: int = 15) -> dict[str, Any]: + req = urllib.request.Request(url, method="GET", headers={"User-Agent": "dbis-cwusdc-link-check/1.0"}) + try: + with urllib.request.urlopen(req, timeout=timeout) as response: + return {"ok": 200 <= response.status < 400, "status": response.status} + except Exception as exc: # noqa: BLE001 - report exact probe failure. + return {"ok": False, "error": str(exc)} + + +def collect_links(path: Path) -> list[str]: + text = path.read_text() + links = [match.group(1).strip() for match in LINK_RE.finditer(text)] + links.extend(match.group(0).strip(".,") for match in BARE_URL_RE.finditer(text)) + return sorted(set(links)) + + +def build(args: argparse.Namespace) -> dict[str, Any]: + records: list[dict[str, Any]] = [] + for doc in DOCS: + source = ROOT / doc + if not source.exists(): + records.append({"source": doc, "target": doc, "type": "source", "ok": False, "error": "source missing"}) + continue + for target in collect_links(source): + if is_skipped(target): + continue + if target.startswith("http://") or target.startswith("https://"): + result = {"source": doc, "target": target, "type": "http", "ok": True, "checked": False} + if args.check_http: + result.update(http_status(target, timeout=args.timeout)) + result["checked"] = True + records.append(result) + else: + resolved = resolve_local(source, target) + records.append( + { + "source": doc, + "target": target, + "type": "local", + "resolved": str(resolved.relative_to(ROOT)) if resolved.exists() else str(resolved), + "ok": resolved.exists(), + } + ) + failures = [record for record in records if not record.get("ok")] + return { + "schema": "cwusdc-institutional-doc-link-check/v1", + "generatedAt": dt.datetime.now(dt.UTC).isoformat().replace("+00:00", "Z"), + "checkedHttp": args.check_http, + "sourceCount": len(DOCS), + "linkCount": len(records), + "failureCount": len(failures), + "status": "pass" if not failures else "fail", + "failures": failures, + "records": records, + } + + +def write_md(payload: dict[str, Any], path: Path) -> None: + lines = [ + "# cWUSDC Institutional Doc Link Check", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Status: `{payload['status']}`", + f"- Sources: `{payload['sourceCount']}`", + f"- Links checked: `{payload['linkCount']}`", + f"- HTTP checked: `{payload['checkedHttp']}`", + f"- Failures: `{payload['failureCount']}`", + "", + ] + if payload["failures"]: + lines.extend(["## Failures", "", "| Source | Target | Error |", "|---|---|---|"]) + for failure in payload["failures"]: + lines.append( + f"| `{failure.get('source')}` | `{failure.get('target')}` | `{failure.get('error', failure.get('resolved', 'missing'))}` |" + ) + else: + lines.append("No broken institutional packet links were found.") + path.write_text("\n".join(lines) + "\n") + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--check-http", action="store_true", help="Probe public HTTP(S) links too.") + parser.add_argument("--timeout", type=int, default=15) + parser.add_argument("--json-out", type=Path, default=REPORT_JSON) + parser.add_argument("--md-out", type=Path, default=REPORT_MD) + args = parser.parse_args() + payload = build(args) + 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) + print(f"Wrote {args.json_out.relative_to(ROOT)}") + print(f"Wrote {args.md_out.relative_to(ROOT)}") + return 0 if payload["status"] == "pass" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/check-cwusdc-provider-readiness-ci.sh b/scripts/verify/check-cwusdc-provider-readiness-ci.sh new file mode 100755 index 00000000..3e956221 --- /dev/null +++ b/scripts/verify/check-cwusdc-provider-readiness-ci.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# CI-safe cWUSDC provider readiness gate. +# Fails only on repo-controlled prerequisites. External provider blockers are reported, not gated. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" + +JSON_OUT="reports/status/cwusdc-provider-readiness-ci-latest.json" +MD_OUT="reports/status/cwusdc-provider-readiness-ci-latest.md" +HANDOFF_JSON="reports/status/cwusdc-provider-handoff-latest.json" +DOC_LINK_JSON="reports/status/cwusdc-institutional-doc-link-check-latest.json" + +WRAPPER_STATUS=0 +bash "$SCRIPT_DIR/run-cwusdc-provider-nonmanual-checks.sh" --strict-repo || WRAPPER_STATUS=$? +python3 "$SCRIPT_DIR/check-cwusdc-institutional-doc-links.py" + +python3 - "$HANDOFF_JSON" "$DOC_LINK_JSON" "$JSON_OUT" "$MD_OUT" <<'PY' +import json +import sys +from datetime import datetime, timezone +from pathlib import Path + +handoff_path = Path(sys.argv[1]) +doc_link_path = Path(sys.argv[2]) +json_out = Path(sys.argv[3]) +md_out = Path(sys.argv[4]) +handoff = json.loads(handoff_path.read_text()) +doc_links = json.loads(doc_link_path.read_text()) +repo_ok = bool(handoff["summary"]["repoControlledPrereqsPassed"]) +doc_links_ok = doc_links.get("status") == "pass" +external_blockers = [b for b in handoff.get("blockers", []) if b.get("type") != "repo_controlled"] +payload = { + "schema": "cwusdc-provider-readiness-ci/v1", + "generatedAt": datetime.now(timezone.utc).isoformat(), + "status": "success" if repo_ok and doc_links_ok else "failed", + "repoControlledPrereqsPassed": repo_ok and doc_links_ok, + "baseRepoControlledPrereqsPassed": repo_ok, + "institutionalDocLinksPassed": doc_links_ok, + "institutionalDocLinksReport": str(doc_link_path), + "externalBlockersAdvisoryCount": len(external_blockers), + "externalBlockersAdvisory": external_blockers, + "handoffReport": str(handoff_path), +} +json_out.parent.mkdir(parents=True, exist_ok=True) +json_out.write_text(json.dumps(payload, indent=2) + "\n") +lines = [ + "# cWUSDC Provider Readiness CI", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Status: `{payload['status']}`", + f"- Repo-controlled prerequisites passed: `{payload['repoControlledPrereqsPassed']}`", + f"- Base provider prerequisites passed: `{repo_ok}`", + f"- Institutional doc links passed: `{doc_links_ok}`", + f"- External blockers advisory count: `{len(external_blockers)}`", + f"- Handoff report: `{handoff_path}`", + "", + "External provider blockers are advisory in this CI gate. They require provider acceptance or operator action and should not fail repo-controlled CI.", +] +md_out.write_text("\n".join(lines) + "\n") +print(f"Wrote {json_out}") +print(f"Wrote {md_out}") +if not repo_ok: + raise SystemExit(1) +if not doc_links_ok: + raise SystemExit(1) +PY + +exit "$WRAPPER_STATUS" diff --git a/scripts/verify/check-engine-x-mev-defense-readiness.sh b/scripts/verify/check-engine-x-mev-defense-readiness.sh new file mode 100755 index 00000000..1a4c7a40 --- /dev/null +++ b/scripts/verify/check-engine-x-mev-defense-readiness.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/mev-protection.sh +source "${PROJECT_ROOT}/scripts/lib/mev-protection.sh" + +POLICY_PATH="${POLICY_PATH:-config/extraction/mainnet-cwusdc-usdc-support-policy.json}" +OUT_JSON="${OUT_JSON:-reports/status/engine-x-mev-defense-readiness-latest.json}" +OUT_MD="${OUT_MD:-reports/status/engine-x-mev-defense-readiness-latest.md}" +mkdir -p "$(dirname "${OUT_JSON}")" + +RPC_LABEL="$(mev_write_rpc_label)" +PRIVATE_KEY_NAME="$(mev_private_rpc_key 2>/dev/null || true)" +HAS_PRIVATE_RPC=0 +[[ -n "${PRIVATE_KEY_NAME}" ]] && HAS_PRIVATE_RPC=1 +ALLOW_PUBLIC="${ENGINE_X_ALLOW_PUBLIC_BROADCAST:-0}" +MEV_ENABLED="${ENGINE_X_MEV_PROTECTION:-1}" +READY=0 +STATUS="blocked" +if [[ "${MEV_ENABLED}" != "1" ]]; then + STATUS="disabled_by_operator" +elif [[ "${HAS_PRIVATE_RPC}" == "1" ]]; then + READY=1 + STATUS="ready" +elif [[ "${ALLOW_PUBLIC}" == "1" ]]; then + STATUS="public_broadcast_override" +fi + +python3 - "${POLICY_PATH}" "${OUT_JSON}" "${OUT_MD}" "${READY}" "${STATUS}" "${RPC_LABEL}" "${PRIVATE_KEY_NAME:-}" "${MEV_ENABLED}" "${ALLOW_PUBLIC}" <<'PY' +import json +import sys +from datetime import datetime, timezone +from pathlib import Path + +policy_path, out_json, out_md, ready, status, rpc_label, private_key_name, mev_enabled, allow_public = sys.argv[1:] +policy = json.loads(Path(policy_path).read_text()) +mev_policy = policy.get("mevDefense", {}) +surfaces = policy.get("quoteDefenseSurfaces", []) +payload = { + "schema": "engine-x-mev-defense-readiness/v1", + "generatedAt": datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z"), + "ready": ready == "1", + "status": status, + "policyPath": policy_path, + "configuredWriteRpcLabel": rpc_label, + "configuredPrivateRpcEnvKey": private_key_name or None, + "mevProtectionEnabled": mev_enabled == "1", + "publicBroadcastOverride": allow_public == "1", + "sensitiveSurfaces": [ + { + "id": surface.get("id"), + "venue": surface.get("venue"), + "role": surface.get("role"), + "poolAddress": surface.get("poolAddress"), + "defenseMode": surface.get("defenseMode"), + } + for surface in surfaces + if "quote" in (surface.get("role") or "") or surface.get("venue") in {"uniswap_v2_pair", "uniswap_v3_pool", "dodo_pmm"} + ], + "policy": mev_policy, + "blockers": [], + "operatorEnvironment": { + "acceptedPrivateRpcEnvKeys": [ + "ENGINE_X_PRIVATE_TX_RPC", + "MEV_BLOCKER_RPC_URL", + "FLASHBOTS_RPC_URL", + "BLOXROUTE_RPC_URL", + "BLINK_RPC_URL", + ], + "publicOverrideEnvKey": "ENGINE_X_ALLOW_PUBLIC_BROADCAST", + "disableGuardEnvKey": "ENGINE_X_MEV_PROTECTION", + }, +} +if not payload["ready"]: + if payload["status"] == "blocked": + payload["blockers"].append("No private/protected transaction RPC is configured for sensitive Engine X broadcasts.") + elif payload["status"] == "disabled_by_operator": + payload["blockers"].append("MEV guard is disabled by operator environment.") + elif payload["status"] == "public_broadcast_override": + payload["blockers"].append("Public broadcast override is enabled; do not use for adversarially sensitive loops.") + +Path(out_json).write_text(json.dumps(payload, indent=2) + "\n") + +lines = [ + "# Engine X MEV Defense Readiness", + "", + f"- generatedAt: `{payload['generatedAt']}`", + f"- ready: `{str(payload['ready']).lower()}`", + f"- status: `{payload['status']}`", + f"- write RPC label: `{payload['configuredWriteRpcLabel']}`", + f"- private RPC env key: `{payload['configuredPrivateRpcEnvKey'] or 'none'}`", + f"- public override: `{str(payload['publicBroadcastOverride']).lower()}`", + "", + "## Sensitive Surfaces", +] +for surface in payload["sensitiveSurfaces"]: + lines.append(f"- `{surface['id']}` / `{surface['venue']}` / `{surface['role']}` / `{surface['poolAddress']}`") +lines.extend(["", "## Blockers"]) +if payload["blockers"]: + lines.extend(f"- {blocker}" for blocker in payload["blockers"]) +else: + lines.append("- none") +lines.extend([ + "", + "## Operator Rule", + "", + "Sensitive Engine X swaps, LP migrations, and quote-defense repairs must use `mev_cast_send`; scripts fail closed unless a protected RPC is configured or the operator explicitly enables the public broadcast override.", +]) +Path(out_md).write_text("\n".join(lines) + "\n") +print(json.dumps({"ready": payload["ready"], "status": payload["status"], "writeRpcLabel": payload["configuredWriteRpcLabel"]}, indent=2)) +PY diff --git a/scripts/verify/check-engine-x-public-indexed-readiness.sh b/scripts/verify/check-engine-x-public-indexed-readiness.sh index 99e2fa5d..c34404b5 100755 --- a/scripts/verify/check-engine-x-public-indexed-readiness.sh +++ b/scripts/verify/check-engine-x-public-indexed-readiness.sh @@ -90,7 +90,13 @@ from datetime import datetime, timezone def units(raw): return str(Decimal(int(raw or 0)) / Decimal(10**6)) -v2_nums = [int(x) for x in re.findall(r"\b\d+\b", v2_reserves or "")] +v2_nums = [] +for line in (v2_reserves or "").splitlines(): + match = re.search(r"\b\d+\b", line) + if match: + v2_nums.append(int(match.group(0))) +if len(v2_nums) < 2: + v2_nums = [int(x) for x in re.findall(r"\b\d+\b", v2_reserves or "")] blockers = [] if accounting_aware != "1": blockers.append("configured Engine X vault is not accounting-aware") diff --git a/scripts/verify/check-mainnet-cwusdc-usdc-support-health.py b/scripts/verify/check-mainnet-cwusdc-usdc-support-health.py index f2e84920..fc2b5dae 100755 --- a/scripts/verify/check-mainnet-cwusdc-usdc-support-health.py +++ b/scripts/verify/check-mainnet-cwusdc-usdc-support-health.py @@ -75,7 +75,13 @@ def parse_uint(value: str) -> int: def parse_uints(value: str, count: int) -> list[int]: - matches = [int(match) for match in UINT_RE.findall(value)] + matches: list[int] = [] + for line in value.splitlines(): + line_matches = UINT_RE.findall(line) + if line_matches: + matches.append(int(line_matches[0])) + if len(matches) < count: + matches = [int(match) for match in UINT_RE.findall(value)] if len(matches) < count: raise ValueError(f"expected at least {count} integers, got {matches!r}") return matches[:count] @@ -198,6 +204,71 @@ def query_dodo_health(rpc_url: str, defended_venue: dict) -> dict: } +def query_uniswap_v3_health(rpc_url: str, surface: dict) -> dict: + pool_address = surface["poolAddress"] + try: + token0 = parse_address(cast_call(rpc_url, pool_address, "token0()(address)")) + token1 = parse_address(cast_call(rpc_url, pool_address, "token1()(address)")) + fee = parse_uint(cast_call(rpc_url, pool_address, "fee()(uint24)")) + slot0 = cast_call(rpc_url, pool_address, "slot0()(uint160,int24,uint16,uint16,uint16,uint8,bool)") + slot_values = parse_uints(slot0, 2) + sqrt_price_x96 = slot_values[0] + # The tick may be signed; parse it directly from the second line before falling back. + slot_lines = [line.strip().split()[0] for line in slot0.splitlines() if line.strip()] + tick = int(slot_lines[1]) if len(slot_lines) > 1 else int(slot_values[1]) + liquidity = parse_uint(cast_call(rpc_url, pool_address, "liquidity()(uint128)")) + token0_balance = parse_uint(cast_call(rpc_url, token0, "balanceOf(address)(uint256)", pool_address)) + token1_balance = parse_uint(cast_call(rpc_url, token1, "balanceOf(address)(uint256)", pool_address)) + decimals0 = parse_uint(cast_call(rpc_url, token0, "decimals()(uint8)")) + decimals1 = parse_uint(cast_call(rpc_url, token1, "decimals()(uint8)")) + except Exception as exc: + return {"live": False, "poolAddress": pool_address, "error": str(exc)} + + preferred = surface.get("activeRange", {}) + lower_tick = int(preferred.get("preferredLowerTick", tick)) + upper_tick = int(preferred.get("preferredUpperTick", tick)) + target_tick = int(preferred.get("targetTick", 0)) + if tick < lower_tick: + range_status = "below_preferred_range" + elif tick > upper_tick: + range_status = "above_preferred_range" + else: + range_status = "inside_preferred_range" + + return { + "live": True, + "poolAddress": pool_address, + "token0": token0, + "token1": token1, + "fee": fee, + "sqrtPriceX96": str(sqrt_price_x96), + "tick": tick, + "targetTick": target_tick, + "preferredLowerTick": lower_tick, + "preferredUpperTick": upper_tick, + "rangeStatus": range_status, + "activeLiquidity": str(liquidity), + "token0BalanceRaw": str(token0_balance), + "token1BalanceRaw": str(token1_balance), + "token0BalanceUnits": str(normalize_units(token0_balance, decimals0)), + "token1BalanceUnits": str(normalize_units(token1_balance, decimals1)), + } + + +def query_quote_defense_surface(rpc_url: str, surface: dict, base_address: str, quote_address: str) -> dict: + venue = surface.get("venue") + if venue == "uniswap_v3_pool": + return query_uniswap_v3_health(rpc_url, surface) + if venue == "uniswap_v2_pair": + pair = dict(surface) + pair["baseAddress"] = base_address + pair["quoteAddress"] = quote_address + return query_uniswap_pair_health(rpc_url, pair) + if venue == "dodo_pmm": + return query_dodo_health(rpc_url, surface) + return {"live": False, "poolAddress": surface.get("poolAddress"), "error": f"unsupported venue {venue!r}"} + + def choose_flash_amount(policy: dict, deviation_bps: Decimal) -> int: for row in policy["managedCycle"]["quoteAmountByDeviationBps"]: if deviation_bps >= Decimal(row["minDeviationBps"]): @@ -282,6 +353,42 @@ def render_shell(result: dict) -> str: return "\n".join(lines) +def build_quote_defense_decision(surfaces: list[dict], health_by_id: dict[str, dict]) -> dict: + candidates: list[dict] = [] + blockers: list[str] = [] + for surface in surfaces: + surface_id = surface["id"] + health = health_by_id.get(surface_id, {}) + if not health.get("live"): + blockers.append(f"{surface_id}: unreadable") + continue + venue = surface.get("venue") + if venue == "uniswap_v3_pool": + if health.get("rangeStatus") == "inside_preferred_range" and int(health.get("activeLiquidity", "0")) > 0: + candidates.append({"surfaceId": surface_id, "action": "use_for_public_indexed_quote_defense"}) + else: + candidates.append({"surfaceId": surface_id, "action": "rebalance_tick_before_use"}) + elif venue == "uniswap_v2_pair": + quote_units = Decimal(health.get("quoteReserveUnits", "0")) + if quote_units > 0: + candidates.append({"surfaceId": surface_id, "action": "secondary_public_repair_or_activity_lane"}) + else: + blockers.append(f"{surface_id}: zero quote reserve") + elif venue == "dodo_pmm": + if health.get("live"): + candidates.append({"surfaceId": surface_id, "action": "managed_defended_lane_when_capital_and_quotes_pass"}) + + preferred = next( + (row for row in candidates if row["action"] == "use_for_public_indexed_quote_defense"), + next((row for row in candidates if row["action"] == "rebalance_tick_before_use"), None), + ) + return { + "preferredSurface": preferred, + "candidates": candidates, + "blockers": blockers, + } + + def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--shell", action="store_true", help="Emit shell-friendly KEY=VALUE lines.") @@ -293,13 +400,21 @@ def main() -> int: rpc_url = resolve_rpc_url(policy, env_values) chain = deployment_status["chains"][str(policy["network"]["chainId"])] + base_address = chain["cwTokens"]["cWUSDC"] + quote_address = chain["anchorAddresses"]["USDC"] public_pair = load_public_pair_from_policy(policy, deployment_status) - public_pair["baseAddress"] = chain["cwTokens"][public_pair["base"]] - public_pair["quoteAddress"] = chain["anchorAddresses"][public_pair["quote"]] + public_pair["baseAddress"] = base_address + public_pair["quoteAddress"] = quote_address defended_venue = dict(policy["defendedVenue"]) public_health = query_uniswap_pair_health(rpc_url, public_pair) defended_health = query_dodo_health(rpc_url, defended_venue) + quote_surfaces = policy.get("quoteDefenseSurfaces", []) + quote_surface_health = { + surface["id"]: query_quote_defense_surface(rpc_url, surface, base_address, quote_address) + for surface in quote_surfaces + } + quote_defense_decision = build_quote_defense_decision(quote_surfaces, quote_surface_health) decision = build_decision(policy, public_health, defended_health) if public_health.get("live"): decision["publicDeviationBps"] = public_health["deviationBps"] @@ -313,6 +428,9 @@ def main() -> int: "publicPairHealth": public_health, "defendedVenue": defended_venue, "defendedVenueHealth": defended_health, + "quoteDefenseSurfaces": quote_surfaces, + "quoteDefenseSurfaceHealth": quote_surface_health, + "quoteDefenseDecision": quote_defense_decision, "decision": decision, } diff --git a/scripts/verify/evaluate-mainnet-cwusdc-weth-liquidity-surfaces.sh b/scripts/verify/evaluate-mainnet-cwusdc-weth-liquidity-surfaces.sh new file mode 100755 index 00000000..e86abd25 --- /dev/null +++ b/scripts/verify/evaluate-mainnet-cwusdc-weth-liquidity-surfaces.sh @@ -0,0 +1,356 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" + +: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}" + +OUT_JSON="${OUT_JSON:-reports/status/mainnet-cwusdc-weth-liquidity-surfaces-latest.json}" +OUT_MD="${OUT_MD:-reports/status/mainnet-cwusdc-weth-liquidity-surfaces-latest.md}" +CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}" +USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}" +WETH="${WETH9_MAINNET:-0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2}" +UNIV2_FACTORY="${CHAIN_1_UNISWAP_V2_FACTORY:-0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f}" +UNIV2_ROUTER="${CHAIN_1_UNISWAP_V2_ROUTER:-0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D}" +UNIV3_FACTORY="${CHAIN_1_UNISWAP_V3_FACTORY:-0x1F98431c8aD98523631AE4a59f267346ea31F984}" +DODO_INTEGRATION="${DODO_PMM_INTEGRATION_MAINNET:-${CHAIN_1_DODO_PMM_INTEGRATION:-}}" +DODO_VENDING_MACHINE="${MAINNET_DODO_VENDING_MACHINE_ADDRESS:-${ETHEREUM_DODO_VENDING_MACHINE_ADDRESS:-}}" +MAINNET_CCIP_WETH9_BRIDGE="${MAINNET_CCIP_WETH9_BRIDGE:-}" +GAS_RESERVE_WEI="${ENGINE_X_WETH_POOL_GAS_RESERVE_WEI:-5000000000000000}" +MAX_ETH_WRAP_WEI="${ENGINE_X_WETH_POOL_MAX_WRAP_WEI:-0}" +PEG_TEST_AMOUNTS_USD="${ENGINE_X_WETH_PEG_TEST_AMOUNTS_USD:-0.005,0.01,0.025}" + +if [[ -n "${PRIVATE_KEY:-}" ]]; then + DEPLOYER="$(cast wallet address --private-key "${PRIVATE_KEY}")" +else + DEPLOYER="${DEPLOYER_ADDRESS:-}" +fi +if [[ -z "${DEPLOYER}" ]]; then + echo "Set PRIVATE_KEY or DEPLOYER_ADDRESS" >&2 + exit 1 +fi + +BLOCK_NUMBER="$(cast block-number --rpc-url "${ETHEREUM_MAINNET_RPC}")" +GAS_PRICE_WEI="$(cast gas-price --rpc-url "${ETHEREUM_MAINNET_RPC}")" +ETH_WEI="$(cast balance "${DEPLOYER}" --rpc-url "${ETHEREUM_MAINNET_RPC}")" +CWUSDC_RAW="$(cast call "${CWUSDC}" 'balanceOf(address)(uint256)' "${DEPLOYER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +USDC_RAW="$(cast call "${USDC}" 'balanceOf(address)(uint256)' "${DEPLOYER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +WETH_RAW="$(cast call "${WETH}" 'balanceOf(address)(uint256)' "${DEPLOYER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" + +UNIV2_PAIR="$(cast call "${UNIV2_FACTORY}" 'getPair(address,address)(address)' "${CWUSDC}" "${WETH}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)" +UNIV2_RESERVES="" +if [[ "${UNIV2_PAIR}" != "0x0000000000000000000000000000000000000000" ]]; then + UNIV2_RESERVES="$(cast call "${UNIV2_PAIR}" 'getReserves()(uint112,uint112,uint32)' --rpc-url "${ETHEREUM_MAINNET_RPC}" || true)" +fi + +TOKEN0="$(printf '%s\n%s\n' "${CWUSDC}" "${WETH}" | tr '[:upper:]' '[:lower:]' | sort | sed -n '1p')" +TOKEN1="$(printf '%s\n%s\n' "${CWUSDC}" "${WETH}" | tr '[:upper:]' '[:lower:]' | sort | sed -n '2p')" + +V3_POOLS_JSON="$( + for fee in 100 500 3000 10000; do + pool="$(cast call "${UNIV3_FACTORY}" 'getPool(address,address,uint24)(address)' "${TOKEN0}" "${TOKEN1}" "${fee}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)" + slot0="" + liquidity="0" + if [[ "${pool}" != "0x0000000000000000000000000000000000000000" ]]; then + slot0="$(cast call "${pool}" 'slot0()(uint160,int24,uint16,uint16,uint16,uint8,bool)' --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null || true)" + liquidity="$(cast call "${pool}" 'liquidity()(uint128)' --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)" + fi + python3 - "${fee}" "${pool}" "${liquidity}" "${slot0}" <<'PY' +import json, sys +fee, pool, liquidity, slot0 = sys.argv[1:] +print(json.dumps({"fee": int(fee), "pool": pool, "liquidity": liquidity, "slot0": slot0 or None})) +PY + done | python3 -c 'import json,sys; print(json.dumps([json.loads(line) for line in sys.stdin if line.strip()]))' +)" + +WETH_QUOTES_JSON="$( + for amt in 100000000000000 200000000000000 500000000000000 1000000000000000 2000000000000000 5000000000000000; do + raw="$(cast call "${UNIV2_ROUTER}" 'getAmountsOut(uint256,address[])(uint256[])' "${amt}" "[${WETH},${USDC}]" --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null || true)" + python3 - "${amt}" "${raw}" <<'PY' +import json, re, sys +amt = int(sys.argv[1]) +raw = sys.argv[2] +parts = raw.split(",", 1) +out = 0 +if len(parts) == 2: + match = re.search(r"(\d+)", parts[1]) + out = int(match.group(1)) if match else 0 +print(json.dumps({"wethInRaw": str(amt), "usdcOutRaw": str(out)})) +PY + done | python3 -c 'import json,sys; print(json.dumps([json.loads(line) for line in sys.stdin if line.strip()]))' +)" + +DODO_HAS_MANAGER="false" +DODO_CODE_LEN="0" +DODO_VENDING_CODE_LEN="0" +if [[ -n "${DODO_INTEGRATION}" ]]; then + DODO_CODE_LEN="$(cast code "${DODO_INTEGRATION}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | wc -c | tr -d ' ')" + POOL_MANAGER_ROLE="$(cast keccak "POOL_MANAGER_ROLE")" + DODO_HAS_MANAGER="$(cast call "${DODO_INTEGRATION}" 'hasRole(bytes32,address)(bool)' "${POOL_MANAGER_ROLE}" "${DEPLOYER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | tr -d '[:space:]' || echo false)" +fi +if [[ -n "${DODO_VENDING_MACHINE}" ]]; then + DODO_VENDING_CODE_LEN="$(cast code "${DODO_VENDING_MACHINE}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | wc -c | tr -d ' ')" +fi + +mkdir -p "$(dirname "${OUT_JSON}")" +python3 - "${OUT_JSON}" "${OUT_MD}" \ + "${BLOCK_NUMBER}" "${GAS_PRICE_WEI}" "${DEPLOYER}" "${CWUSDC}" "${WETH}" "${USDC}" "${MAINNET_CCIP_WETH9_BRIDGE}" \ + "${ETH_WEI}" "${CWUSDC_RAW}" "${USDC_RAW}" "${WETH_RAW}" "${GAS_RESERVE_WEI}" "${MAX_ETH_WRAP_WEI}" \ + "${UNIV2_PAIR}" "${UNIV2_RESERVES}" "${V3_POOLS_JSON}" "${WETH_QUOTES_JSON}" \ + "${DODO_INTEGRATION}" "${DODO_VENDING_MACHINE}" "${DODO_HAS_MANAGER}" "${DODO_CODE_LEN}" "${DODO_VENDING_CODE_LEN}" \ + "${PEG_TEST_AMOUNTS_USD}" <<'PY' +from decimal import Decimal, getcontext +from datetime import datetime, timezone +from pathlib import Path +import json +import re +import sys + +getcontext().prec = 80 +( + out_json, out_md, block_number, gas_price, deployer, cwusdc, weth, usdc, ccip_bridge, + eth_wei, cw_raw, usdc_raw, weth_raw, gas_reserve, max_wrap, univ2_pair, univ2_reserves, + v3_pools_json, weth_quotes_json, dodo_integration, dodo_vm, dodo_has_manager, dodo_code_len, dodo_vm_code_len, + peg_test_amounts_usd, +) = sys.argv[1:] + +eth_wei_i = int(eth_wei) +weth_raw_i = int(weth_raw) +cw_raw_i = int(cw_raw) +gas_reserve_i = int(gas_reserve) +max_wrap_i = int(max_wrap) +usable_eth_for_wrap = max(eth_wei_i - gas_reserve_i, 0) +if max_wrap_i: + usable_eth_for_wrap = min(usable_eth_for_wrap, max_wrap_i) +usable_weth_raw = weth_raw_i + usable_eth_for_wrap + +quotes = json.loads(weth_quotes_json) +price = Decimal(0) +for q in sorted(quotes, key=lambda x: int(x["wethInRaw"]), reverse=True): + if int(q["usdcOutRaw"]) > 0: + price = (Decimal(int(q["usdcOutRaw"])) / Decimal(10**6)) / (Decimal(int(q["wethInRaw"])) / Decimal(10**18)) + break + +cw_needed_for_wallet_weth_raw = int((Decimal(usable_weth_raw) / Decimal(10**18) * price * Decimal(10**6)).to_integral_value()) if price else 0 +usable_pair_cw_raw = min(cw_raw_i, cw_needed_for_wallet_weth_raw) +usable_pair_weth_raw = usable_weth_raw if cw_needed_for_wallet_weth_raw <= cw_raw_i else int((Decimal(cw_raw_i) / Decimal(10**6) / price * Decimal(10**18)).to_integral_value()) if price else 0 + +v3_pools = json.loads(v3_pools_json) +v3_existing = [p for p in v3_pools if p["pool"].lower() != "0x0000000000000000000000000000000000000000"] +v2_exists = univ2_pair.lower() != "0x0000000000000000000000000000000000000000" + +def fmt_decimal(value: Decimal, places: int = 18) -> str: + text = f"{value:.{places}f}" + return text.rstrip("0").rstrip(".") if "." in text else text + +def cp_out(amount_in: Decimal, reserve_in: Decimal, reserve_out: Decimal, fee_bps: Decimal = Decimal(30)) -> Decimal: + if amount_in <= 0 or reserve_in <= 0 or reserve_out <= 0: + return Decimal(0) + amount_in_after_fee = amount_in * (Decimal(10000) - fee_bps) / Decimal(10000) + return (amount_in_after_fee * reserve_out) / (reserve_in + amount_in_after_fee) + +fee_scenarios_bps = [ + ("uniswapV3_1bp", Decimal(1)), + ("uniswapV3_5bp", Decimal(5)), + ("uniswapV2_30bp", Decimal(30)), +] +peg_tests = [] +for raw_amount in [a.strip() for a in peg_test_amounts_usd.split(",") if a.strip()]: + amount_usd = Decimal(raw_amount) + cw_in = amount_usd + ideal_weth = (amount_usd / price) if price else Decimal(0) + fee_models = {} + for scenario_name, fee_bps in fee_scenarios_bps: + seeded_weth_out = cp_out( + cw_in, + Decimal(usable_pair_cw_raw) / Decimal(10**6), + Decimal(usable_pair_weth_raw) / Decimal(10**18), + fee_bps, + ) + effective_usd_out = seeded_weth_out * price if price else Decimal(0) + loss_pct = ((amount_usd - effective_usd_out) / amount_usd * Decimal(100)) if amount_usd > 0 and effective_usd_out else Decimal(0) + reverse_cw_out = cp_out( + ideal_weth, + Decimal(usable_pair_weth_raw) / Decimal(10**18), + Decimal(usable_pair_cw_raw) / Decimal(10**6), + fee_bps, + ) + reverse_loss_pct = ((amount_usd - reverse_cw_out) / amount_usd * Decimal(100)) if amount_usd > 0 and reverse_cw_out else Decimal(0) + fee_models[scenario_name] = { + "feeBps": str(fee_bps), + "cwusdcToWethOut": fmt_decimal(seeded_weth_out), + "cwusdcToWethUsdOut": fmt_decimal(effective_usd_out, 12), + "cwusdcToWethLossPct": fmt_decimal(loss_pct, 8), + "wethToCwusdcOut": fmt_decimal(reverse_cw_out, 12), + "wethToCwusdcLossPct": fmt_decimal(reverse_loss_pct, 8), + } + peg_tests.append({ + "usdAmount": str(amount_usd), + "cwusdcRaw": str(int((amount_usd * Decimal(10**6)).to_integral_value())), + "idealWethRaw": str(int((ideal_weth * Decimal(10**18)).to_integral_value())) if price else "0", + "idealWeth": fmt_decimal(ideal_weth), + "walletSeededModels": fee_models, + "lossAccounting": "For cWUSDC->WETH9 canaries, loss is paid by spending slightly more cWUSDC value for the same WETH/USD reference value. WETH/USDC inventory is protected by exact-output/min-out guards.", + }) + +native_boundary = "Native ETH execution wraps to WETH9; DODO and UniV3 require ERC-20 WETH9, while UniV2 addLiquidityETH still creates a WETH9 pair." +usd_support_model = ( + "cWUSDC/WETH9 pools support the USD peg as an indirect public price anchor through deep WETH/USD markets. " + "They do not replace direct cWUSDC/USDC quote liquidity for redemption-style proof, but they can provide indexable " + "on-chain evidence that 1 cWUSDC is priced near 1 USD when the implied cWUSDC/WETH price matches the live WETH/USD reference." +) +blockers = [] +if int(usdc_raw) == 0: + blockers.append("deployer has 0 USDC; this is fine for WETH pools but means no cWUSDC/USDC repair can accompany them") +if usable_weth_raw == 0: + blockers.append("no WETH or spare ETH available after gas reserve") +if not dodo_integration: + blockers.append("DODO_PMM_INTEGRATION_MAINNET is not configured") +elif dodo_has_manager != "true": + blockers.append("deployer lacks DODO POOL_MANAGER_ROLE on Mainnet integration") +if dodo_vm and int(dodo_vm_code_len) <= 3: + blockers.append("configured DODO vending machine has no code") + +dodo_single_sided = { + "requestedBaseToken": cwusdc, + "supportedAsEngineXInventory": cw_raw_i > 0, + "executableThroughCurrentIntegration": False, + "reason": "DODOPMMIntegration.addLiquidity requires both baseAmount > 0 and quoteAmount > 0; cWUSDC-only deposits are inventory/accounting support until a wrapper or quote-side seed is available.", + "currentCwusdcAvailable": str(Decimal(cw_raw_i) / Decimal(10**6)), + "recommendedMode": "record cWUSDC-only inventory inside Engine X first; promote to executable DODO PMM only after adding WETH9/USDC quote inventory or deploying a single-sided wrapper that controls solvency and min-out proofs", +} + +payload = { + "schema": "mainnet-cwusdc-weth-liquidity-surfaces/v1", + "generatedAt": datetime.now(timezone.utc).isoformat(), + "blockNumber": int(block_number), + "gasPriceWei": gas_price, + "addresses": { + "deployer": deployer, + "cWUSDC": cwusdc, + "WETH9": weth, + "USDC": usdc, + "mainnetCcipWeth9Bridge": ccip_bridge or None, + }, + "balances": { + "ethWei": eth_wei, + "eth": str(Decimal(int(eth_wei)) / Decimal(10**18)), + "wethRaw": weth_raw, + "weth": str(Decimal(weth_raw_i) / Decimal(10**18)), + "cwusdcRaw": cw_raw, + "cwusdc": str(Decimal(cw_raw_i) / Decimal(10**6)), + "usdcRaw": usdc_raw, + "usdc": str(Decimal(int(usdc_raw)) / Decimal(10**6)), + }, + "marketReference": { + "wethUsdcPriceFromUniV2": str(price), + "wethQuotes": quotes, + }, + "availableSeed": { + "gasReserveWei": str(gas_reserve_i), + "ethUsableForWrapWei": str(usable_eth_for_wrap), + "totalUsableWethRaw": str(usable_weth_raw), + "totalUsableWeth": str(Decimal(usable_weth_raw) / Decimal(10**18)), + "cwusdcNeededForAllUsableWethRaw": str(cw_needed_for_wallet_weth_raw), + "cwusdcNeededForAllUsableWeth": str(Decimal(cw_needed_for_wallet_weth_raw) / Decimal(10**6)), + "recommendedPairCwusdcRaw": str(usable_pair_cw_raw), + "recommendedPairCwusdc": str(Decimal(usable_pair_cw_raw) / Decimal(10**6)), + "recommendedPairWethRaw": str(usable_pair_weth_raw), + "recommendedPairWeth": str(Decimal(usable_pair_weth_raw) / Decimal(10**18)), + }, + "pegTestAmounts": peg_tests, + "surfaces": { + "uniswapV2": {"pair": univ2_pair, "exists": v2_exists, "reservesRawText": univ2_reserves or None}, + "uniswapV3": {"token0": min(cwusdc.lower(), weth.lower()), "token1": max(cwusdc.lower(), weth.lower()), "pools": v3_pools, "existingPools": v3_existing}, + "dodo": { + "integration": dodo_integration or None, + "vendingMachine": dodo_vm or None, + "integrationCodeLength": int(dodo_code_len), + "vendingMachineCodeLength": int(dodo_vm_code_len), + "deployerHasPoolManagerRole": dodo_has_manager == "true", + }, + }, + "singleSidedDodoCwusdc": dodo_single_sided, + "boundary": native_boundary, + "usdPegSupportModel": usd_support_model, + "blockers": blockers, + "recommendation": [ + "Treat cWUSDC/ETH and cWUSDC/WETH as the same WETH9-backed public market for indexers.", + "Use WETH-backed pools as an indirect USD peg support surface by comparing cWUSDC/WETH9 pool price against live WETH/USDC reference markets.", + "Create at most one canonical UniV3 cWUSDC/WETH9 pool first, using private/protected execution and a wider tick range than the cWUSDC/USDC attempt.", + "Use UniV2 addLiquidityETH only as a secondary indexable surface; it creates the same WETH9 pair.", + "Use DODO PMM only after confirming createPool against the configured vending machine and seeding with ERC-20 WETH9, not native ETH.", + ], +} +Path(out_json).write_text(json.dumps(payload, indent=2) + "\n") + +lines = [ + "# Mainnet cWUSDC/WETH9 Liquidity Surface Evaluation", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Block: `{payload['blockNumber']}`", + f"- cWUSDC: `{cwusdc}`", + f"- WETH9 Cc2: `{weth}`", + f"- Native ETH boundary: {native_boundary}", + f"- USD peg support model: {usd_support_model}", + "", + "## Live Balances", + "", + f"- ETH: `{payload['balances']['eth']}`", + f"- WETH9: `{payload['balances']['weth']}`", + f"- cWUSDC: `{payload['balances']['cwusdc']}`", + f"- USDC: `{payload['balances']['usdc']}`", + "", + "## Existing Pools", + "", + f"- UniV2 cWUSDC/WETH9: `{univ2_pair}`", +] +for pool in v3_pools: + lines.append(f"- UniV3 fee `{pool['fee']}`: `{pool['pool']}` liquidity `{pool['liquidity']}`") +lines.extend([ + f"- DODO integration: `{dodo_integration or 'not configured'}`", + f"- DODO deployer pool-manager role: `{dodo_has_manager == 'true'}`", + "", + "## Seed Capacity", + "", + f"- WETH/USDC reference price: `{price}`", + f"- Total usable WETH after gas reserve: `{payload['availableSeed']['totalUsableWeth']}`", + f"- cWUSDC needed to pair all usable WETH: `{payload['availableSeed']['cwusdcNeededForAllUsableWeth']}`", + f"- Recommended max seed from current wallet: `{payload['availableSeed']['recommendedPairCwusdc']} cWUSDC + {payload['availableSeed']['recommendedPairWeth']} WETH9`", + "", + "## Single-Sided DODO cWUSDC", + "", + f"- Supported as Engine X inventory: `{dodo_single_sided['supportedAsEngineXInventory']}`", + f"- Executable through current DODOPMMIntegration: `{dodo_single_sided['executableThroughCurrentIntegration']}`", + f"- Reason: {dodo_single_sided['reason']}", + f"- Recommended mode: {dodo_single_sided['recommendedMode']}", + "", + "## Peg Test Amounts", + "", + "| USD amount | cWUSDC in | Ideal WETH value | Modeled cWUSDC->WETH USD out | Modeled loss | Reverse WETH->cWUSDC out | Reverse loss |", + "|---:|---:|---:|---:|---:|---:|---:|", +]) +for test in peg_tests: + for scenario_name, model in test["walletSeededModels"].items(): + lines.append( + f"| `{test['usdAmount']} ({scenario_name})` | `{Decimal(test['cwusdcRaw']) / Decimal(10**6)}` | `{test['idealWeth']}` | " + f"`{model['cwusdcToWethUsdOut']}` | `{model['cwusdcToWethLossPct']}%` | " + f"`{model['wethToCwusdcOut']}` | `{model['wethToCwusdcLossPct']}%` |" + ) +lines.extend([ + "", + "Loss accounting: for cWUSDC->WETH9 canaries, loss is paid by spending slightly more cWUSDC value for the same WETH/USD reference value. Use exact-output or strict min-out guards so WETH, USDC, and lender inventory are not silently depleted.", + "", + "## Blockers", +]) +lines.extend([f"- {b}" for b in blockers] if blockers else ["- none for a tiny WETH-backed pool seed"]) +lines.extend(["", "## Recommendation"]) +lines.extend([f"- {r}" for r in payload["recommendation"]]) +Path(out_md).write_text("\n".join(lines) + "\n") +print(out_json) +print(out_md) +PY diff --git a/scripts/verify/generate-cwusdc-supply-circulating-attestation.py b/scripts/verify/generate-cwusdc-supply-circulating-attestation.py new file mode 100644 index 00000000..5714673b --- /dev/null +++ b/scripts/verify/generate-cwusdc-supply-circulating-attestation.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python3 +"""Generate a current cWUSDC supply and circulating-supply attestation. + +The output is intentionally tracker-facing: it separates on-chain total supply +from proposed circulating-supply methodology and does not silently exclude +operator or protocol balances unless explicitly requested. +""" + +from __future__ import annotations + +import argparse +import datetime as dt +import json +import os +import re +import time +import urllib.parse +import urllib.request +from decimal import Decimal, getcontext +from pathlib import Path +from typing import Any + + +getcontext().prec = 80 + +ROOT = Path(__file__).resolve().parents[2] +REPORT_JSON = ROOT / "reports" / "status" / "cwusdc-supply-circulating-attestation-latest.json" +REPORT_MD = ROOT / "reports" / "status" / "cwusdc-supply-circulating-attestation-latest.md" +ETHERSCAN_API = "https://api.etherscan.io/v2/api" +ETHERSCAN_PAGE = "https://etherscan.io/token/{address}" +CWUSDC = "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a" +DECIMALS = 6 + +KNOWN_BALANCES = { + "operator": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "engineXVirtualBatchVault": "0xf108586d1FC330EA1D4EA4ff8fd983cde94279B1", + "uniswapV3CwusdcUsdcPool": "0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3", + "uniswapV2CwusdcUsdcPair": "0xC28706F899266b36BC43cc072b3a921BDf2C48D9", +} + + +def load_dotenv(path: Path) -> None: + if not path.exists(): + return + for line in path.read_text().splitlines(): + line = line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + key, value = line.split("=", 1) + key = key.strip() + value = value.strip().strip('"').strip("'") + if key and key not in os.environ: + os.environ[key] = value + + +def fetch_json(url: str, timeout: int = 30) -> Any: + req = urllib.request.Request(url, headers={"User-Agent": "dbis-cwusdc-supply-attestation/1.0"}) + with urllib.request.urlopen(req, timeout=timeout) as response: + return json.loads(response.read().decode("utf-8")) + + +def etherscan_call(params: dict[str, str], api_key: str) -> Any: + query = {"chainid": "1", **params, "apikey": api_key} + url = f"{ETHERSCAN_API}?{urllib.parse.urlencode(query)}" + last_payload: Any = None + for attempt in range(6): + payload = fetch_json(url) + last_payload = payload + message = str(payload.get("message", "")) + result = payload.get("result") + if str(payload.get("status")) != "0" or message.lower() == "no transactions found": + time.sleep(0.35) + return result + if isinstance(result, str) and result.startswith("0x"): + time.sleep(0.35) + return result + if "rate limit" in str(result).lower() or "rate limit" in message.lower(): + time.sleep(1.25 + attempt * 0.5) + continue + raise RuntimeError(f"Etherscan API error: {payload.get('message')} {payload.get('result')}") + raise RuntimeError(f"Etherscan API error after retries: {last_payload}") + + +def human(raw: int, decimals: int = DECIMALS) -> str: + scaled = Decimal(raw) / (Decimal(10) ** decimals) + return f"{scaled:f}" + + +def parse_int(value: Any) -> int: + if isinstance(value, str) and value.startswith("0x"): + return int(value, 16) + return int(str(value)) + + +def fetch_etherscan_page_stats(address: str) -> dict[str, Any]: + url = ETHERSCAN_PAGE.format(address=address) + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 DBIS-cwusdc-attestation/1.0"}) + with urllib.request.urlopen(req, timeout=30) as response: + html = response.read().decode("utf-8", errors="replace") + + holders_match = re.search(r"]*>\s*Holders\s*\s*]*>\s*
\s*([0-9,]+)", html, re.I) + total_supply_match = re.search(r'id="ContentPlaceHolder1_hdnTotalSupply" value="([^"]+)"', html) + onchain_marketcap_missing = "id=\"ContentPlaceHolder1_tr_marketcap\"" in html and re.search( + r"id=\"ContentPlaceHolder1_tr_marketcap\".*?
\s*-\s*
", html, re.I | re.S + ) + circulating_marketcap_missing = "id=\"ContentPlaceHolder1_tr_circulatingmarketcap\"" in html and re.search( + r"id=\"ContentPlaceHolder1_tr_circulatingmarketcap\".*?
\s*-\s*
", html, re.I | re.S + ) + return { + "url": url, + "holdersText": holders_match.group(1) if holders_match else None, + "totalSupplyText": total_supply_match.group(1) if total_supply_match else None, + "onchainMarketCapMissing": bool(onchain_marketcap_missing), + "circulatingMarketCapMissing": bool(circulating_marketcap_missing), + } + + +def build(args: argparse.Namespace) -> dict[str, Any]: + load_dotenv(ROOT / ".env") + api_key = args.etherscan_api_key or os.environ.get("ETHERSCAN_API_KEY", "") + if not api_key: + raise SystemExit("ETHERSCAN_API_KEY is required") + + latest_block_raw = etherscan_call({"module": "proxy", "action": "eth_blockNumber"}, api_key) + latest_block = parse_int(latest_block_raw) + total_supply_raw = parse_int( + etherscan_call( + {"module": "stats", "action": "tokensupply", "contractaddress": args.token}, + api_key, + ) + ) + + known: dict[str, Any] = {} + excluded_raw = 0 + exclude_set = set(args.exclude_known or []) + for label, address in KNOWN_BALANCES.items(): + raw = parse_int( + etherscan_call( + { + "module": "account", + "action": "tokenbalance", + "contractaddress": args.token, + "address": address, + "tag": "latest", + }, + api_key, + ) + ) + excluded = label in exclude_set + if excluded: + excluded_raw += raw + known[label] = { + "address": address, + "balanceRaw": str(raw), + "balanceUnits": human(raw), + "excludedFromCirculatingSupply": excluded, + } + + circulating_raw = total_supply_raw - excluded_raw + page_stats = fetch_etherscan_page_stats(args.token) + + return { + "schema": "cwusdc-supply-circulating-attestation/v1", + "generatedAt": dt.datetime.now(dt.UTC).isoformat().replace("+00:00", "Z"), + "purpose": "Tracker-facing supply and circulating-supply attestation for Etherscan Value propagation.", + "network": {"chainId": 1, "name": "Ethereum Mainnet", "referenceBlock": latest_block}, + "token": { + "address": args.token, + "caip19": f"eip155:1/erc20:{args.token}", + "name": "Wrapped cUSDC", + "symbol": "cWUSDC", + "decimals": DECIMALS, + "etherscan": ETHERSCAN_PAGE.format(address=args.token), + }, + "supply": { + "totalSupplyRaw": str(total_supply_raw), + "totalSupplyUnits": human(total_supply_raw), + "excludedProtocolControlledRaw": str(excluded_raw), + "excludedProtocolControlledUnits": human(excluded_raw), + "circulatingSupplyRaw": str(circulating_raw), + "circulatingSupplyUnits": human(circulating_raw), + "formula": "circulatingSupply = totalSupply - explicitlyExcludedProtocolControlledNonCirculatingBalances", + "defaultPolicy": "No known balance is excluded unless the operator passes --exclude-known for that label or a tracker requests a specific exclusion methodology.", + }, + "knownBalances": known, + "etherscanPageObservation": page_stats, + "submissionPosition": { + "readyForTrackerReview": True, + "requestedProviderAction": "Accept total/circulating supply for the exact Mainnet cWUSDC contract and use it with accepted USD price data to populate market cap/value surfaces.", + "caveats": [ + "This is an on-chain supply attestation, not third-party listing approval.", + "Chain 138 cUSDC source-asset activity must not be counted as Ethereum Mainnet cWUSDC transfer activity.", + "If a tracker requires treasury, bridge, operator, or pool exclusions, regenerate with explicit --exclude-known labels and attach the requested signed inventory.", + ], + }, + } + + +def write_md(payload: dict[str, Any], path: Path) -> None: + supply = payload["supply"] + lines = [ + "# cWUSDC Supply and Circulating-Supply Attestation", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Reference block: `{payload['network']['referenceBlock']}`", + f"- Token: `{payload['token']['address']}`", + f"- CAIP-19: `{payload['token']['caip19']}`", + "", + "## Supply", + "", + "| Field | Value |", + "|---|---:|", + f"| Total supply | `{supply['totalSupplyUnits']}` |", + f"| Explicitly excluded protocol-controlled balances | `{supply['excludedProtocolControlledUnits']}` |", + f"| Circulating supply | `{supply['circulatingSupplyUnits']}` |", + "", + f"Formula: `{supply['formula']}`", + "", + "## Known Balances", + "", + "| Label | Address | Balance | Excluded |", + "|---|---|---:|---:|", + ] + for label, item in payload["knownBalances"].items(): + lines.append( + f"| `{label}` | `{item['address']}` | `{item['balanceUnits']}` | `{item['excludedFromCirculatingSupply']}` |" + ) + page = payload["etherscanPageObservation"] + lines.extend( + [ + "", + "## Etherscan Observation", + "", + f"- URL: `{page['url']}`", + f"- Holders text: `{page['holdersText']}`", + f"- Total supply text: `{page['totalSupplyText']}`", + f"- Onchain market cap missing: `{page['onchainMarketCapMissing']}`", + f"- Circulating market cap missing: `{page['circulatingMarketCapMissing']}`", + "", + "## Caveats", + "", + ] + ) + for caveat in payload["submissionPosition"]["caveats"]: + lines.append(f"- {caveat}") + path.write_text("\n".join(lines) + "\n") + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--token", default=CWUSDC) + parser.add_argument("--etherscan-api-key", default="") + parser.add_argument("--exclude-known", action="append", choices=sorted(KNOWN_BALANCES), help="Known balance label to exclude from circulating supply. Repeatable.") + parser.add_argument("--json-out", type=Path, default=REPORT_JSON) + parser.add_argument("--md-out", type=Path, default=REPORT_MD) + args = parser.parse_args() + + payload = build(args) + 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) + print(f"Wrote {args.json_out.relative_to(ROOT)}") + print(f"Wrote {args.md_out.relative_to(ROOT)}") + print(f"circulatingSupply={payload['supply']['circulatingSupplyUnits']}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/generate-global-cusdc-cwusdc-family-supply-proof.py b/scripts/verify/generate-global-cusdc-cwusdc-family-supply-proof.py new file mode 100644 index 00000000..a0e14231 --- /dev/null +++ b/scripts/verify/generate-global-cusdc-cwusdc-family-supply-proof.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 +"""Generate a global cUSDC/cWUSDC family supply proof from report APIs. + +This is deliberately NOT the Ethereum Mainnet cWUSDC Etherscan supply proof. +It is a cross-chain family inventory. Entries without supply proof are listed +but excluded from aggregate totals. +""" + +from __future__ import annotations + +import argparse +import datetime as dt +import json +import urllib.request +from decimal import Decimal, getcontext +from pathlib import Path +from typing import Any + + +getcontext().prec = 80 + +ROOT = Path(__file__).resolve().parents[2] +DEFAULT_API = "https://explorer.d-bis.org/api/v1/report/all" +DEFAULT_JSON = ROOT / "reports" / "status" / "global-cusdc-cwusdc-family-supply-proof-latest.json" +DEFAULT_MD = ROOT / "reports" / "status" / "global-cusdc-cwusdc-family-supply-proof-latest.md" +ETH_MAINNET_CWUSDC = "0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a" + + +def fetch_json(url: str) -> Any: + req = urllib.request.Request(url, headers={"User-Agent": "dbis-global-cusdc-cwusdc-proof/1.0"}) + with urllib.request.urlopen(req, timeout=30) as response: + return json.loads(response.read().decode("utf-8")) + + +def decimal_or_none(value: Any) -> Decimal | None: + if value is None: + return None + try: + return Decimal(str(value)) + except Exception: # noqa: BLE001 - proof should keep malformed entries out of totals + return None + + +def fmt(value: Decimal) -> str: + return f"{value:f}" + + +def iter_family_tokens(report: dict[str, Any]) -> list[dict[str, Any]]: + tokens_by_chain = report.get("tokens") or {} + rows: list[dict[str, Any]] = [] + for chain_id, tokens in tokens_by_chain.items(): + if not isinstance(tokens, list): + continue + for token in tokens: + if token.get("symbol") not in {"cUSDC", "cWUSDC"}: + continue + total = decimal_or_none(token.get("totalSupply")) + circulating = decimal_or_none(token.get("circulatingSupply")) + provenance = token.get("supplyProofProvenance") or {} + proved = total is not None and str(provenance.get("status", "")).lower() != "proof_required" + address = str(token.get("address") or "").lower() + rows.append( + { + "chainId": int(token.get("chainId") or chain_id), + "address": address, + "symbol": token.get("symbol"), + "name": token.get("name"), + "type": token.get("type"), + "decimals": token.get("decimals"), + "totalSupply": str(total) if total is not None else None, + "circulatingSupply": str(circulating) if circulating is not None else None, + "provedForAggregate": proved, + "isEthereumMainnetCwusdc": int(token.get("chainId") or chain_id) == 1 and address == ETH_MAINNET_CWUSDC, + "supplyProofProvenance": provenance, + "trackerCaveats": token.get("trackerCaveats") or [], + } + ) + return sorted(rows, key=lambda item: (item["chainId"], item["symbol"], item["address"])) + + +def build(api_url: str) -> dict[str, Any]: + report = fetch_json(api_url) + rows = iter_family_tokens(report) + proved = [row for row in rows if row["provedForAggregate"]] + unproved = [row for row in rows if not row["provedForAggregate"]] + + totals: dict[str, Decimal] = { + "globalFamilyTotalSupply": Decimal(0), + "globalFamilyCirculatingSupply": Decimal(0), + "baseCusdcTotalSupply": Decimal(0), + "baseCusdcCirculatingSupply": Decimal(0), + "wrappedCwusdcTotalSupply": Decimal(0), + "wrappedCwusdcCirculatingSupply": Decimal(0), + } + for row in proved: + total = Decimal(row["totalSupply"]) + circulating = Decimal(row["circulatingSupply"] or row["totalSupply"]) + totals["globalFamilyTotalSupply"] += total + totals["globalFamilyCirculatingSupply"] += circulating + if row["symbol"] == "cUSDC": + totals["baseCusdcTotalSupply"] += total + totals["baseCusdcCirculatingSupply"] += circulating + elif row["symbol"] == "cWUSDC": + totals["wrappedCwusdcTotalSupply"] += total + totals["wrappedCwusdcCirculatingSupply"] += circulating + + eth_mainnet = next((row for row in rows if row["isEthereumMainnetCwusdc"]), None) + return { + "schema": "global-cusdc-cwusdc-family-supply-proof/v1", + "generatedAt": dt.datetime.now(dt.UTC).isoformat().replace("+00:00", "Z"), + "source": { + "api": api_url, + "reportGeneratedAt": report.get("generatedAt"), + }, + "scope": { + "description": "Cross-chain cUSDC/cWUSDC family supply inventory across report API chains.", + "notForEtherscanEthereumTokenPage": True, + "etherscanEthereumOnlyToken": { + "chainId": 1, + "address": ETH_MAINNET_CWUSDC, + "note": "Use the Ethereum-only cWUSDC supply attestation for Etherscan Value submissions, not this global family total.", + "entry": eth_mainnet, + }, + }, + "summary": { + "familyEntryCount": len(rows), + "provedAggregateEntryCount": len(proved), + "proofRequiredEntryCount": len(unproved), + **{key: fmt(value) for key, value in totals.items()}, + }, + "entries": rows, + "proofRequiredEntries": unproved, + "caveats": [ + "This is a global cross-chain family inventory, not a circulating-supply claim for any single chain explorer.", + "Entries marked proof_required are excluded from aggregate totals.", + "Ethereum Etherscan Value for cWUSDC must use only the Ethereum Mainnet cWUSDC contract supply.", + "cUSDC source assets and cWUSDC wrapped assets may represent related economic rails; global totals should not be used as a market-cap input without a tracker-approved methodology that prevents double counting.", + ], + } + + +def write_md(payload: dict[str, Any], path: Path) -> None: + summary = payload["summary"] + lines = [ + "# Global cUSDC/cWUSDC Family Supply Proof", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Source API: `{payload['source']['api']}`", + f"- Source report generated: `{payload['source']['reportGeneratedAt']}`", + f"- Not for Ethereum Etherscan token page: `{payload['scope']['notForEtherscanEthereumTokenPage']}`", + "", + "## Aggregate Totals", + "", + "| Field | Value |", + "|---|---:|", + f"| Family entries | `{summary['familyEntryCount']}` |", + f"| Proved aggregate entries | `{summary['provedAggregateEntryCount']}` |", + f"| Proof-required entries | `{summary['proofRequiredEntryCount']}` |", + f"| Global family total supply, proved only | `{summary['globalFamilyTotalSupply']}` |", + f"| Global family circulating supply, proved only | `{summary['globalFamilyCirculatingSupply']}` |", + f"| Base cUSDC total supply, proved only | `{summary['baseCusdcTotalSupply']}` |", + f"| Wrapped cWUSDC total supply, proved only | `{summary['wrappedCwusdcTotalSupply']}` |", + "", + "## Ethereum Mainnet cWUSDC Reminder", + "", + "For Etherscan Value, use only:", + "", + f"`{payload['scope']['etherscanEthereumOnlyToken']['address']}`", + "", + "Do not use the global family total for the Ethereum token page.", + "", + "## Entries", + "", + "| Chain | Symbol | Type | Address | Total supply | Circulating supply | Proved |", + "|---:|---|---|---|---:|---:|---:|", + ] + for row in payload["entries"]: + lines.append( + f"| `{row['chainId']}` | `{row['symbol']}` | `{row['type']}` | `{row['address']}` | `{row['totalSupply']}` | `{row['circulatingSupply']}` | `{row['provedForAggregate']}` |" + ) + lines.extend(["", "## Caveats", ""]) + for caveat in payload["caveats"]: + lines.append(f"- {caveat}") + path.write_text("\n".join(lines) + "\n") + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--api-url", default=DEFAULT_API) + parser.add_argument("--json-out", type=Path, default=DEFAULT_JSON) + parser.add_argument("--md-out", type=Path, default=DEFAULT_MD) + args = parser.parse_args() + + payload = build(args.api_url) + 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) + print(f"Wrote {args.json_out.relative_to(ROOT)}") + print(f"Wrote {args.md_out.relative_to(ROOT)}") + print(f"globalFamilyTotalSupply={payload['summary']['globalFamilyTotalSupply']}") + print(f"proofRequiredEntryCount={payload['summary']['proofRequiredEntryCount']}") + 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 new file mode 100644 index 00000000..3d7c2cca --- /dev/null +++ b/scripts/verify/monitor-cwusdc-etherscan-value-propagation.py @@ -0,0 +1,361 @@ +#!/usr/bin/env python3 +"""Monitor whether cWUSDC USD value has propagated to Etherscan and upstream feeds.""" + +from __future__ import annotations + +import argparse +import datetime as dt +import json +import os +import re +import time +import urllib.error +import urllib.parse +import urllib.request +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +REPORT_JSON = ROOT / "reports" / "status" / "cwusdc-etherscan-value-propagation-latest.json" +REPORT_MD = ROOT / "reports" / "status" / "cwusdc-etherscan-value-propagation-latest.md" +CWUSDC = "0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a" +ETHERSCAN_API = "https://api.etherscan.io/v2/api" +ETHERSCAN_PAGE = f"https://etherscan.io/token/{CWUSDC}" +COINGECKO_PRICE = ( + "https://api.coingecko.com/api/v3/simple/token_price/ethereum?" + f"contract_addresses={CWUSDC}&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_last_updated_at=true" +) +DEXSCREENER_TOKEN_PAIRS = f"https://api.dexscreener.com/token-pairs/v1/ethereum/{CWUSDC}" +GECKOTERMINAL_POOLS = [ + "https://api.geckoterminal.com/api/v2/networks/eth/pools/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3", + "https://api.geckoterminal.com/api/v2/networks/eth/pools/0xc28706f899266b36bc43cc072b3a921bdf2c48d9", +] + + +def load_dotenv(path: Path) -> None: + if not path.exists(): + return + for line in path.read_text().splitlines(): + line = line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + key, value = line.split("=", 1) + key = key.strip() + value = value.strip().strip('"').strip("'") + if key and key not in os.environ: + os.environ[key] = value + + +def fetch_text(url: str, timeout: int = 30) -> tuple[int | None, str, str]: + req = urllib.request.Request( + url, + headers={ + "User-Agent": "Mozilla/5.0 DBIS-cwusdc-value-monitor/1.0", + "Accept": "application/json,text/html;q=0.9,*/*;q=0.8", + }, + ) + try: + with urllib.request.urlopen(req, timeout=timeout) as response: + return response.status, response.headers.get("content-type", ""), response.read().decode("utf-8", errors="replace") + except urllib.error.HTTPError as exc: + body = exc.read().decode("utf-8", errors="replace") if exc.fp else "" + return exc.code, exc.headers.get("content-type", "") if exc.headers else "", body + except Exception as exc: # noqa: BLE001 - monitor evidence should capture transient failures + return None, "", str(exc) + + +def fetch_json(url: str, timeout: int = 30) -> tuple[int | None, str, Any, str]: + status, content_type, text = fetch_text(url, timeout) + try: + return status, content_type, json.loads(text), "" + except json.JSONDecodeError as exc: + return status, content_type, None, str(exc) + + +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)}" + last: tuple[int | None, str, Any, str] = (None, "", None, "") + for attempt in range(5): + status, content_type, data, error = fetch_json(url) + last = (status, content_type, data, error) + if error: + return last + if not isinstance(data, dict): + return last + message = str(data.get("message", "")) + result = data.get("result") + if str(data.get("status")) != "0": + time.sleep(0.25) + return last + if "rate limit" in message.lower() or "rate limit" in str(result).lower(): + time.sleep(1.25 + attempt * 0.5) + continue + return last + return last + + +def extract_div_missing(html: str, element_id: str) -> bool: + pattern = rf'id="{re.escape(element_id)}".*?
\s*-\s*
' + return bool(re.search(pattern, html, flags=re.I | re.S)) + + +def parse_etherscan() -> dict[str, Any]: + status, content_type, html = fetch_text(ETHERSCAN_PAGE) + has_profile = "Wrapped cUSDC" in html and "cWUSDC" in html + total_supply_match = re.search(r'id="ContentPlaceHolder1_hdnTotalSupply" value="([^"]+)"', html) + holders_match = re.search(r"]*>\s*Holders\s*\s*]*>\s*
\s*([0-9,]+)", html, re.I) + market_missing = extract_div_missing(html, "ContentPlaceHolder1_tr_marketcap") + circ_market_missing = extract_div_missing(html, "ContentPlaceHolder1_tr_circulatingmarketcap") + value_ready = bool(status and 200 <= status < 300 and has_profile and not market_missing and not circ_market_missing) + return { + "id": "etherscan_token_page", + "url": ETHERSCAN_PAGE, + "status": status, + "contentType": content_type, + "profileDetected": has_profile, + "holdersText": holders_match.group(1) if holders_match else None, + "totalSupplyText": total_supply_match.group(1) if total_supply_match else None, + "onchainMarketCapMissing": market_missing, + "circulatingMarketCapMissing": circ_market_missing, + "valueReady": value_ready, + } + + +def parse_etherscan_tokeninfo(api_key: str) -> dict[str, Any]: + if not api_key: + return { + "id": "etherscan_tokeninfo_api", + "url": ETHERSCAN_API, + "status": None, + "contentType": "", + "parseError": "", + "skipped": True, + "skipReason": "ETHERSCAN_API_KEY is not set.", + "metadataReady": False, + "priceReady": False, + } + + status, content_type, data, error = fetch_etherscan_api( + {"module": "token", "action": "tokeninfo", "contractaddress": CWUSDC}, + api_key, + ) + result = data.get("result") if isinstance(data, dict) else None + entry = result[0] if isinstance(result, list) and result and isinstance(result[0], dict) else None + token_price_raw = entry.get("tokenPriceUSD") if isinstance(entry, dict) else None + try: + token_price = float(token_price_raw) if token_price_raw not in (None, "") else 0.0 + except (TypeError, ValueError): + token_price = 0.0 + metadata_ready = bool( + isinstance(entry, dict) + and entry.get("contractAddress", "").lower() == CWUSDC + and entry.get("symbol") == "cWUSDC" + and entry.get("tokenName") + ) + profile_enriched = bool( + isinstance(entry, dict) + and (entry.get("image") or entry.get("website") or entry.get("description") or entry.get("twitter")) + ) + return { + "id": "etherscan_tokeninfo_api", + "url": ETHERSCAN_API, + "status": status, + "contentType": content_type, + "parseError": error, + "skipped": False, + "apiStatus": data.get("status") if isinstance(data, dict) else None, + "apiMessage": data.get("message") if isinstance(data, dict) else None, + "apiResultPreview": result, + "metadataReady": metadata_ready, + "profileEnriched": profile_enriched, + "priceReady": token_price > 0, + "tokenPriceUSD": token_price_raw, + "tokenName": entry.get("tokenName") if isinstance(entry, dict) else None, + "symbol": entry.get("symbol") if isinstance(entry, dict) else None, + "divisor": entry.get("divisor") if isinstance(entry, dict) else None, + "tokenType": entry.get("tokenType") if isinstance(entry, dict) else None, + "totalSupply": entry.get("totalSupply") if isinstance(entry, dict) else None, + "blueCheckmark": entry.get("blueCheckmark") if isinstance(entry, dict) else None, + "image": entry.get("image") if isinstance(entry, dict) else None, + "website": entry.get("website") if isinstance(entry, dict) else None, + "descriptionPresent": bool(entry.get("description")) if isinstance(entry, dict) else False, + } + + +def parse_coingecko() -> dict[str, Any]: + status, content_type, data, error = fetch_json(COINGECKO_PRICE) + entry = None + if isinstance(data, dict): + entry = data.get(CWUSDC) + usd = entry.get("usd") if isinstance(entry, dict) else None + return { + "id": "coingecko_token_price", + "url": COINGECKO_PRICE, + "status": status, + "contentType": content_type, + "parseError": error, + "listedByContract": isinstance(entry, dict), + "usd": usd, + "marketCapUsd": entry.get("usd_market_cap") if isinstance(entry, dict) else None, + "volume24hUsd": entry.get("usd_24h_vol") if isinstance(entry, dict) else None, + "lastUpdatedAt": entry.get("last_updated_at") if isinstance(entry, dict) else None, + "priceReady": isinstance(usd, (int, float)) and usd > 0, + "jsonPreview": data, + } + + +def parse_dexscreener() -> dict[str, Any]: + status, content_type, data, error = fetch_json(DEXSCREENER_TOKEN_PAIRS) + pair_count = len(data) if isinstance(data, list) else 0 + return { + "id": "dexscreener_token_pairs", + "url": DEXSCREENER_TOKEN_PAIRS, + "status": status, + "contentType": content_type, + "parseError": error, + "pairCount": pair_count, + "indexed": pair_count > 0, + "jsonPreview": data[:3] if isinstance(data, list) else data, + } + + +def parse_geckoterminal() -> list[dict[str, Any]]: + checks: list[dict[str, Any]] = [] + for url in GECKOTERMINAL_POOLS: + status, content_type, data, error = fetch_json(url) + attrs = ((data or {}).get("data") or {}).get("attributes") if isinstance(data, dict) else None + checks.append( + { + "id": "geckoterminal_pool", + "url": url, + "status": status, + "contentType": content_type, + "parseError": error, + "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), + } + ) + return checks + + +def build() -> 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() + blockers: list[str] = [] + if not etherscan["profileDetected"]: + blockers.append("Etherscan token profile text was not detected.") + if etherscan["onchainMarketCapMissing"]: + blockers.append("Etherscan Onchain Market Cap is still blank.") + if etherscan["circulatingMarketCapMissing"]: + blockers.append("Etherscan Circulating Supply Market Cap is still blank.") + tokeninfo_preview = str(etherscan_tokeninfo.get("apiResultPreview", "")) + if "API Pro endpoint" in tokeninfo_preview: + blockers.append("Etherscan tokeninfo API is an API Pro endpoint for the current key; tokeninfo propagation cannot be monitored with the current plan.") + elif not etherscan_tokeninfo["metadataReady"]: + blockers.append("Etherscan tokeninfo API does not return accepted token metadata for cWUSDC.") + if "API Pro endpoint" not in tokeninfo_preview and not etherscan_tokeninfo["priceReady"]: + blockers.append("Etherscan tokeninfo API does not return a positive USD token price.") + if not coingecko["priceReady"]: + blockers.append("CoinGecko contract price API does not return a positive USD price.") + if not dexscreener["indexed"]: + blockers.append("DexScreener token-pairs API still does not index cWUSDC pairs.") + + return { + "schema": "cwusdc-etherscan-value-propagation/v1", + "generatedAt": dt.datetime.now(dt.UTC).isoformat().replace("+00:00", "Z"), + "token": { + "chainId": 1, + "address": CWUSDC, + "caip19": f"eip155:1/erc20:{CWUSDC}", + "symbol": "cWUSDC", + }, + "summary": { + "etherscanValueReady": etherscan["valueReady"], + "etherscanTokenInfoMetadataReady": etherscan_tokeninfo["metadataReady"], + "etherscanTokenInfoPriceReady": etherscan_tokeninfo["priceReady"], + "coingeckoPriceReady": coingecko["priceReady"], + "readyForEtherscanValuePropagation": etherscan["valueReady"] or coingecko["priceReady"], + "blockers": blockers, + }, + "checks": { + "etherscan": etherscan, + "etherscanTokenInfo": etherscan_tokeninfo, + "coingecko": coingecko, + "dexscreener": dexscreener, + "geckoterminal": gecko, + }, + } + + +def write_md(payload: dict[str, Any], path: Path) -> None: + summary = payload["summary"] + checks = payload["checks"] + lines = [ + "# cWUSDC Etherscan Value Propagation Monitor", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Token: `{payload['token']['address']}`", + f"- CAIP-19: `{payload['token']['caip19']}`", + f"- Etherscan value ready: `{summary['etherscanValueReady']}`", + f"- Etherscan tokeninfo metadata ready: `{summary['etherscanTokenInfoMetadataReady']}`", + f"- Etherscan tokeninfo price ready: `{summary['etherscanTokenInfoPriceReady']}`", + f"- CoinGecko price ready: `{summary['coingeckoPriceReady']}`", + "", + "## Blockers", + "", + ] + if summary["blockers"]: + lines.extend(f"- {item}" for item in summary["blockers"]) + else: + lines.append("- None detected by this monitor.") + lines.extend( + [ + "", + "## Checks", + "", + "| Surface | Status | Ready / indexed | Key fields |", + "|---|---:|---:|---|", + f"| Etherscan | `{checks['etherscan']['status']}` | `{checks['etherscan']['valueReady']}` | marketCapMissing={checks['etherscan']['onchainMarketCapMissing']}; circulatingMarketCapMissing={checks['etherscan']['circulatingMarketCapMissing']}; holders={checks['etherscan']['holdersText']} |", + f"| Etherscan tokeninfo API | `{checks['etherscanTokenInfo']['status']}` | `{checks['etherscanTokenInfo']['metadataReady']}` / price `{checks['etherscanTokenInfo']['priceReady']}` | symbol={checks['etherscanTokenInfo']['symbol']}; price={checks['etherscanTokenInfo']['tokenPriceUSD']}; image={checks['etherscanTokenInfo']['image']}; website={checks['etherscanTokenInfo']['website']} |", + f"| CoinGecko contract price | `{checks['coingecko']['status']}` | `{checks['coingecko']['priceReady']}` | usd={checks['coingecko']['usd']}; marketCap={checks['coingecko']['marketCapUsd']}; lastUpdated={checks['coingecko']['lastUpdatedAt']} |", + f"| DexScreener token pairs | `{checks['dexscreener']['status']}` | `{checks['dexscreener']['indexed']}` | pairCount={checks['dexscreener']['pairCount']} |", + ] + ) + for item in checks["geckoterminal"]: + lines.append( + f"| GeckoTerminal pool | `{item['status']}` | `{item['indexed']}` | reserveUsd={item['reserveUsd']}; volume24hUsd={item['volume24hUsd']}; url={item['url']} |" + ) + path.write_text("\n".join(lines) + "\n") + + +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("--strict", action="store_true") + args = parser.parse_args() + + payload = build() + 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) + print(f"Wrote {args.json_out.relative_to(ROOT)}") + print(f"Wrote {args.md_out.relative_to(ROOT)}") + print(f"etherscanValueReady={payload['summary']['etherscanValueReady']}") + if payload["summary"]["blockers"]: + print("Blockers: " + "; ".join(payload["summary"]["blockers"])) + if args.strict and not payload["summary"]["etherscanValueReady"]: + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/plan-engine-x-automated-liquidity-advisor.py b/scripts/verify/plan-engine-x-automated-liquidity-advisor.py new file mode 100755 index 00000000..af2f7ff9 --- /dev/null +++ b/scripts/verify/plan-engine-x-automated-liquidity-advisor.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import json +import os +import subprocess +from datetime import datetime, timezone +from decimal import Decimal, ROUND_FLOOR, getcontext +from pathlib import Path +from typing import Any + +getcontext().prec = 80 + +ROOT = Path(__file__).resolve().parents[2] +POLICY_PATH = ROOT / "config/engine-x/automation-policy.json" +OUT_JSON = ROOT / "reports/status/engine-x-automated-liquidity-advisor-latest.json" +OUT_MD = ROOT / "reports/status/engine-x-automated-liquidity-advisor-latest.md" +XAUT_MAINNET = "0x68749665FF8D2d112Fa859AA293F07A622782F38" + + +def read_json(path: Path) -> dict[str, Any] | None: + try: + return json.loads(path.read_text()) + except FileNotFoundError: + return None + except json.JSONDecodeError as exc: + return {"_error": f"invalid json: {exc}"} + + +def env_decimal(name: str, default: str | None = None) -> Decimal | None: + value = os.environ.get(name, default) + if value is None or value == "": + return None + return Decimal(value) + + +def env_int(name: str, default: str | None = None) -> int | None: + value = os.environ.get(name, default) + if value is None or value == "": + return None + return int(Decimal(value)) + + +def decstr(value: Decimal, places: int = 6) -> str: + q = Decimal(10) ** -places + return f"{value.quantize(q)}" + + +def raw6(units: Decimal) -> int: + return int((units * Decimal(10**6)).to_integral_value(rounding=ROUND_FLOOR)) + + +def units_from_raw(raw: int, decimals: int) -> Decimal: + return Decimal(raw) / Decimal(10**decimals) + + +def get_path(payload: dict[str, Any] | None, *parts: str, default: Any = None) -> Any: + cur: Any = payload + for part in parts: + if not isinstance(cur, dict) or part not in cur: + return default + cur = cur[part] + return cur + + +def cast_call_raw(token: str, owner: str, rpc: str) -> int | None: + try: + result = subprocess.run( + ["cast", "call", token, "balanceOf(address)(uint256)", owner, "--rpc-url", rpc], + check=True, + capture_output=True, + text=True, + timeout=30, + ) + except (OSError, subprocess.CalledProcessError, subprocess.TimeoutExpired): + return None + first = result.stdout.strip().split()[0] if result.stdout.strip() else "" + return int(first) if first.isdigit() else None + + +def cast_decimals(token: str, rpc: str, fallback: int) -> int: + try: + result = subprocess.run( + ["cast", "call", token, "decimals()(uint8)", "--rpc-url", rpc], + check=True, + capture_output=True, + text=True, + timeout=30, + ) + except (OSError, subprocess.CalledProcessError, subprocess.TimeoutExpired): + return fallback + first = result.stdout.strip().split()[0] if result.stdout.strip() else "" + return int(first) if first.isdigit() else fallback + + +def main() -> int: + policy = read_json(POLICY_PATH) or {} + reports = { + "supportHealth": read_json(ROOT / "reports/status/mainnet-cwusdc-usdc-support-health-latest.json"), + "publicIndexedReadiness": read_json(ROOT / "reports/status/engine-x-public-indexed-readiness-latest.json"), + "mevDefenseReadiness": read_json(ROOT / "reports/status/engine-x-mev-defense-readiness-latest.json"), + "wethSupport": read_json(ROOT / "reports/status/mainnet-cwusdc-weth-liquidity-surfaces-latest.json"), + } + + requested_symbol = os.environ.get("ENGINE_X_REQUESTED_CW_SYMBOL", "cWUSDC") + requested_units = env_decimal("ENGINE_X_REQUESTED_OUTPUT_UNITS") + requested_raw = env_int("ENGINE_X_REQUESTED_OUTPUT_RAW") + if requested_raw is None and requested_units is not None: + requested_raw = raw6(requested_units) + if requested_units is None and requested_raw is not None: + requested_units = Decimal(requested_raw) / Decimal(10**6) + if requested_raw is None: + requested_raw = 10_000 + requested_units = Decimal("0.01") + + xaut_token = os.environ.get("XAUT_MAINNET", XAUT_MAINNET) + xaut_decimals = 6 + if os.environ.get("ETHEREUM_MAINNET_RPC"): + xaut_decimals = cast_decimals(xaut_token, os.environ["ETHEREUM_MAINNET_RPC"], xaut_decimals) + + xaut_available_raw = env_int("ENGINE_X_XAUT_AVAILABLE_RAW") + if xaut_available_raw is None: + xaut_units_env = env_decimal("ENGINE_X_XAUT_AVAILABLE_UNITS") + xaut_available_raw = int((xaut_units_env * Decimal(10**xaut_decimals)).to_integral_value(rounding=ROUND_FLOOR)) if xaut_units_env is not None else 0 + xaut_available_units = units_from_raw(xaut_available_raw, xaut_decimals) + + xaut_usd_price6 = Decimal(env_int("ENGINE_X_XAUT_USD_PRICE6", str(get_path(policy, "inputs", "collateral", "defaultUsdPrice6", default="3226640000")))) + xaut_usd_price = xaut_usd_price6 / Decimal(10**6) + ltv_bps = Decimal(env_int("ENGINE_X_BORROW_LTV_BPS", str(get_path(policy, "inputs", "risk", "defaultLtvBps", default=7500)))) + hf_bps = Decimal(env_int("ENGINE_X_BORROW_MIN_HEALTH_FACTOR_BPS", str(get_path(policy, "inputs", "risk", "defaultMinHealthFactorBps", default=11000)))) + max_round_trip_loss_bps = Decimal(env_int("ENGINE_X_MAX_ROUND_TRIP_LOSS_BPS", str(get_path(policy, "inputs", "risk", "defaultMaxRoundTripLossBps", default=100)))) + min_gas_reserve_wei = env_int("ENGINE_X_MIN_GAS_RESERVE_WEI", str(get_path(policy, "inputs", "risk", "defaultMinGasReserveWei", default="5000000000000000"))) + + deployer = get_path(reports["publicIndexedReadiness"], "deployer", default={}) or {} + deployer_address = str(deployer.get("address") or os.environ.get("DEPLOYER_ADDRESS") or "") + wallet_usdc = Decimal(str(deployer.get("usdc", "0"))) + wallet_cwusdc = Decimal(str(deployer.get("cwusdc", "0"))) + wallet_eth = Decimal(str(get_path(reports["wethSupport"], "balances", "eth", default="0"))) + + xaut_balance_source = "env" + if xaut_available_raw == 0 and deployer_address and os.environ.get("ETHEREUM_MAINNET_RPC"): + live_xaut = cast_call_raw( + xaut_token, + deployer_address, + os.environ["ETHEREUM_MAINNET_RPC"], + ) + if live_xaut is not None: + xaut_available_raw = live_xaut + xaut_available_units = units_from_raw(xaut_available_raw, xaut_decimals) + xaut_balance_source = "live_wallet_balance" + + collateral_usd = xaut_available_units * xaut_usd_price + ltv_borrow_capacity = collateral_usd * ltv_bps / Decimal(10_000) + # Conservative single-asset health-factor capacity: debt <= collateral_ltv_value / target_hf. + hf_borrow_capacity = ltv_borrow_capacity * Decimal(10_000) / hf_bps if hf_bps > 0 else Decimal(0) + borrow_capacity = min(ltv_borrow_capacity, hf_borrow_capacity) + + mev_ready = bool(get_path(reports["mevDefenseReadiness"], "ready", default=False)) + public_ready = bool(get_path(reports["publicIndexedReadiness"], "summary", "readyForPublicIndexedProof", default=False)) + support_preferred = get_path(reports["supportHealth"], "quoteDefenseDecision", "preferredSurface", default={}) or {} + support_blockers = get_path(reports["supportHealth"], "quoteDefenseDecision", "blockers", default=[]) or [] + v3_tick = get_path(reports["supportHealth"], "quoteDefenseSurfaceHealth", "mainnet-cwusdc-usdc-univ3-100", "tick") + v3_range_status = get_path(reports["supportHealth"], "quoteDefenseSurfaceHealth", "mainnet-cwusdc-usdc-univ3-100", "rangeStatus") + + requested_usd = requested_units or Decimal(0) + available_loop_usd = min(wallet_usdc + borrow_capacity, wallet_cwusdc, requested_usd) + can_satisfy_requested = available_loop_usd >= requested_usd and requested_usd > 0 + + blockers: list[str] = [] + warnings: list[str] = [] + if requested_symbol not in get_path(policy, "inputs", "requestedOutput", "supportedInitialSymbols", default=["cWUSDC"]): + blockers.append(f"requested output symbol is not yet supported by this advisor: {requested_symbol}") + if not public_ready: + blockers.append("public indexed readiness is not passing") + if not mev_ready: + blockers.append("MEV/protected broadcast readiness is not passing for live automation") + if support_preferred.get("action") not in {"ready_for_tiny_canary", "use_for_tiny_public_canary"}: + warnings.append(f"preferred quote-defense surface action is {support_preferred.get('action', 'unknown')}; rebalance or operator review may be needed") + if support_blockers: + blockers.extend(f"support health blocker: {item}" for item in support_blockers) + if wallet_eth * Decimal(10**18) < Decimal(min_gas_reserve_wei or 0): + blockers.append("wallet ETH is below configured gas reserve") + if not can_satisfy_requested: + blockers.append("requested output exceeds current wallet plus XAUt-backed conservative USDC capacity") + + live_execution_ready = not blockers and can_satisfy_requested + endpoint_publication_ready = live_execution_ready and public_ready + + phases = [] + for phase in get_path(policy, "automationPhases", default=[]): + status = "ready" + if phase["id"] == "phase_1_canary" and not live_execution_ready: + status = "blocked" + elif phase["id"] == "phase_2_liquidity_defense" and (not live_execution_ready or warnings): + status = "operator_review" + elif phase["id"] == "phase_3_endpoint_publication" and not endpoint_publication_ready: + status = "blocked" + elif phase["id"] == "phase_4_multi_asset_forex_crypto": + status = "design_required" + phases.append({**phase, "status": status}) + + payload = { + "schema": "engine-x-automated-liquidity-advisor/v1", + "generatedAt": datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z"), + "policyPath": str(POLICY_PATH.relative_to(ROOT)), + "request": { + "outputSymbol": requested_symbol, + "outputRaw": str(requested_raw), + "outputUnits": decstr(requested_units or Decimal(0), 6), + }, + "inputs": { + "wallet": { + "usdc": decstr(wallet_usdc, 6), + "cwusdc": decstr(wallet_cwusdc, 6), + "eth": str(wallet_eth), + }, + "xautCollateral": { + "availableRaw": str(xaut_available_raw), + "availableUnits": decstr(xaut_available_units, min(xaut_decimals, 8)), + "decimals": xaut_decimals, + "token": xaut_token, + "source": xaut_balance_source, + "usdPrice": decstr(xaut_usd_price, 6), + "collateralUsd": decstr(collateral_usd, 6), + }, + "risk": { + "ltvBps": str(ltv_bps), + "minHealthFactorBps": str(hf_bps), + "maxRoundTripLossBps": str(max_round_trip_loss_bps), + "minGasReserveWei": str(min_gas_reserve_wei), + }, + }, + "calculator": { + "ltvBorrowCapacityUsdc": decstr(ltv_borrow_capacity, 6), + "healthFactorBorrowCapacityUsdc": decstr(hf_borrow_capacity, 6), + "conservativeBorrowCapacityUsdc": decstr(borrow_capacity, 6), + "walletPlusBorrowUsdcCapacity": decstr(wallet_usdc + borrow_capacity, 6), + "maxCurrentRequestFillUnits": decstr(available_loop_usd, 6), + "canSatisfyRequestedOutput": can_satisfy_requested, + "debtNeutralLoopInvariant": "borrow USDC, swap cWUSDC->USDC to repay debt, then swap borrowed USDC->cWUSDC; ending USDC debt must be zero before XAUt withdrawal", + }, + "feedStatus": { + "publicIndexedReady": public_ready, + "mevDefenseReady": mev_ready, + "preferredQuoteDefenseSurface": support_preferred, + "uniV3Tick": v3_tick, + "uniV3RangeStatus": v3_range_status, + }, + "recommendedPhases": phases, + "endpointReporting": { + "ready": endpoint_publication_ready, + "targets": get_path(policy, "feeds", "externalPublicationTargets", default=[]), + "requiredEvidence": get_path(policy, "decisionGates", "publication", default=[]), + }, + "blockers": blockers, + "warnings": warnings, + "operatorCommands": { + "regenerateFeeds": get_path(policy, "feeds", "onChainReadinessCommands", default=[]), + "dryRunAdvisor": "pnpm engine-x:automation-advisor", + "liveExecution": "blocked unless this report has no blockers and operator-approved scripts are run with protected RPC", + }, + } + + OUT_JSON.parent.mkdir(parents=True, exist_ok=True) + OUT_JSON.write_text(json.dumps(payload, indent=2) + "\n") + + lines = [ + "# Engine X Automated Liquidity Advisor", + "", + f"- generatedAt: `{payload['generatedAt']}`", + f"- request: `{payload['request']['outputUnits']} {requested_symbol}`", + f"- max current request fill: `{payload['calculator']['maxCurrentRequestFillUnits']} {requested_symbol}`", + f"- conservative XAUt-backed USDC capacity: `{payload['calculator']['conservativeBorrowCapacityUsdc']} USDC`", + f"- public indexed ready: `{str(public_ready).lower()}`", + f"- MEV defense ready: `{str(mev_ready).lower()}`", + f"- endpoint publication ready: `{str(endpoint_publication_ready).lower()}`", + "", + "## Calculator", + "", + f"- wallet USDC: `{payload['inputs']['wallet']['usdc']}`", + f"- wallet cWUSDC: `{payload['inputs']['wallet']['cwusdc']}`", + f"- XAUt available: `{payload['inputs']['xautCollateral']['availableUnits']}`", + f"- XAUt collateral USD value: `{payload['inputs']['xautCollateral']['collateralUsd']}`", + f"- wallet plus conservative borrow capacity: `{payload['calculator']['walletPlusBorrowUsdcCapacity']} USDC`", + "", + "## Phases", + ] + for phase in phases: + lines.append(f"- `{phase['id']}`: `{phase['status']}` - {phase['name']}") + lines.extend(["", "## Blockers"]) + lines.extend([f"- {item}" for item in blockers] or ["- none"]) + lines.extend(["", "## Warnings"]) + lines.extend([f"- {item}" for item in warnings] or ["- none"]) + lines.extend(["", "## Reporting Targets"]) + lines.extend([f"- {item}" for item in payload["endpointReporting"]["targets"]]) + OUT_MD.write_text("\n".join(lines) + "\n") + + print(json.dumps({"ready": live_execution_ready, "blockers": blockers, "warnings": warnings}, indent=2)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/verify/plan-engine-x-automated-liquidity-advisor.sh b/scripts/verify/plan-engine-x-automated-liquidity-advisor.sh new file mode 100755 index 00000000..5255341b --- /dev/null +++ b/scripts/verify/plan-engine-x-automated-liquidity-advisor.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" + +python3 "${PROJECT_ROOT}/scripts/verify/plan-engine-x-automated-liquidity-advisor.py" diff --git a/scripts/verify/plan-token-aggregation-liquidity-gap-funding.mjs b/scripts/verify/plan-token-aggregation-liquidity-gap-funding.mjs new file mode 100644 index 00000000..9e570eb8 --- /dev/null +++ b/scripts/verify/plan-token-aggregation-liquidity-gap-funding.mjs @@ -0,0 +1,932 @@ +#!/usr/bin/env node +/** + * Read-only funding planner for token-aggregation adoption-readiness liquidity gaps. + * + * It does not broadcast transactions. It checks the deployer wallet's native and ERC-20 balances + * for every current liquidityMissingDetails row and classifies each row as: + * - fundable_token_balance_present + * - gas_gated + * - token_balance_gated + * - pool_binding_gated + */ + +import { createHash } from "node:crypto"; +import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { resolve } from "node:path"; + +const repoRoot = resolve(new URL("../..", import.meta.url).pathname); +const readinessPath = resolve(repoRoot, "reports/status/token-aggregation-adoption-readiness-live-20260509.json"); +const nonEvmHealthPath = resolve(repoRoot, "reports/status/non-evm-network-health-latest.json"); +const nonEvmLaneStatusPath = resolve(repoRoot, "reports/status/non-evm-lane-status-latest.json"); +const jsonOut = resolve(repoRoot, "reports/status/token-aggregation-liquidity-gap-funding-plan-latest.json"); +const mdOut = resolve(repoRoot, "reports/status/token-aggregation-liquidity-gap-funding-plan-latest.md"); +const deployer = (process.env.DEPLOYER_ADDRESS || process.env.DEPLOYER || "0x4A666F96fC8764181194447A7dFdb7d471b301C8").trim(); +const envFiles = [resolve(repoRoot, ".env"), resolve(repoRoot, "smom-dbis-138/.env")]; +const stabilityCycles = Number(process.env.TOKEN_AGGREGATION_STABILITY_CYCLES || "30"); +const gasSafetyBps = BigInt(process.env.TOKEN_AGGREGATION_GAS_SAFETY_BPS || "15000"); +const coffeeMoneyUsdAvailable = Number(process.env.DEPLOYER_COFFEE_MONEY_USD || "48"); +const coffeeMoneyLiquidityUsdPerRow = Number(process.env.COFFEE_MONEY_LIQUIDITY_USD_PER_ROW || "1"); +const bridgeCapableChains = new Set([1, 10, 25, 56, 100, 137, 42161, 42220, 43114, 8453]); +const protocolinkCandidateChains = new Set([1, 10, 56, 100, 137, 42161, 42220, 43114, 8453]); +const officialQuoteAssets = new Set([ + "1:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "1:0xdac17f958d2ee523a2206206994597c13d831ec7", + "10:0x0b2c639c533813f4aa9d7837caf62653d097ff85", + "10:0x94b008aa00579c1307b0ef2c499ad98a8ce58e58", + "25:0xc21223249ca28397b4b6541dffaecc539bff0c59", + "25:0x66e428c3f67a68878562e79a0234c1f83c208770", + "56:0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d", + "56:0x55d398326f99059ff775485246999027b3197955", + "100:0xddafbb505ad214d7b80b1f830fccc89b60fb7a83", + "100:0x4ecaba5870353805a9f068101a40e0f32ed605c6", + "137:0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", + "137:0xc2132d05d31c914a87c6611c10748aeb04b58e8f", + "8453:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", + "42161:0xaf88d065e77c8cc2239327c5edb3a432268e5831", + "42161:0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "42220:0x765de816845861e75a25fca122bb6898b8b1282a", + "42220:0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e", + "43114:0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e", + "43114:0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7", +]); +const ethereumSourceTokens = [ + { symbol: "USDC", address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", decimals: 6, role: "official_quote_capital" }, + { symbol: "USDT", address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", decimals: 6, role: "official_quote_capital" }, + { symbol: "LINK", address: "0x514910771AF9Ca656af840dff83E8264EcF986CA", decimals: 18, role: "route_quote_before_use" }, + { symbol: "WETH", address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", decimals: 18, role: "wrapped_native_gas_source" }, + { symbol: "XAUt", address: "0x68749665FF8D2d112Fa859AA293F07A622782F38", decimals: 6, role: "route_quote_before_use" }, + { symbol: "cWUSDC", address: "0x66a3c2fa3e467aa586e90912f977e648589cabaf", decimals: 6, role: "evidence_or_pair_side_not_native_gas" }, +]; + +const rpcByChain = { + 1: process.env.ETHEREUM_MAINNET_RPC || process.env.RPC_URL_1 || "https://ethereum.publicnode.com", + 10: process.env.OPTIMISM_MAINNET_RPC || process.env.OPTIMISM_RPC_URL || process.env.RPC_URL_10 || "https://optimism.publicnode.com", + 25: process.env.CRONOS_RPC_URL || process.env.CRONOS_MAINNET_RPC || process.env.RPC_URL_25 || "https://cronos-evm-rpc.publicnode.com", + 56: process.env.BSC_RPC_URL || process.env.BSC_MAINNET_RPC || process.env.RPC_URL_56 || "https://bsc-rpc.publicnode.com", + 100: process.env.GNOSIS_MAINNET_RPC || process.env.GNOSIS_RPC_URL || process.env.GNOSIS_RPC || process.env.RPC_URL_100 || "https://gnosis.publicnode.com", + 137: process.env.POLYGON_MAINNET_RPC || process.env.POLYGON_RPC_URL || process.env.RPC_URL_137 || "https://polygon-bor-rpc.publicnode.com", + 138: process.env.RPC_URL_138_PUBLIC || process.env.RPC_URL_138 || process.env.CHAIN138_RPC_URL || "http://192.168.11.221:8545", + 1111: process.env.WEMIX_MAINNET_RPC || process.env.WEMIX_RPC || process.env.RPC_URL_1111 || "https://api.wemix.com", + 8453: process.env.BASE_MAINNET_RPC || process.env.BASE_RPC_URL || process.env.RPC_URL_8453 || "https://base-rpc.publicnode.com", + 42161: process.env.ARBITRUM_MAINNET_RPC || process.env.ARBITRUM_RPC_URL || process.env.RPC_URL_42161 || "https://arbitrum-one-rpc.publicnode.com", + 42220: process.env.CELO_MAINNET_RPC || process.env.CELO_RPC_URL || process.env.CELO_RPC || process.env.RPC_URL_42220 || "https://celo-rpc.publicnode.com", + 43114: process.env.AVALANCHE_RPC_URL || process.env.AVALANCHE_MAINNET_RPC || process.env.RPC_URL_43114 || "https://avalanche-c-chain-rpc.publicnode.com", + 651940: process.env.CHAIN_651940_RPC_URL || process.env.ALL_MAINNET_RPC || "https://mainnet-rpc.alltra.global", +}; +const nativeSymbolsByChain = { + 1: "ETH", + 10: "ETH", + 25: "CRO", + 56: "BNB", + 100: "xDAI", + 137: "POL", + 138: "DBIS", + 1111: "WEMIX", + 8453: "ETH", + 42161: "ETH", + 42220: "CELO", + 43114: "AVAX", + 651940: "ALL", +}; +const gasPriceCache = new Map(); + +function padAddress(address) { + return String(address).replace(/^0x/i, "").padStart(64, "0"); +} + +async function rpcCall(rpcUrl, method, params) { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 12_000); + try { + const response = await fetch(rpcUrl, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ jsonrpc: "2.0", method, params, id: 1 }), + signal: controller.signal, + }); + const json = await response.json(); + if (json.error) return { ok: false, error: json.error.message || JSON.stringify(json.error) }; + return { ok: true, result: json.result }; + } catch (error) { + return { ok: false, error: error.message }; + } finally { + clearTimeout(timeout); + } +} + +function bigintFromHex(hex) { + if (!hex || hex === "0x") return 0n; + return BigInt(hex); +} + +function decimalUnits(raw, decimals) { + const scale = 10n ** BigInt(decimals); + const whole = raw / scale; + const frac = raw % scale; + const fracText = frac.toString().padStart(decimals, "0").replace(/0+$/, ""); + return fracText ? `${whole}.${fracText}` : whole.toString(); +} + +async function erc20Balance(rpcUrl, token, holder) { + const [balance, decimals] = await Promise.all([ + rpcCall(rpcUrl, "eth_call", [{ to: token, data: `0x70a08231${padAddress(holder)}` }, "latest"]), + rpcCall(rpcUrl, "eth_call", [{ to: token, data: "0x313ce567" }, "latest"]), + ]); + const raw = balance.ok ? bigintFromHex(balance.result) : 0n; + const dec = decimals.ok ? Number(bigintFromHex(decimals.result)) : 18; + return { + ok: balance.ok, + raw: raw.toString(), + units: decimalUnits(raw, Number.isFinite(dec) ? dec : 18), + decimals: Number.isFinite(dec) ? dec : 18, + error: balance.ok ? undefined : balance.error, + }; +} + +async function nativeBalance(rpcUrl, holder) { + const balance = await rpcCall(rpcUrl, "eth_getBalance", [holder, "latest"]); + const raw = balance.ok ? bigintFromHex(balance.result) : 0n; + return { + ok: balance.ok, + raw: raw.toString(), + units: decimalUnits(raw, 18), + error: balance.ok ? undefined : balance.error, + }; +} + +async function buildEthereumSourceInventory() { + const rpcUrl = rpcByChain[1]; + const native = await nativeBalance(rpcUrl, deployer); + const tokens = await Promise.all(ethereumSourceTokens.map(async (token) => { + const balance = await erc20Balance(rpcUrl, token.address, deployer); + return { + ...token, + balance: balance.units, + balanceRaw: balance.raw, + balanceStatus: BigInt(balance.raw || "0") > 0n ? "present" : "zero", + error: balance.error, + }; + })); + return { + chainId: 1, + network: "Ethereum Mainnet", + deployer, + native: { + symbol: "ETH", + balance: native.units, + balanceRaw: native.raw, + role: "mainnet_transaction_gas_do_not_fully_drain", + }, + tokens, + interpretation: [ + "Ethereum portfolio value is not the same as immediately spendable cross-chain gas.", + "Keep enough ETH for Mainnet approvals, swaps, and liquidity/stability events.", + "Use USDC/USDT first as official quote capital; use LINK/XAUt only after a live route quote proves acceptable output.", + "Treat cWUSDC as pair-side/evidence inventory unless a real public route converts it into the exact official token needed.", + ], + }; +} + +async function gasPrice(rpcUrl, chainId) { + if (!rpcUrl) return { ok: false, raw: "0", units: "0", error: "missing_rpc" }; + if (gasPriceCache.has(chainId)) return gasPriceCache.get(chainId); + const result = await rpcCall(rpcUrl, "eth_gasPrice", []); + const raw = result.ok ? bigintFromHex(result.result) : 0n; + const payload = { + ok: result.ok, + raw: raw.toString(), + gwei: decimalUnits(raw, 9), + error: result.ok ? undefined : result.error, + }; + gasPriceCache.set(chainId, payload); + return payload; +} + +function table(headers, rows) { + return [ + `| ${headers.join(" | ")} |`, + `| ${headers.map(() => "---").join(" | ")} |`, + ...rows.map((row) => `| ${row.map((cell) => String(cell ?? "").replace(/\|/g, "\\|")).join(" | ")} |`), + ].join("\n"); +} + +function readJsonIfExists(path, fallback = null) { + try { + return JSON.parse(readFileSync(path, "utf8")); + } catch { + return fallback; + } +} + +function readEnvValue(...keys) { + for (const key of keys) { + if (process.env[key]) return process.env[key].trim(); + } + for (const file of envFiles) { + let text = ""; + try { + text = readFileSync(file, "utf8"); + } catch { + continue; + } + for (const key of keys) { + const match = text.match(new RegExp(`^${key}=([^\\n#]*)`, "m")); + if (match?.[1]) return match[1].trim().replace(/^['"]|['"]$/g, ""); + } + } + return ""; +} + +const base58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + +function base58Encode(bytes) { + let value = BigInt(`0x${Buffer.from(bytes).toString("hex") || "0"}`); + let output = ""; + while (value > 0n) { + const remainder = Number(value % 58n); + output = `${base58Alphabet[remainder]}${output}`; + value /= 58n; + } + let leadingZeroes = 0; + for (const byte of bytes) { + if (byte !== 0) break; + leadingZeroes += 1; + } + return `${"1".repeat(leadingZeroes)}${output || ""}`; +} + +function base58Decode(text) { + let value = 0n; + for (const char of text) { + const index = base58Alphabet.indexOf(char); + if (index < 0) throw new Error(`invalid_base58_char_${char}`); + value = value * 58n + BigInt(index); + } + let hex = value.toString(16); + if (hex.length % 2) hex = `0${hex}`; + const decoded = hex ? Buffer.from(hex, "hex") : Buffer.alloc(0); + const leading = [...text].findIndex((char) => char !== "1"); + const zeroCount = leading < 0 ? text.length : leading; + return Buffer.concat([Buffer.alloc(zeroCount), decoded]); +} + +function solanaWalletFromConfig() { + const explicit = readEnvValue("SOLANA_DEPLOYER_ADDRESS", "SOLANA_WALLET_ADDRESS", "SOLANA_PUBLIC_KEY"); + if (explicit) return { address: explicit, source: "env_public_key" }; + + const keypairPath = readEnvValue("SOLANA_KEYPAIR_PATH"); + if (keypairPath) { + try { + const keypair = JSON.parse(readFileSync(keypairPath, "utf8")); + if (Array.isArray(keypair) && keypair.length >= 64) { + return { address: base58Encode(Buffer.from(keypair.slice(32, 64))), source: "SOLANA_KEYPAIR_PATH_public_key" }; + } + } catch { + // Fall through to private-key decode if present. + } + } + + const privateKey = readEnvValue("PRIVATE_KEY_SOLANA_DEPLOYER", "SOLANA_PRIVATE_KEY"); + if (privateKey) { + try { + const decoded = base58Decode(privateKey); + if (decoded.length >= 64) return { address: base58Encode(decoded.subarray(32, 64)), source: "solana_private_key_public_half" }; + } catch { + // Keep address gated if the value is a seed-only key. + } + } + return { address: "", source: "missing" }; +} + +function tronWalletFromConfig() { + const explicit = readEnvValue("TRON_DEPLOYER_ADDRESS", "TRON_WALLET_ADDRESS", "TRON_PUBLIC_ADDRESS", "TRON_ACCOUNT_ADDRESS"); + if (explicit) return { address: explicit, source: "env_tron_address" }; + const ethAddress = deployer.replace(/^0x/i, ""); + if (/^[0-9a-fA-F]{40}$/.test(ethAddress)) { + const payload = Buffer.from(`41${ethAddress}`, "hex"); + const checksum = createHash("sha256").update(createHash("sha256").update(payload).digest()).digest().subarray(0, 4); + return { address: base58Encode(Buffer.concat([payload, checksum])), source: "derived_from_evm_deployer_address" }; + } + return { address: "", source: "missing" }; +} + +function xrplWalletFromConfig() { + const explicit = readEnvValue("XRPL_DEPLOYER_ADDRESS", "XRP_DEPLOYER_ADDRESS", "XRPL_WALLET_ADDRESS", "XRP_WALLET_ADDRESS", "XRPL_ACCOUNT"); + return explicit ? { address: explicit, source: "env_xrpl_address" } : { address: "", source: "missing" }; +} + +async function solanaNativeBalance(address) { + if (!address) return { ok: false, units: "address_required", raw: "0", error: "missing_solana_address" }; + const rpcUrl = readEnvValue("SOLANA_RPC_URL") || "https://solana-rpc.publicnode.com"; + const result = await rpcCall(rpcUrl, "getBalance", [address]); + const lamports = result.ok ? BigInt(result.result?.value ?? 0) : 0n; + return { + ok: result.ok, + raw: lamports.toString(), + units: decimalUnits(lamports, 9), + error: result.ok ? undefined : result.error, + }; +} + +async function tronNativeBalance(address) { + if (!address) return { ok: false, units: "address_required", raw: "0", error: "missing_tron_address" }; + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 12_000); + try { + const headers = { "content-type": "application/json" }; + const apiKey = readEnvValue("TRONGRID_API_KEY"); + if (apiKey) headers["TRON-PRO-API-KEY"] = apiKey; + const response = await fetch("https://api.trongrid.io/wallet/getaccount", { + method: "POST", + headers, + body: JSON.stringify({ address, visible: true }), + signal: controller.signal, + }); + const json = await response.json(); + const sun = BigInt(json.balance ?? 0); + return { ok: response.ok, raw: sun.toString(), units: decimalUnits(sun, 6), error: response.ok ? undefined : JSON.stringify(json) }; + } catch (error) { + return { ok: false, raw: "0", units: "0", error: error.message }; + } finally { + clearTimeout(timeout); + } +} + +async function xrplNativeBalance(address) { + if (!address) return { ok: false, units: "address_required", raw: "0", error: "missing_xrpl_address" }; + const result = await rpcCall(readEnvValue("XRPL_RPC_URL") || "https://xrplcluster.com", "account_info", [{ account: address, ledger_index: "validated" }]); + const drops = result.ok ? BigInt(result.result?.account_data?.Balance ?? 0) : 0n; + return { + ok: result.ok, + raw: drops.toString(), + units: decimalUnits(drops, 6), + error: result.ok ? undefined : result.error, + }; +} + +function networkHealth(health, network) { + return (health?.checks ?? []).find((row) => row.network === network) ?? null; +} + +function classifyFundingPath(detail, token, native) { + const chainId = Number(detail.chainId); + const addressKey = `${chainId}:${String(detail.address ?? "").toLowerCase()}`; + const hasToken = BigInt(token.raw || "0") > 0n; + const hasGas = BigInt(native.raw || "0") > 0n; + const isOfficialQuoteAsset = officialQuoteAssets.has(addressKey); + const canBridge = bridgeCapableChains.has(chainId); + const canProtocolink = protocolinkCandidateChains.has(chainId); + + if (hasToken && detail.category === "configured_or_indexed_pools_zero_tvl") { + return { + fundingPath: "seed_existing_visible_pool_from_deployer_balance", + fundingPathStatus: hasGas ? "executable_after_operator_approval" : "native_gas_topup_required", + assetClass: isOfficialQuoteAsset ? "official_quote_asset" : "repo_or_wrapped_asset", + protocolinkUse: "not_required_for_seed; optional for pre-seed rebalance quote", + }; + } + + if (hasToken && detail.category === "no_visible_pool_binding") { + return { + fundingPath: "create_or_bind_pool_then_seed_from_deployer_balance", + fundingPathStatus: hasGas ? "pool_binding_required_before_funding" : "native_gas_and_pool_binding_required", + assetClass: isOfficialQuoteAsset ? "official_quote_asset" : "repo_or_wrapped_asset", + protocolinkUse: "not_required_until pool exists; optional to rebalance paired side", + }; + } + + if (isOfficialQuoteAsset) { + return { + fundingPath: canProtocolink ? "protocolink_swap_candidate_for_non_mintable_quote_asset" : "external_quote_asset_required", + fundingPathStatus: canProtocolink ? "requires_live_route_quote_source_asset_and_min_out" : "external_funding_required", + assetClass: "official_quote_asset", + protocolinkUse: canProtocolink + ? "use only after live quote proves deployer-held source asset converts into this exact token" + : "unsupported_by_current_protocolink_candidate_set", + }; + } + + if (canBridge) { + return { + fundingPath: "bridge_or_destination_mint_repo_asset_then_seed", + fundingPathStatus: hasGas ? "bridge_or_mint_route_required" : "native_gas_topup_then_bridge_or_mint", + assetClass: "repo_or_wrapped_asset", + protocolinkUse: "optional only if a public swap route beats bridge/mint for the needed asset", + }; + } + + return { + fundingPath: "manual_asset_source_required", + fundingPathStatus: hasGas ? "asset_source_required" : "native_gas_and_asset_source_required", + assetClass: "unknown_or_unclassified_asset", + protocolinkUse: "route support not classified", + }; +} + +function gasUnitsForFundingPath(fundingPath) { + const policy = { + seed_existing_visible_pool_from_deployer_balance: { + oneTimeGasUnits: 650_000, + stabilityGasUnitsPerCycle: 260_000, + rationale: "approve plus add/sync/validation transaction budget for already visible pools", + }, + create_or_bind_pool_then_seed_from_deployer_balance: { + oneTimeGasUnits: 1_350_000, + stabilityGasUnitsPerCycle: 320_000, + rationale: "factory create/bind plus seed transaction budget", + }, + bridge_or_destination_mint_repo_asset_then_seed: { + oneTimeGasUnits: 1_150_000, + stabilityGasUnitsPerCycle: 300_000, + rationale: "bridge-or-mint plus destination seed transaction budget", + }, + protocolink_swap_candidate_for_non_mintable_quote_asset: { + oneTimeGasUnits: 900_000, + stabilityGasUnitsPerCycle: 360_000, + rationale: "Protocolink route execution plus seed/rebalance budget after live quote", + }, + external_quote_asset_required: { + oneTimeGasUnits: 450_000, + stabilityGasUnitsPerCycle: 260_000, + rationale: "post-funding seed/rebalance budget; asset funding is out of band", + }, + manual_asset_source_required: { + oneTimeGasUnits: 650_000, + stabilityGasUnitsPerCycle: 260_000, + rationale: "manual source then seed/rebalance budget", + }, + }; + return policy[fundingPath] ?? { + oneTimeGasUnits: 650_000, + stabilityGasUnitsPerCycle: 260_000, + rationale: "default seed/rebalance budget", + }; +} + +function buildGasPlan({ chainId, native, gas, fundingPath }) { + const units = gasUnitsForFundingPath(fundingPath); + const gasPriceWei = BigInt(gas.raw || "0"); + const oneTimeRaw = BigInt(units.oneTimeGasUnits) * gasPriceWei; + const stabilityRaw = BigInt(units.stabilityGasUnitsPerCycle) * BigInt(stabilityCycles) * gasPriceWei; + const subtotalRaw = oneTimeRaw + stabilityRaw; + const requiredRaw = (subtotalRaw * gasSafetyBps + 9_999n) / 10_000n; + const nativeRaw = BigInt(native.raw || "0"); + const shortfallRaw = requiredRaw > nativeRaw ? requiredRaw - nativeRaw : 0n; + const surplusRaw = nativeRaw > requiredRaw ? nativeRaw - requiredRaw : 0n; + return { + nativeSymbol: nativeSymbolsByChain[chainId] ?? "native", + gasPriceWei: gas.raw, + gasPriceGwei: gas.gwei, + oneTimeGasUnits: units.oneTimeGasUnits, + stabilityCycles, + stabilityGasUnitsPerCycle: units.stabilityGasUnitsPerCycle, + safetyBps: Number(gasSafetyBps), + requiredNativeRaw: requiredRaw.toString(), + requiredNative: decimalUnits(requiredRaw, 18), + oneTimeNative: decimalUnits(oneTimeRaw, 18), + stabilityNative: decimalUnits(stabilityRaw, 18), + nativeBalanceRaw: native.raw, + nativeBalance: native.units, + shortfallNativeRaw: shortfallRaw.toString(), + shortfallNative: decimalUnits(shortfallRaw, 18), + surplusNativeRaw: surplusRaw.toString(), + surplusNative: decimalUnits(surplusRaw, 18), + status: shortfallRaw === 0n ? "gas_budget_satisfied" : "gas_budget_shortfall", + rationale: units.rationale, + gasPriceError: gas.error, + }; +} + +async function buildNonEvmFundingRequirements() { + const health = readJsonIfExists(nonEvmHealthPath, null); + const laneStatus = readJsonIfExists(nonEvmLaneStatusPath, null); + const lanes = laneStatus?.lanes ?? {}; + const solanaWallet = solanaWalletFromConfig(); + const tronWallet = tronWalletFromConfig(); + const xrplWallet = xrplWalletFromConfig(); + const [solanaBalance, tronBalance, xrplBalance] = await Promise.all([ + solanaNativeBalance(solanaWallet.address), + tronNativeBalance(tronWallet.address), + xrplNativeBalance(xrplWallet.address), + ]); + return [ + { + network: "Solana", + target: "mainnet-beta", + includedInFundingScope: true, + walletAddress: solanaWallet.address || "missing", + walletSource: solanaWallet.source, + currentBalanceStatus: solanaBalance.ok ? `${solanaBalance.units} SOL` : solanaBalance.units, + currentBalanceRaw: solanaBalance.raw, + nativeGasAsset: "SOL", + bridgeOrWrappedAsset: lanes.solana?.destinationAsset?.symbol ?? "cWAUSDT", + requiredFunding: "TBD", + status: solanaWallet.address ? "spl_mint_inventory_and_minimum_funding_targets_required" : "wallet_and_spl_mint_inventory_required", + networkHealth: networkHealth(health, "Solana"), + requirements: [ + solanaWallet.address ? "Canonical Solana deployer public key is bound for native SOL checks." : "Bind canonical Solana custody wallet/public key for funding checks.", + "Populate SPL mint addresses in config/solana-gru-bridge-lineup.json.", + "Check SOL gas/rent balance and SPL token balances for each promoted cW* mint.", + "Set minimum pool/rent/execution funding targets per Solana venue before declaring positive liquidity.", + ], + }, + { + network: "Tron", + target: "mainnet", + includedInFundingScope: true, + walletAddress: tronWallet.address || "missing", + walletSource: tronWallet.source, + currentBalanceStatus: tronBalance.ok ? `${tronBalance.units} TRX` : tronBalance.units, + currentBalanceRaw: tronBalance.raw, + nativeGasAsset: "TRX", + bridgeOrWrappedAsset: "TronAdapter relay inventory", + requiredFunding: "TBD", + status: tronWallet.source === "derived_from_evm_deployer_address" ? "derived_tron_wallet_needs_operator_confirmation_and_asset_inventory" : "native_tron_wallet_and_asset_inventory_required", + networkHealth: networkHealth(health, "Tron"), + requirements: [ + tronWallet.source === "derived_from_evm_deployer_address" ? "Confirm whether the EVM deployer-derived Tron address is the canonical native Tron deployer." : "Bind canonical Tron custody wallet address.", + "Check TRX energy/bandwidth funding and any native TRC-20 inventory needed for relay settlement.", + "Promote or document native Tron-side contracts/assets before treating Tron as liquidity-ready.", + "Close Chain 138 TronAdapter source/publication evidence separately from native Tron funding.", + ], + }, + { + network: "XRPL", + target: "mainnet", + includedInFundingScope: true, + walletAddress: xrplWallet.address || "missing", + walletSource: xrplWallet.source, + currentBalanceStatus: xrplBalance.ok ? `${xrplBalance.units} XRP` : xrplBalance.units, + currentBalanceRaw: xrplBalance.raw, + nativeGasAsset: "XRP", + bridgeOrWrappedAsset: lanes.xrpl?.wrappedAsset?.address ? `wXRP ${lanes.xrpl.wrappedAsset.address}` : "wXRP", + requiredFunding: "TBD", + status: xrplWallet.address ? "xrpl_reserve_trustline_and_bridge_inventory_required" : "xrpl_wallet_reserve_and_bridge_inventory_required", + networkHealth: networkHealth(health, "XRPL"), + requirements: [ + xrplWallet.address ? "Canonical XRPL account is bound for native XRP checks." : "Bind canonical XRPL account and optional destination tag policy.", + "Check XRP reserve, transfer-fee cushion, and any trustline/issuer requirements.", + "Check Chain 138 wXRP inventory and MintBurnController authorization readiness.", + "Close Chain 138 XRPLAdapter/wXRP/MintBurnController source-publication evidence separately from XRPL funding.", + ], + }, + { + network: "Other non-EVM majors", + target: "BTC/SOL/XRP/ADA/XLM/HBAR/SUI/TON class expansion", + includedInFundingScope: true, + walletAddress: "per-network wallet not bound", + walletSource: "missing", + currentBalanceStatus: "not_supported_by_current_balance_planner", + nativeGasAsset: "varies", + bridgeOrWrappedAsset: "not bound", + requiredFunding: "TBD", + status: "adapter_wallet_asset_and_venue_requirements_not_yet_bound", + networkHealth: null, + requirements: [ + "Create per-network custody wallet and balance checker.", + "Bind asset IDs/mints/trustlines/program IDs in repo config.", + "Define minimum native gas/rent/reserve and liquidity targets per network.", + "Add lane evidence before including the network in tracker-facing liquidity claims.", + ], + }, + ]; +} + +const readiness = JSON.parse(readFileSync(readinessPath, "utf8")); +const details = readiness.blockerInventory?.liquidityMissingDetails ?? []; +const rows = []; + +for (const detail of details) { + const chainId = Number(detail.chainId); + const rpcUrl = rpcByChain[chainId]; + const native = rpcUrl ? await nativeBalance(rpcUrl, deployer) : { ok: false, raw: "0", units: "0", error: "missing_rpc" }; + const token = rpcUrl && detail.address?.startsWith("0x") + ? await erc20Balance(rpcUrl, detail.address, deployer) + : { ok: false, raw: "0", units: "0", decimals: 18, error: "missing_token_or_rpc" }; + const hasGas = BigInt(native.raw || "0") > 0n; + const hasToken = BigInt(token.raw || "0") > 0n; + const funding = classifyFundingPath(detail, token, native); + const gas = await gasPrice(rpcUrl, chainId); + const gasPlan = buildGasPlan({ chainId, native, gas, fundingPath: funding.fundingPath }); + let status = "token_balance_gated"; + if (detail.category === "no_visible_pool_binding") status = hasToken ? "pool_binding_gated" : "pool_binding_and_token_balance_gated"; + if (detail.category === "configured_or_indexed_pools_zero_tvl" && hasToken) status = "fundable_token_balance_present"; + if (!hasGas) status = `${status}+gas_gated`; + + rows.push({ + chainId, + symbol: detail.symbol, + address: detail.address, + category: detail.category, + poolCount: detail.poolCount, + zeroTvlPoolCount: detail.zeroTvlPoolCount, + nativeBalance: native.units, + tokenBalance: token.units, + tokenBalanceRaw: token.raw, + status, + ...funding, + gasPlan, + rpcError: native.error || token.error, + }); +} + +const gasBudgetRows = rows.map((row) => ({ + chainId: row.chainId, + symbol: row.symbol, + nativeSymbol: row.gasPlan.nativeSymbol, + fundingPath: row.fundingPath, + requiredNative: row.gasPlan.requiredNative, + nativeBalance: row.gasPlan.nativeBalance, + shortfallNative: row.gasPlan.shortfallNative, + status: row.gasPlan.status, +})); + +const chainGasBudgetMap = new Map(); +for (const row of rows) { + const existing = chainGasBudgetMap.get(row.chainId) ?? { + chainId: row.chainId, + nativeSymbol: row.gasPlan.nativeSymbol, + gasPriceGwei: row.gasPlan.gasPriceGwei, + requiredNativeRaw: 0n, + nativeBalanceRaw: BigInt(row.gasPlan.nativeBalanceRaw || "0"), + rows: 0, + symbols: [], + }; + existing.requiredNativeRaw += BigInt(row.gasPlan.requiredNativeRaw || "0"); + existing.rows += 1; + existing.symbols.push(row.symbol); + chainGasBudgetMap.set(row.chainId, existing); +} + +const chainGasBudgetRows = [...chainGasBudgetMap.values()] + .sort((a, b) => Number(a.chainId) - Number(b.chainId)) + .map((row) => { + const shortfallRaw = row.requiredNativeRaw > row.nativeBalanceRaw ? row.requiredNativeRaw - row.nativeBalanceRaw : 0n; + const surplusRaw = row.nativeBalanceRaw > row.requiredNativeRaw ? row.nativeBalanceRaw - row.requiredNativeRaw : 0n; + return { + chainId: row.chainId, + nativeSymbol: row.nativeSymbol, + gasPriceGwei: row.gasPriceGwei, + rows: row.rows, + symbols: [...new Set(row.symbols)].join(", "), + requiredNativeRaw: row.requiredNativeRaw.toString(), + requiredNative: decimalUnits(row.requiredNativeRaw, 18), + nativeBalanceRaw: row.nativeBalanceRaw.toString(), + nativeBalance: decimalUnits(row.nativeBalanceRaw, 18), + shortfallNativeRaw: shortfallRaw.toString(), + shortfallNative: decimalUnits(shortfallRaw, 18), + surplusNativeRaw: surplusRaw.toString(), + surplusNative: decimalUnits(surplusRaw, 18), + status: shortfallRaw === 0n ? "chain_gas_budget_satisfied" : "chain_gas_budget_shortfall", + }; + }); + +const etherscanStability = { + purpose: "off_chain_indexing_stability_for_token_trackers", + boundary: "Etherscan/token trackers index public on-chain facts; gas only funds the transactions that create and refresh those facts.", + requiredOnChainFacts: [ + "Verified token contract and correct metadata/logoURI publication path.", + "Visible/indexable pool contract for each promoted token pair.", + "Positive, non-dust liquidity on the visible pool.", + "Recent real swap or liquidity-change events when tracker freshness is required.", + "Official quote-token evidence when claiming cW*/USDC or c*/USDC peg support.", + ], + gasBudgetRole: [ + "Create or bind missing pools.", + "Approve and seed liquidity.", + "Execute Protocolink/bridge/mint/swap actions when needed.", + "Run recurring stability/rebalance transactions so public indexers observe fresh state.", + ], + cannotBeSolvedByGasAlone: [ + "A missing verified-source listing.", + "A token logo or page-info package that is not published at the expected endpoint.", + "A pool that exists only in internal config but is not visible/indexable on the public chain.", + "A c* balance that has not been bridged or swapped into the exact official quote asset required by the tracker claim.", + ], + readinessStatus: chainGasBudgetRows.some((row) => row.status === "chain_gas_budget_shortfall") + ? "on_chain_stability_transactions_gas_shortfall" + : "on_chain_stability_transactions_gas_budget_satisfied", +}; + +const coffeeMoneyExecutableRows = rows.filter((row) => [ + "seed_existing_visible_pool_from_deployer_balance", + "create_or_bind_pool_then_seed_from_deployer_balance", +].includes(row.fundingPath)); +const coffeeMoneyGasShortfallChains = chainGasBudgetRows.filter((row) => row.status === "chain_gas_budget_shortfall"); +const coffeeMoneyPlan = { + purpose: "start_visible_indexable_liquidity_with_coffee_money", + operatorObservedUsdAvailable: coffeeMoneyUsdAvailable, + liquidityDustUsdPerRow: coffeeMoneyLiquidityUsdPerRow, + immediatelyUsefulRows: coffeeMoneyExecutableRows.length, + estimatedLiquidityDustUsd: Number((coffeeMoneyExecutableRows.length * coffeeMoneyLiquidityUsdPerRow).toFixed(2)), + gasShortfallChains: coffeeMoneyGasShortfallChains.map((row) => ({ + chainId: row.chainId, + nativeSymbol: row.nativeSymbol, + shortfallNative: row.shortfallNative, + symbols: row.symbols, + })), + assessment: coffeeMoneyUsdAvailable >= 35 + ? "enough_to_start_coffee_money_liquidity_if_routed_into_missing_native_gas" + : "not_enough_for_full_coffee_money_set", + recommendedOrder: [ + "Top up native gas on shortfall chains first: Optimism, BSC, Polygon, Arbitrum.", + "Seed existing visible pools that already have deployer token balance.", + "Create or bind missing visible pools for rows that already have deployer token balance.", + "Run tiny real swaps/liquidity events so Etherscan/tracker indexers see fresh public facts.", + "Leave Protocolink-only official quote-asset rows for last unless a live quote proves conversion from current deployer assets.", + ], + boundary: "This starts indexable public liquidity evidence; it does not create deep market depth or a large 1:1 peg reserve.", +}; + +const counts = rows.reduce((acc, row) => { + acc[row.status] = (acc[row.status] || 0) + 1; + return acc; +}, {}); + +const nonEvmFundingRequirements = await buildNonEvmFundingRequirements(); +const ethereumSourceInventory = await buildEthereumSourceInventory(); + +const payload = { + generatedAt: new Date().toISOString(), + mode: "read_only_no_broadcast", + deployer, + sourceReadiness: "reports/status/token-aggregation-adoption-readiness-live-20260509.json", + summary: { + rows: rows.length, + nonEvmFundingRequirementRows: nonEvmFundingRequirements.length, + fundableTokenBalancePresent: rows.filter((row) => row.status.startsWith("fundable_token_balance_present")).length, + poolBindingGated: rows.filter((row) => row.status.includes("pool_binding")).length, + gasGated: rows.filter((row) => row.status.includes("gas_gated")).length, + protocolinkSwapCandidates: rows.filter((row) => row.fundingPath === "protocolink_swap_candidate_for_non_mintable_quote_asset").length, + bridgeOrMintCandidates: rows.filter((row) => row.fundingPath === "bridge_or_destination_mint_repo_asset_then_seed").length, + poolCreateOrBindFirst: rows.filter((row) => row.fundingPath === "create_or_bind_pool_then_seed_from_deployer_balance").length, + seedExistingVisiblePoolNow: rows.filter((row) => row.fundingPath === "seed_existing_visible_pool_from_deployer_balance").length, + gasBudgetSatisfied: rows.filter((row) => row.gasPlan.status === "gas_budget_satisfied").length, + gasBudgetShortfall: rows.filter((row) => row.gasPlan.status === "gas_budget_shortfall").length, + chainGasBudgetSatisfied: chainGasBudgetRows.filter((row) => row.status === "chain_gas_budget_satisfied").length, + chainGasBudgetShortfall: chainGasBudgetRows.filter((row) => row.status === "chain_gas_budget_shortfall").length, + gasPolicy: { + stabilityCycles, + gasSafetyBps: Number(gasSafetyBps), + }, + statusCounts: counts, + }, + rows, + gasBudgetRows, + chainGasBudgetRows, + etherscanStability, + coffeeMoneyPlan, + ethereumSourceInventory, + nonEvmFundingRequirements, +}; + +const md = [ + "# Token-Aggregation Liquidity Gap Funding Plan", + "", + `- Generated: \`${payload.generatedAt}\``, + `- Mode: \`${payload.mode}\``, + `- Deployer: \`${deployer}\``, + "", + table(["Metric", "Count"], Object.entries(payload.summary).map(([key, value]) => [key, typeof value === "object" ? JSON.stringify(value) : value])), + "", + "## Rows", + "", + table( + ["Chain", "Symbol", "Category", "Pools", "Native", "Token balance", "Status", "Funding path", "Gas shortfall"], + rows.map((row) => [row.chainId, row.symbol, row.category, row.poolCount, row.nativeBalance, row.tokenBalance, row.status, row.fundingPath, `${row.gasPlan.shortfallNative} ${row.gasPlan.nativeSymbol}`]), + ), + "", + "### Chain-Level Gas Budget", + "", + "This aggregates all planned row actions by network because the same deployer native balance pays every deployment, seed, swap, bridge, and stability transaction on that chain.", + "", + table( + ["Chain", "Symbols", "Native", "Rows", "Gas price gwei", "Required", "Balance", "Shortfall", "Status"], + chainGasBudgetRows.map((row) => [ + row.chainId, + row.symbols, + row.nativeSymbol, + row.rows, + row.gasPriceGwei, + `${row.requiredNative} ${row.nativeSymbol}`, + `${row.nativeBalance} ${row.nativeSymbol}`, + `${row.shortfallNative} ${row.nativeSymbol}`, + row.status, + ]), + ), + "", + "## Gas Budget", + "", + `Gas is budgeted for one deployment/seed action plus \`${stabilityCycles}\` continual stability cycles, with a \`${Number(gasSafetyBps) / 100}%\` safety multiplier. Etherscan/token-tracker stability itself is off-chain indexing; gas only funds the on-chain facts that Etherscan can index.`, + "", + table( + ["Chain", "Symbol", "Native", "Gas price gwei", "One-time gas", "Stability gas/cycle", "Required", "Balance", "Shortfall", "Status"], + rows.map((row) => [ + row.chainId, + row.symbol, + row.gasPlan.nativeSymbol, + row.gasPlan.gasPriceGwei, + row.gasPlan.oneTimeGasUnits, + row.gasPlan.stabilityGasUnitsPerCycle, + `${row.gasPlan.requiredNative} ${row.gasPlan.nativeSymbol}`, + `${row.gasPlan.nativeBalance} ${row.gasPlan.nativeSymbol}`, + `${row.gasPlan.shortfallNative} ${row.gasPlan.nativeSymbol}`, + row.gasPlan.status, + ]), + ), + "", + "## Etherscan Stability Boundary", + "", + `- Purpose: \`${etherscanStability.purpose}\``, + `- Status: \`${etherscanStability.readinessStatus}\``, + `- Boundary: ${etherscanStability.boundary}`, + "", + "Required on-chain facts for Etherscan/tracker stability:", + "", + ...etherscanStability.requiredOnChainFacts.map((item) => `- ${item}`), + "", + "Gas budget role:", + "", + ...etherscanStability.gasBudgetRole.map((item) => `- ${item}`), + "", + "Cannot be solved by gas alone:", + "", + ...etherscanStability.cannotBeSolvedByGasAlone.map((item) => `- ${item}`), + "", + "## Coffee-Money Start Plan", + "", + `- Operator-observed deployer value available: \`$${coffeeMoneyPlan.operatorObservedUsdAvailable}\``, + `- Assessment: \`${coffeeMoneyPlan.assessment}\``, + `- Immediately useful rows: \`${coffeeMoneyPlan.immediatelyUsefulRows}\``, + `- Planning dust liquidity: \`$${coffeeMoneyPlan.liquidityDustUsdPerRow}\` per row`, + `- Estimated dust liquidity: \`$${coffeeMoneyPlan.estimatedLiquidityDustUsd}\``, + `- Boundary: ${coffeeMoneyPlan.boundary}`, + "", + "Native gas shortfall chains to fill first:", + "", + table( + ["Chain", "Symbols", "Shortfall"], + coffeeMoneyPlan.gasShortfallChains.map((row) => [row.chainId, row.symbols, `${row.shortfallNative} ${row.nativeSymbol}`]), + ), + "", + "Recommended order:", + "", + ...coffeeMoneyPlan.recommendedOrder.map((item) => `- ${item}`), + "", + "### Ethereum Source Inventory", + "", + `- Native ETH: \`${ethereumSourceInventory.native.balance}\``, + "", + table( + ["Token", "Balance", "Role", "Status"], + ethereumSourceInventory.tokens.map((token) => [token.symbol, token.balance, token.role, token.balanceStatus]), + ), + "", + "Interpretation:", + "", + ...ethereumSourceInventory.interpretation.map((item) => `- ${item}`), + "", + "## Funding Path Interpretation", + "", + "- `seed_existing_visible_pool_from_deployer_balance`: token and gas are present; only operator approval and pool-specific seeding rules remain.", + "- `create_or_bind_pool_then_seed_from_deployer_balance`: token and gas are present, but no visible/indexable pool binding exists yet.", + "- `bridge_or_destination_mint_repo_asset_then_seed`: repo-controlled c*/cW* inventory can be moved or minted once the bridge/mint path and destination gas are ready.", + "- `protocolink_swap_candidate_for_non_mintable_quote_asset`: the needed asset is an official/non-mintable quote asset; Protocolink can help only after a live quote proves a deployer-held source asset converts into the exact target token with acceptable minOut.", + "- `external_quote_asset_required`: neither bridge nor Protocolink coverage is classified for that exact non-mintable quote asset.", + "", + "## Non-EVM Funding Requirements", + "", + "These networks are now part of funding scope. The planner resolves non-EVM deployer wallets where the repo can prove them, checks native gas balances where possible, and leaves funding amounts `TBD` until asset IDs and minimum venue targets are bound.", + "", + table( + ["Network", "Target", "Wallet", "Source", "Native gas", "Current balance", "Required funding", "Status"], + payload.nonEvmFundingRequirements.map((row) => [ + row.network, + row.target, + row.walletAddress, + row.walletSource, + row.nativeGasAsset, + row.currentBalanceStatus, + row.requiredFunding, + row.status, + ]), + ), + "", + "### Non-EVM Requirement Details", + "", + ...payload.nonEvmFundingRequirements.flatMap((row) => [ + `#### ${row.network}`, + "", + ...row.requirements.map((requirement) => `- ${requirement}`), + "", + ]), + "", + "## Execution Boundary", + "", + "This planner is read-only. It proves whether the deployer currently holds token and gas inventory for each liquidity gap. It does not create pools, add liquidity, approve tokens, bridge assets, or broadcast transactions.", +].join("\n"); + +mkdirSync(resolve(repoRoot, "reports/status"), { recursive: true }); +writeFileSync(jsonOut, `${JSON.stringify(payload, null, 2)}\n`); +writeFileSync(mdOut, `${md}\n`); +console.log(jsonOut); diff --git a/scripts/verify/reconcile-env-canonical.sh b/scripts/verify/reconcile-env-canonical.sh index 44bdcff6..d7c5c06c 100755 --- a/scripts/verify/reconcile-env-canonical.sh +++ b/scripts/verify/reconcile-env-canonical.sh @@ -32,7 +32,8 @@ FEE_COLLECTOR=0xF78246eB94c6CB14018E507E60661314E5f4C53f DEBT_REGISTRY=0x95BC4A997c0670d5DAC64d55cDf3769B53B63C28 POLICY_MANAGER=0x0C4FD27018130A00762a802f91a72D6a64a60F14 TOKEN_IMPLEMENTATION=0x0059e237973179146237aB49f1322E8197c22b21 -CCIPWETH9_BRIDGE_CHAIN138=0x9cba0D04Ae5f6f16e3C599025aB97a05c4A593d5 +# Alternate legacy deployment (do not use for ops): 0x9cba0D04Ae5f6f16e3C599025aB97a05c4A593d5 +CCIPWETH9_BRIDGE_CHAIN138=0xcacfd227A040002e49e2e01626363071324f820a CCIPWETH10_BRIDGE_CHAIN138=0xe0E93247376aa097dB308B92e6Ba36bA015535D0 LINK_TOKEN=0xb7721dD53A8c629d9f1Ba31a5819AFe250002b03 CCIP_FEE_TOKEN=0xb7721dD53A8c629d9f1Ba31a5819AFe250002b03 diff --git a/scripts/verify/run-all-validation.sh b/scripts/verify/run-all-validation.sh index 6045e1d4..4dc3a63d 100644 --- a/scripts/verify/run-all-validation.sh +++ b/scripts/verify/run-all-validation.sh @@ -3,6 +3,9 @@ # Use for CI or pre-deploy: dependencies, config files, optional genesis. # Usage: bash scripts/verify/run-all-validation.sh [--skip-genesis] # --skip-genesis: do not run validate-genesis.sh (default: run if smom-dbis-138 present). +# Optional: EI_MATRIX_ONCHAIN_AUDIT_CI=1 runs scripts/lib/ei_matrix_onchain_readiness_audit.py (needs +# ETHEREUM_MAINNET_RPC + RPC_URL_138 in .env). EI_MATRIX_ONCHAIN_AUDIT_CI_LIMIT default 120; 0 = full grid. +# Strict gate: set EI_MATRIX_AUDIT_MIN_MAINNET_RAW_CI / EI_MATRIX_AUDIT_MIN_138_RAW_CI. # Steps: dependencies, config files, cW* mesh matrix (if pair-discovery JSON exists), optional advisory non-EVM public health, genesis. set -euo pipefail @@ -170,6 +173,56 @@ run_summary_record_step "3d" "Non-EVM public network health" "success" "$((SECON step_done "$STEP_STARTED" echo "" +echo "3d1. d-bis.org CWUSDC Etherscan profile prereq URLs (advisory)..." +STEP_STARTED=$SECONDS +CWU_URLS="$SCRIPT_DIR/check-cwusdc-etherscan-prereq-urls.sh" +CWU_STATUS="skipped" +if [[ -x "$CWU_URLS" ]] && command -v curl &>/dev/null; then + if bash "$CWU_URLS"; then + log_ok "d-bis.org token-profile URLs OK (CWUSDC E2E prereq)" + CWU_STATUS="success" + else + echo " (advisory: one or more URLs not HTTP 200 — fix site or network; see CWUSDC_ETHERSCAN_E2E_RECOMMENDATIONS.md)" + CWU_STATUS="advisory_fail" + fi +else + echo " (skip: curl or $CWU_URLS missing)" +fi +run_summary_record_step "3d1" "CWUSDC d-bis URL prereqs" "$CWU_STATUS" "$((SECONDS - STEP_STARTED))" +step_done "$STEP_STARTED" +echo "" + +echo "3e. EI matrix on-chain readiness audit (optional)..." +STEP_STARTED=$SECONDS +EIM_STEP_STATUS="skipped" +if [[ "${EI_MATRIX_ONCHAIN_AUDIT_CI:-}" == "1" ]]; then + # shellcheck disable=SC1091 + source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" + if [[ -z "${ETHEREUM_MAINNET_RPC:-}" || -z "${RPC_URL_138:-}" ]]; then + echo " (skip: ETHEREUM_MAINNET_RPC or RPC_URL_138 unset — set RPCs in .env for CI gate)" + else + CI_LIMIT="${EI_MATRIX_ONCHAIN_AUDIT_CI_LIMIT:-120}" + CI_SHARD="${EI_MATRIX_ONCHAIN_AUDIT_CI_SHARD:-200}" + CI_WORKERS="${EI_MATRIX_ONCHAIN_AUDIT_CI_WORKERS:-2}" + MIN_M="${EI_MATRIX_AUDIT_MIN_MAINNET_RAW_CI:-0}" + MIN_138="${EI_MATRIX_AUDIT_MIN_138_RAW_CI:-0}" + JSON_CI="${EI_MATRIX_ONCHAIN_AUDIT_JSON_CI:-reports/status/ei-matrix-readiness-audit-ci.json}" + AUDIT_PY="$PROJECT_ROOT/scripts/lib/ei_matrix_onchain_readiness_audit.py" + EXTRA=(--shard-size "$CI_SHARD" --workers "$CI_WORKERS" --both --min-mainnet-raw "$MIN_M" --min-138-raw "$MIN_138" --json-out "$JSON_CI") + if [[ "$CI_LIMIT" != "0" ]]; then + EXTRA+=(--limit "$CI_LIMIT") + fi + python3 "$AUDIT_PY" "${EXTRA[@]}" || log_err "EI matrix on-chain audit failed (thresholds or RPC)" + log_ok "EI matrix on-chain audit OK ($JSON_CI)" + EIM_STEP_STATUS="success" + fi +else + echo " (skip: set EI_MATRIX_ONCHAIN_AUDIT_CI=1 to run; optional EI_MATRIX_ONCHAIN_AUDIT_CI_LIMIT=0 for full grid)" +fi +run_summary_record_step "3e" "EI matrix on-chain audit" "$EIM_STEP_STATUS" "$((SECONDS - STEP_STARTED))" +step_done "$STEP_STARTED" +echo "" + if [[ "$SKIP_GENESIS" == true ]]; then echo "4. Genesis — skipped (--skip-genesis)" run_summary_record_step "4" "Genesis (smom-dbis-138)" "skipped" "0" diff --git a/scripts/verify/run-cwusdc-provider-monitoring-snapshot.sh b/scripts/verify/run-cwusdc-provider-monitoring-snapshot.sh new file mode 100755 index 00000000..3f6ffa07 --- /dev/null +++ b/scripts/verify/run-cwusdc-provider-monitoring-snapshot.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# Read-only cWUSDC provider propagation monitor. +set -euo pipefail + +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$PROJECT_ROOT" + +TS="$(date -u +%Y-%m-%dT%H:%M:%SZ)" +JSON_OUT="reports/status/cwusdc-provider-monitoring-snapshot-latest.json" +MD_OUT="reports/status/cwusdc-provider-monitoring-snapshot-latest.md" +BUNDLE_DATE="${CWUSDC_EVIDENCE_BUNDLE_DATE:-$(date -u +%Y%m%d)}" + +pnpm cwusdc:etherscan-dossier +pnpm cwusdc:role-audit +pnpm cwusdc:provider-ci +pnpm cwusdc:doc-links +pnpm cwusdc:role-appendix +pnpm cwusdc:submission-prefill +CWUSDC_EVIDENCE_BUNDLE_DATE="$BUNDLE_DATE" pnpm cwusdc:evidence-bundle + +python3 - "$TS" "$JSON_OUT" "$MD_OUT" "$BUNDLE_DATE" <<'PY' +import json +import sys +from pathlib import Path + +ts = sys.argv[1] +json_out = Path(sys.argv[2]) +md_out = Path(sys.argv[3]) +bundle_date = sys.argv[4] + +def load(path): + p = Path(path) + return json.loads(p.read_text()) if p.exists() else {} + +dossier = load("reports/status/cwusdc-etherscan-value-dossier-latest.json") +dossier_readiness = dossier.get("readiness") or dossier.get("summary") or {} +provider_ci = load("reports/status/cwusdc-provider-readiness-ci-latest.json") +links = load("reports/status/cwusdc-institutional-doc-link-check-latest.json") +role_appendix = load("reports/status/cwusdc-role-deployment-appendix-latest.json") +payload = { + "schema": "cwusdc-provider-monitoring-snapshot/v1", + "generatedAt": ts, + "status": "success" if provider_ci.get("repoControlledPrereqsPassed") and links.get("status") == "pass" else "attention", + "readyForExternalSubmission": dossier_readiness.get("readyForExternalSubmission"), + "etherscanValueReady": dossier_readiness.get("etherscanValueReady"), + "coinGeckoPriceReady": dossier_readiness.get("coinGeckoPriceReady"), + "repoControlledPrereqsPassed": provider_ci.get("repoControlledPrereqsPassed"), + "externalBlockersAdvisory": provider_ci.get("externalBlockersAdvisory", []), + "docLinkStatus": links.get("status"), + "roleEventCount": role_appendix.get("eventCount"), + "artifacts": { + "dossier": "reports/status/cwusdc-etherscan-value-dossier-latest.json", + "providerCi": "reports/status/cwusdc-provider-readiness-ci-latest.json", + "docLinks": "reports/status/cwusdc-institutional-doc-link-check-latest.json", + "roleAppendix": "reports/status/cwusdc-role-deployment-appendix-latest.json", + "evidenceBundleSha256": f"reports/status/cwusdc-institutional-evidence-bundle-{bundle_date}.sha256", + }, +} +json_out.write_text(json.dumps(payload, indent=2) + "\n") +lines = [ + "# cWUSDC Provider Monitoring Snapshot", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Status: `{payload['status']}`", + f"- Ready for external submission: `{payload['readyForExternalSubmission']}`", + f"- Etherscan Value ready: `{payload['etherscanValueReady']}`", + f"- CoinGecko price ready: `{payload['coinGeckoPriceReady']}`", + f"- Repo-controlled prerequisites passed: `{payload['repoControlledPrereqsPassed']}`", + f"- Institutional doc link status: `{payload['docLinkStatus']}`", + f"- Role event count: `{payload['roleEventCount']}`", + f"- External advisory blockers: `{len(payload['externalBlockersAdvisory'])}`", +] +md_out.write_text("\n".join(lines) + "\n") +print(f"Wrote {json_out}") +print(f"Wrote {md_out}") +PY diff --git a/scripts/verify/run-cwusdc-provider-nonmanual-checks.sh b/scripts/verify/run-cwusdc-provider-nonmanual-checks.sh new file mode 100755 index 00000000..e3c7d941 --- /dev/null +++ b/scripts/verify/run-cwusdc-provider-nonmanual-checks.sh @@ -0,0 +1,79 @@ +#!/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/. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" + +STRICT_REPO=false +while [[ $# -gt 0 ]]; do + case "$1" in + --strict-repo) + STRICT_REPO=true + shift + ;; + -h|--help) + sed -n '1,4p' "$0" + echo " --strict-repo Exit non-zero if repo-controlled URL prerequisites fail." + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + exit 1 + ;; + esac +done + +PREREQ_JSON="reports/status/cwusdc-etherscan-prereq-urls-latest.json" +PREREQ_MD="reports/status/cwusdc-etherscan-prereq-urls-latest.md" +TRACKERS_JSON="reports/status/cwusdc-external-trackers-live-latest.json" +TRACKERS_MD="reports/status/cwusdc-external-trackers-live-latest.md" +LIQUIDITY_JSON="reports/status/token-aggregation-liquidity-gap-funding-plan-latest.json" +CMC_SANITY_JSON="reports/status/cmc-provider-report-sanity-latest.json" +HANDOFF_JSON="reports/status/cwusdc-provider-handoff-latest.json" +HANDOFF_MD="reports/status/cwusdc-provider-handoff-latest.md" + +echo "=== cWUSDC provider non-manual checks ===" +echo "Mode: public/read-only, report writes only" +echo "" + +REPO_STATUS=0 +echo "1. Repo-controlled Etherscan prerequisite URLs..." +if ! bash "$SCRIPT_DIR/check-cwusdc-etherscan-prereq-urls.sh" --json-out "$PREREQ_JSON" --md-out "$PREREQ_MD"; then + REPO_STATUS=1 + echo " Repo-controlled URL prerequisites failed." +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 +echo "" + +echo "3. Liquidity-gap funding planner (read-only)..." +node "$SCRIPT_DIR/plan-token-aggregation-liquidity-gap-funding.mjs" +echo "" + +echo "3b. CMC-shaped report sanity (advisory)..." +python3 "$SCRIPT_DIR/check-cmc-provider-report-sanity.py" || true +echo "" + +echo "4. Provider handoff report..." +python3 "$SCRIPT_DIR/build-cwusdc-provider-handoff-report.py" \ + --prereq-json "$PREREQ_JSON" \ + --trackers-json "$TRACKERS_JSON" \ + --liquidity-json "$LIQUIDITY_JSON" \ + --cmc-sanity-json "$CMC_SANITY_JSON" \ + --json-out "$HANDOFF_JSON" \ + --md-out "$HANDOFF_MD" +echo "" + +echo "Handoff: $HANDOFF_MD" + +if [[ "$STRICT_REPO" == "true" && "$REPO_STATUS" -ne 0 ]]; then + exit "$REPO_STATUS" +fi + +exit 0 diff --git a/scripts/verify/run-ei-matrix-full-readiness-audit.sh b/scripts/verify/run-ei-matrix-full-readiness-audit.sh new file mode 100755 index 00000000..447b5260 --- /dev/null +++ b/scripts/verify/run-ei-matrix-full-readiness-audit.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# Full-grid EI matrix on-chain readiness: mainnet cWUSDC + Chain 138 cUSDC (sharded RPC). +# Sources scripts/lib/load-project-env.sh for RPCs and token defaults. +# +# Environment (optional): +# EI_MATRIX_AUDIT_SHARD_SIZE default 400 +# EI_MATRIX_AUDIT_WORKERS default 3 +# EI_MATRIX_AUDIT_MIN_MAINNET_RAW default 12000000 (12 USDC units, 6 decimals) +# EI_MATRIX_AUDIT_MIN_138_RAW default 0 (set >0 to require 138 cUSDC everywhere) +# EI_MATRIX_AUDIT_JSON_OUT default reports/status/ei-matrix-readiness-audit-latest.json +# EI_MATRIX_AUDIT_GAPS_MAINNET default reports/status/ei-matrix-readiness-gaps-mainnet-indices.txt +# EI_MATRIX_AUDIT_GAPS_138 default reports/status/ei-matrix-readiness-gaps-138-indices.txt +# +# Pass-through: any extra args after optional -- are forwarded to ei_matrix_onchain_readiness_audit.py +# ./scripts/verify/run-ei-matrix-full-readiness-audit.sh -- --offset 0 --limit 500 +# +# Exit 1 if any wallet is below configured minima (operator gate). +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" +# shellcheck disable=SC1091 +source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" +[[ "${1:-}" == "--" ]] && shift + +SHARD="${EI_MATRIX_AUDIT_SHARD_SIZE:-400}" +WORKERS="${EI_MATRIX_AUDIT_WORKERS:-3}" +MIN_M="${EI_MATRIX_AUDIT_MIN_MAINNET_RAW:-12000000}" +MIN_138="${EI_MATRIX_AUDIT_MIN_138_RAW:-0}" +JSON_OUT="${EI_MATRIX_AUDIT_JSON_OUT:-reports/status/ei-matrix-readiness-audit-latest.json}" +GAPS_M="${EI_MATRIX_AUDIT_GAPS_MAINNET:-reports/status/ei-matrix-readiness-gaps-mainnet-indices.txt}" +GAPS_138="${EI_MATRIX_AUDIT_GAPS_138:-reports/status/ei-matrix-readiness-gaps-138-indices.txt}" + +exec python3 "$PROJECT_ROOT/scripts/lib/ei_matrix_onchain_readiness_audit.py" \ + --shard-size "$SHARD" \ + --workers "$WORKERS" \ + --both \ + --min-mainnet-raw "$MIN_M" \ + --min-138-raw "$MIN_138" \ + --report-by-class \ + --json-out "$JSON_OUT" \ + --gaps-mainnet-out "$GAPS_M" \ + --gaps-138-out "$GAPS_138" \ + "$@" diff --git a/scripts/verify/snapshot-mainnet-cwusdc-usdc-preflight.py b/scripts/verify/snapshot-mainnet-cwusdc-usdc-preflight.py index 7ba839cd..75fdae86 100755 --- a/scripts/verify/snapshot-mainnet-cwusdc-usdc-preflight.py +++ b/scripts/verify/snapshot-mainnet-cwusdc-usdc-preflight.py @@ -78,7 +78,13 @@ def parse_uint(value: str) -> int: def parse_uints(value: str, count: int) -> list[int]: - matches = [int(match) for match in UINT_RE.findall(value)] + matches: list[int] = [] + for line in value.splitlines(): + line_matches = UINT_RE.findall(line) + if line_matches: + matches.append(int(line_matches[0])) + if len(matches) < count: + matches = [int(match) for match in UINT_RE.findall(value)] if len(matches) < count: raise ValueError(f"expected at least {count} integers from {value!r}") return matches[:count] diff --git a/token-lists/chainlists/chain-138.json b/token-lists/chainlists/chain-138.json index 8a0c7daa..212ccac8 100644 --- a/token-lists/chainlists/chain-138.json +++ b/token-lists/chainlists/chain-138.json @@ -24,6 +24,5 @@ "standard": "EIP3091" } ], - "icon": "https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png" + "icon": "https://explorer.d-bis.org/token-icons/chain-138.png" } - diff --git a/token-lists/lists/dbis-138.tokenlist.json b/token-lists/lists/dbis-138.tokenlist.json index 447f5f0e..0cd266e1 100644 --- a/token-lists/lists/dbis-138.tokenlist.json +++ b/token-lists/lists/dbis-138.tokenlist.json @@ -11,7 +11,7 @@ "chain138", "defi oracle meta" ], - "logoURI": "https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong", + "logoURI": "https://explorer.d-bis.org/api/v1/report/logo/chain-138", "tokens": [ { "chainId": 138, @@ -31,7 +31,7 @@ "name": "Wrapped Ether", "symbol": "WETH", "decimals": 18, - "logoURI": "https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong", + "logoURI": "https://explorer.d-bis.org/api/v1/report/logo/ETH", "tags": [ "defi", "wrapped" @@ -53,7 +53,7 @@ "name": "Wrapped Ether v10", "symbol": "WETH10", "decimals": 18, - "logoURI": "https://ipfs.io/ipfs/QmanDFPHxnbKd6SSNzzXHf9GbpL9dLXSphxDZSPPYE6ds4", + "logoURI": "https://explorer.d-bis.org/api/v1/report/logo/ETH", "tags": [ "defi", "wrapped" @@ -73,7 +73,7 @@ "name": "Chainlink Token", "symbol": "LINK", "decimals": 18, - "logoURI": "https://ipfs.io/ipfs/QmenWcmfNGfssz4HXvrRV912eZDiKqLTt6z2brRYuTGz9A", + "logoURI": "https://explorer.d-bis.org/api/v1/report/logo/LINK", "tags": [ "defi", "oracle",