- bootstrap: split mnemonic export (SC2155), PMM_SOAK_RPC_URL_OVERRIDE, noprofile fund-grid, ASCII echoes, help header only - chain138-tick: document POOLS global for SC2153 - random/grid bots: log line references INTEGRATION, USD bounds, gold, gas, log_tag (SC2034) - validate-config: ShellCheck all 11 PMM soak + chain138-pmm shell scripts incl. smoke + bootstrap Made-with: Cursor
254 lines
8.3 KiB
Bash
Executable File
254 lines
8.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Chain 138 PMM soak — 33x33x6 grid wallets (6534) rotate swaps; deployer is operator (fund + tune via env).
|
|
#
|
|
# Wallets: BIP44 path m/44'/60'/0'/0/{linearIndex} for linearIndex = lpbca*198 + branch*6 + class
|
|
# (lpbca, branch in 0..32; class in 0..5).
|
|
#
|
|
# Optional inter-wallet ERC-20 transfers: set PMM_SOAK_TRANSFER_PROBABILITY and PMM_SOAK_TRANSFER_TOKEN
|
|
#
|
|
# Usage:
|
|
# PMM_SOAK_GRID_MNEMONIC='twelve words...' bash scripts/deployment/chain138-pmm-soak-grid-bot.sh --dry-run --max-ticks 5
|
|
# PMM_SOAK_GRID_JSON=config/pmm-soak-wallet-grid.json bash scripts/deployment/chain138-pmm-soak-grid-bot.sh --dry-run --max-ticks 5
|
|
# ... --apply
|
|
#
|
|
# Export addresses (no keys in file): pmm-soak-export-wallet-grid.py
|
|
# Fund from operator: pmm-soak-operator-fund-grid.sh
|
|
#
|
|
# Env (tuning):
|
|
# PMM_SOAK_GRID_MNEMONIC — required for --apply; for address lookup without exposing mnemonic in process use PMM_SOAK_GRID_JSON
|
|
# PMM_SOAK_GRID_JSON — optional exported manifest (faster address resolve)
|
|
# PMM_SOAK_INTERVAL_SEC — default 6
|
|
# PMM_SOAK_TRANSFER_PROBABILITY — 0..1, default 0
|
|
# PMM_SOAK_TRANSFER_TOKEN / PMM_SOAK_TRANSFER_USD_MIN / PMM_SOAK_TRANSFER_USD_MAX
|
|
# PMM_SOAK_LINEAR_MIN / PMM_SOAK_LINEAR_MAX — default 0..6533
|
|
# PMM_SOAK_MAX_ITER / --max-ticks
|
|
# PMM_SOAK_POOLS | PMM_SOAK_POOLS_FILE | PMM_SOAK_POOL_PRESET (scripts/lib/pmm-soak-pools.sh)
|
|
# CHAIN138_PMM_SOAK_SWAP_VIA — pool (default) | integration (see scripts/lib/pmm-soak-chain138-tick.sh)
|
|
# Caller exports for pool/swap-via override values from root .env after load-project-env (see pmm-soak-dotenv-override.sh).
|
|
# CLI: --pool-preset <name> | --swap-via pool|integration
|
|
#
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
|
|
|
APPLY=0
|
|
CLI_MAX_ITER=""
|
|
CLI_POOL_PRESET=""
|
|
CLI_SWAP_VIA=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--apply) APPLY=1 ;;
|
|
--dry-run) APPLY=0 ;;
|
|
--max-ticks)
|
|
CLI_MAX_ITER="$2"
|
|
shift
|
|
;;
|
|
--pool-preset)
|
|
CLI_POOL_PRESET="$2"
|
|
shift
|
|
;;
|
|
--swap-via)
|
|
CLI_SWAP_VIA="$2"
|
|
shift
|
|
;;
|
|
-h | --help)
|
|
echo "chain138-pmm-soak-grid-bot.sh — PMM soak across 6,534 grid wallets"
|
|
echo ""
|
|
echo "Usage: $0 [--dry-run|--apply] [--max-ticks N] [--pool-preset NAME] [--swap-via pool|integration]"
|
|
echo "Env: PMM_SOAK_GRID_MNEMONIC, PMM_SOAK_GRID_JSON, PMM_SOAK_LINEAR_MIN/MAX, PMM_SOAK_POOL_PRESET, ..."
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "[pmm-grid] unknown arg: $1" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# 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 "$CLI_SWAP_VIA" ]]; then
|
|
export CHAIN138_PMM_SOAK_SWAP_VIA="$CLI_SWAP_VIA"
|
|
fi
|
|
if [[ -n "$CLI_POOL_PRESET" ]]; then
|
|
export PMM_SOAK_POOL_PRESET="$CLI_POOL_PRESET"
|
|
unset PMM_SOAK_POOLS PMM_SOAK_POOLS_FILE 2>/dev/null || true
|
|
fi
|
|
|
|
# shellcheck source=/dev/null
|
|
source "${PROJECT_ROOT}/scripts/lib/pmm-soak-chain138-tick.sh"
|
|
# shellcheck source=/dev/null
|
|
source "${PROJECT_ROOT}/scripts/lib/pmm-soak-pools.sh"
|
|
|
|
require_cmd() {
|
|
command -v "$1" >/dev/null 2>&1 || {
|
|
echo "[pmm-grid] missing: $1" >&2
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
require_cmd cast
|
|
require_cmd python3
|
|
require_cmd bc
|
|
|
|
MN="${PMM_SOAK_GRID_MNEMONIC:-}"
|
|
RPC="${RPC_URL_138:-http://192.168.11.211:8545}"
|
|
INTEGRATION="${DODO_PMM_INTEGRATION_ADDRESS:-${CHAIN_138_DODO_PMM_INTEGRATION:-0x86ADA6Ef91A3B450F89f2b751e93B1b7A3218895}}"
|
|
INTERVAL="${PMM_SOAK_INTERVAL_SEC:-6}"
|
|
# For 6-decimal stables, amount = random(usd_min..usd_max) * 10^6 (face value in token units, not wei of ETH).
|
|
USD_MIN="${PMM_SOAK_USD_MIN:-10}"
|
|
USD_MAX="${PMM_SOAK_USD_MAX:-5000}"
|
|
SLIP_BPS="${PMM_SOAK_SLIPPAGE_BPS:-100}"
|
|
GOLD_USD="${GOLD_USD_PRICE:-3300}"
|
|
GAS_WEI="${CHAIN138_DEPLOY_GAS_PRICE_WEI:-1000}"
|
|
SOAK_MAX_ITER="${CLI_MAX_ITER:-${PMM_SOAK_MAX_ITER:-}}"
|
|
LINEAR_MIN="${PMM_SOAK_LINEAR_MIN:-0}"
|
|
LINEAR_MAX="${PMM_SOAK_LINEAR_MAX:-6533}"
|
|
TRANSFER_P="${PMM_SOAK_TRANSFER_PROBABILITY:-0}"
|
|
TRANSFER_TOKEN="${PMM_SOAK_TRANSFER_TOKEN:-}"
|
|
TRANSFER_USD_MIN="${PMM_SOAK_TRANSFER_USD_MIN:-100}"
|
|
TRANSFER_USD_MAX="${PMM_SOAK_TRANSFER_USD_MAX:-10000}"
|
|
GRID_JSON="${PMM_SOAK_GRID_JSON:-}"
|
|
|
|
PMM_SOAK_LOG_TAG="pmm-grid"
|
|
log() { pmm_soak_log "$@"; }
|
|
|
|
pmm_soak_load_pools || exit 1
|
|
|
|
grid_address_for_linear() {
|
|
local li="$1"
|
|
local path="m/44'/60'/0'/0/${li}"
|
|
if [[ -n "$GRID_JSON" && -f "$GRID_JSON" ]]; then
|
|
python3 - "$GRID_JSON" "$li" <<'PY' || return 1
|
|
import json, sys
|
|
path, li = sys.argv[1], int(sys.argv[2])
|
|
with open(path, encoding="utf-8") as f:
|
|
doc = json.load(f)
|
|
for w in doc["wallets"]:
|
|
if int(w["linearIndex"]) == li:
|
|
print(w["address"])
|
|
raise SystemExit(0)
|
|
raise SystemExit(1)
|
|
PY
|
|
return 0
|
|
fi
|
|
if [[ -z "$MN" ]]; then
|
|
return 1
|
|
fi
|
|
cast wallet address --mnemonic "$MN" --mnemonic-derivation-path "$path" 2>/dev/null
|
|
}
|
|
|
|
grid_private_key_for_linear() {
|
|
local li="$1"
|
|
local path="m/44'/60'/0'/0/${li}"
|
|
[[ -n "$MN" ]] || return 1
|
|
cast wallet private-key --mnemonic "$MN" --mnemonic-derivation-path "$path" 2>/dev/null
|
|
}
|
|
|
|
pick_linear_index() {
|
|
local span=$((LINEAR_MAX - LINEAR_MIN + 1))
|
|
echo $((LINEAR_MIN + RANDOM % span))
|
|
}
|
|
|
|
maybe_inter_wallet_transfer() {
|
|
local tick="$1"
|
|
[[ -n "$TRANSFER_TOKEN" ]] || return 0
|
|
python3 -c "import random,sys; sys.exit(0 if random.random() < float('$TRANSFER_P') else 1)" || return 0
|
|
|
|
local li_a li_b addr_a addr_b pk_a amt bal_out
|
|
li_a="$(pick_linear_index)"
|
|
li_b="$(pick_linear_index)"
|
|
[[ "$li_a" != "$li_b" ]] || return 0
|
|
|
|
addr_a="$(grid_address_for_linear "$li_a")" || return 0
|
|
addr_b="$(grid_address_for_linear "$li_b")" || return 0
|
|
[[ -n "$addr_a" && -n "$addr_b" ]] || return 0
|
|
|
|
amt="$(python3 -c "import random; print(random.randint(int('$TRANSFER_USD_MIN'), int('$TRANSFER_USD_MAX')) * 10**6)")"
|
|
|
|
log "tick $tick transfer prep linear $li_a -> $li_b amt_raw=$amt token=$TRANSFER_TOKEN"
|
|
|
|
if [[ "$APPLY" -eq 0 ]]; then
|
|
return 0
|
|
fi
|
|
|
|
if ! pk_a="$(grid_private_key_for_linear "$li_a")"; then
|
|
log "WARN: could not derive sender key"
|
|
return 0
|
|
fi
|
|
|
|
if ! bal_out="$(cast call "$TRANSFER_TOKEN" 'balanceOf(address)(uint256)' "$addr_a" --rpc-url "$RPC" 2>/dev/null | awk '{print $1}')"; then
|
|
return 0
|
|
fi
|
|
if (( $(echo "$bal_out < $amt" | bc -l) )); then
|
|
log "tick $tick skip transfer (low balance sender $li_a)"
|
|
return 0
|
|
fi
|
|
|
|
if ! cast send "$TRANSFER_TOKEN" 'transfer(address,uint256)(bool)' "$addr_b" "$amt" \
|
|
--rpc-url "$RPC" --private-key "$pk_a" --legacy --gas-price "$GAS_WEI"; then
|
|
log "WARN: transfer failed $li_a -> $li_b"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
if [[ "$APPLY" -eq 1 ]]; then
|
|
if [[ -z "$MN" ]]; then
|
|
log "FATAL: --apply requires PMM_SOAK_GRID_MNEMONIC"
|
|
exit 1
|
|
fi
|
|
else
|
|
log "Dry-run (no chain writes). Use --apply for live grid txs."
|
|
fi
|
|
|
|
LOCK="/tmp/chain138-pmm-soak-grid.${USER}.lock"
|
|
if [[ "$APPLY" -eq 1 ]]; then
|
|
if ! mkdir "$LOCK" 2>/dev/null; then
|
|
log "FATAL: lock $LOCK — another grid bot running?"
|
|
exit 1
|
|
fi
|
|
trap 'rmdir "$LOCK" 2>/dev/null || true' EXIT
|
|
fi
|
|
|
|
SWAP_VIA_LOG="${CHAIN138_PMM_SOAK_SWAP_VIA:-pool}"
|
|
log "RPC=$RPC integration=$INTEGRATION pools=${#POOLS[@]} swap_via=$SWAP_VIA_LOG linear=[$LINEAR_MIN,$LINEAR_MAX] interval=${INTERVAL}s USD=${USD_MIN}-${USD_MAX} slip_bps=$SLIP_BPS gold_usd=$GOLD_USD gas_wei=$GAS_WEI log_tag=$PMM_SOAK_LOG_TAG transfer_p=$TRANSFER_P apply=$APPLY"
|
|
|
|
tick=0
|
|
while true; do
|
|
tick=$((tick + 1))
|
|
li="$(pick_linear_index)"
|
|
|
|
maybe_inter_wallet_transfer "$tick" || true
|
|
|
|
if ! addr="$(grid_address_for_linear "$li")"; then
|
|
log "tick $tick skip (no address for linear=$li; set PMM_SOAK_GRID_MNEMONIC or PMM_SOAK_GRID_JSON)"
|
|
else
|
|
pk=""
|
|
if [[ "$APPLY" -eq 1 ]]; then
|
|
if ! pk="$(grid_private_key_for_linear "$li")"; then
|
|
log "tick $tick skip linear=$li (no private key)"
|
|
else
|
|
log "tick $tick linear=$li trader=$addr"
|
|
pmm_soak_chain138_run_tick_iteration "$tick" "$addr" "$pk" "$APPLY"
|
|
fi
|
|
else
|
|
log "tick $tick linear=$li trader=$addr"
|
|
pmm_soak_chain138_run_tick_iteration "$tick" "$addr" "$pk" "$APPLY"
|
|
fi
|
|
fi
|
|
|
|
if [[ -n "${SOAK_MAX_ITER:-}" && "$tick" -ge "$SOAK_MAX_ITER" ]]; then
|
|
log "SOAK_MAX_ITER=$SOAK_MAX_ITER done"
|
|
break
|
|
fi
|
|
sleep "$INTERVAL"
|
|
done
|