feat: restore operator WIP — PMM JSON sync entrypoint, dotenv RPC trim + secrets, pool env alignment
- Resolve stash: merge load_deployment_env path with secure-secrets and CR/LF RPC strip - create-pmm-full-mesh-chain138.sh delegates to sync-chain138-pmm-pools-from-json.sh - env.additions.example: canonical PMM pool defaults (cUSDT/USDT per crosscheck) - Include Chain138 scripts, official mirror deploy scaffolding, and prior staged changes Made-with: Cursor
This commit is contained in:
@@ -25,7 +25,7 @@ const CONFIG = {
|
||||
rpcUrl: process.env.RPC_URL_138 || 'http://localhost:8545',
|
||||
keeperPrivateKey: process.env.KEEPER_PRIVATE_KEY,
|
||||
keeperAddress: process.env.PRICE_FEED_KEEPER_ADDRESS,
|
||||
updateInterval: parseInt(process.env.UPDATE_INTERVAL || '30') * 1000, // Convert to milliseconds
|
||||
updateInterval: parseInt(process.env.UPDATE_INTERVAL || '6') * 1000, // PMM mesh default cadence
|
||||
maxRetries: 3,
|
||||
retryDelay: 5000, // 5 seconds
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ fi
|
||||
# Configuration
|
||||
RPC_URL="${RPC_URL_138:-http://localhost:8545}"
|
||||
KEEPER_ADDRESS="${PRICE_FEED_KEEPER_ADDRESS}"
|
||||
UPDATE_INTERVAL="${UPDATE_INTERVAL:-30}" # seconds
|
||||
UPDATE_INTERVAL="${UPDATE_INTERVAL:-6}" # seconds (PMM mesh / peg tick cadence)
|
||||
MAX_RETRIES=3
|
||||
|
||||
# Colors for output
|
||||
|
||||
169
scripts/reserve/pmm-mesh-6s-automation.sh
Executable file
169
scripts/reserve/pmm-mesh-6s-automation.sh
Executable file
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env bash
|
||||
# Chain 138 PMM / oracle mesh tick — default every 6 seconds.
|
||||
# Off-chain automation (systemd recommended). Keeps oracle + PriceFeedKeeper aligned with
|
||||
# DODOPMMIntegration / ReserveSystem reads; optional WETH/ETH polling for mesh depth signals.
|
||||
#
|
||||
# Env (smom-dbis-138/.env or parent load-project-env):
|
||||
# PMM_MESH_INTERVAL_SEC=6 Sleep between ticks
|
||||
# RPC_URL_138 / RPC_URL
|
||||
# PRIVATE_KEY or DEPLOYER_PRIVATE_KEY — ETH/USD oracle push (update-oracle-price.sh)
|
||||
# KEEPER_PRIVATE_KEY + PRICE_FEED_KEEPER_ADDRESS — on-chain keeper performUpkeep when needed
|
||||
# DODO_PMM_INTEGRATION_ADDRESS — default 0x5BDc… (current canonical integration)
|
||||
# PMM_MESH_POLL_POOLS — space-separated pool addresses (default: cUSDT/cUSDC PMM pool)
|
||||
# ENABLE_MESH_ORACLE_TICK=1 Run scripts/update-oracle-price.sh each tick (skips on-chain if <1% move)
|
||||
# ENABLE_MESH_KEEPER_TICK=1 Run keeper when checkUpkeep is true
|
||||
# ENABLE_MESH_PMM_READS=1 eth_call getPoolPriceOrOracle per pool (warm path / observability)
|
||||
# ENABLE_MESH_WETH_READS=1 eth_call WETH9/WETH10 totalSupply (ETH mesh signal)
|
||||
# MESH_WETH_WRAP_WEI=0 If >0 and KEEPER_PRIVATE_KEY set: WETH9.deposit{value} (costs gas; rare)
|
||||
# MESH_WETH_WRAP_EVERY_N=60 Only wrap every N ticks when MESH_WETH_WRAP_WEI>0
|
||||
# MAX_TICKS= Exit after N ticks (empty = run forever)
|
||||
# DRY_RUN=1 Log only, no txs
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
cd "$SMOM_ROOT"
|
||||
|
||||
if [ -f "$SMOM_ROOT/.env" ]; then
|
||||
set -a
|
||||
# shellcheck source=/dev/null
|
||||
source "$SMOM_ROOT/.env"
|
||||
set +a
|
||||
fi
|
||||
|
||||
# Proxmox repo root .env (parent of smom-dbis-138) if key not in submodule .env
|
||||
REPO_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
|
||||
if [ -z "${PRIVATE_KEY:-}" ] && [ -f "$REPO_ROOT/.env" ]; then
|
||||
set -a
|
||||
# shellcheck source=/dev/null
|
||||
source "$REPO_ROOT/.env" 2>/dev/null || true
|
||||
set +a
|
||||
fi
|
||||
|
||||
RPC="${RPC_URL_138:-${RPC_URL:-http://192.168.11.211:8545}}"
|
||||
INTERVAL="${PMM_MESH_INTERVAL_SEC:-6}"
|
||||
DODO="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-0x5BDc62f1ae7D630c37A8B363a1d49845356Ee72d}}"
|
||||
# Canonical cUSDT/cUSDC PMM pool on Chain 138 (current integration)
|
||||
DEFAULT_POOLS="0xff8d3b8fDF7B112759F076B69f4271D4209C0849"
|
||||
POOLS="${PMM_MESH_POLL_POOLS:-$DEFAULT_POOLS}"
|
||||
WETH9="${WETH9_ADDRESS:-0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2}"
|
||||
WETH10="${WETH10_ADDRESS:-0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f}"
|
||||
|
||||
ENABLE_ORACLE="${ENABLE_MESH_ORACLE_TICK:-1}"
|
||||
ENABLE_KEEPER="${ENABLE_MESH_KEEPER_TICK:-1}"
|
||||
ENABLE_PMM_READS="${ENABLE_MESH_PMM_READS:-1}"
|
||||
ENABLE_WETH_READS="${ENABLE_MESH_WETH_READS:-1}"
|
||||
WRAP_WEI="${MESH_WETH_WRAP_WEI:-0}"
|
||||
WRAP_EVERY_N="${MESH_WETH_WRAP_EVERY_N:-60}"
|
||||
# Besu often needs an explicit gas price for replacement / mempool policy.
|
||||
MESH_CAST_GAS_PRICE="${MESH_CAST_GAS_PRICE:-2gwei}"
|
||||
|
||||
ORACLE_PK="${PRIVATE_KEY:-${DEPLOYER_PRIVATE_KEY:-}}"
|
||||
KEEPER_PK="${KEEPER_PRIVATE_KEY:-${PRIVATE_KEY:-}}"
|
||||
|
||||
log() { echo "[$(date -Iseconds)] $*"; }
|
||||
|
||||
eth_call_price() {
|
||||
local to="$1" data="$2"
|
||||
cast rpc eth_call "{\"to\":\"$to\",\"data\":\"$data\"}" latest --rpc-url "$RPC" 2>/dev/null | tr -d '\n\"' || true
|
||||
}
|
||||
|
||||
tick_pmm_reads() {
|
||||
[ "$ENABLE_PMM_READS" = "1" ] || return 0
|
||||
[ -n "$DODO" ] || return 0
|
||||
for pool in $POOLS; do
|
||||
[ "$pool" = "0x0000000000000000000000000000000000000000" ] && continue
|
||||
data=$(cast calldata "getPoolPriceOrOracle(address)" "$pool" 2>/dev/null) || continue
|
||||
out=$(eth_call_price "$DODO" "$data")
|
||||
if [ -n "$out" ]; then
|
||||
log "PMM getPoolPriceOrOracle($pool) -> $out"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
tick_weth_reads() {
|
||||
[ "$ENABLE_WETH_READS" = "1" ] || return 0
|
||||
for w in "$WETH9" "$WETH10"; do
|
||||
data=$(cast calldata "totalSupply()" 2>/dev/null) || continue
|
||||
out=$(eth_call_price "$w" "$data")
|
||||
if [ -n "$out" ]; then
|
||||
log "WETH totalSupply($w) raw -> $out"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
tick_keeper() {
|
||||
[ "$ENABLE_KEEPER" = "1" ] || return 0
|
||||
local k="${PRICE_FEED_KEEPER_ADDRESS:-}"
|
||||
[ -n "$k" ] && [ -n "$KEEPER_PK" ] || { log "keeper tick skipped (set PRICE_FEED_KEEPER_ADDRESS + KEEPER_PRIVATE_KEY)"; return 0; }
|
||||
local raw dec first
|
||||
raw="$(cast rpc eth_call "{\"to\":\"$k\",\"data\":\"$(cast calldata "checkUpkeep()")\"}" latest --rpc-url "$RPC" 2>/dev/null | tr -d '\n\"')" || return 0
|
||||
dec="$(cast abi-decode "checkUpkeep()(bool,address[])" "$raw" 2>/dev/null)" || return 0
|
||||
first=$(echo "$dec" | head -1)
|
||||
if [ "$first" = "true" ]; then
|
||||
log "keeper checkUpkeep=true; performUpkeep"
|
||||
if [ -n "${DRY_RUN:-}" ]; then
|
||||
log "[dry-run] cast send performUpkeep"
|
||||
return 0
|
||||
fi
|
||||
if ! cast send "$k" "performUpkeep()" --rpc-url "$RPC" --private-key "$KEEPER_PK" \
|
||||
--legacy --gas-limit 500000 --gas-price "$MESH_CAST_GAS_PRICE"; then
|
||||
log "WARN: performUpkeep failed (RPC / gas / nonce); next tick in ${INTERVAL}s"
|
||||
fi
|
||||
else
|
||||
log "keeper checkUpkeep=false (no tx)"
|
||||
fi
|
||||
}
|
||||
|
||||
tick_oracle() {
|
||||
[ "$ENABLE_ORACLE" = "1" ] || return 0
|
||||
[ -n "$ORACLE_PK" ] || { log "oracle tick skipped (set PRIVATE_KEY)"; return 0; }
|
||||
if [ -n "${DRY_RUN:-}" ]; then
|
||||
log "[dry-run] update-oracle-price.sh"
|
||||
return 0
|
||||
fi
|
||||
# Exits 0 when price unchanged (<1%) or on success; failures must not stop the loop
|
||||
if ! bash "$SMOM_ROOT/scripts/update-oracle-price.sh" "$RPC"; then
|
||||
log "WARN: update-oracle-price.sh failed (rate limit / RPC / gas); next tick in ${INTERVAL}s"
|
||||
fi
|
||||
}
|
||||
|
||||
tick_weth_wrap() {
|
||||
(( ${WRAP_WEI:-0} > 0 )) || return 0
|
||||
[ -n "$KEEPER_PK" ] || { log "WETH wrap skipped (no KEEPER_PRIVATE_KEY)"; return 0; }
|
||||
local n="${TICK_COUNT:-0}"
|
||||
[ "$WRAP_EVERY_N" -gt 0 ] || WRAP_EVERY_N=1
|
||||
if (( n % WRAP_EVERY_N != 0 )); then
|
||||
return 0
|
||||
fi
|
||||
log "WETH9.deposit value=$WRAP_WEI wei (mesh activity)"
|
||||
if [ -n "${DRY_RUN:-}" ]; then
|
||||
log "[dry-run] cast send deposit"
|
||||
return 0
|
||||
fi
|
||||
if ! cast send "$WETH9" "deposit()" --value "$WRAP_WEI" --rpc-url "$RPC" --private-key "$KEEPER_PK" \
|
||||
--legacy --gas-limit 150000 --gas-price "$MESH_CAST_GAS_PRICE"; then
|
||||
log "WARN: WETH9.deposit failed; next tick in ${INTERVAL}s"
|
||||
fi
|
||||
}
|
||||
|
||||
TICK_COUNT=0
|
||||
log "=== PMM mesh automation === interval=${INTERVAL}s RPC=$RPC"
|
||||
log "DODO=$DODO pools=$POOLS"
|
||||
|
||||
while true; do
|
||||
TICK_COUNT=$((TICK_COUNT + 1))
|
||||
log "--- tick $TICK_COUNT ---"
|
||||
tick_pmm_reads
|
||||
tick_weth_reads
|
||||
tick_keeper
|
||||
tick_oracle
|
||||
tick_weth_wrap
|
||||
|
||||
if [ -n "${MAX_TICKS:-}" ] && [ "$TICK_COUNT" -ge "$MAX_TICKS" ]; then
|
||||
log "MAX_TICKS=$MAX_TICKS reached, exit"
|
||||
break
|
||||
fi
|
||||
sleep "$INTERVAL"
|
||||
done
|
||||
25
scripts/reserve/set-price-feed-keeper-interval.sh
Executable file
25
scripts/reserve/set-price-feed-keeper-interval.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
# One-shot: set PriceFeedKeeper.updateInterval to match PMM mesh cadence (default 6 seconds).
|
||||
# Caller must hold DEFAULT_ADMIN_ROLE on the keeper.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/reserve/set-price-feed-keeper-interval.sh [seconds]
|
||||
# Env: PRICE_FEED_KEEPER_ADDRESS, PRIVATE_KEY (admin), RPC_URL_138
|
||||
#
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
[ -f .env ] && set -a && source .env && set +a
|
||||
|
||||
SEC="${1:-6}"
|
||||
K="${PRICE_FEED_KEEPER_ADDRESS:-}"
|
||||
RPC="${RPC_URL_138:-${RPC_URL:-http://192.168.11.211:8545}}"
|
||||
PK="${PRIVATE_KEY:-}"
|
||||
|
||||
[ -n "$K" ] || { echo "PRICE_FEED_KEEPER_ADDRESS required"; exit 1; }
|
||||
[ -n "$PK" ] || { echo "PRIVATE_KEY required (admin on keeper)"; exit 1; }
|
||||
|
||||
echo "Setting PriceFeedKeeper $K updateInterval to ${SEC}s"
|
||||
cast send "$K" "setUpdateInterval(uint256)" "$SEC" \
|
||||
--rpc-url "$RPC" --private-key "$PK" --legacy --gas-limit 120000
|
||||
echo "Done."
|
||||
68
scripts/reserve/sync-weth-mock-price.sh
Executable file
68
scripts/reserve/sync-weth-mock-price.sh
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
# Push ETH/USD into the on-chain WETH MockPriceFeed used by OraclePriceFeed / PriceFeedKeeper
|
||||
# (avoids Besu vs aggregator updatedAt skew on the public ETH/USD aggregator).
|
||||
#
|
||||
# Env: smom-dbis-138/.env — RPC_URL_138, PRIVATE_KEY, optional CHAIN138_WETH_MOCK_PRICE_FEED,
|
||||
# COINGECKO_API_KEY (Pro URL + header when set).
|
||||
# Usage: ./scripts/reserve/sync-weth-mock-price.sh [rpc-url]
|
||||
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
cd "$SMOM_ROOT"
|
||||
|
||||
if [ -f "$SMOM_ROOT/.env" ]; then
|
||||
set -a
|
||||
# shellcheck source=/dev/null
|
||||
source "$SMOM_ROOT/.env"
|
||||
set +a
|
||||
fi
|
||||
|
||||
RPC="${1:-${RPC_URL_138:-${RPC_URL:-http://192.168.11.211:8545}}}"
|
||||
MOCK="${CHAIN138_WETH_MOCK_PRICE_FEED:-0x3e8725b8De386feF3eFE5678c92eA6aDB41992B2}"
|
||||
CG_UA="${COINGECKO_USER_AGENT:-proxmox-chain138-oracle/1.0 (dbis-138)}"
|
||||
|
||||
if [ -z "${PRIVATE_KEY:-}" ]; then
|
||||
echo "ERROR: PRIVATE_KEY required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
fetch_usd() {
|
||||
local u="0"
|
||||
if [ -n "${COINGECKO_API_KEY:-}" ]; then
|
||||
u=$(curl -s --max-time 20 -A "$CG_UA" -H "x-cg-demo-api-key: $COINGECKO_API_KEY" \
|
||||
'https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd' | \
|
||||
python3 -c "import sys, json; d=json.load(sys.stdin); print(d.get('ethereum',{}).get('usd',0) or 0)" 2>/dev/null || echo 0)
|
||||
fi
|
||||
if { [ "$u" = "0" ] || [ -z "$u" ]; } && [ -n "${COINGECKO_API_KEY:-}" ]; then
|
||||
u=$(curl -s --max-time 20 -A "$CG_UA" -H "x-cg-pro-api-key: $COINGECKO_API_KEY" \
|
||||
'https://pro-api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd' | \
|
||||
python3 -c "import sys, json; d=json.load(sys.stdin); print(d.get('ethereum',{}).get('usd',0) or 0)" 2>/dev/null || echo 0)
|
||||
fi
|
||||
if [ "$u" = "0" ] || [ -z "$u" ]; then
|
||||
u=$(curl -s --max-time 20 -A "$CG_UA" 'https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd' | \
|
||||
python3 -c "import sys, json; d=json.load(sys.stdin); print(d.get('ethereum',{}).get('usd',0) or 0)" 2>/dev/null || echo 0)
|
||||
fi
|
||||
echo "$u"
|
||||
}
|
||||
|
||||
USD="$(fetch_usd)"
|
||||
if [ "$(python3 -c "print(1 if float('$USD')>0 else 0)" 2>/dev/null || echo 0)" != 1 ]; then
|
||||
echo "WARN: CoinGecko failed, trying Binance..." >&2
|
||||
USD=$(curl -s 'https://api.binance.com/api/v3/ticker/price?symbol=ETHUSDT' | \
|
||||
python3 -c "import sys, json; print(json.load(sys.stdin).get('price',0) or 0)" 2>/dev/null || echo 0)
|
||||
fi
|
||||
|
||||
PRICE_I256="$(python3 -c "u=float('$USD'); print(int(round(u*1e8)))")"
|
||||
if [ "$PRICE_I256" -le 0 ]; then
|
||||
echo "ERROR: invalid ETH price: $USD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Mock $MOCK updatePrice(int256) = $PRICE_I256 (8 dec; USD ~ $USD)"
|
||||
cast send "$MOCK" "updatePrice(int256)" "$PRICE_I256" \
|
||||
--rpc-url "$RPC" \
|
||||
--private-key "$PRIVATE_KEY" \
|
||||
--legacy \
|
||||
--gas-limit 200000 \
|
||||
--gas-price "${MESH_CAST_GAS_PRICE:-2gwei}"
|
||||
Reference in New Issue
Block a user