chore: sync workspace — configs, docs, scripts, CI, pnpm, submodules
- Submodule pins: dbis_core, cross-chain-pmm-lps, mcp-proxmox (local, push may be pending), metamask-integration, smom-dbis-138 - Atomic swap + cross-chain-pmm-lops-publish, deploy-portal workflow, phoenix deploy-targets, routing/aggregator matrices - Docs, token-lists, forge proxy, phoenix API, runbooks, verify scripts Made-with: Cursor
This commit is contained in:
@@ -1,21 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
# Check deployer wallet balances on ChainID 138 (native ETH + ERC-20: WETH, WETH10, LINK, cUSDT, cUSDC).
|
||||
# Output half of each balance as the funding plan for the three PMM liquidity pools.
|
||||
# Check deployer wallet balances on ChainID 138 (native ETH + ERC-20: WETH, WETH10, LINK, cUSDT, cUSDC, cEURT).
|
||||
# Output half of each balance as the funding plan for the canonical PMM liquidity pools.
|
||||
#
|
||||
# Usage:
|
||||
# RPC_URL_138=https://rpc-core.d-bis.org ./scripts/deployment/check-deployer-balance-chain138-and-funding-plan.sh
|
||||
# ./scripts/deployment/check-deployer-balance-chain138-and-funding-plan.sh
|
||||
# RPC_URL_138=https://rpc-core.d-bis.org ./scripts/deployment/... # override
|
||||
# # Or from smom-dbis-138: source .env then run from repo root with RPC_URL_138 set
|
||||
#
|
||||
# Requires: cast (foundry), jq (optional). RPC_URL_138 must be set and reachable.
|
||||
# Requires: cast (foundry), jq (optional). Defaults to public Chain 138 HTTP RPC if RPC_URL_138 unset.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DEPLOYER="${DEPLOYER_ADDRESS:-0x4A666F96fC8764181194447A7dFdb7d471b301C8}"
|
||||
RPC="${RPC_URL_138:-}"
|
||||
if [ -z "$RPC" ]; then
|
||||
echo "ERROR: Set RPC_URL_138 (e.g. https://rpc-core.d-bis.org or http://192.168.11.211:8545)"
|
||||
exit 1
|
||||
fi
|
||||
CHAIN138_PUBLIC_RPC_DEFAULT="https://rpc-http-pub.d-bis.org"
|
||||
RPC="${RPC_URL_138:-${CHAIN138_PUBLIC_RPC_URL:-$CHAIN138_PUBLIC_RPC_DEFAULT}}"
|
||||
|
||||
CHAIN_ID=138
|
||||
|
||||
@@ -25,27 +23,48 @@ WETH10="0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f"
|
||||
LINK="0xb7721dD53A8c629d9f1Ba31a5819AFe250002b03"
|
||||
CUSDT="0x93E66202A11B1772E55407B32B44e5Cd8eda7f22"
|
||||
CUSDC="0xf22258f57794CC8E06237084b353Ab30fFfa640b"
|
||||
CEURT="0xdf4b71c61E5912712C1Bdd451416B9aC26949d72"
|
||||
USDT_OFFICIAL="0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1"
|
||||
|
||||
# PMM pool addresses (from LIQUIDITY_POOLS_MASTER_MAP / ADDRESS_MATRIX)
|
||||
POOL_CUSDTCUSDC="0x9fcB06Aa1FD5215DC0E91Fd098aeff4B62fEa5C8"
|
||||
POOL_CUSDTUSDT="0x6fc60DEDc92a2047062294488539992710b99D71"
|
||||
POOL_CUSDCUSDC="0x90bd9Bf18Daa26Af3e814ea224032d015db58Ea5"
|
||||
POOL_CUSDTCUSDC="0x9e89bAe009adf128782E19e8341996c596ac40dC"
|
||||
POOL_CUSDTUSDT="0x866Cb44b59303d8dc5f4F9E3E7A8e8b0bf238d66"
|
||||
POOL_CUSDCUSDC="0xc39B7D0F40838cbFb54649d327f49a6DAC964062"
|
||||
POOL_CUSDTWETH="0xaE38a008Ba4Dbf8D9F141D03e9dC8f7Dbe0ce17c"
|
||||
POOL_CUSDCWETH="0xAAE68830a55767722618E869882c6Ed064Cc1eb2"
|
||||
POOL_CEURTWETH="0x4a64c886cedF00db42ea5B946D6b304121ad9529"
|
||||
|
||||
get_balance() {
|
||||
local addr="$1"
|
||||
cast call "$addr" "balanceOf(address)(uint256)" "$DEPLOYER" --rpc-url "$RPC" 2>/dev/null || echo "0"
|
||||
cast call "$addr" "balanceOf(address)(uint256)" "$DEPLOYER" --rpc-url "$RPC" 2>/dev/null | awk '{print $1}' || echo "0"
|
||||
}
|
||||
|
||||
get_decimals() {
|
||||
local addr="$1"
|
||||
cast call "$addr" "decimals()(uint8)" --rpc-url "$RPC" 2>/dev/null | cast --to-dec 2>/dev/null || echo "18"
|
||||
half_of() {
|
||||
python3 - "$1" <<'PY'
|
||||
import sys
|
||||
print(int(sys.argv[1]) // 2)
|
||||
PY
|
||||
}
|
||||
|
||||
format_units() {
|
||||
python3 - "$1" "$2" <<'PY'
|
||||
from decimal import Decimal, getcontext
|
||||
import sys
|
||||
getcontext().prec = 80
|
||||
raw = Decimal(sys.argv[1])
|
||||
decimals = int(sys.argv[2])
|
||||
value = raw / (Decimal(10) ** decimals)
|
||||
if decimals == 18:
|
||||
print(f"{value:.6f}")
|
||||
else:
|
||||
print(f"{value:.2f}")
|
||||
PY
|
||||
}
|
||||
|
||||
# Native balance
|
||||
native_wei=$(cast balance "$DEPLOYER" --rpc-url "$RPC" 2>/dev/null || echo "0")
|
||||
native_eth=$(awk "BEGIN { printf \"%.6f\", $native_wei / 1e18 }" 2>/dev/null || echo "0")
|
||||
half_native_wei=$((native_wei / 2))
|
||||
native_wei=$(cast balance "$DEPLOYER" --rpc-url "$RPC" 2>/dev/null | awk '{print $1}' || echo "0")
|
||||
native_eth=$(format_units "$native_wei" 18)
|
||||
half_native_wei=$(half_of "$native_wei")
|
||||
|
||||
echo "============================================"
|
||||
echo "Deployer wallet — ChainID 138"
|
||||
@@ -57,27 +76,23 @@ echo "--- Current balances ---"
|
||||
echo " Native ETH: $native_eth ETH (raw: $native_wei wei)"
|
||||
echo ""
|
||||
|
||||
RAW_WETH=0; RAW_WETH10=0; RAW_LINK=0; RAW_CUSDT=0; RAW_CUSDC=0
|
||||
HALF_WETH=0; HALF_WETH10=0; HALF_LINK=0; HALF_CUSDT=0; HALF_CUSDC=0
|
||||
RAW_WETH=0; RAW_WETH10=0; RAW_LINK=0; RAW_CUSDT=0; RAW_CUSDC=0; RAW_CEURT=0
|
||||
HALF_WETH=0; HALF_WETH10=0; HALF_LINK=0; HALF_CUSDT=0; HALF_CUSDC=0; HALF_CEURT=0
|
||||
|
||||
for entry in "WETH:$WETH:18" "WETH10:$WETH10:18" "LINK:$LINK:18" "cUSDT:$CUSDT:6" "cUSDC:$CUSDC:6"; do
|
||||
for entry in "WETH:$WETH:18" "WETH10:$WETH10:18" "LINK:$LINK:18" "cUSDT:$CUSDT:6" "cUSDC:$CUSDC:6" "cEURT:$CEURT:6"; do
|
||||
sym="${entry%%:*}"; rest="${entry#*:}"; addr="${rest%%:*}"; dec="${rest#*:}"
|
||||
raw=$(get_balance "$addr")
|
||||
half=$((raw / 2))
|
||||
half=$(half_of "$raw")
|
||||
case "$sym" in
|
||||
WETH) RAW_WETH=$raw; HALF_WETH=$half ;;
|
||||
WETH10) RAW_WETH10=$raw; HALF_WETH10=$half ;;
|
||||
LINK) RAW_LINK=$raw; HALF_LINK=$half ;;
|
||||
cUSDT) RAW_CUSDT=$raw; HALF_CUSDT=$half ;;
|
||||
cUSDC) RAW_CUSDC=$raw; HALF_CUSDC=$half ;;
|
||||
cEURT) RAW_CEURT=$raw; HALF_CEURT=$half ;;
|
||||
esac
|
||||
if [ "$dec" = "18" ]; then
|
||||
disp=$(awk "BEGIN { printf \"%.6f\", $raw / 1e18 }" 2>/dev/null || echo "0")
|
||||
half_disp=$(awk "BEGIN { printf \"%.6f\", $half / 1e18 }" 2>/dev/null || echo "0")
|
||||
else
|
||||
disp=$(awk "BEGIN { printf \"%.2f\", $raw / 1e$dec }" 2>/dev/null || echo "0")
|
||||
half_disp=$(awk "BEGIN { printf \"%.2f\", $half / 1e$dec }" 2>/dev/null || echo "0")
|
||||
fi
|
||||
disp=$(format_units "$raw" "$dec")
|
||||
half_disp=$(format_units "$half" "$dec")
|
||||
echo " $sym: $disp (raw: $raw) → half for LP: $half_disp (raw: $half)"
|
||||
done
|
||||
|
||||
@@ -96,18 +111,36 @@ echo "Pool 3: cUSDC/USDC ($POOL_CUSDCUSDC)"
|
||||
echo " Base (cUSDC): $HALF_CUSDC (decimals 6)"
|
||||
echo " Quote (USDC): use same amount in USDC (official) — check deployer USDC balance separately if needed"
|
||||
echo ""
|
||||
echo "Pool 4: cUSDT/WETH ($POOL_CUSDTWETH)"
|
||||
echo " Base (cUSDT): $HALF_CUSDT (decimals 6)"
|
||||
echo " Quote (WETH): $HALF_WETH (decimals 18)"
|
||||
echo ""
|
||||
echo "Pool 5: cUSDC/WETH ($POOL_CUSDCWETH)"
|
||||
echo " Base (cUSDC): $HALF_CUSDC (decimals 6)"
|
||||
echo " Quote (WETH): $HALF_WETH (decimals 18)"
|
||||
echo ""
|
||||
echo "Pool 6: cEURT/WETH ($POOL_CEURTWETH)"
|
||||
echo " Base (cEURT): $HALF_CEURT (decimals 6)"
|
||||
echo " Quote (WETH): $HALF_WETH (decimals 18)"
|
||||
echo ""
|
||||
echo "--- Env vars for AddLiquidityPMMPoolsChain138 (half of cUSDT/cUSDC) ---"
|
||||
echo "# Add to smom-dbis-138/.env and run: forge script script/dex/AddLiquidityPMMPoolsChain138.s.sol:AddLiquidityPMMPoolsChain138 --rpc-url \$RPC_URL_138 --broadcast --private-key \$PRIVATE_KEY"
|
||||
echo "POOL_CUSDTCUSDC=$POOL_CUSDTCUSDC"
|
||||
echo "POOL_CUSDTUSDT=$POOL_CUSDTUSDT"
|
||||
echo "POOL_CUSDCUSDC=$POOL_CUSDCUSDC"
|
||||
echo "POOL_CUSDTWETH=$POOL_CUSDTWETH"
|
||||
echo "POOL_CUSDCWETH=$POOL_CUSDCWETH"
|
||||
echo "POOL_CEURTWETH=$POOL_CEURTWETH"
|
||||
echo "ADD_LIQUIDITY_BASE_AMOUNT=$HALF_CUSDT"
|
||||
echo "ADD_LIQUIDITY_QUOTE_AMOUNT=$HALF_CUSDC"
|
||||
echo "# For pool cUSDT/cUSDC only (base=cUSDT, quote=cUSDC). For cUSDT/USDT and cUSDC/USDC use per-pool vars:"
|
||||
echo "# ADD_LIQUIDITY_CUSDTUSDT_BASE=$HALF_CUSDT ADD_LIQUIDITY_CUSDTUSDT_QUOTE=<deployer USDT balance / 2>"
|
||||
echo "# ADD_LIQUIDITY_CUSDCUSDC_BASE=$HALF_CUSDC ADD_LIQUIDITY_CUSDCUSDC_QUOTE=<deployer USDC balance / 2>"
|
||||
echo "# ADD_LIQUIDITY_CUSDTWETH_BASE=$HALF_CUSDT ADD_LIQUIDITY_CUSDTWETH_QUOTE=$HALF_WETH"
|
||||
echo "# ADD_LIQUIDITY_CUSDCWETH_BASE=$HALF_CUSDC ADD_LIQUIDITY_CUSDCWETH_QUOTE=$HALF_WETH"
|
||||
echo "# ADD_LIQUIDITY_CEURTWETH_BASE=$HALF_CEURT ADD_LIQUIDITY_CEURTWETH_QUOTE=$HALF_WETH"
|
||||
echo ""
|
||||
echo "--- Reserve ---"
|
||||
echo " Keep half of native ETH for gas. Half for LP (if wrapping to WETH for a pool): $((half_native_wei / 2)) wei."
|
||||
echo " Keep half of native ETH for gas. Half for LP (if wrapping to WETH for a pool): $(half_of "$half_native_wei") wei."
|
||||
echo " WETH/LINK: half amounts above can be reserved for other use or future pools."
|
||||
echo "============================================"
|
||||
|
||||
516
scripts/deployment/check-deployer-lp-balances.py
Executable file
516
scripts/deployment/check-deployer-lp-balances.py
Executable file
@@ -0,0 +1,516 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Enumerate PMM pool addresses (deployment-status.json) and Uniswap V2 pair addresses
|
||||
(pair-discovery JSON), then report deployer balances with **LP token resolution**.
|
||||
|
||||
**Uniswap V2:** The pair contract *is* the LP ERC-20 (`lpToken` = pair).
|
||||
|
||||
**DODO PMM (DVM / IDODOPMMPool):** Official DODO Vending Machine pools inherit ERC-20;
|
||||
`balanceOf(pool)` is the LP share balance — the pool address **is** the LP token.
|
||||
|
||||
**DODO V1-style PMM:** Some pools expose ``_BASE_CAPITAL_TOKEN_`` / ``_QUOTE_CAPITAL_TOKEN_``;
|
||||
LP exposure may be split across two capital ERC-20s (balances reported separately).
|
||||
|
||||
When ``balanceOf(pool)`` fails (RPC flake, proxy, or non-DVM), this script optionally
|
||||
re-probes with DODO view calls and alternate public RPCs (see ``--resolve-dodo``).
|
||||
|
||||
Deployer: ``--deployer`` / ``DEPLOYER_ADDRESS`` / ``PRIVATE_KEY`` (see below).
|
||||
|
||||
Usage:
|
||||
python3 scripts/deployment/check-deployer-lp-balances.py --summary-only
|
||||
python3 scripts/deployment/check-deployer-lp-balances.py --resolve-dodo --json-out /tmp/lp.json
|
||||
|
||||
Requires: ``cast`` (Foundry). Environment: ``DEPLOYER_ADDRESS``, ``PRIVATE_KEY``, etc.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
DEFAULT_STATUS = ROOT / "cross-chain-pmm-lps" / "config" / "deployment-status.json"
|
||||
DEFAULT_DISCOVERY = ROOT / "reports" / "extraction" / "promod-uniswap-v2-live-pair-discovery-latest.json"
|
||||
DEFAULT_ENV = ROOT / "smom-dbis-138" / ".env"
|
||||
|
||||
ZERO = "0x0000000000000000000000000000000000000000"
|
||||
|
||||
DEFAULT_RPC: dict[str, str] = {
|
||||
"1": "https://eth.llamarpc.com",
|
||||
"10": "https://mainnet.optimism.io",
|
||||
"25": "https://evm.cronos.org",
|
||||
"56": "https://bsc-dataseed.binance.org",
|
||||
"100": "https://rpc.gnosischain.com",
|
||||
"137": "https://polygon-rpc.com",
|
||||
"8453": "https://mainnet.base.org",
|
||||
"42161": "https://arbitrum-one.publicnode.com",
|
||||
"42220": "https://forno.celo.org",
|
||||
"43114": "https://avalanche-c-chain.publicnode.com",
|
||||
"1111": "https://api.wemix.com",
|
||||
}
|
||||
|
||||
# Extra public RPCs (retry when primary fails — connection resets, rate limits).
|
||||
RPC_FALLBACKS: dict[str, list[str]] = {
|
||||
"1": [
|
||||
"https://ethereum.publicnode.com",
|
||||
"https://1rpc.io/eth",
|
||||
"https://rpc.ankr.com/eth",
|
||||
],
|
||||
"137": ["https://polygon-bor.publicnode.com", "https://1rpc.io/matic"],
|
||||
"42161": ["https://arbitrum.llamarpc.com"],
|
||||
"56": ["https://bsc.publicnode.com"],
|
||||
"8453": ["https://base.llamarpc.com"],
|
||||
"10": ["https://optimism.publicnode.com"],
|
||||
}
|
||||
|
||||
RPC_KEYS: dict[str, list[str]] = {
|
||||
"1": ["ETHEREUM_MAINNET_RPC", "ETH_MAINNET_RPC_URL"],
|
||||
"10": ["OPTIMISM_RPC_URL", "OPTIMISM_MAINNET_RPC"],
|
||||
"25": ["CRONOS_RPC_URL", "CRONOS_MAINNET_RPC"],
|
||||
"56": ["BSC_RPC_URL", "BSC_MAINNET_RPC"],
|
||||
"100": ["GNOSIS_RPC_URL", "GNOSIS_MAINNET_RPC", "GNOSIS_RPC"],
|
||||
"137": ["POLYGON_MAINNET_RPC", "POLYGON_RPC_URL"],
|
||||
"138": ["RPC_URL_138", "CHAIN_138_RPC_URL"],
|
||||
"8453": ["BASE_RPC_URL", "BASE_MAINNET_RPC"],
|
||||
"42161": ["ARBITRUM_RPC_URL", "ARBITRUM_MAINNET_RPC"],
|
||||
"42220": ["CELO_RPC_URL", "CELO_MAINNET_RPC", "CELO_RPC"],
|
||||
"43114": ["AVALANCHE_RPC_URL", "AVALANCHE_MAINNET_RPC"],
|
||||
"1111": ["WEMIX_RPC_URL", "WEMIX_RPC"],
|
||||
"651940": ["ALL_MAINNET_RPC", "CHAIN_651940_RPC_URL"],
|
||||
}
|
||||
|
||||
|
||||
def load_dotenv(path: Path) -> dict[str, str]:
|
||||
out: dict[str, str] = {}
|
||||
if not path.is_file():
|
||||
return out
|
||||
for raw in path.read_text().splitlines():
|
||||
line = raw.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
k, v = line.split("=", 1)
|
||||
out[k.strip()] = v.strip().strip('"').strip("'")
|
||||
return out
|
||||
|
||||
|
||||
def resolve(env: dict[str, str], key: str, default: str = "") -> str:
|
||||
v = env.get(key, "")
|
||||
if v.startswith("${") and ":-" in v:
|
||||
inner = v[2:-1]
|
||||
alt = inner.split(":-", 1)
|
||||
return env.get(alt[0], alt[1] if len(alt) > 1 else "")
|
||||
return v or default
|
||||
|
||||
|
||||
def rpc_for(env: dict[str, str], cid: str) -> str:
|
||||
for k in RPC_KEYS.get(cid, []):
|
||||
v = resolve(env, k, "")
|
||||
if v and not v.startswith("$"):
|
||||
return v
|
||||
return DEFAULT_RPC.get(cid, "") or (
|
||||
resolve(env, "RPC_URL_138", "http://127.0.0.1:8545") if cid == "138" else ""
|
||||
)
|
||||
|
||||
|
||||
def rpc_chain_list(env: dict[str, str], cid: str) -> list[str]:
|
||||
primary = rpc_for(env, cid)
|
||||
seen: set[str] = set()
|
||||
out: list[str] = []
|
||||
for u in [primary] + RPC_FALLBACKS.get(cid, []):
|
||||
if u and u not in seen:
|
||||
seen.add(u)
|
||||
out.append(u)
|
||||
return out
|
||||
|
||||
|
||||
def deployer_address(env: dict[str, str], override: str | None) -> str:
|
||||
if override:
|
||||
return override
|
||||
for k in ("DEPLOYER_ADDRESS", "DEPLOYER"):
|
||||
v = (os.environ.get(k) or "").strip()
|
||||
if v:
|
||||
return v
|
||||
pk = env.get("PRIVATE_KEY", "") or (os.environ.get("PRIVATE_KEY") or "").strip()
|
||||
if pk:
|
||||
r = subprocess.run(
|
||||
["cast", "wallet", "address", pk],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
if r.returncode == 0 and r.stdout.strip():
|
||||
return r.stdout.strip()
|
||||
return (env.get("DEPLOYER_ADDRESS") or "").strip()
|
||||
|
||||
|
||||
def parse_uint(s: str) -> int:
|
||||
return int(s.strip().split()[0])
|
||||
|
||||
|
||||
def parse_address_line(s: str) -> str | None:
|
||||
s = s.strip()
|
||||
if not s:
|
||||
return None
|
||||
m = re.search(r"(0x[a-fA-F0-9]{40})", s)
|
||||
return m.group(1) if m else None
|
||||
|
||||
|
||||
def cast_call(to: str, sig: str, rpc: str) -> tuple[str, str]:
|
||||
cmd = ["cast", "call", to, sig, "--rpc-url", rpc]
|
||||
r = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if r.returncode != 0:
|
||||
return "err", (r.stderr or r.stdout or "").strip()[:400]
|
||||
return "ok", r.stdout.strip()
|
||||
|
||||
|
||||
def erc20_balance(token: str, holder: str, rpc: str) -> tuple[str, int | str]:
|
||||
cmd = ["cast", "call", token, "balanceOf(address)(uint256)", holder, "--rpc-url", rpc]
|
||||
r = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if r.returncode != 0:
|
||||
return "err", (r.stderr or r.stdout or "").strip()[:400]
|
||||
try:
|
||||
return "ok", parse_uint(r.stdout)
|
||||
except (ValueError, IndexError):
|
||||
return "err", f"parse:{r.stdout[:120]}"
|
||||
|
||||
|
||||
def erc20_balance_any_rpc(
|
||||
token: str, holder: str, rpcs: list[str]
|
||||
) -> tuple[str, int | str, str]:
|
||||
"""Returns (status, value|err, rpc_used)."""
|
||||
last_err = ""
|
||||
for rpc in rpcs:
|
||||
st, val = erc20_balance(token, holder, rpc)
|
||||
if st == "ok":
|
||||
return st, val, rpc
|
||||
last_err = str(val)
|
||||
return "err", last_err, rpcs[0] if rpcs else ""
|
||||
|
||||
|
||||
def resolve_pmm_row(
|
||||
pool: str,
|
||||
deployer: str,
|
||||
rpcs: list[str],
|
||||
do_resolve_dodo: bool,
|
||||
) -> dict:
|
||||
"""
|
||||
Build a result dict with lp resolution fields.
|
||||
Tries: pool ERC20 balance (any RPC) -> DODO _BASE/_QUOTE -> capital tokens.
|
||||
"""
|
||||
rec: dict = {
|
||||
"contract": pool,
|
||||
"lpTokenAddress": pool,
|
||||
"lpResolution": "unknown",
|
||||
"dodoBaseToken": None,
|
||||
"dodoQuoteToken": None,
|
||||
"lpBalances": [],
|
||||
"balanceRaw": 0,
|
||||
"status": "pending",
|
||||
"error": None,
|
||||
"rpcUsed": rpcs[0] if rpcs else None,
|
||||
}
|
||||
|
||||
st, val, used = erc20_balance_any_rpc(pool, deployer, rpcs)
|
||||
rec["rpcUsed"] = used
|
||||
if st == "ok":
|
||||
rec["status"] = "ok"
|
||||
rec["lpResolution"] = "dvm_or_erc20_pool"
|
||||
rec["balanceRaw"] = int(val)
|
||||
rec["lpBalances"] = [
|
||||
{
|
||||
"role": "lp_erc20",
|
||||
"token": pool,
|
||||
"raw": int(val),
|
||||
"note": "balanceOf(pool): DVM LP shares are usually the pool contract itself",
|
||||
}
|
||||
]
|
||||
return rec
|
||||
|
||||
rec["error"] = str(val)
|
||||
if not do_resolve_dodo:
|
||||
rec["status"] = "erc20_error"
|
||||
rec["lpResolution"] = "unresolved_pass_resolve_dodo"
|
||||
return rec
|
||||
|
||||
base_tok: str | None = None
|
||||
quote_tok: str | None = None
|
||||
for rpc in rpcs:
|
||||
stb, outb = cast_call(pool, "_BASE_TOKEN_()(address)", rpc)
|
||||
if stb == "ok":
|
||||
base_tok = parse_address_line(outb)
|
||||
rec["rpcUsed"] = rpc
|
||||
break
|
||||
if base_tok:
|
||||
rec["dodoBaseToken"] = base_tok
|
||||
for rpc in rpcs:
|
||||
stq, outq = cast_call(pool, "_QUOTE_TOKEN_()(address)", rpc)
|
||||
if stq == "ok":
|
||||
quote_tok = parse_address_line(outq)
|
||||
rec["dodoQuoteToken"] = quote_tok
|
||||
break
|
||||
|
||||
# Retry pool balanceOf after confirming DVM interface (fresh RPC may fix flake)
|
||||
st2, val2, used2 = erc20_balance_any_rpc(pool, deployer, rpcs)
|
||||
if st2 == "ok":
|
||||
rec["status"] = "ok"
|
||||
rec["lpResolution"] = "dvm_erc20_pool_after_probe"
|
||||
rec["balanceRaw"] = int(val2)
|
||||
rec["rpcUsed"] = used2
|
||||
rec["error"] = None
|
||||
rec["lpBalances"] = [
|
||||
{
|
||||
"role": "lp_erc20",
|
||||
"token": pool,
|
||||
"raw": int(val2),
|
||||
"note": "balanceOf(pool) succeeded after _BASE_TOKEN_ probe + RPC retry",
|
||||
}
|
||||
]
|
||||
return rec
|
||||
|
||||
capital_balances: list[dict] = []
|
||||
for cap_sig, role in (
|
||||
("_BASE_CAPITAL_TOKEN_()(address)", "base_capital"),
|
||||
("_QUOTE_CAPITAL_TOKEN_()(address)", "quote_capital"),
|
||||
):
|
||||
tok_a: str | None = None
|
||||
for rpc in rpcs:
|
||||
stc, outc = cast_call(pool, cap_sig, rpc)
|
||||
if stc == "ok":
|
||||
tok_a = parse_address_line(outc)
|
||||
if tok_a and tok_a != ZERO:
|
||||
bst, bval, bused = erc20_balance_any_rpc(tok_a, deployer, rpcs)
|
||||
if bst == "ok":
|
||||
capital_balances.append(
|
||||
{
|
||||
"role": role,
|
||||
"token": tok_a,
|
||||
"raw": int(bval),
|
||||
"note": f"DODO V1-style {cap_sig.split('(')[0]} balanceOf",
|
||||
}
|
||||
)
|
||||
break
|
||||
|
||||
if capital_balances:
|
||||
rec["status"] = "ok"
|
||||
rec["lpResolution"] = "v1_capital_tokens"
|
||||
rec["lpTokenAddress"] = pool
|
||||
rec["lpBalances"] = capital_balances
|
||||
rec["balanceRaw"] = sum(x["raw"] for x in capital_balances)
|
||||
rec["error"] = None
|
||||
return rec
|
||||
|
||||
if base_tok:
|
||||
rec["lpResolution"] = "dvm_interface_no_balance"
|
||||
rec["status"] = "erc20_error"
|
||||
rec["error"] = (
|
||||
f"Pool responds as DVM (_BASE_TOKEN_={base_tok}) but balanceOf(pool) failed: {rec.get('error', '')[:200]}"
|
||||
)
|
||||
else:
|
||||
rec["status"] = "erc20_error"
|
||||
rec["lpResolution"] = "unresolved"
|
||||
return rec
|
||||
|
||||
|
||||
def collect_entries(status_path: Path, discovery_path: Path) -> list[tuple]:
|
||||
status = json.loads(status_path.read_text())
|
||||
rows: list[tuple] = []
|
||||
for cid, ch in (status.get("chains") or {}).items():
|
||||
name = ch.get("name", cid)
|
||||
for pool in (ch.get("pmmPools") or []) + (ch.get("pmmPoolsVolatile") or []) + (ch.get("gasPmmPools") or []):
|
||||
addr = pool.get("poolAddress") or ""
|
||||
if not addr or addr == ZERO:
|
||||
continue
|
||||
label = f"{pool.get('base')}/{pool.get('quote')}"
|
||||
rows.append((cid, name, "PMM", label, addr))
|
||||
if discovery_path.is_file():
|
||||
disc = json.loads(discovery_path.read_text())
|
||||
for ent in disc.get("entries") or []:
|
||||
cid = str(ent.get("chain_id"))
|
||||
name = ent.get("network", cid)
|
||||
for pr in ent.get("pairsChecked") or []:
|
||||
addr = pr.get("poolAddress") or ""
|
||||
if not addr or addr == ZERO:
|
||||
continue
|
||||
label = f"{pr.get('base')}/{pr.get('quote')}"
|
||||
rows.append((cid, name, "UniV2", label, addr))
|
||||
seen: set[tuple[str, str]] = set()
|
||||
out: list[tuple] = []
|
||||
for row in rows:
|
||||
k = (row[0], row[4].lower())
|
||||
if k in seen:
|
||||
continue
|
||||
seen.add(k)
|
||||
out.append(row)
|
||||
return out
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(
|
||||
description="Deployer LP balances with DODO / Uni V2 LP token resolution."
|
||||
)
|
||||
ap.add_argument("--status", type=Path, default=DEFAULT_STATUS)
|
||||
ap.add_argument("--discovery", type=Path, default=DEFAULT_DISCOVERY)
|
||||
ap.add_argument("--env", type=Path, default=DEFAULT_ENV)
|
||||
ap.add_argument("--deployer", default=None)
|
||||
ap.add_argument("--summary-only", action="store_true")
|
||||
ap.add_argument("--only-nonzero", action="store_true")
|
||||
ap.add_argument(
|
||||
"--no-resolve-dodo",
|
||||
action="store_true",
|
||||
help="Skip DODO _BASE_TOKEN_ / capital-token probes and extra RPC fallbacks (faster; more erc20_error).",
|
||||
)
|
||||
ap.add_argument(
|
||||
"--chain-id",
|
||||
type=int,
|
||||
default=None,
|
||||
metavar="N",
|
||||
help="Only check this chain (e.g. 1 for Ethereum). Default: all chains.",
|
||||
)
|
||||
ap.add_argument(
|
||||
"--json-out",
|
||||
type=Path,
|
||||
default=None,
|
||||
help="Full report JSON.",
|
||||
)
|
||||
ap.add_argument(
|
||||
"--errors-json",
|
||||
type=Path,
|
||||
default=None,
|
||||
help="Rows that remain erc20_error or no_rpc.",
|
||||
)
|
||||
args = ap.parse_args()
|
||||
|
||||
env = load_dotenv(args.env)
|
||||
dep = deployer_address(env, args.deployer)
|
||||
if not dep:
|
||||
print("No deployer: set PRIVATE_KEY or DEPLOYER_ADDRESS in .env or pass --deployer", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
rows = collect_entries(args.status, args.discovery)
|
||||
if args.chain_id is not None:
|
||||
want = str(args.chain_id)
|
||||
rows = [r for r in rows if r[0] == want]
|
||||
results: list[dict] = []
|
||||
nonzero: list[dict] = []
|
||||
errors: list[dict] = []
|
||||
|
||||
for cid, name, venue, label, addr in sorted(rows, key=lambda x: (int(x[0]), x[2], x[3])):
|
||||
rpcs = rpc_chain_list(env, cid)
|
||||
base_rec: dict = {
|
||||
"chainId": cid,
|
||||
"network": name,
|
||||
"venue": venue,
|
||||
"pair": label,
|
||||
"contract": addr,
|
||||
}
|
||||
if not rpcs or not rpcs[0]:
|
||||
base_rec["status"] = "no_rpc"
|
||||
base_rec["lpResolution"] = "no_rpc"
|
||||
errors.append(base_rec)
|
||||
results.append(base_rec)
|
||||
continue
|
||||
|
||||
if venue == "UniV2":
|
||||
st, val, used = erc20_balance_any_rpc(addr, dep, rpcs)
|
||||
r = {
|
||||
**base_rec,
|
||||
"lpTokenAddress": addr,
|
||||
"lpResolution": "uniswap_v2_pair",
|
||||
"rpcUsed": used,
|
||||
"lpBalances": [
|
||||
{
|
||||
"role": "pair_lp",
|
||||
"token": addr,
|
||||
"raw": int(val) if st == "ok" else 0,
|
||||
"note": "Uniswap V2 pair contract is the LP ERC-20",
|
||||
}
|
||||
],
|
||||
"balanceRaw": int(val) if st == "ok" else 0,
|
||||
"status": "ok" if st == "ok" else "erc20_error",
|
||||
"error": None if st == "ok" else str(val),
|
||||
"dodoBaseToken": None,
|
||||
"dodoQuoteToken": None,
|
||||
}
|
||||
if st != "ok":
|
||||
r["status"] = "erc20_error"
|
||||
errors.append(r)
|
||||
elif r["balanceRaw"] > 0:
|
||||
nonzero.append(r)
|
||||
results.append(r)
|
||||
continue
|
||||
|
||||
# PMM
|
||||
r = {**base_rec, **resolve_pmm_row(addr, dep, rpcs, not args.no_resolve_dodo)}
|
||||
if r.get("status") == "ok" and r.get("balanceRaw", 0) > 0:
|
||||
nonzero.append(r)
|
||||
if r.get("status") != "ok":
|
||||
errors.append(r)
|
||||
results.append(r)
|
||||
|
||||
# Summary stats
|
||||
by_res: dict[str, int] = {}
|
||||
for r in results:
|
||||
lr = r.get("lpResolution") or "unknown"
|
||||
by_res[lr] = by_res.get(lr, 0) + 1
|
||||
|
||||
print(f"Deployer: {dep}")
|
||||
print(f"Contracts checked: {len(rows)}")
|
||||
print(f"Non-zero LP exposure (sum of components): {len(nonzero)}")
|
||||
print(f"Errors / no RPC: {len(errors)}")
|
||||
print(f"Resolution breakdown: {by_res}")
|
||||
if args.no_resolve_dodo:
|
||||
print("(Re-run without --no-resolve-dodo to probe DODO interfaces + RPC fallbacks.)")
|
||||
|
||||
if not args.summary_only:
|
||||
print("\n=== Non-zero LP / capital balances ===")
|
||||
for r in nonzero:
|
||||
lp = r.get("lpTokenAddress", r.get("contract"))
|
||||
print(
|
||||
f" chain {r['chainId']} {r['venue']} {r['pair']} | lpToken={lp} | "
|
||||
f"resolution={r.get('lpResolution')} | raw_total={r.get('balanceRaw')}"
|
||||
)
|
||||
for leg in r.get("lpBalances") or []:
|
||||
if leg.get("raw", 0) > 0:
|
||||
print(f" - {leg.get('role')} {leg.get('token')}: {leg.get('raw')} ({leg.get('note', '')[:60]})")
|
||||
if not args.only_nonzero and errors:
|
||||
print("\nSample unresolved / errors:")
|
||||
for r in errors[:12]:
|
||||
e = r.get("error", r.get("status", ""))
|
||||
print(
|
||||
f" chain {r['chainId']} {r['venue']} {r['pair']} | "
|
||||
f"{r.get('lpResolution', '')}: {str(e)[:120]}"
|
||||
)
|
||||
|
||||
if args.json_out:
|
||||
payload = {
|
||||
"deployer": dep,
|
||||
"resolveDodo": not args.no_resolve_dodo,
|
||||
"summary": {
|
||||
"checked": len(rows),
|
||||
"nonzero": len(nonzero),
|
||||
"errors": len(errors),
|
||||
"byLpResolution": by_res,
|
||||
},
|
||||
"nonzero": nonzero,
|
||||
"all": results,
|
||||
}
|
||||
args.json_out.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.json_out.write_text(json.dumps(payload, indent=2) + "\n")
|
||||
print(f"Wrote {args.json_out}")
|
||||
|
||||
if args.errors_json:
|
||||
err_only = [r for r in results if r.get("status") in ("no_rpc", "erc20_error")]
|
||||
args.errors_json.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.errors_json.write_text(json.dumps(err_only, indent=2) + "\n")
|
||||
print(f"Wrote {args.errors_json} ({len(err_only)} rows)")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -1,31 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# Check deployer nonce and balance on Mainnet, Cronos, and Arbitrum.
|
||||
# Check deployer nonce and balance on Mainnet, Cronos, and Arbitrum (public RPCs by default).
|
||||
# Use to diagnose "nonce too high" / "invalid nonce" and "insufficient funds" before retrying cW* deploy.
|
||||
# Usage: ./scripts/deployment/check-deployer-nonce-and-balance.sh
|
||||
# Requires: smom-dbis-138/.env with PRIVATE_KEY, ETHEREUM_MAINNET_RPC, CRONOS_RPC_URL, ARBITRUM_MAINNET_RPC
|
||||
# Requires: PRIVATE_KEY (repo root .env, smom-dbis-138/.env, or ~/.secure-secrets/private-keys.env via load-project-env).
|
||||
# Optional: ETHEREUM_MAINNET_RPC, CRONOS_RPC_URL, ARBITRUM_MAINNET_RPC — if unset, uses public endpoints
|
||||
# (override per chain with ETHEREUM_MAINNET_PUBLIC_RPC / CRONOS_MAINNET_PUBLIC_RPC / ARBITRUM_MAINNET_PUBLIC_RPC).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
SMOM="${PROJECT_ROOT}/smom-dbis-138"
|
||||
[[ -f "$SMOM/.env" ]] || { echo "Missing $SMOM/.env" >&2; exit 1; }
|
||||
set -a
|
||||
source "$SMOM/.env"
|
||||
set +a
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh"
|
||||
|
||||
# Defaults when .env does not set provider URLs (read-only public RPCs).
|
||||
PUBLIC_ETHEREUM_RPC="${ETHEREUM_MAINNET_PUBLIC_RPC:-https://ethereum-rpc.publicnode.com}"
|
||||
PUBLIC_CRONOS_RPC="${CRONOS_MAINNET_PUBLIC_RPC:-https://evm.cronos.org}"
|
||||
PUBLIC_ARBITRUM_RPC="${ARBITRUM_MAINNET_PUBLIC_RPC:-https://arbitrum-one-rpc.publicnode.com}"
|
||||
|
||||
DEPLOYER=""
|
||||
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
||||
DEPLOYER=$(cast wallet address "$PRIVATE_KEY" 2>/dev/null || true)
|
||||
fi
|
||||
[[ -z "$DEPLOYER" ]] && { echo "Could not derive deployer address (set PRIVATE_KEY in $SMOM/.env)" >&2; exit 1; }
|
||||
[[ -z "$DEPLOYER" ]] && {
|
||||
echo "Could not derive deployer address. Set PRIVATE_KEY in ${PROJECT_ROOT}/.env, smom-dbis-138/.env, or ~/.secure-secrets/private-keys.env" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "Deployer address: $DEPLOYER"
|
||||
echo ""
|
||||
|
||||
for label in "Mainnet (1)" "Cronos (25)" "Arbitrum (42161)"; do
|
||||
case "$label" in
|
||||
"Mainnet (1)") RPC="${ETHEREUM_MAINNET_RPC:-$ETH_MAINNET_RPC_URL}"; CHAIN=1 ;;
|
||||
"Cronos (25)") RPC="${CRONOS_RPC_URL:-$CRONOS_RPC}"; CHAIN=25 ;;
|
||||
"Arbitrum (42161)") RPC="${ARBITRUM_MAINNET_RPC:-$ARBITRUM_MAINNET_RPC_URL}"; CHAIN=42161 ;;
|
||||
"Mainnet (1)")
|
||||
RPC="${ETHEREUM_MAINNET_RPC:-${ETH_MAINNET_RPC_URL:-$PUBLIC_ETHEREUM_RPC}}"
|
||||
CHAIN=1
|
||||
;;
|
||||
"Cronos (25)")
|
||||
RPC="${CRONOS_RPC_URL:-${CRONOS_RPC:-$PUBLIC_CRONOS_RPC}}"
|
||||
CHAIN=25
|
||||
;;
|
||||
"Arbitrum (42161)")
|
||||
RPC="${ARBITRUM_MAINNET_RPC:-${ARBITRUM_MAINNET_RPC_URL:-$PUBLIC_ARBITRUM_RPC}}"
|
||||
CHAIN=42161
|
||||
;;
|
||||
esac
|
||||
[[ -z "$RPC" ]] && { echo "$label: no RPC set, skip"; continue; }
|
||||
NONCE=$(cast nonce "$DEPLOYER" --rpc-url "$RPC" 2>/dev/null || echo "?")
|
||||
|
||||
144
scripts/deployment/deploy-atomic-swap-dapp-5801.sh
Normal file
144
scripts/deployment/deploy-atomic-swap-dapp-5801.sh
Normal file
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
SUBMODULE_ROOT="$PROJECT_ROOT/atomic-swap-dapp"
|
||||
source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true
|
||||
PROXMOX_HOST="${PROXMOX_HOST:-${PROXMOX_HOST_R630_02:-192.168.11.12}}"
|
||||
VMID="${VMID:-5801}"
|
||||
DEPLOY_ROOT="${DEPLOY_ROOT:-/var/www/atomic-swap}"
|
||||
TMP_ARCHIVE="/tmp/atomic-swap-dapp-5801.tgz"
|
||||
DIST_DIR="$SUBMODULE_ROOT/dist"
|
||||
SKIP_BUILD="${SKIP_BUILD:-0}"
|
||||
|
||||
cleanup() {
|
||||
rm -f "$TMP_ARCHIVE"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
if [ ! -d "$SUBMODULE_ROOT" ]; then
|
||||
echo "Missing submodule at $SUBMODULE_ROOT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$SUBMODULE_ROOT"
|
||||
if [ "$SKIP_BUILD" != "1" ]; then
|
||||
npm run sync:ecosystem >/dev/null
|
||||
npm run validate:manifest >/dev/null
|
||||
npm run build >/dev/null
|
||||
fi
|
||||
|
||||
for required_path in \
|
||||
"$DIST_DIR/index.html" \
|
||||
"$DIST_DIR/data/ecosystem-manifest.json" \
|
||||
"$DIST_DIR/data/live-route-registry.json" \
|
||||
"$DIST_DIR/data/deployed-venue-inventory.json"; do
|
||||
if [ ! -f "$required_path" ]; then
|
||||
echo "Missing required build artifact: $required_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
jq -e '.supportedNetworks[] | select(.chainId == 138) | .deployedVenuePoolCount >= 19 and .publicRoutingPoolCount >= 19' \
|
||||
"$DIST_DIR/data/ecosystem-manifest.json" >/dev/null
|
||||
jq -e '.liveSwapRoutes | length >= 19' "$DIST_DIR/data/live-route-registry.json" >/dev/null
|
||||
jq -e '.liveBridgeRoutes | length >= 12' "$DIST_DIR/data/live-route-registry.json" >/dev/null
|
||||
jq -e '.networks[] | select(.chainId == 138) | .venueCounts.deployedVenuePoolCount >= 19 and .summary.totalVenues >= 19' \
|
||||
"$DIST_DIR/data/deployed-venue-inventory.json" >/dev/null
|
||||
|
||||
rm -f "$TMP_ARCHIVE"
|
||||
tar -C "$SUBMODULE_ROOT" -czf "$TMP_ARCHIVE" dist
|
||||
|
||||
scp -q -o StrictHostKeyChecking=accept-new "$TMP_ARCHIVE" "root@$PROXMOX_HOST:/tmp/atomic-swap-dapp-5801.tgz"
|
||||
|
||||
ssh -o StrictHostKeyChecking=accept-new "root@$PROXMOX_HOST" "
|
||||
pct push $VMID /tmp/atomic-swap-dapp-5801.tgz /tmp/atomic-swap-dapp-5801.tgz
|
||||
pct exec $VMID -- bash -lc '
|
||||
set -euo pipefail
|
||||
mkdir -p \"$DEPLOY_ROOT\"
|
||||
find \"$DEPLOY_ROOT\" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
|
||||
rm -rf /tmp/dist
|
||||
tar -xzf /tmp/atomic-swap-dapp-5801.tgz -C /tmp
|
||||
cp -R /tmp/dist/. \"$DEPLOY_ROOT/\"
|
||||
mkdir -p /var/cache/nginx/atomic-swap-api
|
||||
cat > /etc/nginx/conf.d/atomic-swap-api-cache.conf <<\"EOF\"
|
||||
proxy_cache_path /var/cache/nginx/atomic-swap-api
|
||||
levels=1:2
|
||||
keys_zone=atomic_swap_api_cache:10m
|
||||
max_size=256m
|
||||
inactive=30m
|
||||
use_temp_path=off;
|
||||
EOF
|
||||
cat > /etc/nginx/sites-available/atomic-swap <<\"EOF\"
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
server_name _;
|
||||
|
||||
root $DEPLOY_ROOT;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files \$uri \$uri/ /index.html;
|
||||
}
|
||||
|
||||
location = /index.html {
|
||||
add_header Cache-Control \"no-store, no-cache, must-revalidate\" always;
|
||||
}
|
||||
|
||||
location /data/ {
|
||||
add_header Cache-Control \"no-store, no-cache, must-revalidate\" always;
|
||||
}
|
||||
|
||||
location /assets/ {
|
||||
add_header Cache-Control \"public, max-age=31536000, immutable\" always;
|
||||
}
|
||||
|
||||
location /api/v1/ {
|
||||
proxy_pass https://explorer.d-bis.org/api/v1/;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_set_header Host explorer.d-bis.org;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Host \$host;
|
||||
proxy_http_version 1.1;
|
||||
proxy_buffering on;
|
||||
proxy_cache atomic_swap_api_cache;
|
||||
proxy_cache_methods GET HEAD;
|
||||
proxy_cache_key \"\$scheme\$proxy_host\$request_uri\";
|
||||
proxy_cache_lock on;
|
||||
proxy_cache_lock_timeout 10s;
|
||||
proxy_cache_lock_age 10s;
|
||||
proxy_cache_background_update on;
|
||||
proxy_cache_revalidate on;
|
||||
proxy_cache_valid 200 10s;
|
||||
proxy_cache_valid 404 1s;
|
||||
proxy_cache_valid any 0;
|
||||
proxy_cache_use_stale error timeout invalid_header updating http_429 http_500 http_502 http_503 http_504;
|
||||
add_header X-Atomic-Swap-Cache \$upstream_cache_status always;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
ln -sfn /etc/nginx/sites-available/atomic-swap /etc/nginx/sites-enabled/atomic-swap
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
rm -f /etc/nginx/sites-enabled/dapp
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
curl -fsS http://127.0.0.1/index.html >/dev/null
|
||||
curl -fsS http://127.0.0.1/data/ecosystem-manifest.json >/dev/null
|
||||
curl -fsS http://127.0.0.1/data/live-route-registry.json >/dev/null
|
||||
curl -fsS http://127.0.0.1/data/deployed-venue-inventory.json >/dev/null
|
||||
rm -rf /tmp/dist /tmp/atomic-swap-dapp-5801.tgz
|
||||
'
|
||||
rm -f /tmp/atomic-swap-dapp-5801.tgz
|
||||
"
|
||||
|
||||
curl -fsS https://atomic-swap.defi-oracle.io/ >/dev/null
|
||||
curl -fsS https://atomic-swap.defi-oracle.io/data/ecosystem-manifest.json | jq -e '.supportedNetworks[] | select(.chainId == 138) | .deployedVenuePoolCount >= 19 and .publicRoutingPoolCount >= 19' >/dev/null
|
||||
curl -fsS https://atomic-swap.defi-oracle.io/data/live-route-registry.json | jq -e '.liveSwapRoutes | length >= 19' >/dev/null
|
||||
curl -fsS https://atomic-swap.defi-oracle.io/data/live-route-registry.json | jq -e '.liveBridgeRoutes | length >= 12' >/dev/null
|
||||
curl -fsS https://atomic-swap.defi-oracle.io/data/deployed-venue-inventory.json | jq -e '.networks[] | select(.chainId == 138) | .venueCounts.deployedVenuePoolCount >= 19 and .summary.totalVenues >= 19' >/dev/null
|
||||
|
||||
echo "Deployed atomic-swap-dapp to VMID $VMID via $PROXMOX_HOST"
|
||||
191
scripts/deployment/price-cw-token-mainnet.sh
Executable file
191
scripts/deployment/price-cw-token-mainnet.sh
Executable file
@@ -0,0 +1,191 @@
|
||||
#!/usr/bin/env bash
|
||||
# Price cW* wrapped stables on Ethereum Mainnet using:
|
||||
# (1) Accounting peg assumption ($1 per token for USD-stable wrappers — document in reporting)
|
||||
# (2) DODO PMM integration: getPoolPrice / getPoolPriceOrOracle (1e18 = $1 when oracle aligned)
|
||||
# (3) Implied ratio from pool vault reserves (USDC per cWUSDT if base/quote match manifest)
|
||||
# (4) Optional Chainlink ETH/USD (macro reference only; not cW* direct)
|
||||
#
|
||||
# Usage (from repo root):
|
||||
# source scripts/lib/load-project-env.sh
|
||||
# ./scripts/deployment/price-cw-token-mainnet.sh
|
||||
# ./scripts/deployment/price-cw-token-mainnet.sh --json
|
||||
# POOL_CWUSDT_USDC_MAINNET=0x... ./scripts/deployment/price-cw-token-mainnet.sh
|
||||
#
|
||||
# Requires: cast (Foundry), jq (for --json)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
# shellcheck disable=SC1091
|
||||
[[ -f "$PROJECT_ROOT/scripts/lib/load-project-env.sh" ]] && source "$PROJECT_ROOT/scripts/lib/load-project-env.sh"
|
||||
|
||||
JSON=false
|
||||
for a in "$@"; do [[ "$a" == "--json" ]] && JSON=true; done
|
||||
|
||||
RPC="${ETHEREUM_MAINNET_RPC:-https://ethereum-rpc.publicnode.com}"
|
||||
INT="${DODO_PMM_INTEGRATION_MAINNET:-0xa9F284eD010f4F7d7F8F201742b49b9f58e29b84}"
|
||||
CWUSDT="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
||||
# Mainnet canonical USDC (do not use Chain 138 OFFICIAL_USDC_ADDRESS for this script)
|
||||
USDC_MAINNET_CANON="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}"
|
||||
USDC_OFFICIAL="${USDC_MAINNET_CANON}"
|
||||
POOL="${POOL_CWUSDT_USDC_MAINNET:-0x27f3aE7EE71Be3d77bAf17d4435cF8B895DD25D2}"
|
||||
# Chainlink ETH/USD — verified on mainnet; optional macro reference
|
||||
CHAINLINK_ETH_USD="${CHAINLINK_ETH_USD_FEED:-0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419}"
|
||||
|
||||
first_word() {
|
||||
awk '{print $1}' | head -1
|
||||
}
|
||||
|
||||
to_dec() {
|
||||
local x="$1"
|
||||
[[ -z "$x" ]] && echo "" && return
|
||||
echo "$x" | sed 's/\[.*//g' | awk '{print $1}'
|
||||
}
|
||||
|
||||
get_decimals() {
|
||||
local tok="$1"
|
||||
to_dec "$(cast call "$tok" "decimals()(uint8)" --rpc-url "$RPC" 2>/dev/null || echo "")"
|
||||
}
|
||||
|
||||
price_oracle_or_mid() {
|
||||
local pool="$1"
|
||||
local out
|
||||
out=$(cast call "$INT" "getPoolPriceOrOracle(address)(uint256)" "$pool" --rpc-url "$RPC" 2>/dev/null | first_word) || true
|
||||
if [[ -n "$out" && "$out" != *Error* ]]; then
|
||||
printf '%s|%s\n' "getPoolPriceOrOracle" "$out"
|
||||
return
|
||||
fi
|
||||
out=$(cast call "$INT" "getPoolPrice(address)(uint256)" "$pool" --rpc-url "$RPC" 2>/dev/null | first_word) || true
|
||||
printf '%s|%s\n' "getPoolPrice" "$out"
|
||||
}
|
||||
|
||||
parse_two_uints() {
|
||||
python3 -c "
|
||||
import re, sys
|
||||
s = sys.stdin.read()
|
||||
# cast may print scientific notation for large numbers
|
||||
parts = re.findall(r'-?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?', s)
|
||||
if len(parts) >= 2:
|
||||
print(int(float(parts[0])), int(float(parts[1])))
|
||||
"
|
||||
}
|
||||
|
||||
reserves() {
|
||||
local pool="$1"
|
||||
cast call "$INT" "getPoolReserves(address)(uint256,uint256)" "$pool" --rpc-url "$RPC" 2>/dev/null | parse_two_uints
|
||||
}
|
||||
|
||||
eth_usd_chainlink() {
|
||||
local feed="$1"
|
||||
[[ -z "$feed" ]] && { echo ""; return; }
|
||||
local code
|
||||
code=$(cast code "$feed" --rpc-url "$RPC" 2>/dev/null || true)
|
||||
[[ -z "$code" || "$code" == "0x" ]] && { echo ""; return; }
|
||||
cast call "$feed" "latestRoundData()(uint80,int256,uint256,uint256,uint80)" --rpc-url "$RPC" 2>/dev/null
|
||||
}
|
||||
|
||||
db=$(get_decimals "$CWUSDT")
|
||||
dq=$(get_decimals "$USDC_OFFICIAL")
|
||||
read -r br_raw qr_raw < <(reserves "$POOL" | awk '{print $1, $2}')
|
||||
br_raw=$(to_dec "${br_raw:-0}")
|
||||
qr_raw=$(to_dec "${qr_raw:-0}")
|
||||
|
||||
_pom=$(price_oracle_or_mid "$POOL")
|
||||
pm_method="${_pom%%|*}"
|
||||
price_raw=$(to_dec "${_pom#*|}")
|
||||
|
||||
# price from integration is 1e18-scaled "USD" in docs
|
||||
price_human=""
|
||||
if [[ -n "$price_raw" && "$price_raw" =~ ^[0-9]+$ ]]; then
|
||||
price_human=$(python3 -c "print(int('$price_raw')/1e18)" 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
implied_usdc_per_cwusdt=""
|
||||
if [[ -n "$br_raw" && -n "$qr_raw" && "$br_raw" != "0" && -n "$db" && -n "$dq" ]]; then
|
||||
implied_usdc_per_cwusdt=$(python3 -c "br=int('$br_raw'); qr=int('$qr_raw'); db=int('$db'); dq=int('$dq'); print((qr/10**dq)/(br/10**db))" 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
cl_eth=$(eth_usd_chainlink "$CHAINLINK_ETH_USD")
|
||||
cl_eth_price=""
|
||||
if [[ -n "$cl_eth" ]]; then
|
||||
# latestRoundData(): cast prints one value per line; answer is the 2nd field (int256, 1e8 USD for ETH/USD)
|
||||
ans=$(echo "$cl_eth" | awk 'NR == 2 { print $1; exit }')
|
||||
[[ -n "$ans" ]] && cl_eth_price=$(python3 -c "a=int(float('$ans')); print(a/1e8)" 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
if [[ "$JSON" == true ]]; then
|
||||
jq_n() { [[ -z "$1" ]] && echo null || echo "$1"; }
|
||||
jq -n \
|
||||
--arg network "ethereum-mainnet" \
|
||||
--arg token "cWUSDT" \
|
||||
--arg token_addr "$CWUSDT" \
|
||||
--arg pool "$POOL" \
|
||||
--arg integration "$INT" \
|
||||
--argjson accounting_usd_assumption 1 \
|
||||
--arg pool_price_method "${pm_method:-none}" \
|
||||
--argjson pool_price_raw "$(jq_n "$price_raw")" \
|
||||
--argjson pool_price_usd_scale "$(jq_n "$price_human")" \
|
||||
--argjson base_reserve_raw "$(jq_n "$br_raw")" \
|
||||
--argjson quote_reserve_raw "$(jq_n "$qr_raw")" \
|
||||
--arg implied_str "${implied_usdc_per_cwusdt:-}" \
|
||||
--argjson chainlink_eth_usd "$(jq_n "$cl_eth_price")" \
|
||||
'{
|
||||
network: $network,
|
||||
token: $token,
|
||||
tokenAddress: $token_addr,
|
||||
poolAddress: $pool,
|
||||
integrationAddress: $integration,
|
||||
accountingUsdAssumptionPerToken: $accounting_usd_assumption,
|
||||
poolMidOrOracle: {
|
||||
method: $pool_price_method,
|
||||
priceRaw1e18: $pool_price_raw,
|
||||
priceAsUsdIf1e18EqualsOneDollar: $pool_price_usd_scale
|
||||
},
|
||||
impliedFromReserves: {
|
||||
baseReserveRaw: $base_reserve_raw,
|
||||
quoteReserveRaw: $quote_reserve_raw,
|
||||
impliedUsdcPerCwusdtIfBaseIsCwusdtAndQuoteIsUsdc: (if $implied_str == "" then null else ($implied_str | tonumber) end)
|
||||
},
|
||||
chainlinkEthUsd: $chainlink_eth_usd
|
||||
}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cat << EOF
|
||||
========================================
|
||||
cWUSDT (Mainnet) — USD pricing toolkit
|
||||
========================================
|
||||
RPC: ${RPC%%\?*}
|
||||
Integration: $INT
|
||||
Pool (cWUSDT/USDC): $POOL
|
||||
cWUSDT token: $CWUSDT
|
||||
USDC (mainnet): $USDC_OFFICIAL
|
||||
|
||||
(1) ACCOUNTING ASSUMPTION (treasury / reporting)
|
||||
Treat 1 cWUSDT ≈ 1 USD only if your policy says the wrapper tracks USDT 1:1.
|
||||
Use for: internal books, dashboards where Etherscan shows \$0.
|
||||
|
||||
(2) POOL MID / ORACLE (DODOPMMIntegration)
|
||||
Method: ${pm_method:-n/a}
|
||||
Raw price (uint256, doc scale 1e18 = \$1): ${price_raw:-n/a}
|
||||
As decimal (if 1e18 = \$1): ${price_human:-n/a}
|
||||
|
||||
(3) IMPLIED FROM VAULT RESERVES (USDC per cWUSDT)
|
||||
Base reserve (cWUSDT, ${db:-?} dp): ${br_raw:-n/a}
|
||||
Quote reserve (USDC, ${dq:-?} dp): ${qr_raw:-n/a}
|
||||
Implied USDC per 1 cWUSDT: ${implied_usdc_per_cwusdt:-n/a}
|
||||
(Thin or imbalanced pools skew this; use for sanity check, not sole mark.)
|
||||
|
||||
(4) CHAINLINK ETH/USD (macro only — not cWUSDT)
|
||||
Feed: ${CHAINLINK_ETH_USD}
|
||||
ETH/USD: ${cl_eth_price:-n/a}
|
||||
|
||||
Optional env overrides:
|
||||
ETHEREUM_MAINNET_RPC, DODO_PMM_INTEGRATION_MAINNET,
|
||||
CWUSDT_MAINNET, POOL_CWUSDT_USDC_MAINNET, USDC_MAINNET,
|
||||
CHAINLINK_ETH_USD_FEED
|
||||
|
||||
See: docs/03-deployment/CW_TOKEN_USD_PRICING_RUNBOOK.md
|
||||
========================================
|
||||
EOF
|
||||
@@ -1,13 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run remaining cW* steps: deploy (or dry-run), update token-mapping from .env, optional verify.
|
||||
# See docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md and docs/00-meta/CW_BRIDGE_TASK_LIST.md.
|
||||
# Mainnet Etherscan (CompliantWrappedToken): smom-dbis-138/scripts/deployment/verify-mainnet-cw-etherscan.sh
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/deployment/run-cw-remaining-steps.sh [--dry-run] [--deploy] [--update-mapping] [--verify]
|
||||
# --dry-run Run deploy-cw in dry-run mode (print commands only).
|
||||
# --deploy Run deploy-cw on all chains (requires RPC/PRIVATE_KEY in smom-dbis-138/.env).
|
||||
# --update-mapping Update config/token-mapping-multichain.json from CWUSDT_*/CWUSDC_* in .env.
|
||||
# --verify For each chain with CWUSDT_* set, check MINTER_ROLE/BURNER_ROLE on cW* for CW_BRIDGE_*.
|
||||
# --update-mapping Update config/token-mapping-multichain.json from CW*_CHAIN keys in .env (12 cW symbols × mapped chains).
|
||||
# --verify For each chain with CW_BRIDGE_* set, check MINTER_ROLE/BURNER_ROLE on every deployed cW* (CWUSDT…CWXAUT) vs CW_BRIDGE_*.
|
||||
# With no options, runs --dry-run then --update-mapping (if any CWUSDT_* in .env).
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
@@ -103,18 +104,18 @@ if $DO_UPDATE_MAPPING; then
|
||||
fi
|
||||
|
||||
if $DO_VERIFY; then
|
||||
echo "=== Verify MINTER/BURNER roles on cW* for each chain ==="
|
||||
echo "=== Verify MINTER/BURNER roles on cW* for each chain (12 symbols) ==="
|
||||
MINTER_ROLE=$(cast keccak "MINTER_ROLE" 2>/dev/null || echo "0x")
|
||||
BURNER_ROLE=$(cast keccak "BURNER_ROLE" 2>/dev/null || echo "0x")
|
||||
# Env suffix -> env var for CompliantWrappedToken (must match update_mapping keyToEnv)
|
||||
CW_KEYS=(CWUSDT CWUSDC CWEURC CWEURT CWGBPC CWGBPT CWAUDC CWJPYC CWCHFC CWCADC CWXAUC CWXAUT)
|
||||
for name in MAINNET CRONOS BSC POLYGON GNOSIS AVALANCHE BASE ARBITRUM OPTIMISM; do
|
||||
cwusdt_var="CWUSDT_${name}"
|
||||
bridge_var="CW_BRIDGE_${name}"
|
||||
cwusdt="${!cwusdt_var:-}"
|
||||
bridge="${!bridge_var:-}"
|
||||
rpc_var="${name}_RPC_URL"
|
||||
[[ -z "$rpc_var" ]] && rpc_var="${name}_RPC"
|
||||
rpc="${!rpc_var:-}"
|
||||
if [[ -z "$cwusdt" || -z "$bridge" ]]; then continue; fi
|
||||
if [[ -z "$bridge" ]]; then continue; fi
|
||||
if [[ -z "$rpc" ]]; then
|
||||
case "$name" in
|
||||
MAINNET) rpc="${ETH_MAINNET_RPC_URL:-${ETHEREUM_MAINNET_RPC:-}}";;
|
||||
@@ -129,9 +130,17 @@ if $DO_VERIFY; then
|
||||
esac
|
||||
fi
|
||||
if [[ -z "$rpc" ]]; then echo " Skip $name: no RPC"; continue; fi
|
||||
m=$(cast call "$cwusdt" "hasRole(bytes32,address)(bool)" "$MINTER_ROLE" "$bridge" --rpc-url "$rpc" 2>/dev/null || echo "false")
|
||||
b=$(cast call "$cwusdt" "hasRole(bytes32,address)(bool)" "$BURNER_ROLE" "$bridge" --rpc-url "$rpc" 2>/dev/null || echo "false")
|
||||
echo " $name: MINTER=$m BURNER=$b (cWUSDT=$cwusdt bridge=$bridge)"
|
||||
any=false
|
||||
for key in "${CW_KEYS[@]}"; do
|
||||
var="${key}_${name}"
|
||||
addr="${!var:-}"
|
||||
[[ -z "$addr" ]] && continue
|
||||
any=true
|
||||
m=$(cast call "$addr" "hasRole(bytes32,address)(bool)" "$MINTER_ROLE" "$bridge" --rpc-url "$rpc" 2>/dev/null || echo "false")
|
||||
b=$(cast call "$addr" "hasRole(bytes32,address)(bool)" "$BURNER_ROLE" "$bridge" --rpc-url "$rpc" 2>/dev/null || echo "false")
|
||||
echo " $name $key: MINTER=$m BURNER=$b ($addr)"
|
||||
done
|
||||
if ! $any; then echo " $name: no CW*_* addresses in .env; skipped"; fi
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,24 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
# Send a random amount between 5 and 9 ETH (inclusive) to each address in
|
||||
# config/pmm-soak-wallet-grid.json (Elemental Imperium 33×33×6 matrix).
|
||||
# Send native ETH to each address in config/pmm-soak-wallet-grid.json (Elemental
|
||||
# Imperium 33×33×6 matrix).
|
||||
#
|
||||
# Default mode (spread): take BALANCE_PCT of the signer's native balance (minus
|
||||
# optional reserve), split across the selected wallet slice with independent
|
||||
# uniform random multipliers in [1 - SPREAD_PCT/100, 1 + SPREAD_PCT/100] around
|
||||
# equal weight, then renormalize so the total sent equals that budget exactly.
|
||||
#
|
||||
# Legacy mode: random 5–9 ETH per wallet (--uniform-eth).
|
||||
#
|
||||
# Requires: cast (Foundry), jq, python3. Loads PRIVATE_KEY and RPC via load-project-env.sh.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/deployment/send-eth-ei-matrix-wallets.sh [--dry-run] [--limit N] [--offset N]
|
||||
#
|
||||
# --dry-run Print planned sends only (no transactions).
|
||||
# --limit N Process at most N wallets (after offset). Default: all.
|
||||
# --offset N Skip the first N wallets (resume / partial run).
|
||||
# --dry-run Print planned sends only (no transactions).
|
||||
# --limit N Process at most N wallets (after offset). Default: all.
|
||||
# --offset N Skip the first N wallets (resume / partial run).
|
||||
# --balance-pct P Percent of (balance minus reserve) to distribute (default: 80).
|
||||
# --spread-pct S Per-wallet multiplier range ±S%% around fair share (default: 15).
|
||||
# --reserve-wei W Wei to leave untouchable before applying balance-pct (default: 0).
|
||||
# --uniform-eth Legacy: random 5–9 ETH per wallet (ignores balance-pct/spread).
|
||||
#
|
||||
# Gas (Chain 138 / Besu): defaults avoid stuck pending txs from near-zero EIP-1559 caps.
|
||||
# Override if needed:
|
||||
# EI_MATRIX_GAS_PRICE=100000000000
|
||||
# EI_MATRIX_PRIORITY_GAS_PRICE=20000000000
|
||||
#
|
||||
# Spread mode defaults (same as --balance-pct / --spread-pct / --reserve-wei):
|
||||
# EI_MATRIX_BALANCE_PCT=80 EI_MATRIX_SPREAD_PCT=15 EI_MATRIX_RESERVE_WEI=0
|
||||
# Balance query uses public RPC by default; override with EI_MATRIX_BALANCE_RPC. Tx RPC: RPC_URL_138 or public.
|
||||
#
|
||||
# Nonces: each send uses an explicit --nonce from eth_getTransactionCount(..., "pending")
|
||||
# and increments locally so --async does not race duplicate nonces.
|
||||
#
|
||||
# RPC: defaults to Chain 138 public HTTP (reachable without LAN). Override with RPC_URL_138
|
||||
# (e.g. core or Besu on 192.168.x.x). Public URL env: CHAIN138_PUBLIC_RPC_URL or RPC_URL_138_PUBLIC.
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
@@ -28,11 +46,19 @@ cd "$PROJECT_ROOT"
|
||||
DRY_RUN=false
|
||||
LIMIT=""
|
||||
OFFSET="0"
|
||||
BALANCE_PCT="${EI_MATRIX_BALANCE_PCT:-80}"
|
||||
SPREAD_PCT="${EI_MATRIX_SPREAD_PCT:-15}"
|
||||
RESERVE_WEI="${EI_MATRIX_RESERVE_WEI:-0}"
|
||||
UNIFORM_ETH_LEGACY=false
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--dry-run) DRY_RUN=true; shift ;;
|
||||
--limit) LIMIT="${2:?}"; shift 2 ;;
|
||||
--offset) OFFSET="${2:?}"; shift 2 ;;
|
||||
--balance-pct) BALANCE_PCT="${2:?}"; shift 2 ;;
|
||||
--spread-pct) SPREAD_PCT="${2:?}"; shift 2 ;;
|
||||
--reserve-wei) RESERVE_WEI="${2:?}"; shift 2 ;;
|
||||
--uniform-eth) UNIFORM_ETH_LEGACY=true; shift ;;
|
||||
*) echo "Unknown arg: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
@@ -40,6 +66,13 @@ done
|
||||
# shellcheck disable=SC1091
|
||||
source "$PROJECT_ROOT/scripts/lib/load-project-env.sh"
|
||||
|
||||
CHAIN138_PUBLIC_RPC_DEFAULT="https://rpc-http-pub.d-bis.org"
|
||||
RPC_PUBLIC="${CHAIN138_PUBLIC_RPC_URL:-${RPC_URL_138_PUBLIC:-$CHAIN138_PUBLIC_RPC_DEFAULT}}"
|
||||
# Balance read for spread budget: public RPC first (aligns with deployer checks on public endpoints).
|
||||
# After that, nonce + sends use RPC (same as RPC_URL_138 when set, else public).
|
||||
BALANCE_RPC="${EI_MATRIX_BALANCE_RPC:-$RPC_PUBLIC}"
|
||||
RPC="${RPC_URL_138:-$RPC_PUBLIC}"
|
||||
|
||||
LOCK_FILE="${PROJECT_ROOT}/reports/status/ei-matrix-eth-send.lock"
|
||||
mkdir -p "$(dirname "$LOCK_FILE")"
|
||||
exec 200>"$LOCK_FILE"
|
||||
@@ -47,8 +80,6 @@ if ! flock -n 200; then
|
||||
echo "Another send-eth-ei-matrix-wallets.sh is already running (lock: $LOCK_FILE)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RPC="${RPC_URL_138:-http://192.168.11.211:8545}"
|
||||
GRID="$PROJECT_ROOT/config/pmm-soak-wallet-grid.json"
|
||||
DEPLOYER_CANONICAL="0x4A666F96fC8764181194447A7dFdb7d471b301C8"
|
||||
# Wei per gas — must exceed stuck-replacement threshold on busy pools (see script header).
|
||||
@@ -63,13 +94,21 @@ command -v jq &>/dev/null || { echo "jq required" >&2; exit 1; }
|
||||
|
||||
FROM_ADDR=$(cast wallet address --private-key "$PRIVATE_KEY")
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "EI matrix ETH distribution (random 5–9 ETH per wallet)"
|
||||
if $UNIFORM_ETH_LEGACY; then
|
||||
echo "EI matrix ETH distribution (legacy: random 5–9 ETH per wallet)"
|
||||
else
|
||||
echo "EI matrix ETH distribution (${BALANCE_PCT}% of balance, ±${SPREAD_PCT}% jitter, normalized)"
|
||||
fi
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "RPC: $RPC"
|
||||
echo "Balance RPC (public): $BALANCE_RPC"
|
||||
echo "Tx / nonce RPC: $RPC"
|
||||
echo "Signer: $FROM_ADDR"
|
||||
echo "Grid: $GRID"
|
||||
echo "Dry-run: $DRY_RUN"
|
||||
echo "Offset: $OFFSET Limit: ${LIMIT:-all}"
|
||||
if ! $UNIFORM_ETH_LEGACY; then
|
||||
echo "Reserve: ${RESERVE_WEI} wei excluded before balance-pct"
|
||||
fi
|
||||
echo "Gas: maxFee=$EI_MATRIX_GAS_PRICE wei priorityFee=$EI_MATRIX_PRIORITY_GAS_PRICE wei"
|
||||
echo ""
|
||||
|
||||
@@ -87,10 +126,41 @@ pending_nonce() {
|
||||
cast to-dec "$hex"
|
||||
}
|
||||
|
||||
random_wei() {
|
||||
random_wei_uniform_legacy() {
|
||||
python3 -c "import random; from decimal import Decimal; print(int(Decimal(str(random.uniform(5.0, 9.0))) * 10**18))"
|
||||
}
|
||||
|
||||
# Args: count budget_wei spread_pct — prints one wei amount per line; sum(lines) == budget_wei
|
||||
generate_spread_amounts_wei() {
|
||||
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]")
|
||||
# Integer-only weights (float on ~10^27 wei rounds to 0 → huge remainder loop and hang)
|
||||
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 w in raw:
|
||||
print(w)
|
||||
PY
|
||||
}
|
||||
|
||||
ERR_LOG="${PROJECT_ROOT}/reports/status/ei-matrix-eth-send-failures.log"
|
||||
mkdir -p "$(dirname "$ERR_LOG")"
|
||||
|
||||
@@ -102,55 +172,120 @@ else
|
||||
NONCE=0
|
||||
fi
|
||||
|
||||
# Args: addr wei idx — uses global NONCE, sent, failed, DRY_RUN, RPC, PRIVATE_KEY, EI_MATRIX_* , ERR_LOG
|
||||
matrix_try_send() {
|
||||
local addr="$1" wei="$2" idx="$3"
|
||||
local eth_approx out tx GP PP attempt
|
||||
if [[ "$wei" == "0" ]]; then
|
||||
echo "[skip] idx=$idx $addr zero wei"
|
||||
return 0
|
||||
fi
|
||||
eth_approx=$(python3 -c "w=int('$wei'); print(f'{w / 1e18:.6f}')")
|
||||
if $DRY_RUN; then
|
||||
echo "[dry-run] idx=$idx $addr ${wei} wei (~${eth_approx} ETH)"
|
||||
return 0
|
||||
fi
|
||||
GP="$EI_MATRIX_GAS_PRICE"
|
||||
PP="$EI_MATRIX_PRIORITY_GAS_PRICE"
|
||||
attempt=1
|
||||
while [[ "$attempt" -le 2 ]]; do
|
||||
if out=$(cast send "$addr" --value "$wei" --rpc-url "$RPC" --private-key "$PRIVATE_KEY" \
|
||||
--nonce "$NONCE" \
|
||||
--async \
|
||||
--gas-price "$GP" \
|
||||
--priority-gas-price "$PP" \
|
||||
2>&1); then
|
||||
tx=$(echo "$out" | tail -n1)
|
||||
echo "[ok] idx=$idx nonce=$NONCE $addr ${eth_approx} ETH tx=$tx"
|
||||
sent=$((sent + 1))
|
||||
NONCE=$((NONCE + 1))
|
||||
echo "$idx" > "${PROJECT_ROOT}/reports/status/ei-matrix-eth-send-last-idx.txt"
|
||||
break
|
||||
fi
|
||||
if echo "$out" | grep -q "Replacement transaction underpriced" && [[ "$attempt" -eq 1 ]]; then
|
||||
GP=$((GP * 2))
|
||||
PP=$((PP * 2))
|
||||
NONCE=$(pending_nonce) || true
|
||||
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
|
||||
break
|
||||
done
|
||||
}
|
||||
|
||||
stream_addresses() {
|
||||
# Slice in jq only — jq|head breaks with SIGPIPE under pipefail when LIMIT is set.
|
||||
if [[ -n "${LIMIT:-}" ]]; then
|
||||
jq -r '.wallets[] | .address' "$GRID" | tail -n +$((OFFSET + 1)) | head -n "$LIMIT"
|
||||
jq -r --argjson o "$OFFSET" --argjson l "$LIMIT" '.wallets[$o:$o+$l][] | .address' "$GRID"
|
||||
else
|
||||
jq -r '.wallets[] | .address' "$GRID" | tail -n +$((OFFSET + 1))
|
||||
jq -r --argjson o "$OFFSET" '.wallets[$o:][] | .address' "$GRID"
|
||||
fi
|
||||
}
|
||||
|
||||
compute_budget_wei() {
|
||||
python3 - "$1" "$2" "$3" <<'PY'
|
||||
import sys
|
||||
bal = int(sys.argv[1])
|
||||
reserve = int(sys.argv[2])
|
||||
pct = float(sys.argv[3])
|
||||
avail = max(0, bal - reserve)
|
||||
print(int(avail * pct / 100.0))
|
||||
PY
|
||||
}
|
||||
|
||||
ADDR_TMP=""
|
||||
AMOUNTS_TMP=""
|
||||
cleanup_tmp() {
|
||||
[[ -n "$ADDR_TMP" && -f "$ADDR_TMP" ]] && rm -f "$ADDR_TMP"
|
||||
[[ -n "$AMOUNTS_TMP" && -f "$AMOUNTS_TMP" ]] && rm -f "$AMOUNTS_TMP"
|
||||
}
|
||||
trap cleanup_tmp EXIT
|
||||
|
||||
if $UNIFORM_ETH_LEGACY; then
|
||||
:
|
||||
else
|
||||
ADDR_TMP=$(mktemp)
|
||||
AMOUNTS_TMP=$(mktemp)
|
||||
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
|
||||
BAL_WEI=$(cast balance "$FROM_ADDR" --rpc-url "$BALANCE_RPC" 2>/dev/null | awk '{print $1}' || echo "0")
|
||||
BUDGET_WEI=$(compute_budget_wei "$BAL_WEI" "$RESERVE_WEI" "$BALANCE_PCT")
|
||||
eth_bal=$(python3 -c "print(f'{int(\"$BAL_WEI\") / 1e18:.8f}')" 2>/dev/null || echo "?")
|
||||
eth_bud=$(python3 -c "print(f'{int(\"$BUDGET_WEI\") / 1e18:.8f}')" 2>/dev/null || echo "?")
|
||||
echo "Balance: $BAL_WEI wei (~$eth_bal ETH)"
|
||||
echo "Budget: $BUDGET_WEI wei (~$eth_bud ETH) (${BALANCE_PCT}% of max(0, balance − ${RESERVE_WEI} reserve))"
|
||||
echo "Wallets: $WALLET_COUNT spread: ±${SPREAD_PCT}% then normalized to budget"
|
||||
echo ""
|
||||
if [[ "$BUDGET_WEI" == "0" ]]; then
|
||||
echo "Budget is zero (balance below reserve or pct=0). Nothing to send." >&2
|
||||
exit 1
|
||||
fi
|
||||
generate_spread_amounts_wei "$WALLET_COUNT" "$BUDGET_WEI" "$SPREAD_PCT" > "$AMOUNTS_TMP"
|
||||
fi
|
||||
|
||||
sent=0
|
||||
failed=0
|
||||
idx=$OFFSET
|
||||
while read -r addr; do
|
||||
wei=$(random_wei)
|
||||
eth_approx=$(python3 -c "print(f'{$wei / 1e18:.6f}')")
|
||||
if $DRY_RUN; then
|
||||
echo "[dry-run] idx=$idx $addr ${wei} wei (~${eth_approx} ETH)"
|
||||
else
|
||||
GP="$EI_MATRIX_GAS_PRICE"
|
||||
PP="$EI_MATRIX_PRIORITY_GAS_PRICE"
|
||||
attempt=1
|
||||
while [[ "$attempt" -le 2 ]]; do
|
||||
if out=$(cast send "$addr" --value "$wei" --rpc-url "$RPC" --private-key "$PRIVATE_KEY" \
|
||||
--nonce "$NONCE" \
|
||||
--async \
|
||||
--gas-price "$GP" \
|
||||
--priority-gas-price "$PP" \
|
||||
2>&1); then
|
||||
tx=$(echo "$out" | tail -n1)
|
||||
echo "[ok] idx=$idx nonce=$NONCE $addr ${eth_approx} ETH tx=$tx"
|
||||
sent=$((sent + 1))
|
||||
NONCE=$((NONCE + 1))
|
||||
echo "$idx" > "${PROJECT_ROOT}/reports/status/ei-matrix-eth-send-last-idx.txt"
|
||||
break
|
||||
fi
|
||||
if echo "$out" | grep -q "Replacement transaction underpriced" && [[ "$attempt" -eq 1 ]]; then
|
||||
GP=$((GP * 2))
|
||||
PP=$((PP * 2))
|
||||
NONCE=$(pending_nonce) || true
|
||||
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
|
||||
break
|
||||
done
|
||||
fi
|
||||
idx=$((idx + 1))
|
||||
done < <(stream_addresses)
|
||||
|
||||
if $UNIFORM_ETH_LEGACY; then
|
||||
while read -r addr; do
|
||||
wei=$(random_wei_uniform_legacy)
|
||||
matrix_try_send "$addr" "$wei" "$idx"
|
||||
idx=$((idx + 1))
|
||||
done < <(stream_addresses)
|
||||
else
|
||||
while IFS=$'\t' read -r addr wei; do
|
||||
matrix_try_send "$addr" "$wei" "$idx"
|
||||
idx=$((idx + 1))
|
||||
done < <(paste -d $'\t' "$ADDR_TMP" "$AMOUNTS_TMP")
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if $DRY_RUN; then
|
||||
|
||||
Reference in New Issue
Block a user