Files
proxmox/scripts/deployment/mint-cwusdc-ei-matrix-wallets.sh
defiQUG dd02f4b59b
All checks were successful
Deploy to Phoenix / validate (push) Successful in 1m11s
Deploy to Phoenix / deploy (push) Successful in 43s
Deploy to Phoenix / deploy-atomic-swap-dapp (push) Successful in 1m32s
phoenix-deploy Deployed to cloudflare-sync
Deploy to Phoenix / cloudflare (push) Successful in 38s
Enhance .env configuration with Infura support and add new RPC endpoints for various networks. Update package.json with new deployment scripts for Engine X. Improve public LP compliance documentation in runbooks and scripts, including guidance for public pair repairs and funding strategies.
2026-05-07 18:19:37 -07:00

340 lines
12 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Mint Ethereum mainnet cWUSDC directly to each address in config/pmm-soak-wallet-grid.json
# (EI matrix). Requires PRIVATE_KEY with MINTER_ROLE on the cWUSDC token.
#
# Modes (exactly one):
# --mint-raw R Same raw units minted to every wallet in the slice.
# --total-mint-raw B Total supply to mint across the slice, split with ±spread
# then renormalized to B (same algorithm as transfer distribution).
#
# Usage:
# ./scripts/deployment/mint-cwusdc-ei-matrix-wallets.sh [--dry-run] [--limit N] [--offset N|--resume-next]
# (--mint-raw R | --total-mint-raw B [--spread-pct S])
#
# --quiet-dry-run With --dry-run, suppress per-wallet lines.
# --legacy Pass --legacy to cast send.
#
# Env: ETHEREUM_MAINNET_RPC, CWUSDC_MAINNET, PRIVATE_KEY,
# EI_MATRIX_MINT_GAS_EST (default 60000), EI_MATRIX_GAS_HEADROOM_BPS (default 10500),
# EI_MATRIX_SKIP_GAS_CHECK=1 to bypass ETH preflight.
#
# Progress: reports/status/ei-matrix-cwusdc-mint-last-idx.txt
# Failures: reports/status/ei-matrix-cwusdc-mint-failures.log
# Lock: reports/status/ei-matrix-cwusdc-mint.lock
#
# On-chain: cWUSDC uses CompliantWrappedToken-style mint(address,uint256) for MINTER_ROLE.
# If mint reverts (reserve policy, roles), fix on-chain state before retrying.
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
cd "$PROJECT_ROOT"
DRY_RUN=false
LIMIT=""
OFFSET="0"
OFFSET_EXPLICIT=false
RESUME_NEXT=false
SPREAD_PCT="${EI_MATRIX_SPREAD_PCT:-15}"
CAST_LEGACY=false
QUIET_DRY_RUN=false
MINT_RAW=""
TOTAL_MINT_RAW=""
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run) DRY_RUN=true; shift ;;
--quiet-dry-run) QUIET_DRY_RUN=true; shift ;;
--limit) LIMIT="${2:?}"; shift 2 ;;
--resume-next) RESUME_NEXT=true; shift ;;
--offset) OFFSET="${2:?}"; OFFSET_EXPLICIT=true; shift 2 ;;
--spread-pct) SPREAD_PCT="${2:?}"; shift 2 ;;
--mint-raw) MINT_RAW="${2:?}"; shift 2 ;;
--total-mint-raw) TOTAL_MINT_RAW="${2:?}"; shift 2 ;;
--legacy) CAST_LEGACY=true; shift ;;
*) echo "Unknown arg: $1" >&2; exit 1 ;;
esac
done
LAST_IDX_FILE="${EI_MATRIX_CWUSDC_MINT_LAST_IDX_FILE:-${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-mint-last-idx.txt}"
if $RESUME_NEXT && $OFFSET_EXPLICIT; then
echo "Use only one of --offset or --resume-next." >&2
exit 1
fi
if $RESUME_NEXT; then
[[ -f "$LAST_IDX_FILE" ]] || { echo "Missing last-index file for --resume-next: $LAST_IDX_FILE" >&2; exit 1; }
_last="$(tr -d '[:space:]' < "$LAST_IDX_FILE" || echo "")"
[[ -n "$_last" ]] || { echo "Empty $LAST_IDX_FILE" >&2; exit 1; }
OFFSET=$((_last + 1))
echo "Resume-next (mint): last completed idx=$_last → offset=$OFFSET"
fi
if [[ -n "$MINT_RAW" && -n "$TOTAL_MINT_RAW" ]]; then
echo "Use only one of --mint-raw or --total-mint-raw." >&2
exit 1
fi
if [[ -z "$MINT_RAW" && -z "$TOTAL_MINT_RAW" ]]; then
echo "Set --mint-raw or --total-mint-raw." >&2
exit 1
fi
# shellcheck disable=SC1091
source "$PROJECT_ROOT/scripts/lib/load-project-env.sh"
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
PUBLIC_ETH_RPC="${ETHEREUM_MAINNET_PUBLIC_RPC:-https://ethereum-rpc.publicnode.com}"
RPC="${ETHEREUM_MAINNET_RPC:-${RPC_URL_1:-${ETH_MAINNET_RPC_URL:-$PUBLIC_ETH_RPC}}}"
BALANCE_RPC="${EI_MATRIX_BALANCE_RPC:-$RPC}"
LOCK_FILE="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-mint.lock"
MANIFEST_DIR="${PROJECT_ROOT}/reports/status"
mkdir -p "$MANIFEST_DIR"
exec 200>"$LOCK_FILE"
if ! flock -n 200; then
echo "Another mint-cwusdc-ei-matrix-wallets.sh is already running (lock: $LOCK_FILE)." >&2
exit 1
fi
GRID="$PROJECT_ROOT/config/pmm-soak-wallet-grid.json"
DEPLOYER_CANONICAL="0x4A666F96fC8764181194447A7dFdb7d471b301C8"
[[ -f "$GRID" ]] || { echo "Missing $GRID" >&2; exit 1; }
command -v cast &>/dev/null || { echo "cast required" >&2; exit 1; }
command -v jq &>/dev/null || { echo "jq required" >&2; exit 1; }
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY not set" >&2; exit 1; }
FROM_ADDR=$(cast wallet address --private-key "$PRIVATE_KEY")
CHAIN_ID=$(cast chain-id --rpc-url "$RPC" 2>/dev/null | tr -d '[:space:]' || true)
[[ -n "$CHAIN_ID" ]] || CHAIN_ID="1"
if [[ "$CHAIN_ID" != "1" ]]; then
echo "[WARN] chain-id=$CHAIN_ID (expected 1)." >&2
fi
pending_nonce() {
local resp hex
resp=$(curl -sS -X POST "$RPC" -H "Content-Type: application/json" \
-d "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionCount\",\"params\":[\"${FROM_ADDR}\",\"pending\"],\"id\":1}" 2>/dev/null) || return 1
hex=$(echo "$resp" | jq -r '.result // empty')
[[ -n "$hex" ]] || return 1
cast to-dec "$hex"
}
token_decimals() {
cast call "$CWUSDC" 'decimals()(uint8)' --rpc-url "$BALANCE_RPC" 2>/dev/null | awk '{print $1}'
}
generate_spread_amounts_raw() {
local count="$1" budget="$2" spread="$3"
python3 - "$count" "$budget" "$spread" <<'PY'
import random
import sys
n = int(sys.argv[1])
budget = int(sys.argv[2])
spread = float(sys.argv[3])
if n <= 0:
sys.exit("count must be positive")
if budget < 0:
sys.exit("budget must be non-negative")
if spread < 0 or spread > 100:
sys.exit("spread-pct must be in [0, 100]")
base = 10000
low_w = max(1, (100 * base - int(spread * base)) // 100)
high_w = (100 * base + int(spread * base)) // 100
w = [random.randint(low_w, high_w) for _ in range(n)]
s = sum(w)
raw = [(budget * wi) // s for wi in w]
rem = budget - sum(raw)
for i in range(rem):
raw[i % n] += 1
for x in raw:
print(x)
PY
}
stream_addresses() {
if [[ -n "${LIMIT:-}" ]]; then
jq -r --argjson o "$OFFSET" --argjson l "$LIMIT" '.wallets[$o:$o+$l][] | .address' "$GRID"
else
jq -r --argjson o "$OFFSET" '.wallets[$o:][] | .address' "$GRID"
fi
}
ERR_LOG="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-mint-failures.log"
LAST_IDX="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-mint-last-idx.txt"
matrix_try_mint() {
local addr="$1" raw_amt="$2" idx="$3"
local dec human out tx attempt=1
dec="${DECIMALS:-6}"
if [[ "$raw_amt" == "0" ]]; then
echo "[skip] idx=$idx $addr zero raw"
return 0
fi
human=$(python3 -c "d=int('$dec'); a=int('$raw_amt'); print(f'{a / (10**d):.{min(d,8)}f}')" 2>/dev/null || echo "$raw_amt")
if $DRY_RUN; then
if ! $QUIET_DRY_RUN; then
echo "[dry-run] idx=$idx $addr raw=$raw_amt (~$human)"
fi
return 0
fi
local cast_extra=()
$CAST_LEGACY && cast_extra+=(--legacy)
while [[ "$attempt" -le 2 ]]; do
if out=$(cast send "$CWUSDC" "mint(address,uint256)" "$addr" "$raw_amt" \
--rpc-url "$RPC" --private-key "$PRIVATE_KEY" \
--nonce "$NONCE" "${cast_extra[@]}" 2>&1); then
tx=$(echo "$out" | tail -n1)
echo "[ok] idx=$idx nonce=$NONCE $addr raw=$raw_amt (~$human) tx=$tx"
sent=$((sent + 1))
NONCE=$((NONCE + 1))
echo "$idx" > "$LAST_IDX"
return 0
fi
if [[ "$attempt" -eq 1 ]] && echo "$out" | grep -qi 'nonce too low'; then
NONCE=$(pending_nonce) || true
echo "[retry] idx=$idx nonce refreshed to $NONCE (nonce too low)" >&2
attempt=$((attempt + 1))
continue
fi
echo "[fail] idx=$idx nonce=$NONCE $addr $out" | tee -a "$ERR_LOG" >&2
failed=$((failed + 1))
NONCE=$(pending_nonce) || true
return 0
done
}
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "EI matrix cWUSDC mint (mainnet)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "RPC: $RPC"
echo "Token: $CWUSDC"
echo "Signer: $FROM_ADDR"
echo "Grid: $GRID"
echo "Dry-run: $DRY_RUN Quiet: $QUIET_DRY_RUN"
echo "Offset: $OFFSET Limit: ${LIMIT:-all}"
if [[ -n "$MINT_RAW" ]]; then
echo "Mode: fixed --mint-raw $MINT_RAW per wallet"
else
echo "Mode: --total-mint-raw $TOTAL_MINT_RAW spread: ±${SPREAD_PCT}% normalized"
fi
echo ""
if [[ "${FROM_ADDR,,}" != "${DEPLOYER_CANONICAL,,}" ]]; then
echo "[WARN] Signer is not canonical deployer $DEPLOYER_CANONICAL — minter role may still be granted."
echo ""
fi
DECIMALS=$(token_decimals || echo "6")
ADDR_TMP=$(mktemp)
AMOUNTS_TMP=$(mktemp)
cleanup_tmp() {
[[ -f "$ADDR_TMP" ]] && rm -f "$ADDR_TMP"
[[ -f "$AMOUNTS_TMP" ]] && rm -f "$AMOUNTS_TMP"
}
trap cleanup_tmp EXIT
stream_addresses > "$ADDR_TMP"
WALLET_COUNT=$(wc -l < "$ADDR_TMP" | tr -d '[:space:]')
if [[ -z "$WALLET_COUNT" || "$WALLET_COUNT" -eq 0 ]]; then
echo "No wallets in range (offset=$OFFSET limit=${LIMIT:-all})." >&2
exit 1
fi
if [[ -n "$MINT_RAW" ]]; then
awk -v r="$MINT_RAW" '{print r}' "$ADDR_TMP" > "$AMOUNTS_TMP"
BUDGET_RAW=$((MINT_RAW * WALLET_COUNT))
else
BUDGET_RAW="$TOTAL_MINT_RAW"
if [[ "$BUDGET_RAW" -le 0 ]]; then
echo "total-mint-raw must be positive." >&2
exit 1
fi
generate_spread_amounts_raw "$WALLET_COUNT" "$BUDGET_RAW" "$SPREAD_PCT" > "$AMOUNTS_TMP"
fi
SUM_CHECK=$(awk '{s+=$1} END {print s}' "$AMOUNTS_TMP")
if [[ "$SUM_CHECK" != "$BUDGET_RAW" ]]; then
echo "INTERNAL: amount sum $SUM_CHECK != budget $BUDGET_RAW" >&2
exit 1
fi
AMOUNTS_SHA256=$(sha256sum "$AMOUNTS_TMP" | awk '{print $1}')
TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
MANIFEST="$MANIFEST_DIR/ei-matrix-cwusdc-mint-manifest-${TS//:/-}.json"
cat >"$MANIFEST" <<EOF
{
"version": 1,
"kind": "ei-matrix-cwusdc-mint",
"timestamp": "$TS",
"chainId": 1,
"token": "$CWUSDC",
"signer": "$FROM_ADDR",
"offset": $OFFSET,
"limit": ${LIMIT:-null},
"walletCount": $WALLET_COUNT,
"budgetRaw": "$BUDGET_RAW",
"fixedMintRaw": ${MINT_RAW:-null},
"spreadPct": ${SPREAD_PCT},
"amountsSha256": "$AMOUNTS_SHA256",
"manifestPath": "$MANIFEST"
}
EOF
echo "Manifest: $MANIFEST"
echo "Amounts SHA256: $AMOUNTS_SHA256"
echo ""
GAS_EST="${EI_MATRIX_MINT_GAS_EST:-60000}"
HEADROOM_BPS="${EI_MATRIX_GAS_HEADROOM_BPS:-10500}"
ETH_WEI=$(cast balance "$FROM_ADDR" --rpc-url "$BALANCE_RPC" 2>/dev/null | awk '{print $1}' || echo "0")
ETH_HUMAN=$(python3 -c "print(f'{int(\"$ETH_WEI\") / 1e18:.6f}')" 2>/dev/null || echo "?")
echo "Signer ETH (gas): ${ETH_WEI} wei (~$ETH_HUMAN ETH)"
if ! $DRY_RUN && [[ "${EI_MATRIX_SKIP_GAS_CHECK:-}" != "1" ]]; then
GAS_PRICE_WEI=$(cast gas-price --rpc-url "$RPC" 2>/dev/null | awk '{print $1}' | head -1)
[[ -n "$GAS_PRICE_WEI" ]] || GAS_PRICE_WEI=0
MIN_WEI=$(python3 -c "c=int('$WALLET_COUNT'); g=int('$GAS_EST'); p=int('$GAS_PRICE_WEI'); b=int('$HEADROOM_BPS'); print(c*g*p*b//10000)")
if python3 -c "import sys; sys.exit(0 if int('$ETH_WEI') >= int('$MIN_WEI') else 1)"; then
echo "Gas preflight OK: est ${GAS_EST} gas/tx × $WALLET_COUNT × gasPrice $GAS_PRICE_WEI × (${HEADROOM_BPS}/10000) ≈ $MIN_WEI wei."
else
echo "Insufficient ETH for gas preflight. Need ≈ $MIN_WEI wei." >&2
echo "Set EI_MATRIX_SKIP_GAS_CHECK=1 to override (operator risk)." >&2
exit 1
fi
fi
echo ""
echo "Sample (first 3, last 3):"
_s_idx=$OFFSET
while IFS= read -r s_addr && IFS= read -r s_raw <&3; do
h=$(python3 -c "d=int('$DECIMALS'); a=int('$s_raw'); print(f'{a / (10**d):.6f}')" 2>/dev/null || echo "$s_raw")
echo " idx=$_s_idx $s_addr raw=$s_raw (~$h cWUSDC)"
_s_idx=$((_s_idx + 1))
done < <(head -3 "$ADDR_TMP") 3< <(head -3 "$AMOUNTS_TMP")
_s_idx=$((OFFSET + WALLET_COUNT - 3))
while IFS= read -r s_addr && IFS= read -r s_raw <&3; do
h=$(python3 -c "d=int('$DECIMALS'); a=int('$s_raw'); print(f'{a / (10**d):.6f}')" 2>/dev/null || echo "$s_raw")
echo " idx=$_s_idx $s_addr raw=$s_raw (~$h cWUSDC)"
_s_idx=$((_s_idx + 1))
done < <(tail -3 "$ADDR_TMP") 3< <(tail -3 "$AMOUNTS_TMP")
echo ""
sent=0
failed=0
idx=$OFFSET
NONCE=$(pending_nonce) || { echo "Could not read pending nonce" >&2; exit 1; }
echo "Starting nonce (pending): $NONCE"
echo ""
while IFS=$'\t' read -r addr raw_amt; do
matrix_try_mint "$addr" "$raw_amt" "$idx"
idx=$((idx + 1))
done < <(paste -d $'\t' "$ADDR_TMP" "$AMOUNTS_TMP")
if $DRY_RUN; then
echo "Dry-run complete. Indices covered: $OFFSET..$((idx - 1))."
else
echo "Done. Mint txs attempted: sent=$sent failed=$failed"
fi