- 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
209 lines
8.7 KiB
Bash
Executable File
209 lines
8.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Chain 138 PMM soak — one tick (quote + optional approve/swap). Source after setting:
|
|
# POOLS (array), RPC, INTEGRATION, SLIP_BPS, GAS_WEI, GOLD_USD, USD_MIN, USD_MAX
|
|
# Optional: PMM_SOAK_LOG_TAG (default pmm-soak)
|
|
#
|
|
# Optional: CHAIN138_PMM_SOAK_GAS_LIMIT — when set, passed as --gas-limit to approve/swap cast send (helps Besu estimation).
|
|
# Optional: CHAIN138_PMM_SOAK_SWAP_VIA — pool (default) | integration
|
|
# On Chain 138, DODOPMMIntegration.swapExactIn reverts inside the pool when msg.sender is the integration contract;
|
|
# EOA transfer(token, pool, amt) + pool.sellBase/sellQuote(trader) succeeds. Default pool path matches that.
|
|
# Requires: cast, bc, python3 (pick_random_amount)
|
|
# shellcheck shell=bash
|
|
|
|
PMM_SOAK_CXAUC_LC="${PMM_SOAK_CXAUC_LC:-0x290e52a8819a4fbd0714e517225429aa2b70ec6b}"
|
|
PMM_SOAK_CXAUT_LC="${PMM_SOAK_CXAUT_LC:-0x94e408e26c6fd8f4ee00b54df19082fda07dc96e}"
|
|
|
|
pmm_soak_to_lower() {
|
|
printf '%s' "$1" | tr '[:upper:]' '[:lower:]'
|
|
}
|
|
|
|
pmm_soak_log() {
|
|
echo "[${PMM_SOAK_LOG_TAG:-pmm-soak} $(date -Iseconds)] $*"
|
|
}
|
|
|
|
pmm_soak_pick_random_amount() {
|
|
local token_lc="$1"
|
|
local decimals="$2"
|
|
PMM_SOAK_XAU_ADDRS="${PMM_SOAK_CXAUC_LC},${PMM_SOAK_CXAUT_LC}" \
|
|
GOLD_USD="$GOLD_USD" USD_MIN="$USD_MIN" USD_MAX="$USD_MAX" \
|
|
python3 - "$token_lc" "$decimals" <<'PY'
|
|
import os, random, sys
|
|
tok = sys.argv[1].lower()
|
|
d = int(sys.argv[2])
|
|
usd_min = int(os.environ["USD_MIN"])
|
|
usd_max = int(os.environ["USD_MAX"])
|
|
gold = float(os.environ["GOLD_USD"])
|
|
scale = 10 ** d
|
|
xau = {a.strip().lower() for a in os.environ["PMM_SOAK_XAU_ADDRS"].split(",") if a.strip()}
|
|
|
|
if tok in xau:
|
|
usd = random.randint(usd_min, usd_max)
|
|
oz = usd / gold
|
|
raw = int(oz * scale)
|
|
print(max(raw, 1))
|
|
else:
|
|
u = random.randint(usd_min, usd_max)
|
|
print(u * scale)
|
|
PY
|
|
}
|
|
|
|
# One loop iteration: random pool, quote as trader, swap with signer_pk when apply=1.
|
|
# Globals: POOLS RPC INTEGRATION SLIP_BPS GAS_WEI
|
|
pmm_soak_chain138_run_tick_iteration() {
|
|
local tick="$1"
|
|
local trader="$2"
|
|
local signer_pk="$3"
|
|
local apply="$4"
|
|
|
|
# POOLS[] is set by the caller (pmm-soak-pools.sh); not the local POOL below.
|
|
# shellcheck disable=SC2153
|
|
local idx=$((RANDOM % ${#POOLS[@]}))
|
|
local POOL base_out quote_out base quote base_lc quote_lc token_in token_in_lc token_out
|
|
local dec_out dec bal_out bal amt qraw quoted min_out
|
|
local swap_via="${CHAIN138_PMM_SOAK_SWAP_VIA:-pool}"
|
|
POOL="${POOLS[$idx]}"
|
|
|
|
if ! base_out="$(cast call "$POOL" '_BASE_TOKEN_()(address)' --rpc-url "$RPC" 2>/dev/null)"; then
|
|
pmm_soak_log "tick $tick skip pool=$POOL (base token read failed)"
|
|
return 0
|
|
fi
|
|
if ! quote_out="$(cast call "$POOL" '_QUOTE_TOKEN_()(address)' --rpc-url "$RPC" 2>/dev/null)"; then
|
|
pmm_soak_log "tick $tick skip pool=$POOL (quote token read failed)"
|
|
return 0
|
|
fi
|
|
base="$(printf '%s\n' "$base_out" | awk '{print $1}')"
|
|
quote="$(printf '%s\n' "$quote_out" | awk '{print $1}')"
|
|
base_lc="$(pmm_soak_to_lower "$base")"
|
|
quote_lc="$(pmm_soak_to_lower "$quote")"
|
|
|
|
if (( RANDOM % 2 == 0 )); then
|
|
token_in="$base"
|
|
token_in_lc="$base_lc"
|
|
token_out="$quote"
|
|
else
|
|
token_in="$quote"
|
|
token_in_lc="$quote_lc"
|
|
token_out="$base"
|
|
fi
|
|
|
|
if ! dec_out="$(cast call "$token_in" 'decimals()(uint8)' --rpc-url "$RPC" 2>/dev/null)"; then
|
|
pmm_soak_log "tick $tick skip pool=$POOL (decimals failed)"
|
|
return 0
|
|
fi
|
|
dec="$(printf '%s\n' "$dec_out" | awk '{print $1}')"
|
|
if ! bal_out="$(cast call "$token_in" 'balanceOf(address)(uint256)' "$trader" --rpc-url "$RPC" 2>/dev/null)"; then
|
|
pmm_soak_log "tick $tick skip pool=$POOL (balance read failed)"
|
|
return 0
|
|
fi
|
|
bal="$(printf '%s\n' "$bal_out" | awk '{print $1}')"
|
|
|
|
amt="$(pmm_soak_pick_random_amount "$token_in_lc" "$dec")"
|
|
if [[ -z "$amt" || "$amt" == "0" ]]; then
|
|
pmm_soak_log "tick $tick skip pool=$POOL (zero amount)"
|
|
return 0
|
|
fi
|
|
|
|
if (( $(echo "$bal < $amt" | bc -l) )); then
|
|
if (( $(echo "$bal == 0" | bc -l) )); then
|
|
pmm_soak_log "tick $tick skip pool=$POOL token_in=$token_in zero balance"
|
|
return 0
|
|
fi
|
|
amt=$((bal * 9 / 10))
|
|
pmm_soak_log "tick $tick clamp amount to 90% balance -> $amt"
|
|
fi
|
|
|
|
if [[ "$token_in_lc" == "$base_lc" ]]; then
|
|
if ! qraw="$(cast call "$POOL" 'querySellBase(address,uint256)(uint256,uint256)' "$trader" "$amt" --rpc-url "$RPC" 2>/dev/null)"; then
|
|
pmm_soak_log "tick $tick skip pool=$POOL querySellBase reverted amt=$amt"
|
|
return 0
|
|
fi
|
|
else
|
|
if ! qraw="$(cast call "$POOL" 'querySellQuote(address,uint256)(uint256,uint256)' "$trader" "$amt" --rpc-url "$RPC" 2>/dev/null)"; then
|
|
pmm_soak_log "tick $tick skip pool=$POOL querySellQuote reverted amt=$amt"
|
|
return 0
|
|
fi
|
|
fi
|
|
quoted="$(printf '%s\n' "$qraw" | head -1 | awk '{print $1}')"
|
|
|
|
if [[ -z "$quoted" || "$quoted" == "0" ]]; then
|
|
pmm_soak_log "tick $tick skip pool=$POOL token_in=$token_in amt=$amt (no quote)"
|
|
return 0
|
|
fi
|
|
|
|
min_out="$(echo "scale=0; (${quoted} * (10000 - ${SLIP_BPS})) / 10000" | bc)"
|
|
|
|
pmm_soak_log "tick $tick trader=$trader pool=$POOL token_in=$token_in amt=$amt quoted_out=$quoted min_out=$min_out"
|
|
|
|
if [[ "$apply" -eq 1 ]]; then
|
|
if [[ -z "$signer_pk" ]]; then
|
|
pmm_soak_log "WARN: apply=1 but empty signer_pk; skip swap"
|
|
return 0
|
|
fi
|
|
local _gas_t _gas_s _out_bal_before _out_bal_after _received
|
|
_gas_t=()
|
|
_gas_s=()
|
|
[[ -n "${CHAIN138_PMM_SOAK_GAS_LIMIT:-}" ]] && _gas_t=(--gas-limit "${CHAIN138_PMM_SOAK_GAS_LIMIT}")
|
|
[[ -n "${CHAIN138_PMM_SOAK_GAS_LIMIT:-}" ]] && _gas_s=(--gas-limit "${CHAIN138_PMM_SOAK_GAS_LIMIT}")
|
|
|
|
if [[ "$swap_via" == "integration" ]]; then
|
|
local allowance al_out can_swap
|
|
if ! al_out="$(cast call "$token_in" 'allowance(address,address)(uint256)' "$trader" "$INTEGRATION" --rpc-url "$RPC" 2>/dev/null)"; then
|
|
pmm_soak_log "WARN: allowance read failed; skip swap"
|
|
else
|
|
allowance="$(printf '%s\n' "$al_out" | awk '{print $1}')"
|
|
can_swap=1
|
|
if (( $(echo "$allowance < $amt" | bc -l) )); then
|
|
pmm_soak_log "approve $token_in -> integration amount=$amt"
|
|
if ! cast send "$token_in" 'approve(address,uint256)(bool)' "$INTEGRATION" "$amt" \
|
|
--rpc-url "$RPC" --private-key "$signer_pk" --legacy --gas-price "$GAS_WEI" "${_gas_t[@]}" >/dev/null; then
|
|
pmm_soak_log "WARN: approve failed; skip swap"
|
|
can_swap=0
|
|
fi
|
|
fi
|
|
if [[ "$can_swap" -eq 1 ]]; then
|
|
if ! cast send "$INTEGRATION" \
|
|
'swapExactIn(address,address,uint256,uint256)(uint256)' \
|
|
"$POOL" "$token_in" "$amt" "$min_out" \
|
|
--rpc-url "$RPC" --private-key "$signer_pk" --legacy --gas-price "$GAS_WEI" "${_gas_s[@]}"; then
|
|
pmm_soak_log "WARN: swap failed pool=$POOL (continuing)"
|
|
fi
|
|
fi
|
|
fi
|
|
else
|
|
# Direct pool: transfer in then sellBase / sellQuote (EOA msg.sender; avoids integration+pool revert on this chain).
|
|
if ! _out_bal_before="$(cast call "$token_out" 'balanceOf(address)(uint256)' "$trader" --rpc-url "$RPC" 2>/dev/null | awk '{print $1}')"; then
|
|
pmm_soak_log "WARN: token_out balance read failed; skip swap"
|
|
else
|
|
pmm_soak_log "swap_via=pool transfer token_in -> pool amt=$amt"
|
|
if ! cast send "$token_in" 'transfer(address,uint256)(bool)' "$POOL" "$amt" \
|
|
--rpc-url "$RPC" --private-key "$signer_pk" --legacy --gas-price "$GAS_WEI" "${_gas_t[@]}" >/dev/null; then
|
|
pmm_soak_log "WARN: transfer to pool failed pool=$POOL (continuing)"
|
|
else
|
|
if [[ "$token_in_lc" == "$base_lc" ]]; then
|
|
if ! cast send "$POOL" 'sellBase(address)(uint256)' "$trader" \
|
|
--rpc-url "$RPC" --private-key "$signer_pk" --legacy --gas-price "$GAS_WEI" "${_gas_s[@]}" >/dev/null; then
|
|
pmm_soak_log "WARN: sellBase failed pool=$POOL (continuing)"
|
|
fi
|
|
else
|
|
if ! cast send "$POOL" 'sellQuote(address)(uint256)' "$trader" \
|
|
--rpc-url "$RPC" --private-key "$signer_pk" --legacy --gas-price "$GAS_WEI" "${_gas_s[@]}" >/dev/null; then
|
|
pmm_soak_log "WARN: sellQuote failed pool=$POOL (continuing)"
|
|
fi
|
|
fi
|
|
if ! _out_bal_after="$(cast call "$token_out" 'balanceOf(address)(uint256)' "$trader" --rpc-url "$RPC" 2>/dev/null | awk '{print $1}')"; then
|
|
pmm_soak_log "WARN: token_out balance re-read failed after swap"
|
|
else
|
|
_received="$(echo "${_out_bal_after} - ${_out_bal_before}" | bc)"
|
|
if (( $(echo "${_received} < ${min_out}" | bc -l) )); then
|
|
pmm_soak_log "WARN: output ${_received} < min_out ${min_out} (slippage / partial fill)"
|
|
else
|
|
pmm_soak_log "swap_via=pool ok received_out=${_received}"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
return 0
|
|
}
|