PMM soak grid: fund-grid progress/ERR trap, complete-grid START_LEG and timing, tranche RPC override, grid funding doc

- pmm-soak-operator-fund-grid: PMM_SOAK_FUND_PROGRESS_EVERY, ERR trap, help text comments only
- pmm-soak-complete-grid-funding-operator: PMM_SOAK_START_LEG resume, per-leg and total wall time
- pmm-soak-operator-fund-full-grid-tranches: PMM_SOAK_RPC_URL_OVERRIDE, bash --noprofile --norc, manual hint
- pmm-soak-mint-mirror-usdc-deployer-shortfall: ASCII-only operator messages
- CHAIN138_GRID_6534_WALLET_FUNDING_PLAN: full-grid orchestrator, env vars, log markers

Made-with: Cursor
This commit is contained in:
defiQUG
2026-04-11 20:56:40 -07:00
parent c6c57fa585
commit dc8abb06e2
5 changed files with 943 additions and 0 deletions

View File

@@ -0,0 +1,291 @@
#!/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)"

View File

@@ -0,0 +1,90 @@
#!/usr/bin/env bash
# Mint Official USDC mirror (Chain 138) to deployer so PMM soak grid funding can transfer out.
# OfficialStableMirrorToken: onlyOwner mint(address,uint256). Deployer is owner.
#
# Waits until deployer latest nonce == pending (no in-flight txs from this EOA), then mints shortfall:
# (PMM_SOAK_SEED_MIRROR_USDC_RAW × (6534 wallets)) deployer balance
#
# Usage:
# bash scripts/deployment/pmm-soak-mint-mirror-usdc-deployer-shortfall.sh --dry-run
# bash scripts/deployment/pmm-soak-mint-mirror-usdc-deployer-shortfall.sh --apply
#
# Env: RPC_URL_138, PMM_SOAK_RPC_URL_OVERRIDE, PRIVATE_KEY / DEPLOYER_PRIVATE_KEY, PMM_SOAK_GRID_JSON, PMM_SOAK_SEED_MIRROR_USDC_RAW,
# PMM_SOAK_WAIT_NONCE_CLEAR_SEC, CHAIN138_DEPLOY_GAS_PRICE_WEI
#
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
USDC_M=0x71D6687F38b93CCad569Fa6352c876eea967201b
D=0x4A666F96fC8764181194447A7dFdb7d471b301C8
RPC="${RPC_URL_138:-http://192.168.11.211:8545}"
PK="${DEPLOYER_PRIVATE_KEY:-${PRIVATE_KEY:-}}"
SEED_USDC="${PMM_SOAK_SEED_MIRROR_USDC_RAW:-100000000}"
MAX_LI=6533
GAS_WEI="${CHAIN138_DEPLOY_GAS_PRICE_WEI:-1000}"
MAX_WAIT="${PMM_SOAK_WAIT_NONCE_CLEAR_SEC:-7200}"
APPLY=0
while [[ $# -gt 0 ]]; do
case "$1" in
--apply) APPLY=1 ;;
--dry-run) APPLY=0 ;;
*) echo "unknown: $1" >&2; exit 2 ;;
esac
shift
done
if [[ "$APPLY" -eq 1 && -z "$PK" ]]; then
echo "[mint-usdc] FATAL: --apply needs PRIVATE_KEY" >&2
exit 1
fi
USDC_NEEDED="$(python3 -c "print(int('$SEED_USDC') * (int('$MAX_LI') + 1))")"
USDC_BAL="$(cast call "$USDC_M" 'balanceOf(address)(uint256)' "$D" --rpc-url "$RPC" | awk '{print $1}')"
if (( USDC_BAL >= USDC_NEEDED )); then
echo "[mint-usdc] deployer already has $USDC_BAL >= need $USDC_NEEDED - nothing to do"
exit 0
fi
SHORTFALL="$(python3 -c "print(int('$USDC_NEEDED') - int('$USDC_BAL'))")"
echo "[mint-usdc] need=$USDC_NEEDED bal=$USDC_BAL shortfall=$SHORTFALL"
if [[ "$APPLY" -eq 0 ]]; then
echo "[mint-usdc] dry-run: after mempool clear, would run:"
echo " cast send $USDC_M 'mint(address,uint256)' $D $SHORTFALL --rpc-url \"\$RPC_URL_138\" --private-key \"\$PRIVATE_KEY\" --legacy --gas-price $GAS_WEI"
exit 0
fi
start="$SECONDS"
while true; do
nl="$(cast nonce "$D" --rpc-url "$RPC" -B latest)"
np="$(cast nonce "$D" --rpc-url "$RPC" -B pending)"
if [[ "$nl" == "$np" ]]; then
echo "[mint-usdc] nonce clear (latest=pending=$nl)"
break
fi
if (( SECONDS - start > MAX_WAIT )); then
echo "[mint-usdc] FATAL: pending nonce (latest=$nl pending=$np) after ${MAX_WAIT}s" >&2
exit 1
fi
echo "[mint-usdc] waiting mempool latest=$nl pending=$np ..."
sleep 3
done
cast send "$USDC_M" 'mint(address,uint256)' "$D" "$SHORTFALL" \
--rpc-url "$RPC" --private-key "$PK" --legacy --gas-price "$GAS_WEI"
USDC_BAL="$(cast call "$USDC_M" 'balanceOf(address)(uint256)' "$D" --rpc-url "$RPC" | awk '{print $1}')"
echo "[mint-usdc] done deployer mirror USDC balance=$USDC_BAL"

