Files
proxmox/scripts/deployment/mint-cwusdc-ei-matrix-wallets.sh
defiQUG 4ebf2d7902
Some checks failed
Deploy to Phoenix / validate (push) Failing after 1s
Deploy to Phoenix / deploy (push) Has been skipped
Deploy to Phoenix / deploy-atomic-swap-dapp (push) Has been skipped
Deploy to Phoenix / cloudflare (push) Has been skipped
chore(repo): sync operator workspace (config, scripts, docs, multi-chain)
Add optional Cosmos/Engine-X/act-runner templates, CWUSDC/EI-matrix tooling,
non-EVM route planner in multi-chain-execution (tests passing), token list and
extraction updates, and documentation (MetaMask matrix, GRU/CWUSDC packets).

Ignore institutional evidence tarballs/sha256 under reports/status.

Validated with: bash scripts/verify/run-all-validation.sh --skip-genesis

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 16:25:08 -07:00

349 lines
12 KiB
Bash
Executable File
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 mints:"
if [[ "$WALLET_COUNT" -le 6 ]]; then
_s_idx=$OFFSET
while IFS=$'\t' read -r s_addr s_raw; 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 < <(paste -d $'\t' "$ADDR_TMP" "$AMOUNTS_TMP")
else
_s_idx=$OFFSET
while IFS=$'\t' read -r s_addr s_raw; 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 < <(paste -d $'\t' "$ADDR_TMP" "$AMOUNTS_TMP" | head -3)
_s_idx=$((OFFSET + WALLET_COUNT - 3))
while IFS=$'\t' read -r s_addr s_raw; 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 < <(paste -d $'\t' "$ADDR_TMP" "$AMOUNTS_TMP" | tail -3)
fi
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