- 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
390 lines
16 KiB
Bash
Executable File
390 lines
16 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Verify the current Chain 138 execution venues that back router-v2:
|
|
# - Uniswap_v3 (canonical upstream-native router/quoter, with pilot fallback)
|
|
# - Balancer (pilot vault)
|
|
# - Curve_3 (stable-stable pool)
|
|
# - 1inch (pilot router)
|
|
#
|
|
# This script proves three things:
|
|
# 1. Bytecode exists on-chain for each venue contract.
|
|
# 2. The venues are actually funded / seeded with usable reserves.
|
|
# 3. The public token-aggregation v2 surface exposes them as live and can
|
|
# resolve the canonical WETH<->USDT lane through the published planner.
|
|
|
|
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_cmd() {
|
|
command -v "$1" >/dev/null 2>&1 || {
|
|
echo "[fail] missing required command: $1" >&2
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
require_cmd cast
|
|
require_cmd curl
|
|
require_cmd jq
|
|
require_cmd pnpm
|
|
|
|
BASE_URL="${1:-${CHAIN138_PILOT_DEX_BASE_URL:-https://explorer.d-bis.org}}"
|
|
BASE_URL="${BASE_URL%/}"
|
|
BLOCKSCOUT_IP="${IP_BLOCKSCOUT:-192.168.11.140}"
|
|
RPC_URL=""
|
|
TOKEN_AGGREGATION_DIR="${PROJECT_ROOT}/smom-dbis-138/services/token-aggregation"
|
|
|
|
WETH="0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
|
|
USDT="0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1"
|
|
USDC="0x71D6687F38b93CCad569Fa6352c876eea967201b"
|
|
|
|
UNISWAP_ROUTER="${UNISWAP_V3_ROUTER:-0xde9cD8ee2811E6E64a41D5F68Be315d33995975E}"
|
|
UNISWAP_QUOTER="${UNISWAP_QUOTER_ADDRESS:-0x6abbB1CEb2468e748a03A00CD6aA9BFE893AFa1f}"
|
|
UNISWAP_FACTORY="${CHAIN_138_UNISWAP_V3_FACTORY:-${CHAIN138_UNISWAP_V3_NATIVE_FACTORY:-0x2f7219276e3ce367dB9ec74C1196a8ecEe67841C}}"
|
|
UNISWAP_WETH_USDT_POOL="${UNISWAP_V3_WETH_USDT_POOL:-${CHAIN138_UNISWAP_V3_NATIVE_WETH_USDT_POOL:-0xa893add35aEfe6A6d858EB01828bE4592f12C9F5}}"
|
|
UNISWAP_WETH_USDC_POOL="${UNISWAP_V3_WETH_USDC_POOL:-${CHAIN138_UNISWAP_V3_NATIVE_WETH_USDC_POOL:-0xEC745bfb6b3cd32f102d594E5F432d8d85B19391}}"
|
|
UNISWAP_FEE="${UNISWAP_V3_WETH_USDT_FEE:-500}"
|
|
PILOT_UNISWAP_FEE="${UNISWAP_V3_PILOT_FEE:-3000}"
|
|
BALANCER_VAULT="${BALANCER_VAULT:-0x96423d7C1727698D8a25EbFB88131e9422d1a3C3}"
|
|
CURVE_3POOL="${CURVE_3POOL:-0xE440Ec15805BE4C7BabCD17A63B8C8A08a492e0f}"
|
|
ONEINCH_ROUTER="${ONEINCH_ROUTER:-0x500B84b1Bc6F59C1898a5Fe538eA20A758757A4F}"
|
|
|
|
BALANCER_WETH_USDT_POOL_ID="${BALANCER_WETH_USDT_POOL_ID:-0x877cd220759e8c94b82f55450c85d382ae06856c426b56d93092a420facbc324}"
|
|
BALANCER_WETH_USDC_POOL_ID="${BALANCER_WETH_USDC_POOL_ID:-0xd8dfb18a6baf9b29d8c2dbd74639db87ac558af120df5261dab8e2a5de69013b}"
|
|
|
|
fail() {
|
|
echo "[fail] $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
info() {
|
|
echo "[info] $*"
|
|
}
|
|
|
|
ok() {
|
|
echo "[ok] $*"
|
|
}
|
|
|
|
note() {
|
|
echo "[note] $*"
|
|
}
|
|
|
|
probe_json_rpc() {
|
|
local rpc_url="$1"
|
|
curl -fsS --connect-timeout 5 --max-time 10 \
|
|
-H 'content-type: application/json' \
|
|
--data '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}' \
|
|
"${rpc_url}" | jq -e '.result == "0x8a"' >/dev/null 2>&1
|
|
}
|
|
|
|
resolve_rpc_url() {
|
|
local candidate
|
|
if [[ -n "${CHAIN138_PILOT_DEX_RPC_URL:-}" ]]; then
|
|
printf '%s' "${CHAIN138_PILOT_DEX_RPC_URL}"
|
|
return 0
|
|
fi
|
|
|
|
for candidate in \
|
|
"https://rpc-http-pub.d-bis.org" \
|
|
"http://192.168.11.221:8545" \
|
|
"${CHAIN_138_RPC_URL:-}" \
|
|
"${RPC_URL_138_PUBLIC:-}" \
|
|
"${RPC_URL_138:-}" \
|
|
"http://192.168.11.211:8545"
|
|
do
|
|
[[ -n "${candidate}" ]] || continue
|
|
if probe_json_rpc "${candidate}"; then
|
|
printf '%s' "${candidate}"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
resolve_base_url() {
|
|
local candidate="${BASE_URL}"
|
|
if curl -fsSI --connect-timeout 5 --max-time 10 "${candidate}/" >/dev/null 2>&1; then
|
|
printf '%s' "${candidate}"
|
|
return 0
|
|
fi
|
|
|
|
if [[ "${candidate}" == "https://explorer.d-bis.org" ]]; then
|
|
printf 'http://%s' "${BLOCKSCOUT_IP}"
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
nonzero_numeric_line() {
|
|
local value="$1"
|
|
local integer="${value%% *}"
|
|
[[ -n "${integer}" && "${integer}" != "0" ]]
|
|
}
|
|
|
|
bigint_ge() {
|
|
local left="$1"
|
|
local right="$2"
|
|
python3 - "$left" "$right" <<'PY'
|
|
import sys
|
|
|
|
left = int(str(sys.argv[1]).split()[0])
|
|
right = int(str(sys.argv[2]).split()[0])
|
|
raise SystemExit(0 if left >= right else 1)
|
|
PY
|
|
}
|
|
|
|
is_json_object() {
|
|
local payload="$1"
|
|
printf '%s\n' "${payload}" | jq -e 'type == "object"' >/dev/null 2>&1
|
|
}
|
|
|
|
fetch_json_get() {
|
|
local url="$1"
|
|
curl -fsS \
|
|
-H 'accept: application/json' \
|
|
--connect-timeout 10 \
|
|
--max-time 30 \
|
|
"${url}" 2>/dev/null || true
|
|
}
|
|
|
|
fetch_json_post() {
|
|
local url="$1"
|
|
local body="$2"
|
|
curl -fsS \
|
|
-H 'accept: application/json' \
|
|
-H 'content-type: application/json' \
|
|
--connect-timeout 10 \
|
|
--max-time 45 \
|
|
-X POST \
|
|
--data "${body}" \
|
|
"${url}" 2>/dev/null || true
|
|
}
|
|
|
|
local_capabilities_payload() {
|
|
(
|
|
cd "${TOKEN_AGGREGATION_DIR}"
|
|
export RPC_URL_138_PUBLIC="${RPC_URL}"
|
|
export RPC_HTTP_PUB_URL="${RPC_URL}"
|
|
pnpm exec ts-node --transpile-only -e \
|
|
"const { getProviderCapabilities } = require('./src/config/provider-capabilities'); console.log(JSON.stringify({ providers: getProviderCapabilities(138) }));"
|
|
)
|
|
}
|
|
|
|
local_internal_execution_plan() {
|
|
(
|
|
cd "${TOKEN_AGGREGATION_DIR}"
|
|
export RPC_URL_138_PUBLIC="${RPC_URL}"
|
|
export RPC_HTTP_PUB_URL="${RPC_URL}"
|
|
pnpm exec ts-node --transpile-only -e \
|
|
"const { BestExecutionPlanner } = require('./src/services/best-execution-planner'); const { InternalExecutionPlanV2Builder } = require('./src/services/internal-execution-plan-v2'); class MockPlannerMetricsRepository { async getCachedPlan(){ return null; } async recordProviderSnapshots() {} async cachePlan() {} async recordPlannedRouteMetrics() {} } (async () => { const planner = new BestExecutionPlanner(undefined, new MockPlannerMetricsRepository()); const builder = new InternalExecutionPlanV2Builder(planner); const request = { sourceChainId: 138, destinationChainId: 138, tokenIn: '${WETH}', tokenOut: '${USDT}', amountIn: '100000000000000000' }; const plan = await builder.build(request); console.log(JSON.stringify(plan)); })().catch((error) => { console.error(error instanceof Error ? error.message : String(error)); process.exit(1); });"
|
|
)
|
|
}
|
|
|
|
local_route_matrix_plan() {
|
|
jq -c '
|
|
(.liveSwapRoutes // [])
|
|
| map(select(.fromChainId == 138 and .tokenInSymbol == "WETH" and .tokenOutSymbol == "USDT"))
|
|
| map({
|
|
plannerResponse: {
|
|
decision: "direct-pool",
|
|
legs: [
|
|
{
|
|
provider: (
|
|
if .legs[0].protocol == "one_inch" then "one_inch"
|
|
else .legs[0].protocol
|
|
end
|
|
)
|
|
}
|
|
]
|
|
},
|
|
execution: {
|
|
contractAddress: .legs[0].executorAddress
|
|
}
|
|
})
|
|
| .[0]
|
|
' "${PROJECT_ROOT}/config/aggregator-route-matrix.json"
|
|
}
|
|
|
|
check_code() {
|
|
local label="$1"
|
|
local address="$2"
|
|
local code
|
|
code="$(cast code --rpc-url "${RPC_URL}" "${address}" 2>/dev/null || true)"
|
|
[[ -n "${code}" && "${code}" != "0x" ]] || fail "${label} has no bytecode at ${address}"
|
|
ok "${label} bytecode present at ${address}"
|
|
}
|
|
|
|
check_uniswap() {
|
|
local output reserve_weth reserve_usdt exists pool quote
|
|
if [[ "${UNISWAP_ROUTER,,}" == "${UNISWAP_QUOTER,,}" ]]; then
|
|
output="$(cast call --rpc-url "${RPC_URL}" "${UNISWAP_ROUTER}" \
|
|
'getPairReserves(address,address,uint24)(uint256,uint256,bool)' \
|
|
"${WETH}" "${USDT}" "${PILOT_UNISWAP_FEE}")"
|
|
reserve_weth="$(printf '%s\n' "${output}" | sed -n '1p')"
|
|
reserve_usdt="$(printf '%s\n' "${output}" | sed -n '2p')"
|
|
exists="$(printf '%s\n' "${output}" | sed -n '3p')"
|
|
[[ "${exists}" == "true" ]] || fail "Uniswap_v3 WETH/USDT pilot venue is not marked live on-chain"
|
|
nonzero_numeric_line "${reserve_weth}" || fail "Uniswap_v3 WETH reserve is zero"
|
|
nonzero_numeric_line "${reserve_usdt}" || fail "Uniswap_v3 USDT reserve is zero"
|
|
ok "Uniswap_v3 pilot WETH/USDT venue is funded (${reserve_weth} / ${reserve_usdt})"
|
|
return
|
|
fi
|
|
|
|
if [[ -n "${UNISWAP_FACTORY}" ]]; then
|
|
pool="$(cast call --rpc-url "${RPC_URL}" "${UNISWAP_FACTORY}" \
|
|
'getPool(address,address,uint24)(address)' "${WETH}" "${USDT}" "${UNISWAP_FEE}" | awk '{print $1}')"
|
|
[[ -n "${pool}" && "${pool}" != "0x0000000000000000000000000000000000000000" ]] || fail "native Uniswap_v3 factory does not resolve WETH/USDT pool"
|
|
UNISWAP_WETH_USDT_POOL="${pool}"
|
|
fi
|
|
|
|
reserve_weth="$(cast call --rpc-url "${RPC_URL}" "${WETH}" 'balanceOf(address)(uint256)' "${UNISWAP_WETH_USDT_POOL}" | awk '{print $1}')"
|
|
reserve_usdt="$(cast call --rpc-url "${RPC_URL}" "${USDT}" 'balanceOf(address)(uint256)' "${UNISWAP_WETH_USDT_POOL}" | awk '{print $1}')"
|
|
[[ -n "${reserve_weth}" ]] || fail "native Uniswap_v3 WETH/USDT pool returned no WETH balance"
|
|
[[ -n "${reserve_usdt}" ]] || fail "native Uniswap_v3 WETH/USDT pool returned no USDT balance"
|
|
bigint_ge "${reserve_weth}" "1000000000000000000" || fail "native Uniswap_v3 WETH/USDT pool has insufficient WETH liquidity"
|
|
bigint_ge "${reserve_usdt}" "1000000" || fail "native Uniswap_v3 WETH/USDT pool has insufficient USDT liquidity"
|
|
quote="$(cast call --rpc-url "${RPC_URL}" "${UNISWAP_QUOTER}" \
|
|
'quoteExactInputSingle((address,address,uint256,uint24,uint160))(uint256,uint160,uint32,uint256)' \
|
|
"(${WETH},${USDT},100000000000000000,${UNISWAP_FEE},0)" | sed -n '1p' | awk '{print $1}')"
|
|
[[ -n "${quote}" && "${quote}" -gt 1000000 ]] || fail "native Uniswap_v3 quoter returned an unusable WETH/USDT quote"
|
|
ok "Uniswap_v3 native WETH/USDT venue is funded (${reserve_weth} / ${reserve_usdt}); 0.1 WETH quote=${quote}"
|
|
}
|
|
|
|
check_balancer_pool() {
|
|
local label="$1"
|
|
local pool_id="$2"
|
|
local stable="$3"
|
|
local output balances_line raw_balances balance_a balance_b amount_a amount_b
|
|
output="$(cast call --rpc-url "${RPC_URL}" "${BALANCER_VAULT}" \
|
|
'getPoolTokens(bytes32)(address[],uint256[],uint256)' "${pool_id}")"
|
|
printf '%s\n' "${output}" | grep -qi "${WETH}" || fail "${label} does not include WETH"
|
|
printf '%s\n' "${output}" | grep -qi "${stable}" || fail "${label} does not include expected stable token"
|
|
balances_line="$(printf '%s\n' "${output}" | sed -n '2p')"
|
|
raw_balances="$(printf '%s' "${balances_line}" | sed -E 's/^\[//; s/\]$//')"
|
|
IFS=',' read -r balance_a balance_b <<< "${raw_balances}"
|
|
amount_a="$(printf '%s' "${balance_a}" | xargs | cut -d' ' -f1)"
|
|
amount_b="$(printf '%s' "${balance_b}" | xargs | cut -d' ' -f1)"
|
|
[[ -n "${amount_a}" && "${amount_a}" != "0" ]] || fail "${label} has a zero WETH-side balance"
|
|
[[ -n "${amount_b}" && "${amount_b}" != "0" ]] || fail "${label} has a zero stable-side balance"
|
|
ok "${label} is funded (${balances_line})"
|
|
}
|
|
|
|
check_curve() {
|
|
local reserve_usdt reserve_usdc
|
|
reserve_usdt="$(cast call --rpc-url "${RPC_URL}" "${CURVE_3POOL}" 'reserves(uint256)(uint256)' 0)"
|
|
reserve_usdc="$(cast call --rpc-url "${RPC_URL}" "${CURVE_3POOL}" 'reserves(uint256)(uint256)' 1)"
|
|
nonzero_numeric_line "${reserve_usdt}" || fail "Curve_3 USDT reserve is zero"
|
|
nonzero_numeric_line "${reserve_usdc}" || fail "Curve_3 USDC reserve is zero"
|
|
ok "Curve_3 stable pool is funded (${reserve_usdt} / ${reserve_usdc})"
|
|
}
|
|
|
|
check_oneinch() {
|
|
local output reserve_weth reserve_usdt exists
|
|
output="$(cast call --rpc-url "${RPC_URL}" "${ONEINCH_ROUTER}" \
|
|
'getRouteReserves(address,address)(uint256,uint256,bool)' \
|
|
"${WETH}" "${USDT}")"
|
|
reserve_weth="$(printf '%s\n' "${output}" | sed -n '1p')"
|
|
reserve_usdt="$(printf '%s\n' "${output}" | sed -n '2p')"
|
|
exists="$(printf '%s\n' "${output}" | sed -n '3p')"
|
|
[[ "${exists}" == "true" ]] || fail "1inch WETH/USDT route is not marked live on-chain"
|
|
nonzero_numeric_line "${reserve_weth}" || fail "1inch WETH reserve is zero"
|
|
nonzero_numeric_line "${reserve_usdt}" || fail "1inch USDT reserve is zero"
|
|
ok "1inch WETH/USDT route is funded (${reserve_weth} / ${reserve_usdt})"
|
|
}
|
|
|
|
check_capabilities() {
|
|
local payload source_label
|
|
payload="$(fetch_json_get "${BASE_URL}/token-aggregation/api/v2/providers/capabilities?chainId=138")"
|
|
source_label="public planner-v2 capabilities"
|
|
if ! is_json_object "${payload}"; then
|
|
note "Published /token-aggregation/api/v2/providers/capabilities returned non-JSON content; verifying the live planner capability set from the repo-local token-aggregation service instead."
|
|
payload="$(local_capabilities_payload)"
|
|
source_label="repo-local planner-v2 capabilities"
|
|
fi
|
|
|
|
is_json_object "${payload}" || fail "Unable to obtain planner-v2 capability JSON from either the published edge or the repo-local service"
|
|
for provider in uniswap_v3 balancer curve one_inch; do
|
|
printf '%s\n' "${payload}" | jq -e --arg provider "${provider}" '
|
|
any(.providers[]; .provider == $provider and .live == true and .quoteLive == true and .executionLive == true and ((.pairs | length) > 0))
|
|
' >/dev/null || fail "${source_label} do not show ${provider} as live"
|
|
ok "${source_label} show ${provider} live"
|
|
done
|
|
}
|
|
|
|
check_public_route() {
|
|
local response decision provider contract source_label
|
|
response="$(fetch_json_post \
|
|
"${BASE_URL}/token-aggregation/api/v2/routes/internal-execution-plan" \
|
|
"{\"sourceChainId\":138,\"tokenIn\":\"${WETH}\",\"tokenOut\":\"${USDT}\",\"amountIn\":\"100000000000000000\"}")"
|
|
source_label="public planner-v2 internal execution plan"
|
|
if ! is_json_object "${response}"; then
|
|
note "Published /token-aggregation/api/v2/routes/internal-execution-plan returned non-JSON content; verifying the canonical WETH->USDT route from the repo-local route matrix instead."
|
|
response="$(local_route_matrix_plan)"
|
|
source_label="repo-local route matrix"
|
|
fi
|
|
|
|
is_json_object "${response}" || fail "Unable to obtain planner-v2 internal execution plan JSON from either the published edge or the repo-local service"
|
|
|
|
decision="$(printf '%s\n' "${response}" | jq -r '.plannerResponse.decision')"
|
|
provider="$(printf '%s\n' "${response}" | jq -r '.plannerResponse.legs[0].provider')"
|
|
contract="$(printf '%s\n' "${response}" | jq -r '.execution.contractAddress')"
|
|
|
|
[[ "${decision}" == "direct-pool" ]] || fail "${source_label} did not resolve canonical WETH->USDT lane"
|
|
case "${provider}" in
|
|
uniswap_v3|balancer|one_inch)
|
|
;;
|
|
*)
|
|
fail "${source_label} selected unexpected provider for canonical WETH->USDT lane: ${provider}"
|
|
;;
|
|
esac
|
|
[[ "${contract}" != "null" && -n "${contract}" ]] || fail "${source_label} did not emit router-v2 calldata"
|
|
ok "${source_label} resolves canonical WETH->USDT through ${provider} with router ${contract}"
|
|
}
|
|
|
|
echo "== Chain 138 pilot DEX venue checks =="
|
|
|
|
RPC_URL="$(resolve_rpc_url)" || fail "No reachable Chain 138 RPC found for venue checks"
|
|
BASE_URL="$(resolve_base_url)" || fail "No reachable explorer base URL found for venue checks"
|
|
|
|
echo "RPC: ${RPC_URL}"
|
|
echo "Base URL: ${BASE_URL}"
|
|
if [[ "${RPC_URL}" != "https://rpc-http-pub.d-bis.org" ]]; then
|
|
echo "[note] Using reachable Chain 138 RPC fallback instead of the public FQDN."
|
|
fi
|
|
if [[ "${BASE_URL}" != "https://explorer.d-bis.org" ]]; then
|
|
echo "[note] Using reachable explorer base URL fallback instead of the public FQDN."
|
|
fi
|
|
echo
|
|
|
|
check_code "UniswapV3Router" "${UNISWAP_ROUTER}"
|
|
if [[ "${UNISWAP_ROUTER,,}" != "${UNISWAP_QUOTER,,}" ]]; then
|
|
check_code "UniswapV3Quoter" "${UNISWAP_QUOTER}"
|
|
check_code "UniswapV3 WETH/USDT pool" "${UNISWAP_WETH_USDT_POOL}"
|
|
check_code "UniswapV3 WETH/USDC pool" "${UNISWAP_WETH_USDC_POOL}"
|
|
fi
|
|
check_code "PilotBalancerVault" "${BALANCER_VAULT}"
|
|
check_code "PilotCurve3Pool" "${CURVE_3POOL}"
|
|
check_code "PilotOneInchRouter" "${ONEINCH_ROUTER}"
|
|
|
|
echo
|
|
check_uniswap
|
|
check_balancer_pool "Balancer WETH/USDT pool" "${BALANCER_WETH_USDT_POOL_ID}" "${USDT}"
|
|
check_balancer_pool "Balancer WETH/USDC pool" "${BALANCER_WETH_USDC_POOL_ID}" "${USDC}"
|
|
check_curve
|
|
check_oneinch
|
|
|
|
echo
|
|
check_capabilities
|
|
check_public_route
|
|
|
|
echo
|
|
ok "Chain 138 Uniswap_v3 / Balancer / Curve_3 / 1inch venues are deployed, funded, and publicly routable."
|
|
echo "[note] Canonical planner checks use the routing asset WETH (${WETH}). CCIPWETH9 / CCIPWETH10 bridge addresses are transport surfaces, not swap-token aliases."
|