From ba71943c3f258aff1aba7b360690dd17d0c0709c Mon Sep 17 00:00:00 2001 From: defiQUG Date: Tue, 14 Apr 2026 07:13:22 -0700 Subject: [PATCH] Add Chain138 native DODO verification tooling --- MEV_Bot | 2 +- cross-chain-pmm-lps | 2 +- ...IN138_NATIVE_DODO_READ_SURFACE_VERIFIER.md | 37 +++ .../GRU_NATIVE_PROTOCOL_ONLY_POLICY.md | 58 +++++ docs/04-configuration/README.md | 1 + .../sync-mev-chain138-token-symbols.sh | 76 ++++++ ...heck-chain138-native-dodo-read-surfaces.sh | 243 ++++++++++++++++++ 7 files changed, 417 insertions(+), 2 deletions(-) create mode 100644 docs/04-configuration/CHAIN138_NATIVE_DODO_READ_SURFACE_VERIFIER.md create mode 100644 docs/04-configuration/GRU_NATIVE_PROTOCOL_ONLY_POLICY.md create mode 100755 scripts/deployment/sync-mev-chain138-token-symbols.sh create mode 100755 scripts/verify/check-chain138-native-dodo-read-surfaces.sh diff --git a/MEV_Bot b/MEV_Bot index 03c59eee..57001548 160000 --- a/MEV_Bot +++ b/MEV_Bot @@ -1 +1 @@ -Subproject commit 03c59eeea8c81e959dc13fd998b5ce5ac86a9361 +Subproject commit 57001548b04a9865fd068f0e829556d528b81eda diff --git a/cross-chain-pmm-lps b/cross-chain-pmm-lps index 24119f8a..0b089f11 160000 --- a/cross-chain-pmm-lps +++ b/cross-chain-pmm-lps @@ -1 +1 @@ -Subproject commit 24119f8a6e6efae0ae8a4f515993b6354ab3874a +Subproject commit 0b089f118144a8e392606fd6329dfed85d28ef2c diff --git a/docs/04-configuration/CHAIN138_NATIVE_DODO_READ_SURFACE_VERIFIER.md b/docs/04-configuration/CHAIN138_NATIVE_DODO_READ_SURFACE_VERIFIER.md new file mode 100644 index 00000000..b24f69e0 --- /dev/null +++ b/docs/04-configuration/CHAIN138_NATIVE_DODO_READ_SURFACE_VERIFIER.md @@ -0,0 +1,37 @@ +# Chain 138 Native DODO Read Surface Verifier + +This verifier checks the native Chain 138 DODO read surfaces directly on-chain and prints explicit pass/fail output for the canonical pools. + +## What it checks + +- DODO PMM pools: + - `getVaultReserve()` + - `getPMMStateForCall()` +- D3MM pilot pool: + - `getTokenReserve(address)` + - `querySellTokens(address,address,uint256)` + +Canonical pools: + +- `cUSDT/cUSDC` - `0x9e89bAe009adf128782E19e8341996c596ac40dC` +- `cUSDT/USDT` - `0x866Cb44b59303d8dc5f4F9E3E7A8e8b0bf238d66` +- `cUSDC/USDC` - `0xc39B7D0F40838cbFb54649d327f49a6DAC964062` +- `WETH10/USDT` D3MM pilot - `0x6550a3a59070061a262a893A1D6F3F490afFDBDA` + +## Usage + +```bash +bash scripts/verify/check-chain138-native-dodo-read-surfaces.sh +``` + +Optional overrides: + +- `RPC_URL_138` +- `CHAIN138_RPC_URL` +- `CHAIN138_RPC` +- `CHAIN138_DODO_PMM_PROBE_AMOUNT` +- `CHAIN138_D3_PROBE_SELL_AMOUNT` + +The script exits non-zero if any required native read surface fails or returns an empty / invalid response. + +If the D3MM `querySellTokens(...)` call fails with `Chainlink: Stale Price`, that is a real native read-surface issue on the canonical pilot pool and the verifier will intentionally fail until the upstream oracle path is healthy again. diff --git a/docs/04-configuration/GRU_NATIVE_PROTOCOL_ONLY_POLICY.md b/docs/04-configuration/GRU_NATIVE_PROTOCOL_ONLY_POLICY.md new file mode 100644 index 00000000..3e6bb428 --- /dev/null +++ b/docs/04-configuration/GRU_NATIVE_PROTOCOL_ONLY_POLICY.md @@ -0,0 +1,58 @@ +# GRU Native Protocol Only Policy + +## Purpose + +This policy makes one rule explicit for GRU public venue coverage, gas-family mirrors, and MEV/public routing claims: + +- A protocol lane counts as `live`, `routingVisible`, or `publicRoutingEnabled` only when it points at a **native, discoverable protocol contract** on the target network. +- Placeholder, scaffold, synthetic, or documentation-only addresses must never be represented as live protocol support. + +## Applies To + +- `cross-chain-pmm-lps/config/deployment-status.json` +- public protocol coverage docs and status tables +- MEV route / venue inventory derived from those surfaces +- operator rollout checklists that reference protocol readiness + +## Required Behavior + +### Live lane requirements + +A protocol lane marked live must have all of the following: + +1. A real on-chain address for the native protocol contract family +2. Contract discovery or provenance that ties it back to the intended upstream protocol +3. Matching route / planner / indexer visibility where the surface claims it is routable + +### Not allowed as live support + +These must not be counted as live protocol coverage: + +- scaffold addresses +- synthetic addresses used only for planning +- placeholder addresses shaped from chain IDs or protocol markers +- inventory rows that exist only to reserve a future slot + +## Current Implication + +If a venue family is still using scaffold addresses, its status must be represented as one of: + +- `planned` +- `scaffolded` +- `not yet native` +- `out of scope` + +but not: + +- `live` +- `routingVisible` +- `publicRoutingEnabled` + +## Enforcement + +The deployment-status validator now rejects obvious placeholder addresses for: + +- `gasPmmPools[*].poolAddress` when `publicRoutingEnabled=true` +- `gasReferenceVenues[*].venueAddress` when `live=true` or `routingVisible=true` + +This is a guardrail, not a full provenance proof. Operator review still needs to confirm the upstream-native protocol family is correct. diff --git a/docs/04-configuration/README.md b/docs/04-configuration/README.md index a27d5879..c20b3f2e 100644 --- a/docs/04-configuration/README.md +++ b/docs/04-configuration/README.md @@ -76,6 +76,7 @@ This directory contains setup and configuration guides. **DEX and aggregators (Chain 138 tokens and routing):** - **[DEX_AND_AGGREGATORS_CHAIN138_EXPLAINER.md](DEX_AND_AGGREGATORS_CHAIN138_EXPLAINER.md)** ⭐⭐⭐ - Using DEX and aggregators with Chain 138 coins/tokens; routing for DEXs; token-aggregation API, DODO PMM, swap–bridge–swap flows. +- **[CHAIN138_NATIVE_DODO_READ_SURFACE_VERIFIER.md](CHAIN138_NATIVE_DODO_READ_SURFACE_VERIFIER.md)** — Native DODO PMM / D3MM read-surface verifier for Chain 138 (`getVaultReserve`, `getPMMStateForCall`, `getTokenReserve`, `querySellTokens`) **Chain 138 / Wallets (overview first; all repos in ~/projects/):** - **[CHAIN138_WALLET_REPOSITORIES.md](CHAIN138_WALLET_REPOSITORIES.md)** ⭐⭐⭐ - **Canonical layout:** metamask-integration, LedgerLive, app-ethereum, TrustWallet-Integration each in `~/projects/`. All items **Yes / Completed.** diff --git a/scripts/deployment/sync-mev-chain138-token-symbols.sh b/scripts/deployment/sync-mev-chain138-token-symbols.sh new file mode 100755 index 00000000..c91af504 --- /dev/null +++ b/scripts/deployment/sync-mev-chain138-token-symbols.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +set -euo pipefail + +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +REGISTRY_JSON="${PROJECT_ROOT}/config/smart-contracts-master.json" +DB_URL="${DB_URL:-postgresql://postgres:postgres@127.0.0.1:5434/mev}" +CHAIN_ID="${CHAIN_ID:-138}" +APPLY="${APPLY:-0}" + +usage() { + cat <<'EOF' +Usage: sync-mev-chain138-token-symbols.sh [--db-url URL] [--chain-id ID] [--apply] + +Builds a symbol map for the live Chain 138 MEV token set from canonical repo sources +and prints or applies UPDATE statements against the MEV Postgres database. +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --db-url) + DB_URL="$2" + shift 2 + ;; + --chain-id) + CHAIN_ID="$2" + shift 2 + ;; + --apply) + APPLY=1 + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown arg: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +sql="$( + python3 - "$REGISTRY_JSON" "$CHAIN_ID" <<'PY' +import json, sys +from pathlib import Path + +registry = json.loads(Path(sys.argv[1]).read_text()) +chain_id = str(sys.argv[2]) +contracts = registry["chains"][chain_id]["contracts"] + +mapping = { + contracts["cUSDT"].lower(): "cUSDT", + contracts["cUSDC"].lower(): "cUSDC", + contracts["WETH10"].lower(): "WETH10", + "0x004b63a7b5b0e06f6bb6adb4a5f9f590bf3182d1": "USDT", + "0x798f6762bb40d6801a593459d08f890603d3979c": "USDC", +} + +for address, symbol in mapping.items(): + safe_symbol = symbol.replace("'", "''") + print( + f"UPDATE tokens SET symbol = '{safe_symbol}' " + f"WHERE chain_id = {chain_id} AND LOWER(token_id) = LOWER('{address}');" + ) +PY +)" + +if [[ "${APPLY}" != "1" ]]; then + printf '%s\n' "$sql" + exit 0 +fi + +psql "$DB_URL" -v ON_ERROR_STOP=1 -c "$sql" diff --git a/scripts/verify/check-chain138-native-dodo-read-surfaces.sh b/scripts/verify/check-chain138-native-dodo-read-surfaces.sh new file mode 100755 index 00000000..e8346b55 --- /dev/null +++ b/scripts/verify/check-chain138-native-dodo-read-surfaces.sh @@ -0,0 +1,243 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Verify the native DODO read surfaces on Chain 138. +# +# Scope: +# - DODO PMM pools: +# * getVaultReserve() +# * getPMMStateForCall() +# - D3MM pilot pool: +# * getTokenReserve(address) +# * querySellTokens(address,address,uint256) +# +# The script prints explicit PASS / FAIL lines per surface and exits non-zero +# if any required surface is missing or returns an empty / invalid result. +# +# Default RPC comes from the project env when available, then falls back to the +# standard Chain 138 core RPC used elsewhere in the repo. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +if [[ -f "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" ]]; then + # shellcheck source=/dev/null + source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" +fi + +require_command() { + command -v "$1" >/dev/null 2>&1 || { + printf '[fail] missing required command: %s\n' "$1" >&2 + exit 1 + } +} + +require_command cast +require_command timeout + +RPC_URL="${RPC_URL_138:-${CHAIN138_RPC_URL:-${CHAIN138_RPC:-http://192.168.11.211:8545}}}" +PROBE_AMOUNT_PMM="${CHAIN138_DODO_PMM_PROBE_AMOUNT:-1000000}" +PROBE_AMOUNT_D3="${CHAIN138_D3_PROBE_SELL_AMOUNT:-100000000000000000}" +TRADER_ADDRESS="${CHAIN138_DODO_READ_TRADER:-${DEPLOYER_ADDRESS:-0x4A666F96fC8764181194447A7dFdb7d471b301C8}}" + +PMM_POOLS=( + "cUSDT/cUSDC|0x9e89bAe009adf128782E19e8341996c596ac40dC|0x93E66202A11B1772E55407B32B44e5Cd8eda7f22|0xf22258f57794CC8E06237084b353Ab30fFfa640b|6|6" + "cUSDT/USDT|0x866Cb44b59303d8dc5f4F9E3E7A8e8b0bf238d66|0x93E66202A11B1772E55407B32B44e5Cd8eda7f22|0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1|6|6" + "cUSDC/USDC|0xc39B7D0F40838cbFb54649d327f49a6DAC964062|0xf22258f57794CC8E06237084b353Ab30fFfa640b|0x71D6687F38b93CCad569Fa6352c876eea967201b|6|6" +) + +D3_POOL="0x6550a3a59070061a262a893a1d6f3f490afFDBDA" +D3_WETH10="0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f" +D3_USDT="0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1" + +pass_count=0 +fail_count=0 +LAST_CALL_OUTPUT="" + +pass() { + printf '[PASS] %s\n' "$*" + pass_count=$((pass_count + 1)) +} + +fail() { + printf '[FAIL] %s\n' "$*" >&2 + fail_count=$((fail_count + 1)) +} + +run_cast_call() { + local label="$1" + shift + + LAST_CALL_OUTPUT="" + + local output + if ! output="$(timeout 20s cast call --rpc-url "${RPC_URL}" "$@" 2>&1)"; then + fail "${label}: call failed" + printf ' %s\n' "${output}" >&2 + LAST_CALL_OUTPUT="${output}" + return 1 + fi + + if [[ -z "${output}" ]]; then + fail "${label}: empty response" + return 1 + fi + + printf '%s\n' "${output}" + LAST_CALL_OUTPUT="${output}" +} + +parse_first_uint() { + awk 'NR==1 { print $1 }' +} + +check_gt_zero() { + local label="$1" + local value="$2" + if [[ ! "${value}" =~ ^[0-9]+$ ]] || (( value <= 0 )); then + fail "${label}: expected > 0, got ${value:-}" + return 1 + fi + pass "${label}: ${value}" +} + +check_pmm_pool() { + local label="$1" + local pool="$2" + local base="$3" + local quote="$4" + + printf '\n=== PMM: %s ===\n' "${label}" + printf 'pool=%s\n' "${pool}" + + local reserves + if ! run_cast_call "${label} getVaultReserve" "${pool}" 'getVaultReserve()(uint256,uint256)'; then + return 1 + fi + reserves="${LAST_CALL_OUTPUT}" + + local reserve_base reserve_quote + reserve_base="$(printf '%s\n' "${reserves}" | sed -n '1p' | parse_first_uint)" + reserve_quote="$(printf '%s\n' "${reserves}" | sed -n '2p' | parse_first_uint)" + + check_gt_zero "${label} getVaultReserve base" "${reserve_base}" || true + check_gt_zero "${label} getVaultReserve quote" "${reserve_quote}" || true + + local state + if ! run_cast_call "${label} getPMMStateForCall" "${pool}" 'getPMMStateForCall()(uint256,uint256,uint256,uint256,uint256,uint256,uint256)'; then + return 1 + fi + state="${LAST_CALL_OUTPUT}" + + local state_i state_k state_b state_q state_b0 state_q0 state_r + state_i="$(printf '%s\n' "${state}" | sed -n '1p' | parse_first_uint)" + state_k="$(printf '%s\n' "${state}" | sed -n '2p' | parse_first_uint)" + state_b="$(printf '%s\n' "${state}" | sed -n '3p' | parse_first_uint)" + state_q="$(printf '%s\n' "${state}" | sed -n '4p' | parse_first_uint)" + state_b0="$(printf '%s\n' "${state}" | sed -n '5p' | parse_first_uint)" + state_q0="$(printf '%s\n' "${state}" | sed -n '6p' | parse_first_uint)" + state_r="$(printf '%s\n' "${state}" | sed -n '7p' | parse_first_uint)" + + check_gt_zero "${label} getPMMStateForCall i" "${state_i}" || true + check_gt_zero "${label} getPMMStateForCall B" "${state_b}" || true + check_gt_zero "${label} getPMMStateForCall Q" "${state_q}" || true + if [[ "${state_k}" =~ ^[0-9]+$ ]]; then + pass "${label} getPMMStateForCall K: ${state_k}" + else + fail "${label} getPMMStateForCall K: invalid value ${state_k:-}" + fi + if [[ "${state_r}" =~ ^[0-9]+$ ]]; then + pass "${label} getPMMStateForCall R: ${state_r}" + else + fail "${label} getPMMStateForCall R: invalid value ${state_r:-}" + fi + printf ' stateB0=%s stateQ0=%s\n' "${state_b0:-}" "${state_q0:-}" + + local query_base query_quote + if ! run_cast_call "${label} querySellBase" "${pool}" "querySellBase(address,uint256)(uint256,uint256)" "${TRADER_ADDRESS}" "${PROBE_AMOUNT_PMM}"; then + return 1 + fi + query_base="${LAST_CALL_OUTPUT}" + if ! run_cast_call "${label} querySellQuote" "${pool}" "querySellQuote(address,uint256)(uint256,uint256)" "${TRADER_ADDRESS}" "${PROBE_AMOUNT_PMM}"; then + return 1 + fi + query_quote="${LAST_CALL_OUTPUT}" + + local base_out quote_out + base_out="$(printf '%s\n' "${query_base}" | sed -n '1p' | parse_first_uint)" + quote_out="$(printf '%s\n' "${query_quote}" | sed -n '1p' | parse_first_uint)" + + check_gt_zero "${label} querySellBase out" "${base_out}" || true + check_gt_zero "${label} querySellQuote out" "${quote_out}" || true + + printf ' tokens=%s/%s\n' "${base}" "${quote}" +} + +check_d3_pool() { + printf '\n=== D3MM: canonical WETH10/USDT pilot ===\n' + printf 'pool=%s\n' "${D3_POOL}" + + local weth_reserve usdt_reserve + if ! run_cast_call "D3MM getTokenReserve(WETH10)" "${D3_POOL}" 'getTokenReserve(address)(uint256)' "${D3_WETH10}"; then + return 1 + fi + weth_reserve="${LAST_CALL_OUTPUT}" + if ! run_cast_call "D3MM getTokenReserve(USDT)" "${D3_POOL}" 'getTokenReserve(address)(uint256)' "${D3_USDT}"; then + return 1 + fi + usdt_reserve="${LAST_CALL_OUTPUT}" + + local weth_raw usdt_raw + weth_raw="$(printf '%s\n' "${weth_reserve}" | parse_first_uint)" + usdt_raw="$(printf '%s\n' "${usdt_reserve}" | parse_first_uint)" + check_gt_zero "D3MM getTokenReserve WETH10" "${weth_raw}" || true + check_gt_zero "D3MM getTokenReserve USDT" "${usdt_raw}" || true + + local quote + if ! run_cast_call "D3MM querySellTokens(WETH10->USDT)" "${D3_POOL}" 'querySellTokens(address,address,uint256)(uint256,uint256,uint256,uint256,uint256)' "${D3_WETH10}" "${D3_USDT}" "${PROBE_AMOUNT_D3}"; then + return 1 + fi + quote="${LAST_CALL_OUTPUT}" + + local pay_from receive_to vusd_amount swap_fee mt_fee + pay_from="$(printf '%s\n' "${quote}" | sed -n '1p' | parse_first_uint)" + receive_to="$(printf '%s\n' "${quote}" | sed -n '2p' | parse_first_uint)" + vusd_amount="$(printf '%s\n' "${quote}" | sed -n '3p' | parse_first_uint)" + swap_fee="$(printf '%s\n' "${quote}" | sed -n '4p' | parse_first_uint)" + mt_fee="$(printf '%s\n' "${quote}" | sed -n '5p' | parse_first_uint)" + + if [[ "${pay_from}" != "${PROBE_AMOUNT_D3}" ]]; then + fail "D3MM querySellTokens payFrom mismatch: expected ${PROBE_AMOUNT_D3}, got ${pay_from:-}" + else + pass "D3MM querySellTokens payFrom: ${pay_from}" + fi + check_gt_zero "D3MM querySellTokens receiveTo" "${receive_to}" || true + printf ' vusd=%s swapFee=%s mtFee=%s\n' "${vusd_amount:-}" "${swap_fee:-}" "${mt_fee:-}" +} + +main() { + printf 'Chain 138 native DODO read-surface verifier\n' + printf 'rpc=%s\n' "${RPC_URL}" + printf 'probeAmountPmm=%s\n' "${PROBE_AMOUNT_PMM}" + printf 'probeAmountD3=%s\n' "${PROBE_AMOUNT_D3}" + printf 'trader=%s\n' "${TRADER_ADDRESS}" + + local row + for row in "${PMM_POOLS[@]}"; do + IFS='|' read -r label pool base quote _ _ <<<"${row}" + if ! check_pmm_pool "${label}" "${pool}" "${base}" "${quote}"; then + : + fi + done + + if ! check_d3_pool; then + : + fi + + printf '\nSummary: %s passed, %s failed.\n' "${pass_count}" "${fail_count}" + if (( fail_count > 0 )); then + exit 1 + fi +} + +main "$@"