Files
proxmox/scripts/verify/check-mainnet-pmm-peg-bot-readiness.sh
defiQUG dbd517b279 Sync workspace: config, docs, scripts, CI, operator rules, and submodule pointers.
- Update dbis_core, cross-chain-pmm-lps, explorer-monorepo, metamask-integration, pr-workspace/chains
- Omit embedded publish git dirs and empty placeholders from index

Made-with: Cursor
2026-04-12 06:12:20 -07:00

228 lines
7.7 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# Mainnet PMM peg + bot readiness checks for recorded chain-1 pools.
#
# - Confirms RPC is Ethereum mainnet (chain id 1)
# - Reads pools from cross-chain-pmm-lps/config/deployment-status.json
# (pmmPools hub rows + pmmPoolsVolatile for cW/TRUU when poolAddress is set)
# - Verifies DODO_PMM_INTEGRATION_MAINNET mapping and non-zero reserves
# - For USD-class cW vs USDC/USDT pools, compares reserve imbalance (bps) to peg-bands.json
#
# Env:
# ETHEREUM_MAINNET_RPC, DODO_PMM_INTEGRATION_MAINNET (required)
# MIN_POOL_RESERVE_RAW — minimum per-leg reserve (default: 1)
# PMM_TRUU_BASE_TOKEN, PMM_TRUU_QUOTE_TOKEN — optional extra pair to verify (ERC-20 addresses)
# SKIP_EXIT=1 — exit 0 even when checks fail (reporting mode)
#
# See: docs/03-deployment/MAINNET_PMM_TRUU_CWUSD_PEG_AND_BOT_RUNBOOK.md
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
STATUS_JSON="${REPO_ROOT}/cross-chain-pmm-lps/config/deployment-status.json"
PEG_JSON="${REPO_ROOT}/cross-chain-pmm-lps/config/peg-bands.json"
source "${REPO_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1 || true
require_cmd() {
command -v "$1" >/dev/null 2>&1 || {
echo "[fail] missing required command: $1" >&2
exit 1
}
}
require_cmd cast
require_cmd jq
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
INTEGRATION="${DODO_PMM_INTEGRATION_MAINNET:-}"
MIN_POOL_RESERVE_RAW="${MIN_POOL_RESERVE_RAW:-1}"
SKIP_EXIT="${SKIP_EXIT:-0}"
if [[ -z "$RPC_URL" || -z "$INTEGRATION" ]]; then
echo "[fail] ETHEREUM_MAINNET_RPC and DODO_PMM_INTEGRATION_MAINNET are required" >&2
exit 1
fi
if [[ ! -f "$STATUS_JSON" ]]; then
echo "[fail] missing $STATUS_JSON" >&2
exit 1
fi
if [[ ! -f "$PEG_JSON" ]]; then
echo "[fail] missing $PEG_JSON" >&2
exit 1
fi
failures=0
warnings=0
normal_max_bps="$(jq -r '.usdPegged.normalBps.max // 25' "$PEG_JSON")"
circuit_bps="$(jq -r '.usdPegged.circuitBreakBps // 200' "$PEG_JSON")"
is_usd_pegged_base() {
local sym="$1"
jq -e --arg s "$sym" '.usdPegged.tokens | index($s) != null' "$PEG_JSON" >/dev/null 2>&1
}
chain_id="$(cast chain-id --rpc-url "$RPC_URL" 2>/dev/null | head -1 | tr -d '\r' || true)"
if [[ "$chain_id" != "1" ]]; then
echo "[fail] expected eth_chainId 1, got '${chain_id:-unknown}'" >&2
exit 1
fi
echo "=== Mainnet PMM peg / bot readiness ==="
echo "RPC: $RPC_URL"
echo "Integration: $INTEGRATION"
echo "Min reserve (raw per leg): $MIN_POOL_RESERVE_RAW"
echo "USD peg bands (from peg-bands.json): normal max ${normal_max_bps} bps, circuit ${circuit_bps} bps"
echo
check_pool_row() {
local base_sym="$1"
local quote_sym="$2"
local expected_pool="$3"
local label="${base_sym}/${quote_sym}"
local base_addr
base_addr="$(jq -r --arg b "$base_sym" '.chains["1"].cwTokens[$b] // empty' "$STATUS_JSON")"
local quote_addr
if [[ "$quote_sym" == "TRUU" ]]; then
quote_addr="$(jq -r '.chains["1"].anchorAddresses.TRUU // empty' "$STATUS_JSON")"
else
quote_addr="$(jq -r --arg q "$quote_sym" '.chains["1"].anchorAddresses[$q] // empty' "$STATUS_JSON")"
fi
if [[ -z "$base_addr" || -z "$quote_addr" ]]; then
echo "[fail] $label — could not resolve token address (base=$base_addr quote=$quote_addr)" >&2
failures=$((failures + 1))
return
fi
local mapped
mapped="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$base_addr" "$quote_addr" --rpc-url "$RPC_URL" 2>/dev/null | awk '{print tolower($1)}' || true)"
local exp_lower
exp_lower="$(printf '%s' "$expected_pool" | tr '[:upper:]' '[:lower:]')"
if [[ "$mapped" != "$exp_lower" ]]; then
echo "[fail] $label — integration mapping mismatch: expected $expected_pool got ${mapped:-empty}" >&2
failures=$((failures + 1))
return
fi
local reserve_output
reserve_output="$(cast call "$expected_pool" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL" 2>/dev/null || true)"
local r0 r1
r0="$(printf '%s\n' "$reserve_output" | sed -n '1p' | awk '{print $1}')"
r1="$(printf '%s\n' "$reserve_output" | sed -n '2p' | awk '{print $1}')"
if [[ -z "$r0" || -z "$r1" ]]; then
echo "[fail] $label — could not read reserves" >&2
failures=$((failures + 1))
return
fi
if [[ "$r0" -lt "$MIN_POOL_RESERVE_RAW" || "$r1" -lt "$MIN_POOL_RESERVE_RAW" ]]; then
echo "[fail] $label — reserves below MIN_POOL_RESERVE_RAW ($r0 / $r1)" >&2
failures=$((failures + 1))
return
fi
local imb_bps=0
local peg_info=""
local peg_circuit_failed=0
if is_usd_pegged_base "$base_sym" && [[ "$quote_sym" == "USDC" || "$quote_sym" == "USDT" ]]; then
local max_r
if [[ "$r0" -ge "$r1" ]]; then
max_r="$r0"
else
max_r="$r1"
fi
if [[ "$max_r" -eq 0 ]]; then
imb_bps=10000
else
local diff
if [[ "$r0" -ge "$r1" ]]; then
diff=$((r0 - r1))
else
diff=$((r1 - r0))
fi
imb_bps=$((10000 * diff / max_r))
fi
peg_info="imbalance_bps=${imb_bps}"
if [[ "$imb_bps" -gt "$circuit_bps" ]]; then
echo "[fail] $label — reserve imbalance ${imb_bps} bps exceeds circuit ${circuit_bps} bps (r0=$r0 r1=$r1)" >&2
failures=$((failures + 1))
peg_circuit_failed=1
elif [[ "$imb_bps" -gt "$normal_max_bps" ]]; then
echo "[warn] $label — reserve imbalance ${imb_bps} bps exceeds normal band max ${normal_max_bps} bps (r0=$r0 r1=$r1)" >&2
warnings=$((warnings + 1))
fi
fi
if [[ "$peg_circuit_failed" -eq 0 ]]; then
echo "- $label pool=$expected_pool reserves=$r0/$r1 ${peg_info:+$peg_info}"
fi
}
while IFS= read -r line; do
[[ -z "$line" ]] && continue
base_sym="${line%%|*}"
rest="${line#*|}"
quote_sym="${rest%%|*}"
pool_addr="${rest##*|}"
check_pool_row "$base_sym" "$quote_sym" "$pool_addr"
done < <(jq -r '.chains["1"].pmmPools[]? | "\(.base)|\(.quote)|\(.poolAddress)"' "$STATUS_JSON" 2>/dev/null || true)
while IFS= read -r line; do
[[ -z "$line" ]] && continue
base_sym="${line%%|*}"
rest="${line#*|}"
quote_sym="${rest%%|*}"
pool_addr="${rest##*|}"
check_pool_row "$base_sym" "$quote_sym" "$pool_addr"
done < <(jq -r '
.chains["1"].pmmPoolsVolatile[]?
| select(
(.poolAddress // "") != ""
and ((.poolAddress | ascii_downcase) != "0x0000000000000000000000000000000000000000")
)
| "\(.base)|\(.quote)|\(.poolAddress)"
' "$STATUS_JSON" 2>/dev/null || true)
if [[ -n "${PMM_TRUU_BASE_TOKEN:-}" && -n "${PMM_TRUU_QUOTE_TOKEN:-}" ]]; then
echo
echo "=== Optional TRUU PMM pair (PMM_TRUU_BASE_TOKEN / PMM_TRUU_QUOTE_TOKEN) ==="
tpool="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$PMM_TRUU_BASE_TOKEN" "$PMM_TRUU_QUOTE_TOKEN" --rpc-url "$RPC_URL" 2>/dev/null | awk '{print $1}')"
if [[ "$tpool" == "0x0000000000000000000000000000000000000000" ]]; then
echo "[warn] No pool registered for TRUU pair (base=$PMM_TRUU_BASE_TOKEN quote=$PMM_TRUU_QUOTE_TOKEN)" >&2
warnings=$((warnings + 1))
else
tres="$(cast call "$tpool" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL" 2>/dev/null || true)"
tr0="$(printf '%s\n' "$tres" | sed -n '1p' | awk '{print $1}')"
tr1="$(printf '%s\n' "$tres" | sed -n '2p' | awk '{print $1}')"
if [[ -z "$tr0" || -z "$tr1" || "$tr0" -lt "$MIN_POOL_RESERVE_RAW" || "$tr1" -lt "$MIN_POOL_RESERVE_RAW" ]]; then
echo "[warn] TRUU PMM pool $tpool has weak or unreadable reserves ($tr0 / $tr1)" >&2
warnings=$((warnings + 1))
else
echo "- TRUU PMM pool=$tpool reserves=$tr0/$tr1"
fi
fi
fi
echo
if (( failures > 0 )); then
echo "[fail] $failures hard failure(s), $warnings warning(s)." >&2
if [[ "$SKIP_EXIT" == "1" ]]; then
exit 0
fi
exit 1
fi
if (( warnings > 0 )); then
echo "[ok] checks passed with $warnings warning(s)."
exit 0
fi
echo "[ok] all checks passed."
exit 0