#!/usr/bin/env bash # Finish PMM soak grid funding after stalls or partial runs: wait for blocks + mempool, optional # owner-mint of mirror USDC shortfall, then fund native (resume) + cUSDT + cUSDC + mirror USDT + mirror USDC. # # Requires: cast, jq, python3, PRIVATE_KEY, RPC_URL_138 (via load-project-env) # # Usage: # bash scripts/deployment/pmm-soak-complete-grid-funding-operator.sh --dry-run # bash scripts/deployment/pmm-soak-complete-grid-funding-operator.sh --apply # # Env: # PMM_SOAK_GRID_JSON, PMM_SOAK_FUND_CHUNK (default 250), NATIVE_AMOUNT_WEI, seed RAW vars (same as tranche script) # PMM_SOAK_RESUME_NATIVE_FROM_LINEAR — if set, first linear index to receive native (skip lower). If unset, auto-detect. # PMM_SOAK_WAIT_BLOCK_SEC — max seconds to wait for eth_blockNumber to increase (default 86400) # PMM_SOAK_WAIT_NONCE_CLEAR_SEC — max wait for deployer latest==pending nonce (default 7200) # CAST_SEND_TIMEOUT_SEC / CAST_RPC_TIMEOUT_SEC — forwarded via fund-grid # PMM_SOAK_RPC_URL_OVERRIDE — after dotenv load, force RPC_URL_138 (e.g. https://rpc-http-pub.d-bis.org for long runs) # PMM_SOAK_START_LEG — optional resume: run from this leg onward only (native | mint | cusdt | cusdc | mirr_usdt | mirr_usdc). # Skipped legs are not re-run; ensure deployer balances and mempool are already suitable (e.g. start at cusdt only after native + mint done). # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" cd "$PROJECT_ROOT" # shellcheck source=/dev/null source "${PROJECT_ROOT}/scripts/lib/pmm-soak-dotenv-override.sh" pmm_soak_snapshot_pool_env_for_restore # shellcheck source=/dev/null [[ -f "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" ]] && source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" pmm_soak_restore_pool_env_after_dotenv if [[ -n "${PMM_SOAK_RPC_URL_OVERRIDE:-}" ]]; then export RPC_URL_138="$PMM_SOAK_RPC_URL_OVERRIDE" export CHAIN138_RPC_URL="$RPC_URL_138" export CHAIN138_RPC="$RPC_URL_138" export ETH_RPC_URL="$RPC_URL_138" fi GRID_JSON="${PMM_SOAK_GRID_JSON:-${PROJECT_ROOT}/config/pmm-soak-wallet-grid.json}" CHUNK="${PMM_SOAK_FUND_CHUNK:-250}" NATIVE_WEI="${NATIVE_AMOUNT_WEI:-20000000000000000}" SEED_CUSDT="${PMM_SOAK_SEED_CUSDT_RAW:-100000000}" SEED_CUSDC="${PMM_SOAK_SEED_CUSDC_RAW:-100000000}" SEED_USDT="${PMM_SOAK_SEED_MIRROR_USDT_RAW:-100000000}" SEED_USDC="${PMM_SOAK_SEED_MIRROR_USDC_RAW:-100000000}" CUSDT=0x93E66202A11B1772E55407B32B44e5Cd8eda7f22 CUSDC=0xf22258f57794CC8E06237084b353Ab30fFfa640b USDT_M=0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1 USDC_M=0x71D6687F38b93CCad569Fa6352c876eea967201b RPC="${RPC_URL_138:-http://192.168.11.211:8545}" D=0x4A666F96fC8764181194447A7dFdb7d471b301C8 MAX_LI=6533 WAIT_BLOCK="${PMM_SOAK_WAIT_BLOCK_SEC:-86400}" WAIT_NONCE="${PMM_SOAK_WAIT_NONCE_CLEAR_SEC:-7200}" APPLY=0 while [[ $# -gt 0 ]]; do case "$1" in --apply) APPLY=1 ;; --dry-run) APPLY=0 ;; -h | --help) sed -n '2,21p' "$0"; exit 0 ;; *) echo "unknown: $1" >&2; exit 2 ;; esac shift done if [[ ! -f "$GRID_JSON" ]]; then echo "[complete] missing $GRID_JSON" >&2 exit 1 fi if [[ -n "${PMM_SOAK_START_LEG:-}" ]]; then case "${PMM_SOAK_START_LEG}" in native | mint | cusdt | cusdc | mirr_usdt | mirr_usdc) ;; *) echo "[complete] FATAL: PMM_SOAK_START_LEG must be one of: native mint cusdt cusdc mirr_usdt mirr_usdc (got: ${PMM_SOAK_START_LEG})" >&2 exit 2 ;; esac fi _leg_idx() { case "$1" in native) echo 0 ;; mint) echo 1 ;; cusdt) echo 2 ;; cusdc) echo 3 ;; mirr_usdt) echo 4 ;; mirr_usdc) echo 5 ;; *) echo -1 ;; esac } _leg_should_run() { local leg="$1" local st="${PMM_SOAK_START_LEG:-}" [[ -z "$st" ]] && return 0 local a b a="$(_leg_idx "$leg")" b="$(_leg_idx "$st")" (( a >= b )) } wait_chain_block_progress() { echo "[complete] waiting for block number to advance (max ${WAIT_BLOCK}s) ..." local first stall=0 first="$(cast block-number --rpc-url "$RPC")" while true; do local now now="$(cast block-number --rpc-url "$RPC")" if (( now > first )); then echo "[complete] chain progressed blocks $first -> $now" return 0 fi if (( stall >= WAIT_BLOCK )); then echo "[complete] FATAL: no new blocks in ${WAIT_BLOCK}s (stuck at $first). Fix Chain 138 consensus / RPC, then re-run." >&2 exit 1 fi sleep 5 stall=$((stall + 5)) done } wait_deployer_nonce_mempool_clear() { local start="$SECONDS" while true; do local nl np nl="$(cast nonce "$D" --rpc-url "$RPC" -B latest)" np="$(cast nonce "$D" --rpc-url "$RPC" -B pending)" if [[ "$nl" == "$np" ]]; then echo "[complete] deployer nonce clear (latest=pending=$nl)" return 0 fi if (( SECONDS - start > WAIT_NONCE )); then echo "[complete] FATAL: deployer mempool stuck (latest=$nl pending=$np) after ${WAIT_NONCE}s" >&2 exit 1 fi echo "[complete] waiting deployer mempool latest=$nl pending=$np ..." sleep 3 done } detect_native_resume_linear() { local target="$NATIVE_WEI" # 95% of target: allow tiny fee dust below nominal local thresh thresh="$(python3 -c "print(int(int('$target') * 95 // 100))")" local li for ((li = 0; li <= MAX_LI; li++)); do local addr bal addr="$(jq -r --argjson li "$li" '.wallets[] | select(.linearIndex==$li) | .address' "$GRID_JSON")" bal="$(cast balance "$addr" --rpc-url "$RPC" | head -1 || echo 0)" if (( bal < thresh )); then echo "$li" return 0 fi done echo "$((MAX_LI + 1))" } run_native_chunk() { local from="$1" to="$2" echo "[complete] === native $from..$to apply=$APPLY ===" if [[ "$APPLY" -eq 1 ]]; then PMM_SOAK_GRID_JSON="$GRID_JSON" NATIVE_AMOUNT_WEI="$NATIVE_WEI" \ bash --noprofile --norc "${PROJECT_ROOT}/scripts/deployment/pmm-soak-operator-fund-grid.sh" --apply --native --from-linear "$from" --to-linear "$to" else PMM_SOAK_GRID_JSON="$GRID_JSON" NATIVE_AMOUNT_WEI="$NATIVE_WEI" \ bash --noprofile --norc "${PROJECT_ROOT}/scripts/deployment/pmm-soak-operator-fund-grid.sh" --native --from-linear "$from" --to-linear "$to" fi } run_erc20_chunk() { local from="$1" to="$2" tok="$3" amt="$4" label="$5" echo "[complete] === $label $tok $from..$to apply=$APPLY ===" if [[ "$APPLY" -eq 1 ]]; then PMM_SOAK_GRID_JSON="$GRID_JSON" TOKEN="$tok" AMOUNT_WEI_PER_WALLET="$amt" \ bash --noprofile --norc "${PROJECT_ROOT}/scripts/deployment/pmm-soak-operator-fund-grid.sh" --apply --from-linear "$from" --to-linear "$to" else PMM_SOAK_GRID_JSON="$GRID_JSON" TOKEN="$tok" AMOUNT_WEI_PER_WALLET="$amt" \ bash --noprofile --norc "${PROJECT_ROOT}/scripts/deployment/pmm-soak-operator-fund-grid.sh" --from-linear "$from" --to-linear "$to" fi } if [[ "$APPLY" -eq 0 ]]; then echo "[complete] dry-run: on --apply would: wait new block -> clear deployer mempool -> native from first under-funded linear (or PMM_SOAK_RESUME_NATIVE_FROM_LINEAR) -> mint mirror USDC shortfall -> cUSDT/cUSDC/mirror USDT/mirror USDC for 0..$MAX_LI in chunks of $CHUNK" [[ -n "${PMM_SOAK_START_LEG:-}" ]] && echo "[complete] dry-run: PMM_SOAK_START_LEG=${PMM_SOAK_START_LEG} would skip earlier legs on --apply" RESUME_LI="${PMM_SOAK_RESUME_NATIVE_FROM_LINEAR:-0}" echo "[complete] dry-run sample native chunk from linear $RESUME_LI" to=$((RESUME_LI + CHUNK - 1)) [[ "$to" -gt "$MAX_LI" ]] && to="$MAX_LI" run_native_chunk "$RESUME_LI" "$to" bash --noprofile --norc "${PROJECT_ROOT}/scripts/deployment/pmm-soak-mint-mirror-usdc-deployer-shortfall.sh" --dry-run _dto=$((CHUNK - 1)) [[ "$_dto" -gt "$MAX_LI" ]] && _dto="$MAX_LI" run_erc20_chunk 0 "$_dto" "$CUSDT" "$SEED_CUSDT" "cUSDT (sample chunk)" echo "[complete] dry-run done (apply=0)" exit 0 fi wait_chain_block_progress wait_deployer_nonce_mempool_clear _complete_apply_start="$SECONDS" _phase_wall_start="$SECONDS" if _leg_should_run native; then RESUME_LI="${PMM_SOAK_RESUME_NATIVE_FROM_LINEAR:-}" if [[ -z "$RESUME_LI" ]]; then echo "[complete] auto-detecting first linear missing native (threshold 95% of $NATIVE_WEI wei) ..." RESUME_LI="$(detect_native_resume_linear)" echo "[complete] native resume linear index: $RESUME_LI" else echo "[complete] native resume linear from env: $RESUME_LI" fi if (( RESUME_LI > MAX_LI )); then echo "[complete] all wallets already at/above native threshold - skipping native legs" else for ((from = RESUME_LI; from <= MAX_LI; from += CHUNK)); do to=$((from + CHUNK - 1)) [[ "$to" -gt "$MAX_LI" ]] && to="$MAX_LI" run_native_chunk "$from" "$to" done fi echo "[complete] phase native wall_s=$((SECONDS - _phase_wall_start))" else echo "[complete] skipping native leg (PMM_SOAK_START_LEG=${PMM_SOAK_START_LEG:-unset})" fi _phase_wall_start="$SECONDS" if _leg_should_run mint; then echo "[complete] owner-mint mirror USDC shortfall (if any) ..." bash --noprofile --norc "${PROJECT_ROOT}/scripts/deployment/pmm-soak-mint-mirror-usdc-deployer-shortfall.sh" --apply echo "[complete] phase mint wall_s=$((SECONDS - _phase_wall_start))" else echo "[complete] skipping mint leg (PMM_SOAK_START_LEG=${PMM_SOAK_START_LEG:-unset})" fi _phase_wall_start="$SECONDS" if _leg_should_run cusdt; then for ((from = 0; from <= MAX_LI; from += CHUNK)); do to=$((from + CHUNK - 1)) [[ "$to" -gt "$MAX_LI" ]] && to="$MAX_LI" run_erc20_chunk "$from" "$to" "$CUSDT" "$SEED_CUSDT" "cUSDT" done echo "[complete] phase cusdt wall_s=$((SECONDS - _phase_wall_start))" else echo "[complete] skipping cUSDT leg (PMM_SOAK_START_LEG=${PMM_SOAK_START_LEG:-unset})" fi _phase_wall_start="$SECONDS" if _leg_should_run cusdc; then for ((from = 0; from <= MAX_LI; from += CHUNK)); do to=$((from + CHUNK - 1)) [[ "$to" -gt "$MAX_LI" ]] && to="$MAX_LI" run_erc20_chunk "$from" "$to" "$CUSDC" "$SEED_CUSDC" "cUSDC" done echo "[complete] phase cusdc wall_s=$((SECONDS - _phase_wall_start))" else echo "[complete] skipping cUSDC leg (PMM_SOAK_START_LEG=${PMM_SOAK_START_LEG:-unset})" fi _phase_wall_start="$SECONDS" if _leg_should_run mirr_usdt; then for ((from = 0; from <= MAX_LI; from += CHUNK)); do to=$((from + CHUNK - 1)) [[ "$to" -gt "$MAX_LI" ]] && to="$MAX_LI" run_erc20_chunk "$from" "$to" "$USDT_M" "$SEED_USDT" "mirror USDT" done echo "[complete] phase mirr_usdt wall_s=$((SECONDS - _phase_wall_start))" else echo "[complete] skipping mirror USDT leg (PMM_SOAK_START_LEG=${PMM_SOAK_START_LEG:-unset})" fi _phase_wall_start="$SECONDS" if _leg_should_run mirr_usdc; then for ((from = 0; from <= MAX_LI; from += CHUNK)); do to=$((from + CHUNK - 1)) [[ "$to" -gt "$MAX_LI" ]] && to="$MAX_LI" run_erc20_chunk "$from" "$to" "$USDC_M" "$SEED_USDC" "mirror USDC" done echo "[complete] phase mirr_usdc wall_s=$((SECONDS - _phase_wall_start))" else echo "[complete] skipping mirror USDC leg (PMM_SOAK_START_LEG=${PMM_SOAK_START_LEG:-unset})" fi echo "[complete] total apply wall_s=$((SECONDS - _complete_apply_start)) (after waits)" echo "[complete] all legs finished (apply=$APPLY)"