Files
proxmox/scripts/deployment/pmm-soak-operator-fund-grid.sh

210 lines
7.2 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# Operator (deployer) funds grid wallets from an exported JSON manifest.
#
# Modes:
# ERC-20 (default): TOKEN + AMOUNT_WEI_PER_WALLET — transfer() from operator to each grid address.
# Native (--native): NATIVE_AMOUNT_WEI (or AMOUNT_WEI_PER_WALLET) — simple value transfer for gas on Chain 138.
#
# Default: dry-run. Live: --apply
#
# Usage (ERC-20):
# PMM_SOAK_GRID_JSON=config/pmm-soak-wallet-grid.json \
# TOKEN=0x93E66202A11B1772E55407B32B44e5Cd8eda7f22 AMOUNT_WEI_PER_WALLET=10000000000 \
# bash scripts/deployment/pmm-soak-operator-fund-grid.sh --apply --from-linear 0 --to-linear 99
#
# Usage (native / gas):
# PMM_SOAK_GRID_JSON=config/pmm-soak-wallet-grid.json \
# NATIVE_AMOUNT_WEI=20000000000000000 \
# bash scripts/deployment/pmm-soak-operator-fund-grid.sh --native --apply --from-linear 0 --to-linear 199
#
# Env:
# PRIVATE_KEY / DEPLOYER_PRIVATE_KEY — operator
# RPC_URL_138
# PMM_SOAK_GRID_JSON — default: $PROJECT_ROOT/config/pmm-soak-wallet-grid.json
# TOKEN — ERC-20 (not used with --native)
# AMOUNT_WEI_PER_WALLET — per-recipient raw amount (ERC-20); also accepted for --native if NATIVE_AMOUNT_WEI unset
# NATIVE_AMOUNT_WEI — preferred for --native (wei per wallet)
# CHAIN138_DEPLOY_GAS_PRICE_WEI — default 1000
# CAST_SEND_TIMEOUT_SEC — passed to cast as ETH_TIMEOUT (seconds to wait for tx confirmation; default 900)
# CAST_RPC_TIMEOUT_SEC — ETH_RPC_TIMEOUT for cast (default 120)
# PMM_SOAK_RPC_URL_OVERRIDE — after dotenv load, force RPC_URL_138 for all cast calls
# PMM_SOAK_FUND_PROGRESS_EVERY — during --apply, log ETA every N successful txs (0=off except final; default 50)
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
# 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
require_cmd() {
command -v "$1" >/dev/null 2>&1 || {
echo "[fund-grid] missing: $1" >&2
exit 1
}
}
require_cmd cast
require_cmd python3
APPLY=0
FROM_L=0
TO_L=6533
MODE="erc20"
while [[ $# -gt 0 ]]; do
case "$1" in
--apply) APPLY=1 ;;
--native) MODE="native" ;;
--erc20) MODE="erc20" ;;
--from-linear)
FROM_L="$2"
shift
;;
--to-linear)
TO_L="$2"
shift
;;
-h | --help)
sed -n '2,31p' "$0"
exit 0
;;
*)
echo "[fund-grid] unknown arg: $1" >&2
exit 2
;;
esac
shift
done
PK="${DEPLOYER_PRIVATE_KEY:-${PRIVATE_KEY:-}}"
RPC="${RPC_URL_138:-http://192.168.11.211:8545}"
GRID_JSON="${PMM_SOAK_GRID_JSON:-${PROJECT_ROOT}/config/pmm-soak-wallet-grid.json}"
TOKEN_ADDR="${TOKEN:-}"
GAS_WEI="${CHAIN138_DEPLOY_GAS_PRICE_WEI:-1000}"
NATIVE_AMOUNT="${NATIVE_AMOUNT_WEI:-}"
ERC20_AMOUNT="${AMOUNT_WEI_PER_WALLET:-}"
if [[ "$MODE" == "native" ]]; then
AMOUNT="${NATIVE_AMOUNT:-${ERC20_AMOUNT:-}}"
if [[ -z "$AMOUNT" ]]; then
echo "[fund-grid] --native requires NATIVE_AMOUNT_WEI or AMOUNT_WEI_PER_WALLET (wei per wallet)" >&2
exit 1
fi
else
AMOUNT="${ERC20_AMOUNT:-}"
if [[ -z "$TOKEN_ADDR" || -z "$AMOUNT" ]]; then
echo "[fund-grid] ERC-20 mode: set TOKEN and AMOUNT_WEI_PER_WALLET" >&2
exit 1
fi
fi
if [[ ! -f "$GRID_JSON" ]]; then
echo "[fund-grid] missing grid JSON: $GRID_JSON" >&2
exit 1
fi
if [[ "$APPLY" -eq 1 && -z "$PK" ]]; then
echo "[fund-grid] --apply requires PRIVATE_KEY / DEPLOYER_PRIVATE_KEY" >&2
exit 1
fi
OPERATOR="$(cast wallet address --private-key "$PK" 2>/dev/null || true)"
if [[ "$APPLY" -eq 1 && -z "$OPERATOR" ]]; then
echo "[fund-grid] could not derive operator address" >&2
exit 1
fi
mapfile -t RECIPIENTS < <(
python3 - "$GRID_JSON" "$FROM_L" "$TO_L" <<'PY'
import json, sys
path, lo, hi = sys.argv[1], int(sys.argv[2]), int(sys.argv[3])
with open(path, encoding="utf-8") as f:
doc = json.load(f)
for w in doc["wallets"]:
li = int(w["linearIndex"])
if lo <= li <= hi:
print(w["address"])
PY
)
total="${#RECIPIENTS[@]}"
if [[ "$MODE" == "native" ]]; then
echo "[fund-grid] mode=native recipients=[$FROM_L,$TO_L] count=$total amount_each_wei=$AMOUNT apply=$APPLY"
else
echo "[fund-grid] mode=erc20 recipients=[$FROM_L,$TO_L] count=$total token=$TOKEN_ADDR amount_each=$AMOUNT apply=$APPLY"
fi
if [[ "$total" -eq 0 ]]; then
exit 0
fi
export ETH_TIMEOUT="${CAST_SEND_TIMEOUT_SEC:-900}"
export ETH_RPC_TIMEOUT="${CAST_RPC_TIMEOUT_SEC:-120}"
total_needed="$(python3 -c "print(int('$AMOUNT') * $total)")"
if [[ "$APPLY" -eq 0 ]]; then
echo "[fund-grid] dry-run sample (first 3):"
for i in 0 1 2; do
[[ "$i" -lt "$total" ]] || break
if [[ "$MODE" == "native" ]]; then
echo " cast send ${RECIPIENTS[$i]} --value $AMOUNT --rpc-url \"\$RPC_URL_138\" --private-key \"\$PRIVATE_KEY\" --legacy --gas-price $GAS_WEI"
else
echo " cast send $TOKEN_ADDR 'transfer(address,uint256)(bool)' ${RECIPIENTS[$i]} $AMOUNT --rpc-url \"\$RPC_URL_138\" --private-key \"\$PRIVATE_KEY\" --legacy --gas-price $GAS_WEI"
fi
done
if [[ "$MODE" == "native" ]]; then
echo "[fund-grid] aggregate native to recipients: $total_needed wei (+ operator gas for each funding tx)"
else
echo "[fund-grid] aggregate ERC-20 to recipients: $total_needed raw units (+ operator gas for each funding tx)"
fi
echo "[fund-grid] rerun with --apply to broadcast $total transactions"
exit 0
fi
if [[ "$MODE" == "native" && -n "$OPERATOR" ]]; then
op_bal="$(cast balance "$OPERATOR" --rpc-url "$RPC" 2>/dev/null | head -1 || echo "0")"
echo "[fund-grid] operator native balance (approx): $op_bal wei - need $total_needed wei to recipients + gas for $total txs"
fi
_pmm_fund_grid_err() {
echo "[fund-grid] ERR line=${BASH_LINENO[0]} n=${n:-0}/${total} mode=${MODE} recipients=[$FROM_L,$TO_L]" >&2
}
trap '_pmm_fund_grid_err' ERR
_chunk_wall_start="$SECONDS"
_prog_every="${PMM_SOAK_FUND_PROGRESS_EVERY:-50}"
[[ "$_prog_every" =~ ^[0-9]+$ ]] || _prog_every=50
n=0
for r in "${RECIPIENTS[@]}"; do
n=$((n + 1))
echo "[fund-grid] $n/$total -> $r"
if [[ "$MODE" == "native" ]]; then
cast send "$r" --value "$AMOUNT" --rpc-url "$RPC" --private-key "$PK" --legacy --gas-price "$GAS_WEI"
else
cast send "$TOKEN_ADDR" 'transfer(address,uint256)(bool)' "$r" "$AMOUNT" \
--rpc-url "$RPC" --private-key "$PK" --legacy --gas-price "$GAS_WEI"
fi
if [[ "$_prog_every" -gt 0 ]] && (( n % _prog_every == 0 || n == total )); then
_el=$((SECONDS - _chunk_wall_start))
python3 -c "n=$n;total=$total;el=$_el;avg=el/n if n else 0.0;rem=total-n;eta=int(rem*avg) if rem>0 else 0;print(f'[fund-grid] progress {n}/{total} chunk_elapsed_s={el} avg_s_per_tx={avg:.2f} est_remaining_this_chunk_s={eta}')" 2>/dev/null || true
elif [[ "$_prog_every" -eq 0 ]] && [[ "$n" -eq "$total" ]]; then
echo "[fund-grid] progress ${n}/${total} chunk_elapsed_s=$((SECONDS - _chunk_wall_start)) (final)"
fi
done
trap - ERR
echo "[fund-grid] done"