feat: restore operator WIP — PMM JSON sync entrypoint, dotenv RPC trim + secrets, pool env alignment
- Resolve stash: merge load_deployment_env path with secure-secrets and CR/LF RPC strip - create-pmm-full-mesh-chain138.sh delegates to sync-chain138-pmm-pools-from-json.sh - env.additions.example: canonical PMM pool defaults (cUSDT/USDT per crosscheck) - Include Chain138 scripts, official mirror deploy scaffolding, and prior staged changes Made-with: Cursor
This commit is contained in:
307
scripts/deployment/sync-chain138-pmm-pools-from-json.sh
Executable file
307
scripts/deployment/sync-chain138-pmm-pools-from-json.sh
Executable file
@@ -0,0 +1,307 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
ENV_FILE="$SMOM_ROOT/.env"
|
||||
CONFIG_JSON="${POOL_CONFIG_JSON:-$SMOM_ROOT/config/chain138-pmm-pools.json}"
|
||||
ORIG_RPC_URL_138="${RPC_URL_138-}"
|
||||
ORIG_RPC_URL="${RPC_URL-}"
|
||||
ORIG_DODO_PMM_INTEGRATION_ADDRESS="${DODO_PMM_INTEGRATION_ADDRESS-}"
|
||||
ORIG_DODO_PMM_INTEGRATION="${DODO_PMM_INTEGRATION-}"
|
||||
ORIG_DODO_PMM_PROVIDER_ADDRESS="${DODO_PMM_PROVIDER_ADDRESS-}"
|
||||
ORIG_DODO_PMM_PROVIDER="${DODO_PMM_PROVIDER-}"
|
||||
ORIG_PRIVATE_KEY="${PRIVATE_KEY-}"
|
||||
ORIG_DRY_RUN="${DRY_RUN-}"
|
||||
ORIG_CHAIN_GAS_PRICE="${CHAIN_GAS_PRICE-}"
|
||||
ORIG_TX_TIMEOUT_SECONDS="${TX_TIMEOUT_SECONDS-}"
|
||||
ORIG_POST_CREATE_POLL_SECONDS="${POST_CREATE_POLL_SECONDS-}"
|
||||
ORIG_POST_CREATE_POLL_INTERVAL="${POST_CREATE_POLL_INTERVAL-}"
|
||||
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$ENV_FILE"
|
||||
set +a
|
||||
fi
|
||||
|
||||
[[ -n "$ORIG_RPC_URL_138" ]] && RPC_URL_138="$ORIG_RPC_URL_138"
|
||||
[[ -n "$ORIG_RPC_URL" ]] && RPC_URL="$ORIG_RPC_URL"
|
||||
[[ -n "$ORIG_DODO_PMM_INTEGRATION_ADDRESS" ]] && DODO_PMM_INTEGRATION_ADDRESS="$ORIG_DODO_PMM_INTEGRATION_ADDRESS"
|
||||
[[ -n "$ORIG_DODO_PMM_INTEGRATION" ]] && DODO_PMM_INTEGRATION="$ORIG_DODO_PMM_INTEGRATION"
|
||||
[[ -n "$ORIG_DODO_PMM_PROVIDER_ADDRESS" ]] && DODO_PMM_PROVIDER_ADDRESS="$ORIG_DODO_PMM_PROVIDER_ADDRESS"
|
||||
[[ -n "$ORIG_DODO_PMM_PROVIDER" ]] && DODO_PMM_PROVIDER="$ORIG_DODO_PMM_PROVIDER"
|
||||
[[ -n "$ORIG_PRIVATE_KEY" ]] && PRIVATE_KEY="$ORIG_PRIVATE_KEY"
|
||||
[[ -n "$ORIG_DRY_RUN" ]] && DRY_RUN="$ORIG_DRY_RUN"
|
||||
[[ -n "$ORIG_CHAIN_GAS_PRICE" ]] && CHAIN_GAS_PRICE="$ORIG_CHAIN_GAS_PRICE"
|
||||
[[ -n "$ORIG_TX_TIMEOUT_SECONDS" ]] && TX_TIMEOUT_SECONDS="$ORIG_TX_TIMEOUT_SECONDS"
|
||||
[[ -n "$ORIG_POST_CREATE_POLL_SECONDS" ]] && POST_CREATE_POLL_SECONDS="$ORIG_POST_CREATE_POLL_SECONDS"
|
||||
[[ -n "$ORIG_POST_CREATE_POLL_INTERVAL" ]] && POST_CREATE_POLL_INTERVAL="$ORIG_POST_CREATE_POLL_INTERVAL"
|
||||
|
||||
RPC_URL_138="${RPC_URL_138:-${RPC_URL:-http://192.168.11.211:8545}}"
|
||||
DODO_PMM_INTEGRATION_ADDRESS="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-}}"
|
||||
DODO_PMM_PROVIDER_ADDRESS="${DODO_PMM_PROVIDER_ADDRESS:-${DODO_PMM_PROVIDER:-}}"
|
||||
DRY_RUN="${DRY_RUN:-0}"
|
||||
|
||||
[[ -f "$CONFIG_JSON" ]] || { echo "POOL_CONFIG_JSON not found: $CONFIG_JSON" >&2; exit 1; }
|
||||
command -v jq >/dev/null 2>&1 || { echo "jq is required" >&2; exit 1; }
|
||||
command -v cast >/dev/null 2>&1 || { echo "cast is required" >&2; exit 1; }
|
||||
[[ -n "$DODO_PMM_INTEGRATION_ADDRESS" ]] || { echo "DODO_PMM_INTEGRATION_ADDRESS not set" >&2; exit 1; }
|
||||
[[ -n "$DODO_PMM_PROVIDER_ADDRESS" ]] || { echo "DODO_PMM_PROVIDER_ADDRESS not set" >&2; exit 1; }
|
||||
[[ "$DRY_RUN" == "1" || -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY not set (required unless DRY_RUN=1)" >&2; exit 1; }
|
||||
CHAIN_GAS_PRICE="${CHAIN_GAS_PRICE:-1000000000}"
|
||||
TX_TIMEOUT_SECONDS="${TX_TIMEOUT_SECONDS:-120}"
|
||||
POST_CREATE_POLL_SECONDS="${POST_CREATE_POLL_SECONDS:-20}"
|
||||
POST_CREATE_POLL_INTERVAL="${POST_CREATE_POLL_INTERVAL:-1}"
|
||||
|
||||
LP_FEE="${LP_FEE_RATE:-$(jq -r '.defaults.lpFeeRate' "$CONFIG_JSON")}"
|
||||
INITIAL_PRICE="${INITIAL_PRICE:-$(jq -r '.defaults.initialPrice' "$CONFIG_JSON")}"
|
||||
K_FACTOR="${K_FACTOR:-$(jq -r '.defaults.kFactor' "$CONFIG_JSON")}"
|
||||
ENABLE_TWAP="${ENABLE_TWAP:-$(jq -r '.defaults.enableTwap' "$CONFIG_JSON")}"
|
||||
|
||||
declare -A TOKENS=()
|
||||
while IFS=$'\t' read -r sym addr; do
|
||||
TOKENS["$sym"]="$addr"
|
||||
done < <(jq -r '.tokens | to_entries[] | [.key, .value] | @tsv' "$CONFIG_JSON")
|
||||
|
||||
mapfile -t C_STARS < <(jq -r '.groups.cStarSymbols[]' "$CONFIG_JSON")
|
||||
mapfile -t OFFICIALS < <(jq -r '.groups.officialStableSymbols[]' "$CONFIG_JSON")
|
||||
WETH_SYMBOL="$(jq -r '.groups.wethSymbol' "$CONFIG_JSON")"
|
||||
DEPLOY_CSTAR_CSTAR="$(jq -r '.groups.deploy.cStarVsCStar' "$CONFIG_JSON")"
|
||||
DEPLOY_CSTAR_OFFICIAL="$(jq -r '.groups.deploy.cStarVsOfficial' "$CONFIG_JSON")"
|
||||
DEPLOY_CSTAR_WETH="$(jq -r '.groups.deploy.cStarVsWeth' "$CONFIG_JSON")"
|
||||
DEPLOY_OFFICIAL_WETH="$(jq -r '.groups.deploy.officialVsWeth' "$CONFIG_JSON")"
|
||||
|
||||
declare -A SEEN=()
|
||||
DESIRED_PAIRS=()
|
||||
|
||||
add_pair() {
|
||||
local base_sym="$1" quote_sym="$2"
|
||||
local base="${TOKENS[$base_sym]:-}"
|
||||
local quote="${TOKENS[$quote_sym]:-}"
|
||||
local key="${base_sym}|${quote_sym}"
|
||||
|
||||
[[ -n "$base" && -n "$quote" ]] || return 0
|
||||
[[ "$base" != "0x0000000000000000000000000000000000000000" ]] || return 0
|
||||
[[ "$quote" != "0x0000000000000000000000000000000000000000" ]] || return 0
|
||||
[[ -z "${SEEN[$key]:-}" ]] || return 0
|
||||
|
||||
SEEN["$key"]=1
|
||||
DESIRED_PAIRS+=("${base_sym}|${base}|${quote_sym}|${quote}")
|
||||
}
|
||||
|
||||
if [[ "$DEPLOY_CSTAR_CSTAR" == "true" ]]; then
|
||||
for ((i=0; i<${#C_STARS[@]}; i++)); do
|
||||
for ((j=i+1; j<${#C_STARS[@]}; j++)); do
|
||||
add_pair "${C_STARS[$i]}" "${C_STARS[$j]}"
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "$DEPLOY_CSTAR_OFFICIAL" == "true" ]]; then
|
||||
for cstar in "${C_STARS[@]}"; do
|
||||
for official in "${OFFICIALS[@]}"; do
|
||||
add_pair "$cstar" "$official"
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "$DEPLOY_CSTAR_WETH" == "true" ]]; then
|
||||
for cstar in "${C_STARS[@]}"; do
|
||||
add_pair "$cstar" "$WETH_SYMBOL"
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "$DEPLOY_OFFICIAL_WETH" == "true" ]]; then
|
||||
for official in "${OFFICIALS[@]}"; do
|
||||
add_pair "$official" "$WETH_SYMBOL"
|
||||
done
|
||||
fi
|
||||
|
||||
while IFS=$'\t' read -r base_sym quote_sym; do
|
||||
add_pair "$base_sym" "$quote_sym"
|
||||
done < <(jq -r '.explicitPairs[]? | [.baseSymbol, .quoteSymbol] | @tsv' "$CONFIG_JSON")
|
||||
|
||||
zero_addr='0x0000000000000000000000000000000000000000'
|
||||
created=0
|
||||
registered=0
|
||||
skipped_existing=0
|
||||
failed=0
|
||||
CURRENT_NONCE=""
|
||||
|
||||
if [[ "$DRY_RUN" != "1" ]]; then
|
||||
DEPLOYER_ADDRESS="$(cast wallet address --private-key "$PRIVATE_KEY" 2>/dev/null || true)"
|
||||
[[ -n "$DEPLOYER_ADDRESS" ]] || { echo "Failed to derive deployer address from PRIVATE_KEY" >&2; exit 1; }
|
||||
fi
|
||||
|
||||
rpc_call() {
|
||||
local to="$1" data="$2"
|
||||
local payload
|
||||
payload=$(printf '{"jsonrpc":"2.0","method":"eth_call","params":[{"to":"%s","data":"%s"},"latest"],"id":1}' "$to" "$data")
|
||||
curl -s -X POST "$RPC_URL_138" -H 'Content-Type: application/json' --data "$payload"
|
||||
}
|
||||
|
||||
json_result() {
|
||||
sed -n 's/.*"result":"\([^"]*\)".*/\1/p'
|
||||
}
|
||||
|
||||
hex_to_addr() {
|
||||
local value="${1#0x}"
|
||||
if [[ ${#value} -lt 40 ]]; then
|
||||
echo "$zero_addr"
|
||||
return 0
|
||||
fi
|
||||
printf '0x%s\n' "${value: -40}"
|
||||
}
|
||||
|
||||
pool_for() {
|
||||
local calldata result
|
||||
calldata="$(cast calldata "pools(address,address)" "$1" "$2" 2>/dev/null || true)"
|
||||
[[ -n "$calldata" ]] || { echo "$zero_addr"; return 0; }
|
||||
result="$(rpc_call "$DODO_PMM_INTEGRATION_ADDRESS" "$calldata" | json_result)"
|
||||
[[ -n "$result" ]] || { echo "$zero_addr"; return 0; }
|
||||
hex_to_addr "$result"
|
||||
}
|
||||
|
||||
provider_pool_for() {
|
||||
local calldata result
|
||||
calldata="$(cast calldata "pools(address,address)" "$1" "$2" 2>/dev/null || true)"
|
||||
[[ -n "$calldata" ]] || { echo "$zero_addr"; return 0; }
|
||||
result="$(rpc_call "$DODO_PMM_PROVIDER_ADDRESS" "$calldata" | json_result)"
|
||||
[[ -n "$result" ]] || { echo "$zero_addr"; return 0; }
|
||||
hex_to_addr "$result"
|
||||
}
|
||||
|
||||
send_tx() {
|
||||
local to="$1"
|
||||
shift
|
||||
local attempts nonce output rc
|
||||
for attempts in 1 2 3; do
|
||||
nonce="$(cast nonce "$DEPLOYER_ADDRESS" --block pending --rpc-url "$RPC_URL_138" 2>/dev/null || true)"
|
||||
[[ -n "$nonce" ]] || {
|
||||
echo "Failed to fetch pending nonce for deployer" >&2
|
||||
return 1
|
||||
}
|
||||
set +e
|
||||
output="$(cast send "$to" "$@" \
|
||||
--rpc-url "$RPC_URL_138" \
|
||||
--private-key "$PRIVATE_KEY" \
|
||||
--legacy \
|
||||
--gas-price "$CHAIN_GAS_PRICE" \
|
||||
--nonce "$nonce" \
|
||||
--confirmations 1 \
|
||||
--timeout "$TX_TIMEOUT_SECONDS" \
|
||||
-q 2>&1)"
|
||||
rc=$?
|
||||
set -e
|
||||
if (( rc == 0 )); then
|
||||
return 0
|
||||
fi
|
||||
if [[ "$output" == *"Nonce too low"* || "$output" == *"Replacement transaction underpriced"* ]]; then
|
||||
sleep 1
|
||||
continue
|
||||
fi
|
||||
printf '%s\n' "$output" >&2
|
||||
return "$rc"
|
||||
done
|
||||
printf '%s\n' "$output" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_for_pool() {
|
||||
local base="$1" quote="$2"
|
||||
local max_polls poll current
|
||||
max_polls=$((POST_CREATE_POLL_SECONDS / POST_CREATE_POLL_INTERVAL))
|
||||
(( max_polls < 1 )) && max_polls=1
|
||||
for ((poll=0; poll<max_polls; poll++)); do
|
||||
current="$(pool_for "$base" "$quote")"
|
||||
if [[ -n "$current" && "$current" != "$zero_addr" ]]; then
|
||||
printf '%s\n' "$current"
|
||||
return 0
|
||||
fi
|
||||
sleep "$POST_CREATE_POLL_INTERVAL"
|
||||
done
|
||||
printf '%s\n' "$zero_addr"
|
||||
return 1
|
||||
}
|
||||
|
||||
register_pair() {
|
||||
local base="$1" quote="$2" pool="$3" label="$4"
|
||||
if [[ -z "$pool" || "$pool" == "$zero_addr" ]]; then
|
||||
echo " FAIL register $label -> zero pool address"
|
||||
((failed++)) || true
|
||||
return 1
|
||||
fi
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
echo " [DRY] would ensure provider registration for $label -> $pool (both directions)"
|
||||
((registered++)) || true
|
||||
return 0
|
||||
fi
|
||||
if send_tx "$DODO_PMM_PROVIDER_ADDRESS" "registerPool(address,address,address)" "$base" "$quote" "$pool" --gas-limit 200000 \
|
||||
&& send_tx "$DODO_PMM_PROVIDER_ADDRESS" "registerPool(address,address,address)" "$quote" "$base" "$pool" --gas-limit 200000; then
|
||||
echo " OK register $label -> $pool"
|
||||
((registered++)) || true
|
||||
return 0
|
||||
fi
|
||||
echo " FAIL register $label -> $pool"
|
||||
((failed++)) || true
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "=== Chain 138 PMM Desired-State Sync ==="
|
||||
echo "Config: $CONFIG_JSON"
|
||||
echo "Integration: $DODO_PMM_INTEGRATION_ADDRESS"
|
||||
echo "Provider: $DODO_PMM_PROVIDER_ADDRESS"
|
||||
echo "RPC: $RPC_URL_138"
|
||||
echo "Dry run: $DRY_RUN"
|
||||
echo "Desired pairs:${#DESIRED_PAIRS[@]}"
|
||||
echo ""
|
||||
|
||||
for pair in "${DESIRED_PAIRS[@]}"; do
|
||||
IFS='|' read -r base_sym base quote_sym quote <<< "$pair"
|
||||
label="${base_sym}/${quote_sym}"
|
||||
existing_pool="$(pool_for "$base" "$quote")"
|
||||
provider_pool="$(provider_pool_for "$base" "$quote")"
|
||||
if [[ -n "$existing_pool" && "$existing_pool" != "$zero_addr" ]]; then
|
||||
echo "SKIP create $label (exists: $existing_pool)"
|
||||
((skipped_existing++)) || true
|
||||
register_pair "$base" "$quote" "$existing_pool" "$label"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ -n "$provider_pool" && "$provider_pool" != "$zero_addr" ]]; then
|
||||
echo "BLOCKER create $label (provider already points to existing pool $provider_pool, but integration has no pool record)"
|
||||
echo " Repair requires importing the existing pool into DODOPMMIntegration with importExistingPool(...)."
|
||||
((failed++)) || true
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
echo "[DRY] would create $label"
|
||||
((created++)) || true
|
||||
continue
|
||||
fi
|
||||
|
||||
if send_tx "$DODO_PMM_INTEGRATION_ADDRESS" "createPool(address,address,uint256,uint256,uint256,bool)" \
|
||||
"$base" "$quote" "$LP_FEE" "$INITIAL_PRICE" "$K_FACTOR" "$ENABLE_TWAP" --gas-limit 500000; then
|
||||
new_pool="$(wait_for_pool "$base" "$quote")"
|
||||
if [[ -z "$new_pool" || "$new_pool" == "$zero_addr" ]]; then
|
||||
echo "FAIL create $label (tx confirmed but integration still reports zero pool address)"
|
||||
((failed++)) || true
|
||||
continue
|
||||
fi
|
||||
echo "OK create $label -> $new_pool"
|
||||
((created++)) || true
|
||||
register_pair "$base" "$quote" "$new_pool" "$label"
|
||||
else
|
||||
echo "FAIL create $label"
|
||||
((failed++)) || true
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Summary:"
|
||||
echo " Created: $created"
|
||||
echo " Registered: $registered"
|
||||
echo " Existing: $skipped_existing"
|
||||
echo " Failed: $failed"
|
||||
Reference in New Issue
Block a user