View File

@@ -0,0 +1,195 @@
#!/usr/bin/env bash
# Fund all grid wallets (0..6533) in linear tranches — native + ERC-20 from deployer.
# Each tranche invokes pmm-soak-operator-fund-grid.sh (one tx per recipient per tranche).
#
# Usage (dry-run first):
# bash scripts/deployment/pmm-soak-operator-fund-full-grid-tranches.sh --dry-run
# bash scripts/deployment/pmm-soak-operator-fund-full-grid-tranches.sh --apply
#
# Env (optional overrides):
# PMM_SOAK_GRID_JSON — default config/pmm-soak-wallet-grid.json
# PMM_SOAK_FUND_CHUNK — wallets per tranche (default 250)
# NATIVE_AMOUNT_WEI — default 20000000000000000 (0.02)
# PMM_SOAK_SEED_CUSDT_RAW / PMM_SOAK_SEED_CUSDC_RAW / PMM_SOAK_SEED_MIRROR_USDT_RAW — 6-decimal raw per wallet (default 100000000 = 100 face)
# PMM_SOAK_SEED_MIRROR_USDC_RAW — per-wallet raw amount (default 100000000).
# PMM_SOAK_SKIP_MIRROR_USDC_OWNER_MINT=1 — do not auto-mint OfficialStableMirrorToken USDC to deployer when balance is short (default: mint shortfall via owner mint() before transfers).
# PMM_SOAK_WAIT_NONCE_CLEAR_SEC — before owner-mint, wait up to this many seconds for deployer latest==pending nonce (default 7200). Avoids "replacement transaction underpriced" when another process holds the next nonce.
# PMM_SOAK_RPC_URL_OVERRIDE — after dotenv load, force RPC_URL_138 (same as complete-grid / fund-grid long runs).
#
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}"
PK="${DEPLOYER_PRIVATE_KEY:-${PRIVATE_KEY:-}}"
D=0x4A666F96fC8764181194447A7dFdb7d471b301C8
APPLY=0
while [[ $# -gt 0 ]]; do
case "$1" in
--apply) APPLY=1 ;;
--dry-run) APPLY=0 ;;
-h | --help)
sed -n '1,28p' "$0" | tail -n +2
exit 0
;;
*) echo "unknown: $1" >&2; exit 2 ;;
esac
shift
done
if [[ "$APPLY" -eq 1 && -z "$PK" ]]; then
echo "[fund-tranches] FATAL: --apply needs PRIVATE_KEY / DEPLOYER_PRIVATE_KEY" >&2
exit 1
fi
if [[ ! -f "$GRID_JSON" ]]; then
echo "[fund-tranches] missing $GRID_JSON" >&2
exit 1
fi
MAX_LI=6533
GAS_WEI="${CHAIN138_DEPLOY_GAS_PRICE_WEI:-1000}"
refresh_usdc_bal() {
USDC_BAL="$(cast call "$USDC_M" 'balanceOf(address)(uint256)' "$D" --rpc-url "$RPC" | awk '{print $1}')"
}
# Wait until deployer has no pending txs (latest nonce tag == pending nonce tag).
wait_deployer_nonce_mempool_clear() {
local max_wait="${PMM_SOAK_WAIT_NONCE_CLEAR_SEC:-7200}"
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 "[fund-tranches] deployer nonce clear (latest=pending=$nl)"
return 0
fi
if (( SECONDS - start > max_wait )); then
echo "[fund-tranches] FATAL: deployer still has pending nonce (latest=$nl pending=$np) after ${max_wait}s — stop other deployer senders or raise PMM_SOAK_WAIT_NONCE_CLEAR_SEC" >&2
exit 1
fi
echo "[fund-tranches] waiting for deployer mempool (latest=$nl pending=$np) …"
sleep 3
done
}
USDC_NEEDED="$(python3 -c "print(int('$SEED_USDC') * (int('$MAX_LI') + 1))")"
refresh_usdc_bal
DO_MIRROR_USDC=1
if (( USDC_BAL < USDC_NEEDED )); then
SHORTFALL="$(python3 -c "print(int('$USDC_NEEDED') - int('$USDC_BAL'))")"
if [[ "${PMM_SOAK_SKIP_MIRROR_USDC_OWNER_MINT:-0}" == "1" ]]; then
DO_MIRROR_USDC=0
echo "[fund-tranches] WARN: deployer mirror USDC balance $USDC_BAL < need $USDC_NEEDED; PMM_SOAK_SKIP_MIRROR_USDC_OWNER_MINT=1 — skipping mirror USDC tranches"
elif [[ "$APPLY" -eq 0 ]]; then
echo "[fund-tranches] deployer mirror USDC $USDC_BAL < need $USDC_NEEDED — dry-run: on --apply would owner-mint $SHORTFALL to deployer then fund mirror USDC"
echo " cast send $USDC_M 'mint(address,uint256)' $D $SHORTFALL --rpc-url \"\$RPC_URL_138\" --private-key \"\$PRIVATE_KEY\" --legacy --gas-price $GAS_WEI"
DO_MIRROR_USDC=1
elif [[ -z "$PK" ]]; then
DO_MIRROR_USDC=0
echo "[fund-tranches] WARN: shortfall $SHORTFALL but no PRIVATE_KEY — cannot mint; skipping mirror USDC tranches" >&2
else
echo "[fund-tranches] owner-mint mirror USDC shortfall $SHORTFALL to deployer (need $USDC_NEEDED, have $USDC_BAL)"
wait_deployer_nonce_mempool_clear
cast send "$USDC_M" 'mint(address,uint256)' "$D" "$SHORTFALL" \
--rpc-url "$RPC" --private-key "$PK" --legacy --gas-price "$GAS_WEI"
refresh_usdc_bal
if (( USDC_BAL < USDC_NEEDED )); then
echo "[fund-tranches] FATAL: after mint deployer USDC $USDC_BAL still < $USDC_NEEDED" >&2
exit 1
fi
echo "[fund-tranches] deployer mirror USDC balance now $USDC_BAL"
fi
fi
run_native() {
local from="$1" to="$2"
echo "[fund-tranches] === native linear $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() {
local from="$1" to="$2" tok="$3" amt="$4"
echo "[fund-tranches] === ERC-20 $tok linear $from..$to amount=$amt (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
}
for ((from = 0; from <= MAX_LI; from += CHUNK)); do
to=$((from + CHUNK - 1))
[[ "$to" -gt "$MAX_LI" ]] && to="$MAX_LI"
run_native "$from" "$to"
done
for ((from = 0; from <= MAX_LI; from += CHUNK)); do
to=$((from + CHUNK - 1))
[[ "$to" -gt "$MAX_LI" ]] && to="$MAX_LI"
run_erc20 "$from" "$to" "$CUSDT" "$SEED_CUSDT"
done
for ((from = 0; from <= MAX_LI; from += CHUNK)); do
to=$((from + CHUNK - 1))
[[ "$to" -gt "$MAX_LI" ]] && to="$MAX_LI"
run_erc20 "$from" "$to" "$CUSDC" "$SEED_CUSDC"
done
for ((from = 0; from <= MAX_LI; from += CHUNK)); do
to=$((from + CHUNK - 1))
[[ "$to" -gt "$MAX_LI" ]] && to="$MAX_LI"
run_erc20 "$from" "$to" "$USDT_M" "$SEED_USDT"
done
if [[ "$DO_MIRROR_USDC" -eq 1 ]]; then
for ((from = 0; from <= MAX_LI; from += CHUNK)); do
to=$((from + CHUNK - 1))
[[ "$to" -gt "$MAX_LI" ]] && to="$MAX_LI"
run_erc20 "$from" "$to" "$USDC_M" "$SEED_USDC"
done
else
echo "[fund-tranches] mirror USDC tranches skipped (see WARN above). Options: unset PMM_SOAK_SKIP_MIRROR_USDC_OWNER_MINT and re-run --apply (auto-mint), or manual mint then:"
echo " PMM_SOAK_GRID_JSON=$GRID_JSON TOKEN=$USDC_M AMOUNT_WEI_PER_WALLET=$SEED_USDC bash --noprofile --norc scripts/deployment/pmm-soak-operator-fund-grid.sh --apply --from-linear 0 --to-linear $MAX_LI"
fi
echo "[fund-tranches] done (apply=$APPLY)"

View File

@@ -0,0 +1,209 @@
#!/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"