#!/usr/bin/env bash # Verify that the public token-aggregation/report API is reachable and not misrouted to Blockscout. # Usage: bash scripts/verify/check-public-report-api.sh [base_url] # base_url: Optional API base, defaults to https://explorer.d-bis.org # # Exit codes: # 0 = all expected endpoints returned token-aggregation-style JSON # 1 = one or more endpoints returned the wrong shape or were unreachable # Set SKIP_EXIT=1 to print diagnostics but exit 0. # Set KEEP_GOING=1 to keep checking every endpoint before exiting non-zero. # Set SKIP_BRIDGE_ROUTES=0 to assert /api/v1/bridge/routes payload shape. # Set SKIP_BRIDGE_PREFLIGHT=0 to assert /api/v1/bridge/preflight payload shape. set -euo pipefail BASE_URL="${1:-https://explorer.d-bis.org}" SKIP_EXIT="${SKIP_EXIT:-0}" KEEP_GOING="${KEEP_GOING:-0}" HAD_FAILURE=0 log() { printf '%s\n' "$*"; } ok() { printf '[OK] %s\n' "$*"; } warn() { printf '[WARN] %s\n' "$*"; } # Use apex /api/v1 first; if misrouted to Blockscout, try /token-aggregation/api/v1 (nginx layouts differ). detect_token_aggregation_prefix() { local path="/api/v1/networks" local prefix url response status body for prefix in "" "/token-aggregation"; do url="${BASE_URL%/}${prefix}${path}" response="$(curl -sSL --max-time 15 -w $'\n%{http_code}' "$url" 2>/dev/null)" || continue status="$(printf '%s' "$response" | tail -n 1)" body="$(printf '%s' "$response" | sed '$d')" if printf '%s' "$body" | jq -e 'type == "object" and has("message") and has("result") and has("status")' >/dev/null 2>&1; then continue fi if printf '%s' "$body" | jq -e 'type == "object" and (.networks | type == "array")' >/dev/null 2>&1; then printf '%s\n' "$prefix" return 0 fi done printf '%s\n' "" } fail() { printf '[FAIL] %s\n' "$*" HAD_FAILURE=1 if [[ "$SKIP_EXIT" != "1" && "$KEEP_GOING" != "1" ]]; then exit 1 fi } check_json_shape() { local name="$1" local url="$2" local jq_expr="$3" local expected_desc="$4" local response local body local status if ! response="$(curl -sSL --max-time 20 -w $'\n%{http_code}' "$url" 2>/dev/null)"; then fail "$name request failed: $url" return 0 fi status="$(printf '%s' "$response" | tail -n 1)" body="$(printf '%s' "$response" | sed '$d')" if printf '%s' "$body" | jq -e 'type == "object" and has("message") and has("result") and has("status")' >/dev/null 2>&1; then fail "$name is returning Blockscout-style JSON (HTTP $status) instead of token-aggregation JSON. See docs/04-configuration/TOKEN_AGGREGATION_REPORT_API_RUNBOOK.md" return 0 fi if printf '%s' "$body" | jq -e 'type == "object" and has("error")' >/dev/null 2>&1; then local api_error api_error="$(printf '%s' "$body" | jq -r '.error' 2>/dev/null || echo 'unknown error')" fail "$name returned token-aggregation error payload (HTTP $status): $api_error" return 0 fi if printf '%s' "$body" | jq -e "$jq_expr" >/dev/null 2>&1; then ok "$name healthy ($expected_desc, HTTP $status)" return 0 fi local summary summary="$(printf '%s' "$body" | head -c 240 | tr '\n' ' ')" fail "$name returned unexpected payload (HTTP $status). Expected $expected_desc. Sample: $summary" } log "=== Public report API check ===" log "Base URL: $BASE_URL" TA_PREFIX="$(detect_token_aggregation_prefix)" if [[ -n "$TA_PREFIX" ]]; then log "Using token-aggregation path prefix: ${TA_PREFIX}/api/v1/" else log "Using token-aggregation path prefix: /api/v1/ (default; may still be Blockscout if both layouts fail shape test)" fi log "" check_json_shape \ "token-list" \ "${BASE_URL%/}${TA_PREFIX}/api/v1/report/token-list?chainId=138" \ 'type == "object" and (.tokens | type == "array")' \ 'object with .tokens[]' check_json_shape \ "coingecko report" \ "${BASE_URL%/}${TA_PREFIX}/api/v1/report/coingecko?chainId=138" \ 'type == "object"' \ 'token-aggregation report JSON object' check_json_shape \ "cmc report" \ "${BASE_URL%/}${TA_PREFIX}/api/v1/report/cmc?chainId=138" \ 'type == "object"' \ 'token-aggregation report JSON object' check_json_shape \ "networks" \ "${BASE_URL%/}${TA_PREFIX}/api/v1/networks" \ 'type == "object" and (.networks | type == "array")' \ 'object with .networks[]' # Bridge routes (requires token-aggregation build with GET /api/v1/bridge/routes). Off by default until edge is deployed. if [[ "${SKIP_BRIDGE_ROUTES:-1}" != "1" ]]; then check_json_shape \ "bridge-routes" \ "${BASE_URL%/}${TA_PREFIX}/api/v1/bridge/routes" \ 'type == "object" and (.chain138Bridges | type == "object") and (.routes | type == "object")' \ 'object with .chain138Bridges and .routes' fi # GRU preflight (shape only; does not require all pairs to be runtime-ready). Off by default until edge is deployed. if [[ "${SKIP_BRIDGE_PREFLIGHT:-1}" != "1" ]]; then check_json_shape \ "bridge-preflight" \ "${BASE_URL%/}${TA_PREFIX}/api/v1/bridge/preflight" \ 'type == "object" and (.gruTransport | type == "object") and (.gruTransport.summary.transportPairs | type == "number") and (.gruTransport.blockedPairs | type == "array")' \ 'object with .gruTransport.summary and .gruTransport.blockedPairs[]' fi log "" if (( HAD_FAILURE > 0 )); then if [[ "$SKIP_EXIT" == "1" ]]; then warn "SKIP_EXIT=1 set: non-healthy endpoints were reported without failing." elif [[ "$KEEP_GOING" == "1" ]]; then exit 1 fi elif [[ "$SKIP_EXIT" == "1" ]]; then warn "SKIP_EXIT=1 set: non-healthy endpoints were reported without failing." else ok "Public report API endpoints look healthy." fi