fix(pmm-soak): shellcheck-clean bots/tick/bootstrap; expand CI ShellCheck
- 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
This commit is contained in:
208
scripts/lib/pmm-soak-chain138-tick.sh
Executable file
208
scripts/lib/pmm-soak-chain138-tick.sh
Executable file
@@ -0,0 +1,208 @@
|
||||
#!/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
|
||||
}
|
||||
Reference in New Issue
Block a user