Sync workspace: config, docs, scripts, CI, operator rules, and submodule pointers.
- Update dbis_core, cross-chain-pmm-lps, explorer-monorepo, metamask-integration, pr-workspace/chains - Omit embedded publish git dirs and empty placeholders from index Made-with: Cursor
This commit is contained in:
70
scripts/deployment/acquire-official-usdc-from-cusdc-chain138.sh
Executable file
70
scripts/deployment/acquire-official-usdc-from-cusdc-chain138.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Acquire official Chain 138 USDC from the deployer's cUSDC inventory using the
|
||||
# canonical DODO cUSDC/USDC mirror pool.
|
||||
#
|
||||
# Default amount: 250,000 USDC worth of cUSDC (250000e6).
|
||||
# This unblocks upstream-native WETH/USDC seeding without disturbing the live
|
||||
# router-v2 venue layer.
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
if [[ -f "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh"
|
||||
fi
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd cast
|
||||
|
||||
RPC_URL="${CHAIN138_CUSDC_USDC_SWAP_RPC_URL:-http://192.168.11.221:8545}"
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-}"
|
||||
DEPLOYER="${DEPLOYER_ADDRESS:-0x4A666F96fC8764181194447A7dFdb7d471b301C8}"
|
||||
POOL="${POOL_CUSDCUSDC:-0xc39B7D0F40838cbFb54649d327f49a6DAC964062}"
|
||||
INTEGRATION="${DODO_PMM_INTEGRATION_ADDRESS:-${CHAIN_138_DODO_PMM_INTEGRATION:-0x86ADA6Ef91A3B450F89f2b751e93B1b7A3218895}}"
|
||||
CUSDC="${COMPLIANT_USDC_ADDRESS:-0xf22258f57794CC8E06237084b353Ab30fFfa640b}"
|
||||
USDC="${OFFICIAL_USDC_ADDRESS:-0x71D6687F38b93CCad569Fa6352c876eea967201b}"
|
||||
AMOUNT_IN="${AMOUNT_IN:-250000000000}" # 250,000 with 6 decimals
|
||||
MIN_OUT="${MIN_OUT:-249000000000}" # 249,000 with 6 decimals
|
||||
GAS_PRICE_WEI="${CHAIN138_DEPLOY_GAS_PRICE_WEI:-1000}"
|
||||
|
||||
if [[ -z "${PRIVATE_KEY}" ]]; then
|
||||
echo "[fail] PRIVATE_KEY is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "== Acquire official USDC from cUSDC on Chain 138 =="
|
||||
echo "RPC: ${RPC_URL}"
|
||||
echo "Deployer: ${DEPLOYER}"
|
||||
echo "Pool: ${POOL}"
|
||||
echo "Integration: ${INTEGRATION}"
|
||||
echo "Amount in: ${AMOUNT_IN} (cUSDC, 6 decimals)"
|
||||
echo "Min out: ${MIN_OUT} (USDC, 6 decimals)"
|
||||
echo
|
||||
|
||||
allowance="$(cast call --rpc-url "${RPC_URL}" "${CUSDC}" 'allowance(address,address)(uint256)' "${DEPLOYER}" "${INTEGRATION}")"
|
||||
echo "[info] current cUSDC allowance to integration: ${allowance}"
|
||||
|
||||
quote="$(cast call --rpc-url "${RPC_URL}" "${POOL}" 'querySellBase(address,uint256)(uint256,uint256)' "${DEPLOYER}" "${AMOUNT_IN}")"
|
||||
quoted_out="$(printf '%s\n' "${quote}" | sed -n '1p' | awk '{print $1}')"
|
||||
echo "[info] quoted output via canonical mirror pool: ${quoted_out} USDC raw"
|
||||
|
||||
cast send "${INTEGRATION}" \
|
||||
'swapExactIn(address,address,uint256,uint256)' \
|
||||
"${POOL}" "${CUSDC}" "${AMOUNT_IN}" "${MIN_OUT}" \
|
||||
--rpc-url "${RPC_URL}" \
|
||||
--private-key "${PRIVATE_KEY}" \
|
||||
--legacy \
|
||||
--gas-price "${GAS_PRICE_WEI}"
|
||||
|
||||
echo
|
||||
new_usdc="$(cast call --rpc-url "${RPC_URL}" "${USDC}" 'balanceOf(address)(uint256)' "${DEPLOYER}" | awk '{print $1}')"
|
||||
echo "[ok] new official USDC balance: ${new_usdc}"
|
||||
270
scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh
Executable file
270
scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh
Executable file
@@ -0,0 +1,270 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Add liquidity to an existing Mainnet public DODO cW/USD pool.
|
||||
#
|
||||
# Run from repo root (directory that contains scripts/):
|
||||
# bash scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh ...
|
||||
#
|
||||
# Amount arguments are raw token units (6 decimals for current cW/USD suite).
|
||||
#
|
||||
# Examples:
|
||||
# bash scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh \
|
||||
# --pair=cwusdc-usdc --base-amount=118471 --quote-amount=49527316 --dry-run
|
||||
#
|
||||
# bash scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh \
|
||||
# --pair=cwusdc-usdc --base-amount=118471 --quote-amount=49527316
|
||||
#
|
||||
# # --execute is accepted as an explicit alias for "not --dry-run" (optional).
|
||||
# bash scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh \
|
||||
# --pair=cwusdc-usdc --base-amount=118471 --quote-amount=10000000 --execute
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Add liquidity to an existing Mainnet public DODO cW/USD pool.
|
||||
|
||||
Run from repo root (directory that contains scripts/):
|
||||
bash scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh --pair=PAIR ...
|
||||
|
||||
Amounts are raw token units (6 decimals for the current cW/USD suite).
|
||||
|
||||
Required:
|
||||
--pair=PAIR cwusdc-usdc | cwusdt-usdc | cwusdt-usdt | cwusdc-usdt | cwusdt-cwusdc
|
||||
--base-amount=N base token amount (wei-like integer, 6 decimals)
|
||||
--quote-amount=N quote token amount
|
||||
|
||||
Optional:
|
||||
--dry-run plan only (no transactions)
|
||||
--execute explicit live run (default is live if neither flag is set; do not combine with --dry-run)
|
||||
|
||||
Env:
|
||||
ETHEREUM_MAINNET_RPC
|
||||
PRIVATE_KEY
|
||||
DODO_PMM_INTEGRATION_MAINNET
|
||||
|
||||
Example (plan first):
|
||||
bash scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh \
|
||||
--pair=cwusdc-usdc --base-amount=118471 --quote-amount=49527316 --dry-run
|
||||
EOF
|
||||
}
|
||||
|
||||
source "${PROJECT_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd cast
|
||||
|
||||
PAIR=""
|
||||
BASE_AMOUNT=""
|
||||
QUOTE_AMOUNT=""
|
||||
USE_DRY_RUN=0
|
||||
EXPLICIT_EXECUTE=0
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h) usage; exit 0 ;;
|
||||
--pair=*) PAIR="${arg#*=}" ;;
|
||||
--base-amount=*) BASE_AMOUNT="${arg#*=}" ;;
|
||||
--quote-amount=*) QUOTE_AMOUNT="${arg#*=}" ;;
|
||||
--dry-run) USE_DRY_RUN=1 ;;
|
||||
--execute) EXPLICIT_EXECUTE=1 ;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $arg (try --help)" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( USE_DRY_RUN && EXPLICIT_EXECUTE )); then
|
||||
echo "[fail] use either --dry-run or --execute, not both" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
DRY_RUN="$USE_DRY_RUN"
|
||||
|
||||
if [[ -z "$PAIR" || -z "$BASE_AMOUNT" || -z "$QUOTE_AMOUNT" ]]; then
|
||||
echo "[fail] required args: --pair, --base-amount, --quote-amount (see --help)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-}"
|
||||
INTEGRATION="${DODO_PMM_INTEGRATION_MAINNET:-}"
|
||||
USDC_MAINNET="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
USDT_MAINNET="0xdAC17F958D2ee523a2206206994597C13D831ec7"
|
||||
|
||||
if [[ -z "$RPC_URL" || -z "$PRIVATE_KEY" || -z "$INTEGRATION" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC, PRIVATE_KEY, and DODO_PMM_INTEGRATION_MAINNET are required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEPLOYER="$(cast wallet address --private-key "$PRIVATE_KEY")"
|
||||
|
||||
BASE_TOKEN=""
|
||||
QUOTE_TOKEN=""
|
||||
POOL=""
|
||||
PAIR_LABEL=""
|
||||
|
||||
case "$PAIR" in
|
||||
cwusdc-usdc)
|
||||
BASE_TOKEN="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
QUOTE_TOKEN="$USDC_MAINNET"
|
||||
# Default matches cross-chain-pmm-lps/config/deployment-status.json (public cWUSDC/USDC).
|
||||
POOL="${POOL_CWUSDC_USDC_MAINNET:-0x69776fc607e9edA8042e320e7e43f54d06c68f0E}"
|
||||
PAIR_LABEL="cWUSDC/USDC"
|
||||
;;
|
||||
cwusdt-usdc)
|
||||
BASE_TOKEN="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
||||
QUOTE_TOKEN="$USDC_MAINNET"
|
||||
POOL="${POOL_CWUSDT_USDC_MAINNET:-0x27f3aE7EE71Be3d77bAf17d4435cF8B895DD25D2}"
|
||||
PAIR_LABEL="cWUSDT/USDC"
|
||||
;;
|
||||
cwusdt-usdt)
|
||||
BASE_TOKEN="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
||||
QUOTE_TOKEN="$USDT_MAINNET"
|
||||
POOL="${POOL_CWUSDT_USDT_MAINNET:-0x79156F6B7bf71a1B72D78189B540A89A6C13F6FC}"
|
||||
PAIR_LABEL="cWUSDT/USDT"
|
||||
;;
|
||||
cwusdc-usdt)
|
||||
BASE_TOKEN="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
QUOTE_TOKEN="$USDT_MAINNET"
|
||||
POOL="${POOL_CWUSDC_USDT_MAINNET:-0xCC0fd27A40775c9AfcD2BBd3f7c902b0192c247A}"
|
||||
PAIR_LABEL="cWUSDC/USDT"
|
||||
;;
|
||||
cwusdt-cwusdc)
|
||||
BASE_TOKEN="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
||||
QUOTE_TOKEN="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
POOL="${POOL_CWUSDT_CWUSDC_MAINNET:-0xe944b7Cb012A0820c07f54D51e92f0e1C74168DB}"
|
||||
PAIR_LABEL="cWUSDT/cWUSDC"
|
||||
;;
|
||||
*)
|
||||
echo "[fail] unsupported pair: $PAIR" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
mapped_pool="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$BASE_TOKEN" "$QUOTE_TOKEN" --rpc-url "$RPC_URL" 2>/dev/null | awk '{print $1}' || true)"
|
||||
if [[ -n "$mapped_pool" && "$mapped_pool" != "0x0000000000000000000000000000000000000000" && "${mapped_pool,,}" != "${POOL,,}" ]]; then
|
||||
POOL="$mapped_pool"
|
||||
fi
|
||||
|
||||
parse_tx_hash() {
|
||||
local output="$1"
|
||||
local tx_hash
|
||||
tx_hash="$(printf '%s\n' "$output" | grep -E '^0x[0-9a-fA-F]{64}$' | tail -n1 || true)"
|
||||
if [[ -z "$tx_hash" ]]; then
|
||||
tx_hash="$(printf '%s\n' "$output" | grep -E '^transactionHash[[:space:]]+0x[0-9a-fA-F]{64}$' | awk '{print $2}' | tail -n1 || true)"
|
||||
fi
|
||||
if [[ -z "$tx_hash" ]]; then
|
||||
return 1
|
||||
fi
|
||||
printf '%s\n' "$tx_hash"
|
||||
}
|
||||
|
||||
base_balance_before="$(cast call "$BASE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
quote_balance_before="$(cast call "$QUOTE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
base_allowance_before="$(cast call "$BASE_TOKEN" 'allowance(address,address)(uint256)' "$DEPLOYER" "$INTEGRATION" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
quote_allowance_before="$(cast call "$QUOTE_TOKEN" 'allowance(address,address)(uint256)' "$DEPLOYER" "$INTEGRATION" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
reserves_before="$(cast call "$POOL" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL")"
|
||||
base_reserve_before="$(printf '%s\n' "$reserves_before" | sed -n '1p' | awk '{print $1}')"
|
||||
quote_reserve_before="$(printf '%s\n' "$reserves_before" | sed -n '2p' | awk '{print $1}')"
|
||||
add_gas="$(
|
||||
cast estimate "$INTEGRATION" 'addLiquidity(address,uint256,uint256)(uint256,uint256,uint256)' \
|
||||
"$POOL" "$BASE_AMOUNT" "$QUOTE_AMOUNT" \
|
||||
--from "$DEPLOYER" \
|
||||
--rpc-url "$RPC_URL" 2>/dev/null || true
|
||||
)"
|
||||
|
||||
if (( base_balance_before < BASE_AMOUNT )); then
|
||||
echo "[fail] insufficient base balance: have=$base_balance_before need=$BASE_AMOUNT" >&2
|
||||
exit 1
|
||||
fi
|
||||
if (( quote_balance_before < QUOTE_AMOUNT )); then
|
||||
echo "[fail] insufficient quote balance: have=$quote_balance_before need=$QUOTE_AMOUNT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( DRY_RUN == 1 )); then
|
||||
echo "pair=$PAIR_LABEL"
|
||||
echo "pool=$POOL"
|
||||
echo "baseToken=$BASE_TOKEN"
|
||||
echo "quoteToken=$QUOTE_TOKEN"
|
||||
echo "baseAmount=$BASE_AMOUNT"
|
||||
echo "quoteAmount=$QUOTE_AMOUNT"
|
||||
echo "addGas=${add_gas:-unknown_pre_approval}"
|
||||
echo "baseReserveBefore=$base_reserve_before"
|
||||
echo "quoteReserveBefore=$quote_reserve_before"
|
||||
echo "baseReserveAfter=$(( base_reserve_before + BASE_AMOUNT ))"
|
||||
echo "quoteReserveAfter=$(( quote_reserve_before + QUOTE_AMOUNT ))"
|
||||
echo "baseBalanceBefore=$base_balance_before"
|
||||
echo "quoteBalanceBefore=$quote_balance_before"
|
||||
echo "baseAllowanceBefore=$base_allowance_before"
|
||||
echo "quoteAllowanceBefore=$quote_allowance_before"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
approve_base_tx=""
|
||||
approve_quote_tx=""
|
||||
|
||||
if (( base_allowance_before < BASE_AMOUNT )); then
|
||||
approve_base_output="$(
|
||||
cast send "$BASE_TOKEN" \
|
||||
'approve(address,uint256)(bool)' \
|
||||
"$INTEGRATION" "$BASE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
approve_base_tx="$(parse_tx_hash "$approve_base_output")"
|
||||
fi
|
||||
|
||||
if (( quote_allowance_before < QUOTE_AMOUNT )); then
|
||||
approve_quote_output="$(
|
||||
cast send "$QUOTE_TOKEN" \
|
||||
'approve(address,uint256)(bool)' \
|
||||
"$INTEGRATION" "$QUOTE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
approve_quote_tx="$(parse_tx_hash "$approve_quote_output")"
|
||||
fi
|
||||
|
||||
add_output="$(
|
||||
cast send "$INTEGRATION" \
|
||||
'addLiquidity(address,uint256,uint256)(uint256,uint256,uint256)' \
|
||||
"$POOL" "$BASE_AMOUNT" "$QUOTE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
add_liquidity_tx="$(parse_tx_hash "$add_output")"
|
||||
|
||||
reserves_after="$(cast call "$POOL" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL")"
|
||||
base_reserve_after="$(printf '%s\n' "$reserves_after" | sed -n '1p' | awk '{print $1}')"
|
||||
quote_reserve_after="$(printf '%s\n' "$reserves_after" | sed -n '2p' | awk '{print $1}')"
|
||||
base_balance_after="$(cast call "$BASE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
quote_balance_after="$(cast call "$QUOTE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
echo "pair=$PAIR_LABEL"
|
||||
echo "pool=$POOL"
|
||||
echo "baseToken=$BASE_TOKEN"
|
||||
echo "quoteToken=$QUOTE_TOKEN"
|
||||
echo "baseAmount=$BASE_AMOUNT"
|
||||
echo "quoteAmount=$QUOTE_AMOUNT"
|
||||
echo "approveBaseTx=${approve_base_tx:-none}"
|
||||
echo "approveQuoteTx=${approve_quote_tx:-none}"
|
||||
echo "addLiquidityTx=$add_liquidity_tx"
|
||||
echo "baseReserveBefore=$base_reserve_before"
|
||||
echo "quoteReserveBefore=$quote_reserve_before"
|
||||
echo "baseReserveAfter=$base_reserve_after"
|
||||
echo "quoteReserveAfter=$quote_reserve_after"
|
||||
echo "baseBalanceBefore=$base_balance_before"
|
||||
echo "baseBalanceAfter=$base_balance_after"
|
||||
echo "quoteBalanceBefore=$quote_balance_before"
|
||||
echo "quoteBalanceAfter=$quote_balance_after"
|
||||
128
scripts/deployment/add-mainnet-truu-pmm-fund-both-pools.sh
Executable file
128
scripts/deployment/add-mainnet-truu-pmm-fund-both-pools.sh
Executable file
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Add liquidity to **both** Mainnet volatile pools (cWUSDT/TRUU then cWUSDC/TRUU) in one run,
|
||||
# sharing one TRUU balance. Optionally keep a fraction of each pair's ratio-matched maximum
|
||||
# on the deployer for spot/arbitrage inventory (same idea as --reserve-bps on the single-pair top-up).
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/deployment/add-mainnet-truu-pmm-fund-both-pools.sh
|
||||
# bash scripts/deployment/add-mainnet-truu-pmm-fund-both-pools.sh --reserve-bps=2000 --dry-run
|
||||
#
|
||||
# Requires: load-env (ETHEREUM_MAINNET_RPC, PRIVATE_KEY, DODO_PMM_INTEGRATION_MAINNET, token addresses).
|
||||
# See: docs/03-deployment/MAINNET_PMM_TRUU_CWUSD_PEG_AND_BOT_RUNBOOK.md
|
||||
|
||||
# load-env.sh overwrites SCRIPT_DIR; keep this repo's deployment dir for sibling scripts.
|
||||
DEPLOYMENT_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${DEPLOYMENT_SCRIPT_DIR}/../.." && pwd)"
|
||||
source "${PROJECT_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1
|
||||
|
||||
RESERVE_BPS=0
|
||||
DRY_RUN=0
|
||||
B_REF=1500000
|
||||
Q_REF=372348929900893
|
||||
INITIAL_I="${PMM_TRUU_INITIAL_I:-2482326199339289065}"
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--reserve-bps=*) RESERVE_BPS="${arg#*=}" ;;
|
||||
--dry-run) DRY_RUN=1 ;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$RESERVE_BPS" -lt 0 || "$RESERVE_BPS" -ge 10000 ]]; then
|
||||
echo "[fail] --reserve-bps must be 0..9999" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
command -v cast >/dev/null 2>&1 || {
|
||||
echo "[fail] cast required" >&2
|
||||
exit 1
|
||||
}
|
||||
command -v python3 >/dev/null 2>&1 || {
|
||||
echo "[fail] python3 required" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-}"
|
||||
if [[ -z "$RPC_URL" || -z "$PRIVATE_KEY" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC and PRIVATE_KEY required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEPLOYER="$(cast wallet address --private-key "$PRIVATE_KEY")"
|
||||
CWUSDT="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
TRUU="${TRUU_MAINNET:-0xDAe0faFD65385E7775Cf75b1398735155EF6aCD2}"
|
||||
|
||||
scale_pair() {
|
||||
local cb="$1"
|
||||
local ct="$2"
|
||||
python3 - <<PY
|
||||
b_ref = $B_REF
|
||||
q_ref = $Q_REF
|
||||
reserve_bps = int("$RESERVE_BPS")
|
||||
cb = int("$cb")
|
||||
ct = int("$ct")
|
||||
if cb <= 0 or ct <= 0:
|
||||
print(0, 0)
|
||||
raise SystemExit(0)
|
||||
base_max_from_truu = ct * b_ref // q_ref
|
||||
base = min(cb, base_max_from_truu)
|
||||
quote = base * q_ref // b_ref
|
||||
if reserve_bps > 0:
|
||||
m = 10000 - reserve_bps
|
||||
base = base * m // 10000
|
||||
quote = quote * m // 10000
|
||||
print(base, quote)
|
||||
PY
|
||||
}
|
||||
|
||||
run_one() {
|
||||
local pair="$1"
|
||||
local b="$2"
|
||||
local q="$3"
|
||||
if [[ "$b" -le 0 || "$q" -le 0 ]]; then
|
||||
echo "[info] skip $pair (base=$b quote=$q)"
|
||||
return 0
|
||||
fi
|
||||
echo "[info] $pair base=$b quote=$q"
|
||||
bash "${DEPLOYMENT_SCRIPT_DIR}/deploy-mainnet-pmm-cw-truu-pool.sh" \
|
||||
--pair="$pair" \
|
||||
--initial-price="$INITIAL_I" \
|
||||
--base-amount="$b" \
|
||||
--quote-amount="$q" \
|
||||
$([[ "$DRY_RUN" -eq 1 ]] && echo --dry-run)
|
||||
}
|
||||
|
||||
CB="$(cast call "$CWUSDT" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
CC="$(cast call "$CWUSDC" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
CT="$(cast call "$TRUU" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
echo "[info] deployer=$DEPLOYER reserve_bps=$RESERVE_BPS dry_run=$DRY_RUN"
|
||||
echo "[info] balances cWUSDT=$CB cWUSDC=$CC TRUU=$CT"
|
||||
|
||||
read -r B1 Q1 <<<"$(scale_pair "$CB" "$CT")"
|
||||
CT2="$(python3 -c "print(int('$CT') - int('$Q1'))")"
|
||||
read -r B2 Q2 <<<"$(scale_pair "$CC" "$CT2")"
|
||||
|
||||
if [[ "$RESERVE_BPS" -gt 0 ]]; then
|
||||
echo "[info] planned adds after reserve: cwusdt-truu $B1 $Q1 | cwusdc-truu $B2 $Q2"
|
||||
fi
|
||||
|
||||
run_one cwusdt-truu "$B1" "$Q1"
|
||||
# Re-read cWUSDC balance if not dry-run (cWUSDT changed on-chain)
|
||||
if [[ "$DRY_RUN" -eq 0 ]]; then
|
||||
CC="$(cast call "$CWUSDC" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
CT="$(cast call "$TRUU" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
read -r B2 Q2 <<<"$(scale_pair "$CC" "$CT")"
|
||||
echo "[info] second leg after tx1: cWUSDC=$CC TRUU=$CT -> base=$B2 quote=$Q2"
|
||||
fi
|
||||
run_one cwusdc-truu "$B2" "$Q2"
|
||||
|
||||
echo "[ok] done."
|
||||
142
scripts/deployment/add-mainnet-truu-pmm-topup.sh
Executable file
142
scripts/deployment/add-mainnet-truu-pmm-topup.sh
Executable file
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Add liquidity to an existing Mainnet cWUSDT/TRUU or cWUSDC/TRUU DODO PMM pool using
|
||||
# **all wallet balance that fits** both legs at the USD reference ratio from the first
|
||||
# cWUSDT/TRUU seed (B_ref=1_500_000 raw cW, Q_ref=372_348_929_900_893 raw TRUU).
|
||||
#
|
||||
# Requires: same env as deploy-mainnet-pmm-cw-truu-pool.sh (ETHEREUM_MAINNET_RPC,
|
||||
# PRIVATE_KEY, DODO_PMM_INTEGRATION_MAINNET). Pool must already exist.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/deployment/add-mainnet-truu-pmm-topup.sh --pair=cwusdc-truu
|
||||
# bash scripts/deployment/add-mainnet-truu-pmm-topup.sh --pair=cwusdt-truu --dry-run
|
||||
# bash scripts/deployment/add-mainnet-truu-pmm-topup.sh --pair=cwusdt-truu --reserve-bps=2000
|
||||
# (keep 20% of the ratio-matched max on-wallet for trading; deploy 80%)
|
||||
#
|
||||
# Optional: PMM_TRUU_INITIAL_I (default matches live pools: 2482326199339289065).
|
||||
# If TRUU or cW balance is zero, exits 0 after printing a short message (nothing to do).
|
||||
#
|
||||
# See: docs/03-deployment/MAINNET_PMM_TRUU_CWUSD_PEG_AND_BOT_RUNBOOK.md
|
||||
|
||||
# load-env.sh overwrites SCRIPT_DIR; keep proxmox/scripts/deployment for sibling deploy script.
|
||||
DEPLOYMENT_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${DEPLOYMENT_SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
source "${PROJECT_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1
|
||||
|
||||
PAIR=""
|
||||
DRY_RUN=0
|
||||
RESERVE_BPS=0
|
||||
B_REF=1500000
|
||||
Q_REF=372348929900893
|
||||
INITIAL_I="${PMM_TRUU_INITIAL_I:-2482326199339289065}"
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--pair=*) PAIR="${arg#*=}" ;;
|
||||
--dry-run) DRY_RUN=1 ;;
|
||||
--reserve-bps=*) RESERVE_BPS="${arg#*=}" ;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$RESERVE_BPS" -lt 0 || "$RESERVE_BPS" -ge 10000 ]]; then
|
||||
echo "[fail] --reserve-bps must be 0..9999" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ -z "$PAIR" ]]; then
|
||||
echo "[fail] required: --pair=cwusdt-truu or --pair=cwusdc-truu" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
command -v cast >/dev/null 2>&1 || {
|
||||
echo "[fail] cast required" >&2
|
||||
exit 1
|
||||
}
|
||||
command -v python3 >/dev/null 2>&1 || {
|
||||
echo "[fail] python3 required" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-}"
|
||||
if [[ -z "$RPC_URL" || -z "$PRIVATE_KEY" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC and PRIVATE_KEY required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TRUU_MAINNET="${TRUU_MAINNET:-0xDAe0faFD65385E7775Cf75b1398735155EF6aCD2}"
|
||||
case "$PAIR" in
|
||||
cwusdt-truu)
|
||||
BASE_TOKEN="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
||||
;;
|
||||
cwusdc-truu)
|
||||
BASE_TOKEN="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
;;
|
||||
*)
|
||||
echo "[fail] unsupported pair: $PAIR" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
DEPLOYER="$(cast wallet address --private-key "$PRIVATE_KEY")"
|
||||
BASE_BAL="$(cast call "$BASE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
TRUU_BAL="$(cast call "$TRUU_MAINNET" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
read -r BASE_AMT QUOTE_AMT <<EOF
|
||||
$(python3 - <<PY
|
||||
b_ref = $B_REF
|
||||
q_ref = $Q_REF
|
||||
cb = int("$BASE_BAL")
|
||||
ct = int("$TRUU_BAL")
|
||||
if cb <= 0 or ct <= 0:
|
||||
print(0, 0)
|
||||
raise SystemExit(0)
|
||||
# Max base limited by TRUU: quote = base * q_ref / b_ref <= ct
|
||||
base_max_from_truu = ct * b_ref // q_ref
|
||||
base = min(cb, base_max_from_truu)
|
||||
quote = base * q_ref // b_ref
|
||||
print(base, quote)
|
||||
PY
|
||||
)
|
||||
EOF
|
||||
|
||||
if [[ -z "${BASE_AMT:-}" || -z "${QUOTE_AMT:-}" ]]; then
|
||||
echo "[fail] could not compute amounts" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$BASE_AMT" -eq 0 || "$QUOTE_AMT" -eq 0 ]]; then
|
||||
if [[ "$BASE_BAL" -eq 0 || "$TRUU_BAL" -eq 0 ]]; then
|
||||
echo "[info] nothing to add: cW balance=$BASE_BAL TRUU balance=$TRUU_BAL (need both > 0)"
|
||||
else
|
||||
echo "[info] nothing to add: TRUU balance=$TRUU_BAL is below one reference-ratio step of cW (need ≥$(( Q_REF / B_REF )) raw TRUU per 1 raw cW increment, 6-dec tokens; B_ref/Q_ref=$B_REF/$Q_REF; fund more TRUU or pass explicit amounts to deploy-mainnet-pmm-cw-truu-pool.sh)"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$RESERVE_BPS" -gt 0 ]]; then
|
||||
DEPLOY_MULT=$((10000 - RESERVE_BPS))
|
||||
BASE_AMT=$((BASE_AMT * DEPLOY_MULT / 10000))
|
||||
QUOTE_AMT=$((QUOTE_AMT * DEPLOY_MULT / 10000))
|
||||
echo "[info] applied --reserve-bps=$RESERVE_BPS (deploy $DEPLOY_MULT/10000 of ratio-matched max)"
|
||||
fi
|
||||
|
||||
if [[ "$BASE_AMT" -eq 0 || "$QUOTE_AMT" -eq 0 ]]; then
|
||||
echo "[info] nothing to add after reserve scaling (try smaller --reserve-bps)" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "[info] pair=$PAIR deployer=$DEPLOYER base_raw=$BASE_AMT quote_raw=$QUOTE_AMT (ratio B_ref/Q_ref=$B_REF/$Q_REF)"
|
||||
|
||||
exec bash "${DEPLOYMENT_SCRIPT_DIR}/deploy-mainnet-pmm-cw-truu-pool.sh" \
|
||||
--pair="$PAIR" \
|
||||
--initial-price="$INITIAL_I" \
|
||||
--base-amount="$BASE_AMT" \
|
||||
--quote-amount="$QUOTE_AMT" \
|
||||
$([[ "$DRY_RUN" -eq 1 ]] && echo --dry-run)
|
||||
170
scripts/deployment/apply-mainnet-cwusdc-usdc-peg-tranche-from-wallet.sh
Executable file
170
scripts/deployment/apply-mainnet-cwusdc-usdc-peg-tranche-from-wallet.sh
Executable file
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Apply one on-chain liquidity tranche toward Mainnet cWUSDC/USDC 1:1 vault peg
|
||||
# using the deployer wallet (PRIVATE_KEY). Wraps add-mainnet-public-dodo-cw-liquidity.sh.
|
||||
#
|
||||
# Requires: ETHEREUM_MAINNET_RPC, PRIVATE_KEY, DODO_PMM_INTEGRATION_MAINNET
|
||||
# Amounts are chosen like plan-mainnet-cwusdc-usdc-rebalance-liquidity.sh:
|
||||
# - prefer largest asymmetric peg tranche when base_reserve > quote_reserve
|
||||
# - else matched 1:1 deepen (min wallet cWUSDC, wallet USDC)
|
||||
#
|
||||
# Usage (from repo root):
|
||||
# source scripts/lib/load-project-env.sh
|
||||
# bash scripts/deployment/apply-mainnet-cwusdc-usdc-peg-tranche-from-wallet.sh --dry-run
|
||||
# bash scripts/deployment/apply-mainnet-cwusdc-usdc-peg-tranche-from-wallet.sh --apply
|
||||
#
|
||||
# Exit:
|
||||
# 0 — dry-run or live add submitted
|
||||
# 3 — wallet cannot fund any tranche (print funding hint)
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROJECT_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1 || true
|
||||
|
||||
MODE="dry-run"
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
--dry-run) MODE=dry-run ;;
|
||||
--apply) MODE=apply ;;
|
||||
--help|-h)
|
||||
sed -n '1,25p' "$0"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $a (use --dry-run or --apply)" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd cast
|
||||
require_cmd python3
|
||||
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-}"
|
||||
INTEGRATION="${DODO_PMM_INTEGRATION_MAINNET:-}"
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
USDC="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
POOL="${POOL_CWUSDC_USDC_MAINNET:-0x69776fc607e9edA8042e320e7e43f54d06c68f0E}"
|
||||
|
||||
if [[ -z "$RPC_URL" || -z "$PRIVATE_KEY" || -z "$INTEGRATION" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC, PRIVATE_KEY, and DODO_PMM_INTEGRATION_MAINNET are required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$INTEGRATION" ]]; then
|
||||
mapped_pool="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$CWUSDC" "$USDC" --rpc-url "$RPC_URL" 2>/dev/null | awk '{print $1}' || true)"
|
||||
if [[ -n "$mapped_pool" && "$mapped_pool" != "0x0000000000000000000000000000000000000000" && "${mapped_pool,,}" != "${POOL,,}" ]]; then
|
||||
POOL="$mapped_pool"
|
||||
fi
|
||||
fi
|
||||
|
||||
dep="$(cast wallet address --private-key "$PRIVATE_KEY")"
|
||||
out="$(cast call "$POOL" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL")"
|
||||
base_r="$(printf '%s\n' "$out" | sed -n '1p' | awk '{print $1}')"
|
||||
quote_r="$(printf '%s\n' "$out" | sed -n '2p' | awk '{print $1}')"
|
||||
wb="$(cast call "$CWUSDC" 'balanceOf(address)(uint256)' "$dep" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
wq="$(cast call "$USDC" 'balanceOf(address)(uint256)' "$dep" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
if (( base_r > quote_r )); then
|
||||
G=$(( base_r - quote_r ))
|
||||
else
|
||||
G=$(( quote_r - base_r ))
|
||||
fi
|
||||
|
||||
read -r opt_b opt_q <<<"$(
|
||||
python3 - "$wb" "$wq" "$base_r" "$quote_r" <<'PY'
|
||||
import sys
|
||||
wb, wq, br, qr = map(int, sys.argv[1:5])
|
||||
if br > qr:
|
||||
g = br - qr
|
||||
if g <= 0:
|
||||
print("0 0")
|
||||
else:
|
||||
cap = min(wb, wq - g) if wq > g else 0
|
||||
if cap < 1:
|
||||
print("0 0")
|
||||
else:
|
||||
print(cap, cap + g)
|
||||
else:
|
||||
g = qr - br
|
||||
if g <= 0:
|
||||
print("0 0")
|
||||
else:
|
||||
cap = min(wq, wb - g) if wb > g else 0
|
||||
if cap < 1:
|
||||
print("0 0")
|
||||
else:
|
||||
print(cap + g, cap)
|
||||
PY
|
||||
)"
|
||||
|
||||
BASE_AMOUNT=""
|
||||
QUOTE_AMOUNT=""
|
||||
|
||||
if (( opt_b >= 1 && opt_q >= 1 )); then
|
||||
BASE_AMOUNT="$opt_b"
|
||||
QUOTE_AMOUNT="$opt_q"
|
||||
else
|
||||
matched="$(python3 - "$wb" "$wq" <<'PY'
|
||||
import sys
|
||||
print(min(int(sys.argv[1]), int(sys.argv[2])))
|
||||
PY
|
||||
)"
|
||||
if (( matched >= 1 )); then
|
||||
BASE_AMOUNT="$matched"
|
||||
QUOTE_AMOUNT="$matched"
|
||||
fi
|
||||
fi
|
||||
|
||||
to_human() {
|
||||
python3 - "$1" <<'PY'
|
||||
import sys
|
||||
print(f"{int(sys.argv[1]) / 1_000_000:.6f}")
|
||||
PY
|
||||
}
|
||||
|
||||
echo "=== apply-mainnet-cwusdc-usdc-peg-tranche-from-wallet ($MODE) ==="
|
||||
echo "deployer=$dep"
|
||||
echo "pool=$POOL"
|
||||
echo "reserve_base=$(to_human "$base_r") cWUSDC reserve_quote=$(to_human "$quote_r") USDC"
|
||||
echo "wallet_cWUSDC=$(to_human "$wb") wallet_USDC=$(to_human "$wq")"
|
||||
|
||||
if [[ -z "$BASE_AMOUNT" || -z "$QUOTE_AMOUNT" ]]; then
|
||||
echo
|
||||
echo "[stop] No affordable tranche: need USDC on deployer for peg-close (asymmetric needs USDC_raw > gap when base-heavy)."
|
||||
echo " gap_raw=$G human=$(to_human "$G") USDC-side deficit vs cWUSDC reserve"
|
||||
echo " Send USDC (and keep cWUSDC) to: $dep"
|
||||
echo " Then: bash scripts/deployment/apply-mainnet-cwusdc-usdc-peg-tranche-from-wallet.sh --dry-run"
|
||||
echo " Flash path (no wallet USDC): see scripts/deployment/plan-mainnet-cwusdc-flash-quote-push-rebalance.sh"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
echo "chosen_base_amount_raw=$BASE_AMOUNT chosen_quote_amount_raw=$QUOTE_AMOUNT"
|
||||
echo "human_base=$(to_human "$BASE_AMOUNT") human_quote=$(to_human "$QUOTE_AMOUNT")"
|
||||
|
||||
ADD_ARGS=(
|
||||
"${PROJECT_ROOT}/scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh"
|
||||
"--pair=cwusdc-usdc"
|
||||
"--base-amount=$BASE_AMOUNT"
|
||||
"--quote-amount=$QUOTE_AMOUNT"
|
||||
)
|
||||
|
||||
if [[ "$MODE" == "dry-run" ]]; then
|
||||
ADD_ARGS+=("--dry-run")
|
||||
else
|
||||
ADD_ARGS+=("--execute")
|
||||
fi
|
||||
|
||||
exec bash "${ADD_ARGS[@]}"
|
||||
@@ -95,9 +95,10 @@ log "upserted IT_READ_API_URL and IT_READ_API_KEY in repo .env (gitignored)"
|
||||
|
||||
log "rsync minimal repo tree → root@${PROXMOX_HOST}:${REMOTE_ROOT}"
|
||||
ssh "${SSH_OPTS[@]}" "root@${PROXMOX_HOST}" \
|
||||
"mkdir -p '${REMOTE_ROOT}/config' '${REMOTE_ROOT}/scripts/it-ops' '${REMOTE_ROOT}/services/sankofa-it-read-api' '${REMOTE_ROOT}/docs/04-configuration' '${REMOTE_ROOT}/reports/status'"
|
||||
"mkdir -p '${REMOTE_ROOT}/config' '${REMOTE_ROOT}/config/it-operations' '${REMOTE_ROOT}/scripts/it-ops' '${REMOTE_ROOT}/services/sankofa-it-read-api' '${REMOTE_ROOT}/docs/04-configuration' '${REMOTE_ROOT}/reports/status'"
|
||||
cd "$PROJECT_ROOT"
|
||||
rsync -az --delete -e "$RSYNC_SSH" ./config/ip-addresses.conf "root@${PROXMOX_HOST}:${REMOTE_ROOT}/config/"
|
||||
rsync -az --delete -e "$RSYNC_SSH" ./config/it-operations/ "root@${PROXMOX_HOST}:${REMOTE_ROOT}/config/it-operations/"
|
||||
rsync -az --delete -e "$RSYNC_SSH" ./scripts/it-ops/ "root@${PROXMOX_HOST}:${REMOTE_ROOT}/scripts/it-ops/"
|
||||
rsync -az --delete -e "$RSYNC_SSH" ./services/sankofa-it-read-api/server.py "root@${PROXMOX_HOST}:${REMOTE_ROOT}/services/sankofa-it-read-api/"
|
||||
rsync -az --delete -e "$RSYNC_SSH" ./docs/04-configuration/ALL_VMIDS_ENDPOINTS.md "root@${PROXMOX_HOST}:${REMOTE_ROOT}/docs/04-configuration/"
|
||||
|
||||
262
scripts/deployment/cancel-chain138-eoa-nonce-range.sh
Executable file
262
scripts/deployment/cancel-chain138-eoa-nonce-range.sh
Executable file
@@ -0,0 +1,262 @@
|
||||
#!/usr/bin/env bash
|
||||
# Cancel a queued nonce range for a Chain 138 EOA by sending 0-value self-transfers
|
||||
# at each nonce with a bumped gas price. Default mode is dry-run.
|
||||
#
|
||||
# Example:
|
||||
# PRIVATE_KEY=0xabc... bash scripts/deployment/cancel-chain138-eoa-nonce-range.sh \
|
||||
# --rpc http://192.168.11.217:8545 \
|
||||
# --from-nonce 1
|
||||
#
|
||||
# PRIVATE_KEY=0xabc... bash scripts/deployment/cancel-chain138-eoa-nonce-range.sh \
|
||||
# --rpc http://192.168.11.217:8545 \
|
||||
# --from-nonce 1 \
|
||||
# --to-nonce 8 \
|
||||
# --apply
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
if [[ -f "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
RPC_URL="${RPC_URL_138:-http://192.168.11.211:8545}"
|
||||
PRIVATE_KEY_INPUT="${PRIVATE_KEY:-${DEPLOYER_PRIVATE_KEY:-}}"
|
||||
FROM_NONCE=""
|
||||
TO_NONCE=""
|
||||
APPLY=0
|
||||
GAS_LIMIT=21000
|
||||
GAS_BUMP_MULTIPLIER=2
|
||||
MIN_GAS_WEI=2000
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: cancel-chain138-eoa-nonce-range.sh [options]
|
||||
|
||||
Options:
|
||||
--private-key <hex> Private key for the EOA owner.
|
||||
--rpc <url> Chain 138 RPC URL. Default: RPC_URL_138 or http://192.168.11.211:8545
|
||||
--from-nonce <n> First nonce to cancel. Required.
|
||||
--to-nonce <n> Last nonce to cancel. Optional; defaults to highest pending owner nonce seen on the RPC.
|
||||
--apply Actually send cancel/self-transfer txs.
|
||||
--min-gas-wei <n> Floor gas price in wei. Default: 2000
|
||||
--gas-multiplier <n> Multiply current eth_gasPrice by this factor. Default: 2
|
||||
--help Show this help.
|
||||
|
||||
Behavior:
|
||||
1. Derives the owner address from the provided private key.
|
||||
2. Reads latest nonce and owner pending txs from txpool_besuPendingTransactions.
|
||||
3. Plans a cancel range from --from-nonce to --to-nonce.
|
||||
4. In --apply mode, sends 0-value self-transfers at each nonce in that range.
|
||||
|
||||
Notes:
|
||||
- Dry-run is the default and is recommended first.
|
||||
- Use this when the owner wants to invalidate old deployment attempts instead of completing them.
|
||||
- Requires: cast, curl, jq
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--private-key)
|
||||
PRIVATE_KEY_INPUT="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--rpc)
|
||||
RPC_URL="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--from-nonce)
|
||||
FROM_NONCE="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--to-nonce)
|
||||
TO_NONCE="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--apply)
|
||||
APPLY=1
|
||||
shift
|
||||
;;
|
||||
--min-gas-wei)
|
||||
MIN_GAS_WEI="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--gas-multiplier)
|
||||
GAS_BUMP_MULTIPLIER="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--help|-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
for cmd in cast curl jq; do
|
||||
command -v "$cmd" >/dev/null 2>&1 || {
|
||||
echo "Missing required command: $cmd" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
if [[ -z "$PRIVATE_KEY_INPUT" ]]; then
|
||||
echo "Missing private key. Pass --private-key or set PRIVATE_KEY." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$FROM_NONCE" ]]; then
|
||||
echo "Missing required --from-nonce." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$FROM_NONCE" in
|
||||
''|*[!0-9]*)
|
||||
echo "--from-nonce must be a non-negative integer." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -n "$TO_NONCE" ]]; then
|
||||
case "$TO_NONCE" in
|
||||
''|*[!0-9]*)
|
||||
echo "--to-nonce must be a non-negative integer." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
OWNER_ADDRESS="$(cast wallet address --private-key "$PRIVATE_KEY_INPUT" 2>/dev/null || true)"
|
||||
if [[ -z "$OWNER_ADDRESS" ]]; then
|
||||
echo "Could not derive owner address from private key." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OWNER_ADDRESS_LC="$(printf '%s' "$OWNER_ADDRESS" | tr '[:upper:]' '[:lower:]')"
|
||||
|
||||
rpc_call() {
|
||||
local method="$1"
|
||||
local params_json="$2"
|
||||
curl -fsS "$RPC_URL" \
|
||||
-H 'content-type: application/json' \
|
||||
--data "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"${method}\",\"params\":${params_json}}"
|
||||
}
|
||||
|
||||
hex_to_dec() {
|
||||
cast --to-dec "$1"
|
||||
}
|
||||
|
||||
latest_hex="$(rpc_call eth_getTransactionCount "[\"${OWNER_ADDRESS}\",\"latest\"]" | jq -r '.result // "0x0"')"
|
||||
pending_hex="$(rpc_call eth_getTransactionCount "[\"${OWNER_ADDRESS}\",\"pending\"]" | jq -r '.result // "0x0"')"
|
||||
gas_hex="$(rpc_call eth_gasPrice "[]" | jq -r '.result // "0x0"')"
|
||||
latest_nonce="$(hex_to_dec "$latest_hex")"
|
||||
pending_nonce="$(hex_to_dec "$pending_hex")"
|
||||
network_gas_wei="$(hex_to_dec "$gas_hex")"
|
||||
|
||||
pending_json="$(rpc_call txpool_besuPendingTransactions "[]")"
|
||||
owner_pending_json="$(
|
||||
printf '%s' "$pending_json" | jq --arg owner "$OWNER_ADDRESS_LC" '
|
||||
[
|
||||
.result[]?
|
||||
| select(((.from // .sender // "") | ascii_downcase) == $owner)
|
||||
| {
|
||||
hash,
|
||||
nonce_hex: .nonce,
|
||||
to,
|
||||
gas,
|
||||
gasPrice,
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
type
|
||||
}
|
||||
]
|
||||
'
|
||||
)"
|
||||
|
||||
owner_pending_count="$(printf '%s' "$owner_pending_json" | jq 'length')"
|
||||
highest_pending_nonce="$latest_nonce"
|
||||
if [[ "$owner_pending_count" -gt 0 ]]; then
|
||||
highest_pending_nonce="$(
|
||||
printf '%s' "$owner_pending_json" \
|
||||
| jq -r '.[].nonce_hex' \
|
||||
| while read -r nonce_hex; do cast --to-dec "$nonce_hex"; done \
|
||||
| sort -n \
|
||||
| tail -n1
|
||||
)"
|
||||
fi
|
||||
|
||||
if [[ -z "$TO_NONCE" ]]; then
|
||||
TO_NONCE="$highest_pending_nonce"
|
||||
fi
|
||||
|
||||
if (( TO_NONCE < FROM_NONCE )); then
|
||||
echo "--to-nonce must be greater than or equal to --from-nonce." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
effective_gas_wei="$(( network_gas_wei * GAS_BUMP_MULTIPLIER ))"
|
||||
if (( effective_gas_wei < MIN_GAS_WEI )); then
|
||||
effective_gas_wei="$MIN_GAS_WEI"
|
||||
fi
|
||||
|
||||
echo "=== Chain 138 EOA nonce-range cancel ==="
|
||||
echo "Owner address: $OWNER_ADDRESS"
|
||||
echo "RPC URL: $RPC_URL"
|
||||
echo "Latest nonce: $latest_nonce"
|
||||
echo "Pending nonce view: $pending_nonce"
|
||||
echo "Pending tx count: $owner_pending_count"
|
||||
echo "Highest pending nonce:${highest_pending_nonce}"
|
||||
echo "Cancel from nonce: $FROM_NONCE"
|
||||
echo "Cancel to nonce: $TO_NONCE"
|
||||
echo "Network gas price: $network_gas_wei wei"
|
||||
echo "Planned gas price: $effective_gas_wei wei"
|
||||
echo ""
|
||||
|
||||
if [[ "$owner_pending_count" -gt 0 ]]; then
|
||||
echo "--- Pending txs for owner in txpool_besuPendingTransactions ---"
|
||||
printf '%s\n' "$owner_pending_json" | jq '.'
|
||||
echo ""
|
||||
else
|
||||
echo "No owner txs found in txpool_besuPendingTransactions."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "--- Planned cancel range ---"
|
||||
for ((nonce = FROM_NONCE; nonce <= TO_NONCE; nonce++)); do
|
||||
echo "nonce $nonce -> self-transfer cancel"
|
||||
done
|
||||
echo ""
|
||||
|
||||
if [[ "$APPLY" -ne 1 ]]; then
|
||||
echo "Dry-run only. Re-run with --apply to submit cancels for the full nonce range above."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "--- Applying cancels ---"
|
||||
for ((nonce = FROM_NONCE; nonce <= TO_NONCE; nonce++)); do
|
||||
echo "Sending cancel tx for nonce $nonce ..."
|
||||
cast send "$OWNER_ADDRESS" \
|
||||
--private-key "$PRIVATE_KEY_INPUT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--nonce "$nonce" \
|
||||
--value 0 \
|
||||
--gas-limit "$GAS_LIMIT" \
|
||||
--gas-price "$effective_gas_wei" \
|
||||
>/tmp/cancel-chain138-eoa-nonce-range.out 2>/tmp/cancel-chain138-eoa-nonce-range.err || {
|
||||
echo "Failed to send cancel for nonce $nonce" >&2
|
||||
cat /tmp/cancel-chain138-eoa-nonce-range.err >&2 || true
|
||||
exit 1
|
||||
}
|
||||
cat /tmp/cancel-chain138-eoa-nonce-range.out
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Submitted cancel transactions. Wait for inclusion, then rerun in dry-run mode to verify the queue is gone."
|
||||
@@ -6,7 +6,7 @@
|
||||
# RPC_URL_138=https://rpc-core.d-bis.org ./scripts/deployment/check-deployer-balance-chain138-and-funding-plan.sh
|
||||
# # 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), python3 (uint256-safe halving), jq (optional). RPC_URL_138 must be set and reachable.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -28,13 +28,19 @@ CUSDC="0xf22258f57794CC8E06237084b353Ab30fFfa640b"
|
||||
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"
|
||||
|
||||
get_balance() {
|
||||
local addr="$1"
|
||||
cast call "$addr" "balanceOf(address)(uint256)" "$DEPLOYER" --rpc-url "$RPC" 2>/dev/null || echo "0"
|
||||
# cast may append human-readable suffix like " [6.971e14]" — only the first field is valid for arithmetic
|
||||
cast call "$addr" "balanceOf(address)(uint256)" "$DEPLOYER" --rpc-url "$RPC" 2>/dev/null | awk '{print $1; exit}' || echo "0"
|
||||
}
|
||||
|
||||
# Integer halving for uint256-sized values (bash $(( )) overflows past ~2^63)
|
||||
idiv2() {
|
||||
python3 -c "print(int('$1') // 2)" 2>/dev/null || echo "0"
|
||||
}
|
||||
|
||||
get_decimals() {
|
||||
@@ -42,10 +48,10 @@ get_decimals() {
|
||||
cast call "$addr" "decimals()(uint8)" --rpc-url "$RPC" 2>/dev/null | cast --to-dec 2>/dev/null || echo "18"
|
||||
}
|
||||
|
||||
# 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 balance (strip any cast suffix)
|
||||
native_wei=$(cast balance "$DEPLOYER" --rpc-url "$RPC" 2>/dev/null | awk '{print $1; exit}' || echo "0")
|
||||
native_eth=$(python3 -c "print(f'{int(\"$native_wei\")/1e18:.6f}')" 2>/dev/null || echo "0")
|
||||
half_native_wei=$(idiv2 "$native_wei")
|
||||
|
||||
echo "============================================"
|
||||
echo "Deployer wallet — ChainID 138"
|
||||
@@ -63,7 +69,7 @@ HALF_WETH=0; HALF_WETH10=0; HALF_LINK=0; HALF_CUSDT=0; HALF_CUSDC=0
|
||||
for entry in "WETH:$WETH:18" "WETH10:$WETH10:18" "LINK:$LINK:18" "cUSDT:$CUSDT:6" "cUSDC:$CUSDC:6"; do
|
||||
sym="${entry%%:*}"; rest="${entry#*:}"; addr="${rest%%:*}"; dec="${rest#*:}"
|
||||
raw=$(get_balance "$addr")
|
||||
half=$((raw / 2))
|
||||
half=$(idiv2 "$raw")
|
||||
case "$sym" in
|
||||
WETH) RAW_WETH=$raw; HALF_WETH=$half ;;
|
||||
WETH10) RAW_WETH10=$raw; HALF_WETH10=$half ;;
|
||||
@@ -108,6 +114,6 @@ echo "# ADD_LIQUIDITY_CUSDTUSDT_BASE=$HALF_CUSDT ADD_LIQUIDITY_CUSDTUSDT_QUOTE=
|
||||
echo "# ADD_LIQUIDITY_CUSDCUSDC_BASE=$HALF_CUSDC ADD_LIQUIDITY_CUSDCUSDC_QUOTE=<deployer USDC balance / 2>"
|
||||
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): $(idiv2 "$half_native_wei") wei."
|
||||
echo " WETH/LINK: half amounts above can be reserved for other use or future pools."
|
||||
echo "============================================"
|
||||
|
||||
@@ -9,9 +9,18 @@ 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
|
||||
if [[ -f "$SMOM/scripts/lib/deployment/dotenv.sh" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$SMOM/scripts/lib/deployment/dotenv.sh"
|
||||
load_deployment_env --repo-root "$SMOM"
|
||||
else
|
||||
set -a
|
||||
source "$SMOM/.env"
|
||||
set +a
|
||||
if [[ -z "${PRIVATE_KEY:-}" && -n "${DEPLOYER_PRIVATE_KEY:-}" ]]; then
|
||||
export PRIVATE_KEY="$DEPLOYER_PRIVATE_KEY"
|
||||
fi
|
||||
fi
|
||||
|
||||
DEPLOYER=""
|
||||
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
||||
|
||||
159
scripts/deployment/complete-mainnet-flash-quote-push-path.sh
Normal file
159
scripts/deployment/complete-mainnet-flash-quote-push-path.sh
Normal file
@@ -0,0 +1,159 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# End-to-end Mainnet flash quote-push (cWUSDC/USDC) using:
|
||||
# - AaveQuotePushFlashReceiver (default: already deployed at 0x241cb416aaFC2654078b7E2376adED2bDeFbCBa2)
|
||||
# - TwoHopDodoIntegrationUnwinder (cWUSDC -> cWUSDT -> USDC on registered PMM pools)
|
||||
#
|
||||
# Why two-hop: no Uniswap V3 cwUSDC/USDC pool; single-hop cwUSDC/USDC PMM is the illiquid venue you are fixing.
|
||||
#
|
||||
# Usage (repo root, env loaded):
|
||||
# source scripts/lib/load-project-env.sh
|
||||
# bash scripts/deployment/complete-mainnet-flash-quote-push-path.sh --dry-run
|
||||
# bash scripts/deployment/complete-mainnet-flash-quote-push-path.sh --apply
|
||||
#
|
||||
# Env overrides (optional):
|
||||
# AAVE_QUOTE_PUSH_RECEIVER_MAINNET (default canonical broadcast below)
|
||||
# FLASH_QUOTE_AMOUNT_RAW default 200000 (0.2 USDC; keep small vs thin cWUSDT/USDC pool)
|
||||
# MIN_OUT_PMM / MIN_OUT_UNWIND set explicitly if forge simulation reverts on PMM leg
|
||||
# UNWIND_MIN_MID_OUT_RAW default 1
|
||||
#
|
||||
# References: cross-chain-pmm-lps/config/deployment-status.json chains."1".pmmPools
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROXMOX_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
SMOM="${PROXMOX_ROOT}/smom-dbis-138"
|
||||
|
||||
# Preserve explicit caller overrides across sourced repo env files.
|
||||
_qp_private_key="${PRIVATE_KEY-}"
|
||||
_qp_rpc="${ETHEREUM_MAINNET_RPC-}"
|
||||
_qp_receiver="${AAVE_QUOTE_PUSH_RECEIVER_MAINNET-}"
|
||||
_qp_amount="${FLASH_QUOTE_AMOUNT_RAW-}"
|
||||
_qp_integration="${DODO_PMM_INTEGRATION_MAINNET-}"
|
||||
_qp_pool="${POOL_CWUSDC_USDC_MAINNET-}"
|
||||
_qp_pool_a="${UNWIND_TWO_HOP_POOL_A-}"
|
||||
_qp_pool_b="${UNWIND_TWO_HOP_POOL_B-}"
|
||||
_qp_mid_token="${UNWIND_TWO_HOP_MID_TOKEN-}"
|
||||
_qp_min_mid_out="${UNWIND_MIN_MID_OUT_RAW-}"
|
||||
_qp_min_out_pmm="${MIN_OUT_PMM-}"
|
||||
_qp_min_out_unwind="${MIN_OUT_UNWIND-}"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROXMOX_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
# shellcheck disable=SC1091
|
||||
source "${SMOM}/scripts/load-env.sh" >/dev/null 2>&1 || true
|
||||
|
||||
[[ -n "$_qp_private_key" ]] && export PRIVATE_KEY="$_qp_private_key"
|
||||
[[ -n "$_qp_rpc" ]] && export ETHEREUM_MAINNET_RPC="$_qp_rpc"
|
||||
[[ -n "$_qp_receiver" ]] && export AAVE_QUOTE_PUSH_RECEIVER_MAINNET="$_qp_receiver"
|
||||
[[ -n "$_qp_amount" ]] && export FLASH_QUOTE_AMOUNT_RAW="$_qp_amount"
|
||||
[[ -n "$_qp_integration" ]] && export DODO_PMM_INTEGRATION_MAINNET="$_qp_integration"
|
||||
[[ -n "$_qp_pool" ]] && export POOL_CWUSDC_USDC_MAINNET="$_qp_pool"
|
||||
[[ -n "$_qp_pool_a" ]] && export UNWIND_TWO_HOP_POOL_A="$_qp_pool_a"
|
||||
[[ -n "$_qp_pool_b" ]] && export UNWIND_TWO_HOP_POOL_B="$_qp_pool_b"
|
||||
[[ -n "$_qp_mid_token" ]] && export UNWIND_TWO_HOP_MID_TOKEN="$_qp_mid_token"
|
||||
[[ -n "$_qp_min_mid_out" ]] && export UNWIND_MIN_MID_OUT_RAW="$_qp_min_mid_out"
|
||||
[[ -n "$_qp_min_out_pmm" ]] && export MIN_OUT_PMM="$_qp_min_out_pmm"
|
||||
[[ -n "$_qp_min_out_unwind" ]] && export MIN_OUT_UNWIND="$_qp_min_out_unwind"
|
||||
|
||||
unset _qp_private_key _qp_rpc _qp_receiver _qp_amount _qp_integration _qp_pool
|
||||
unset _qp_pool_a _qp_pool_b _qp_mid_token _qp_min_mid_out _qp_min_out_pmm _qp_min_out_unwind
|
||||
|
||||
MODE="dry-run"
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
--apply) MODE=apply ;;
|
||||
--dry-run) MODE=dry-run ;;
|
||||
--help|-h)
|
||||
sed -n '1,35p' "$0"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $a (use --dry-run or --apply)" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd jq
|
||||
|
||||
if [[ -z "${ETHEREUM_MAINNET_RPC:-}" || -z "${PRIVATE_KEY:-}" || -z "${DODO_PMM_INTEGRATION_MAINNET:-}" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC, PRIVATE_KEY, DODO_PMM_INTEGRATION_MAINNET required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export AAVE_QUOTE_PUSH_RECEIVER_MAINNET="${AAVE_QUOTE_PUSH_RECEIVER_MAINNET:-0x241cb416aaFC2654078b7E2376adED2bDeFbCBa2}"
|
||||
if [[ -z "$_qp_pool" ]]; then
|
||||
export POOL_CWUSDC_USDC_MAINNET="0x69776fc607e9edA8042e320e7e43f54d06c68f0E"
|
||||
fi
|
||||
|
||||
# Canonical Mainnet public PMM pools (deployment-status.json)
|
||||
export UNWIND_MODE=4
|
||||
export UNWIND_TWO_HOP_POOL_A="${UNWIND_TWO_HOP_POOL_A:-0xe944b7Cb012A0820c07f54D51e92f0e1C74168DB}"
|
||||
export UNWIND_TWO_HOP_POOL_B="${UNWIND_TWO_HOP_POOL_B:-0x27f3aE7EE71Be3d77bAf17d4435cF8B895DD25D2}"
|
||||
export UNWIND_TWO_HOP_MID_TOKEN="${UNWIND_TWO_HOP_MID_TOKEN:-0xaF5017d0163ecb99d9B5D94e3b4D7b09Af44D8AE}"
|
||||
export UNWIND_MIN_MID_OUT_RAW="${UNWIND_MIN_MID_OUT_RAW:-1}"
|
||||
export FLASH_QUOTE_AMOUNT_RAW="${FLASH_QUOTE_AMOUNT_RAW:-200000}"
|
||||
|
||||
# PMM pool querySellQuote often reverts off-chain for this venue; operator should set MIN_OUT_PMM explicitly for production.
|
||||
if [[ -z "${MIN_OUT_PMM:-}" ]]; then
|
||||
export MIN_OUT_PMM=1
|
||||
echo "[warn] MIN_OUT_PMM unset — using 1 (unsafe). Set MIN_OUT_PMM for real slippage protection." >&2
|
||||
fi
|
||||
|
||||
DEPLOY_ARGS=()
|
||||
if [[ "$MODE" == "apply" ]]; then
|
||||
DEPLOY_ARGS=(--apply)
|
||||
else
|
||||
DEPLOY_ARGS=(--dry-run)
|
||||
fi
|
||||
|
||||
echo "=== 1/2 Deploy TwoHopDodoIntegrationUnwinder (skip Aave receiver redeploy) ==="
|
||||
DEPLOY_MAINNET_FLASH_SKIP_RECEIVER=1 QUOTE_PUSH_UNWINDER_TYPE=two_hop_dodo \
|
||||
bash "${PROXMOX_ROOT}/scripts/deployment/deploy-mainnet-aave-quote-push-stack.sh" "${DEPLOY_ARGS[@]}"
|
||||
|
||||
pick_twohop_addr() {
|
||||
local j1="${SMOM}/broadcast/DeployTwoHopDodoIntegrationUnwinder.s.sol/1/run-latest.json"
|
||||
if [[ ! -f "$j1" ]]; then
|
||||
echo "[fail] could not find a broadcasted TwoHop deployment under smom-dbis-138/broadcast/ (forge dry-run artifacts are not reusable across scripts)" >&2
|
||||
exit 1
|
||||
fi
|
||||
jq -r '.transactions[]? | select(.transactionType == "CREATE" and .contractName == "TwoHopDodoIntegrationUnwinder") | .contractAddress' "$j1" | tail -n1
|
||||
}
|
||||
|
||||
export QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET="${QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET:-$(pick_twohop_addr)}"
|
||||
if [[ -z "$QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET" || "$QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET" == "null" ]]; then
|
||||
echo "[fail] could not parse TwoHopDodoIntegrationUnwinder address from broadcast json" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "AAVE_QUOTE_PUSH_RECEIVER_MAINNET=$AAVE_QUOTE_PUSH_RECEIVER_MAINNET"
|
||||
echo "QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET=$QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET"
|
||||
echo "FLASH_QUOTE_AMOUNT_RAW=$FLASH_QUOTE_AMOUNT_RAW UNWIND_MODE=$UNWIND_MODE"
|
||||
|
||||
echo "=== 2/2 Run one flash quote-push ==="
|
||||
RUN_ARGS=()
|
||||
if [[ "$MODE" == "apply" ]]; then
|
||||
RUN_ARGS=(--apply)
|
||||
else
|
||||
RUN_ARGS=(--dry-run)
|
||||
fi
|
||||
|
||||
bash "${PROXMOX_ROOT}/scripts/deployment/run-mainnet-aave-cwusdc-quote-push-once.sh" "${RUN_ARGS[@]}"
|
||||
|
||||
echo
|
||||
echo "Done. Append to operator .env (do not commit secrets):"
|
||||
echo " AAVE_QUOTE_PUSH_RECEIVER_MAINNET=$AAVE_QUOTE_PUSH_RECEIVER_MAINNET"
|
||||
echo " QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET=$QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET"
|
||||
echo " UNWIND_MODE=4"
|
||||
echo " UNWIND_TWO_HOP_POOL_A=$UNWIND_TWO_HOP_POOL_A"
|
||||
echo " UNWIND_TWO_HOP_POOL_B=$UNWIND_TWO_HOP_POOL_B"
|
||||
echo " UNWIND_TWO_HOP_MID_TOKEN=$UNWIND_TWO_HOP_MID_TOKEN"
|
||||
echo "Then: bash scripts/verify/check-mainnet-cwusdc-usdc-reserve-peg.sh"
|
||||
135
scripts/deployment/compute-mainnet-cwusdt-cwusdc-seed-amounts.sh
Executable file
135
scripts/deployment/compute-mainnet-cwusdt-cwusdc-seed-amounts.sh
Executable file
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Suggest matched 1:1 seed raw amounts (6 decimals) for a new Mainnet cWUSDT/cWUSDC PMM pool
|
||||
# by scaling the smaller of the two reference rails' matched depth:
|
||||
# - cWUSDT/USDT
|
||||
# - cWUSDC/USDC
|
||||
#
|
||||
# This is a planning helper only. Review wallet balances before executing deploy/add-liquidity.
|
||||
#
|
||||
# Usage:
|
||||
# source scripts/lib/load-project-env.sh
|
||||
# bash scripts/deployment/compute-mainnet-cwusdt-cwusdc-seed-amounts.sh --multiplier=50
|
||||
# bash scripts/deployment/compute-mainnet-cwusdt-cwusdc-seed-amounts.sh --multiplier=50 --cap-raw=500000000000
|
||||
#
|
||||
# Env overrides:
|
||||
# POOL_CWUSDT_USDT_MAINNET, POOL_CWUSDC_USDC_MAINNET
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd cast
|
||||
require_cmd python3
|
||||
|
||||
MULTIPLIER="50"
|
||||
CAP_RAW=""
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--multiplier=*) MULTIPLIER="${arg#*=}" ;;
|
||||
--cap-raw=*) CAP_RAW="${arg#*=}" ;;
|
||||
-h | --help)
|
||||
sed -n '1,22p' "$0"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
if [[ -z "$RPC_URL" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
POOL_UT="${POOL_CWUSDT_USDT_MAINNET:-0x79156F6B7bf71a1B72D78189B540A89A6C13F6FC}"
|
||||
POOL_UC="${POOL_CWUSDC_USDC_MAINNET:-0x69776fc607e9edA8042e320e7e43f54d06c68f0E}"
|
||||
|
||||
read_reserves() {
|
||||
local pool="$1"
|
||||
cast call "$pool" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL"
|
||||
}
|
||||
|
||||
min_side() {
|
||||
python3 - "$1" "$2" <<'PY'
|
||||
import sys
|
||||
b, q = int(sys.argv[1]), int(sys.argv[2])
|
||||
print(min(b, q))
|
||||
PY
|
||||
}
|
||||
|
||||
line1_ut="$(read_reserves "$POOL_UT" | sed -n '1p' | awk '{print $1}')"
|
||||
line2_ut="$(read_reserves "$POOL_UT" | sed -n '2p' | awk '{print $1}')"
|
||||
line1_uc="$(read_reserves "$POOL_UC" | sed -n '1p' | awk '{print $1}')"
|
||||
line2_uc="$(read_reserves "$POOL_UC" | sed -n '2p' | awk '{print $1}')"
|
||||
|
||||
min_ut="$(min_side "$line1_ut" "$line2_ut")"
|
||||
min_uc="$(min_side "$line1_uc" "$line2_uc")"
|
||||
|
||||
ref="$(python3 - "$min_ut" "$min_uc" <<'PY'
|
||||
import sys
|
||||
print(min(int(sys.argv[1]), int(sys.argv[2])))
|
||||
PY
|
||||
)"
|
||||
|
||||
seed="$(python3 - "$ref" "$MULTIPLIER" <<'PY'
|
||||
import sys
|
||||
r, m = int(sys.argv[1]), int(sys.argv[2])
|
||||
print(r * m)
|
||||
PY
|
||||
)"
|
||||
|
||||
if [[ -n "$CAP_RAW" ]]; then
|
||||
seed="$(python3 - "$seed" "$CAP_RAW" <<'PY'
|
||||
import sys
|
||||
s, c = int(sys.argv[1]), int(sys.argv[2])
|
||||
print(min(s, c))
|
||||
PY
|
||||
)"
|
||||
fi
|
||||
|
||||
echo "=== cWUSDT/cWUSDC matched seed suggestion (1:1 raw on both legs) ==="
|
||||
echo "referencePools: cWUSDT/USDT=$POOL_UT cWUSDC/USDC=$POOL_UC"
|
||||
echo "minSide_cWUSDT_USDT_raw=$min_ut"
|
||||
echo "minSide_cWUSDC_USDC_raw=$min_uc"
|
||||
echo "referenceMin_raw=$ref"
|
||||
echo "multiplier=$MULTIPLIER"
|
||||
echo "suggested_base_raw(cWUSDT)=$seed"
|
||||
echo "suggested_quote_raw(cWUSDC)=$seed"
|
||||
echo
|
||||
echo "Dry-run deploy + seed:"
|
||||
echo " bash scripts/deployment/deploy-mainnet-cwusdt-cwusdc-pool.sh \\"
|
||||
echo " --initial-price=1000000000000000000 \\"
|
||||
echo " --base-amount=$seed --quote-amount=$seed --dry-run"
|
||||
echo
|
||||
echo "Optional: deepen the two reference rails toward ~${MULTIPLIER}x their current min-side depth"
|
||||
echo "(only if you intend matched adds and pool state supports equal base/quote adds; verify live reserves first):"
|
||||
delta_ut="$(python3 - "$min_ut" "$MULTIPLIER" <<'PY'
|
||||
import sys
|
||||
m, k = int(sys.argv[1]), int(sys.argv[2])
|
||||
print(max(0, m * (k - 1)))
|
||||
PY
|
||||
)"
|
||||
delta_uc="$(python3 - "$min_uc" "$MULTIPLIER" <<'PY'
|
||||
import sys
|
||||
m, k = int(sys.argv[1]), int(sys.argv[2])
|
||||
print(max(0, m * (k - 1)))
|
||||
PY
|
||||
)"
|
||||
echo " suggested extra raw per side cWUSDT/USDT: base=$delta_ut quote=$delta_ut"
|
||||
echo " bash scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh --pair=cwusdt-usdt --base-amount=$delta_ut --quote-amount=$delta_ut --dry-run"
|
||||
echo " suggested extra raw per side cWUSDC/USDC: base=$delta_uc quote=$delta_uc"
|
||||
echo " bash scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh --pair=cwusdc-usdc --base-amount=$delta_uc --quote-amount=$delta_uc --dry-run"
|
||||
78
scripts/deployment/compute-mainnet-truu-liquidity-amounts.sh
Executable file
78
scripts/deployment/compute-mainnet-truu-liquidity-amounts.sh
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Print base_raw / quote_raw for Mainnet cW/TRUU PMM adds at 1:1 USD notional per leg
|
||||
# (same ratio as seeds: B_ref=1_500_000 cW raw, Q_ref=372_348_929_900_893 TRUU raw).
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/deployment/compute-mainnet-truu-liquidity-amounts.sh --usd-per-leg=125000
|
||||
# bash scripts/deployment/compute-mainnet-truu-liquidity-amounts.sh --usd-per-leg=50000 --print-cast=0
|
||||
#
|
||||
# After funding the deployer wallet, run (existing pool → addLiquidity only):
|
||||
# bash scripts/deployment/deploy-mainnet-pmm-cw-truu-pool.sh \
|
||||
# --pair=cwusdt-truu --initial-price=<I> --base-amount=<base_raw> --quote-amount=<quote_raw>
|
||||
#
|
||||
# See: docs/03-deployment/MAINNET_PMM_TRUU_CWUSD_PEG_AND_BOT_RUNBOOK.md section 11
|
||||
|
||||
USD_PER_LEG=""
|
||||
PRINT_CAST=1
|
||||
I_DEFAULT="${PMM_TRUU_INITIAL_I:-2482326199339289065}"
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--usd-per-leg=*) USD_PER_LEG="${arg#*=}" ;;
|
||||
--print-cast=*) PRINT_CAST="${arg#*=}" ;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$USD_PER_LEG" ]]; then
|
||||
echo "[fail] required: --usd-per-leg=N (USD notional on cW leg = TRUU leg at reference ratio)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
command -v python3 >/dev/null 2>&1 || {
|
||||
echo "[fail] python3 required" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
read -r BASE_RAW QUOTE_RAW <<EOF
|
||||
$(python3 - <<PY
|
||||
from decimal import Decimal
|
||||
usd = Decimal("$USD_PER_LEG")
|
||||
if usd <= 0:
|
||||
raise SystemExit("usd must be positive")
|
||||
base = int(usd * Decimal(10) ** 6)
|
||||
b_ref = 1_500_000
|
||||
q_ref = 372_348_929_900_893
|
||||
quote = base * q_ref // b_ref
|
||||
print(base, quote)
|
||||
PY
|
||||
)
|
||||
EOF
|
||||
|
||||
echo "=== Mainnet cW/TRUU add — 1:1 USD legs (reference ratio) ==="
|
||||
echo "usd_per_leg=$USD_PER_LEG"
|
||||
echo "base_raw (cWUSDT or cWUSDC, 6 dec)=$BASE_RAW"
|
||||
echo "quote_raw (TRUU, 10 dec)=$QUOTE_RAW"
|
||||
truu_full="$(python3 -c "print($QUOTE_RAW / 10**10)")"
|
||||
echo "TRUU full tokens ≈ $truu_full"
|
||||
echo "implied USD/TRUU if legs 1:1 ≈ $(python3 -c "print(float($USD_PER_LEG) / ($QUOTE_RAW / 10**10))")"
|
||||
echo
|
||||
echo "Both pools at this size need 2× cW (cWUSDT + cWUSDC) and 2× TRUU quote if funded to the same USD each."
|
||||
echo
|
||||
|
||||
if [[ "$PRINT_CAST" == "1" ]]; then
|
||||
echo "deploy-mainnet-pmm-cw-truu-pool.sh --pair=cwusdt-truu \\"
|
||||
echo " --initial-price=$I_DEFAULT \\"
|
||||
echo " --base-amount=$BASE_RAW \\"
|
||||
echo " --quote-amount=$QUOTE_RAW"
|
||||
echo
|
||||
echo "deploy-mainnet-pmm-cw-truu-pool.sh --pair=cwusdc-truu \\"
|
||||
echo " --initial-price=$I_DEFAULT \\"
|
||||
echo " --base-amount=$BASE_RAW \\"
|
||||
echo " --quote-amount=$QUOTE_RAW"
|
||||
fi
|
||||
74
scripts/deployment/compute-mainnet-truu-pmm-seed-amounts.sh
Executable file
74
scripts/deployment/compute-mainnet-truu-pmm-seed-amounts.sh
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Compute raw token amounts for a cWUSD* / TRUU PMM seed where legs are 1:1 in USD NOTIONAL
|
||||
# (same dollar value on each side at deposit). This is NOT 1:1 raw token count unless TRUU/USD = $1.
|
||||
#
|
||||
# Args (env or flags):
|
||||
# USD_NOTIONAL_PER_LEG — USD value to place on EACH side (default: 1000)
|
||||
# TRUU_USD_PRICE — USD price of one full TRUU token (10 decimals), e.g. 0.00004
|
||||
#
|
||||
# Output: base_amount (6-dec cW), quote_amount (10-dec TRUU) for deploy-mainnet-pmm-cw-truu-pool.sh
|
||||
#
|
||||
# Example:
|
||||
# USD_NOTIONAL_PER_LEG=5000 TRUU_USD_PRICE=0.00004 bash scripts/deployment/compute-mainnet-truu-pmm-seed-amounts.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
USD_NOTIONAL_PER_LEG="${USD_NOTIONAL_PER_LEG:-1000}"
|
||||
TRUU_USD_PRICE="${TRUU_USD_PRICE:-}"
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--usd-per-leg=*) USD_NOTIONAL_PER_LEG="${arg#*=}" ;;
|
||||
--truu-usd-price=*) TRUU_USD_PRICE="${arg#*=}" ;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $arg (use --usd-per-leg= N --truu-usd-price= P)" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$TRUU_USD_PRICE" ]]; then
|
||||
echo "[fail] TRUU_USD_PRICE required (USD per 1 full TRUU token), e.g. export TRUU_USD_PRICE=0.00004" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
command -v python3 >/dev/null 2>&1 || {
|
||||
echo "[fail] python3 required" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
read -r BASE_RAW QUOTE_RAW TRUU_FULL <<EOF
|
||||
$(python3 - <<PY
|
||||
from decimal import Decimal, getcontext
|
||||
getcontext().prec = 40
|
||||
usd = Decimal("$USD_NOTIONAL_PER_LEG")
|
||||
p = Decimal("$TRUU_USD_PRICE")
|
||||
if p <= 0:
|
||||
raise SystemExit("price must be positive")
|
||||
# cW leg: 6 decimals, 1 unit = 1 USD notionally
|
||||
base_raw = int((usd * Decimal(10) ** 6).quantize(Decimal("1")))
|
||||
# TRUU leg: 10 decimals; token count = usd / price
|
||||
truu_tokens = usd / p
|
||||
quote_raw = int((truu_tokens * Decimal(10) ** 10).quantize(Decimal("1")))
|
||||
print(base_raw, quote_raw, format(truu_tokens.normalize(), "f"))
|
||||
PY
|
||||
)
|
||||
EOF
|
||||
|
||||
echo "=== cW / TRUU seed (1:1 USD notional per leg) ==="
|
||||
echo "usd_per_leg=$USD_NOTIONAL_PER_LEG"
|
||||
echo "truu_usd_price=$TRUU_USD_PRICE"
|
||||
echo "cW_base_amount_raw (6 dec)=$BASE_RAW (~${USD_NOTIONAL_PER_LEG} USD of cW)"
|
||||
echo "truu_quote_amount_raw (10 dec)=$QUOTE_RAW (~${USD_NOTIONAL_PER_LEG} USD of TRUU ≈ $TRUU_FULL full tokens)"
|
||||
echo
|
||||
echo "deploy-mainnet-pmm-cw-truu-pool.sh \\"
|
||||
echo " --pair=cwusdt-truu # or --pair=cwusdc-truu \\"
|
||||
echo " --initial-price=<set from DODO fork> \\"
|
||||
echo " --base-amount=$BASE_RAW \\"
|
||||
echo " --quote-amount=$QUOTE_RAW \\"
|
||||
echo " --dry-run"
|
||||
echo
|
||||
echo "# After pools exist, ratio-matched top-up from wallet balances:"
|
||||
echo "# bash scripts/deployment/add-mainnet-truu-pmm-topup.sh --pair=cwusdt-truu"
|
||||
316
scripts/deployment/configure-cstar-cw-bridge-pair.sh
Executable file
316
scripts/deployment/configure-cstar-cw-bridge-pair.sh
Executable file
@@ -0,0 +1,316 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Wire one generic Chain 138 canonical c* asset to its cW* mirror on a public chain.
|
||||
# Dry-run by default; pass --execute to broadcast writes.
|
||||
#
|
||||
# Examples:
|
||||
# ./scripts/deployment/configure-cstar-cw-bridge-pair.sh --chain MAINNET --asset cUSDT
|
||||
# ./scripts/deployment/configure-cstar-cw-bridge-pair.sh --chain POLYGON --asset cWEURC --freeze --execute
|
||||
# ./scripts/deployment/configure-cstar-cw-bridge-pair.sh --chain AVALANCHE --asset cUSDC --max-outstanding 1000000000000 --freeze --execute
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
SMOM_ROOT="${PROJECT_ROOT}/smom-dbis-138"
|
||||
CONFIG_FILE="${PROJECT_ROOT}/config/token-mapping-multichain.json"
|
||||
|
||||
CHAIN=""
|
||||
ASSET=""
|
||||
MAX_OUTSTANDING=""
|
||||
FREEZE=false
|
||||
EXECUTE=false
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
./scripts/deployment/configure-cstar-cw-bridge-pair.sh --chain <CHAIN> --asset <c*> [options]
|
||||
|
||||
Required:
|
||||
--chain <CHAIN> MAINNET | CRONOS | BSC | POLYGON | GNOSIS | AVALANCHE | BASE | ARBITRUM | OPTIMISM | CELO | ALL
|
||||
--asset <c*> cUSDT | cUSDC | cAUSDT | cUSDW | cEURC | cEURT | cGBPC | cGBPT | cAUDC | cJPYC | cCHFC | cCADC | cXAUC | cXAUT
|
||||
|
||||
Options:
|
||||
--max-outstanding <raw> Set L1 maxOutstanding(canonical, selector, rawAmount)
|
||||
--freeze Freeze the L2 token pair and the 138 destination after wiring
|
||||
--execute Broadcast writes; default is dry-run
|
||||
--help Show this message
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--chain) CHAIN="${2:-}"; shift 2 ;;
|
||||
--asset) ASSET="${2:-}"; shift 2 ;;
|
||||
--max-outstanding) MAX_OUTSTANDING="${2:-}"; shift 2 ;;
|
||||
--freeze) FREEZE=true; shift ;;
|
||||
--execute) EXECUTE=true; shift ;;
|
||||
--help|-h) usage; exit 0 ;;
|
||||
*)
|
||||
echo "Unknown argument: $1" >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -n "$CHAIN" && -n "$ASSET" ]] || { usage >&2; exit 2; }
|
||||
|
||||
if [[ -f "${SMOM_ROOT}/scripts/lib/deployment/dotenv.sh" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "${SMOM_ROOT}/scripts/lib/deployment/dotenv.sh"
|
||||
load_deployment_env --repo-root "$SMOM_ROOT"
|
||||
else
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "${SMOM_ROOT}/.env"
|
||||
set +a
|
||||
fi
|
||||
|
||||
command -v cast >/dev/null 2>&1 || { echo "cast (foundry) required" >&2; exit 1; }
|
||||
command -v node >/dev/null 2>&1 || { echo "node required" >&2; exit 1; }
|
||||
|
||||
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY required in ${SMOM_ROOT}/.env" >&2; exit 1; }
|
||||
[[ -f "$CONFIG_FILE" ]] || { echo "Missing $CONFIG_FILE" >&2; exit 1; }
|
||||
|
||||
declare -A CHAIN_NAME_TO_ID=(
|
||||
[MAINNET]=1
|
||||
[CRONOS]=25
|
||||
[BSC]=56
|
||||
[POLYGON]=137
|
||||
[GNOSIS]=100
|
||||
[AVALANCHE]=43114
|
||||
[BASE]=8453
|
||||
[ARBITRUM]=42161
|
||||
[OPTIMISM]=10
|
||||
[CELO]=42220
|
||||
[ALL]=651940
|
||||
)
|
||||
|
||||
declare -A CHAIN_NAME_TO_SELECTOR_VAR=(
|
||||
[MAINNET]=ETH_MAINNET_SELECTOR
|
||||
[CRONOS]=CRONOS_SELECTOR
|
||||
[BSC]=BSC_SELECTOR
|
||||
[POLYGON]=POLYGON_SELECTOR
|
||||
[GNOSIS]=GNOSIS_SELECTOR
|
||||
[AVALANCHE]=AVALANCHE_SELECTOR
|
||||
[BASE]=BASE_SELECTOR
|
||||
[ARBITRUM]=ARBITRUM_SELECTOR
|
||||
[OPTIMISM]=OPTIMISM_SELECTOR
|
||||
[CELO]=CELO_SELECTOR
|
||||
)
|
||||
|
||||
declare -A ASSET_TO_MAPPING_KEY=(
|
||||
[cUSDT]=Compliant_USDT_cW
|
||||
[cUSDC]=Compliant_USDC_cW
|
||||
[cAUSDT]=Compliant_AUSDT_cW
|
||||
[cUSDW]=Compliant_USDW_cW
|
||||
[cEURC]=Compliant_EURC_cW
|
||||
[cEURT]=Compliant_EURT_cW
|
||||
[cGBPC]=Compliant_GBPC_cW
|
||||
[cGBPT]=Compliant_GBPT_cW
|
||||
[cAUDC]=Compliant_AUDC_cW
|
||||
[cJPYC]=Compliant_JPYC_cW
|
||||
[cCHFC]=Compliant_CHFC_cW
|
||||
[cCADC]=Compliant_CADC_cW
|
||||
[cXAUC]=Compliant_XAUC_cW
|
||||
[cXAUT]=Compliant_XAUT_cW
|
||||
)
|
||||
|
||||
declare -A ASSET_TO_CW_PREFIX=(
|
||||
[cUSDT]=CWUSDT
|
||||
[cUSDC]=CWUSDC
|
||||
[cAUSDT]=CWAUSDT
|
||||
[cUSDW]=CWUSDW
|
||||
[cEURC]=CWEURC
|
||||
[cEURT]=CWEURT
|
||||
[cGBPC]=CWGBPC
|
||||
[cGBPT]=CWGBPT
|
||||
[cAUDC]=CWAUDC
|
||||
[cJPYC]=CWJPYC
|
||||
[cCHFC]=CWCHFC
|
||||
[cCADC]=CWCADC
|
||||
[cXAUC]=CWXAUC
|
||||
[cXAUT]=CWXAUT
|
||||
)
|
||||
|
||||
chain_id="${CHAIN_NAME_TO_ID[$CHAIN]:-}"
|
||||
[[ -n "$chain_id" ]] || { echo "Unsupported chain: $CHAIN" >&2; exit 2; }
|
||||
|
||||
mapping_key="${ASSET_TO_MAPPING_KEY[$ASSET]:-}"
|
||||
cw_prefix="${ASSET_TO_CW_PREFIX[$ASSET]:-}"
|
||||
[[ -n "$mapping_key" && -n "$cw_prefix" ]] || { echo "Unsupported asset: $ASSET" >&2; exit 2; }
|
||||
|
||||
selector_var="${CHAIN_NAME_TO_SELECTOR_VAR[$CHAIN]:-}"
|
||||
selector="${selector_var:+${!selector_var:-}}"
|
||||
if [[ "$CHAIN" != "ALL" && -z "$selector" ]]; then
|
||||
echo "Missing selector env for $CHAIN ($selector_var)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
resolve_chain_rpc() {
|
||||
case "$1" in
|
||||
MAINNET) printf '%s' "${ETH_MAINNET_RPC_URL:-${ETHEREUM_MAINNET_RPC:-}}" ;;
|
||||
CRONOS) printf '%s' "${CRONOS_RPC_URL:-${CRONOS_RPC:-}}" ;;
|
||||
BSC) printf '%s' "${BSC_RPC_URL:-}" ;;
|
||||
POLYGON) printf '%s' "${POLYGON_MAINNET_RPC:-${POLYGON_RPC_URL:-}}" ;;
|
||||
GNOSIS) printf '%s' "${GNOSIS_RPC:-${GNOSIS_MAINNET_RPC:-}}" ;;
|
||||
AVALANCHE) printf '%s' "${AVALANCHE_RPC_URL:-}" ;;
|
||||
BASE) printf '%s' "${BASE_MAINNET_RPC:-}" ;;
|
||||
ARBITRUM) printf '%s' "${ARBITRUM_MAINNET_RPC:-${ARBITRUM_MAINNET_RPC_URL:-${ARBITRUM_RPC_URL:-}}}" ;;
|
||||
OPTIMISM) printf '%s' "${OPTIMISM_MAINNET_RPC:-}" ;;
|
||||
CELO) printf '%s' "${CELO_RPC:-${CELO_MAINNET_RPC:-}}" ;;
|
||||
ALL) printf '%s' "${ALL_MAINNET_RPC_URL:-${ALL_RPC_URL:-}}" ;;
|
||||
*) printf '' ;;
|
||||
esac
|
||||
}
|
||||
|
||||
CHAIN_RPC="$(resolve_chain_rpc "$CHAIN")"
|
||||
[[ -n "${RPC_URL_138:-}" ]] || { echo "RPC_URL_138 required" >&2; exit 1; }
|
||||
[[ -n "$CHAIN_RPC" ]] || { echo "Missing RPC for $CHAIN" >&2; exit 1; }
|
||||
|
||||
CHAIN138_L1_BRIDGE="${CW_L1_BRIDGE_CHAIN138:-${CHAIN138_L1_BRIDGE:-}}"
|
||||
[[ -n "$CHAIN138_L1_BRIDGE" ]] || { echo "Set CW_L1_BRIDGE_CHAIN138 (or CHAIN138_L1_BRIDGE) in .env" >&2; exit 1; }
|
||||
|
||||
CW_BRIDGE_VAR="CW_BRIDGE_${CHAIN}"
|
||||
CHAIN_L2_BRIDGE="${!CW_BRIDGE_VAR:-}"
|
||||
[[ -n "$CHAIN_L2_BRIDGE" ]] || { echo "Set ${CW_BRIDGE_VAR} in .env" >&2; exit 1; }
|
||||
|
||||
MIRRORED_ENV_VAR="${cw_prefix}_${CHAIN}"
|
||||
MIRRORED_ADDRESS_VAR="${cw_prefix}_ADDRESS_${chain_id}"
|
||||
CHAIN_MIRRORED_TOKEN="${!MIRRORED_ENV_VAR:-${!MIRRORED_ADDRESS_VAR:-}}"
|
||||
|
||||
map_info="$(
|
||||
node - <<'NODE' "$CONFIG_FILE" "$chain_id" "$mapping_key"
|
||||
const fs = require('fs');
|
||||
const [configFile, chainIdRaw, mappingKey] = process.argv.slice(2);
|
||||
const chainId = Number(chainIdRaw);
|
||||
const json = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
||||
const pair = (json.pairs || []).find((entry) => entry.fromChainId === 138 && entry.toChainId === chainId);
|
||||
if (!pair) {
|
||||
process.exit(10);
|
||||
}
|
||||
const token = (pair.tokens || []).find((entry) => entry.key === mappingKey);
|
||||
if (!token) {
|
||||
process.exit(11);
|
||||
}
|
||||
process.stdout.write([
|
||||
token.addressFrom || '',
|
||||
token.addressTo || '',
|
||||
token.name || '',
|
||||
pair.notes || ''
|
||||
].join('\n'));
|
||||
NODE
|
||||
)" || {
|
||||
echo "Failed to resolve $ASSET / $CHAIN from $CONFIG_FILE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
canonical_address="$(sed -n '1p' <<<"$map_info")"
|
||||
mapping_mirrored_address="$(sed -n '2p' <<<"$map_info")"
|
||||
mapping_name="$(sed -n '3p' <<<"$map_info")"
|
||||
|
||||
[[ -n "$canonical_address" && "$canonical_address" != "0x0000000000000000000000000000000000000000" ]] || {
|
||||
echo "Canonical address missing for $ASSET -> $CHAIN in $CONFIG_FILE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [[ -z "$CHAIN_MIRRORED_TOKEN" || "$CHAIN_MIRRORED_TOKEN" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
if [[ -n "$mapping_mirrored_address" && "$mapping_mirrored_address" != "0x0000000000000000000000000000000000000000" ]]; then
|
||||
CHAIN_MIRRORED_TOKEN="$mapping_mirrored_address"
|
||||
else
|
||||
echo "Mirrored token for $ASSET on $CHAIN is not configured in .env or $CONFIG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
send_cast() {
|
||||
local rpc="$1"
|
||||
local to="$2"
|
||||
local sig="$3"
|
||||
shift 3
|
||||
if $EXECUTE; then
|
||||
cast send "$to" "$sig" "$@" --rpc-url "$rpc" --private-key "$PRIVATE_KEY" --legacy
|
||||
else
|
||||
printf 'cast send %q %q' "$to" "$sig"
|
||||
for arg in "$@"; do
|
||||
printf ' %q' "$arg"
|
||||
done
|
||||
printf ' --rpc-url %q --private-key "$PRIVATE_KEY" --legacy\n' "$rpc"
|
||||
fi
|
||||
}
|
||||
|
||||
read_struct_field() {
|
||||
local value="$1"
|
||||
tr '\n' ' ' <<<"$value" | xargs 2>/dev/null || true
|
||||
}
|
||||
|
||||
pair_current="$(cast call "$CHAIN_L2_BRIDGE" "canonicalToMirrored(address)(address)" "$canonical_address" --rpc-url "$CHAIN_RPC" 2>/dev/null || true)"
|
||||
l1_dest_current="$(cast call "$CHAIN138_L1_BRIDGE" "destinations(address,uint64)((address,bool))" "$canonical_address" "${selector:-0}" --rpc-url "$RPC_URL_138" 2>/dev/null || true)"
|
||||
l2_dest_current="$(cast call "$CHAIN_L2_BRIDGE" "destinations(uint64)((address,bool))" 138 --rpc-url "$CHAIN_RPC" 2>/dev/null || true)"
|
||||
allowlisted_current="$(cast call "$CHAIN138_L1_BRIDGE" "supportedCanonicalToken(address)(bool)" "$canonical_address" --rpc-url "$RPC_URL_138" 2>/dev/null || true)"
|
||||
freeze_pair_current="$(cast call "$CHAIN_L2_BRIDGE" "tokenPairFrozen(address)(bool)" "$canonical_address" --rpc-url "$CHAIN_RPC" 2>/dev/null || true)"
|
||||
freeze_dest_current="$(cast call "$CHAIN_L2_BRIDGE" "destinationFrozen(uint64)(bool)" 138 --rpc-url "$CHAIN_RPC" 2>/dev/null || true)"
|
||||
|
||||
echo "=== Generic c* -> cW* bridge wiring ==="
|
||||
echo "Chain: $CHAIN (chainId $chain_id)"
|
||||
echo "Asset: $ASSET"
|
||||
echo "Mapping key: $mapping_key"
|
||||
echo "Lane: $mapping_name"
|
||||
echo "Canonical (138): $canonical_address"
|
||||
echo "Mirrored ($CHAIN): $CHAIN_MIRRORED_TOKEN"
|
||||
echo "L1 bridge: $CHAIN138_L1_BRIDGE"
|
||||
echo "L2 bridge: $CHAIN_L2_BRIDGE"
|
||||
if [[ -n "$selector" ]]; then
|
||||
echo "Chain selector: $selector"
|
||||
fi
|
||||
echo "Mode: $([[ "$EXECUTE" == "true" ]] && echo "execute" || echo "dry-run")"
|
||||
echo
|
||||
|
||||
echo "Current state:"
|
||||
echo " L2 canonicalToMirrored: $(read_struct_field "$pair_current")"
|
||||
echo " L1 destinations(): $(read_struct_field "$l1_dest_current")"
|
||||
echo " L2 destinations(138): $(read_struct_field "$l2_dest_current")"
|
||||
echo " L1 allowlisted: ${allowlisted_current:-unavailable}"
|
||||
if [[ -n "$MAX_OUTSTANDING" ]]; then
|
||||
current_max="$(cast call "$CHAIN138_L1_BRIDGE" "maxOutstanding(address,uint64)(uint256)" "$canonical_address" "${selector:-0}" --rpc-url "$RPC_URL_138" 2>/dev/null || true)"
|
||||
echo " L1 maxOutstanding: ${current_max:-unavailable}"
|
||||
fi
|
||||
echo " L2 tokenPairFrozen: ${freeze_pair_current:-unavailable}"
|
||||
echo " L2 destinationFrozen: ${freeze_dest_current:-unavailable}"
|
||||
echo
|
||||
|
||||
echo "Planned actions:"
|
||||
echo "1. Configure L2 token pair"
|
||||
send_cast "$CHAIN_RPC" "$CHAIN_L2_BRIDGE" "configureTokenPair(address,address)" "$canonical_address" "$CHAIN_MIRRORED_TOKEN"
|
||||
|
||||
if [[ "$CHAIN" != "ALL" ]]; then
|
||||
echo "2. Configure L1 destination to L2 bridge"
|
||||
send_cast "$RPC_URL_138" "$CHAIN138_L1_BRIDGE" "configureDestination(address,uint64,address,bool)" "$canonical_address" "$selector" "$CHAIN_L2_BRIDGE" true
|
||||
|
||||
echo "3. Configure L2 destination back to Chain 138"
|
||||
send_cast "$CHAIN_RPC" "$CHAIN_L2_BRIDGE" "configureDestination(uint64,address,bool)" 138 "$CHAIN138_L1_BRIDGE" true
|
||||
else
|
||||
echo "2. ALL Mainnet uses a non-CCIP adapter lane; no selector-driven CWMultiTokenBridge destination writes were generated."
|
||||
fi
|
||||
|
||||
echo "4. Allowlist canonical token on L1"
|
||||
send_cast "$RPC_URL_138" "$CHAIN138_L1_BRIDGE" "configureSupportedCanonicalToken(address,bool)" "$canonical_address" true
|
||||
|
||||
if [[ -n "$MAX_OUTSTANDING" && "$CHAIN" != "ALL" ]]; then
|
||||
echo "5. Set L1 maxOutstanding"
|
||||
send_cast "$RPC_URL_138" "$CHAIN138_L1_BRIDGE" "setMaxOutstanding(address,uint64,uint256)" "$canonical_address" "$selector" "$MAX_OUTSTANDING"
|
||||
fi
|
||||
|
||||
if $FREEZE && [[ "$CHAIN" != "ALL" ]]; then
|
||||
echo "6. Freeze L2 token pair"
|
||||
send_cast "$CHAIN_RPC" "$CHAIN_L2_BRIDGE" "freezeTokenPair(address)" "$canonical_address"
|
||||
|
||||
echo "7. Freeze L2 destination back to Chain 138"
|
||||
send_cast "$CHAIN_RPC" "$CHAIN_L2_BRIDGE" "freezeDestination(uint64)" 138
|
||||
fi
|
||||
|
||||
echo
|
||||
if ! $EXECUTE; then
|
||||
echo "Dry-run only. Re-run with --execute to broadcast."
|
||||
fi
|
||||
@@ -26,7 +26,7 @@ set +a
|
||||
|
||||
RPC="${RPC_URL_138:-http://192.168.11.211:8545}"
|
||||
GAS_PRICE="${GAS_PRICE_138:-${GAS_PRICE:-1000000000}}"
|
||||
export DODO_PMM_INTEGRATION="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-0x5BDc62f1ae7D630c37A8B363a1d49845356Ee72d}}"
|
||||
export DODO_PMM_INTEGRATION="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-0x86ADA6Ef91A3B450F89f2b751e93B1b7A3218895}}"
|
||||
export RPC_URL_138="$RPC"
|
||||
|
||||
cd "$SMOM"
|
||||
|
||||
27
scripts/deployment/deploy-chain138-pilot-dex-venues.sh
Normal file
27
scripts/deployment/deploy-chain138-pilot-dex-venues.sh
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
SMOM_DIR="${PROJECT_ROOT}/smom-dbis-138"
|
||||
|
||||
if [[ -f "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh"
|
||||
fi
|
||||
|
||||
RPC_URL="${RPC_URL_138:-${CHAIN138_RPC_URL:-http://192.168.11.211:8545}}"
|
||||
GAS_PRICE_WEI="${CHAIN138_DEPLOY_GAS_PRICE_WEI:-1000}"
|
||||
BROADCAST_ARGS=()
|
||||
|
||||
if [[ "${1:-}" == "--broadcast" ]]; then
|
||||
BROADCAST_ARGS+=(--broadcast)
|
||||
fi
|
||||
|
||||
cd "${SMOM_DIR}"
|
||||
forge script script/bridge/trustless/DeployChain138PilotDexVenues.s.sol:DeployChain138PilotDexVenues \
|
||||
--rpc-url "${RPC_URL}" \
|
||||
--legacy \
|
||||
--slow \
|
||||
--with-gas-price "${GAS_PRICE_WEI}" \
|
||||
"${BROADCAST_ARGS[@]}"
|
||||
108
scripts/deployment/deploy-chain138-uniswap-v3-upstream-native.sh
Executable file
108
scripts/deployment/deploy-chain138-uniswap-v3-upstream-native.sh
Executable file
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
CORE_REPO="${CHAIN138_UNISWAP_V3_NATIVE_CORE_REPO:-/home/intlc/projects/uniswap-v3-core}"
|
||||
PERIPHERY_REPO="${CHAIN138_UNISWAP_V3_NATIVE_PERIPHERY_REPO:-/home/intlc/projects/uniswap-v3-periphery}"
|
||||
RPC_URL="${CHAIN138_UNISWAP_V3_UPSTREAM_RPC_URL:-http://192.168.11.221:8545}"
|
||||
|
||||
if [[ -f "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh"
|
||||
fi
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd jq
|
||||
require_cmd npm
|
||||
require_cmd npx
|
||||
|
||||
if [[ -z "${PRIVATE_KEY:-}" ]]; then
|
||||
echo "[fail] PRIVATE_KEY is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "== Chain 138 upstream-native Uniswap v3 deployment =="
|
||||
echo "RPC: ${RPC_URL}"
|
||||
echo "Core repo: ${CORE_REPO}"
|
||||
echo "Periphery repo: ${PERIPHERY_REPO}"
|
||||
echo
|
||||
|
||||
echo "1. Installing dependencies..."
|
||||
(
|
||||
cd "${CORE_REPO}"
|
||||
npm install
|
||||
)
|
||||
(
|
||||
cd "${PERIPHERY_REPO}"
|
||||
npm install
|
||||
)
|
||||
|
||||
echo
|
||||
echo "2. Compiling..."
|
||||
(
|
||||
cd "${CORE_REPO}"
|
||||
npx hardhat compile
|
||||
)
|
||||
(
|
||||
cd "${PERIPHERY_REPO}"
|
||||
npx hardhat compile
|
||||
)
|
||||
|
||||
echo
|
||||
echo "3. Deploying factory..."
|
||||
(
|
||||
cd "${CORE_REPO}"
|
||||
npx hardhat run scripts/chain138/deploy-factory.ts --network chain138
|
||||
)
|
||||
|
||||
FACTORY_JSON="${CORE_REPO}/deployments/chain138/factory.json"
|
||||
export CHAIN138_UNISWAP_V3_NATIVE_FACTORY
|
||||
CHAIN138_UNISWAP_V3_NATIVE_FACTORY="$(jq -r '.factory' "${FACTORY_JSON}")"
|
||||
echo " factory=${CHAIN138_UNISWAP_V3_NATIVE_FACTORY}"
|
||||
|
||||
echo
|
||||
echo "4. Deploying periphery..."
|
||||
(
|
||||
cd "${PERIPHERY_REPO}"
|
||||
npx hardhat run scripts/chain138/deploy-periphery.ts --network chain138
|
||||
)
|
||||
|
||||
PERIPHERY_JSON="${PERIPHERY_REPO}/deployments/chain138/periphery.json"
|
||||
export CHAIN138_UNISWAP_V3_NATIVE_POSITION_MANAGER
|
||||
export CHAIN138_UNISWAP_V3_NATIVE_SWAP_ROUTER
|
||||
export CHAIN138_UNISWAP_V3_NATIVE_QUOTER_V2
|
||||
CHAIN138_UNISWAP_V3_NATIVE_POSITION_MANAGER="$(jq -r '.positionManager' "${PERIPHERY_JSON}")"
|
||||
CHAIN138_UNISWAP_V3_NATIVE_SWAP_ROUTER="$(jq -r '.swapRouter' "${PERIPHERY_JSON}")"
|
||||
CHAIN138_UNISWAP_V3_NATIVE_QUOTER_V2="$(jq -r '.quoterV2' "${PERIPHERY_JSON}")"
|
||||
echo " positionManager=${CHAIN138_UNISWAP_V3_NATIVE_POSITION_MANAGER}"
|
||||
echo " swapRouter=${CHAIN138_UNISWAP_V3_NATIVE_SWAP_ROUTER}"
|
||||
echo " quoterV2=${CHAIN138_UNISWAP_V3_NATIVE_QUOTER_V2}"
|
||||
|
||||
echo
|
||||
echo "5. Creating and seeding canonical pools..."
|
||||
(
|
||||
cd "${PERIPHERY_REPO}"
|
||||
npx hardhat run scripts/chain138/create-seed-pools.ts --network chain138
|
||||
)
|
||||
|
||||
POOLS_JSON="${PERIPHERY_REPO}/deployments/chain138/pools.json"
|
||||
export CHAIN138_UNISWAP_V3_NATIVE_WETH_USDT_POOL
|
||||
export CHAIN138_UNISWAP_V3_NATIVE_WETH_USDC_POOL
|
||||
CHAIN138_UNISWAP_V3_NATIVE_WETH_USDT_POOL="$(jq -r '.wethUsdt.pool' "${POOLS_JSON}")"
|
||||
CHAIN138_UNISWAP_V3_NATIVE_WETH_USDC_POOL="$(jq -r '.wethUsdc.pool' "${POOLS_JSON}")"
|
||||
|
||||
echo
|
||||
echo "== deployed addresses =="
|
||||
echo "CHAIN138_UNISWAP_V3_NATIVE_FACTORY=${CHAIN138_UNISWAP_V3_NATIVE_FACTORY}"
|
||||
echo "CHAIN138_UNISWAP_V3_NATIVE_POSITION_MANAGER=${CHAIN138_UNISWAP_V3_NATIVE_POSITION_MANAGER}"
|
||||
echo "CHAIN138_UNISWAP_V3_NATIVE_SWAP_ROUTER=${CHAIN138_UNISWAP_V3_NATIVE_SWAP_ROUTER}"
|
||||
echo "CHAIN138_UNISWAP_V3_NATIVE_QUOTER_V2=${CHAIN138_UNISWAP_V3_NATIVE_QUOTER_V2}"
|
||||
echo "CHAIN138_UNISWAP_V3_NATIVE_WETH_USDT_POOL=${CHAIN138_UNISWAP_V3_NATIVE_WETH_USDT_POOL}"
|
||||
echo "CHAIN138_UNISWAP_V3_NATIVE_WETH_USDC_POOL=${CHAIN138_UNISWAP_V3_NATIVE_WETH_USDC_POOL}"
|
||||
76
scripts/deployment/deploy-cross-chain-flash-infra-chain138.sh
Executable file
76
scripts/deployment/deploy-cross-chain-flash-infra-chain138.sh
Executable file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deploy Chain 138 cross-chain flash infrastructure via Foundry:
|
||||
# - UniversalCCIPFlashBridgeAdapter
|
||||
# - CrossChainFlashRepayReceiver
|
||||
# - CrossChainFlashVaultCreditReceiver
|
||||
#
|
||||
# Requires:
|
||||
# PRIVATE_KEY
|
||||
# RPC_URL_138
|
||||
# UNIVERSAL_CCIP_BRIDGE or FLASH_UNIVERSAL_CCIP_BRIDGE
|
||||
# CCIP_ROUTER / CCIP_ROUTER_ADDRESS / CCIP_ROUTER_CHAIN138 or FLASH_CCIP_ROUTER
|
||||
#
|
||||
# Optional:
|
||||
# FLASH_REPAY_RECEIVER_ROUTER
|
||||
# FLASH_VAULT_CREDIT_ROUTER
|
||||
#
|
||||
# Usage:
|
||||
# source scripts/lib/load-project-env.sh
|
||||
# ./scripts/deployment/deploy-cross-chain-flash-infra-chain138.sh
|
||||
# ./scripts/deployment/deploy-cross-chain-flash-infra-chain138.sh --dry-run
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
SMOM="$PROJECT_ROOT/smom-dbis-138"
|
||||
|
||||
if [[ -f "$PROJECT_ROOT/scripts/lib/load-project-env.sh" ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
RPC="${RPC_URL_138:-}"
|
||||
if [[ -z "$RPC" ]]; then
|
||||
echo "ERROR: Set RPC_URL_138 (Core Chain 138 RPC)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${PRIVATE_KEY:-}" ]]; then
|
||||
echo "ERROR: PRIVATE_KEY must be set." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BRIDGE="${FLASH_UNIVERSAL_CCIP_BRIDGE:-${UNIVERSAL_CCIP_BRIDGE:-}}"
|
||||
if [[ -z "$BRIDGE" ]]; then
|
||||
echo "ERROR: Set FLASH_UNIVERSAL_CCIP_BRIDGE or UNIVERSAL_CCIP_BRIDGE." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ROUTER="${FLASH_CCIP_ROUTER:-${CCIP_ROUTER:-${CCIP_ROUTER_ADDRESS:-${CCIP_ROUTER_CHAIN138:-}}}}"
|
||||
if [[ -z "$ROUTER" ]]; then
|
||||
echo "ERROR: Set FLASH_CCIP_ROUTER or one of CCIP_ROUTER / CCIP_ROUTER_ADDRESS / CCIP_ROUTER_CHAIN138." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$SMOM"
|
||||
|
||||
ARGS=()
|
||||
if [[ "${1:-}" == "--dry-run" ]]; then
|
||||
ARGS=(-vvvv)
|
||||
else
|
||||
ARGS=(--broadcast --with-gas-price 1000000000 --gas-estimate-multiplier 150 -vvvv)
|
||||
fi
|
||||
|
||||
echo "RPC_URL_138=$RPC"
|
||||
echo "FLASH_UNIVERSAL_CCIP_BRIDGE=$BRIDGE"
|
||||
echo "FLASH_CCIP_ROUTER=$ROUTER"
|
||||
if [[ -n "${FLASH_REPAY_RECEIVER_ROUTER:-}" ]]; then
|
||||
echo "FLASH_REPAY_RECEIVER_ROUTER=${FLASH_REPAY_RECEIVER_ROUTER}"
|
||||
fi
|
||||
if [[ -n "${FLASH_VAULT_CREDIT_ROUTER:-}" ]]; then
|
||||
echo "FLASH_VAULT_CREDIT_ROUTER=${FLASH_VAULT_CREDIT_ROUTER}"
|
||||
fi
|
||||
|
||||
exec forge script script/deploy/DeployCrossChainFlashInfrastructure.s.sol:DeployCrossChainFlashInfrastructure \
|
||||
--rpc-url "$RPC" "${ARGS[@]}"
|
||||
136
scripts/deployment/deploy-gru-v2-chain138.sh
Normal file
136
scripts/deployment/deploy-gru-v2-chain138.sh
Normal file
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deploy fresh source-aligned GRU c* V2 USD tokens to Chain 138 and stage them in UniversalAssetRegistry.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/deployment/deploy-gru-v2-chain138.sh
|
||||
# bash scripts/deployment/deploy-gru-v2-chain138.sh --dry-run
|
||||
#
|
||||
# Defaults:
|
||||
# - INITIAL_SUPPLY=0
|
||||
# - FORWARD_CANONICAL=1
|
||||
# - REGISTER_IN_GRU=1
|
||||
# - GOVERNANCE_CONTROLLER from env or canonical Chain 138 inventory
|
||||
# - per-token metadata/disclosure/reporting URIs can be exported from
|
||||
# scripts/deployment/publish-gru-v2-chain138-metadata-to-ipfs.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
SMOM_ROOT="${PROJECT_ROOT}/smom-dbis-138"
|
||||
DRY_RUN=0
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dry-run) DRY_RUN=1 ;;
|
||||
*)
|
||||
echo "Unknown argument: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -f "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" ]]; then
|
||||
set +eu
|
||||
# shellcheck source=../lib/load-project-env.sh
|
||||
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
set -euo pipefail
|
||||
fi
|
||||
|
||||
: "${PRIVATE_KEY:?PRIVATE_KEY is required}"
|
||||
|
||||
RPC_URL_138="${RPC_URL_138:-http://192.168.11.211:8545}"
|
||||
UNIVERSAL_ASSET_REGISTRY="${UNIVERSAL_ASSET_REGISTRY:-0xAEE4b7fBe82E1F8295951584CBc772b8BBD68575}"
|
||||
GOVERNANCE_CONTROLLER="${GOVERNANCE_CONTROLLER:-0xA6891D5229f2181a34D4FF1B515c3Aa37dd90E0e}"
|
||||
INITIAL_SUPPLY="${INITIAL_SUPPLY:-0}"
|
||||
FORWARD_CANONICAL="${FORWARD_CANONICAL:-1}"
|
||||
REGISTER_IN_GRU="${REGISTER_IN_GRU:-1}"
|
||||
DEPLOY_CUSDT_V2="${DEPLOY_CUSDT_V2:-1}"
|
||||
DEPLOY_CUSDC_V2="${DEPLOY_CUSDC_V2:-1}"
|
||||
GAS_PRICE_WEI="${GAS_PRICE_WEI:-1000000000}"
|
||||
|
||||
if ! command -v cast >/dev/null 2>&1; then
|
||||
echo "cast is required for nonce preflight checks" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
deployer_address="$(cast wallet address --private-key "$PRIVATE_KEY" 2>/dev/null)"
|
||||
latest_nonce="$(cast rpc --rpc-url "$RPC_URL_138" eth_getTransactionCount "$deployer_address" latest | tr -d '\"')"
|
||||
pending_nonce="$(cast rpc --rpc-url "$RPC_URL_138" eth_getTransactionCount "$deployer_address" pending | tr -d '\"')"
|
||||
|
||||
if [[ "$latest_nonce" != "$pending_nonce" ]]; then
|
||||
echo "Refusing GRU v2 deploy: deployer has pending transactions." >&2
|
||||
echo "deployer=$deployer_address latest_nonce=$latest_nonce pending_nonce=$pending_nonce" >&2
|
||||
echo "Resolve or replace the pending transaction set before retrying." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CMD=(
|
||||
forge script script/deploy/DeployAndStageCompliantFiatTokensV2ForChain.s.sol
|
||||
--rpc-url "$RPC_URL_138"
|
||||
--broadcast
|
||||
--private-key "$PRIVATE_KEY"
|
||||
--with-gas-price "$GAS_PRICE_WEI"
|
||||
)
|
||||
|
||||
echo "=== GRU v2 Chain 138 deploy ==="
|
||||
echo "RPC_URL_138=$RPC_URL_138"
|
||||
echo "UNIVERSAL_ASSET_REGISTRY=$UNIVERSAL_ASSET_REGISTRY"
|
||||
echo "GOVERNANCE_CONTROLLER=$GOVERNANCE_CONTROLLER"
|
||||
echo "INITIAL_SUPPLY=$INITIAL_SUPPLY"
|
||||
echo "FORWARD_CANONICAL=$FORWARD_CANONICAL"
|
||||
echo "REGISTER_IN_GRU=$REGISTER_IN_GRU"
|
||||
echo "DEPLOY_CUSDT_V2=$DEPLOY_CUSDT_V2"
|
||||
echo "DEPLOY_CUSDC_V2=$DEPLOY_CUSDC_V2"
|
||||
echo "DEPLOYER=$deployer_address"
|
||||
echo "NONCE_LATEST=$latest_nonce"
|
||||
echo "NONCE_PENDING=$pending_nonce"
|
||||
echo "CUSDT_V2_TOKEN_URI=${CUSDT_V2_TOKEN_URI:-}"
|
||||
echo "CUSDC_V2_TOKEN_URI=${CUSDC_V2_TOKEN_URI:-}"
|
||||
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
printf '[DRY_RUN] (cd %s && ' "$SMOM_ROOT"
|
||||
printf 'INITIAL_SUPPLY=%q FORWARD_CANONICAL=%q REGISTER_IN_GRU=%q GOVERNANCE_CONTROLLER=%q UNIVERSAL_ASSET_REGISTRY=%q DEPLOY_CUSDT_V2=%q DEPLOY_CUSDC_V2=%q GAS_PRICE_WEI=%q ' \
|
||||
"$INITIAL_SUPPLY" "$FORWARD_CANONICAL" "$REGISTER_IN_GRU" "$GOVERNANCE_CONTROLLER" "$UNIVERSAL_ASSET_REGISTRY" "$DEPLOY_CUSDT_V2" "$DEPLOY_CUSDC_V2" "$GAS_PRICE_WEI"
|
||||
printf 'CUSDT_V2_TOKEN_URI=%q CUSDT_V2_REGULATORY_DISCLOSURE_URI=%q CUSDT_V2_REPORTING_URI=%q ' \
|
||||
"${CUSDT_V2_TOKEN_URI:-}" "${CUSDT_V2_REGULATORY_DISCLOSURE_URI:-}" "${CUSDT_V2_REPORTING_URI:-}"
|
||||
printf 'CUSDC_V2_TOKEN_URI=%q CUSDC_V2_REGULATORY_DISCLOSURE_URI=%q CUSDC_V2_REPORTING_URI=%q ' \
|
||||
"${CUSDC_V2_TOKEN_URI:-}" "${CUSDC_V2_REGULATORY_DISCLOSURE_URI:-}" "${CUSDC_V2_REPORTING_URI:-}"
|
||||
printed=()
|
||||
skip_next=0
|
||||
for arg in "${CMD[@]}"; do
|
||||
if [[ "$skip_next" == "1" ]]; then
|
||||
printed+=("<hidden>")
|
||||
skip_next=0
|
||||
continue
|
||||
fi
|
||||
if [[ "$arg" == "--private-key" ]]; then
|
||||
printed+=("$arg")
|
||||
skip_next=1
|
||||
continue
|
||||
fi
|
||||
printed+=("$arg")
|
||||
done
|
||||
printf '%q ' "${printed[@]}"
|
||||
printf ')\n'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$SMOM_ROOT"
|
||||
INITIAL_SUPPLY="$INITIAL_SUPPLY" \
|
||||
FORWARD_CANONICAL="$FORWARD_CANONICAL" \
|
||||
REGISTER_IN_GRU="$REGISTER_IN_GRU" \
|
||||
DEPLOY_CUSDT_V2="$DEPLOY_CUSDT_V2" \
|
||||
DEPLOY_CUSDC_V2="$DEPLOY_CUSDC_V2" \
|
||||
GOVERNANCE_CONTROLLER="$GOVERNANCE_CONTROLLER" \
|
||||
UNIVERSAL_ASSET_REGISTRY="$UNIVERSAL_ASSET_REGISTRY" \
|
||||
GAS_PRICE_WEI="$GAS_PRICE_WEI" \
|
||||
CUSDT_V2_TOKEN_URI="${CUSDT_V2_TOKEN_URI:-}" \
|
||||
CUSDT_V2_REGULATORY_DISCLOSURE_URI="${CUSDT_V2_REGULATORY_DISCLOSURE_URI:-}" \
|
||||
CUSDT_V2_REPORTING_URI="${CUSDT_V2_REPORTING_URI:-}" \
|
||||
CUSDC_V2_TOKEN_URI="${CUSDC_V2_TOKEN_URI:-}" \
|
||||
CUSDC_V2_REGULATORY_DISCLOSURE_URI="${CUSDC_V2_REGULATORY_DISCLOSURE_URI:-}" \
|
||||
CUSDC_V2_REPORTING_URI="${CUSDC_V2_REPORTING_URI:-}" \
|
||||
"${CMD[@]}"
|
||||
)
|
||||
155
scripts/deployment/deploy-gru-v2-generic-chain138.sh
Normal file
155
scripts/deployment/deploy-gru-v2-generic-chain138.sh
Normal file
@@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deploy a generic GRU c* V2 asset to Chain 138 and optionally stage it in UniversalAssetRegistry.
|
||||
#
|
||||
# Usage:
|
||||
# TOKEN_NAME="Euro Coin (Compliant V2)" TOKEN_SYMBOL="cEURC" CURRENCY_CODE=EUR \
|
||||
# REGISTRY_NAME="Euro Coin (Compliant V2)" REGISTRY_SYMBOL="cEURC.v2" \
|
||||
# bash scripts/deployment/deploy-gru-v2-generic-chain138.sh
|
||||
# ... --dry-run
|
||||
#
|
||||
# Required env:
|
||||
# PRIVATE_KEY
|
||||
# TOKEN_NAME
|
||||
# TOKEN_SYMBOL
|
||||
# CURRENCY_CODE
|
||||
#
|
||||
# Optional env:
|
||||
# TOKEN_DECIMALS=6
|
||||
# VERSION_TAG=2
|
||||
# INITIAL_SUPPLY=0
|
||||
# FORWARD_CANONICAL=1
|
||||
# REGISTER_IN_GRU=1
|
||||
# GOVERNANCE_CONTROLLER, UNIVERSAL_ASSET_REGISTRY
|
||||
# REGISTRY_NAME, REGISTRY_SYMBOL
|
||||
# TOKEN_URI, REGULATORY_DISCLOSURE_URI, REPORTING_URI
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
SMOM_ROOT="${PROJECT_ROOT}/smom-dbis-138"
|
||||
DRY_RUN=0
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dry-run) DRY_RUN=1 ;;
|
||||
*)
|
||||
echo "Unknown argument: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -f "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" ]]; then
|
||||
set +eu
|
||||
# shellcheck source=../lib/load-project-env.sh
|
||||
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
set -euo pipefail
|
||||
fi
|
||||
|
||||
: "${PRIVATE_KEY:?PRIVATE_KEY is required}"
|
||||
: "${TOKEN_NAME:?TOKEN_NAME is required}"
|
||||
: "${TOKEN_SYMBOL:?TOKEN_SYMBOL is required}"
|
||||
: "${CURRENCY_CODE:?CURRENCY_CODE is required}"
|
||||
|
||||
RPC_URL_138="${RPC_URL_138:-http://192.168.11.211:8545}"
|
||||
UNIVERSAL_ASSET_REGISTRY="${UNIVERSAL_ASSET_REGISTRY:-0xAEE4b7fBe82E1F8295951584CBc772b8BBD68575}"
|
||||
GOVERNANCE_CONTROLLER="${GOVERNANCE_CONTROLLER:-0xA6891D5229f2181a34D4FF1B515c3Aa37dd90E0e}"
|
||||
TOKEN_DECIMALS="${TOKEN_DECIMALS:-6}"
|
||||
VERSION_TAG="${VERSION_TAG:-2}"
|
||||
INITIAL_SUPPLY="${INITIAL_SUPPLY:-0}"
|
||||
FORWARD_CANONICAL="${FORWARD_CANONICAL:-1}"
|
||||
REGISTER_IN_GRU="${REGISTER_IN_GRU:-1}"
|
||||
REGISTRY_NAME="${REGISTRY_NAME:-$TOKEN_NAME}"
|
||||
REGISTRY_SYMBOL="${REGISTRY_SYMBOL:-${TOKEN_SYMBOL}.v${VERSION_TAG}}"
|
||||
GAS_PRICE_WEI="${GAS_PRICE_WEI:-1000000000}"
|
||||
|
||||
if ! command -v cast >/dev/null 2>&1; then
|
||||
echo "cast is required for nonce preflight checks" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
deployer_address="$(cast wallet address --private-key "$PRIVATE_KEY" 2>/dev/null)"
|
||||
latest_nonce="$(cast rpc --rpc-url "$RPC_URL_138" eth_getTransactionCount "$deployer_address" latest | tr -d '\"')"
|
||||
pending_nonce="$(cast rpc --rpc-url "$RPC_URL_138" eth_getTransactionCount "$deployer_address" pending | tr -d '\"')"
|
||||
|
||||
if [[ "$latest_nonce" != "$pending_nonce" ]]; then
|
||||
echo "Refusing generic GRU v2 deploy: deployer has pending transactions." >&2
|
||||
echo "deployer=$deployer_address latest_nonce=$latest_nonce pending_nonce=$pending_nonce" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CMD=(
|
||||
forge script script/deploy/DeployAndStageGenericCompliantFiatTokenV2ForChain.s.sol
|
||||
--rpc-url "$RPC_URL_138"
|
||||
--broadcast
|
||||
--private-key "$PRIVATE_KEY"
|
||||
--with-gas-price "$GAS_PRICE_WEI"
|
||||
)
|
||||
|
||||
echo "=== Generic GRU v2 Chain 138 deploy ==="
|
||||
echo "RPC_URL_138=$RPC_URL_138"
|
||||
echo "TOKEN_NAME=$TOKEN_NAME"
|
||||
echo "TOKEN_SYMBOL=$TOKEN_SYMBOL"
|
||||
echo "CURRENCY_CODE=$CURRENCY_CODE"
|
||||
echo "TOKEN_DECIMALS=$TOKEN_DECIMALS"
|
||||
echo "VERSION_TAG=$VERSION_TAG"
|
||||
echo "REGISTRY_NAME=$REGISTRY_NAME"
|
||||
echo "REGISTRY_SYMBOL=$REGISTRY_SYMBOL"
|
||||
echo "UNIVERSAL_ASSET_REGISTRY=$UNIVERSAL_ASSET_REGISTRY"
|
||||
echo "GOVERNANCE_CONTROLLER=$GOVERNANCE_CONTROLLER"
|
||||
echo "INITIAL_SUPPLY=$INITIAL_SUPPLY"
|
||||
echo "FORWARD_CANONICAL=$FORWARD_CANONICAL"
|
||||
echo "REGISTER_IN_GRU=$REGISTER_IN_GRU"
|
||||
echo "DEPLOYER=$deployer_address"
|
||||
echo "NONCE_LATEST=$latest_nonce"
|
||||
echo "NONCE_PENDING=$pending_nonce"
|
||||
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
printf '[DRY_RUN] (cd %s && ' "$SMOM_ROOT"
|
||||
printf 'TOKEN_NAME=%q TOKEN_SYMBOL=%q CURRENCY_CODE=%q TOKEN_DECIMALS=%q VERSION_TAG=%q ' \
|
||||
"$TOKEN_NAME" "$TOKEN_SYMBOL" "$CURRENCY_CODE" "$TOKEN_DECIMALS" "$VERSION_TAG"
|
||||
printf 'INITIAL_SUPPLY=%q FORWARD_CANONICAL=%q REGISTER_IN_GRU=%q GOVERNANCE_CONTROLLER=%q UNIVERSAL_ASSET_REGISTRY=%q REGISTRY_NAME=%q REGISTRY_SYMBOL=%q GAS_PRICE_WEI=%q ' \
|
||||
"$INITIAL_SUPPLY" "$FORWARD_CANONICAL" "$REGISTER_IN_GRU" "$GOVERNANCE_CONTROLLER" "$UNIVERSAL_ASSET_REGISTRY" "$REGISTRY_NAME" "$REGISTRY_SYMBOL" "$GAS_PRICE_WEI"
|
||||
printf 'TOKEN_URI=%q REGULATORY_DISCLOSURE_URI=%q REPORTING_URI=%q ' \
|
||||
"${TOKEN_URI:-}" "${REGULATORY_DISCLOSURE_URI:-}" "${REPORTING_URI:-}"
|
||||
printed=()
|
||||
skip_next=0
|
||||
for arg in "${CMD[@]}"; do
|
||||
if [[ "$skip_next" == "1" ]]; then
|
||||
printed+=("<hidden>")
|
||||
skip_next=0
|
||||
continue
|
||||
fi
|
||||
if [[ "$arg" == "--private-key" ]]; then
|
||||
printed+=("$arg")
|
||||
skip_next=1
|
||||
continue
|
||||
fi
|
||||
printed+=("$arg")
|
||||
done
|
||||
printf '%q ' "${printed[@]}"
|
||||
printf ')\n'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$SMOM_ROOT"
|
||||
TOKEN_NAME="$TOKEN_NAME" \
|
||||
TOKEN_SYMBOL="$TOKEN_SYMBOL" \
|
||||
CURRENCY_CODE="$CURRENCY_CODE" \
|
||||
TOKEN_DECIMALS="$TOKEN_DECIMALS" \
|
||||
VERSION_TAG="$VERSION_TAG" \
|
||||
INITIAL_SUPPLY="$INITIAL_SUPPLY" \
|
||||
FORWARD_CANONICAL="$FORWARD_CANONICAL" \
|
||||
REGISTER_IN_GRU="$REGISTER_IN_GRU" \
|
||||
GOVERNANCE_CONTROLLER="$GOVERNANCE_CONTROLLER" \
|
||||
UNIVERSAL_ASSET_REGISTRY="$UNIVERSAL_ASSET_REGISTRY" \
|
||||
REGISTRY_NAME="$REGISTRY_NAME" \
|
||||
REGISTRY_SYMBOL="$REGISTRY_SYMBOL" \
|
||||
GAS_PRICE_WEI="$GAS_PRICE_WEI" \
|
||||
TOKEN_URI="${TOKEN_URI:-}" \
|
||||
REGULATORY_DISCLOSURE_URI="${REGULATORY_DISCLOSURE_URI:-}" \
|
||||
REPORTING_URI="${REPORTING_URI:-}" \
|
||||
"${CMD[@]}"
|
||||
)
|
||||
170
scripts/deployment/deploy-mainnet-aave-quote-push-stack.sh
Executable file
170
scripts/deployment/deploy-mainnet-aave-quote-push-stack.sh
Executable file
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Deploy Mainnet flash quote-push stack (Aave receiver + external unwinder).
|
||||
# Default: simulation only (no --broadcast). Use --apply to broadcast.
|
||||
#
|
||||
# Env:
|
||||
# PRIVATE_KEY, ETHEREUM_MAINNET_RPC
|
||||
# DODO_PMM_INTEGRATION_MAINNET required when QUOTE_PUSH_UNWINDER_TYPE=dodo
|
||||
# UNISWAP_V3_SWAP_ROUTER_MAINNET optional for univ3; defaults to legacy SwapRouter `0xE592...`
|
||||
# QUOTE_PUSH_UNWINDER_TYPE univ3 (default) | dodo | two_hop_dodo | dodo_univ3
|
||||
#
|
||||
# Usage:
|
||||
# source scripts/lib/load-project-env.sh
|
||||
# bash scripts/deployment/deploy-mainnet-aave-quote-push-stack.sh --dry-run
|
||||
# bash scripts/deployment/deploy-mainnet-aave-quote-push-stack.sh --apply
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROXMOX_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
SMOM="${PROXMOX_ROOT}/smom-dbis-138"
|
||||
|
||||
# Preserve explicit caller overrides across sourced repo env files.
|
||||
_qp_private_key="${PRIVATE_KEY-}"
|
||||
_qp_rpc="${ETHEREUM_MAINNET_RPC-}"
|
||||
_qp_unwind_type="${QUOTE_PUSH_UNWINDER_TYPE-}"
|
||||
_qp_integration="${DODO_PMM_INTEGRATION_MAINNET-}"
|
||||
_qp_router="${UNISWAP_V3_SWAP_ROUTER_MAINNET-}"
|
||||
_qp_skip_receiver="${DEPLOY_MAINNET_FLASH_SKIP_RECEIVER-}"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROXMOX_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
# shellcheck disable=SC1091
|
||||
source "${SMOM}/scripts/load-env.sh" >/dev/null 2>&1 || true
|
||||
|
||||
[[ -n "$_qp_private_key" ]] && export PRIVATE_KEY="$_qp_private_key"
|
||||
[[ -n "$_qp_rpc" ]] && export ETHEREUM_MAINNET_RPC="$_qp_rpc"
|
||||
[[ -n "$_qp_unwind_type" ]] && export QUOTE_PUSH_UNWINDER_TYPE="$_qp_unwind_type"
|
||||
[[ -n "$_qp_integration" ]] && export DODO_PMM_INTEGRATION_MAINNET="$_qp_integration"
|
||||
[[ -n "$_qp_router" ]] && export UNISWAP_V3_SWAP_ROUTER_MAINNET="$_qp_router"
|
||||
[[ -n "$_qp_skip_receiver" ]] && export DEPLOY_MAINNET_FLASH_SKIP_RECEIVER="$_qp_skip_receiver"
|
||||
|
||||
unset _qp_private_key _qp_rpc _qp_unwind_type _qp_integration _qp_router _qp_skip_receiver
|
||||
|
||||
pick_latest_create_address() {
|
||||
local script_name="$1"
|
||||
local contract_name="$2"
|
||||
local latest_json="${SMOM}/broadcast/${script_name}/1/run-latest.json"
|
||||
if [[ ! -f "$latest_json" ]] || ! command -v jq >/dev/null 2>&1; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
jq -r --arg contract "$contract_name" \
|
||||
'.transactions[]? | select(.transactionType == "CREATE" and .contractName == $contract) | .contractAddress' \
|
||||
"$latest_json" | tail -n1
|
||||
}
|
||||
|
||||
BROADCAST=()
|
||||
MODE="dry-run"
|
||||
if (($# == 0)); then
|
||||
:
|
||||
else
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
--apply) BROADCAST=(--broadcast); MODE=apply ;;
|
||||
--dry-run) BROADCAST=(); MODE=dry-run ;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $a (use --dry-run or --apply)" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ -z "${ETHEREUM_MAINNET_RPC:-}" || -z "${PRIVATE_KEY:-}" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC and PRIVATE_KEY are required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
UNW="${QUOTE_PUSH_UNWINDER_TYPE:-univ3}"
|
||||
|
||||
echo "mode=$MODE unwinder_type=$UNW"
|
||||
if [[ "${DEPLOY_MAINNET_FLASH_SKIP_RECEIVER:-}" == "1" ]]; then
|
||||
echo "Step 1/2: skipped (DEPLOY_MAINNET_FLASH_SKIP_RECEIVER=1; reuse AAVE_QUOTE_PUSH_RECEIVER_MAINNET)"
|
||||
else
|
||||
echo "Step 1/2: DeployAaveQuotePushFlashReceiver"
|
||||
(
|
||||
cd "$SMOM"
|
||||
forge script script/deploy/DeployAaveQuotePushFlashReceiver.s.sol:DeployAaveQuotePushFlashReceiver \
|
||||
--rpc-url "$ETHEREUM_MAINNET_RPC" \
|
||||
"${BROADCAST[@]}" \
|
||||
-vvvv
|
||||
)
|
||||
fi
|
||||
|
||||
if [[ "$UNW" == "dodo" ]]; then
|
||||
if [[ -z "${DODO_PMM_INTEGRATION_MAINNET:-}" ]]; then
|
||||
echo "[fail] DODO_PMM_INTEGRATION_MAINNET required for dodo unwinder" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Step 2/2: DeployDODOIntegrationExternalUnwinder"
|
||||
(
|
||||
cd "$SMOM"
|
||||
forge script script/deploy/DeployDODOIntegrationExternalUnwinder.s.sol:DeployDODOIntegrationExternalUnwinder \
|
||||
--rpc-url "$ETHEREUM_MAINNET_RPC" \
|
||||
"${BROADCAST[@]}" \
|
||||
-vvvv
|
||||
)
|
||||
elif [[ "$UNW" == "two_hop_dodo" ]]; then
|
||||
if [[ -z "${DODO_PMM_INTEGRATION_MAINNET:-}" ]]; then
|
||||
echo "[fail] DODO_PMM_INTEGRATION_MAINNET required for two_hop_dodo unwinder" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Step 2/2: DeployTwoHopDodoIntegrationUnwinder"
|
||||
(
|
||||
cd "$SMOM"
|
||||
forge script script/deploy/DeployTwoHopDodoIntegrationUnwinder.s.sol:DeployTwoHopDodoIntegrationUnwinder \
|
||||
--rpc-url "$ETHEREUM_MAINNET_RPC" \
|
||||
"${BROADCAST[@]}" \
|
||||
-vvvv
|
||||
)
|
||||
elif [[ "$UNW" == "dodo_univ3" ]]; then
|
||||
if [[ -z "${DODO_PMM_INTEGRATION_MAINNET:-}" ]]; then
|
||||
echo "[fail] DODO_PMM_INTEGRATION_MAINNET required for dodo_univ3 unwinder" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Step 2/2: DeployDODOToUniswapV3MultiHopExternalUnwinder"
|
||||
(
|
||||
cd "$SMOM"
|
||||
forge script script/deploy/DeployDODOToUniswapV3MultiHopExternalUnwinder.s.sol:DeployDODOToUniswapV3MultiHopExternalUnwinder \
|
||||
--rpc-url "$ETHEREUM_MAINNET_RPC" \
|
||||
"${BROADCAST[@]}" \
|
||||
-vvvv
|
||||
)
|
||||
else
|
||||
echo "Step 2/2: DeployUniswapV3ExternalUnwinder"
|
||||
(
|
||||
cd "$SMOM"
|
||||
forge script script/deploy/DeployUniswapV3ExternalUnwinder.s.sol:DeployUniswapV3ExternalUnwinder \
|
||||
--rpc-url "$ETHEREUM_MAINNET_RPC" \
|
||||
"${BROADCAST[@]}" \
|
||||
-vvvv
|
||||
)
|
||||
fi
|
||||
|
||||
echo
|
||||
receiver_addr="$(pick_latest_create_address "DeployAaveQuotePushFlashReceiver.s.sol" "AaveQuotePushFlashReceiver" || true)"
|
||||
unwinder_contract="UniswapV3ExternalUnwinder"
|
||||
unwinder_script="DeployUniswapV3ExternalUnwinder.s.sol"
|
||||
case "$UNW" in
|
||||
dodo)
|
||||
unwinder_contract="DODOIntegrationExternalUnwinder"
|
||||
unwinder_script="DeployDODOIntegrationExternalUnwinder.s.sol"
|
||||
;;
|
||||
two_hop_dodo)
|
||||
unwinder_contract="TwoHopDodoIntegrationUnwinder"
|
||||
unwinder_script="DeployTwoHopDodoIntegrationUnwinder.s.sol"
|
||||
;;
|
||||
dodo_univ3)
|
||||
unwinder_contract="DODOToUniswapV3MultiHopExternalUnwinder"
|
||||
unwinder_script="DeployDODOToUniswapV3MultiHopExternalUnwinder.s.sol"
|
||||
;;
|
||||
esac
|
||||
unwinder_addr="$(pick_latest_create_address "$unwinder_script" "$unwinder_contract" || true)"
|
||||
|
||||
echo "After --apply: copy deployed addresses into .env:"
|
||||
echo " AAVE_QUOTE_PUSH_RECEIVER_MAINNET=${receiver_addr:-...}"
|
||||
echo " QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET=${unwinder_addr:-...}"
|
||||
echo "Or rerun immediately with QUOTE_PUSH_UNWINDER_TYPE=${UNW} so run-mainnet-aave-cwusdc-quote-push-once.sh can auto-pick the latest broadcast unwinder."
|
||||
echo "Then set FLASH_QUOTE_AMOUNT_RAW, UNWIND_MODE, UNWIND_V3_FEE_U24 (or UNWIND_DODO_POOL / two-hop vars) and run:"
|
||||
echo " bash scripts/deployment/run-mainnet-aave-cwusdc-quote-push-once.sh --dry-run"
|
||||
256
scripts/deployment/deploy-mainnet-cwusdt-cwusdc-pool.sh
Executable file
256
scripts/deployment/deploy-mainnet-cwusdt-cwusdc-pool.sh
Executable file
@@ -0,0 +1,256 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Create and seed Mainnet DODO PMM pool cWUSDT (base) / cWUSDC (quote) at 1:1 initial price.
|
||||
#
|
||||
# One integration pool supports swaps in both directions; there is no second pool for the reverse.
|
||||
# lpFeeRate defaults to 3 bps to match other public cW*/USD rails; k=0; TWAP off (same as wave-1 public stables).
|
||||
#
|
||||
# initialPrice uses DODO convention (1e18 = unity peg for same-decimal USD-linked stables).
|
||||
#
|
||||
# Examples:
|
||||
# bash scripts/deployment/compute-mainnet-cwusdt-cwusdc-seed-amounts.sh --multiplier=50 --cap-raw=...
|
||||
# bash scripts/deployment/deploy-mainnet-cwusdt-cwusdc-pool.sh \
|
||||
# --initial-price=1000000000000000000 \
|
||||
# --base-amount=<seed> --quote-amount=<seed> \
|
||||
# --dry-run
|
||||
#
|
||||
# Registry: cross-chain-pmm-lps/config/deployment-status.json lists the live vault
|
||||
# 0xe944b7Cb012A0820c07f54D51e92f0e1C74168DB when already created; this script skips
|
||||
# createPool when integration mapping exists and only tops up liquidity if you pass amounts.
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
source "${PROJECT_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd cast
|
||||
|
||||
INITIAL_PRICE="1000000000000000000"
|
||||
BASE_AMOUNT=""
|
||||
QUOTE_AMOUNT=""
|
||||
MINT_BASE_AMOUNT="0"
|
||||
MINT_QUOTE_AMOUNT="0"
|
||||
FEE_BPS="3"
|
||||
K_VALUE="0"
|
||||
OPEN_TWAP="false"
|
||||
DRY_RUN=0
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--initial-price=*) INITIAL_PRICE="${arg#*=}" ;;
|
||||
--base-amount=*) BASE_AMOUNT="${arg#*=}" ;;
|
||||
--quote-amount=*) QUOTE_AMOUNT="${arg#*=}" ;;
|
||||
--mint-base-amount=*) MINT_BASE_AMOUNT="${arg#*=}" ;;
|
||||
--mint-quote-amount=*) MINT_QUOTE_AMOUNT="${arg#*=}" ;;
|
||||
--fee-bps=*) FEE_BPS="${arg#*=}" ;;
|
||||
--k=*) K_VALUE="${arg#*=}" ;;
|
||||
--open-twap=*) OPEN_TWAP="${arg#*=}" ;;
|
||||
--dry-run) DRY_RUN=1 ;;
|
||||
-h | --help)
|
||||
sed -n '1,28p' "$0"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$BASE_AMOUNT" || -z "$QUOTE_AMOUNT" ]]; then
|
||||
echo "[fail] required: --base-amount and --quote-amount (use matched 1:1 raw for peg seed)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-}"
|
||||
INTEGRATION="${DODO_PMM_INTEGRATION_MAINNET:-}"
|
||||
|
||||
CWUSDT="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
|
||||
if [[ -z "$RPC_URL" || -z "$PRIVATE_KEY" || -z "$INTEGRATION" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC, PRIVATE_KEY, and DODO_PMM_INTEGRATION_MAINNET are required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEPLOYER="$(cast wallet address --private-key "$PRIVATE_KEY")"
|
||||
BASE_TOKEN="$CWUSDT"
|
||||
QUOTE_TOKEN="$CWUSDC"
|
||||
PAIR_LABEL="cWUSDT/cWUSDC"
|
||||
|
||||
parse_tx_hash() {
|
||||
local output="$1"
|
||||
local tx_hash
|
||||
tx_hash="$(printf '%s\n' "$output" | grep -E '^0x[0-9a-fA-F]{64}$' | tail -n1 || true)"
|
||||
if [[ -z "$tx_hash" ]]; then
|
||||
tx_hash="$(printf '%s\n' "$output" | grep -E '^transactionHash[[:space:]]+0x[0-9a-fA-F]{64}$' | awk '{print $2}' | tail -n1 || true)"
|
||||
fi
|
||||
if [[ -z "$tx_hash" ]]; then
|
||||
return 1
|
||||
fi
|
||||
printf '%s\n' "$tx_hash"
|
||||
}
|
||||
|
||||
bool_to_cli() {
|
||||
if [[ "$1" == "true" ]]; then
|
||||
printf 'true'
|
||||
else
|
||||
printf 'false'
|
||||
fi
|
||||
}
|
||||
|
||||
existing_pool="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$BASE_TOKEN" "$QUOTE_TOKEN" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
base_balance_before="$(cast call "$BASE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
quote_balance_before="$(cast call "$QUOTE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
base_allowance_before="$(cast call "$BASE_TOKEN" 'allowance(address,address)(uint256)' "$DEPLOYER" "$INTEGRATION" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
quote_allowance_before="$(cast call "$QUOTE_TOKEN" 'allowance(address,address)(uint256)' "$DEPLOYER" "$INTEGRATION" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
create_gas="0"
|
||||
if [[ "$existing_pool" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
create_gas="$(cast estimate "$INTEGRATION" \
|
||||
'createPool(address,address,uint256,uint256,uint256,bool)(address)' \
|
||||
"$BASE_TOKEN" "$QUOTE_TOKEN" "$FEE_BPS" "$INITIAL_PRICE" "$K_VALUE" "$(bool_to_cli "$OPEN_TWAP")" \
|
||||
--from "$DEPLOYER" \
|
||||
--rpc-url "$RPC_URL")"
|
||||
fi
|
||||
|
||||
if (( base_balance_before < BASE_AMOUNT )); then
|
||||
base_after_mint=$(( base_balance_before + MINT_BASE_AMOUNT ))
|
||||
if (( MINT_BASE_AMOUNT == 0 || base_after_mint < BASE_AMOUNT )); then
|
||||
echo "[fail] insufficient cWUSDT: have=$base_balance_before need=$BASE_AMOUNT mint_base=$MINT_BASE_AMOUNT" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if (( quote_balance_before < QUOTE_AMOUNT )); then
|
||||
quote_after_mint=$(( quote_balance_before + MINT_QUOTE_AMOUNT ))
|
||||
if (( MINT_QUOTE_AMOUNT == 0 || quote_after_mint < QUOTE_AMOUNT )); then
|
||||
echo "[fail] insufficient cWUSDC: have=$quote_balance_before need=$QUOTE_AMOUNT mint_quote=$MINT_QUOTE_AMOUNT" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if (( DRY_RUN == 1 )); then
|
||||
echo "pair=$PAIR_LABEL"
|
||||
echo "baseToken=$BASE_TOKEN"
|
||||
echo "quoteToken=$QUOTE_TOKEN"
|
||||
echo "integration=$INTEGRATION"
|
||||
echo "existingPool=$existing_pool"
|
||||
echo "initialPrice=$INITIAL_PRICE"
|
||||
echo "feeBps=$FEE_BPS"
|
||||
echo "k=$K_VALUE"
|
||||
echo "openTwap=$OPEN_TWAP"
|
||||
echo "createGas=$create_gas"
|
||||
echo "baseAmount=$BASE_AMOUNT"
|
||||
echo "quoteAmount=$QUOTE_AMOUNT"
|
||||
echo "mintBaseAmount=$MINT_BASE_AMOUNT"
|
||||
echo "mintQuoteAmount=$MINT_QUOTE_AMOUNT"
|
||||
echo "baseBalanceBefore=$base_balance_before"
|
||||
echo "quoteBalanceBefore=$quote_balance_before"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mint_base_tx=""
|
||||
mint_quote_tx=""
|
||||
create_tx=""
|
||||
approve_base_tx=""
|
||||
approve_quote_tx=""
|
||||
add_liquidity_tx=""
|
||||
|
||||
if (( MINT_BASE_AMOUNT > 0 )); then
|
||||
mint_base_output="$(
|
||||
cast send "$BASE_TOKEN" \
|
||||
'mint(address,uint256)' \
|
||||
"$DEPLOYER" "$MINT_BASE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
mint_base_tx="$(parse_tx_hash "$mint_base_output")"
|
||||
fi
|
||||
|
||||
if (( MINT_QUOTE_AMOUNT > 0 )); then
|
||||
mint_quote_output="$(
|
||||
cast send "$QUOTE_TOKEN" \
|
||||
'mint(address,uint256)' \
|
||||
"$DEPLOYER" "$MINT_QUOTE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
mint_quote_tx="$(parse_tx_hash "$mint_quote_output")"
|
||||
fi
|
||||
|
||||
if [[ "$existing_pool" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
create_output="$(
|
||||
cast send "$INTEGRATION" \
|
||||
'createPool(address,address,uint256,uint256,uint256,bool)(address)' \
|
||||
"$BASE_TOKEN" "$QUOTE_TOKEN" "$FEE_BPS" "$INITIAL_PRICE" "$K_VALUE" "$(bool_to_cli "$OPEN_TWAP")" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
create_tx="$(parse_tx_hash "$create_output")"
|
||||
existing_pool="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$BASE_TOKEN" "$QUOTE_TOKEN" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
fi
|
||||
|
||||
if [[ "$existing_pool" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
echo "[fail] pool creation did not yield a pool address" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( base_allowance_before < BASE_AMOUNT )); then
|
||||
approve_base_output="$(
|
||||
cast send "$BASE_TOKEN" \
|
||||
'approve(address,uint256)(bool)' \
|
||||
"$INTEGRATION" "$BASE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
approve_base_tx="$(parse_tx_hash "$approve_base_output")"
|
||||
fi
|
||||
|
||||
if (( quote_allowance_before < QUOTE_AMOUNT )); then
|
||||
approve_quote_output="$(
|
||||
cast send "$QUOTE_TOKEN" \
|
||||
'approve(address,uint256)(bool)' \
|
||||
"$INTEGRATION" "$QUOTE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
approve_quote_tx="$(parse_tx_hash "$approve_quote_output")"
|
||||
fi
|
||||
|
||||
add_liquidity_output="$(
|
||||
cast send "$INTEGRATION" \
|
||||
'addLiquidity(address,uint256,uint256)(uint256,uint256,uint256)' \
|
||||
"$existing_pool" "$BASE_AMOUNT" "$QUOTE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
add_liquidity_tx="$(parse_tx_hash "$add_liquidity_output")"
|
||||
|
||||
reserves="$(cast call "$existing_pool" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL")"
|
||||
base_reserve="$(printf '%s\n' "$reserves" | sed -n '1p' | awk '{print $1}')"
|
||||
quote_reserve="$(printf '%s\n' "$reserves" | sed -n '2p' | awk '{print $1}')"
|
||||
|
||||
echo "pair=$PAIR_LABEL"
|
||||
echo "pool=$existing_pool"
|
||||
echo "export POOL_CWUSDT_CWUSDC_MAINNET=$existing_pool"
|
||||
echo "initialPrice=$INITIAL_PRICE"
|
||||
echo "feeBps=$FEE_BPS"
|
||||
echo "mintBaseTx=${mint_base_tx:-none}"
|
||||
echo "mintQuoteTx=${mint_quote_tx:-none}"
|
||||
echo "createTx=${create_tx:-none}"
|
||||
echo "approveBaseTx=${approve_base_tx:-none}"
|
||||
echo "approveQuoteTx=${approve_quote_tx:-none}"
|
||||
echo "addLiquidityTx=$add_liquidity_tx"
|
||||
echo "baseReserve=$base_reserve"
|
||||
echo "quoteReserve=$quote_reserve"
|
||||
268
scripts/deployment/deploy-mainnet-pmm-cw-truu-pool.sh
Executable file
268
scripts/deployment/deploy-mainnet-pmm-cw-truu-pool.sh
Executable file
@@ -0,0 +1,268 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Create and seed a Mainnet DODO PMM pool: cWUSD* (base) / TRUU (quote).
|
||||
#
|
||||
# Seeding "1:1" means **equal USD on each leg**, not equal raw token amounts — see
|
||||
# docs/03-deployment/MAINNET_PMM_TRUU_CWUSD_PEG_AND_BOT_RUNBOOK.md section 9 and:
|
||||
# bash scripts/deployment/compute-mainnet-truu-pmm-seed-amounts.sh
|
||||
# Large fixed USD per leg (prints base/quote + command lines):
|
||||
# bash scripts/deployment/compute-mainnet-truu-liquidity-amounts.sh --usd-per-leg=125000
|
||||
#
|
||||
# Base tokens are 6 decimals (cWUSDT, cWUSDC). TRUU is 10 decimals on mainnet.
|
||||
# You MUST set --initial-price to the correct DODO PMM "i" for your base/quote order
|
||||
# (depends on DODO DVM math — verify on a fork before mainnet size).
|
||||
#
|
||||
# Defaults for volatile-style pools: fee 30 bps, k = 0.5e18, TWAP off.
|
||||
# For peg-only cW↔stable pools use deploy-mainnet-public-dodo-wave1-pool.sh instead.
|
||||
#
|
||||
# Example (dry-run):
|
||||
# bash scripts/deployment/deploy-mainnet-pmm-cw-truu-pool.sh \
|
||||
# --pair=cwusdt-truu \
|
||||
# --initial-price=<YOUR_I_VALUE> \
|
||||
# --base-amount=10000000 \
|
||||
# --quote-amount=25000000000 \
|
||||
# --dry-run
|
||||
#
|
||||
# Top up an **existing** pool (createPool skipped when integration already has base/TRUU):
|
||||
# Use the same --pair, --initial-price (ignored for create but kept for logs), and 1:1 USD legs.
|
||||
# Quote raw from base raw: quote = base * Q_ref / B_ref using any prior deposit (e.g. cWUSDT/TRUU
|
||||
# seed B_ref=1500000, Q_ref=372348929900893). Require wallet TRUU >= quote and cW base >= base.
|
||||
#
|
||||
# After live deploy, record the pool in cross-chain-pmm-lps/config/deployment-status.json
|
||||
# and set PMM_TRUU_BASE_TOKEN / PMM_TRUU_QUOTE_TOKEN for scripts/verify/check-mainnet-pmm-peg-bot-readiness.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
source "${PROJECT_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd cast
|
||||
|
||||
PAIR=""
|
||||
INITIAL_PRICE=""
|
||||
BASE_AMOUNT=""
|
||||
QUOTE_AMOUNT=""
|
||||
MINT_BASE_AMOUNT="0"
|
||||
FEE_BPS="${PMM_TRUU_FEE_BPS:-30}"
|
||||
K_VALUE="${PMM_TRUU_K:-500000000000000000}"
|
||||
OPEN_TWAP="false"
|
||||
DRY_RUN=0
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--pair=*) PAIR="${arg#*=}" ;;
|
||||
--initial-price=*) INITIAL_PRICE="${arg#*=}" ;;
|
||||
--base-amount=*) BASE_AMOUNT="${arg#*=}" ;;
|
||||
--quote-amount=*) QUOTE_AMOUNT="${arg#*=}" ;;
|
||||
--mint-base-amount=*) MINT_BASE_AMOUNT="${arg#*=}" ;;
|
||||
--fee-bps=*) FEE_BPS="${arg#*=}" ;;
|
||||
--k=*) K_VALUE="${arg#*=}" ;;
|
||||
--open-twap=*) OPEN_TWAP="${arg#*=}" ;;
|
||||
--dry-run) DRY_RUN=1 ;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$PAIR" || -z "$INITIAL_PRICE" || -z "$BASE_AMOUNT" || -z "$QUOTE_AMOUNT" ]]; then
|
||||
echo "[fail] required args: --pair, --initial-price, --base-amount, --quote-amount" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-}"
|
||||
INTEGRATION="${DODO_PMM_INTEGRATION_MAINNET:-}"
|
||||
TRUU_MAINNET="${TRUU_MAINNET:-0xDAe0faFD65385E7775Cf75b1398735155EF6aCD2}"
|
||||
|
||||
if [[ -z "$RPC_URL" || -z "$PRIVATE_KEY" || -z "$INTEGRATION" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC, PRIVATE_KEY, and DODO_PMM_INTEGRATION_MAINNET are required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEPLOYER="$(cast wallet address --private-key "$PRIVATE_KEY")"
|
||||
|
||||
BASE_SYMBOL=""
|
||||
BASE_TOKEN=""
|
||||
PAIR_LABEL=""
|
||||
|
||||
case "$PAIR" in
|
||||
cwusdt-truu)
|
||||
BASE_SYMBOL="cWUSDT"
|
||||
BASE_TOKEN="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
||||
PAIR_LABEL="cWUSDT/TRUU"
|
||||
;;
|
||||
cwusdc-truu)
|
||||
BASE_SYMBOL="cWUSDC"
|
||||
BASE_TOKEN="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
PAIR_LABEL="cWUSDC/TRUU"
|
||||
;;
|
||||
*)
|
||||
echo "[fail] unsupported pair: $PAIR (use cwusdt-truu or cwusdc-truu)" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
parse_tx_hash() {
|
||||
local output="$1"
|
||||
local tx_hash
|
||||
tx_hash="$(printf '%s\n' "$output" | grep -E '^0x[0-9a-fA-F]{64}$' | tail -n1 || true)"
|
||||
if [[ -z "$tx_hash" ]]; then
|
||||
tx_hash="$(printf '%s\n' "$output" | grep -E '^transactionHash[[:space:]]+0x[0-9a-fA-F]{64}$' | awk '{print $2}' | tail -n1 || true)"
|
||||
fi
|
||||
if [[ -z "$tx_hash" ]]; then
|
||||
return 1
|
||||
fi
|
||||
printf '%s\n' "$tx_hash"
|
||||
}
|
||||
|
||||
bool_to_cli() {
|
||||
if [[ "$1" == "true" ]]; then
|
||||
printf 'true'
|
||||
else
|
||||
printf 'false'
|
||||
fi
|
||||
}
|
||||
|
||||
QUOTE_TOKEN="$TRUU_MAINNET"
|
||||
|
||||
existing_pool="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$BASE_TOKEN" "$QUOTE_TOKEN" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
base_balance_before="$(cast call "$BASE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
quote_balance_before="$(cast call "$QUOTE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
base_allowance_before="$(cast call "$BASE_TOKEN" 'allowance(address,address)(uint256)' "$DEPLOYER" "$INTEGRATION" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
quote_allowance_before="$(cast call "$QUOTE_TOKEN" 'allowance(address,address)(uint256)' "$DEPLOYER" "$INTEGRATION" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
create_gas="0"
|
||||
if [[ "$existing_pool" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
create_gas="$(cast estimate "$INTEGRATION" \
|
||||
'createPool(address,address,uint256,uint256,uint256,bool)(address)' \
|
||||
"$BASE_TOKEN" "$QUOTE_TOKEN" "$FEE_BPS" "$INITIAL_PRICE" "$K_VALUE" "$(bool_to_cli "$OPEN_TWAP")" \
|
||||
--from "$DEPLOYER" \
|
||||
--rpc-url "$RPC_URL")"
|
||||
fi
|
||||
|
||||
if (( base_balance_before < BASE_AMOUNT )); then
|
||||
base_balance_after_mint=$(( base_balance_before + MINT_BASE_AMOUNT ))
|
||||
if (( MINT_BASE_AMOUNT == 0 || base_balance_after_mint < BASE_AMOUNT )); then
|
||||
echo "[fail] insufficient ${BASE_SYMBOL} balance: have=$base_balance_before need=$BASE_AMOUNT mint_base_amount=$MINT_BASE_AMOUNT" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if (( quote_balance_before < QUOTE_AMOUNT )); then
|
||||
echo "[fail] insufficient TRUU balance: have=$quote_balance_before need=$QUOTE_AMOUNT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( DRY_RUN == 1 )); then
|
||||
echo "pair=$PAIR_LABEL"
|
||||
echo "baseToken=$BASE_TOKEN"
|
||||
echo "quoteToken=$QUOTE_TOKEN"
|
||||
echo "integration=$INTEGRATION"
|
||||
echo "existingPool=$existing_pool"
|
||||
echo "initialPrice=$INITIAL_PRICE"
|
||||
echo "feeBps=$FEE_BPS"
|
||||
echo "k=$K_VALUE"
|
||||
echo "openTwap=$OPEN_TWAP"
|
||||
echo "createGas=$create_gas"
|
||||
echo "baseAmount=$BASE_AMOUNT (6-decimal cW units)"
|
||||
echo "quoteAmount=$QUOTE_AMOUNT (10-decimal TRUU units)"
|
||||
echo "mintBaseAmount=$MINT_BASE_AMOUNT"
|
||||
echo "suggestedVerifierEnv=PMM_TRUU_BASE_TOKEN=$BASE_TOKEN PMM_TRUU_QUOTE_TOKEN=$QUOTE_TOKEN"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mint_tx=""
|
||||
create_tx=""
|
||||
approve_base_tx=""
|
||||
approve_quote_tx=""
|
||||
add_liquidity_tx=""
|
||||
|
||||
if (( MINT_BASE_AMOUNT > 0 )); then
|
||||
mint_output="$(
|
||||
cast send "$BASE_TOKEN" \
|
||||
'mint(address,uint256)' \
|
||||
"$DEPLOYER" "$MINT_BASE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
mint_tx="$(parse_tx_hash "$mint_output")"
|
||||
fi
|
||||
|
||||
if [[ "$existing_pool" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
create_output="$(
|
||||
cast send "$INTEGRATION" \
|
||||
'createPool(address,address,uint256,uint256,uint256,bool)(address)' \
|
||||
"$BASE_TOKEN" "$QUOTE_TOKEN" "$FEE_BPS" "$INITIAL_PRICE" "$K_VALUE" "$(bool_to_cli "$OPEN_TWAP")" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
create_tx="$(parse_tx_hash "$create_output")"
|
||||
existing_pool="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$BASE_TOKEN" "$QUOTE_TOKEN" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
fi
|
||||
|
||||
if [[ "$existing_pool" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
echo "[fail] pool creation did not yield a pool address" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( base_allowance_before < BASE_AMOUNT )); then
|
||||
approve_base_output="$(
|
||||
cast send "$BASE_TOKEN" \
|
||||
'approve(address,uint256)(bool)' \
|
||||
"$INTEGRATION" "$BASE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
approve_base_tx="$(parse_tx_hash "$approve_base_output")"
|
||||
fi
|
||||
|
||||
if (( quote_allowance_before < QUOTE_AMOUNT )); then
|
||||
approve_quote_output="$(
|
||||
cast send "$QUOTE_TOKEN" \
|
||||
'approve(address,uint256)(bool)' \
|
||||
"$INTEGRATION" "$QUOTE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
approve_quote_tx="$(parse_tx_hash "$approve_quote_output")"
|
||||
fi
|
||||
|
||||
add_liquidity_output="$(
|
||||
cast send "$INTEGRATION" \
|
||||
'addLiquidity(address,uint256,uint256)(uint256,uint256,uint256)' \
|
||||
"$existing_pool" "$BASE_AMOUNT" "$QUOTE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
add_liquidity_tx="$(parse_tx_hash "$add_liquidity_output")"
|
||||
|
||||
reserves="$(cast call "$existing_pool" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL")"
|
||||
base_reserve="$(printf '%s\n' "$reserves" | sed -n '1p' | awk '{print $1}')"
|
||||
quote_reserve="$(printf '%s\n' "$reserves" | sed -n '2p' | awk '{print $1}')"
|
||||
|
||||
echo "pair=$PAIR_LABEL"
|
||||
echo "pool=$existing_pool"
|
||||
echo "baseToken=$BASE_TOKEN"
|
||||
echo "quoteToken=$QUOTE_TOKEN"
|
||||
echo "initialPrice=$INITIAL_PRICE"
|
||||
echo "feeBps=$FEE_BPS"
|
||||
echo "k=$K_VALUE"
|
||||
echo "mintTx=${mint_tx:-none}"
|
||||
echo "createTx=${create_tx:-none}"
|
||||
echo "approveBaseTx=${approve_base_tx:-none}"
|
||||
echo "approveQuoteTx=${approve_quote_tx:-none}"
|
||||
echo "addLiquidityTx=$add_liquidity_tx"
|
||||
echo "baseReserve=$base_reserve"
|
||||
echo "quoteReserve=$quote_reserve"
|
||||
echo "nextSteps=Add pool row to cross-chain-pmm-lps/config/deployment-status.json; set PMM_TRUU_BASE_TOKEN and PMM_TRUU_QUOTE_TOKEN; run scripts/verify/check-mainnet-pmm-peg-bot-readiness.sh"
|
||||
317
scripts/deployment/deploy-mainnet-public-dodo-wave1-pool.sh
Normal file
317
scripts/deployment/deploy-mainnet-public-dodo-wave1-pool.sh
Normal file
@@ -0,0 +1,317 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Deploy and seed a first-tier Mainnet DODO PMM Wave 1 cW*/USDC pool.
|
||||
#
|
||||
# Amount arguments are base units (6 decimals for the current cW* / USDC suite).
|
||||
#
|
||||
# Example:
|
||||
# bash scripts/deployment/deploy-mainnet-public-dodo-wave1-pool.sh \
|
||||
# --pair=cweurc-usdc \
|
||||
# --initial-price=1151700000000000000 \
|
||||
# --base-amount=1250000 \
|
||||
# --quote-amount=1439625 \
|
||||
# --mint-base-amount=1300000
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
source "${PROJECT_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd cast
|
||||
|
||||
PAIR=""
|
||||
INITIAL_PRICE=""
|
||||
BASE_AMOUNT=""
|
||||
QUOTE_AMOUNT=""
|
||||
MINT_BASE_AMOUNT="0"
|
||||
FEE_BPS="10"
|
||||
K_VALUE="0"
|
||||
OPEN_TWAP="false"
|
||||
DRY_RUN=0
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--pair=*) PAIR="${arg#*=}" ;;
|
||||
--initial-price=*) INITIAL_PRICE="${arg#*=}" ;;
|
||||
--base-amount=*) BASE_AMOUNT="${arg#*=}" ;;
|
||||
--quote-amount=*) QUOTE_AMOUNT="${arg#*=}" ;;
|
||||
--mint-base-amount=*) MINT_BASE_AMOUNT="${arg#*=}" ;;
|
||||
--fee-bps=*) FEE_BPS="${arg#*=}" ;;
|
||||
--k=*) K_VALUE="${arg#*=}" ;;
|
||||
--open-twap=*) OPEN_TWAP="${arg#*=}" ;;
|
||||
--dry-run) DRY_RUN=1 ;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$PAIR" || -z "$INITIAL_PRICE" || -z "$BASE_AMOUNT" || -z "$QUOTE_AMOUNT" ]]; then
|
||||
echo "[fail] required args: --pair, --initial-price, --base-amount, --quote-amount" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-}"
|
||||
INTEGRATION="${DODO_PMM_INTEGRATION_MAINNET:-}"
|
||||
USDC_MAINNET="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
|
||||
if [[ -z "$RPC_URL" || -z "$PRIVATE_KEY" || -z "$INTEGRATION" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC, PRIVATE_KEY, and DODO_PMM_INTEGRATION_MAINNET are required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEPLOYER="$(cast wallet address --private-key "$PRIVATE_KEY")"
|
||||
|
||||
BASE_SYMBOL=""
|
||||
BASE_TOKEN=""
|
||||
POOL_ENV_KEY=""
|
||||
PAIR_LABEL=""
|
||||
|
||||
case "$PAIR" in
|
||||
cweurc-usdc)
|
||||
BASE_SYMBOL="cWEURC"
|
||||
BASE_TOKEN="${CWEURC_MAINNET:-}"
|
||||
POOL_ENV_KEY="POOL_CWEURC_USDC_MAINNET"
|
||||
PAIR_LABEL="cWEURC/USDC"
|
||||
;;
|
||||
cweurt-usdc)
|
||||
BASE_SYMBOL="cWEURT"
|
||||
BASE_TOKEN="${CWEURT_MAINNET:-}"
|
||||
POOL_ENV_KEY="POOL_CWEURT_USDC_MAINNET"
|
||||
PAIR_LABEL="cWEURT/USDC"
|
||||
;;
|
||||
cwgbpc-usdc)
|
||||
BASE_SYMBOL="cWGBPC"
|
||||
BASE_TOKEN="${CWGBPC_MAINNET:-}"
|
||||
POOL_ENV_KEY="POOL_CWGBPC_USDC_MAINNET"
|
||||
PAIR_LABEL="cWGBPC/USDC"
|
||||
;;
|
||||
cwgbpt-usdc)
|
||||
BASE_SYMBOL="cWGBPT"
|
||||
BASE_TOKEN="${CWGBPT_MAINNET:-}"
|
||||
POOL_ENV_KEY="POOL_CWGBPT_USDC_MAINNET"
|
||||
PAIR_LABEL="cWGBPT/USDC"
|
||||
;;
|
||||
cwaudc-usdc)
|
||||
BASE_SYMBOL="cWAUDC"
|
||||
BASE_TOKEN="${CWAUDC_MAINNET:-}"
|
||||
POOL_ENV_KEY="POOL_CWAUDC_USDC_MAINNET"
|
||||
PAIR_LABEL="cWAUDC/USDC"
|
||||
;;
|
||||
cwjpyc-usdc)
|
||||
BASE_SYMBOL="cWJPYC"
|
||||
BASE_TOKEN="${CWJPYC_MAINNET:-}"
|
||||
POOL_ENV_KEY="POOL_CWJPYC_USDC_MAINNET"
|
||||
PAIR_LABEL="cWJPYC/USDC"
|
||||
;;
|
||||
cwchfc-usdc)
|
||||
BASE_SYMBOL="cWCHFC"
|
||||
BASE_TOKEN="${CWCHFC_MAINNET:-}"
|
||||
POOL_ENV_KEY="POOL_CWCHFC_USDC_MAINNET"
|
||||
PAIR_LABEL="cWCHFC/USDC"
|
||||
;;
|
||||
cwcadc-usdc)
|
||||
BASE_SYMBOL="cWCADC"
|
||||
BASE_TOKEN="${CWCADC_MAINNET:-}"
|
||||
POOL_ENV_KEY="POOL_CWCADC_USDC_MAINNET"
|
||||
PAIR_LABEL="cWCADC/USDC"
|
||||
;;
|
||||
cwxauc-usdc)
|
||||
BASE_SYMBOL="cWXAUC"
|
||||
BASE_TOKEN="${CWXAUC_MAINNET:-}"
|
||||
POOL_ENV_KEY="POOL_CWXAUC_USDC_MAINNET"
|
||||
PAIR_LABEL="cWXAUC/USDC"
|
||||
;;
|
||||
cwxaut-usdc)
|
||||
BASE_SYMBOL="cWXAUT"
|
||||
BASE_TOKEN="${CWXAUT_MAINNET:-}"
|
||||
POOL_ENV_KEY="POOL_CWXAUT_USDC_MAINNET"
|
||||
PAIR_LABEL="cWXAUT/USDC"
|
||||
;;
|
||||
*)
|
||||
echo "[fail] unsupported pair: $PAIR" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -z "$BASE_TOKEN" ]]; then
|
||||
echo "[fail] base token address missing in .env for pair: $PAIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
parse_tx_hash() {
|
||||
local output="$1"
|
||||
local tx_hash
|
||||
tx_hash="$(printf '%s\n' "$output" | grep -E '^0x[0-9a-fA-F]{64}$' | tail -n1 || true)"
|
||||
if [[ -z "$tx_hash" ]]; then
|
||||
tx_hash="$(printf '%s\n' "$output" | grep -E '^transactionHash[[:space:]]+0x[0-9a-fA-F]{64}$' | awk '{print $2}' | tail -n1 || true)"
|
||||
fi
|
||||
if [[ -z "$tx_hash" ]]; then
|
||||
return 1
|
||||
fi
|
||||
printf '%s\n' "$tx_hash"
|
||||
}
|
||||
|
||||
bool_to_cli() {
|
||||
if [[ "$1" == "true" ]]; then
|
||||
printf 'true'
|
||||
else
|
||||
printf 'false'
|
||||
fi
|
||||
}
|
||||
|
||||
existing_pool="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$BASE_TOKEN" "$USDC_MAINNET" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
base_balance_before="$(cast call "$BASE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
quote_balance_before="$(cast call "$USDC_MAINNET" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
base_allowance_before="$(cast call "$BASE_TOKEN" 'allowance(address,address)(uint256)' "$DEPLOYER" "$INTEGRATION" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
quote_allowance_before="$(cast call "$USDC_MAINNET" 'allowance(address,address)(uint256)' "$DEPLOYER" "$INTEGRATION" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
create_gas="0"
|
||||
if [[ "$existing_pool" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
create_gas="$(cast estimate "$INTEGRATION" \
|
||||
'createPool(address,address,uint256,uint256,uint256,bool)(address)' \
|
||||
"$BASE_TOKEN" "$USDC_MAINNET" "$FEE_BPS" "$INITIAL_PRICE" "$K_VALUE" "$(bool_to_cli "$OPEN_TWAP")" \
|
||||
--from "$DEPLOYER" \
|
||||
--rpc-url "$RPC_URL")"
|
||||
fi
|
||||
|
||||
if (( base_balance_before < BASE_AMOUNT )); then
|
||||
base_balance_after_mint=$(( base_balance_before + MINT_BASE_AMOUNT ))
|
||||
if (( MINT_BASE_AMOUNT == 0 || base_balance_after_mint < BASE_AMOUNT )); then
|
||||
echo "[fail] insufficient ${BASE_SYMBOL} balance: have=$base_balance_before need=$BASE_AMOUNT mint_base_amount=$MINT_BASE_AMOUNT" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if (( quote_balance_before < QUOTE_AMOUNT )); then
|
||||
echo "[fail] insufficient USDC balance: have=$quote_balance_before need=$QUOTE_AMOUNT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( DRY_RUN == 1 )); then
|
||||
echo "pair=$PAIR_LABEL"
|
||||
echo "baseToken=$BASE_TOKEN"
|
||||
echo "quoteToken=$USDC_MAINNET"
|
||||
echo "integration=$INTEGRATION"
|
||||
echo "existingPool=$existing_pool"
|
||||
echo "poolEnvKey=$POOL_ENV_KEY"
|
||||
echo "initialPrice=$INITIAL_PRICE"
|
||||
echo "feeBps=$FEE_BPS"
|
||||
echo "k=$K_VALUE"
|
||||
echo "openTwap=$OPEN_TWAP"
|
||||
echo "createGas=$create_gas"
|
||||
echo "baseAmount=$BASE_AMOUNT"
|
||||
echo "quoteAmount=$QUOTE_AMOUNT"
|
||||
echo "mintBaseAmount=$MINT_BASE_AMOUNT"
|
||||
echo "baseBalanceBefore=$base_balance_before"
|
||||
echo "quoteBalanceBefore=$quote_balance_before"
|
||||
echo "baseAllowanceBefore=$base_allowance_before"
|
||||
echo "quoteAllowanceBefore=$quote_allowance_before"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mint_tx=""
|
||||
create_tx=""
|
||||
approve_base_tx=""
|
||||
approve_quote_tx=""
|
||||
add_liquidity_tx=""
|
||||
|
||||
if (( MINT_BASE_AMOUNT > 0 )); then
|
||||
mint_output="$(
|
||||
cast send "$BASE_TOKEN" \
|
||||
'mint(address,uint256)' \
|
||||
"$DEPLOYER" "$MINT_BASE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
mint_tx="$(parse_tx_hash "$mint_output")"
|
||||
fi
|
||||
|
||||
if [[ "$existing_pool" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
create_output="$(
|
||||
cast send "$INTEGRATION" \
|
||||
'createPool(address,address,uint256,uint256,uint256,bool)(address)' \
|
||||
"$BASE_TOKEN" "$USDC_MAINNET" "$FEE_BPS" "$INITIAL_PRICE" "$K_VALUE" "$(bool_to_cli "$OPEN_TWAP")" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
create_tx="$(parse_tx_hash "$create_output")"
|
||||
existing_pool="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$BASE_TOKEN" "$USDC_MAINNET" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
fi
|
||||
|
||||
if [[ "$existing_pool" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
echo "[fail] pool creation did not yield a pool address" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( base_allowance_before < BASE_AMOUNT )); then
|
||||
approve_base_output="$(
|
||||
cast send "$BASE_TOKEN" \
|
||||
'approve(address,uint256)(bool)' \
|
||||
"$INTEGRATION" "$BASE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
approve_base_tx="$(parse_tx_hash "$approve_base_output")"
|
||||
fi
|
||||
|
||||
if (( quote_allowance_before < QUOTE_AMOUNT )); then
|
||||
approve_quote_output="$(
|
||||
cast send "$USDC_MAINNET" \
|
||||
'approve(address,uint256)(bool)' \
|
||||
"$INTEGRATION" "$QUOTE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
approve_quote_tx="$(parse_tx_hash "$approve_quote_output")"
|
||||
fi
|
||||
|
||||
add_liquidity_output="$(
|
||||
cast send "$INTEGRATION" \
|
||||
'addLiquidity(address,uint256,uint256)(uint256,uint256,uint256)' \
|
||||
"$existing_pool" "$BASE_AMOUNT" "$QUOTE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
add_liquidity_tx="$(parse_tx_hash "$add_liquidity_output")"
|
||||
|
||||
reserves="$(cast call "$existing_pool" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL")"
|
||||
base_reserve="$(printf '%s\n' "$reserves" | sed -n '1p' | awk '{print $1}')"
|
||||
quote_reserve="$(printf '%s\n' "$reserves" | sed -n '2p' | awk '{print $1}')"
|
||||
base_balance_after="$(cast call "$BASE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
quote_balance_after="$(cast call "$USDC_MAINNET" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
echo "pair=$PAIR_LABEL"
|
||||
echo "poolEnvKey=$POOL_ENV_KEY"
|
||||
echo "pool=$existing_pool"
|
||||
echo "baseToken=$BASE_TOKEN"
|
||||
echo "quoteToken=$USDC_MAINNET"
|
||||
echo "initialPrice=$INITIAL_PRICE"
|
||||
echo "feeBps=$FEE_BPS"
|
||||
echo "k=$K_VALUE"
|
||||
echo "openTwap=$OPEN_TWAP"
|
||||
echo "baseAmount=$BASE_AMOUNT"
|
||||
echo "quoteAmount=$QUOTE_AMOUNT"
|
||||
echo "mintBaseAmount=$MINT_BASE_AMOUNT"
|
||||
echo "mintTx=${mint_tx:-none}"
|
||||
echo "createTx=${create_tx:-none}"
|
||||
echo "approveBaseTx=${approve_base_tx:-none}"
|
||||
echo "approveQuoteTx=${approve_quote_tx:-none}"
|
||||
echo "addLiquidityTx=$add_liquidity_tx"
|
||||
echo "baseReserve=$base_reserve"
|
||||
echo "quoteReserve=$quote_reserve"
|
||||
echo "baseBalanceBefore=$base_balance_before"
|
||||
echo "baseBalanceAfter=$base_balance_after"
|
||||
echo "quoteBalanceBefore=$quote_balance_before"
|
||||
echo "quoteBalanceAfter=$quote_balance_after"
|
||||
@@ -67,6 +67,55 @@ pct_exec() {
|
||||
run_pct "exec $VMID -- $*"
|
||||
}
|
||||
|
||||
normalize_studio_sources() {
|
||||
local compose_path="$APP_DIR/docker-compose.yml"
|
||||
local app_path="$APP_DIR/src/fusionai_creator/api/app.py"
|
||||
pct_exec "bash -lc 'python3 - <<\"PY\"
|
||||
from pathlib import Path
|
||||
|
||||
compose_path = Path(\"$compose_path\")
|
||||
if compose_path.exists():
|
||||
text = compose_path.read_text()
|
||||
updated = text.replace(\"version: \\\"3.9\\\"\\n\\n\", \"\", 1)
|
||||
updated = updated.replace(
|
||||
\" test: [\\\"CMD\\\", \\\"curl\\\", \\\"-f\\\", \\\"http://localhost:8000/health\\\"]\",
|
||||
\" test: [\\\"CMD\\\", \\\"python3\\\", \\\"-c\\\", \\\"import urllib.request; urllib.request.urlopen(\\\\\\\"http://localhost:8000/health\\\\\\\").read()\\\"]\",
|
||||
1,
|
||||
)
|
||||
if updated != text:
|
||||
compose_path.write_text(updated)
|
||||
|
||||
app_path = Path(\"$app_path\")
|
||||
if app_path.exists():
|
||||
text = app_path.read_text()
|
||||
updated = text
|
||||
|
||||
import_line = \"from fastapi.responses import RedirectResponse\\n\"
|
||||
anchor = \"from fastapi.staticfiles import StaticFiles\\n\"
|
||||
if import_line not in updated and anchor in updated:
|
||||
updated = updated.replace(anchor, anchor + import_line, 1)
|
||||
|
||||
route_block = (
|
||||
\"@app.api_route(\\\"/\\\", methods=[\\\"GET\\\", \\\"HEAD\\\"], include_in_schema=False)\\n\"
|
||||
\"def studio_root():\\n\"
|
||||
\" return RedirectResponse(url=\\\"/studio/\\\", status_code=302)\\n\\n\\n\"
|
||||
)
|
||||
legacy_route = \"@app.get(\\\"/\\\", include_in_schema=False)\\ndef studio_root():\"
|
||||
route_anchor = \"@app.post(\\\"/jobs\\\", dependencies=[Depends(_verify_api_key), Depends(_rate_limit)])\\n\"
|
||||
if legacy_route in updated:
|
||||
updated = updated.replace(
|
||||
legacy_route,
|
||||
\"@app.api_route(\\\\\\\"/\\\\\\\", methods=[\\\\\\\"GET\\\\\\\", \\\\\\\"HEAD\\\\\\\"], include_in_schema=False)\\ndef studio_root():\",
|
||||
1,
|
||||
)
|
||||
elif \"def studio_root()\" not in updated and route_anchor in updated:
|
||||
updated = updated.replace(route_anchor, route_block + route_anchor, 1)
|
||||
|
||||
if updated != text:
|
||||
app_path.write_text(updated)
|
||||
PY'"
|
||||
}
|
||||
|
||||
echo "=== Sankofa Studio LXC ($VMID) — $HOSTNAME ==="
|
||||
echo "URL: https://studio.sankofa.nexus → http://${IP}:8000"
|
||||
echo "IP: $IP | Memory: ${MEMORY_MB}MB | Cores: $CORES | Disk: ${DISK_GB}G"
|
||||
@@ -147,8 +196,13 @@ if [[ -n "$ENV_FILE" && -f "$ENV_FILE" ]]; then
|
||||
run_pct "push $VMID $ENV_FILE $APP_DIR/.env"
|
||||
fi
|
||||
|
||||
echo "Normalizing FusionAI Creator app and compose files for clean Studio routing and health checks..."
|
||||
normalize_studio_sources
|
||||
|
||||
echo "Starting FusionAI Creator stack (docker compose up -d)..."
|
||||
pct_exec "bash -c 'cd \"$APP_DIR\" && docker compose up -d'"
|
||||
echo "Setting FusionAI Creator containers to restart automatically after Docker/container restarts..."
|
||||
pct_exec "bash -c 'cd \"$APP_DIR\" && docker compose ps -q | xargs -r docker update --restart unless-stopped > /dev/null'"
|
||||
|
||||
echo ""
|
||||
echo "Done. Verify: curl -s http://${IP}:8000/health"
|
||||
|
||||
44
scripts/deployment/deploy-simple-erc3156-flash-vault-chain138.sh
Executable file
44
scripts/deployment/deploy-simple-erc3156-flash-vault-chain138.sh
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deploy SimpleERC3156FlashVault on Chain 138 via Foundry (broadcast).
|
||||
#
|
||||
# Requires: forge, PRIVATE_KEY in env (repo root load-project-env loads .env + smom-dbis-138/.env).
|
||||
#
|
||||
# Optional env:
|
||||
# FLASH_VAULT_OWNER, FLASH_VAULT_FEE_BPS (default 5), FLASH_VAULT_TOKEN, FLASH_VAULT_SEED_AMOUNT (raw units)
|
||||
#
|
||||
# Usage:
|
||||
# source scripts/lib/load-project-env.sh
|
||||
# ./scripts/deployment/deploy-simple-erc3156-flash-vault-chain138.sh
|
||||
# ./scripts/deployment/deploy-simple-erc3156-flash-vault-chain138.sh --dry-run
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
SMOM="$PROJECT_ROOT/smom-dbis-138"
|
||||
|
||||
if [[ -f "$PROJECT_ROOT/scripts/lib/load-project-env.sh" ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
RPC="${RPC_URL_138:-}"
|
||||
if [[ -z "$RPC" ]]; then
|
||||
echo "ERROR: Set RPC_URL_138 (e.g. http://192.168.11.211:8545)" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${PRIVATE_KEY:-}" ]]; then
|
||||
echo "ERROR: PRIVATE_KEY must be set for --broadcast" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$SMOM"
|
||||
EXTRA=()
|
||||
if [[ "${1:-}" == "--dry-run" ]]; then
|
||||
EXTRA=(-vvvv)
|
||||
else
|
||||
EXTRA=(--broadcast -vvvv)
|
||||
fi
|
||||
|
||||
exec forge script script/deploy/DeploySimpleERC3156FlashVault.s.sol:DeploySimpleERC3156FlashVault \
|
||||
--rpc-url "$RPC" "${EXTRA[@]}"
|
||||
@@ -110,7 +110,7 @@ fi
|
||||
|
||||
cd "$SMOM"
|
||||
export RPC_URL_138="$RPC"
|
||||
export DODO_PMM_INTEGRATION="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-0x5BDc62f1ae7D630c37A8B363a1d49845356Ee72d}}"
|
||||
export DODO_PMM_INTEGRATION="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-0x86ADA6Ef91A3B450F89f2b751e93B1b7A3218895}}"
|
||||
|
||||
# Skip TransactionMirror deploy if already deployed at TRANSACTION_MIRROR_ADDRESS or if --skip-mirror
|
||||
MIRROR_ADDR="${TRANSACTION_MIRROR_ADDRESS:-}"
|
||||
|
||||
131
scripts/deployment/generate-chain138-keeper-key.sh
Executable file
131
scripts/deployment/generate-chain138-keeper-key.sh
Executable file
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
SECURE_DIR_DEFAULT="${HOME}/.secure-secrets"
|
||||
SECRET_FILE_DEFAULT="${SECURE_DIR_DEFAULT}/chain138-keeper.env"
|
||||
EXPORT_FILE_DEFAULT="${PROJECT_ROOT}/.env.keeper.local"
|
||||
|
||||
SECRET_FILE="${KEEPER_SECRET_FILE:-$SECRET_FILE_DEFAULT}"
|
||||
EXPORT_FILE="${KEEPER_EXPORT_FILE:-$EXPORT_FILE_DEFAULT}"
|
||||
FORCE=0
|
||||
NO_EXPORT=0
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $(basename "$0") [--force] [--no-export-file] [--secret-file PATH] [--export-file PATH]
|
||||
|
||||
Generates a dedicated Chain 138 keeper signer, stores the raw private key outside the repo,
|
||||
and writes a gitignored local helper that sources it.
|
||||
|
||||
Defaults:
|
||||
secret file: $SECRET_FILE_DEFAULT
|
||||
export file: $EXPORT_FILE_DEFAULT
|
||||
|
||||
The secret file contains:
|
||||
KEEPER_PRIVATE_KEY=0x...
|
||||
KEEPER_SIGNER_ADDRESS=0x...
|
||||
|
||||
The export file does not contain the secret. It only sources the secret file.
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--secret-file)
|
||||
SECRET_FILE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--export-file)
|
||||
EXPORT_FILE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--force)
|
||||
FORCE=1
|
||||
shift
|
||||
;;
|
||||
--no-export-file)
|
||||
NO_EXPORT=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
command -v openssl >/dev/null 2>&1 || { echo "openssl is required" >&2; exit 1; }
|
||||
command -v cast >/dev/null 2>&1 || { echo "cast is required" >&2; exit 1; }
|
||||
|
||||
if [[ -f "$SECRET_FILE" && "$FORCE" -ne 1 ]]; then
|
||||
echo "Refusing to overwrite existing secret file: $SECRET_FILE" >&2
|
||||
echo "Re-run with --force to replace it." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
umask 077
|
||||
mkdir -p "$(dirname "$SECRET_FILE")"
|
||||
chmod 700 "$(dirname "$SECRET_FILE")" 2>/dev/null || true
|
||||
|
||||
KEEPER_PRIVATE_KEY=""
|
||||
KEEPER_SIGNER_ADDRESS=""
|
||||
for _ in $(seq 1 8); do
|
||||
candidate="0x$(openssl rand -hex 32)"
|
||||
if addr="$(cast wallet address --private-key "$candidate" 2>/dev/null)"; then
|
||||
KEEPER_PRIVATE_KEY="$candidate"
|
||||
KEEPER_SIGNER_ADDRESS="$addr"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z "$KEEPER_PRIVATE_KEY" || -z "$KEEPER_SIGNER_ADDRESS" ]]; then
|
||||
echo "Failed to generate a valid keeper private key" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat >"$SECRET_FILE" <<EOF
|
||||
# Generated by $(basename "$0") on $(date -Iseconds)
|
||||
KEEPER_PRIVATE_KEY=$KEEPER_PRIVATE_KEY
|
||||
KEEPER_SIGNER_ADDRESS=$KEEPER_SIGNER_ADDRESS
|
||||
EOF
|
||||
chmod 600 "$SECRET_FILE"
|
||||
|
||||
if [[ "$NO_EXPORT" -ne 1 ]]; then
|
||||
cat >"$EXPORT_FILE" <<EOF
|
||||
# Generated by $(basename "$0") on $(date -Iseconds)
|
||||
# Local helper only; this file contains no secret material.
|
||||
export KEEPER_SECRET_FILE="${SECRET_FILE}"
|
||||
if [ -f "\${KEEPER_SECRET_FILE}" ]; then
|
||||
set -a
|
||||
# shellcheck source=/dev/null
|
||||
source "\${KEEPER_SECRET_FILE}"
|
||||
set +a
|
||||
fi
|
||||
EOF
|
||||
chmod 600 "$EXPORT_FILE"
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
Keeper signer generated.
|
||||
Address: $KEEPER_SIGNER_ADDRESS
|
||||
Secret: $SECRET_FILE
|
||||
EOF
|
||||
|
||||
if [[ "$NO_EXPORT" -ne 1 ]]; then
|
||||
cat <<EOF
|
||||
Export: $EXPORT_FILE
|
||||
|
||||
Next:
|
||||
1. source "$EXPORT_FILE"
|
||||
2. grant KEEPER_ROLE / allow the signer where required
|
||||
3. install KEEPER_PRIVATE_KEY on the runtime that submits performUpkeep()
|
||||
EOF
|
||||
fi
|
||||
198
scripts/deployment/grant-cw-bridge-roles-on-chain.sh
Executable file
198
scripts/deployment/grant-cw-bridge-roles-on-chain.sh
Executable file
@@ -0,0 +1,198 @@
|
||||
#!/usr/bin/env bash
|
||||
# Grant MINTER_ROLE and BURNER_ROLE on CompliantWrappedToken (cW*) to the chain CW bridge
|
||||
# when the deployer still has DEFAULT_ADMIN_ROLE and roles are not frozen.
|
||||
#
|
||||
# Use after DeployCWTokens or when verify shows MINTER=false BURNER=false for some symbols.
|
||||
# See: docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md
|
||||
#
|
||||
# Usage (repo root):
|
||||
# ./scripts/deployment/grant-cw-bridge-roles-on-chain.sh AVALANCHE [--dry-run]
|
||||
# ./scripts/deployment/grant-cw-bridge-roles-on-chain.sh MAINNET [--dry-run]
|
||||
#
|
||||
# Env: PRIVATE_KEY, CW_BRIDGE_<CHAIN>, <CHAIN>_RPC from smom-dbis-138/.env (via dotenv).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
SMOM="${PROJECT_ROOT}/smom-dbis-138"
|
||||
|
||||
CHAIN="${1:-}"
|
||||
DRY_RUN=false
|
||||
[[ "${2:-}" == "--dry-run" ]] && DRY_RUN=true
|
||||
|
||||
if [[ -z "$CHAIN" ]]; then
|
||||
echo "Usage: $0 <CHAIN_NAME> [--dry-run]" >&2
|
||||
echo " CHAIN_NAME: MAINNET CRONOS BSC POLYGON GNOSIS AVALANCHE BASE ARBITRUM OPTIMISM CELO WEMIX" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -f "$SMOM/.env" ]]; then
|
||||
echo "Missing $SMOM/.env" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f "$SMOM/scripts/lib/deployment/dotenv.sh" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$SMOM/scripts/lib/deployment/dotenv.sh"
|
||||
load_deployment_env --repo-root "$SMOM"
|
||||
else
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$SMOM/.env"
|
||||
set +a
|
||||
fi
|
||||
|
||||
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY required in smom-dbis-138/.env" >&2; exit 1; }
|
||||
command -v cast >/dev/null 2>&1 || { echo "cast (foundry) required" >&2; exit 1; }
|
||||
command -v python3 >/dev/null 2>&1 || { echo "python3 required (gas bump)" >&2; exit 1; }
|
||||
|
||||
declare -A CHAIN_NAME_TO_ID=(
|
||||
[MAINNET]=1 [CRONOS]=25 [BSC]=56 [POLYGON]=137 [GNOSIS]=100
|
||||
[AVALANCHE]=43114 [BASE]=8453 [ARBITRUM]=42161 [OPTIMISM]=10 [CELO]=42220 [WEMIX]=1111
|
||||
)
|
||||
|
||||
chain_id="${CHAIN_NAME_TO_ID[$CHAIN]:-}"
|
||||
if [[ -z "$chain_id" ]]; then
|
||||
echo "Unknown chain: $CHAIN" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
bridge_var="CW_BRIDGE_${CHAIN}"
|
||||
bridge="${!bridge_var:-}"
|
||||
if [[ -z "$bridge" ]]; then
|
||||
echo "SKIP: CW_BRIDGE_${CHAIN} is not set (nothing to grant on this chain)."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
rpc=""
|
||||
case "$CHAIN" in
|
||||
MAINNET) rpc="${ETH_MAINNET_RPC_URL:-${ETHEREUM_MAINNET_RPC:-}}" ;;
|
||||
CRONOS) rpc="${CRONOS_RPC_URL:-${CRONOS_RPC:-}}" ;;
|
||||
BSC) rpc="${BSC_RPC_URL:-}" ;;
|
||||
POLYGON) rpc="${POLYGON_MAINNET_RPC:-${POLYGON_RPC_URL:-}}" ;;
|
||||
GNOSIS) rpc="${GNOSIS_RPC:-}" ;;
|
||||
AVALANCHE) rpc="${AVALANCHE_RPC_URL:-}" ;;
|
||||
BASE) rpc="${BASE_MAINNET_RPC:-}" ;;
|
||||
ARBITRUM) rpc="${ARBITRUM_MAINNET_RPC:-${ARBITRUM_MAINNET_RPC_URL:-${ARBITRUM_RPC_URL:-}}}" ;;
|
||||
OPTIMISM) rpc="${OPTIMISM_MAINNET_RPC:-}" ;;
|
||||
CELO) rpc="${CELO_RPC:-${CELO_MAINNET_RPC:-}}" ;;
|
||||
WEMIX) rpc="${WEMIX_RPC:-${WEMIX_MAINNET_RPC:-}}" ;;
|
||||
esac
|
||||
|
||||
if [[ -z "$rpc" ]]; then
|
||||
echo "No RPC URL for $CHAIN (set e.g. AVALANCHE_RPC_URL)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Busy networks: base fee can exceed cast defaults — bump legacy gas price (percent, default 150%).
|
||||
CW_GRANT_GAS_BUMP_PCT="${CW_GRANT_GAS_BUMP_PCT:-150}"
|
||||
_gas_price_wei() {
|
||||
local base
|
||||
base="$(cast gas-price --rpc-url "$rpc" 2>/dev/null | head -1 | tr -d '[:space:]')"
|
||||
[[ -z "$base" || ! "$base" =~ ^[0-9]+$ ]] && base="50000000000"
|
||||
python3 -c "import sys; b=int(sys.argv[1]); p=int(sys.argv[2]); print(max(b * p // 100, b + 10**9))" "$base" "$CW_GRANT_GAS_BUMP_PCT"
|
||||
}
|
||||
GAS_PRICE_WEI="${CW_GRANT_GAS_PRICE_WEI:-$(_gas_price_wei)}"
|
||||
|
||||
DEPLOYER="$(cast wallet address "$PRIVATE_KEY" 2>/dev/null)" || { echo "Invalid PRIVATE_KEY" >&2; exit 1; }
|
||||
MINTER_ROLE="$(cast keccak "MINTER_ROLE")"
|
||||
BURNER_ROLE="$(cast keccak "BURNER_ROLE")"
|
||||
# OpenZeppelin AccessControl DEFAULT_ADMIN_ROLE
|
||||
DEFAULT_ADMIN_ROLE="0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
|
||||
CW_TOKEN_PREFIXES=(CWUSDT CWUSDC CWAUSDT CWEURC CWEURT CWGBPC CWGBPT CWAUDC CWJPYC CWCHFC CWCADC CWXAUC CWXAUT)
|
||||
|
||||
resolve_token() {
|
||||
local prefix="$1"
|
||||
local chain_var="${prefix}_${CHAIN}"
|
||||
local address_var="${prefix}_ADDRESS_${chain_id}"
|
||||
if [[ -n "${!chain_var:-}" ]]; then
|
||||
printf '%s' "${!chain_var}"
|
||||
return
|
||||
fi
|
||||
if [[ -n "${!address_var:-}" ]]; then
|
||||
printf '%s' "${!address_var}"
|
||||
return
|
||||
fi
|
||||
printf ''
|
||||
}
|
||||
|
||||
echo "=== Grant cW* bridge roles: $CHAIN (chainId $chain_id) ==="
|
||||
echo "RPC: $rpc"
|
||||
echo "Bridge: $bridge"
|
||||
echo "From: $DEPLOYER"
|
||||
echo "Gas: legacy wei=$GAS_PRICE_WEI (override CW_GRANT_GAS_PRICE_WEI or bump CW_GRANT_GAS_BUMP_PCT)"
|
||||
if $DRY_RUN; then echo "MODE: dry-run (no transactions)"; fi
|
||||
echo ""
|
||||
|
||||
granted=0
|
||||
skipped=0
|
||||
failed=0
|
||||
|
||||
for prefix in "${CW_TOKEN_PREFIXES[@]}"; do
|
||||
token="$(resolve_token "$prefix")"
|
||||
[[ -z "$token" ]] && continue
|
||||
|
||||
# Some proxies revert on operationalRolesFrozen(); skip that probe and rely on grantRole + admin check.
|
||||
is_admin="$(cast call "$token" "hasRole(bytes32,address)(bool)" "$DEFAULT_ADMIN_ROLE" "$DEPLOYER" --rpc-url "$rpc" 2>/dev/null || echo "false")"
|
||||
if [[ "$is_admin" != "true" ]]; then
|
||||
echo " SKIP $prefix $token — deployer lacks DEFAULT_ADMIN_ROLE"
|
||||
skipped=$((skipped + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
hm="$(cast call "$token" "hasRole(bytes32,address)(bool)" "$MINTER_ROLE" "$bridge" --rpc-url "$rpc" 2>/dev/null || echo "false")"
|
||||
hb="$(cast call "$token" "hasRole(bytes32,address)(bool)" "$BURNER_ROLE" "$bridge" --rpc-url "$rpc" 2>/dev/null || echo "false")"
|
||||
|
||||
if [[ "$hm" == "true" && "$hb" == "true" ]]; then
|
||||
skipped=$((skipped + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
echo " FIX $prefix $token MINTER=$hm BURNER=$hb"
|
||||
|
||||
if $DRY_RUN; then
|
||||
[[ "$hm" != "true" ]] && echo " would: grantRole(MINTER_ROLE, bridge)"
|
||||
[[ "$hb" != "true" ]] && echo " would: grantRole(BURNER_ROLE, bridge)"
|
||||
granted=$((granted + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
_after_minter() {
|
||||
cast call "$token" "hasRole(bytes32,address)(bool)" "$MINTER_ROLE" "$bridge" --rpc-url "$rpc" 2>/dev/null | grep -q true
|
||||
}
|
||||
_after_burner() {
|
||||
cast call "$token" "hasRole(bytes32,address)(bool)" "$BURNER_ROLE" "$bridge" --rpc-url "$rpc" 2>/dev/null | grep -q true
|
||||
}
|
||||
|
||||
if [[ "$hm" != "true" ]]; then
|
||||
cast send "$token" "grantRole(bytes32,address)" "$MINTER_ROLE" "$bridge" \
|
||||
--rpc-url "$rpc" --private-key "$PRIVATE_KEY" --legacy --gas-price "$GAS_PRICE_WEI" >/dev/null 2>&1 || true
|
||||
sleep 2
|
||||
if _after_minter; then
|
||||
echo " OK MINTER_ROLE"
|
||||
else
|
||||
echo " FAIL MINTER_ROLE" >&2
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
fi
|
||||
if [[ "$hb" != "true" ]]; then
|
||||
cast send "$token" "grantRole(bytes32,address)" "$BURNER_ROLE" "$bridge" \
|
||||
--rpc-url "$rpc" --private-key "$PRIVATE_KEY" --legacy --gas-price "$GAS_PRICE_WEI" >/dev/null 2>&1 || true
|
||||
sleep 2
|
||||
if _after_burner; then
|
||||
echo " OK BURNER_ROLE"
|
||||
else
|
||||
echo " FAIL BURNER_ROLE" >&2
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
fi
|
||||
granted=$((granted + 1))
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Summary: chains=$CHAIN fixed_tokens=$granted skipped=$skipped tx_failures=$failed"
|
||||
if [[ "$failed" -gt 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# Push Chain 138 PMM mesh into Proxmox LXC and enable systemd.
|
||||
# Copies: pmm-mesh-6s-automation.sh, update-oracle-price.sh, smom-dbis-138/.env, and this host's cast binary.
|
||||
# Copies: pmm-mesh-6s-automation.sh, update-oracle-price.sh, smom-dbis-138/.env,
|
||||
# optional dedicated keeper env, and this host's cast binary.
|
||||
#
|
||||
# Run from repo root (LAN + SSH root@Proxmox BatchMode). Requires: cast in PATH, smom-dbis-138/.env.
|
||||
#
|
||||
@@ -28,6 +29,9 @@ TARGETS="${PMM_MESH_LXC_TARGETS:-192.168.11.11:3500 192.168.11.12:5700}"
|
||||
CAST_SRC="$(command -v cast || true)"
|
||||
[[ -x "$CAST_SRC" ]] || { echo "ERROR: cast not in PATH" >&2; exit 1; }
|
||||
[[ -f "$SMOM/.env" ]] || { echo "ERROR: missing $SMOM/.env" >&2; exit 1; }
|
||||
KEEPER_SECRET_SRC="${KEEPER_SECRET_FILE:-${HOME}/.secure-secrets/chain138-keeper.env}"
|
||||
HAS_KEEPER_SECRET=false
|
||||
[[ -f "$KEEPER_SECRET_SRC" ]] && HAS_KEEPER_SECRET=true
|
||||
|
||||
MESH_TGZ="$(mktemp /tmp/c138-mesh-XXXXXX.tgz)"
|
||||
cleanup() { rm -f "$MESH_TGZ" 2>/dev/null || true; }
|
||||
@@ -53,6 +57,9 @@ for pair in $TARGETS; do
|
||||
|
||||
scp -o BatchMode=yes -o ConnectTimeout=20 "$MESH_TGZ" "root@${host}:/tmp/c138-mesh-install.tgz"
|
||||
scp -o BatchMode=yes -o ConnectTimeout=20 "$CAST_SRC" "root@${host}:/tmp/cast-bin-lxc"
|
||||
if [[ "$HAS_KEEPER_SECRET" == true ]]; then
|
||||
scp -o BatchMode=yes -o ConnectTimeout=20 "$KEEPER_SECRET_SRC" "root@${host}:/tmp/c138-keeper.env"
|
||||
fi
|
||||
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=25 "root@${host}" \
|
||||
"VMID=${vmid} bash -s" <<'REMOTE'
|
||||
@@ -68,6 +75,12 @@ sleep 1
|
||||
|
||||
pct push "$VMID" /tmp/c138-mesh-install.tgz /var/tmp/c138-mesh.tgz
|
||||
pct push "$VMID" /tmp/cast-bin-lxc /var/tmp/cast-bin
|
||||
if [[ -f /tmp/c138-keeper.env ]]; then
|
||||
pct exec "$VMID" -- mkdir -p /root/.secure-secrets
|
||||
pct push "$VMID" /tmp/c138-keeper.env /root/.secure-secrets/chain138-keeper.env
|
||||
pct exec "$VMID" -- chmod 700 /root/.secure-secrets
|
||||
pct exec "$VMID" -- chmod 600 /root/.secure-secrets/chain138-keeper.env
|
||||
fi
|
||||
# Unprivileged LXCs may have /opt and /var/lib root-owned on host as nobody: use /var/tmp (writable as CT root).
|
||||
BASE=/var/tmp/chain138-mesh
|
||||
pct exec "$VMID" -- mkdir -p "$BASE/bin"
|
||||
@@ -128,6 +141,8 @@ Environment=ENABLE_MESH_ORACLE_TICK=1
|
||||
Environment=ENABLE_MESH_KEEPER_TICK=1
|
||||
Environment=ENABLE_MESH_PMM_READS=1
|
||||
Environment=ENABLE_MESH_WETH_READS=1
|
||||
EnvironmentFile=-/opt/oracle-publisher/.env
|
||||
EnvironmentFile=-/root/.secure-secrets/chain138-keeper.env
|
||||
EnvironmentFile=-/var/tmp/chain138-mesh/smom-dbis-138/.env
|
||||
ExecStart=/bin/bash /var/tmp/chain138-mesh/smom-dbis-138/scripts/reserve/pmm-mesh-6s-automation.sh
|
||||
Restart=always
|
||||
@@ -175,7 +190,7 @@ UNIT_HOST
|
||||
}
|
||||
fi
|
||||
|
||||
rm -f /tmp/c138-mesh-install.tgz /tmp/cast-bin-lxc
|
||||
rm -f /tmp/c138-mesh-install.tgz /tmp/cast-bin-lxc /tmp/c138-keeper.env
|
||||
REMOTE
|
||||
|
||||
done
|
||||
|
||||
175
scripts/deployment/keycloak-sankofa-ensure-it-admin-group.sh
Executable file
175
scripts/deployment/keycloak-sankofa-ensure-it-admin-group.sh
Executable file
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env bash
|
||||
# Create Keycloak group sankofa-it-admin (or SANKOFA_IT_ADMIN_GROUP_NAME) and map realm role
|
||||
# sankofa-it-admin onto it. Run after keycloak-sankofa-ensure-it-admin-role.sh.
|
||||
#
|
||||
# Usage: ./scripts/deployment/keycloak-sankofa-ensure-it-admin-group.sh [--dry-run]
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
# shellcheck source=/dev/null
|
||||
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
|
||||
if [ -f "$PROJECT_ROOT/.env" ]; then
|
||||
set +u
|
||||
set -a
|
||||
# shellcheck source=/dev/null
|
||||
source "$PROJECT_ROOT/.env" 2>/dev/null || true
|
||||
set +a
|
||||
set -u
|
||||
fi
|
||||
|
||||
PROXMOX_HOST="${PROXMOX_HOST:-${PROXMOX_HOST_R630_01:-192.168.11.11}}"
|
||||
KEYCLOAK_CT_VMID="${KEYCLOAK_CT_VMID:-${SANKOFA_KEYCLOAK_VMID:-7802}}"
|
||||
REALM="${KEYCLOAK_REALM:-master}"
|
||||
ADMIN_USER="${KEYCLOAK_ADMIN:-admin}"
|
||||
ADMIN_PASS="${KEYCLOAK_ADMIN_PASSWORD:-}"
|
||||
ROLE_NAME="${SANKOFA_IT_ADMIN_ROLE_NAME:-sankofa-it-admin}"
|
||||
GROUP_NAME="${SANKOFA_IT_ADMIN_GROUP_NAME:-sankofa-it-admin}"
|
||||
SSH_OPTS=(-o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15)
|
||||
|
||||
DRY=0
|
||||
[[ "${1:-}" == "--dry-run" ]] && DRY=1
|
||||
|
||||
if [ -z "$ADMIN_PASS" ]; then
|
||||
echo "KEYCLOAK_ADMIN_PASSWORD is not set in .env" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$DRY" = 1 ]; then
|
||||
echo "[dry-run] Would ensure group ${GROUP_NAME} + map realm role ${ROLE_NAME} in ${REALM}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ssh "${SSH_OPTS[@]}" "root@${PROXMOX_HOST}" \
|
||||
"pct exec ${KEYCLOAK_CT_VMID} -- env KC_PASS=\"${ADMIN_PASS}\" ADMUSER=\"${ADMIN_USER}\" REALM=\"${REALM}\" ROLE_NAME=\"${ROLE_NAME}\" GROUP_NAME=\"${GROUP_NAME}\" python3 -u -" <<'PY'
|
||||
import json
|
||||
import os
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
base = "http://127.0.0.1:8080"
|
||||
realm = os.environ["REALM"]
|
||||
role_name = os.environ["ROLE_NAME"]
|
||||
group_name = os.environ["GROUP_NAME"]
|
||||
admin_user = os.environ["ADMUSER"]
|
||||
password = os.environ["KC_PASS"]
|
||||
|
||||
|
||||
def post_form(url: str, data: dict) -> dict:
|
||||
body = urllib.parse.urlencode(data).encode()
|
||||
req = urllib.request.Request(url, data=body, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as resp:
|
||||
return json.loads(resp.read().decode())
|
||||
|
||||
|
||||
def req_json(method: str, url: str, headers: dict, data=None):
|
||||
body = None
|
||||
hdrs = dict(headers)
|
||||
if data is not None:
|
||||
body = json.dumps(data).encode()
|
||||
hdrs["Content-Type"] = "application/json"
|
||||
r = urllib.request.Request(url, data=body, headers=hdrs, method=method)
|
||||
with urllib.request.urlopen(r, timeout=120) as resp:
|
||||
return resp.read().decode()
|
||||
|
||||
|
||||
def req_json_ignore_404(method: str, url: str, headers: dict, data=None):
|
||||
body = None
|
||||
hdrs = dict(headers)
|
||||
if data is not None:
|
||||
body = json.dumps(data).encode()
|
||||
hdrs["Content-Type"] = "application/json"
|
||||
r = urllib.request.Request(url, data=body, headers=hdrs, method=method)
|
||||
try:
|
||||
with urllib.request.urlopen(r, timeout=120) as resp:
|
||||
return resp.getcode(), resp.read().decode()
|
||||
except urllib.error.HTTPError as e:
|
||||
return e.code, e.read().decode() if e.fp else ""
|
||||
|
||||
|
||||
tok = post_form(
|
||||
f"{base}/realms/master/protocol/openid-connect/token",
|
||||
{
|
||||
"grant_type": "password",
|
||||
"client_id": "admin-cli",
|
||||
"username": admin_user,
|
||||
"password": password,
|
||||
},
|
||||
)
|
||||
access = tok.get("access_token")
|
||||
if not access:
|
||||
raise SystemExit(f"token failed: {tok}")
|
||||
|
||||
h = {"Authorization": f"Bearer {access}"}
|
||||
|
||||
# Role id
|
||||
role_url = f"{base}/admin/realms/{realm}/roles/{urllib.parse.quote(role_name, safe='')}"
|
||||
req_r = urllib.request.Request(role_url, headers=h)
|
||||
try:
|
||||
with urllib.request.urlopen(req_r, timeout=60) as resp:
|
||||
role = json.loads(resp.read().decode())
|
||||
except urllib.error.HTTPError as e:
|
||||
raise SystemExit(
|
||||
f"Realm role {role_name!r} missing; run keycloak-sankofa-ensure-it-admin-role.sh first. HTTP {e.code}"
|
||||
) from e
|
||||
|
||||
role_id = role.get("id")
|
||||
if not role_id:
|
||||
raise SystemExit("role JSON missing id")
|
||||
|
||||
# Find or create group
|
||||
list_url = f"{base}/admin/realms/{realm}/groups?search={urllib.parse.quote(group_name)}"
|
||||
req_g = urllib.request.Request(list_url, headers=h)
|
||||
with urllib.request.urlopen(req_g, timeout=60) as resp:
|
||||
groups = json.loads(resp.read().decode())
|
||||
|
||||
gid = None
|
||||
for g in groups:
|
||||
if g.get("name") == group_name:
|
||||
gid = g.get("id")
|
||||
break
|
||||
|
||||
if not gid:
|
||||
try:
|
||||
req_json(
|
||||
"POST",
|
||||
f"{base}/admin/realms/{realm}/groups",
|
||||
h,
|
||||
{"name": group_name},
|
||||
)
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code != 409:
|
||||
raise
|
||||
with urllib.request.urlopen(
|
||||
urllib.request.Request(list_url, headers=h), timeout=60
|
||||
) as resp:
|
||||
groups = json.loads(resp.read().decode())
|
||||
for g in groups:
|
||||
if g.get("name") == group_name:
|
||||
gid = g.get("id")
|
||||
break
|
||||
|
||||
if not gid:
|
||||
raise SystemExit("failed to resolve group id after create")
|
||||
|
||||
# Map realm role to group (idempotent: POST may 204 or 409 depending on KC version)
|
||||
map_url = f"{base}/admin/realms/{realm}/groups/{gid}/role-mappings/realm"
|
||||
code, _body = req_json_ignore_404(
|
||||
"POST",
|
||||
map_url,
|
||||
h,
|
||||
[{"id": role_id, "name": role_name}],
|
||||
)
|
||||
if code in (200, 204):
|
||||
print(f"Mapped realm role {role_name!r} to group {group_name!r}.", flush=True)
|
||||
elif code == 409:
|
||||
print(f"Role likely already mapped to group {group_name!r}.", flush=True)
|
||||
else:
|
||||
print(f"POST role-mappings HTTP {code}: {_body[:500]}", flush=True)
|
||||
|
||||
print(
|
||||
f"Add IT users to group {group_name!r} in Admin Console (Groups → Members).",
|
||||
flush=True,
|
||||
)
|
||||
PY
|
||||
63
scripts/deployment/plan-gru-v2-wave1-chain138.sh
Normal file
63
scripts/deployment/plan-gru-v2-wave1-chain138.sh
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
# Print dry-run deployment commands for the Wave 1 GRU V2 Chain 138 plan.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/deployment/plan-gru-v2-wave1-chain138.sh
|
||||
# bash scripts/deployment/plan-gru-v2-wave1-chain138.sh --code=EUR
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
PLAN_JSON="${PROJECT_ROOT}/config/gru-v2-chain138-wave1-v2-plan.json"
|
||||
FILTER_CODE=""
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--code=*) FILTER_CODE="${arg#--code=}" ;;
|
||||
*)
|
||||
echo "Unknown argument: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "jq is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== GRU V2 Wave 1 Chain 138 plan ==="
|
||||
echo "Plan: $PLAN_JSON"
|
||||
echo ""
|
||||
|
||||
jq -c --arg filter "$FILTER_CODE" '
|
||||
.assets[]
|
||||
| select(($filter == "") or (.code == $filter))
|
||||
| . as $asset
|
||||
| .deployments[]
|
||||
| {
|
||||
code: $asset.code,
|
||||
tokenName,
|
||||
tokenSymbol,
|
||||
currencyCode,
|
||||
registryName,
|
||||
registrySymbol,
|
||||
currentCanonicalAddress
|
||||
}
|
||||
' "$PLAN_JSON" | while IFS= read -r row; do
|
||||
code="$(jq -r '.code' <<<"$row")"
|
||||
token_name="$(jq -r '.tokenName' <<<"$row")"
|
||||
token_symbol="$(jq -r '.tokenSymbol' <<<"$row")"
|
||||
currency_code="$(jq -r '.currencyCode' <<<"$row")"
|
||||
registry_name="$(jq -r '.registryName' <<<"$row")"
|
||||
registry_symbol="$(jq -r '.registrySymbol' <<<"$row")"
|
||||
current_address="$(jq -r '.currentCanonicalAddress' <<<"$row")"
|
||||
|
||||
echo "- ${code} :: ${token_symbol}"
|
||||
echo " legacy/current canonical: ${current_address}"
|
||||
printf " command: TOKEN_NAME=%q TOKEN_SYMBOL=%q CURRENCY_CODE=%q REGISTRY_NAME=%q REGISTRY_SYMBOL=%q bash %q --dry-run\n" \
|
||||
"$token_name" "$token_symbol" "$currency_code" "$registry_name" "$registry_symbol" \
|
||||
"${PROJECT_ROOT}/scripts/deployment/deploy-gru-v2-generic-chain138.sh"
|
||||
echo ""
|
||||
done
|
||||
188
scripts/deployment/plan-mainnet-cw-stabilization.sh
Executable file
188
scripts/deployment/plan-mainnet-cw-stabilization.sh
Executable file
@@ -0,0 +1,188 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Build an operator-safe stabilization plan for the live Mainnet cW/USD rails.
|
||||
# Reads wallet balances and current DODO PMM reserves, then prints:
|
||||
# - current live state
|
||||
# - suggested micro-support swap sizes
|
||||
# - reserve shortfalls versus a target matched-rail depth
|
||||
# - exact dry-run commands for the existing swap helper
|
||||
#
|
||||
# Example:
|
||||
# bash scripts/deployment/plan-mainnet-cw-stabilization.sh
|
||||
# TARGET_MATCHED_QUOTE_UNITS=1000 MICRO_BPS=25 bash scripts/deployment/plan-mainnet-cw-stabilization.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
source "${PROJECT_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd cast
|
||||
require_cmd python3
|
||||
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-}"
|
||||
|
||||
if [[ -z "$RPC_URL" || -z "$PRIVATE_KEY" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC and PRIVATE_KEY are required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEPLOYER="$(cast wallet address --private-key "$PRIVATE_KEY")"
|
||||
|
||||
MICRO_BPS="${MICRO_BPS:-25}"
|
||||
REBALANCE_BPS="${REBALANCE_BPS:-100}"
|
||||
TARGET_MATCHED_QUOTE_UNITS="${TARGET_MATCHED_QUOTE_UNITS:-1000}"
|
||||
|
||||
CWUSDT_MAINNET="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
||||
CWUSDC_MAINNET="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
USDC_MAINNET="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
USDT_MAINNET="0xdAC17F958D2ee523a2206206994597C13D831ec7"
|
||||
|
||||
POOL_CWUSDT_USDC_MAINNET="${POOL_CWUSDT_USDC_MAINNET:-0x27f3aE7EE71Be3d77bAf17d4435cF8B895DD25D2}"
|
||||
POOL_CWUSDC_USDC_MAINNET="${POOL_CWUSDC_USDC_MAINNET:-0x69776fc607e9edA8042e320e7e43f54d06c68f0E}"
|
||||
POOL_CWUSDT_USDT_MAINNET="${POOL_CWUSDT_USDT_MAINNET:-0x79156F6B7bf71a1B72D78189B540A89A6C13F6FC}"
|
||||
POOL_CWUSDC_USDT_MAINNET="${POOL_CWUSDC_USDT_MAINNET:-0xCC0fd27A40775c9AfcD2BBd3f7c902b0192c247A}"
|
||||
|
||||
to_units() {
|
||||
python3 - "$1" <<'PY'
|
||||
import sys
|
||||
raw = int(sys.argv[1])
|
||||
print(f"{raw / 1_000_000:.6f}")
|
||||
PY
|
||||
}
|
||||
|
||||
from_units_raw() {
|
||||
python3 - "$1" <<'PY'
|
||||
import sys
|
||||
units = float(sys.argv[1])
|
||||
print(int(round(units * 1_000_000)))
|
||||
PY
|
||||
}
|
||||
|
||||
read_balance() {
|
||||
cast call "$1" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}'
|
||||
}
|
||||
|
||||
read_mapping_pool() {
|
||||
cast call "${DODO_PMM_INTEGRATION_MAINNET:-}" 'pools(address,address)(address)' "$1" "$2" --rpc-url "$RPC_URL" 2>/dev/null | awk '{print $1}'
|
||||
}
|
||||
|
||||
resolve_pool() {
|
||||
local configured="$1"
|
||||
local base_token="$2"
|
||||
local quote_token="$3"
|
||||
local mapped
|
||||
mapped="$(read_mapping_pool "$base_token" "$quote_token")"
|
||||
if [[ -n "$mapped" && "$mapped" != "0x0000000000000000000000000000000000000000" ]]; then
|
||||
printf '%s\n' "$mapped"
|
||||
else
|
||||
printf '%s\n' "$configured"
|
||||
fi
|
||||
}
|
||||
|
||||
read_reserves() {
|
||||
local output
|
||||
output="$(cast call "$1" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL")"
|
||||
printf '%s\n' "$output" | sed -n '1p' | awk '{print $1}'
|
||||
printf '%s\n' "$output" | sed -n '2p' | awk '{print $1}'
|
||||
}
|
||||
|
||||
suggest_trade_raw() {
|
||||
python3 - "$1" "$2" <<'PY'
|
||||
import sys
|
||||
reserve = int(sys.argv[1])
|
||||
bps = int(sys.argv[2])
|
||||
trade = max(1, reserve * bps // 10_000)
|
||||
print(trade)
|
||||
PY
|
||||
}
|
||||
|
||||
POOL_CWUSDT_USDC_MAINNET="$(resolve_pool "$POOL_CWUSDT_USDC_MAINNET" "$CWUSDT_MAINNET" "$USDC_MAINNET")"
|
||||
POOL_CWUSDC_USDC_MAINNET="$(resolve_pool "$POOL_CWUSDC_USDC_MAINNET" "$CWUSDC_MAINNET" "$USDC_MAINNET")"
|
||||
POOL_CWUSDT_USDT_MAINNET="$(resolve_pool "$POOL_CWUSDT_USDT_MAINNET" "$CWUSDT_MAINNET" "$USDT_MAINNET")"
|
||||
POOL_CWUSDC_USDT_MAINNET="$(resolve_pool "$POOL_CWUSDC_USDT_MAINNET" "$CWUSDC_MAINNET" "$USDT_MAINNET")"
|
||||
|
||||
wallet_cwusdt_raw="$(read_balance "$CWUSDT_MAINNET")"
|
||||
wallet_cwusdc_raw="$(read_balance "$CWUSDC_MAINNET")"
|
||||
wallet_usdt_raw="$(read_balance "$USDT_MAINNET")"
|
||||
wallet_usdc_raw="$(read_balance "$USDC_MAINNET")"
|
||||
|
||||
read -r cwusdt_usdc_base cwusdt_usdc_quote < <(read_reserves "$POOL_CWUSDT_USDC_MAINNET" | xargs)
|
||||
read -r cwusdc_usdc_base cwusdc_usdc_quote < <(read_reserves "$POOL_CWUSDC_USDC_MAINNET" | xargs)
|
||||
read -r cwusdt_usdt_base cwusdt_usdt_quote < <(read_reserves "$POOL_CWUSDT_USDT_MAINNET" | xargs)
|
||||
read -r cwusdc_usdt_base cwusdc_usdt_quote < <(read_reserves "$POOL_CWUSDC_USDT_MAINNET" | xargs)
|
||||
|
||||
target_quote_raw="$(from_units_raw "$TARGET_MATCHED_QUOTE_UNITS")"
|
||||
|
||||
cwusdt_usdt_micro_raw="$(suggest_trade_raw "$cwusdt_usdt_quote" "$MICRO_BPS")"
|
||||
cwusdc_usdc_micro_raw="$(suggest_trade_raw "$cwusdc_usdc_quote" "$MICRO_BPS")"
|
||||
cwusdt_usdc_micro_raw="$(suggest_trade_raw "$cwusdt_usdc_quote" "$MICRO_BPS")"
|
||||
cwusdc_usdt_micro_raw="$(suggest_trade_raw "$cwusdc_usdt_quote" "$MICRO_BPS")"
|
||||
|
||||
cwusdt_usdt_rebalance_raw="$(suggest_trade_raw "$cwusdt_usdt_quote" "$REBALANCE_BPS")"
|
||||
cwusdc_usdc_rebalance_raw="$(suggest_trade_raw "$cwusdc_usdc_quote" "$REBALANCE_BPS")"
|
||||
|
||||
echo "=== Mainnet cW Stabilization Plan ==="
|
||||
echo "deployer=$DEPLOYER"
|
||||
echo "rpc=$RPC_URL"
|
||||
echo
|
||||
echo "Live wallet balances (6 decimals):"
|
||||
echo "- cWUSDT=$(to_units "$wallet_cwusdt_raw")"
|
||||
echo "- cWUSDC=$(to_units "$wallet_cwusdc_raw")"
|
||||
echo "- USDT=$(to_units "$wallet_usdt_raw")"
|
||||
echo "- USDC=$(to_units "$wallet_usdc_raw")"
|
||||
echo
|
||||
echo "Live matched rails (base/quote, 6 decimals):"
|
||||
echo "- cWUSDT/USDT: $(to_units "$cwusdt_usdt_base") / $(to_units "$cwusdt_usdt_quote")"
|
||||
echo "- cWUSDC/USDC: $(to_units "$cwusdc_usdc_base") / $(to_units "$cwusdc_usdc_quote")"
|
||||
echo "- cWUSDT/USDC: $(to_units "$cwusdt_usdc_base") / $(to_units "$cwusdt_usdc_quote")"
|
||||
echo "- cWUSDC/USDT: $(to_units "$cwusdc_usdt_base") / $(to_units "$cwusdc_usdt_quote")"
|
||||
echo
|
||||
echo "Recommended operating mode:"
|
||||
if (( wallet_usdc_raw < target_quote_raw || wallet_usdt_raw < target_quote_raw )); then
|
||||
echo "- meaningful matched-rail deepening is quote-constrained on mainnet today"
|
||||
echo "- use direct liquidity adds only after acquiring more USDC/USDT on mainnet"
|
||||
echo "- until then, prefer micro-support swaps only"
|
||||
else
|
||||
echo "- wallet quote inventory is sufficient for at least one ${TARGET_MATCHED_QUOTE_UNITS} unit tranche on each matched rail"
|
||||
echo "- deepen matched rails first, then use micro-support swaps"
|
||||
fi
|
||||
echo
|
||||
echo "Target matched-rail depth per quote side:"
|
||||
echo "- targetQuoteUnits=${TARGET_MATCHED_QUOTE_UNITS}"
|
||||
echo "- targetQuoteRaw=${target_quote_raw}"
|
||||
echo "- USDT shortfall raw=$(( target_quote_raw > wallet_usdt_raw ? target_quote_raw - wallet_usdt_raw : 0 ))"
|
||||
echo "- USDC shortfall raw=$(( target_quote_raw > wallet_usdc_raw ? target_quote_raw - wallet_usdc_raw : 0 ))"
|
||||
echo
|
||||
echo "Suggested micro-support sizes:"
|
||||
echo "- cWUSDT/USDT micro raw=${cwusdt_usdt_micro_raw} units=$(to_units "$cwusdt_usdt_micro_raw")"
|
||||
echo "- cWUSDC/USDC micro raw=${cwusdc_usdc_micro_raw} units=$(to_units "$cwusdc_usdc_micro_raw")"
|
||||
echo "- cWUSDT/USDC cross-rail micro raw=${cwusdt_usdc_micro_raw} units=$(to_units "$cwusdt_usdc_micro_raw")"
|
||||
echo "- cWUSDC/USDT cross-rail micro raw=${cwusdc_usdt_micro_raw} units=$(to_units "$cwusdc_usdt_micro_raw")"
|
||||
echo
|
||||
echo "Suggested matched-rail rebalance ceilings:"
|
||||
echo "- cWUSDT/USDT rebalance raw=${cwusdt_usdt_rebalance_raw} units=$(to_units "$cwusdt_usdt_rebalance_raw")"
|
||||
echo "- cWUSDC/USDC rebalance raw=${cwusdc_usdc_rebalance_raw} units=$(to_units "$cwusdc_usdc_rebalance_raw")"
|
||||
echo
|
||||
echo "Recommended sequence:"
|
||||
echo "1. Acquire mainnet quote inventory (USDC/USDT) before attempting meaningful depth adds."
|
||||
echo "2. Deepen matched rails first: cWUSDT/USDT and cWUSDC/USDC."
|
||||
echo "3. Use cross-quote rails only for small quote-lane rebalancing."
|
||||
echo "4. Keep support swaps below micro size unless reserves are materially larger."
|
||||
echo
|
||||
echo "Dry-run commands:"
|
||||
cat <<EOF
|
||||
bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cwusdt-usdt --direction=base-to-quote --amount=${cwusdt_usdt_micro_raw} --dry-run
|
||||
bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cwusdc-usdc --direction=base-to-quote --amount=${cwusdc_usdc_micro_raw} --dry-run
|
||||
bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cwusdt-usdc --direction=base-to-quote --amount=${cwusdt_usdc_micro_raw} --dry-run
|
||||
bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cwusdc-usdt --direction=base-to-quote --amount=${cwusdc_usdt_micro_raw} --dry-run
|
||||
bash scripts/verify/check-mainnet-public-dodo-cw-bootstrap-pools.sh
|
||||
EOF
|
||||
108
scripts/deployment/plan-mainnet-cwusdc-flash-quote-push-rebalance.sh
Executable file
108
scripts/deployment/plan-mainnet-cwusdc-flash-quote-push-rebalance.sh
Executable file
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Plan rebalancing Mainnet cWUSDC/USDC when the operator wallet has **no USDC**.
|
||||
#
|
||||
# Direct addLiquidity (add-mainnet-public-dodo-cw-liquidity.sh) cannot run with zero
|
||||
# wallet USDC: DODOPMMIntegration.addLiquidity requires baseAmount>0 AND quoteAmount>0.
|
||||
#
|
||||
# The repo-supported path without wallet USDC is the **flash quote-push** loop
|
||||
# (Aave flashLoan USDC → PMM swap USDC→cWUSDC → external unwind cWUSDC→USDC
|
||||
# → repay Aave + premium). Each successful round **raises PMM quote reserves** and
|
||||
# **draws base** from the pool, moving base-heavy / quote-thin books toward peg.
|
||||
#
|
||||
# This script is read-only + local modeling:
|
||||
# - reads live pool reserves (cast)
|
||||
# - prints copy/paste node commands for pmm-flash-push-break-even.mjs
|
||||
# - points to on-chain execution (deploy + call receiver), not implemented here
|
||||
#
|
||||
# References:
|
||||
# docs/03-deployment/MAINNET_CWUSD_HYBRID_FLASH_LOOP_CALCULATION_WHITEPAPER.md
|
||||
# smom-dbis-138/contracts/flash/AaveQuotePushFlashReceiver.sol
|
||||
# smom-dbis-138/script/deploy/DeployAaveQuotePushFlashReceiver.s.sol
|
||||
# scripts/verify/run-mainnet-cwusdc-usdc-ladder-steps-1-3.sh (preflight + dry-run ladder)
|
||||
# scripts/deployment/run-mainnet-cwusdc-flash-quote-push-model-sweep.sh (multi-size dry-run loop)
|
||||
#
|
||||
# Usage:
|
||||
# source scripts/lib/load-project-env.sh
|
||||
# bash scripts/deployment/plan-mainnet-cwusdc-flash-quote-push-rebalance.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROXMOX_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROXMOX_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROXMOX_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1 || true
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd cast
|
||||
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
USDC="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
INTEGRATION="${DODO_PMM_INTEGRATION_MAINNET:-}"
|
||||
POOL="${POOL_CWUSDC_USDC_MAINNET:-0x69776fc607e9edA8042e320e7e43f54d06c68f0E}"
|
||||
|
||||
if [[ -z "$RPC_URL" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$INTEGRATION" ]]; then
|
||||
mapped_pool="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$CWUSDC" "$USDC" --rpc-url "$RPC_URL" 2>/dev/null | awk '{print $1}' || true)"
|
||||
if [[ -n "$mapped_pool" && "$mapped_pool" != "0x0000000000000000000000000000000000000000" && "${mapped_pool,,}" != "${POOL,,}" ]]; then
|
||||
POOL="$mapped_pool"
|
||||
fi
|
||||
fi
|
||||
|
||||
out="$(cast call "$POOL" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL")"
|
||||
B="$(printf '%s\n' "$out" | sed -n '1p' | awk '{print $1}')"
|
||||
Q="$(printf '%s\n' "$out" | sed -n '2p' | awk '{print $1}')"
|
||||
|
||||
PMM_JS="${PROXMOX_ROOT}/scripts/analytics/pmm-flash-push-break-even.mjs"
|
||||
DEFAULT_EXIT_CMD="printf 1.02"
|
||||
EXIT_CMD="${PMM_FLASH_EXIT_PRICE_CMD:-$DEFAULT_EXIT_CMD}"
|
||||
|
||||
echo "=== Mainnet cWUSDC/USDC rebalance without wallet USDC (flash quote-push plan) ==="
|
||||
echo "pool=$POOL"
|
||||
echo "live_base_reserve_raw=$B live_quote_reserve_raw=$Q"
|
||||
echo
|
||||
echo "Why addLiquidity is not enough: integration requires both tokens from the wallet."
|
||||
echo "Flash quote-push borrows USDC for the PMM leg; wallet only needs ETH for gas + a deployed receiver."
|
||||
echo
|
||||
echo "1) Model one or more flash sizes (read-only; tune -x or use model sweep script):"
|
||||
echo "node \"${PMM_JS}\" \\"
|
||||
echo " -B \"$B\" -Q \"$Q\" \\"
|
||||
echo " --full-loop-dry-run --loop-strategy quote-push \\"
|
||||
echo " -x 10000000 \\"
|
||||
echo " --base-asset cWUSDC --base-decimals 6 --quote-asset USDC --quote-decimals 6 \\"
|
||||
echo " --external-exit-price-cmd \"$EXIT_CMD\" \\"
|
||||
echo " --flash-fee-bps 5 --lp-fee-bps 3 \\"
|
||||
echo " --flash-provider-cap 1000000000000 --flash-provider-name 'Aave mainnet (cap placeholder)' \\"
|
||||
echo " --gas-tx-count 3 --gas-per-tx 250000 --max-fee-gwei 40 --native-token-price 3200 \\"
|
||||
echo " --max-post-trade-deviation-bps 500"
|
||||
echo
|
||||
echo "2) Multi-size modeling loop (still no chain writes):"
|
||||
echo " FLASH_MODEL_SCAN_SIZES=5000000,10000000,25000000 \\"
|
||||
echo " bash scripts/deployment/run-mainnet-cwusdc-flash-quote-push-model-sweep.sh"
|
||||
echo
|
||||
echo "3) On-chain stack (forge, from repo root):"
|
||||
echo " bash scripts/deployment/deploy-mainnet-aave-quote-push-stack.sh --dry-run # then --apply"
|
||||
echo " bash scripts/deployment/run-mainnet-aave-cwusdc-quote-push-once.sh --dry-run"
|
||||
echo " FLASH_LOOP_COUNT=3 bash scripts/deployment/run-mainnet-aave-cwusdc-quote-push-loop.sh --apply"
|
||||
echo " Solidity: smom-dbis-138/script/flash/RunMainnetAaveCwusdcUsdcQuotePushOnce.s.sol"
|
||||
echo " Fork proofs: cd smom-dbis-138 && forge test --match-contract AaveQuotePushFlashReceiverMainnetForkTest"
|
||||
echo
|
||||
echo "4) Reserve peg monitor after any live work:"
|
||||
echo " bash scripts/verify/check-mainnet-cwusdc-usdc-reserve-peg.sh"
|
||||
echo
|
||||
echo "5) Pick a valid Uniswap V3 unwind (no guessing fee tiers):"
|
||||
echo " bash scripts/verify/probe-uniswap-v3-cwusdc-usdc-mainnet.sh"
|
||||
echo " If no pool: UNWIND_MODE=2 — build path: bash scripts/verify/build-uniswap-v3-exact-input-path-hex.sh ... (VERIFY_POOLS=1 optional)"
|
||||
echo " Calculations report (dry-run snapshots): reports/status/mainnet_cwusdc_usdc_quote_push_calculations_2026-04-12.md"
|
||||
231
scripts/deployment/plan-mainnet-cwusdc-usdc-rebalance-liquidity.sh
Executable file
231
scripts/deployment/plan-mainnet-cwusdc-usdc-rebalance-liquidity.sh
Executable file
@@ -0,0 +1,231 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Read-only plan for maintaining a 1:1 vault reserve peg on Mainnet cWUSDC/USDC
|
||||
# (both tokens use 6 decimals — target is base_reserve ≈ quote_reserve in raw units).
|
||||
#
|
||||
# DODOPMMIntegration.addLiquidity requires baseAmount>0 and quoteAmount>0.
|
||||
# First-order correction when base > quote:
|
||||
# quote_add = base_add + (base_reserve - quote_reserve)
|
||||
# When quote > base:
|
||||
# base_add = quote_add + (quote_reserve - base_reserve)
|
||||
#
|
||||
# Largest single tranche the deployer wallet can fund toward that linear model:
|
||||
# base-thick: base_add = min(wallet_cWUSDC, wallet_USDC - gap)
|
||||
# quote-thick: quote_add = min(wallet_USDC, wallet_cWUSDC - gap)
|
||||
# (skipped when the min is < 1). Re-read on-chain reserves after each tx.
|
||||
#
|
||||
# When wallet USDC is insufficient for asymmetric peg-close, matched 1:1 adds still
|
||||
# reduce base/quote imbalance ratio until you can fund quote inventory.
|
||||
#
|
||||
# Usage:
|
||||
# source scripts/lib/load-project-env.sh
|
||||
# bash scripts/deployment/plan-mainnet-cwusdc-usdc-rebalance-liquidity.sh
|
||||
#
|
||||
# Env:
|
||||
# ETHEREUM_MAINNET_RPC (required)
|
||||
# PRIVATE_KEY (optional — wallet balances + suggested add sizes)
|
||||
# POOL_CWUSDC_USDC_MAINNET (optional)
|
||||
# PEG_IMBALANCE_MAX_BPS (optional — printed vs |base-quote|/max; default 25)
|
||||
# MIN_BASE_SUGGEST (optional — small example tranche when base thick; default 1e6 raw)
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROJECT_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1 || true
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd cast
|
||||
require_cmd python3
|
||||
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
USDC="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
INTEGRATION="${DODO_PMM_INTEGRATION_MAINNET:-}"
|
||||
# Default public pool; env may point at a legacy rail — prefer integration mapping when it disagrees.
|
||||
POOL="${POOL_CWUSDC_USDC_MAINNET:-0x69776fc607e9edA8042e320e7e43f54d06c68f0E}"
|
||||
MIN_BASE_SUGGEST="${MIN_BASE_SUGGEST:-1000000}"
|
||||
PEG_MAX_BPS="${PEG_IMBALANCE_MAX_BPS:-25}"
|
||||
|
||||
if [[ -z "$RPC_URL" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$INTEGRATION" ]]; then
|
||||
mapped_pool="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$CWUSDC" "$USDC" --rpc-url "$RPC_URL" 2>/dev/null | awk '{print $1}' || true)"
|
||||
if [[ -n "$mapped_pool" && "$mapped_pool" != "0x0000000000000000000000000000000000000000" && "${mapped_pool,,}" != "${POOL,,}" ]]; then
|
||||
POOL="$mapped_pool"
|
||||
fi
|
||||
fi
|
||||
|
||||
read_reserves() {
|
||||
cast call "$POOL" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL"
|
||||
}
|
||||
|
||||
to_human() {
|
||||
python3 - "$1" <<'PY'
|
||||
import sys
|
||||
print(f"{int(sys.argv[1]) / 1_000_000:.6f}")
|
||||
PY
|
||||
}
|
||||
|
||||
line1="$(read_reserves | sed -n '1p' | awk '{print $1}')"
|
||||
line2="$(read_reserves | sed -n '2p' | awk '{print $1}')"
|
||||
base_r="$line1"
|
||||
quote_r="$line2"
|
||||
|
||||
gap="$(python3 - "$base_r" "$quote_r" <<'PY'
|
||||
import sys
|
||||
b, q = int(sys.argv[1]), int(sys.argv[2])
|
||||
print(abs(b - q))
|
||||
PY
|
||||
)"
|
||||
|
||||
if (( base_r > quote_r )); then
|
||||
thicker=base
|
||||
need_asym_quote_over_base="$(( base_r - quote_r ))"
|
||||
else
|
||||
thicker=quote
|
||||
need_asym_quote_over_base="$(( quote_r - base_r ))"
|
||||
fi
|
||||
|
||||
imb_bps="$(python3 - "$base_r" "$quote_r" <<'PY'
|
||||
import sys
|
||||
b, q = int(sys.argv[1]), int(sys.argv[2])
|
||||
m = max(b, q, 1)
|
||||
print(abs(b - q) * 10000 // m)
|
||||
PY
|
||||
)"
|
||||
peg_ok=1
|
||||
if (( PEG_MAX_BPS <= 0 )); then
|
||||
(( gap == 0 )) || peg_ok=0
|
||||
else
|
||||
(( imb_bps <= PEG_MAX_BPS )) || peg_ok=0
|
||||
fi
|
||||
|
||||
echo "=== Mainnet cWUSDC/USDC 1:1 reserve peg plan ==="
|
||||
echo "pool=$POOL"
|
||||
echo "baseReserve_raw=$base_r human=$(to_human "$base_r") cWUSDC"
|
||||
echo "quoteReserve_raw=$quote_r human=$(to_human "$quote_r") USDC"
|
||||
echo "abs(base-quote)_raw=$gap human=$(to_human "$gap")"
|
||||
echo "imbalance_bps=$imb_bps peg_max_bps=$PEG_MAX_BPS peg_status=$([[ "$peg_ok" == 1 ]] && echo IN_BAND || echo OUT_OF_BAND)"
|
||||
echo "thicker_side=$thicker"
|
||||
echo "peg_check: bash scripts/verify/check-mainnet-cwusdc-usdc-reserve-peg.sh"
|
||||
echo
|
||||
|
||||
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
||||
dep="$(cast wallet address --private-key "$PRIVATE_KEY")"
|
||||
wb="$(cast call "$CWUSDC" 'balanceOf(address)(uint256)' "$dep" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
wq="$(cast call "$USDC" 'balanceOf(address)(uint256)' "$dep" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
echo "deployer=$dep"
|
||||
echo "wallet_cWUSDC_raw=$wb human=$(to_human "$wb")"
|
||||
echo "wallet_USDC_raw=$wq human=$(to_human "$wq")"
|
||||
echo
|
||||
|
||||
matched="$(python3 - "$wb" "$wq" <<'PY'
|
||||
import sys
|
||||
print(min(int(sys.argv[1]), int(sys.argv[2])))
|
||||
PY
|
||||
)"
|
||||
if (( matched > 0 )); then
|
||||
echo "Suggested matched 1:1 deepen (uses min(wallet cWUSDC, wallet USDC)):"
|
||||
echo " bash scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh \\"
|
||||
echo " --pair=cwusdc-usdc --base-amount=$matched --quote-amount=$matched --dry-run"
|
||||
fi
|
||||
|
||||
if [[ "$thicker" == base ]]; then
|
||||
G="$need_asym_quote_over_base"
|
||||
read -r opt_b opt_q <<<"$(
|
||||
python3 - "$wb" "$wq" "$G" <<'PY'
|
||||
import sys
|
||||
wb, wq, G = int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3])
|
||||
if G <= 0:
|
||||
print("0 0")
|
||||
else:
|
||||
cap = min(wb, wq - G) if wq > G else 0
|
||||
if cap < 1:
|
||||
print("0 0")
|
||||
else:
|
||||
print(cap, cap + G)
|
||||
PY
|
||||
)"
|
||||
echo
|
||||
echo "Asymmetric peg correction (base>quote): quote_add = base_add + (base_reserve - quote_reserve)"
|
||||
if (( opt_b >= 1 && opt_q >= 1 )); then
|
||||
echo " largest wallet-sized peg tranche (linear model): base_add=$opt_b quote_add=$opt_q"
|
||||
echo " bash scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh \\"
|
||||
echo " --pair=cwusdc-usdc --base-amount=$opt_b --quote-amount=$opt_q --dry-run"
|
||||
else
|
||||
echo " no affordable asymmetric peg tranche (need USDC_raw > gap in wallet; gap_raw=$G)."
|
||||
fi
|
||||
asym_b="$MIN_BASE_SUGGEST"
|
||||
asym_q="$(python3 - "$asym_b" "$need_asym_quote_over_base" <<'PY'
|
||||
import sys
|
||||
b, g = int(sys.argv[1]), int(sys.argv[2])
|
||||
print(b + g)
|
||||
PY
|
||||
)"
|
||||
echo
|
||||
echo " fixed example MIN_BASE_SUGGEST=$MIN_BASE_SUGGEST -> base_add=$asym_b quote_add=$asym_q"
|
||||
if (( wq >= asym_q && wb >= asym_b )); then
|
||||
echo " wallet sufficient for example tranche — dry-run:"
|
||||
echo " bash scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh \\"
|
||||
echo " --pair=cwusdc-usdc --base-amount=$asym_b --quote-amount=$asym_q --dry-run"
|
||||
else
|
||||
need_q_short="$(( asym_q > wq ? asym_q - wq : 0 ))"
|
||||
need_b_short="$(( asym_b > wb ? asym_b - wb : 0 ))"
|
||||
echo " wallet shortfall vs example: short_quote_raw=$need_q_short short_base_raw=$need_b_short"
|
||||
echo " fund deployer USDC (and cWUSDC if base-thin), then dry-run before live add."
|
||||
fi
|
||||
else
|
||||
G="$need_asym_quote_over_base"
|
||||
read -r opt_b opt_q <<<"$(
|
||||
python3 - "$wb" "$wq" "$G" <<'PY'
|
||||
import sys
|
||||
wb, wq, G = int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3])
|
||||
if G <= 0:
|
||||
print("0 0")
|
||||
else:
|
||||
cap = min(wq, wb - G) if wb > G else 0
|
||||
if cap < 1:
|
||||
print("0 0")
|
||||
else:
|
||||
print(cap + G, cap)
|
||||
PY
|
||||
)"
|
||||
echo
|
||||
echo "Asymmetric peg correction (quote>base): base_add = quote_add + (quote_reserve - base_reserve)"
|
||||
if (( opt_b >= 1 && opt_q >= 1 )); then
|
||||
echo " largest wallet-sized peg tranche (linear model): base_add=$opt_b quote_add=$opt_q"
|
||||
echo " bash scripts/deployment/add-mainnet-public-dodo-cw-liquidity.sh \\"
|
||||
echo " --pair=cwusdc-usdc --base-amount=$opt_b --quote-amount=$opt_q --dry-run"
|
||||
else
|
||||
echo " no affordable asymmetric peg tranche (need cWUSDC_raw > gap in wallet; gap_raw=$G)."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Set PRIVATE_KEY (via .env) to print wallet-based add suggestions."
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Reference: smom-dbis-138/contracts/dex/DODOPMMIntegration.sol addLiquidity (both amounts must be > 0)."
|
||||
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
||||
wq_check="$(cast call "$USDC" 'balanceOf(address)(uint256)' "$(cast wallet address --private-key "$PRIVATE_KEY")" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
if (( wq_check == 0 )); then
|
||||
echo
|
||||
echo "Wallet USDC is 0: direct addLiquidity is impossible. Flash quote-push path:"
|
||||
echo " bash scripts/deployment/plan-mainnet-cwusdc-flash-quote-push-rebalance.sh"
|
||||
echo " bash scripts/deployment/run-mainnet-cwusdc-flash-quote-push-model-sweep.sh"
|
||||
echo " bash scripts/deployment/deploy-mainnet-aave-quote-push-stack.sh --dry-run"
|
||||
echo " bash scripts/deployment/run-mainnet-aave-cwusdc-quote-push-once.sh --dry-run"
|
||||
fi
|
||||
fi
|
||||
141
scripts/deployment/pmm-soak-export-wallet-grid.py
Executable file
141
scripts/deployment/pmm-soak-export-wallet-grid.py
Executable file
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Export 33 x 33 x 6 = 6534 Chain 138 soak wallet addresses from a BIP39 mnemonic.
|
||||
|
||||
Uses `cast wallet address` (Foundry) per index — parallel workers for speed.
|
||||
Never writes the mnemonic; read from env PMM_SOAK_GRID_MNEMONIC only.
|
||||
|
||||
PMM_SOAK_GRID_MNEMONIC='...' python3 scripts/deployment/pmm-soak-export-wallet-grid.py --out config/pmm-soak-wallet-grid.json
|
||||
# Smoke / CI (first N indices only):
|
||||
PMM_SOAK_GRID_MNEMONIC='...' python3 scripts/deployment/pmm-soak-export-wallet-grid.py --out /tmp/grid-smoke.json --count 10
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from concurrent.futures import ProcessPoolExecutor, as_completed
|
||||
|
||||
LPBCA_COUNT = 33
|
||||
BRANCH_COUNT = 33
|
||||
CLASS_COUNT = 6
|
||||
LINEAR_COUNT = LPBCA_COUNT * BRANCH_COUNT * CLASS_COUNT # 6534
|
||||
|
||||
|
||||
def linear_to_coords(linear: int) -> tuple[int, int, int]:
|
||||
lpbca = linear // (BRANCH_COUNT * CLASS_COUNT)
|
||||
rem = linear % (BRANCH_COUNT * CLASS_COUNT)
|
||||
branch = rem // CLASS_COUNT
|
||||
klass = rem % CLASS_COUNT
|
||||
return lpbca, branch, klass
|
||||
|
||||
|
||||
def derive_address(linear_index: int) -> tuple[int, str]:
|
||||
mn = os.environ.get("PMM_SOAK_GRID_MNEMONIC", "").strip()
|
||||
if not mn:
|
||||
raise RuntimeError("PMM_SOAK_GRID_MNEMONIC is not set")
|
||||
path = f"m/44'/60'/0'/0/{linear_index}"
|
||||
r = subprocess.run(
|
||||
[
|
||||
"cast",
|
||||
"wallet",
|
||||
"address",
|
||||
"--mnemonic",
|
||||
mn,
|
||||
"--mnemonic-derivation-path",
|
||||
path,
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
if r.returncode != 0:
|
||||
raise RuntimeError(f"cast failed for index {linear_index}: {r.stderr.strip()}")
|
||||
addr = r.stdout.strip()
|
||||
if not addr.startswith("0x") or len(addr) != 42:
|
||||
raise RuntimeError(f"bad address for index {linear_index}: {addr!r}")
|
||||
return linear_index, addr
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(description="Export PMM soak 33x33x6 wallet grid addresses.")
|
||||
ap.add_argument(
|
||||
"--out",
|
||||
required=True,
|
||||
help="Output JSON path (use a gitignored file for real grids).",
|
||||
)
|
||||
ap.add_argument("--jobs", type=int, default=8, help="Parallel cast workers (default 8).")
|
||||
ap.add_argument(
|
||||
"--count",
|
||||
type=int,
|
||||
default=None,
|
||||
metavar="N",
|
||||
help=f"Export only linear indices 0..N-1 (default: full grid {LINEAR_COUNT}). For smoke tests.",
|
||||
)
|
||||
args = ap.parse_args()
|
||||
|
||||
if not os.environ.get("PMM_SOAK_GRID_MNEMONIC", "").strip():
|
||||
print("ERROR: set PMM_SOAK_GRID_MNEMONIC in the environment.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
n = LINEAR_COUNT if args.count is None else int(args.count)
|
||||
if n < 1 or n > LINEAR_COUNT:
|
||||
print(f"ERROR: --count must be 1..{LINEAR_COUNT}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
addrs: list[str | None] = [None] * n
|
||||
jobs = max(1, min(args.jobs, 32))
|
||||
|
||||
with ProcessPoolExecutor(max_workers=jobs) as ex:
|
||||
futs = {ex.submit(derive_address, i): i for i in range(n)}
|
||||
for fut in as_completed(futs):
|
||||
i, addr = fut.result()
|
||||
addrs[i] = addr
|
||||
|
||||
wallets = []
|
||||
for li in range(n):
|
||||
lpbca, branch, klass = linear_to_coords(li)
|
||||
wallets.append(
|
||||
{
|
||||
"lpbca": lpbca,
|
||||
"branch": branch,
|
||||
"class": klass,
|
||||
"linearIndex": li,
|
||||
"address": addrs[li],
|
||||
}
|
||||
)
|
||||
|
||||
doc: dict = {
|
||||
"version": 1,
|
||||
"dimensions": {
|
||||
"lpbcaCount": LPBCA_COUNT,
|
||||
"branchCount": BRANCH_COUNT,
|
||||
"classCount": CLASS_COUNT,
|
||||
"linearCount": LINEAR_COUNT,
|
||||
"linearCountExported": n,
|
||||
},
|
||||
"derivation": {
|
||||
"pathTemplate": "m/44'/60'/0'/0/{linearIndex}",
|
||||
"linearIndexFormula": "linear = lpbca * 198 + branch * 6 + class",
|
||||
},
|
||||
"wallets": wallets,
|
||||
}
|
||||
if n < LINEAR_COUNT:
|
||||
doc["partialExport"] = True
|
||||
|
||||
out_path = args.out
|
||||
out_dir = os.path.dirname(os.path.abspath(out_path))
|
||||
if out_dir:
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
with open(out_path, "w", encoding="utf-8") as f:
|
||||
json.dump(doc, f, indent=0)
|
||||
f.write("\n")
|
||||
|
||||
print(f"Wrote {n} wallet(s) to {out_path}", file=sys.stderr)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
56
scripts/deployment/pmm-soak-grid-smoke-check.sh
Executable file
56
scripts/deployment/pmm-soak-grid-smoke-check.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# Non-secret smoke: export a small grid (test mnemonic), dry-run fund + soak bots.
|
||||
# Does not broadcast txs. Uses Foundry `cast` + LAN RPC if reachable.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/deployment/pmm-soak-grid-smoke-check.sh
|
||||
# Env:
|
||||
# RPC_URL_138 (optional; in CI defaults to public read-only RPC if unset)
|
||||
# PMM_SOAK_GRID_SMOKE_RPC_URL — override public RPC URL used when CI is set
|
||||
# PMM_SOAK_GRID_SMOKE_MNEMONIC — override test mnemonic (default: Anvil-style test phrase)
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
SMOKE_JSON="${PMM_SOAK_GRID_SMOKE_JSON:-${TMPDIR:-/tmp}/pmm-soak-grid-smoke-$$.json}"
|
||||
MN="${PMM_SOAK_GRID_SMOKE_MNEMONIC:-test test test test test test test test test test test junk}"
|
||||
|
||||
# GitHub Actions (and similar) have no LAN; child scripts source .env and would default to Core LAN RPC.
|
||||
# Export a read-only public endpoint unless the caller already set RPC_URL_138.
|
||||
if [[ -n "${CI:-}" && -z "${RPC_URL_138:-}" ]]; then
|
||||
export RPC_URL_138="${PMM_SOAK_GRID_SMOKE_RPC_URL:-https://rpc-http-pub.d-bis.org}"
|
||||
fi
|
||||
|
||||
echo "=== PMM soak grid smoke (dry-run only) ==="
|
||||
echo "Temp grid JSON: $SMOKE_JSON"
|
||||
|
||||
export PMM_SOAK_GRID_MNEMONIC="$MN"
|
||||
python3 scripts/deployment/pmm-soak-export-wallet-grid.py --out "$SMOKE_JSON" --count 8 --jobs 4
|
||||
|
||||
export PMM_SOAK_GRID_JSON="$SMOKE_JSON"
|
||||
|
||||
echo ""
|
||||
echo "[1/4] Operator fund-grid dry-run (native, linear 0-4)..."
|
||||
NATIVE_AMOUNT_WEI=10000000000000000 \
|
||||
bash scripts/deployment/pmm-soak-operator-fund-grid.sh --native --from-linear 0 --to-linear 4
|
||||
|
||||
echo ""
|
||||
echo "[2/4] Operator fund-grid dry-run (cUSDT, linear 0-3)..."
|
||||
TOKEN=0x93E66202A11B1772E55407B32B44e5Cd8eda7f22 AMOUNT_WEI_PER_WALLET=1000000 \
|
||||
bash scripts/deployment/pmm-soak-operator-fund-grid.sh --from-linear 0 --to-linear 3
|
||||
|
||||
echo ""
|
||||
echo "[3/4] Grid bot dry-run (2 ticks, linear 0-7 only for partial export)..."
|
||||
PMM_SOAK_LINEAR_MIN=0 PMM_SOAK_LINEAR_MAX=7 \
|
||||
bash scripts/deployment/chain138-pmm-soak-grid-bot.sh --dry-run --max-ticks 2 --pool-preset stable
|
||||
|
||||
echo ""
|
||||
echo "[4/4] Single-wallet soak dry-run (1 tick)..."
|
||||
bash scripts/deployment/chain138-pmm-random-soak-swaps.sh --dry-run --max-ticks 1 --pool-preset stable
|
||||
|
||||
rm -f "$SMOKE_JSON"
|
||||
echo ""
|
||||
echo "=== PMM soak grid smoke OK ==="
|
||||
46
scripts/deployment/prepare-chain138-uniswap-v3-upstream-worktree.sh
Executable file
46
scripts/deployment/prepare-chain138-uniswap-v3-upstream-worktree.sh
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Prepare the local upstream-native Uniswap v3 worktree used for the Chain 138
|
||||
# replacement track. This keeps the live pilot venue layer untouched while
|
||||
# giving operators a canonical place to build and audit upstream contracts.
|
||||
|
||||
CORE_REPO="${CHAIN138_UNISWAP_V3_NATIVE_CORE_REPO:-/home/intlc/projects/uniswap-v3-core}"
|
||||
PERIPHERY_REPO="${CHAIN138_UNISWAP_V3_NATIVE_PERIPHERY_REPO:-/home/intlc/projects/uniswap-v3-periphery}"
|
||||
CORE_URL="${CHAIN138_UNISWAP_V3_NATIVE_CORE_URL:-https://github.com/Uniswap/v3-core.git}"
|
||||
PERIPHERY_URL="${CHAIN138_UNISWAP_V3_NATIVE_PERIPHERY_URL:-https://github.com/Uniswap/v3-periphery.git}"
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd git
|
||||
|
||||
prepare_repo() {
|
||||
local label="$1"
|
||||
local path="$2"
|
||||
local url="$3"
|
||||
|
||||
if [[ ! -d "${path}/.git" ]]; then
|
||||
echo "[info] cloning ${label} into ${path}"
|
||||
git clone "${url}" "${path}"
|
||||
else
|
||||
echo "[info] ${label} already present at ${path}"
|
||||
fi
|
||||
|
||||
git -C "${path}" fetch --tags origin >/dev/null 2>&1 || true
|
||||
printf '[ok] %s %s @ %s\n' \
|
||||
"${label}" \
|
||||
"$(git -C "${path}" remote get-url origin 2>/dev/null || echo unknown-remote)" \
|
||||
"$(git -C "${path}" rev-parse --short HEAD)"
|
||||
}
|
||||
|
||||
echo "== Chain 138 upstream-native Uniswap v3 worktree =="
|
||||
prepare_repo "v3-core" "${CORE_REPO}" "${CORE_URL}"
|
||||
prepare_repo "v3-periphery" "${PERIPHERY_REPO}" "${PERIPHERY_URL}"
|
||||
echo
|
||||
echo "[note] These repos support the upstream-native replacement track only."
|
||||
echo "[note] The live Chain 138 planner still routes through the funded pilot-compatible venue layer until native deployment passes preflight."
|
||||
176
scripts/deployment/print-gas-l1-destination-wiring-commands.sh
Normal file
176
scripts/deployment/print-gas-l1-destination-wiring-commands.sh
Normal file
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env bash
|
||||
# Print the exact Chain 138 CWMultiTokenBridgeL1 destination-wiring commands
|
||||
# still required for the active gas-native rollout lanes.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/deployment/print-gas-l1-destination-wiring-commands.sh
|
||||
# bash scripts/deployment/print-gas-l1-destination-wiring-commands.sh --json
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
export PROJECT_ROOT
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" >/dev/null 2>&1 || true
|
||||
|
||||
OUTPUT_JSON=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--json) OUTPUT_JSON=1 ;;
|
||||
*)
|
||||
echo "Unknown argument: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
command -v node >/dev/null 2>&1 || {
|
||||
echo "[FAIL] Missing required command: node" >&2
|
||||
exit 1
|
||||
}
|
||||
command -v cast >/dev/null 2>&1 || {
|
||||
echo "[FAIL] Missing required command: cast" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
OUTPUT_JSON="$OUTPUT_JSON" node <<'NODE'
|
||||
const path = require('path');
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
const loader = require(path.join(process.env.PROJECT_ROOT, 'config/token-mapping-loader.cjs'));
|
||||
const outputJson = process.env.OUTPUT_JSON === '1';
|
||||
|
||||
const chain138RpcUrl = process.env.RPC_URL_138 || '';
|
||||
const l1BridgeAddress =
|
||||
process.env.CHAIN138_L1_BRIDGE ||
|
||||
process.env.CW_L1_BRIDGE_CHAIN138 ||
|
||||
process.env.CW_L1_BRIDGE ||
|
||||
'';
|
||||
|
||||
function callRead(address, rpcUrl, signature, args = []) {
|
||||
if (!address || !/^0x[a-fA-F0-9]{40}$/.test(address) || !rpcUrl) {
|
||||
return { ok: false, value: null, error: 'missing_rpc_or_address' };
|
||||
}
|
||||
try {
|
||||
const out = execFileSync(
|
||||
'cast',
|
||||
['call', address, signature, ...args.map((arg) => String(arg)), '--rpc-url', rpcUrl],
|
||||
{
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
}
|
||||
).trim();
|
||||
return { ok: true, value: out, error: null };
|
||||
} catch (error) {
|
||||
const stderr = error && error.stderr ? String(error.stderr).trim() : '';
|
||||
const stdout = error && error.stdout ? String(error.stdout).trim() : '';
|
||||
return {
|
||||
ok: false,
|
||||
value: null,
|
||||
error: stderr || stdout || (error && error.message ? String(error.message).trim() : 'cast_call_failed'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function parseDestinationConfig(rawValue) {
|
||||
if (typeof rawValue !== 'string') {
|
||||
return { receiverBridge: null, enabled: null };
|
||||
}
|
||||
const match = rawValue.match(/^\((0x[a-fA-F0-9]{40}),\s*(true|false)\)$/);
|
||||
if (!match) {
|
||||
return { receiverBridge: null, enabled: null };
|
||||
}
|
||||
return {
|
||||
receiverBridge: match[1],
|
||||
enabled: match[2] === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
const rows = loader
|
||||
.getActiveTransportPairs()
|
||||
.filter((pair) => pair.assetClass === 'gas_native')
|
||||
.map((pair) => {
|
||||
const selector = typeof pair.destinationChainSelector === 'string' ? pair.destinationChainSelector : '';
|
||||
const l2BridgeAddress = pair.runtimeL2BridgeAddress || '';
|
||||
const destinationProbe =
|
||||
l1BridgeAddress && chain138RpcUrl && pair.canonicalAddress && selector
|
||||
? callRead(l1BridgeAddress, chain138RpcUrl, 'destinations(address,uint64)((address,bool))', [
|
||||
pair.canonicalAddress,
|
||||
selector,
|
||||
])
|
||||
: { ok: false, value: null, error: 'missing_probe_inputs' };
|
||||
const destinationConfig = parseDestinationConfig(destinationProbe.value);
|
||||
const receiverMatches =
|
||||
!!destinationConfig.receiverBridge &&
|
||||
!!l2BridgeAddress &&
|
||||
destinationConfig.receiverBridge.toLowerCase() === l2BridgeAddress.toLowerCase();
|
||||
const needsWiring = destinationConfig.enabled !== true || !receiverMatches;
|
||||
|
||||
return {
|
||||
key: pair.key,
|
||||
chainId: pair.destinationChainId,
|
||||
chainName: pair.destinationChainName || `chain-${pair.destinationChainId}`,
|
||||
familyKey: pair.familyKey,
|
||||
canonicalSymbol: pair.canonicalSymbol,
|
||||
mirroredSymbol: pair.mirroredSymbol,
|
||||
canonicalAddress: pair.canonicalAddress,
|
||||
destinationChainSelector: selector || null,
|
||||
l1BridgeAddress: l1BridgeAddress || null,
|
||||
l2BridgeAddress: l2BridgeAddress || null,
|
||||
destinationConfiguredOnL1: destinationConfig.enabled,
|
||||
destinationReceiverBridgeOnL1: destinationConfig.receiverBridge,
|
||||
destinationReceiverMatchesRuntimeL2: receiverMatches,
|
||||
probeError: destinationProbe.ok ? null : destinationProbe.error,
|
||||
needsWiring,
|
||||
command:
|
||||
selector && l2BridgeAddress
|
||||
? `cast send "$CHAIN138_L1_BRIDGE" "configureDestination(address,uint64,address,bool)" ${pair.canonicalAddress} ${selector} ${l2BridgeAddress} true --rpc-url "$RPC_URL_138" --private-key "$PRIVATE_KEY"`
|
||||
: null,
|
||||
};
|
||||
});
|
||||
|
||||
const blocked = rows.filter((row) => row.needsWiring);
|
||||
const summary = {
|
||||
activeGasPairs: rows.length,
|
||||
pairsNeedingL1DestinationWiring: blocked.length,
|
||||
pairsAlreadyWired: rows.length - blocked.length,
|
||||
l1BridgeAddress: l1BridgeAddress || null,
|
||||
};
|
||||
|
||||
if (outputJson) {
|
||||
console.log(JSON.stringify({ summary, rows: blocked }, null, 2));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log('=== Gas L1 Destination Wiring Commands ===');
|
||||
console.log(`Active gas pairs: ${summary.activeGasPairs}`);
|
||||
console.log(`Pairs needing Chain 138 destination wiring: ${summary.pairsNeedingL1DestinationWiring}`);
|
||||
console.log(`Pairs already wired: ${summary.pairsAlreadyWired}`);
|
||||
console.log(`Chain 138 L1 bridge: ${summary.l1BridgeAddress || '<unset>'}`);
|
||||
|
||||
if (blocked.length === 0) {
|
||||
console.log('All active gas lanes already have Chain 138 destination wiring.');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
for (const row of blocked) {
|
||||
console.log('');
|
||||
console.log(`- ${row.chainId} ${row.chainName} | ${row.familyKey} | ${row.canonicalSymbol}->${row.mirroredSymbol}`);
|
||||
console.log(` selector: ${row.destinationChainSelector || '<missing>'}`);
|
||||
console.log(` destination on L1: ${row.destinationConfiguredOnL1 === true ? 'wired' : 'missing_or_mismatched'}`);
|
||||
if (row.destinationReceiverBridgeOnL1) {
|
||||
console.log(` current L1 receiver: ${row.destinationReceiverBridgeOnL1}`);
|
||||
}
|
||||
if (row.l2BridgeAddress) {
|
||||
console.log(` expected L2 bridge: ${row.l2BridgeAddress}`);
|
||||
}
|
||||
if (row.probeError) {
|
||||
console.log(` probe: ${row.probeError}`);
|
||||
}
|
||||
if (row.command) {
|
||||
console.log(` command: ${row.command}`);
|
||||
}
|
||||
}
|
||||
NODE
|
||||
86
scripts/deployment/provision-info-defi-oracle-web-lxc.sh
Executable file
86
scripts/deployment/provision-info-defi-oracle-web-lxc.sh
Executable file
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env bash
|
||||
# Dedicated LXC for static web: info.defi-oracle.io (nginx + SPA root).
|
||||
# Do not use VMID 2400 (ThirdWeb RPC); use this CT + sync-info-defi-oracle-to-vmid2400.sh.
|
||||
#
|
||||
# Defaults: VMID 2410, 192.168.11.218, Proxmox r630-01 (override PROXMOX_HOST for ml110).
|
||||
#
|
||||
# Usage (from dev machine with SSH to Proxmox):
|
||||
# bash scripts/deployment/provision-info-defi-oracle-web-lxc.sh [--dry-run]
|
||||
# Then:
|
||||
# bash scripts/deployment/sync-info-defi-oracle-to-vmid2400.sh
|
||||
# bash scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh
|
||||
# pnpm run verify:info-defi-oracle-public
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
# shellcheck source=/dev/null
|
||||
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
|
||||
|
||||
PROXMOX_HOST="${PROXMOX_HOST:-${PROXMOX_HOST_R630_01:-192.168.11.11}}"
|
||||
VMID="${INFO_DEFI_ORACLE_WEB_VMID:-${INFO_DEFI_ORACLE_VMID:-2410}}"
|
||||
IP_CT="${IP_INFO_DEFI_ORACLE_WEB:-192.168.11.218}"
|
||||
HOSTNAME_CT="${INFO_DEFI_ORACLE_WEB_HOSTNAME:-info-defi-oracle-web}"
|
||||
APP_DIR="${INFO_DEFI_ORACLE_WEB_ROOT:-/var/www/info.defi-oracle.io/html}"
|
||||
SITE_FILE="${INFO_DEFI_ORACLE_NGINX_SITE:-/etc/nginx/sites-available/info-defi-oracle}"
|
||||
NGINX_TEMPLATE="${PROJECT_ROOT}/config/nginx/info-defi-oracle-io.site.conf"
|
||||
TEMPLATE_CT="${TEMPLATE:-local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst}"
|
||||
STORAGE="${STORAGE:-local-lvm}"
|
||||
NETWORK="${NETWORK:-vmbr0}"
|
||||
GATEWAY="${NETWORK_GATEWAY:-192.168.11.1}"
|
||||
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
|
||||
DRY_RUN=false
|
||||
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
|
||||
|
||||
if [[ ! -f "$NGINX_TEMPLATE" ]]; then
|
||||
echo "ERROR: Missing $NGINX_TEMPLATE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== Provision info.defi-oracle.io web LXC ==="
|
||||
echo "Proxmox: ${PROXMOX_HOST} VMID: ${VMID} IP: ${IP_CT}"
|
||||
|
||||
if $DRY_RUN; then
|
||||
echo "[DRY-RUN] pct create ${VMID} if missing, apt nginx, install ${SITE_FILE}, enable site"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct list 2>/dev/null | grep -q '^${VMID} '"; then
|
||||
echo "CT ${VMID} already exists — skipping pct create"
|
||||
else
|
||||
echo "Creating CT ${VMID} (${HOSTNAME_CT}) @ ${IP_CT}/24..."
|
||||
ssh $SSH_OPTS "root@${PROXMOX_HOST}" bash -s <<EOF
|
||||
set -euo pipefail
|
||||
pct create ${VMID} ${TEMPLATE_CT} \\
|
||||
--hostname ${HOSTNAME_CT} \\
|
||||
--memory 1024 \\
|
||||
--cores 1 \\
|
||||
--rootfs ${STORAGE}:8 \\
|
||||
--net0 name=eth0,bridge=${NETWORK},ip=${IP_CT}/24,gw=${GATEWAY} \\
|
||||
--nameserver ${DNS_PRIMARY:-1.1.1.1} \\
|
||||
--description 'Dedicated nginx static host: info.defi-oracle.io (Chain 138 SPA)' \\
|
||||
--start 1 \\
|
||||
--onboot 1 \\
|
||||
--unprivileged 1
|
||||
EOF
|
||||
echo "Waiting for CT to boot..."
|
||||
sleep 15
|
||||
fi
|
||||
|
||||
ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct status ${VMID}" | grep -q running || {
|
||||
echo "ERROR: CT ${VMID} not running — start with: ssh root@${PROXMOX_HOST} 'pct start ${VMID}'" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Installing nginx inside CT ${VMID}..."
|
||||
ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct exec ${VMID} -- bash -lc \"set -euo pipefail; export DEBIAN_FRONTEND=noninteractive; apt-get update -qq; apt-get install -y -qq nginx ca-certificates curl; mkdir -p '${APP_DIR}'; rm -f /etc/nginx/sites-enabled/default; systemctl enable nginx\""
|
||||
|
||||
echo "Installing nginx site config..."
|
||||
scp $SSH_OPTS "$NGINX_TEMPLATE" "root@${PROXMOX_HOST}:/tmp/info-defi-oracle-io.site.conf"
|
||||
ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct push ${VMID} /tmp/info-defi-oracle-io.site.conf ${SITE_FILE} && rm -f /tmp/info-defi-oracle-io.site.conf"
|
||||
ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct exec ${VMID} -- bash -lc \"ln -sf '${SITE_FILE}' /etc/nginx/sites-enabled/info-defi-oracle && nginx -t && systemctl reload nginx && sleep 1 && curl -fsS -H 'Host: info.defi-oracle.io' http://127.0.0.1/health >/dev/null\""
|
||||
|
||||
echo ""
|
||||
echo "✅ Dedicated web LXC ${VMID} ready at ${IP_CT}:80"
|
||||
echo " Next: bash scripts/deployment/sync-info-defi-oracle-to-vmid2400.sh"
|
||||
echo " NPM: point info.defi-oracle.io → http://${IP_CT}:80 (fleet: update-npmplus-proxy-hosts-api.sh)"
|
||||
85
scripts/deployment/provision-omdnl-org-web-lxc.sh
Executable file
85
scripts/deployment/provision-omdnl-org-web-lxc.sh
Executable file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env bash
|
||||
# Dedicated LXC: static nginx site for https://omdnl.org (and www).
|
||||
#
|
||||
# Defaults: VMID 10203, 192.168.11.222, Proxmox r630-01 (override PROXMOX_HOST).
|
||||
#
|
||||
# Usage (from a host with SSH to Proxmox):
|
||||
# bash scripts/deployment/provision-omdnl-org-web-lxc.sh [--dry-run]
|
||||
# Then:
|
||||
# bash scripts/deployment/sync-omdnl-org-static-to-ct.sh
|
||||
# bash scripts/cloudflare/configure-omdnl-org-dns.sh
|
||||
# bash scripts/nginx-proxy-manager/upsert-omdnl-org-proxy-host.sh
|
||||
# Request TLS in NPMplus UI (or scripts/request-npmplus-certificates.sh) once DNS resolves.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
# shellcheck source=/dev/null
|
||||
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
|
||||
|
||||
PROXMOX_HOST="${PROXMOX_HOST:-${PROXMOX_HOST_R630_01:-192.168.11.11}}"
|
||||
VMID="${OMDNL_ORG_WEB_VMID:-10203}"
|
||||
IP_CT="${IP_OMDNL_ORG_WEB:-192.168.11.222}"
|
||||
HOSTNAME_CT="${OMDNL_ORG_WEB_HOSTNAME:-omdnl-org-web}"
|
||||
APP_DIR="${OMDNL_ORG_WEB_ROOT:-/var/www/omdnl.org/html}"
|
||||
SITE_FILE="${OMDNL_ORG_NGINX_SITE:-/etc/nginx/sites-available/omdnl-org}"
|
||||
NGINX_TEMPLATE="${PROJECT_ROOT}/config/nginx/omdnl-org.site.conf"
|
||||
TEMPLATE_CT="${TEMPLATE:-local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst}"
|
||||
STORAGE="${STORAGE:-local-lvm}"
|
||||
NETWORK="${NETWORK:-vmbr0}"
|
||||
GATEWAY="${NETWORK_GATEWAY:-192.168.11.1}"
|
||||
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
|
||||
DRY_RUN=false
|
||||
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
|
||||
|
||||
if [[ ! -f "$NGINX_TEMPLATE" ]]; then
|
||||
echo "ERROR: Missing $NGINX_TEMPLATE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== Provision omdnl.org web LXC ==="
|
||||
echo "Proxmox: ${PROXMOX_HOST} VMID: ${VMID} IP: ${IP_CT}"
|
||||
|
||||
if $DRY_RUN; then
|
||||
echo "[DRY-RUN] pct create ${VMID} if missing, apt nginx, install ${SITE_FILE}, enable site"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct list 2>/dev/null | grep -q '^${VMID} '"; then
|
||||
echo "CT ${VMID} already exists — skipping pct create"
|
||||
else
|
||||
echo "Creating CT ${VMID} (${HOSTNAME_CT}) @ ${IP_CT}/24..."
|
||||
ssh $SSH_OPTS "root@${PROXMOX_HOST}" bash -s <<EOF
|
||||
set -euo pipefail
|
||||
pct create ${VMID} ${TEMPLATE_CT} \\
|
||||
--hostname ${HOSTNAME_CT} \\
|
||||
--memory 512 \\
|
||||
--cores 1 \\
|
||||
--rootfs ${STORAGE}:4 \\
|
||||
--net0 name=eth0,bridge=${NETWORK},ip=${IP_CT}/24,gw=${GATEWAY} \\
|
||||
--nameserver ${DNS_PRIMARY:-1.1.1.1} \\
|
||||
--description 'Static nginx: omdnl.org (SMOM + Absolute Realms central bank presence)' \\
|
||||
--start 1 \\
|
||||
--onboot 1 \\
|
||||
--unprivileged 1
|
||||
EOF
|
||||
echo "Waiting for CT to boot..."
|
||||
sleep 15
|
||||
fi
|
||||
|
||||
ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct status ${VMID}" | grep -q running || {
|
||||
echo "ERROR: CT ${VMID} not running — start with: ssh root@${PROXMOX_HOST} 'pct start ${VMID}'" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Installing nginx inside CT ${VMID}..."
|
||||
ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct exec ${VMID} -- bash -lc \"set -euo pipefail; export DEBIAN_FRONTEND=noninteractive; apt-get update -qq; apt-get install -y -qq nginx ca-certificates curl; mkdir -p '${APP_DIR}'; rm -f /etc/nginx/sites-enabled/default; systemctl enable nginx\""
|
||||
|
||||
echo "Installing nginx site config..."
|
||||
scp $SSH_OPTS "$NGINX_TEMPLATE" "root@${PROXMOX_HOST}:/tmp/omdnl-org.site.conf"
|
||||
ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct push ${VMID} /tmp/omdnl-org.site.conf ${SITE_FILE} && rm -f /tmp/omdnl-org.site.conf"
|
||||
ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct exec ${VMID} -- bash -lc \"ln -sf '${SITE_FILE}' /etc/nginx/sites-enabled/omdnl-org && nginx -t && systemctl reload nginx && sleep 1 && curl -fsS -H 'Host: omdnl.org' http://127.0.0.1/health >/dev/null\""
|
||||
|
||||
echo ""
|
||||
echo "✅ Web LXC ${VMID} ready at ${IP_CT}:80"
|
||||
echo " Next: bash scripts/deployment/sync-omdnl-org-static-to-ct.sh"
|
||||
227
scripts/deployment/provision-op-stack-operator-lxcs.sh
Executable file
227
scripts/deployment/provision-op-stack-operator-lxcs.sh
Executable file
@@ -0,0 +1,227 @@
|
||||
#!/usr/bin/env bash
|
||||
# Provision the OP Stack operator landing zone on Proxmox r630-02.
|
||||
#
|
||||
# Creates:
|
||||
# 5751 op-stack-deployer-1 (192.168.11.69)
|
||||
# 5752 op-stack-ops-1 (192.168.11.70)
|
||||
#
|
||||
# Installs baseline tooling, enables SSH, seeds repo OP Stack scaffolding,
|
||||
# and prepares /etc/op-stack and /opt/op-stack-bootstrap inside each CT.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/deployment/provision-op-stack-operator-lxcs.sh [--dry-run]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
source "$PROJECT_ROOT/.env" 2>/dev/null || true
|
||||
source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true
|
||||
|
||||
CT_PROXMOX_HOST="${OP_STACK_PROXMOX_HOST:-${PROXMOX_HOST_R630_02:-192.168.11.12}}"
|
||||
CT_STORAGE="${OP_STACK_PROXMOX_STORAGE:-thin5}"
|
||||
CT_TEMPLATE="${OP_STACK_PROXMOX_TEMPLATE:-local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst}"
|
||||
CT_NETWORK="${OP_STACK_PROXMOX_NETWORK:-vmbr0}"
|
||||
CT_GATEWAY="${OP_STACK_PROXMOX_GATEWAY:-${NETWORK_GATEWAY:-192.168.11.1}}"
|
||||
CT_NAMESERVER="${OP_STACK_PROXMOX_NAMESERVER:-${DNS_PRIMARY:-1.1.1.1}}"
|
||||
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
|
||||
|
||||
DRY_RUN=false
|
||||
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
|
||||
|
||||
SSH_PUBKEY_PATH="${SSH_PUBKEY_PATH:-$HOME/.ssh/id_ed25519_proxmox.pub}"
|
||||
if [[ ! -f "$SSH_PUBKEY_PATH" ]]; then
|
||||
SSH_PUBKEY_PATH="${HOME}/.ssh/id_ed25519.pub"
|
||||
fi
|
||||
if [[ ! -f "$SSH_PUBKEY_PATH" ]]; then
|
||||
echo "ERROR: No SSH public key found at ~/.ssh/id_ed25519_proxmox.pub or ~/.ssh/id_ed25519.pub"
|
||||
exit 1
|
||||
fi
|
||||
SSH_KEY_PATH="${SSH_PUBKEY_PATH%.pub}"
|
||||
DIRECT_CT_SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
|
||||
if [[ -f "$SSH_KEY_PATH" ]]; then
|
||||
DIRECT_CT_SSH_OPTS="-i $SSH_KEY_PATH $DIRECT_CT_SSH_OPTS"
|
||||
fi
|
||||
|
||||
BOOTSTRAP_TAR="$(mktemp /tmp/op-stack-bootstrap.XXXXXX.tgz)"
|
||||
REMOTE_BOOTSTRAP_TAR="/root/op-stack-bootstrap.tgz"
|
||||
REMOTE_PUBKEY="/root/op-stack-authorized_key.pub"
|
||||
trap 'rm -f "$BOOTSTRAP_TAR"' EXIT
|
||||
|
||||
CTS=(
|
||||
"${OP_STACK_DEPLOYER_VMID:-5751}|op-stack-deployer-1|${IP_OP_STACK_DEPLOYER_CT:-192.168.11.69}|8192|4|32|op-stack-deployer-operator-workspace"
|
||||
"${OP_STACK_OPS_VMID:-5752}|op-stack-ops-1|${IP_OP_STACK_OPS_CT:-192.168.11.70}|12288|6|48|op-stack-runtime-service-staging"
|
||||
)
|
||||
|
||||
tar -C "$PROJECT_ROOT" -czf "$BOOTSTRAP_TAR" \
|
||||
config/op-stack-superchain \
|
||||
config/wormhole \
|
||||
scripts/op-stack \
|
||||
scripts/wormhole \
|
||||
docs/03-deployment/OP_STACK_STANDARD_ROLLUP_SUPERCHAIN_RUNBOOK.md \
|
||||
docs/03-deployment/OP_STACK_L2_AND_BESU138_BRIDGE_NOTES.md \
|
||||
docs/03-deployment/WORMHOLE_NTT_EXECUTOR_OPERATOR_RUNBOOK.md \
|
||||
config/systemd/op-stack-batcher.example.service \
|
||||
config/systemd/op-stack-challenger.example.service \
|
||||
config/systemd/op-stack-op-node.example.service \
|
||||
config/systemd/op-stack-op-reth.example.service \
|
||||
config/systemd/op-stack-proposer.example.service \
|
||||
config/systemd/op-stack-sequencer.example.service
|
||||
|
||||
echo "=== OP Stack operator landing zone ==="
|
||||
echo "Proxmox host: $CT_PROXMOX_HOST"
|
||||
echo "Storage: $CT_STORAGE | Template: $CT_TEMPLATE | Network: $CT_NETWORK"
|
||||
echo "SSH public key: $SSH_PUBKEY_PATH"
|
||||
echo
|
||||
|
||||
for spec in "${CTS[@]}"; do
|
||||
IFS="|" read -r vmid hostname ip memory cores disk description <<<"$spec"
|
||||
echo " - CT $vmid $hostname @ $ip (${memory}MB RAM, ${cores} cores, ${disk}G disk)"
|
||||
done
|
||||
echo
|
||||
|
||||
if $DRY_RUN; then
|
||||
echo "[DRY-RUN] Would create/bootstrap the CTs above on $CT_PROXMOX_HOST."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
scp $SSH_OPTS "$SSH_PUBKEY_PATH" "root@${CT_PROXMOX_HOST}:${REMOTE_PUBKEY}"
|
||||
scp $SSH_OPTS "$BOOTSTRAP_TAR" "root@${CT_PROXMOX_HOST}:${REMOTE_BOOTSTRAP_TAR}"
|
||||
|
||||
for spec in "${CTS[@]}"; do
|
||||
IFS="|" read -r vmid hostname ip memory cores disk description <<<"$spec"
|
||||
echo "=== Provisioning CT ${vmid} (${hostname}) ==="
|
||||
|
||||
if ssh $SSH_OPTS "root@${CT_PROXMOX_HOST}" "pct list 2>/dev/null | grep -q '^${vmid} '"; then
|
||||
echo "CT ${vmid} already exists — skipping create"
|
||||
else
|
||||
ssh $SSH_OPTS "root@${CT_PROXMOX_HOST}" bash -s -- \
|
||||
"$vmid" "$hostname" "$memory" "$cores" "$disk" "$ip" "$description" \
|
||||
"$CT_TEMPLATE" "$CT_STORAGE" "$CT_NETWORK" "$CT_GATEWAY" "$CT_NAMESERVER" <<'REMOTE_CREATE'
|
||||
set -euo pipefail
|
||||
vmid="$1"
|
||||
hostname="$2"
|
||||
memory="$3"
|
||||
cores="$4"
|
||||
disk="$5"
|
||||
ip="$6"
|
||||
description="$7"
|
||||
template="$8"
|
||||
storage="$9"
|
||||
network="${10}"
|
||||
gateway="${11}"
|
||||
nameserver="${12}"
|
||||
|
||||
pct create "$vmid" "$template" \
|
||||
--hostname "$hostname" \
|
||||
--memory "$memory" \
|
||||
--cores "$cores" \
|
||||
--rootfs "${storage}:${disk}" \
|
||||
--net0 "name=eth0,bridge=${network},ip=${ip}/24,gw=${gateway}" \
|
||||
--nameserver "$nameserver" \
|
||||
--description "$description" \
|
||||
--start 1 \
|
||||
--onboot 1 \
|
||||
--unprivileged 0 \
|
||||
--features nesting=1,keyctl=1
|
||||
REMOTE_CREATE
|
||||
echo "Waiting for CT ${vmid} to boot..."
|
||||
sleep 20
|
||||
fi
|
||||
|
||||
ssh $SSH_OPTS "root@${CT_PROXMOX_HOST}" "pct start ${vmid} >/dev/null 2>&1 || true"
|
||||
|
||||
ssh $SSH_OPTS "root@${CT_PROXMOX_HOST}" "pct push ${vmid} ${REMOTE_PUBKEY} /root/op-stack-authorized_key.pub >/dev/null"
|
||||
ssh $SSH_OPTS "root@${CT_PROXMOX_HOST}" "pct push ${vmid} ${REMOTE_BOOTSTRAP_TAR} /root/op-stack-bootstrap.tgz >/dev/null"
|
||||
|
||||
ssh $SSH_OPTS "root@${CT_PROXMOX_HOST}" bash -s -- "$vmid" "$hostname" <<'REMOTE_BOOTSTRAP'
|
||||
set -euo pipefail
|
||||
vmid="$1"
|
||||
hostname="$2"
|
||||
|
||||
pct exec "$vmid" -- bash -lc '
|
||||
set -euo pipefail
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq \
|
||||
sudo openssh-server ca-certificates curl git jq rsync unzip zip \
|
||||
tar gzip xz-utils make build-essential tmux htop python3 python3-pip golang-go
|
||||
|
||||
systemctl enable ssh >/dev/null 2>&1 || systemctl enable ssh.service >/dev/null 2>&1 || true
|
||||
systemctl restart ssh >/dev/null 2>&1 || systemctl restart ssh.service >/dev/null 2>&1 || true
|
||||
|
||||
id -u opuser >/dev/null 2>&1 || useradd -m -s /bin/bash -G sudo opuser
|
||||
|
||||
install -d -m 700 /root/.ssh /home/opuser/.ssh
|
||||
install -d -m 755 /opt/op-stack /etc/op-stack /etc/op-stack/systemd-examples /opt/op-stack-bootstrap
|
||||
|
||||
if ! grep -qxF "$(cat /root/op-stack-authorized_key.pub)" /root/.ssh/authorized_keys 2>/dev/null; then
|
||||
cat /root/op-stack-authorized_key.pub >> /root/.ssh/authorized_keys
|
||||
fi
|
||||
if ! grep -qxF "$(cat /root/op-stack-authorized_key.pub)" /home/opuser/.ssh/authorized_keys 2>/dev/null; then
|
||||
cat /root/op-stack-authorized_key.pub >> /home/opuser/.ssh/authorized_keys
|
||||
fi
|
||||
|
||||
chown -R opuser:opuser /home/opuser/.ssh /opt/op-stack /opt/op-stack-bootstrap
|
||||
chmod 600 /root/.ssh/authorized_keys /home/opuser/.ssh/authorized_keys
|
||||
|
||||
echo "opuser ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/90-opuser
|
||||
chmod 440 /etc/sudoers.d/90-opuser
|
||||
|
||||
rm -rf /opt/op-stack-bootstrap/*
|
||||
tar -xzf /root/op-stack-bootstrap.tgz -C /opt/op-stack-bootstrap
|
||||
cp /opt/op-stack-bootstrap/config/op-stack-superchain/op-stack-l2.example.env /etc/op-stack/op-stack-l2.example.env
|
||||
cp /opt/op-stack-bootstrap/config/systemd/op-stack-*.example.service /etc/op-stack/systemd-examples/ 2>/dev/null || true
|
||||
|
||||
case "'"${hostname}"'" in
|
||||
op-stack-deployer-1)
|
||||
bash /opt/op-stack-bootstrap/scripts/op-stack/prepare-operator-ct.sh deployer
|
||||
;;
|
||||
op-stack-ops-1)
|
||||
bash /opt/op-stack-bootstrap/scripts/op-stack/prepare-operator-ct.sh ops
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "'"${hostname}"'" >/etc/hostname
|
||||
hostname "'"${hostname}"'"
|
||||
'
|
||||
REMOTE_BOOTSTRAP
|
||||
|
||||
ssh $DIRECT_CT_SSH_OPTS "opuser@${ip}" "hostname && id"
|
||||
done
|
||||
|
||||
echo "Refreshing governance TOMLs inside deployer CT if cache is missing..."
|
||||
ssh $DIRECT_CT_SSH_OPTS "opuser@${IP_OP_STACK_DEPLOYER_CT:-192.168.11.69}" '
|
||||
set -euo pipefail
|
||||
cache_dir=/opt/op-stack-bootstrap/config/op-stack-superchain/cache
|
||||
required=(
|
||||
standard-config-params-mainnet.toml
|
||||
standard-config-roles-mainnet.toml
|
||||
standard-versions-mainnet.toml
|
||||
)
|
||||
missing=0
|
||||
for file in "${required[@]}"; do
|
||||
if [[ ! -s "$cache_dir/$file" ]]; then
|
||||
missing=1
|
||||
fi
|
||||
done
|
||||
if [[ "${OP_STACK_REFRESH_TOMLS:-0}" == "1" || "$missing" == "1" ]]; then
|
||||
cd /opt/op-stack-bootstrap
|
||||
bash scripts/op-stack/fetch-standard-mainnet-toml.sh
|
||||
else
|
||||
echo "Using seeded governance TOML cache in $cache_dir"
|
||||
fi
|
||||
'
|
||||
|
||||
echo
|
||||
echo "✅ OP Stack operator landing zone ready:"
|
||||
echo " - 5751 op-stack-deployer-1 @ ${IP_OP_STACK_DEPLOYER_CT:-192.168.11.69}"
|
||||
echo " - 5752 op-stack-ops-1 @ ${IP_OP_STACK_OPS_CT:-192.168.11.70}"
|
||||
echo
|
||||
echo "Next:"
|
||||
echo " 1. SSH as opuser to the CTs"
|
||||
echo " 2. Copy live secrets/env into /etc/op-stack/"
|
||||
echo " 3. Install pinned Optimism binaries or packages"
|
||||
echo " 4. Run Sepolia rehearsal from 5751, then stage runtime services from 5752"
|
||||
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env bash
|
||||
# Publish the full GRU v2 Chain 138 metadata bundle to IPFS and write a symbol-keyed manifest.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
SOURCE_DIR="${PROJECT_ROOT}/config/gru-v2-metadata/chain138"
|
||||
IPFS_API="${IPFS_API:-http://192.168.11.35:5001}"
|
||||
MANIFEST_OUT="${SOURCE_DIR}/ipfs-manifest.latest.json"
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
echo "curl is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "jq is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tmpdir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmpdir"' EXIT
|
||||
|
||||
metadata_dirs=()
|
||||
while IFS= read -r dir; do
|
||||
metadata_dirs+=("$dir")
|
||||
done < <(find "$SOURCE_DIR" -mindepth 1 -maxdepth 1 -type d | sort)
|
||||
|
||||
if [[ "${#metadata_dirs[@]}" -eq 0 ]]; then
|
||||
echo "No metadata directories found under $SOURCE_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for dir in "${metadata_dirs[@]}"; do
|
||||
base="$(basename "$dir")"
|
||||
mkdir -p "$tmpdir/$base"
|
||||
cp "$dir/"*.json "$tmpdir/$base/"
|
||||
done
|
||||
|
||||
curl_args=(-sS -X POST "${IPFS_API}/api/v0/add?pin=true&wrap-with-directory=true")
|
||||
for dir in "${metadata_dirs[@]}"; do
|
||||
base="$(basename "$dir")"
|
||||
for file in metadata.json regulatory-disclosure.json reporting.json; do
|
||||
curl_args+=(-F "file=@${tmpdir}/${base}/${file};filename=${base}/${file}")
|
||||
done
|
||||
done
|
||||
|
||||
response_file="$tmpdir/ipfs-add.jsonl"
|
||||
curl "${curl_args[@]}" > "$response_file"
|
||||
|
||||
root_cid="$(tail -n1 "$response_file" | jq -r '.Hash')"
|
||||
timestamp="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
||||
|
||||
assets_json="$(
|
||||
{
|
||||
for dir in "${metadata_dirs[@]}"; do
|
||||
base="$(basename "$dir")"
|
||||
symbol="$(jq -r '.symbol' "$dir/metadata.json")"
|
||||
jq -n \
|
||||
--arg symbol "$symbol" \
|
||||
--arg key "$base" \
|
||||
--arg root "$root_cid" \
|
||||
'{
|
||||
($symbol): {
|
||||
metadataKey: $key,
|
||||
tokenURI: ("ipfs://" + $root + "/" + $key + "/metadata.json"),
|
||||
regulatoryDisclosureURI: ("ipfs://" + $root + "/" + $key + "/regulatory-disclosure.json"),
|
||||
reportingURI: ("ipfs://" + $root + "/" + $key + "/reporting.json")
|
||||
}
|
||||
}'
|
||||
done
|
||||
} | jq -s 'add'
|
||||
)"
|
||||
|
||||
jq -n \
|
||||
--arg publishedAt "$timestamp" \
|
||||
--arg rootCID "$root_cid" \
|
||||
--arg ipfsApi "$IPFS_API" \
|
||||
--argjson assets "$assets_json" \
|
||||
'{
|
||||
chainId: 138,
|
||||
publishedAt: $publishedAt,
|
||||
rootCID: $rootCID,
|
||||
ipfsApi: $ipfsApi,
|
||||
assets: $assets
|
||||
}' > "$MANIFEST_OUT"
|
||||
|
||||
echo "Published GRU v2 metadata bundle to IPFS."
|
||||
echo "Root CID: ${root_cid}"
|
||||
echo "Manifest: ${MANIFEST_OUT}"
|
||||
echo
|
||||
echo "Per-symbol URIs:"
|
||||
jq -r '.assets | to_entries[] | "- \(.key): \(.value.tokenURI)"' "$MANIFEST_OUT"
|
||||
57
scripts/deployment/push-token-aggregation-bundle-to-explorer.sh
Executable file
57
scripts/deployment/push-token-aggregation-bundle-to-explorer.sh
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copy a local publication bundle (from deploy-token-aggregation-for-publication.sh) to the explorer host
|
||||
# and optionally restart systemd. Run from repo root on LAN.
|
||||
#
|
||||
# Prereq:
|
||||
# bash scripts/deploy-token-aggregation-for-publication.sh "$PWD/token-aggregation-build"
|
||||
#
|
||||
# Usage:
|
||||
# EXPLORER_SSH=root@192.168.11.140 REMOTE_DIR=/opt/token-aggregation \
|
||||
# bash scripts/deployment/push-token-aggregation-bundle-to-explorer.sh /path/to/token-aggregation-build
|
||||
#
|
||||
# Env:
|
||||
# EXPLORER_SSH default root@192.168.11.140
|
||||
# REMOTE_DIR default /opt/token-aggregation
|
||||
# REMOTE_SERVICE systemd unit to restart (default: token-aggregation); empty to skip restart
|
||||
# SYNC_REMOTE_ENV set to 1 to allow bundle .env to overwrite remote .env (default: preserve remote env)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
BUNDLE_ROOT="${1:?Usage: $0 /path/to/token-aggregation-build}"
|
||||
SERVICE_SRC="$BUNDLE_ROOT/smom-dbis-138/services/token-aggregation"
|
||||
EXPLORER_SSH="${EXPLORER_SSH:-root@192.168.11.140}"
|
||||
REMOTE_DIR="${REMOTE_DIR:-/opt/token-aggregation}"
|
||||
REMOTE_SERVICE="${REMOTE_SERVICE:-token-aggregation}"
|
||||
SYNC_REMOTE_ENV="${SYNC_REMOTE_ENV:-0}"
|
||||
|
||||
if [[ ! -d "$SERVICE_SRC" || ! -f "$SERVICE_SRC/dist/index.js" ]]; then
|
||||
echo "Expected built service at $SERVICE_SRC (run deploy-token-aggregation-for-publication.sh first)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Rsync $SERVICE_SRC/ → ${EXPLORER_SSH}:${REMOTE_DIR}/"
|
||||
rsync_args=(
|
||||
-avz
|
||||
--delete
|
||||
--exclude '.git'
|
||||
--exclude 'src'
|
||||
--exclude '*.test.ts'
|
||||
)
|
||||
|
||||
if [[ "$SYNC_REMOTE_ENV" != "1" ]]; then
|
||||
rsync_args+=(--exclude '.env' --exclude '.env.local')
|
||||
echo "Preserving remote .env files (set SYNC_REMOTE_ENV=1 to overwrite them)."
|
||||
fi
|
||||
|
||||
RSYNC_RSH="ssh -o BatchMode=yes" rsync "${rsync_args[@]}" \
|
||||
"$SERVICE_SRC/" "${EXPLORER_SSH}:${REMOTE_DIR}/"
|
||||
|
||||
if [[ -n "$REMOTE_SERVICE" ]]; then
|
||||
echo "Restart ${REMOTE_SERVICE} on ${EXPLORER_SSH}..."
|
||||
ssh -o BatchMode=yes "$EXPLORER_SSH" "systemctl restart '${REMOTE_SERVICE}'" || {
|
||||
echo "systemctl restart failed (unit may differ). Start manually: cd $REMOTE_DIR && node dist/index.js" >&2
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
echo "Done. Verify: BASE_URL=https://explorer.d-bis.org pnpm run verify:token-aggregation-api"
|
||||
245
scripts/deployment/recover-chain138-eoa-nonce-gaps.sh
Executable file
245
scripts/deployment/recover-chain138-eoa-nonce-gaps.sh
Executable file
@@ -0,0 +1,245 @@
|
||||
#!/usr/bin/env bash
|
||||
# Diagnose and optionally fill nonce gaps for a Chain 138 EOA whose future txs are
|
||||
# stranded in Besu txpool_besuPendingTransactions.
|
||||
#
|
||||
# Default mode is dry-run. Use --apply to actually send gap-filler self-transfers.
|
||||
#
|
||||
# Example:
|
||||
# PRIVATE_KEY=0xabc... bash scripts/deployment/recover-chain138-eoa-nonce-gaps.sh \
|
||||
# --rpc http://192.168.11.217:8545
|
||||
#
|
||||
# bash scripts/deployment/recover-chain138-eoa-nonce-gaps.sh \
|
||||
# --private-key 0xabc... \
|
||||
# --rpc http://192.168.11.217:8545 \
|
||||
# --apply
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
if [[ -f "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
RPC_URL="${RPC_URL_138:-http://192.168.11.211:8545}"
|
||||
PRIVATE_KEY_INPUT="${PRIVATE_KEY:-${DEPLOYER_PRIVATE_KEY:-}}"
|
||||
APPLY=0
|
||||
CHAIN_ID=138
|
||||
GAS_LIMIT=21000
|
||||
GAS_BUMP_MULTIPLIER=2
|
||||
MIN_GAS_WEI=2000
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: recover-chain138-eoa-nonce-gaps.sh [options]
|
||||
|
||||
Options:
|
||||
--private-key <hex> Private key for the EOA owner.
|
||||
--rpc <url> Chain 138 RPC URL. Default: RPC_URL_138 or http://192.168.11.211:8545
|
||||
--apply Actually send self-transfers for missing nonces.
|
||||
--min-gas-wei <n> Floor gas price / max fee in wei. Default: 2000
|
||||
--gas-multiplier <n> Multiply current eth_gasPrice by this factor. Default: 2
|
||||
--help Show this help.
|
||||
|
||||
Behavior:
|
||||
1. Derives the owner address from the provided private key.
|
||||
2. Reads latest nonce and txpool_besuPendingTransactions for that address.
|
||||
3. Finds any missing nonce gaps between latest nonce and the highest pending nonce.
|
||||
4. In --apply mode, sends 0-value self-transfers for those missing nonces.
|
||||
|
||||
Notes:
|
||||
- Dry-run is the default and is recommended first.
|
||||
- This only fills missing nonce gaps. It does not cancel every future tx.
|
||||
- Requires: cast, curl, jq
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--private-key)
|
||||
PRIVATE_KEY_INPUT="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--rpc)
|
||||
RPC_URL="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--apply)
|
||||
APPLY=1
|
||||
shift
|
||||
;;
|
||||
--min-gas-wei)
|
||||
MIN_GAS_WEI="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--gas-multiplier)
|
||||
GAS_BUMP_MULTIPLIER="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--help|-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
for cmd in cast curl jq; do
|
||||
command -v "$cmd" >/dev/null 2>&1 || {
|
||||
echo "Missing required command: $cmd" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
if [[ -z "$PRIVATE_KEY_INPUT" ]]; then
|
||||
echo "Missing private key. Pass --private-key or set PRIVATE_KEY." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OWNER_ADDRESS="$(cast wallet address --private-key "$PRIVATE_KEY_INPUT" 2>/dev/null || true)"
|
||||
if [[ -z "$OWNER_ADDRESS" ]]; then
|
||||
echo "Could not derive owner address from private key." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OWNER_ADDRESS_LC="$(printf '%s' "$OWNER_ADDRESS" | tr '[:upper:]' '[:lower:]')"
|
||||
|
||||
rpc_call() {
|
||||
local method="$1"
|
||||
local params_json="$2"
|
||||
curl -fsS "$RPC_URL" \
|
||||
-H 'content-type: application/json' \
|
||||
--data "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"${method}\",\"params\":${params_json}}"
|
||||
}
|
||||
|
||||
hex_to_dec() {
|
||||
cast --to-dec "$1"
|
||||
}
|
||||
|
||||
latest_hex="$(rpc_call eth_getTransactionCount "[\"${OWNER_ADDRESS}\",\"latest\"]" | jq -r '.result // "0x0"')"
|
||||
pending_hex="$(rpc_call eth_getTransactionCount "[\"${OWNER_ADDRESS}\",\"pending\"]" | jq -r '.result // "0x0"')"
|
||||
gas_hex="$(rpc_call eth_gasPrice "[]" | jq -r '.result // "0x0"')"
|
||||
latest_nonce="$(hex_to_dec "$latest_hex")"
|
||||
pending_nonce="$(hex_to_dec "$pending_hex")"
|
||||
network_gas_wei="$(hex_to_dec "$gas_hex")"
|
||||
|
||||
pending_json="$(rpc_call txpool_besuPendingTransactions "[]")"
|
||||
owner_pending_json="$(printf '%s' "$pending_json" | jq --arg owner "$OWNER_ADDRESS_LC" '
|
||||
[
|
||||
.result[]?
|
||||
| select(((.from // .sender // "") | ascii_downcase) == $owner)
|
||||
| {
|
||||
hash,
|
||||
nonce_hex: .nonce,
|
||||
to,
|
||||
gas,
|
||||
gasPrice,
|
||||
maxFeePerGas,
|
||||
maxPriorityFeePerGas,
|
||||
type
|
||||
}
|
||||
]
|
||||
')"
|
||||
|
||||
owner_pending_json="$(
|
||||
printf '%s' "$owner_pending_json" | jq '
|
||||
map(. + {
|
||||
nonce: (.nonce_hex | sub("^0x"; "") | if . == "" then "0" else . end)
|
||||
})
|
||||
'
|
||||
)"
|
||||
|
||||
owner_pending_count="$(printf '%s' "$owner_pending_json" | jq 'length')"
|
||||
highest_pending_nonce="$latest_nonce"
|
||||
if [[ "$owner_pending_count" -gt 0 ]]; then
|
||||
highest_pending_nonce="$(
|
||||
printf '%s' "$owner_pending_json" \
|
||||
| jq -r '.[].nonce' \
|
||||
| while read -r nonce_hex; do cast --to-dec "0x${nonce_hex}"; done \
|
||||
| sort -n \
|
||||
| tail -n1
|
||||
)"
|
||||
fi
|
||||
|
||||
present_nonce_csv=","
|
||||
if [[ "$owner_pending_count" -gt 0 ]]; then
|
||||
while read -r nonce_dec; do
|
||||
present_nonce_csv+="${nonce_dec},"
|
||||
done < <(
|
||||
printf '%s' "$owner_pending_json" \
|
||||
| jq -r '.[].nonce' \
|
||||
| while read -r nonce_hex; do cast --to-dec "0x${nonce_hex}"; done
|
||||
)
|
||||
fi
|
||||
missing_nonces=()
|
||||
for ((nonce = latest_nonce; nonce <= highest_pending_nonce; nonce++)); do
|
||||
if [[ "$present_nonce_csv" != *",$nonce,"* ]]; then
|
||||
missing_nonces+=("$nonce")
|
||||
fi
|
||||
done
|
||||
|
||||
effective_gas_wei="$(( network_gas_wei * GAS_BUMP_MULTIPLIER ))"
|
||||
if (( effective_gas_wei < MIN_GAS_WEI )); then
|
||||
effective_gas_wei="$MIN_GAS_WEI"
|
||||
fi
|
||||
|
||||
echo "=== Chain 138 EOA nonce-gap recovery ==="
|
||||
echo "Owner address: $OWNER_ADDRESS"
|
||||
echo "RPC URL: $RPC_URL"
|
||||
echo "Latest nonce: $latest_nonce"
|
||||
echo "Pending nonce view: $pending_nonce"
|
||||
echo "Network gas price: $network_gas_wei wei"
|
||||
echo "Planned gas price: $effective_gas_wei wei"
|
||||
echo "Pending tx count: $owner_pending_count"
|
||||
echo "Highest pending nonce:${highest_pending_nonce}"
|
||||
echo ""
|
||||
|
||||
if [[ "$owner_pending_count" -gt 0 ]]; then
|
||||
echo "--- Pending txs for owner in txpool_besuPendingTransactions ---"
|
||||
printf '%s\n' "$owner_pending_json" | jq '.'
|
||||
echo ""
|
||||
else
|
||||
echo "No owner txs found in txpool_besuPendingTransactions."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [[ "${#missing_nonces[@]}" -eq 0 ]]; then
|
||||
echo "No nonce gaps detected between latest nonce and highest pending nonce."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "--- Missing nonce gaps ---"
|
||||
printf 'Missing nonces: %s\n' "${missing_nonces[*]}"
|
||||
echo ""
|
||||
|
||||
if [[ "$APPLY" -ne 1 ]]; then
|
||||
echo "Dry-run only. Re-run with --apply to send 0-value self-transfers for the missing nonces above."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "--- Applying gap fillers ---"
|
||||
for nonce in "${missing_nonces[@]}"; do
|
||||
echo "Sending self-transfer for nonce $nonce ..."
|
||||
cast send "$OWNER_ADDRESS" \
|
||||
--private-key "$PRIVATE_KEY_INPUT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--nonce "$nonce" \
|
||||
--value 0 \
|
||||
--gas-limit "$GAS_LIMIT" \
|
||||
--gas-price "$effective_gas_wei" \
|
||||
>/tmp/recover-chain138-eoa-nonce-gaps.out 2>/tmp/recover-chain138-eoa-nonce-gaps.err || {
|
||||
echo "Failed to send nonce $nonce" >&2
|
||||
cat /tmp/recover-chain138-eoa-nonce-gaps.err >&2 || true
|
||||
exit 1
|
||||
}
|
||||
cat /tmp/recover-chain138-eoa-nonce-gaps.out
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Submitted gap-filler transactions. Wait for inclusion, then rerun this script in dry-run mode."
|
||||
253
scripts/deployment/replace-mainnet-public-dodo-cw-pool.sh
Executable file
253
scripts/deployment/replace-mainnet-public-dodo-cw-pool.sh
Executable file
@@ -0,0 +1,253 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Replace a legacy Mainnet public DODO cW/USD pool in the integration mapping,
|
||||
# create a canonical factory-backed replacement, and seed it with matched liquidity.
|
||||
#
|
||||
# Example:
|
||||
# bash scripts/deployment/replace-mainnet-public-dodo-cw-pool.sh \
|
||||
# --pair=cwusdc-usdc \
|
||||
# --initial-price=1000000000000000000 \
|
||||
# --base-amount=100000000 \
|
||||
# --quote-amount=100000000 \
|
||||
# --dry-run
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
source "${PROJECT_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd cast
|
||||
|
||||
PAIR=""
|
||||
INITIAL_PRICE=""
|
||||
BASE_AMOUNT=""
|
||||
QUOTE_AMOUNT=""
|
||||
FEE_BPS="3"
|
||||
K_VALUE="0"
|
||||
OPEN_TWAP="false"
|
||||
DRY_RUN=0
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--pair=*) PAIR="${arg#*=}" ;;
|
||||
--initial-price=*) INITIAL_PRICE="${arg#*=}" ;;
|
||||
--base-amount=*) BASE_AMOUNT="${arg#*=}" ;;
|
||||
--quote-amount=*) QUOTE_AMOUNT="${arg#*=}" ;;
|
||||
--fee-bps=*) FEE_BPS="${arg#*=}" ;;
|
||||
--k=*) K_VALUE="${arg#*=}" ;;
|
||||
--open-twap=*) OPEN_TWAP="${arg#*=}" ;;
|
||||
--dry-run) DRY_RUN=1 ;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$PAIR" || -z "$INITIAL_PRICE" || -z "$BASE_AMOUNT" || -z "$QUOTE_AMOUNT" ]]; then
|
||||
echo "[fail] required args: --pair, --initial-price, --base-amount, --quote-amount" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-}"
|
||||
INTEGRATION="${DODO_PMM_INTEGRATION_MAINNET:-}"
|
||||
USDC_MAINNET="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
USDT_MAINNET="0xdAC17F958D2ee523a2206206994597C13D831ec7"
|
||||
|
||||
if [[ -z "$RPC_URL" || -z "$PRIVATE_KEY" || -z "$INTEGRATION" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC, PRIVATE_KEY, and DODO_PMM_INTEGRATION_MAINNET are required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEPLOYER="$(cast wallet address --private-key "$PRIVATE_KEY")"
|
||||
|
||||
BASE_TOKEN=""
|
||||
QUOTE_TOKEN=""
|
||||
LEGACY_POOL=""
|
||||
PAIR_LABEL=""
|
||||
ENV_KEY=""
|
||||
|
||||
case "$PAIR" in
|
||||
cwusdc-usdc)
|
||||
BASE_TOKEN="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
QUOTE_TOKEN="$USDC_MAINNET"
|
||||
LEGACY_POOL="${POOL_CWUSDC_USDC_MAINNET:-0xB227dDA4e89E2EA63A1345509C9AC02644e8d526}"
|
||||
PAIR_LABEL="cWUSDC/USDC"
|
||||
ENV_KEY="POOL_CWUSDC_USDC_MAINNET"
|
||||
;;
|
||||
cwusdt-usdc)
|
||||
BASE_TOKEN="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
||||
QUOTE_TOKEN="$USDC_MAINNET"
|
||||
LEGACY_POOL="${POOL_CWUSDT_USDC_MAINNET:-0x27f3aE7EE71Be3d77bAf17d4435cF8B895DD25D2}"
|
||||
PAIR_LABEL="cWUSDT/USDC"
|
||||
ENV_KEY="POOL_CWUSDT_USDC_MAINNET"
|
||||
;;
|
||||
cwusdt-usdt)
|
||||
BASE_TOKEN="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
||||
QUOTE_TOKEN="$USDT_MAINNET"
|
||||
LEGACY_POOL="${POOL_CWUSDT_USDT_MAINNET:-0x79156F6B7bf71a1B72D78189B540A89A6C13F6FC}"
|
||||
PAIR_LABEL="cWUSDT/USDT"
|
||||
ENV_KEY="POOL_CWUSDT_USDT_MAINNET"
|
||||
;;
|
||||
cwusdc-usdt)
|
||||
BASE_TOKEN="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
QUOTE_TOKEN="$USDT_MAINNET"
|
||||
LEGACY_POOL="${POOL_CWUSDC_USDT_MAINNET:-0xCC0fd27A40775c9AfcD2BBd3f7c902b0192c247A}"
|
||||
PAIR_LABEL="cWUSDC/USDT"
|
||||
ENV_KEY="POOL_CWUSDC_USDT_MAINNET"
|
||||
;;
|
||||
*)
|
||||
echo "[fail] unsupported pair: $PAIR" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
bool_to_cli() {
|
||||
if [[ "$1" == "true" ]]; then
|
||||
printf 'true'
|
||||
else
|
||||
printf 'false'
|
||||
fi
|
||||
}
|
||||
|
||||
parse_tx_hash() {
|
||||
local output="$1"
|
||||
local tx_hash
|
||||
tx_hash="$(printf '%s\n' "$output" | grep -E '^0x[0-9a-fA-F]{64}$' | tail -n1 || true)"
|
||||
if [[ -z "$tx_hash" ]]; then
|
||||
tx_hash="$(printf '%s\n' "$output" | grep -E '^transactionHash[[:space:]]+0x[0-9a-fA-F]{64}$' | awk '{print $2}' | tail -n1 || true)"
|
||||
fi
|
||||
if [[ -z "$tx_hash" ]]; then
|
||||
return 1
|
||||
fi
|
||||
printf '%s\n' "$tx_hash"
|
||||
}
|
||||
|
||||
mapped_pool_before="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$BASE_TOKEN" "$QUOTE_TOKEN" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
base_balance_before="$(cast call "$BASE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
quote_balance_before="$(cast call "$QUOTE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
base_allowance_before="$(cast call "$BASE_TOKEN" 'allowance(address,address)(uint256)' "$DEPLOYER" "$INTEGRATION" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
quote_allowance_before="$(cast call "$QUOTE_TOKEN" 'allowance(address,address)(uint256)' "$DEPLOYER" "$INTEGRATION" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
if (( base_balance_before < BASE_AMOUNT )); then
|
||||
echo "[fail] insufficient base balance: have=$base_balance_before need=$BASE_AMOUNT" >&2
|
||||
exit 1
|
||||
fi
|
||||
if (( quote_balance_before < QUOTE_AMOUNT )); then
|
||||
echo "[fail] insufficient quote balance: have=$quote_balance_before need=$QUOTE_AMOUNT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
remove_gas="$(cast estimate --from "$DEPLOYER" "$INTEGRATION" 'removePool(address)' "$LEGACY_POOL" --rpc-url "$RPC_URL")"
|
||||
create_gas="post-remove estimate not available while current mapping exists"
|
||||
|
||||
if (( DRY_RUN == 1 )); then
|
||||
echo "pair=$PAIR_LABEL"
|
||||
echo "envKey=$ENV_KEY"
|
||||
echo "legacyPool=$LEGACY_POOL"
|
||||
echo "mappedPoolBefore=$mapped_pool_before"
|
||||
echo "baseToken=$BASE_TOKEN"
|
||||
echo "quoteToken=$QUOTE_TOKEN"
|
||||
echo "initialPrice=$INITIAL_PRICE"
|
||||
echo "feeBps=$FEE_BPS"
|
||||
echo "k=$K_VALUE"
|
||||
echo "openTwap=$OPEN_TWAP"
|
||||
echo "baseAmount=$BASE_AMOUNT"
|
||||
echo "quoteAmount=$QUOTE_AMOUNT"
|
||||
echo "removeGas=$remove_gas"
|
||||
echo "createGas=$create_gas"
|
||||
echo "baseBalanceBefore=$base_balance_before"
|
||||
echo "quoteBalanceBefore=$quote_balance_before"
|
||||
echo "baseAllowanceBefore=$base_allowance_before"
|
||||
echo "quoteAllowanceBefore=$quote_allowance_before"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
remove_output="$(
|
||||
cast send "$INTEGRATION" \
|
||||
'removePool(address)' \
|
||||
"$LEGACY_POOL" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
remove_tx="$(parse_tx_hash "$remove_output")"
|
||||
|
||||
create_output="$(
|
||||
cast send "$INTEGRATION" \
|
||||
'createPool(address,address,uint256,uint256,uint256,bool)(address)' \
|
||||
"$BASE_TOKEN" "$QUOTE_TOKEN" "$FEE_BPS" "$INITIAL_PRICE" "$K_VALUE" "$(bool_to_cli "$OPEN_TWAP")" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
create_tx="$(parse_tx_hash "$create_output")"
|
||||
|
||||
new_pool="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$BASE_TOKEN" "$QUOTE_TOKEN" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
if [[ "$new_pool" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
echo "[fail] replacement pool mapping is empty after createPool" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
approve_base_tx=""
|
||||
approve_quote_tx=""
|
||||
|
||||
if (( base_allowance_before < BASE_AMOUNT )); then
|
||||
approve_base_output="$(
|
||||
cast send "$BASE_TOKEN" \
|
||||
'approve(address,uint256)(bool)' \
|
||||
"$INTEGRATION" "$BASE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
approve_base_tx="$(parse_tx_hash "$approve_base_output")"
|
||||
fi
|
||||
|
||||
if (( quote_allowance_before < QUOTE_AMOUNT )); then
|
||||
approve_quote_output="$(
|
||||
cast send "$QUOTE_TOKEN" \
|
||||
'approve(address,uint256)(bool)' \
|
||||
"$INTEGRATION" "$QUOTE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
approve_quote_tx="$(parse_tx_hash "$approve_quote_output")"
|
||||
fi
|
||||
|
||||
add_output="$(
|
||||
cast send "$INTEGRATION" \
|
||||
'addLiquidity(address,uint256,uint256)(uint256,uint256,uint256)' \
|
||||
"$new_pool" "$BASE_AMOUNT" "$QUOTE_AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
add_liquidity_tx="$(parse_tx_hash "$add_output")"
|
||||
|
||||
reserves_after="$(cast call "$new_pool" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL")"
|
||||
base_reserve_after="$(printf '%s\n' "$reserves_after" | sed -n '1p' | awk '{print $1}')"
|
||||
quote_reserve_after="$(printf '%s\n' "$reserves_after" | sed -n '2p' | awk '{print $1}')"
|
||||
base_balance_after="$(cast call "$BASE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
quote_balance_after="$(cast call "$QUOTE_TOKEN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
echo "pair=$PAIR_LABEL"
|
||||
echo "envKey=$ENV_KEY"
|
||||
echo "legacyPool=$LEGACY_POOL"
|
||||
echo "newPool=$new_pool"
|
||||
echo "removeTx=$remove_tx"
|
||||
echo "createTx=$create_tx"
|
||||
echo "approveBaseTx=${approve_base_tx:-none}"
|
||||
echo "approveQuoteTx=${approve_quote_tx:-none}"
|
||||
echo "addLiquidityTx=$add_liquidity_tx"
|
||||
echo "baseReserveAfter=$base_reserve_after"
|
||||
echo "quoteReserveAfter=$quote_reserve_after"
|
||||
echo "baseBalanceBefore=$base_balance_before"
|
||||
echo "baseBalanceAfter=$base_balance_after"
|
||||
echo "quoteBalanceBefore=$quote_balance_before"
|
||||
echo "quoteBalanceAfter=$quote_balance_after"
|
||||
62
scripts/deployment/revoke-eip7702-delegation.sh
Executable file
62
scripts/deployment/revoke-eip7702-delegation.sh
Executable file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env bash
|
||||
# Clear EIP-7702 delegation on an EOA by signing authorization for the zero address (EIP-7702).
|
||||
# When the transaction sender is the same account as the authority, the authorization tuple
|
||||
# nonce must equal the account nonce AFTER the outer tx nonce is consumed (pending + 1).
|
||||
#
|
||||
# Usage (repo root):
|
||||
# ./scripts/deployment/revoke-eip7702-delegation.sh [--dry-run] [--rpc-url URL] [--chain ID]
|
||||
#
|
||||
# Env: PRIVATE_KEY (via scripts/lib/load-project-env.sh + smom-dbis-138/.env).
|
||||
# Default RPC: ETHEREUM_MAINNET_RPC; default chain: 1.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
# shellcheck source=../../lib/load-project-env.sh
|
||||
source "$PROJECT_ROOT/scripts/lib/load-project-env.sh"
|
||||
|
||||
DRY_RUN=false
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
CHAIN_ID="${CHAIN_ID:-1}"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--dry-run) DRY_RUN=true ;;
|
||||
--rpc-url=*) RPC_URL="${1#*=}" ;;
|
||||
--rpc-url) RPC_URL="${2:-}"; shift ;;
|
||||
--chain=*) CHAIN_ID="${1#*=}" ;;
|
||||
--chain) CHAIN_ID="${2:-}"; shift ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "ERROR: PRIVATE_KEY not set" >&2; exit 1; }
|
||||
[[ -n "$RPC_URL" ]] || { echo "ERROR: Set ETHEREUM_MAINNET_RPC or pass --rpc-url" >&2; exit 1; }
|
||||
|
||||
DEPLOYER="$(cast wallet address --private-key "$PRIVATE_KEY")"
|
||||
CODE="$(cast code "$DEPLOYER" --rpc-url "$RPC_URL" 2>/dev/null || true)"
|
||||
if [[ "$CODE" == "0x" || -z "$CODE" ]]; then
|
||||
echo "No contract/delegation code at $DEPLOYER (already plain EOA on this RPC)."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
NEXT_NONCE="$(cast nonce "$DEPLOYER" --rpc-url "$RPC_URL")"
|
||||
AUTH_NONCE=$((NEXT_NONCE + 1))
|
||||
echo "Deployer $DEPLOYER — pending tx nonce $NEXT_NONCE; EIP-7702 auth nonce (must be +1) $AUTH_NONCE"
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo "Dry-run: would sign auth for 0x0 with --nonce $AUTH_NONCE and cast send (type 4) to self."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
AUTH="$(cast wallet sign-auth 0x0000000000000000000000000000000000000000 \
|
||||
--rpc-url "$RPC_URL" --chain "$CHAIN_ID" --private-key "$PRIVATE_KEY" --nonce "$AUTH_NONCE")"
|
||||
|
||||
cast send "$DEPLOYER" \
|
||||
--rpc-url "$RPC_URL" --chain "$CHAIN_ID" --private-key "$PRIVATE_KEY" \
|
||||
--value 0 --auth "$AUTH" \
|
||||
--gas-limit 100000
|
||||
|
||||
echo "Verify: cast code $DEPLOYER --rpc-url <rpc>"
|
||||
@@ -7,7 +7,7 @@
|
||||
# --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_*.
|
||||
# --verify For each chain with configured cW* tokens, check MINTER_ROLE/BURNER_ROLE on those tokens for CW_BRIDGE_*.
|
||||
# --verify-hard-peg Check Avalanche hard-peg bridge controls for cWUSDT/cWUSDC.
|
||||
# With no options, runs --dry-run then --update-mapping (if any CWUSDT_* in .env).
|
||||
set -euo pipefail
|
||||
@@ -39,16 +39,26 @@ if [[ ! -f "$SMOM/.env" ]]; then
|
||||
echo "Missing $SMOM/.env" >&2
|
||||
exit 1
|
||||
fi
|
||||
set -a
|
||||
set +u
|
||||
source "$SMOM/.env"
|
||||
set -u
|
||||
set +a
|
||||
|
||||
if [[ -f "$SMOM/scripts/lib/deployment/dotenv.sh" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$SMOM/scripts/lib/deployment/dotenv.sh"
|
||||
load_deployment_env --repo-root "$SMOM"
|
||||
else
|
||||
set -a
|
||||
set +u
|
||||
source "$SMOM/.env"
|
||||
set -u
|
||||
set +a
|
||||
if [[ -z "${PRIVATE_KEY:-}" && -n "${DEPLOYER_PRIVATE_KEY:-}" ]]; then
|
||||
export PRIVATE_KEY="$DEPLOYER_PRIVATE_KEY"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Chain name (env suffix) -> chainId for 138 -> chain pairs
|
||||
declare -A CHAIN_NAME_TO_ID=(
|
||||
[MAINNET]=1 [CRONOS]=25 [BSC]=56 [POLYGON]=137 [GNOSIS]=100
|
||||
[AVALANCHE]=43114 [BASE]=8453 [ARBITRUM]=42161 [OPTIMISM]=10 [ALL]=651940
|
||||
[AVALANCHE]=43114 [BASE]=8453 [ARBITRUM]=42161 [OPTIMISM]=10 [CELO]=42220 [WEMIX]=1111 [ALL]=651940
|
||||
)
|
||||
|
||||
if $DO_DEPLOY; then
|
||||
@@ -79,8 +89,11 @@ function loadEnv(f) {
|
||||
return out;
|
||||
}
|
||||
const env = loadEnv('$env_file');
|
||||
const chainToId = { MAINNET: 1, CRONOS: 25, BSC: 56, POLYGON: 137, GNOSIS: 100, AVALANCHE: 43114, BASE: 8453, ARBITRUM: 42161, OPTIMISM: 10, ALL: 651940 };
|
||||
const keyToEnv = { Compliant_USDT_cW: 'CWUSDT', Compliant_USDC_cW: 'CWUSDC', Compliant_EURC_cW: 'CWEURC', Compliant_EURT_cW: 'CWEURT', Compliant_GBPC_cW: 'CWGBPC', Compliant_GBPT_cW: 'CWGBPT', Compliant_AUDC_cW: 'CWAUDC', Compliant_JPYC_cW: 'CWJPYC', Compliant_CHFC_cW: 'CWCHFC', Compliant_CADC_cW: 'CWCADC', Compliant_XAUC_cW: 'CWXAUC', Compliant_XAUT_cW: 'CWXAUT' };
|
||||
const chainToId = { MAINNET: 1, CRONOS: 25, BSC: 56, POLYGON: 137, GNOSIS: 100, AVALANCHE: 43114, BASE: 8453, ARBITRUM: 42161, OPTIMISM: 10, CELO: 42220, WEMIX: 1111, ALL: 651940 };
|
||||
const keyToEnv = { Compliant_USDT_cW: 'CWUSDT', Compliant_USDC_cW: 'CWUSDC', Compliant_AUSDT_cW: 'CWAUSDT', Compliant_EURC_cW: 'CWEURC', Compliant_EURT_cW: 'CWEURT', Compliant_GBPC_cW: 'CWGBPC', Compliant_GBPT_cW: 'CWGBPT', Compliant_AUDC_cW: 'CWAUDC', Compliant_JPYC_cW: 'CWJPYC', Compliant_CHFC_cW: 'CWCHFC', Compliant_CADC_cW: 'CWCADC', Compliant_XAUC_cW: 'CWXAUC', Compliant_XAUT_cW: 'CWXAUT' };
|
||||
function getEnvAddress(prefix, chainName, chainId) {
|
||||
return env[prefix + '_' + chainName] || env[prefix + '_ADDRESS_' + chainId] || '';
|
||||
}
|
||||
const j = JSON.parse(fs.readFileSync('$config_file', 'utf8'));
|
||||
let updated = 0;
|
||||
for (const [name, chainId] of Object.entries(chainToId)) {
|
||||
@@ -89,7 +102,7 @@ for (const [name, chainId] of Object.entries(chainToId)) {
|
||||
for (const t of pair.tokens) {
|
||||
const envKey = keyToEnv[t.key];
|
||||
if (!envKey) continue;
|
||||
const addr = env[envKey + '_' + name];
|
||||
const addr = getEnvAddress(envKey, name, chainId);
|
||||
if (addr && t.addressTo !== addr) { t.addressTo = addr; updated++; }
|
||||
}
|
||||
}
|
||||
@@ -111,15 +124,33 @@ if $DO_VERIFY; then
|
||||
echo "=== Verify MINTER/BURNER roles on cW* for each chain ==="
|
||||
MINTER_ROLE=$(cast keccak "MINTER_ROLE" 2>/dev/null || echo "0x")
|
||||
BURNER_ROLE=$(cast keccak "BURNER_ROLE" 2>/dev/null || echo "0x")
|
||||
for name in MAINNET CRONOS BSC POLYGON GNOSIS AVALANCHE BASE ARBITRUM OPTIMISM; do
|
||||
cwusdt_var="CWUSDT_${name}"
|
||||
CW_TOKEN_PREFIXES=(CWUSDT CWUSDC CWAUSDT CWEURC CWEURT CWGBPC CWGBPT CWAUDC CWJPYC CWCHFC CWCADC CWXAUC CWXAUT)
|
||||
resolve_cw_token_value() {
|
||||
local prefix="$1"
|
||||
local name="$2"
|
||||
local chain_id="${CHAIN_NAME_TO_ID[$name]:-}"
|
||||
local chain_var="${prefix}_${name}"
|
||||
local address_var=""
|
||||
if [[ -n "$chain_id" ]]; then
|
||||
address_var="${prefix}_ADDRESS_${chain_id}"
|
||||
fi
|
||||
if [[ -n "${!chain_var:-}" ]]; then
|
||||
printf '%s' "${!chain_var}"
|
||||
return
|
||||
fi
|
||||
if [[ -n "$address_var" && -n "${!address_var:-}" ]]; then
|
||||
printf '%s' "${!address_var}"
|
||||
return
|
||||
fi
|
||||
printf ''
|
||||
}
|
||||
for name in MAINNET CRONOS BSC POLYGON GNOSIS AVALANCHE BASE ARBITRUM OPTIMISM CELO WEMIX; do
|
||||
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:-}}";;
|
||||
@@ -131,12 +162,25 @@ if $DO_VERIFY; then
|
||||
BASE) rpc="${BASE_MAINNET_RPC:-}";;
|
||||
ARBITRUM) rpc="${ARBITRUM_MAINNET_RPC:-}";;
|
||||
OPTIMISM) rpc="${OPTIMISM_MAINNET_RPC:-}";;
|
||||
CELO) rpc="${CELO_RPC:-${CELO_MAINNET_RPC:-}}";;
|
||||
WEMIX) rpc="${WEMIX_RPC:-${WEMIX_MAINNET_RPC:-}}";;
|
||||
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)"
|
||||
found_any=false
|
||||
for prefix in "${CW_TOKEN_PREFIXES[@]}"; do
|
||||
token="$(resolve_cw_token_value "$prefix" "$name")"
|
||||
if [[ -z "$token" ]]; then
|
||||
continue
|
||||
fi
|
||||
found_any=true
|
||||
m=$(cast call "$token" "hasRole(bytes32,address)(bool)" "$MINTER_ROLE" "$bridge" --rpc-url "$rpc" 2>/dev/null || echo "false")
|
||||
b=$(cast call "$token" "hasRole(bytes32,address)(bool)" "$BURNER_ROLE" "$bridge" --rpc-url "$rpc" 2>/dev/null || echo "false")
|
||||
echo " $name $prefix: MINTER=$m BURNER=$b (token=$token bridge=$bridge)"
|
||||
done
|
||||
if ! $found_any; then
|
||||
echo " Skip $name: no cW* token addresses configured"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
153
scripts/deployment/run-gas-l1-destination-wiring.sh
Normal file
153
scripts/deployment/run-gas-l1-destination-wiring.sh
Normal file
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env bash
|
||||
# Guarded operator runner for Chain 138 gas-lane destination wiring.
|
||||
# Default mode is echo-only. Set EXECUTE_GAS_L1_DESTINATIONS=1 to broadcast.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/deployment/run-gas-l1-destination-wiring.sh
|
||||
# EXECUTE_GAS_L1_DESTINATIONS=1 bash scripts/deployment/run-gas-l1-destination-wiring.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
export PROJECT_ROOT
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" >/dev/null 2>&1 || true
|
||||
|
||||
command -v bash >/dev/null 2>&1 || { echo "[FAIL] bash is required" >&2; exit 1; }
|
||||
command -v cast >/dev/null 2>&1 || { echo "[FAIL] cast is required" >&2; exit 1; }
|
||||
command -v jq >/dev/null 2>&1 || { echo "[FAIL] jq is required" >&2; exit 1; }
|
||||
|
||||
EXECUTE="${EXECUTE_GAS_L1_DESTINATIONS:-0}"
|
||||
GAS_LIMIT="${GAS_LIMIT:-250000}"
|
||||
GAS_PRICE="${GAS_PRICE:-}"
|
||||
CONTINUE_ON_ERROR="${CONTINUE_ON_ERROR:-0}"
|
||||
|
||||
log() { printf '%s\n' "$*"; }
|
||||
ok() { printf '[OK] %s\n' "$*"; }
|
||||
warn() { printf '[WARN] %s\n' "$*"; }
|
||||
fail() { printf '[FAIL] %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
RUNNER_ADDR="$(cast wallet address --private-key "${PRIVATE_KEY:?PRIVATE_KEY is required}")"
|
||||
CHAIN138_L1_BRIDGE="${CHAIN138_L1_BRIDGE:-${CW_L1_BRIDGE_CHAIN138:-${CW_L1_BRIDGE:-}}}"
|
||||
RPC_URL_138="${RPC_URL_138:?RPC_URL_138 is required}"
|
||||
|
||||
[[ -n "$CHAIN138_L1_BRIDGE" ]] || fail "CHAIN138_L1_BRIDGE or CW_L1_BRIDGE_CHAIN138 is required"
|
||||
|
||||
BRIDGE_ADMIN="$(cast call "$CHAIN138_L1_BRIDGE" 'admin()(address)' --rpc-url "$RPC_URL_138")"
|
||||
if [[ "${RUNNER_ADDR,,}" != "${BRIDGE_ADMIN,,}" ]]; then
|
||||
fail "Loaded PRIVATE_KEY resolves to $RUNNER_ADDR, but bridge admin is $BRIDGE_ADMIN"
|
||||
fi
|
||||
|
||||
tmp_json="$(mktemp)"
|
||||
trap 'rm -f "$tmp_json"' EXIT
|
||||
|
||||
bash "$PROJECT_ROOT/scripts/deployment/print-gas-l1-destination-wiring-commands.sh" --json >"$tmp_json"
|
||||
|
||||
pending_count="$(jq -r '.summary.pairsNeedingL1DestinationWiring // 0' "$tmp_json")"
|
||||
|
||||
log "=== Gas L1 Destination Wiring Runner ==="
|
||||
log "Mode: $([[ "$EXECUTE" == "1" ]] && echo execute || echo dry-run)"
|
||||
log "Bridge: $CHAIN138_L1_BRIDGE"
|
||||
log "RPC: $RPC_URL_138"
|
||||
log "Runner: $RUNNER_ADDR"
|
||||
log "Pending lanes: $pending_count"
|
||||
|
||||
if [[ "$pending_count" == "0" ]]; then
|
||||
ok "All active gas lanes are already wired on Chain 138."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
successes=0
|
||||
failures=0
|
||||
|
||||
while IFS= read -r row_b64; do
|
||||
row_json="$(printf '%s' "$row_b64" | base64 -d)"
|
||||
key="$(jq -r '.key' <<<"$row_json")"
|
||||
chain_id="$(jq -r '.chainId' <<<"$row_json")"
|
||||
chain_name="$(jq -r '.chainName' <<<"$row_json")"
|
||||
family_key="$(jq -r '.familyKey' <<<"$row_json")"
|
||||
canonical_symbol="$(jq -r '.canonicalSymbol' <<<"$row_json")"
|
||||
mirrored_symbol="$(jq -r '.mirroredSymbol' <<<"$row_json")"
|
||||
canonical_address="$(jq -r '.canonicalAddress' <<<"$row_json")"
|
||||
selector="$(jq -r '.destinationChainSelector' <<<"$row_json")"
|
||||
l2_bridge="$(jq -r '.l2BridgeAddress' <<<"$row_json")"
|
||||
|
||||
log ""
|
||||
log "--- $key | $chain_id $chain_name | $family_key | $canonical_symbol->$mirrored_symbol"
|
||||
|
||||
if [[ "$EXECUTE" != "1" ]]; then
|
||||
printf 'cast send %q %q %q %q %q %q --rpc-url %q --private-key %q --legacy --gas-limit %q' \
|
||||
"$CHAIN138_L1_BRIDGE" \
|
||||
"configureDestination(address,uint64,address,bool)" \
|
||||
"$canonical_address" \
|
||||
"$selector" \
|
||||
"$l2_bridge" \
|
||||
"true" \
|
||||
"$RPC_URL_138" \
|
||||
'$PRIVATE_KEY' \
|
||||
"$GAS_LIMIT"
|
||||
if [[ -n "$GAS_PRICE" ]]; then
|
||||
printf ' --gas-price %q' "$GAS_PRICE"
|
||||
fi
|
||||
printf '\n'
|
||||
continue
|
||||
fi
|
||||
|
||||
cmd=(
|
||||
cast send "$CHAIN138_L1_BRIDGE"
|
||||
"configureDestination(address,uint64,address,bool)"
|
||||
"$canonical_address"
|
||||
"$selector"
|
||||
"$l2_bridge"
|
||||
true
|
||||
--rpc-url "$RPC_URL_138"
|
||||
--private-key "$PRIVATE_KEY"
|
||||
--legacy
|
||||
--gas-limit "$GAS_LIMIT"
|
||||
)
|
||||
if [[ -n "$GAS_PRICE" ]]; then
|
||||
cmd+=(--gas-price "$GAS_PRICE")
|
||||
fi
|
||||
|
||||
if ! output="$("${cmd[@]}" 2>&1)"; then
|
||||
printf '%s\n' "$output"
|
||||
warn "Broadcast failed for $key"
|
||||
failures=$((failures + 1))
|
||||
if [[ "$CONTINUE_ON_ERROR" != "1" ]]; then
|
||||
fail "Stopping after first failure; set CONTINUE_ON_ERROR=1 to continue."
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
printf '%s\n' "$output"
|
||||
|
||||
verify="$(cast call "$CHAIN138_L1_BRIDGE" 'destinations(address,uint64)((address,bool))' "$canonical_address" "$selector" --rpc-url "$RPC_URL_138")"
|
||||
verify_normalized="$(printf '%s' "$verify" | tr '[:upper:]' '[:lower:]')"
|
||||
if [[ "$verify_normalized" == *"${l2_bridge,,}"* && "$verify_normalized" == *"true"* ]]; then
|
||||
ok "Verified $key wired on L1"
|
||||
successes=$((successes + 1))
|
||||
else
|
||||
warn "Verification did not match expected receiver for $key: $verify"
|
||||
failures=$((failures + 1))
|
||||
if [[ "$CONTINUE_ON_ERROR" != "1" ]]; then
|
||||
fail "Stopping after verification mismatch; set CONTINUE_ON_ERROR=1 to continue."
|
||||
fi
|
||||
fi
|
||||
done < <(jq -r '.rows[] | @base64' "$tmp_json")
|
||||
|
||||
log ""
|
||||
log "=== Summary ==="
|
||||
log "Successful lanes: $successes"
|
||||
log "Failed lanes: $failures"
|
||||
|
||||
if [[ "$EXECUTE" != "1" ]]; then
|
||||
warn "Dry-run only. Re-run with EXECUTE_GAS_L1_DESTINATIONS=1 to broadcast."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$failures" -gt 0 ]]; then
|
||||
fail "One or more gas-lane destination writes failed."
|
||||
fi
|
||||
|
||||
ok "All pending gas-lane destination writes completed."
|
||||
122
scripts/deployment/run-gru-v2-wave1-chain138.sh
Normal file
122
scripts/deployment/run-gru-v2-wave1-chain138.sh
Normal file
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env bash
|
||||
# Execute or dry-run the GRU V2 Wave 1 Chain 138 deployment plan.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/deployment/run-gru-v2-wave1-chain138.sh
|
||||
# bash scripts/deployment/run-gru-v2-wave1-chain138.sh --code=EUR
|
||||
# bash scripts/deployment/run-gru-v2-wave1-chain138.sh --apply
|
||||
#
|
||||
# Default mode is dry-run. Use --apply to broadcast.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
PLAN_JSON="${PROJECT_ROOT}/config/gru-v2-chain138-wave1-v2-plan.json"
|
||||
DEPLOY_SCRIPT="${PROJECT_ROOT}/scripts/deployment/deploy-gru-v2-generic-chain138.sh"
|
||||
METADATA_MANIFEST="${PROJECT_ROOT}/config/gru-v2-metadata/chain138/ipfs-manifest.latest.json"
|
||||
|
||||
FILTER_CODE=""
|
||||
APPLY=0
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--code=*) FILTER_CODE="${arg#--code=}" ;;
|
||||
--apply) APPLY=1 ;;
|
||||
*)
|
||||
echo "Unknown argument: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "jq is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$PLAN_JSON" ]]; then
|
||||
echo "Missing plan JSON: $PLAN_JSON" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RUN_TS="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
LOG_DIR="${PROJECT_ROOT}/.codex-artifacts/gru-v2-wave1"
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
echo "=== GRU V2 Wave 1 Chain 138 runner ==="
|
||||
echo "Plan: $PLAN_JSON"
|
||||
if [[ -n "$FILTER_CODE" ]]; then
|
||||
echo "Filter: $FILTER_CODE"
|
||||
fi
|
||||
if [[ "$APPLY" == "1" ]]; then
|
||||
echo "Mode: APPLY"
|
||||
: "${PRIVATE_KEY:?PRIVATE_KEY is required for --apply}"
|
||||
echo "Logs: $LOG_DIR/$RUN_TS-*.log"
|
||||
else
|
||||
echo "Mode: DRY_RUN"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
jq -c --arg filter "$FILTER_CODE" '
|
||||
.assets[]
|
||||
| select(($filter == "") or (.code == $filter))
|
||||
| . as $asset
|
||||
| .deployments[]
|
||||
| {
|
||||
code: $asset.code,
|
||||
tokenName,
|
||||
tokenSymbol,
|
||||
currencyCode,
|
||||
metadataKey,
|
||||
registryName,
|
||||
registrySymbol
|
||||
}
|
||||
' "$PLAN_JSON" | while IFS= read -r row; do
|
||||
code="$(jq -r '.code' <<<"$row")"
|
||||
token_name="$(jq -r '.tokenName' <<<"$row")"
|
||||
token_symbol="$(jq -r '.tokenSymbol' <<<"$row")"
|
||||
currency_code="$(jq -r '.currencyCode' <<<"$row")"
|
||||
metadata_key="$(jq -r '.metadataKey' <<<"$row")"
|
||||
registry_name="$(jq -r '.registryName' <<<"$row")"
|
||||
registry_symbol="$(jq -r '.registrySymbol' <<<"$row")"
|
||||
token_uri=""
|
||||
disclosure_uri=""
|
||||
reporting_uri=""
|
||||
|
||||
if [[ -f "$METADATA_MANIFEST" ]]; then
|
||||
token_uri="$(jq -r --arg symbol "$token_symbol" '.assets[$symbol].tokenURI // empty' "$METADATA_MANIFEST")"
|
||||
disclosure_uri="$(jq -r --arg symbol "$token_symbol" '.assets[$symbol].regulatoryDisclosureURI // empty' "$METADATA_MANIFEST")"
|
||||
reporting_uri="$(jq -r --arg symbol "$token_symbol" '.assets[$symbol].reportingURI // empty' "$METADATA_MANIFEST")"
|
||||
fi
|
||||
|
||||
echo "--- ${code} :: ${token_symbol} ---"
|
||||
if [[ -n "$token_uri" ]]; then
|
||||
echo "metadata: ${metadata_key} -> ${token_uri}"
|
||||
else
|
||||
echo "metadata: ${metadata_key} -> no IPFS manifest entry found; deploying without token/disclosure/reporting URIs"
|
||||
fi
|
||||
if [[ "$APPLY" == "1" ]]; then
|
||||
TOKEN_NAME="$token_name" \
|
||||
TOKEN_SYMBOL="$token_symbol" \
|
||||
CURRENCY_CODE="$currency_code" \
|
||||
REGISTRY_NAME="$registry_name" \
|
||||
REGISTRY_SYMBOL="$registry_symbol" \
|
||||
TOKEN_URI="$token_uri" \
|
||||
REGULATORY_DISCLOSURE_URI="$disclosure_uri" \
|
||||
REPORTING_URI="$reporting_uri" \
|
||||
bash "$DEPLOY_SCRIPT" | tee "$LOG_DIR/${RUN_TS}-${token_symbol}.log"
|
||||
else
|
||||
TOKEN_NAME="$token_name" \
|
||||
TOKEN_SYMBOL="$token_symbol" \
|
||||
CURRENCY_CODE="$currency_code" \
|
||||
REGISTRY_NAME="$registry_name" \
|
||||
REGISTRY_SYMBOL="$registry_symbol" \
|
||||
TOKEN_URI="$token_uri" \
|
||||
REGULATORY_DISCLOSURE_URI="$disclosure_uri" \
|
||||
REPORTING_URI="$reporting_uri" \
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-0x1111111111111111111111111111111111111111111111111111111111111111}" \
|
||||
bash "$DEPLOY_SCRIPT" --dry-run
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
38
scripts/deployment/run-mainnet-aave-cwusdc-quote-push-loop.sh
Executable file
38
scripts/deployment/run-mainnet-aave-cwusdc-quote-push-loop.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Repeat run-mainnet-aave-cwusdc-quote-push-once.sh (same FLASH_QUOTE_AMOUNT_RAW) N times.
|
||||
# Reserves move each round; the forge script recomputes MIN_OUT_PMM when MIN_OUT_PMM is unset.
|
||||
#
|
||||
# Env:
|
||||
# FLASH_LOOP_COUNT default 3
|
||||
# FLASH_LOOP_SLEEP_SECS default 15 (pause between broadcasts for inclusion / indexing)
|
||||
# Same env as run-mainnet-aave-cwusdc-quote-push-once.sh
|
||||
#
|
||||
# Usage:
|
||||
# FLASH_LOOP_COUNT=5 bash scripts/deployment/run-mainnet-aave-cwusdc-quote-push-loop.sh --dry-run
|
||||
# FLASH_LOOP_COUNT=5 bash scripts/deployment/run-mainnet-aave-cwusdc-quote-push-loop.sh --apply
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ONCE="${SCRIPT_DIR}/run-mainnet-aave-cwusdc-quote-push-once.sh"
|
||||
COUNT="${FLASH_LOOP_COUNT:-3}"
|
||||
SLEEP_SECS="${FLASH_LOOP_SLEEP_SECS:-15}"
|
||||
|
||||
if [[ ! -f "$ONCE" ]]; then
|
||||
echo "[fail] missing $ONCE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for ((i = 1; i <= COUNT; i++)); do
|
||||
echo "=== quote-push loop $i / $COUNT ==="
|
||||
bash "$ONCE" "$@"
|
||||
if [[ "$*" != *--apply* ]]; then
|
||||
continue
|
||||
fi
|
||||
if ((i < COUNT)); then
|
||||
echo "sleep ${SLEEP_SECS}s before next round..."
|
||||
sleep "$SLEEP_SECS"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Loop finished ($COUNT rounds)."
|
||||
287
scripts/deployment/run-mainnet-aave-cwusdc-quote-push-once.sh
Executable file
287
scripts/deployment/run-mainnet-aave-cwusdc-quote-push-once.sh
Executable file
@@ -0,0 +1,287 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Run one Mainnet Aave flash quote-push via forge (see RunMainnetAaveCwusdcUsdcQuotePushOnce.s.sol).
|
||||
# Default: simulation only. Use --apply to broadcast.
|
||||
#
|
||||
# Required env:
|
||||
# PRIVATE_KEY, ETHEREUM_MAINNET_RPC
|
||||
# DODO_PMM_INTEGRATION_MAINNET
|
||||
# AAVE_QUOTE_PUSH_RECEIVER_MAINNET optional; defaults to canonical receiver or latest broadcast
|
||||
# QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET optional; can be auto-picked from latest broadcast when QUOTE_PUSH_UNWINDER_TYPE is set
|
||||
# FLASH_QUOTE_AMOUNT_RAW optional; defaults to 200000
|
||||
# UNWIND_MODE: 0 = V3 single-hop fee; 1 = DODO pool; 2 = V3 exactInput path (UNWIND_V3_PATH_HEX);
|
||||
# 4 = TwoHopDodoIntegrationUnwinder (UNWIND_TWO_HOP_*)
|
||||
# 5 = DODOToUniswapV3MultiHopExternalUnwinder (UNWIND_DODO_POOL + UNWIND_INTERMEDIATE_TOKEN + UNWIND_V3_PATH_HEX)
|
||||
#
|
||||
# Usage:
|
||||
# source scripts/lib/load-project-env.sh
|
||||
# bash scripts/deployment/run-mainnet-aave-cwusdc-quote-push-once.sh --dry-run
|
||||
# bash scripts/deployment/run-mainnet-aave-cwusdc-quote-push-once.sh --apply
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROXMOX_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
SMOM="${PROXMOX_ROOT}/smom-dbis-138"
|
||||
DEFAULT_AAVE_QUOTE_PUSH_RECEIVER_MAINNET="0x241cb416aaFC2654078b7E2376adED2bDeFbCBa2"
|
||||
DEFAULT_POOL_CWUSDC_USDC_MAINNET="0x69776fc607e9edA8042e320e7e43f54d06c68f0E"
|
||||
|
||||
# Preserve explicit caller overrides across sourced repo env files.
|
||||
_qp_private_key="${PRIVATE_KEY-}"
|
||||
_qp_rpc="${ETHEREUM_MAINNET_RPC-}"
|
||||
_qp_receiver="${AAVE_QUOTE_PUSH_RECEIVER_MAINNET-}"
|
||||
_qp_unwinder="${QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET-}"
|
||||
_qp_amount="${FLASH_QUOTE_AMOUNT_RAW-}"
|
||||
_qp_unwind_type="${QUOTE_PUSH_UNWINDER_TYPE-}"
|
||||
_qp_unwind_mode="${UNWIND_MODE-}"
|
||||
_qp_pool="${POOL_CWUSDC_USDC_MAINNET-}"
|
||||
_qp_integration="${DODO_PMM_INTEGRATION_MAINNET-}"
|
||||
_qp_pool_a="${UNWIND_TWO_HOP_POOL_A-}"
|
||||
_qp_pool_b="${UNWIND_TWO_HOP_POOL_B-}"
|
||||
_qp_mid_token="${UNWIND_TWO_HOP_MID_TOKEN-}"
|
||||
_qp_min_mid_out="${UNWIND_MIN_MID_OUT_RAW-}"
|
||||
_qp_min_out_pmm="${MIN_OUT_PMM-}"
|
||||
_qp_min_out_unwind="${MIN_OUT_UNWIND-}"
|
||||
_qp_fee_u24="${UNWIND_V3_FEE_U24-}"
|
||||
_qp_dodo_pool="${UNWIND_DODO_POOL-}"
|
||||
_qp_v3_path="${UNWIND_V3_PATH_HEX-}"
|
||||
_qp_intermediate_token="${UNWIND_INTERMEDIATE_TOKEN-}"
|
||||
_qp_min_intermediate_out="${UNWIND_MIN_INTERMEDIATE_OUT_RAW-}"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROXMOX_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
# shellcheck disable=SC1091
|
||||
source "${SMOM}/scripts/load-env.sh" >/dev/null 2>&1 || true
|
||||
|
||||
[[ -n "$_qp_private_key" ]] && export PRIVATE_KEY="$_qp_private_key"
|
||||
[[ -n "$_qp_rpc" ]] && export ETHEREUM_MAINNET_RPC="$_qp_rpc"
|
||||
[[ -n "$_qp_receiver" ]] && export AAVE_QUOTE_PUSH_RECEIVER_MAINNET="$_qp_receiver"
|
||||
[[ -n "$_qp_unwinder" ]] && export QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET="$_qp_unwinder"
|
||||
[[ -n "$_qp_amount" ]] && export FLASH_QUOTE_AMOUNT_RAW="$_qp_amount"
|
||||
[[ -n "$_qp_unwind_type" ]] && export QUOTE_PUSH_UNWINDER_TYPE="$_qp_unwind_type"
|
||||
[[ -n "$_qp_unwind_mode" ]] && export UNWIND_MODE="$_qp_unwind_mode"
|
||||
[[ -n "$_qp_pool" ]] && export POOL_CWUSDC_USDC_MAINNET="$_qp_pool"
|
||||
[[ -n "$_qp_integration" ]] && export DODO_PMM_INTEGRATION_MAINNET="$_qp_integration"
|
||||
[[ -n "$_qp_pool_a" ]] && export UNWIND_TWO_HOP_POOL_A="$_qp_pool_a"
|
||||
[[ -n "$_qp_pool_b" ]] && export UNWIND_TWO_HOP_POOL_B="$_qp_pool_b"
|
||||
[[ -n "$_qp_mid_token" ]] && export UNWIND_TWO_HOP_MID_TOKEN="$_qp_mid_token"
|
||||
[[ -n "$_qp_min_mid_out" ]] && export UNWIND_MIN_MID_OUT_RAW="$_qp_min_mid_out"
|
||||
[[ -n "$_qp_min_out_pmm" ]] && export MIN_OUT_PMM="$_qp_min_out_pmm"
|
||||
[[ -n "$_qp_min_out_unwind" ]] && export MIN_OUT_UNWIND="$_qp_min_out_unwind"
|
||||
[[ -n "$_qp_fee_u24" ]] && export UNWIND_V3_FEE_U24="$_qp_fee_u24"
|
||||
[[ -n "$_qp_dodo_pool" ]] && export UNWIND_DODO_POOL="$_qp_dodo_pool"
|
||||
[[ -n "$_qp_v3_path" ]] && export UNWIND_V3_PATH_HEX="$_qp_v3_path"
|
||||
[[ -n "$_qp_intermediate_token" ]] && export UNWIND_INTERMEDIATE_TOKEN="$_qp_intermediate_token"
|
||||
[[ -n "$_qp_min_intermediate_out" ]] && export UNWIND_MIN_INTERMEDIATE_OUT_RAW="$_qp_min_intermediate_out"
|
||||
|
||||
unset _qp_private_key _qp_rpc _qp_receiver _qp_unwinder _qp_amount _qp_unwind_type _qp_unwind_mode
|
||||
unset _qp_pool _qp_integration _qp_pool_a _qp_pool_b _qp_mid_token _qp_min_mid_out _qp_min_out_pmm
|
||||
unset _qp_min_out_unwind _qp_fee_u24 _qp_dodo_pool _qp_v3_path _qp_intermediate_token _qp_min_intermediate_out
|
||||
|
||||
BROADCAST=()
|
||||
if (($# == 0)); then
|
||||
:
|
||||
else
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
--apply) BROADCAST=(--broadcast) ;;
|
||||
--dry-run) BROADCAST=() ;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $a" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
require() {
|
||||
local n="$1"
|
||||
if [[ -z "${!n:-}" ]]; then
|
||||
if [[ "$n" == "QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET" ]]; then
|
||||
cat >&2 <<EOF
|
||||
[fail] missing required env: QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET
|
||||
[hint] No real broadcasted unwinder was found for auto-pick.
|
||||
[hint] Next step:
|
||||
QUOTE_PUSH_UNWINDER_TYPE=two_hop_dodo bash scripts/deployment/deploy-mainnet-aave-quote-push-stack.sh --apply
|
||||
[hint] Then export:
|
||||
AAVE_QUOTE_PUSH_RECEIVER_MAINNET=${AAVE_QUOTE_PUSH_RECEIVER_MAINNET:-$DEFAULT_AAVE_QUOTE_PUSH_RECEIVER_MAINNET}
|
||||
QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET=<real_apply_deployed_unwinder_address>
|
||||
FLASH_QUOTE_AMOUNT_RAW=${FLASH_QUOTE_AMOUNT_RAW:-200000}
|
||||
UNWIND_MODE=${UNWIND_MODE:-4}
|
||||
UNWIND_TWO_HOP_POOL_A=${UNWIND_TWO_HOP_POOL_A:-0xe944b7Cb012A0820c07f54D51e92f0e1C74168DB}
|
||||
UNWIND_TWO_HOP_POOL_B=${UNWIND_TWO_HOP_POOL_B:-0x27f3aE7EE71Be3d77bAf17d4435cF8B895DD25D2}
|
||||
UNWIND_TWO_HOP_MID_TOKEN=${UNWIND_TWO_HOP_MID_TOKEN:-0xaF5017d0163ecb99d9B5D94e3b4D7b09Af44D8AE}
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
echo "[fail] missing required env: $n" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
pick_latest_create_address() {
|
||||
local script_name="$1"
|
||||
local contract_name="$2"
|
||||
local latest_json="${SMOM}/broadcast/${script_name}/1/run-latest.json"
|
||||
if [[ ! -f "$latest_json" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
jq -r --arg contract "$contract_name" \
|
||||
'.transactions[]? | select(.transactionType == "CREATE" and .contractName == $contract) | .contractAddress' \
|
||||
"$latest_json" | tail -n1
|
||||
}
|
||||
|
||||
pick_default_unwinder() {
|
||||
local addr=""
|
||||
PICK_DEFAULT_UNWINDER_ADDR=""
|
||||
PICK_DEFAULT_UNWINDER_MODE=""
|
||||
|
||||
addr="$(pick_latest_create_address "DeployTwoHopDodoIntegrationUnwinder.s.sol" "TwoHopDodoIntegrationUnwinder" || true)"
|
||||
if [[ -n "$addr" && "$addr" != "null" ]]; then
|
||||
PICK_DEFAULT_UNWINDER_ADDR="$addr"
|
||||
PICK_DEFAULT_UNWINDER_MODE="4"
|
||||
return 0
|
||||
fi
|
||||
|
||||
addr="$(pick_latest_create_address "DeployDODOIntegrationExternalUnwinder.s.sol" "DODOIntegrationExternalUnwinder" || true)"
|
||||
if [[ -n "$addr" && "$addr" != "null" ]]; then
|
||||
PICK_DEFAULT_UNWINDER_ADDR="$addr"
|
||||
PICK_DEFAULT_UNWINDER_MODE="1"
|
||||
return 0
|
||||
fi
|
||||
|
||||
addr="$(pick_latest_create_address "DeployUniswapV3ExternalUnwinder.s.sol" "UniswapV3ExternalUnwinder" || true)"
|
||||
if [[ -n "$addr" && "$addr" != "null" ]]; then
|
||||
PICK_DEFAULT_UNWINDER_ADDR="$addr"
|
||||
PICK_DEFAULT_UNWINDER_MODE="0"
|
||||
return 0
|
||||
fi
|
||||
|
||||
addr="$(pick_latest_create_address "DeployDODOToUniswapV3MultiHopExternalUnwinder.s.sol" "DODOToUniswapV3MultiHopExternalUnwinder" || true)"
|
||||
if [[ -n "$addr" && "$addr" != "null" ]]; then
|
||||
PICK_DEFAULT_UNWINDER_ADDR="$addr"
|
||||
PICK_DEFAULT_UNWINDER_MODE="5"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
UNW="${QUOTE_PUSH_UNWINDER_TYPE:-}"
|
||||
if [[ -z "${AAVE_QUOTE_PUSH_RECEIVER_MAINNET:-}" ]]; then
|
||||
inferred_receiver="$(pick_latest_create_address "DeployAaveQuotePushFlashReceiver.s.sol" "AaveQuotePushFlashReceiver" || true)"
|
||||
export AAVE_QUOTE_PUSH_RECEIVER_MAINNET="${inferred_receiver:-$DEFAULT_AAVE_QUOTE_PUSH_RECEIVER_MAINNET}"
|
||||
fi
|
||||
|
||||
if [[ -z "${POOL_CWUSDC_USDC_MAINNET:-}" || "${_qp_pool:-}" == "" ]]; then
|
||||
export POOL_CWUSDC_USDC_MAINNET="$DEFAULT_POOL_CWUSDC_USDC_MAINNET"
|
||||
fi
|
||||
|
||||
if [[ -z "${FLASH_QUOTE_AMOUNT_RAW:-}" ]]; then
|
||||
export FLASH_QUOTE_AMOUNT_RAW=200000
|
||||
fi
|
||||
|
||||
if [[ -z "${MIN_OUT_PMM:-}" ]]; then
|
||||
export MIN_OUT_PMM=1
|
||||
fi
|
||||
|
||||
if [[ -z "${QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET:-}" && -n "$UNW" ]]; then
|
||||
unwind_script=""
|
||||
unwind_contract=""
|
||||
case "$UNW" in
|
||||
univ3)
|
||||
unwind_script="DeployUniswapV3ExternalUnwinder.s.sol"
|
||||
unwind_contract="UniswapV3ExternalUnwinder"
|
||||
export UNWIND_MODE="${UNWIND_MODE:-0}"
|
||||
;;
|
||||
dodo)
|
||||
unwind_script="DeployDODOIntegrationExternalUnwinder.s.sol"
|
||||
unwind_contract="DODOIntegrationExternalUnwinder"
|
||||
export UNWIND_MODE="${UNWIND_MODE:-1}"
|
||||
;;
|
||||
two_hop_dodo)
|
||||
unwind_script="DeployTwoHopDodoIntegrationUnwinder.s.sol"
|
||||
unwind_contract="TwoHopDodoIntegrationUnwinder"
|
||||
export UNWIND_MODE="${UNWIND_MODE:-4}"
|
||||
;;
|
||||
dodo_univ3)
|
||||
unwind_script="DeployDODOToUniswapV3MultiHopExternalUnwinder.s.sol"
|
||||
unwind_contract="DODOToUniswapV3MultiHopExternalUnwinder"
|
||||
export UNWIND_MODE="${UNWIND_MODE:-5}"
|
||||
;;
|
||||
*)
|
||||
echo "[fail] QUOTE_PUSH_UNWINDER_TYPE must be univ3, dodo, two_hop_dodo, or dodo_univ3 when set" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
inferred_unwinder="$(pick_latest_create_address "$unwind_script" "$unwind_contract" || true)"
|
||||
if [[ -n "$inferred_unwinder" && "$inferred_unwinder" != "null" ]]; then
|
||||
export QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET="$inferred_unwinder"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "${QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET:-}" ]]; then
|
||||
if pick_default_unwinder; then
|
||||
if [[ -n "$PICK_DEFAULT_UNWINDER_ADDR" && "$PICK_DEFAULT_UNWINDER_ADDR" != "null" ]]; then
|
||||
export QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET="$PICK_DEFAULT_UNWINDER_ADDR"
|
||||
export UNWIND_MODE="$PICK_DEFAULT_UNWINDER_MODE"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${UNWIND_MODE:-}" == "4" ]]; then
|
||||
export UNWIND_TWO_HOP_POOL_A="${UNWIND_TWO_HOP_POOL_A:-0xe944b7Cb012A0820c07f54D51e92f0e1C74168DB}"
|
||||
export UNWIND_TWO_HOP_POOL_B="${UNWIND_TWO_HOP_POOL_B:-0x27f3aE7EE71Be3d77bAf17d4435cF8B895DD25D2}"
|
||||
export UNWIND_TWO_HOP_MID_TOKEN="${UNWIND_TWO_HOP_MID_TOKEN:-0xaF5017d0163ecb99d9B5D94e3b4D7b09Af44D8AE}"
|
||||
export UNWIND_MIN_MID_OUT_RAW="${UNWIND_MIN_MID_OUT_RAW:-1}"
|
||||
elif [[ "${UNWIND_MODE:-}" == "5" ]]; then
|
||||
export UNWIND_DODO_POOL="${UNWIND_DODO_POOL:-0xCC0fd27A40775c9AfcD2BBd3f7c902b0192c247A}"
|
||||
export UNWIND_INTERMEDIATE_TOKEN="${UNWIND_INTERMEDIATE_TOKEN:-0xdAC17F958D2ee523a2206206994597C13D831ec7}"
|
||||
export UNWIND_MIN_INTERMEDIATE_OUT_RAW="${UNWIND_MIN_INTERMEDIATE_OUT_RAW:-1}"
|
||||
fi
|
||||
|
||||
require ETHEREUM_MAINNET_RPC
|
||||
require PRIVATE_KEY
|
||||
require AAVE_QUOTE_PUSH_RECEIVER_MAINNET
|
||||
require QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET
|
||||
require DODO_PMM_INTEGRATION_MAINNET
|
||||
require FLASH_QUOTE_AMOUNT_RAW
|
||||
|
||||
UM="${UNWIND_MODE:-0}"
|
||||
if [[ "$UM" == "0" ]]; then
|
||||
require UNWIND_V3_FEE_U24
|
||||
elif [[ "$UM" == "1" ]]; then
|
||||
require UNWIND_DODO_POOL
|
||||
elif [[ "$UM" == "2" ]]; then
|
||||
require UNWIND_V3_PATH_HEX
|
||||
elif [[ "$UM" == "4" ]]; then
|
||||
require UNWIND_TWO_HOP_POOL_A
|
||||
require UNWIND_TWO_HOP_POOL_B
|
||||
require UNWIND_TWO_HOP_MID_TOKEN
|
||||
elif [[ "$UM" == "5" ]]; then
|
||||
require UNWIND_DODO_POOL
|
||||
require UNWIND_INTERMEDIATE_TOKEN
|
||||
require UNWIND_V3_PATH_HEX
|
||||
else
|
||||
echo "[fail] UNWIND_MODE must be 0, 1, 2, 4, or 5" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "receiver=$AAVE_QUOTE_PUSH_RECEIVER_MAINNET"
|
||||
echo "unwinder=$QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET"
|
||||
echo "flash_quote_amount_raw=$FLASH_QUOTE_AMOUNT_RAW unwind_mode=$UM"
|
||||
|
||||
(
|
||||
cd "$SMOM"
|
||||
forge script script/flash/RunMainnetAaveCwusdcUsdcQuotePushOnce.s.sol:RunMainnetAaveCwusdcUsdcQuotePushOnce \
|
||||
--rpc-url "$ETHEREUM_MAINNET_RPC" \
|
||||
"${BROADCAST[@]}" \
|
||||
-vvvv
|
||||
)
|
||||
|
||||
echo "Done. Re-check: bash scripts/verify/check-mainnet-cwusdc-usdc-reserve-peg.sh"
|
||||
94
scripts/deployment/run-mainnet-cwusdc-flash-quote-push-model-sweep.sh
Executable file
94
scripts/deployment/run-mainnet-cwusdc-flash-quote-push-model-sweep.sh
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Read-only: loop **modeling** runs (no chain txs) for flash quote-push against live
|
||||
# Mainnet cWUSDC/USDC reserves. Use when wallet USDC is zero but you want sized tranches.
|
||||
#
|
||||
# Env:
|
||||
# ETHEREUM_MAINNET_RPC (required)
|
||||
# FLASH_MODEL_SCAN_SIZES — comma-separated gross flash quote sizes in raw units (default 5e6,1e7,2e7)
|
||||
# PMM_FLASH_EXIT_PRICE_CMD — passed to node --external-exit-price-cmd (default: printf 1.02)
|
||||
# GAS_GWEI, NATIVE_PRICE — optional overrides for the economics model
|
||||
# FLASH_MODEL_GAS_TX_COUNT, FLASH_MODEL_GAS_PER_TX — gas row (defaults 3 / 250000)
|
||||
# FLASH_MODEL_MAX_POST_TRADE_DEV_BPS — deviation guard (default 500; raise only for stress math, not production)
|
||||
#
|
||||
# Usage:
|
||||
# source scripts/lib/load-project-env.sh
|
||||
# bash scripts/deployment/run-mainnet-cwusdc-flash-quote-push-model-sweep.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROXMOX_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROXMOX_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROXMOX_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1 || true
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd cast
|
||||
require_cmd node
|
||||
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
USDC="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
INTEGRATION="${DODO_PMM_INTEGRATION_MAINNET:-}"
|
||||
POOL="${POOL_CWUSDC_USDC_MAINNET:-0x69776fc607e9edA8042e320e7e43f54d06c68f0E}"
|
||||
SCAN="${FLASH_MODEL_SCAN_SIZES:-5000000,10000000,25000000}"
|
||||
EXIT_CMD="${PMM_FLASH_EXIT_PRICE_CMD:-printf 1.02}"
|
||||
GAS_GWEI="${GAS_GWEI:-40}"
|
||||
NATIVE_PRICE="${NATIVE_PRICE:-3200}"
|
||||
GAS_TXN="${FLASH_MODEL_GAS_TX_COUNT:-3}"
|
||||
GAS_PER="${FLASH_MODEL_GAS_PER_TX:-250000}"
|
||||
DEV_BPS="${FLASH_MODEL_MAX_POST_TRADE_DEV_BPS:-500}"
|
||||
|
||||
if [[ -z "$RPC_URL" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$INTEGRATION" ]]; then
|
||||
mapped_pool="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$CWUSDC" "$USDC" --rpc-url "$RPC_URL" 2>/dev/null | awk '{print $1}' || true)"
|
||||
if [[ -n "$mapped_pool" && "$mapped_pool" != "0x0000000000000000000000000000000000000000" && "${mapped_pool,,}" != "${POOL,,}" ]]; then
|
||||
POOL="$mapped_pool"
|
||||
fi
|
||||
fi
|
||||
|
||||
out="$(cast call "$POOL" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL")"
|
||||
B="$(printf '%s\n' "$out" | sed -n '1p' | awk '{print $1}')"
|
||||
Q="$(printf '%s\n' "$out" | sed -n '2p' | awk '{print $1}')"
|
||||
|
||||
PMM_JS="${PROXMOX_ROOT}/scripts/analytics/pmm-flash-push-break-even.mjs"
|
||||
|
||||
echo "=== Flash quote-push model sweep (dry-run only) ==="
|
||||
echo "pool=$POOL B=$B Q=$Q scan=$SCAN gas_tx=$GAS_TXN gas_per=$GAS_PER max_fee_gwei=$GAS_GWEI dev_bps=$DEV_BPS"
|
||||
echo
|
||||
|
||||
# --full-loop-dry-run requires explicit -x; run one node invocation per scan size.
|
||||
IFS=',' read -r -a sizes <<< "${SCAN// /}"
|
||||
for x in "${sizes[@]}"; do
|
||||
[[ -z "$x" ]] && continue
|
||||
echo "----- model trade_raw=$x -----"
|
||||
node "$PMM_JS" \
|
||||
-B "$B" -Q "$Q" \
|
||||
--full-loop-dry-run \
|
||||
--loop-strategy quote-push \
|
||||
-x "$x" \
|
||||
--base-asset cWUSDC --base-decimals 6 \
|
||||
--quote-asset USDC --quote-decimals 6 \
|
||||
--external-exit-price-cmd "$EXIT_CMD" \
|
||||
--flash-fee-bps 5 --lp-fee-bps 3 \
|
||||
--flash-provider-cap 1000000000000 \
|
||||
--flash-provider-name 'Aave mainnet (cap placeholder)' \
|
||||
--gas-tx-count "$GAS_TXN" --gas-per-tx "$GAS_PER" \
|
||||
--max-fee-gwei "$GAS_GWEI" \
|
||||
--native-token-price "$NATIVE_PRICE" \
|
||||
--max-post-trade-deviation-bps "$DEV_BPS"
|
||||
echo
|
||||
done
|
||||
|
||||
echo "Live execution: deploy-mainnet-aave-quote-push-stack.sh then run-mainnet-aave-cwusdc-quote-push-once.sh (see plan-mainnet-cwusdc-flash-quote-push-rebalance.sh)"
|
||||
110
scripts/deployment/run-mainnet-cwusdt-cwusdc-soak-roundtrips.sh
Executable file
110
scripts/deployment/run-mainnet-cwusdt-cwusdc-soak-roundtrips.sh
Executable file
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Deterministic cWUSDT <-> cWUSDC round-trip exercise on the Mainnet PMM pair (planning / canary).
|
||||
#
|
||||
# - Uses fixed operator-supplied raw amounts only (no RNG). Repeating the same list is for soak
|
||||
# testing, not for fabricating "organic" volume.
|
||||
# - Default is dry-run only. One DODO pool already supports both swap directions; there is no
|
||||
# separate "reverse" pool deployment.
|
||||
#
|
||||
# Amounts are 6-decimal raw token units (e.g. 100 USDC face = 100000000 raw).
|
||||
#
|
||||
# Usage:
|
||||
# source scripts/lib/load-project-env.sh
|
||||
# export POOL_CWUSDT_CWUSDC_MAINNET=0x... # required after deploy
|
||||
# bash scripts/deployment/run-mainnet-cwusdt-cwusdc-soak-roundtrips.sh \
|
||||
# --amounts-raw 100000000,500000000,1000000000,5000000000,10000000000 \
|
||||
# --repeat-list 10
|
||||
#
|
||||
# bash scripts/deployment/run-mainnet-cwusdt-cwusdc-soak-roundtrips.sh --dry-run # explicit (default)
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# Proxmox repo root (smom-dbis-138 load-env overwrites PROJECT_ROOT to the submodule)
|
||||
PROXMOX_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROXMOX_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1
|
||||
|
||||
SWAP="${PROXMOX_ROOT}/scripts/deployment/run-mainnet-public-dodo-cw-swap.sh"
|
||||
|
||||
AMOUNTS_RAW=""
|
||||
REPEAT_LIST="1"
|
||||
DRY_RUN=1
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--amounts-raw=*) AMOUNTS_RAW="${arg#*=}" ;;
|
||||
--repeat-list=*) REPEAT_LIST="${arg#*=}" ;;
|
||||
--dry-run) DRY_RUN=1 ;;
|
||||
--apply) DRY_RUN=0 ;;
|
||||
-h | --help)
|
||||
sed -n '1,28p' "$0"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$AMOUNTS_RAW" ]]; then
|
||||
echo "[fail] required: --amounts-raw=comma-separated raw amounts (6 dp)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
POOL_CWUSDT_CWUSDC_MAINNET="${POOL_CWUSDT_CWUSDC_MAINNET:-0xe944b7Cb012A0820c07f54D51e92f0e1C74168DB}"
|
||||
export POOL_CWUSDT_CWUSDC_MAINNET
|
||||
|
||||
if ! [[ "$REPEAT_LIST" =~ ^[0-9]+$ ]] || (( REPEAT_LIST < 1 )); then
|
||||
echo "[fail] --repeat-list must be a positive integer" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DRY_FLAG=()
|
||||
if (( DRY_RUN == 1 )); then
|
||||
DRY_FLAG=(--dry-run)
|
||||
fi
|
||||
|
||||
if (( DRY_RUN == 0 )); then
|
||||
echo "[warn] --apply will broadcast mainnet txs; ensure amounts, slippage, and wallet inventory are correct." >&2
|
||||
fi
|
||||
|
||||
IFS=',' read -r -a AMOUNTS <<<"$AMOUNTS_RAW"
|
||||
|
||||
run_leg() {
|
||||
local direction="$1"
|
||||
local amount="$2"
|
||||
bash "$SWAP" --pair=cwusdt-cwusdc --direction="$direction" --amount="$amount" "${DRY_FLAG[@]}"
|
||||
}
|
||||
|
||||
echo "=== cWUSDT/cWUSDC soak (repeat-list=$REPEAT_LIST dryRun=$DRY_RUN) ==="
|
||||
echo "pool=${POOL_CWUSDT_CWUSDC_MAINNET}"
|
||||
|
||||
for ((c = 1; c <= REPEAT_LIST; c++)); do
|
||||
echo
|
||||
echo "--- list pass $c / $REPEAT_LIST ---"
|
||||
for raw in "${AMOUNTS[@]}"; do
|
||||
raw="$(echo "$raw" | tr -d '[:space:]')"
|
||||
if ! [[ "$raw" =~ ^[0-9]+$ ]] || (( raw < 1 )); then
|
||||
echo "[fail] invalid amount token: $raw" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo
|
||||
echo "leg A base-to-quote amount=$raw"
|
||||
leg_a_out="$(run_leg base-to-quote "$raw")"
|
||||
printf '%s\n' "$leg_a_out"
|
||||
est="$(printf '%s\n' "$leg_a_out" | sed -n 's/^estimatedOut=//p' | head -1)"
|
||||
if [[ -z "$est" || ! "$est" =~ ^[0-9]+$ ]]; then
|
||||
echo "[fail] could not parse estimatedOut from leg A dry-run / output" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "leg B quote-to-base amount=$est (from leg A estimatedOut)"
|
||||
leg_b_out="$(run_leg quote-to-base "$est")"
|
||||
printf '%s\n' "$leg_b_out"
|
||||
done
|
||||
done
|
||||
|
||||
echo
|
||||
echo "=== done ==="
|
||||
408
scripts/deployment/run-mainnet-public-dodo-cw-swap.sh
Executable file
408
scripts/deployment/run-mainnet-public-dodo-cw-swap.sh
Executable file
@@ -0,0 +1,408 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Repeatable swap helper for the public Mainnet DODO PMM cW bootstrap pools.
|
||||
# Uses the live Mainnet integration, exact-amount approvals, and a reserve-based
|
||||
# quote fallback because the direct query/read path on these pools currently
|
||||
# reverts for hosted reads.
|
||||
#
|
||||
# Examples:
|
||||
# bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cwusdt-usdc --direction=base-to-quote --amount=10000
|
||||
# bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cwusdt-usdc --direction=quote-to-base --amount=10000
|
||||
# bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cweurc-usdc --direction=quote-to-base --amount=1000
|
||||
# bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cwgbpc-usdc --direction=base-to-quote --amount=1000
|
||||
# bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cwaudc-usdc --direction=base-to-quote --amount=1000
|
||||
# bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cwcadc-usdc --direction=quote-to-base --amount=1000
|
||||
# bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cwjpyc-usdc --direction=base-to-quote --amount=1000
|
||||
# bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cwchfc-usdc --direction=quote-to-base --amount=1000
|
||||
# bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cwusdc-usdt --direction=base-to-quote --amount=10000 --min-out=9000
|
||||
# bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cwusdt-usdc --direction=base-to-quote --amount=5000 --dry-run
|
||||
# bash scripts/deployment/run-mainnet-public-dodo-cw-swap.sh --pair=cwusdt-cwusdc --direction=base-to-quote --amount=1000000 --dry-run
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
source "${PROJECT_ROOT}/smom-dbis-138/scripts/load-env.sh" >/dev/null 2>&1
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd cast
|
||||
require_cmd python3
|
||||
|
||||
PAIR="cwusdt-usdc"
|
||||
DIRECTION="base-to-quote"
|
||||
AMOUNT="10000"
|
||||
MIN_OUT=""
|
||||
RPC_URL="${ETHEREUM_MAINNET_RPC:-}"
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-}"
|
||||
INTEGRATION="${DODO_PMM_INTEGRATION_MAINNET:-}"
|
||||
SLIPPAGE_BPS="${SLIPPAGE_BPS:-100}"
|
||||
DRY_RUN=0
|
||||
FORCE_DIRECT_DVM=0
|
||||
ALLOW_DIRECT_DVM_FALLBACK=0
|
||||
LOCK_DIR="/tmp/run-mainnet-public-dodo-cw-swap.${PAIR:-default}.lock"
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--pair=*) PAIR="${arg#*=}" ;;
|
||||
--direction=*) DIRECTION="${arg#*=}" ;;
|
||||
--amount=*) AMOUNT="${arg#*=}" ;;
|
||||
--min-out=*) MIN_OUT="${arg#*=}" ;;
|
||||
--rpc-url=*) RPC_URL="${arg#*=}" ;;
|
||||
--integration=*) INTEGRATION="${arg#*=}" ;;
|
||||
--slippage-bps=*) SLIPPAGE_BPS="${arg#*=}" ;;
|
||||
--dry-run) DRY_RUN=1 ;;
|
||||
--force-direct-dvm) FORCE_DIRECT_DVM=1 ;;
|
||||
--allow-direct-dvm-fallback) ALLOW_DIRECT_DVM_FALLBACK=1 ;;
|
||||
*)
|
||||
echo "[fail] unknown arg: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$RPC_URL" || -z "$PRIVATE_KEY" || -z "$INTEGRATION" ]]; then
|
||||
echo "[fail] ETHEREUM_MAINNET_RPC, PRIVATE_KEY, and DODO_PMM_INTEGRATION_MAINNET are required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEPLOYER="$(cast wallet address --private-key "$PRIVATE_KEY")"
|
||||
|
||||
case "$PAIR" in
|
||||
cwusdt-usdc)
|
||||
POOL="${POOL_CWUSDT_USDC_MAINNET:-}"
|
||||
BASE_TOKEN="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
||||
QUOTE_TOKEN="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
;;
|
||||
cwusdc-usdc)
|
||||
POOL="${POOL_CWUSDC_USDC_MAINNET:-}"
|
||||
BASE_TOKEN="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
QUOTE_TOKEN="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
;;
|
||||
cwusdt-usdt)
|
||||
POOL="${POOL_CWUSDT_USDT_MAINNET:-}"
|
||||
BASE_TOKEN="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
||||
QUOTE_TOKEN="0xdAC17F958D2ee523a2206206994597C13D831ec7"
|
||||
;;
|
||||
cwusdc-usdt)
|
||||
POOL="${POOL_CWUSDC_USDT_MAINNET:-}"
|
||||
BASE_TOKEN="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
QUOTE_TOKEN="0xdAC17F958D2ee523a2206206994597C13D831ec7"
|
||||
;;
|
||||
cwusdt-cwusdc)
|
||||
POOL="${POOL_CWUSDT_CWUSDC_MAINNET:-0xe944b7Cb012A0820c07f54D51e92f0e1C74168DB}"
|
||||
BASE_TOKEN="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
||||
QUOTE_TOKEN="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
;;
|
||||
cweurc-usdc)
|
||||
POOL="${POOL_CWEURC_USDC_MAINNET:-}"
|
||||
BASE_TOKEN="${CWEURC_MAINNET:-0xD4aEAa8cD3fB41Dc8437FaC7639B6d91B60A5e8d}"
|
||||
QUOTE_TOKEN="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
;;
|
||||
cwgbpc-usdc)
|
||||
POOL="${POOL_CWGBPC_USDC_MAINNET:-}"
|
||||
BASE_TOKEN="${CWGBPC_MAINNET:-0xc074007dc0bfb384b1cf6426a56287ed23fe4d52}"
|
||||
QUOTE_TOKEN="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
;;
|
||||
cwaudc-usdc)
|
||||
POOL="${POOL_CWAUDC_USDC_MAINNET:-}"
|
||||
BASE_TOKEN="${CWAUDC_MAINNET:-0x5020Db641B3Fc0dAbBc0c688C845bc4E3699f35F}"
|
||||
QUOTE_TOKEN="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
;;
|
||||
cwcadc-usdc)
|
||||
POOL="${POOL_CWCADC_USDC_MAINNET:-}"
|
||||
BASE_TOKEN="${CWCADC_MAINNET:-0x209FE32fe7B541751D190ae4e50cd005DcF8EDb4}"
|
||||
QUOTE_TOKEN="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
;;
|
||||
cwjpyc-usdc)
|
||||
POOL="${POOL_CWJPYC_USDC_MAINNET:-}"
|
||||
BASE_TOKEN="${CWJPYC_MAINNET:-0x07EEd0D7dD40984e47B9D3a3bdded1c536435582}"
|
||||
QUOTE_TOKEN="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
;;
|
||||
cwchfc-usdc)
|
||||
POOL="${POOL_CWCHFC_USDC_MAINNET:-}"
|
||||
BASE_TOKEN="${CWCHFC_MAINNET:-0x0F91C5E6Ddd46403746aAC970D05d70FFe404780}"
|
||||
QUOTE_TOKEN="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||
;;
|
||||
*)
|
||||
echo "[fail] unsupported pair: $PAIR" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -z "$POOL" ]]; then
|
||||
POOL="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$BASE_TOKEN" "$QUOTE_TOKEN" --rpc-url "$RPC_URL" | awk '{print $1}' || true)"
|
||||
else
|
||||
mapped_pool="$(cast call "$INTEGRATION" 'pools(address,address)(address)' "$BASE_TOKEN" "$QUOTE_TOKEN" --rpc-url "$RPC_URL" 2>/dev/null | awk '{print $1}' || true)"
|
||||
if [[ -n "$mapped_pool" && "$mapped_pool" != "0x0000000000000000000000000000000000000000" && "${mapped_pool,,}" != "${POOL,,}" ]]; then
|
||||
POOL="$mapped_pool"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$POOL" || "$POOL" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
echo "[fail] pool missing for pair: $PAIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$DIRECTION" in
|
||||
base-to-quote)
|
||||
TOKEN_IN="$BASE_TOKEN"
|
||||
TOKEN_OUT="$QUOTE_TOKEN"
|
||||
DIRECT_METHOD="sellBase(address)(uint256)"
|
||||
;;
|
||||
quote-to-base)
|
||||
TOKEN_IN="$QUOTE_TOKEN"
|
||||
TOKEN_OUT="$BASE_TOKEN"
|
||||
DIRECT_METHOD="sellQuote(address)(uint256)"
|
||||
;;
|
||||
*)
|
||||
echo "[fail] unsupported direction: $DIRECTION" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
reserve_info="$(cast call "$POOL" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL")"
|
||||
base_reserve="$(printf '%s\n' "$reserve_info" | sed -n '1p' | awk '{print $1}')"
|
||||
quote_reserve="$(printf '%s\n' "$reserve_info" | sed -n '2p' | awk '{print $1}')"
|
||||
fee_rate="$(cast call "$POOL" '_LP_FEE_RATE_()(uint256)' --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
mid_price="$(cast call "$POOL" 'getMidPrice()(uint256)' --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
quote_source="reserve_fallback"
|
||||
query_amount_out=""
|
||||
pool_surface="reserve_only"
|
||||
if [[ "$DIRECTION" == "base-to-quote" ]]; then
|
||||
if query_output="$(cast call "$POOL" 'querySellBase(address,uint256)(uint256,uint256)' "$DEPLOYER" "$AMOUNT" --rpc-url "$RPC_URL" 2>/dev/null)"; then
|
||||
query_amount_out="$(printf '%s\n' "$query_output" | sed -n '1p' | awk '{print $1}')"
|
||||
quote_source="pool_query"
|
||||
fi
|
||||
else
|
||||
if query_output="$(cast call "$POOL" 'querySellQuote(address,uint256)(uint256,uint256)' "$DEPLOYER" "$AMOUNT" --rpc-url "$RPC_URL" 2>/dev/null)"; then
|
||||
query_amount_out="$(printf '%s\n' "$query_output" | sed -n '1p' | awk '{print $1}')"
|
||||
quote_source="pool_query"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$quote_source" == "pool_query" ]]; then
|
||||
pool_surface="full_quote_surface"
|
||||
elif [[ -n "$base_reserve" && -n "$quote_reserve" && -n "$mid_price" ]]; then
|
||||
# This class of mainnet pool exposes reserve/mid-price getters but reverts on querySell*.
|
||||
# Treat it as integration-only unless proven otherwise.
|
||||
pool_surface="partial_dodo_surface_integration_only"
|
||||
fi
|
||||
|
||||
quote_info="$(python3 - "$AMOUNT" "$base_reserve" "$quote_reserve" "$fee_rate" "$DIRECTION" "$SLIPPAGE_BPS" "$quote_source" "$query_amount_out" <<'PY'
|
||||
import sys
|
||||
amount_in = int(sys.argv[1])
|
||||
base_reserve = int(sys.argv[2])
|
||||
quote_reserve = int(sys.argv[3])
|
||||
fee_rate = int(sys.argv[4])
|
||||
direction = sys.argv[5]
|
||||
slippage_bps = int(sys.argv[6])
|
||||
quote_source = sys.argv[7]
|
||||
query_amount_out = int(sys.argv[8] or "0")
|
||||
|
||||
if quote_source == "pool_query":
|
||||
out = query_amount_out
|
||||
else:
|
||||
net = amount_in * max(0, 10000 - fee_rate) // 10000
|
||||
if direction == "base-to-quote":
|
||||
out = (net * quote_reserve) // (base_reserve + net)
|
||||
else:
|
||||
out = (net * base_reserve) // (quote_reserve + net)
|
||||
|
||||
min_out = out * max(0, 10000 - slippage_bps) // 10000
|
||||
print(out)
|
||||
print(min_out)
|
||||
PY
|
||||
)"
|
||||
|
||||
estimated_out="$(printf '%s\n' "$quote_info" | sed -n '1p')"
|
||||
fallback_min_out="$(printf '%s\n' "$quote_info" | sed -n '2p')"
|
||||
|
||||
if [[ -z "$MIN_OUT" ]]; then
|
||||
MIN_OUT="$fallback_min_out"
|
||||
fi
|
||||
|
||||
allowance="$(cast call "$TOKEN_IN" 'allowance(address,address)(uint256)' "$DEPLOYER" "$INTEGRATION" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
balance_in_before="$(cast call "$TOKEN_IN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
balance_out_before="$(cast call "$TOKEN_OUT" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
|
||||
if (( balance_in_before < AMOUNT )); then
|
||||
echo "[fail] insufficient input-token balance: have=$balance_in_before need=$AMOUNT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( DRY_RUN == 1 )); then
|
||||
echo "pair=$PAIR"
|
||||
echo "direction=$DIRECTION"
|
||||
echo "pool=$POOL"
|
||||
echo "integration=$INTEGRATION"
|
||||
echo "poolSurface=$pool_surface"
|
||||
echo "quoteSource=$quote_source"
|
||||
echo "midPrice=$mid_price"
|
||||
echo "lpFeeRate=$fee_rate"
|
||||
echo "baseReserve=$base_reserve"
|
||||
echo "quoteReserve=$quote_reserve"
|
||||
echo "amountIn=$AMOUNT"
|
||||
echo "estimatedOut=$estimated_out"
|
||||
echo "minOut=$MIN_OUT"
|
||||
echo "tokenIn=$TOKEN_IN"
|
||||
echo "tokenOut=$TOKEN_OUT"
|
||||
echo "tokenInBalanceBefore=$balance_in_before"
|
||||
echo "tokenOutBalanceBefore=$balance_out_before"
|
||||
echo "allowanceBefore=$allowance"
|
||||
echo "approvalRequired=$(( allowance < AMOUNT ? 1 : 0 ))"
|
||||
echo "dryRun=1"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
LOCK_DIR="/tmp/run-mainnet-public-dodo-cw-swap.${PAIR}.${DIRECTION}.lock"
|
||||
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
|
||||
echo "[fail] another live $PAIR $DIRECTION swap helper run appears to be in flight; rerun serially to avoid nonce collisions" >&2
|
||||
exit 1
|
||||
fi
|
||||
cleanup_lock() {
|
||||
rm -f "$LOCK_DIR/integration-estimate.err" 2>/dev/null || true
|
||||
rmdir "$LOCK_DIR" 2>/dev/null || true
|
||||
}
|
||||
trap cleanup_lock EXIT
|
||||
|
||||
if (( allowance < AMOUNT )); then
|
||||
cast send "$TOKEN_IN" \
|
||||
'approve(address,uint256)(bool)' \
|
||||
"$INTEGRATION" "$AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY" >/dev/null
|
||||
fi
|
||||
|
||||
tx_mode="integration"
|
||||
tx_output=""
|
||||
integration_error=""
|
||||
transfer_tx_output=""
|
||||
transfer_tx_hash=""
|
||||
|
||||
if (( FORCE_DIRECT_DVM == 1 )); then
|
||||
integration_error="forced direct DVM fallback (--force-direct-dvm)"
|
||||
else
|
||||
if ! cast estimate \
|
||||
--from "$DEPLOYER" \
|
||||
"$INTEGRATION" \
|
||||
'swapExactIn(address,address,uint256,uint256)(uint256)' \
|
||||
"$POOL" "$TOKEN_IN" "$AMOUNT" "$MIN_OUT" \
|
||||
--rpc-url "$RPC_URL" >/dev/null 2>"$LOCK_DIR/integration-estimate.err"; then
|
||||
integration_error="$(tr '\n' ' ' <"$LOCK_DIR/integration-estimate.err" | sed 's/[[:space:]]\+/ /g; s/^ //; s/ $//')"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$integration_error" ]]; then
|
||||
if ! tx_output="$(
|
||||
cast send "$INTEGRATION" \
|
||||
'swapExactIn(address,address,uint256,uint256)(uint256)' \
|
||||
"$POOL" "$TOKEN_IN" "$AMOUNT" "$MIN_OUT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY" 2>&1
|
||||
)"; then
|
||||
integration_error="$(printf '%s' "$tx_output" | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g; s/^ //; s/ $//')"
|
||||
tx_output=""
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$integration_error" ]]; then
|
||||
if [[ "$pool_surface" == "partial_dodo_surface_integration_only" ]]; then
|
||||
echo "[fail] integration swap failed on a partial DODO surface / integration-only pool; refusing raw direct fallback" >&2
|
||||
echo "[fail] integration error: $integration_error" >&2
|
||||
echo "[fail] pool=$POOL base=$BASE_TOKEN quote=$QUOTE_TOKEN tokenIn=$TOKEN_IN direction=$DIRECTION" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( FORCE_DIRECT_DVM == 0 && ALLOW_DIRECT_DVM_FALLBACK == 0 )); then
|
||||
echo "[fail] integration swap failed; refusing direct DVM fallback unless --allow-direct-dvm-fallback or --force-direct-dvm is set" >&2
|
||||
echo "[fail] integration error: $integration_error" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[warn] integration swap failed; falling back to direct DVM flow" >&2
|
||||
echo "[warn] integration error: $integration_error" >&2
|
||||
|
||||
transfer_tx_output="$(
|
||||
cast send "$TOKEN_IN" \
|
||||
'transfer(address,uint256)(bool)' \
|
||||
"$POOL" "$AMOUNT" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY"
|
||||
)"
|
||||
transfer_tx_hash="$(printf '%s\n' "$transfer_tx_output" | grep -E '^0x[0-9a-fA-F]{64}$' | tail -n1 || true)"
|
||||
if [[ -z "$transfer_tx_hash" ]]; then
|
||||
transfer_tx_hash="$(printf '%s\n' "$transfer_tx_output" | grep -E '^transactionHash[[:space:]]+0x[0-9a-fA-F]{64}$' | awk '{print $2}' | tail -n1 || true)"
|
||||
fi
|
||||
if [[ -z "$transfer_tx_hash" ]]; then
|
||||
echo "[fail] could not parse transfer transaction hash from cast output" >&2
|
||||
printf '%s\n' "$transfer_tx_output" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cast receipt "$transfer_tx_hash" --rpc-url "$RPC_URL" >/dev/null
|
||||
|
||||
if ! tx_output="$(
|
||||
cast send "$POOL" \
|
||||
"$DIRECT_METHOD" \
|
||||
"$DEPLOYER" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY" 2>&1
|
||||
)"; then
|
||||
echo "[fail] direct DVM fallback failed" >&2
|
||||
printf '%s\n' "$tx_output" >&2
|
||||
exit 1
|
||||
fi
|
||||
tx_mode="direct_dvm"
|
||||
fi
|
||||
|
||||
tx_hash="$(printf '%s\n' "$tx_output" | grep -E '^0x[0-9a-fA-F]{64}$' | tail -n1 || true)"
|
||||
if [[ -z "$tx_hash" ]]; then
|
||||
tx_hash="$(printf '%s\n' "$tx_output" | grep -E '^transactionHash[[:space:]]+0x[0-9a-fA-F]{64}$' | awk '{print $2}' | tail -n1 || true)"
|
||||
fi
|
||||
if [[ -z "$tx_hash" ]]; then
|
||||
echo "[fail] could not parse transaction hash from cast output" >&2
|
||||
printf '%s\n' "$tx_output" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
balance_in_after="$(cast call "$TOKEN_IN" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
balance_out_after="$(cast call "$TOKEN_OUT" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
amount_out_delta=$((balance_out_after - balance_out_before))
|
||||
|
||||
if (( amount_out_delta < MIN_OUT )); then
|
||||
echo "[fail] observed output below minOut after ${tx_mode}: got=$amount_out_delta minOut=$MIN_OUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "pair=$PAIR"
|
||||
echo "direction=$DIRECTION"
|
||||
echo "pool=$POOL"
|
||||
echo "integration=$INTEGRATION"
|
||||
echo "txMode=$tx_mode"
|
||||
echo "poolSurface=$pool_surface"
|
||||
if [[ -n "$transfer_tx_hash" ]]; then
|
||||
echo "transferTxHash=$transfer_tx_hash"
|
||||
fi
|
||||
echo "quoteSource=$quote_source"
|
||||
echo "midPrice=$mid_price"
|
||||
echo "lpFeeRate=$fee_rate"
|
||||
echo "baseReserve=$base_reserve"
|
||||
echo "quoteReserve=$quote_reserve"
|
||||
echo "amountIn=$AMOUNT"
|
||||
echo "estimatedOut=$estimated_out"
|
||||
echo "minOut=$MIN_OUT"
|
||||
echo "tokenIn=$TOKEN_IN"
|
||||
echo "tokenOut=$TOKEN_OUT"
|
||||
echo "txHash=$tx_hash"
|
||||
echo "tokenInBalanceBefore=$balance_in_before"
|
||||
echo "tokenInBalanceAfter=$balance_in_after"
|
||||
echo "tokenOutBalanceBefore=$balance_out_before"
|
||||
echo "tokenOutBalanceAfter=$balance_out_after"
|
||||
echo "observedAmountOut=$amount_out_delta"
|
||||
164
scripts/deployment/run-progressive-router-v2-swaps-chain138.sh
Normal file
164
scripts/deployment/run-progressive-router-v2-swaps-chain138.sh
Normal file
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh"
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "[fail] missing required command: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
require_cmd cast
|
||||
require_cmd curl
|
||||
require_cmd jq
|
||||
require_cmd python3
|
||||
|
||||
RPC_URL="${RPC_URL_138:-https://rpc-http-pub.d-bis.org}"
|
||||
BASE_URL="${BASE_URL:-https://explorer.d-bis.org/token-aggregation}"
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-}"
|
||||
if [[ -z "${PRIVATE_KEY}" ]]; then
|
||||
echo "[fail] PRIVATE_KEY is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SIGNER="$(cast wallet address --private-key "${PRIVATE_KEY}")"
|
||||
USDT="${USDT_ADDRESS_138:-0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1}"
|
||||
WETH="${WETH9_ADDRESS_138:-0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2}"
|
||||
ROUTER="${ENHANCED_SWAP_ROUTER_V2_ADDRESS:-0xF1c93F54A5C2fc0d7766Ccb0Ad8f157DFB4C99Ce}"
|
||||
APPROVE_TOTAL_RAW="${APPROVE_TOTAL_RAW:-2000000000}" # 2,000 USDT
|
||||
if (( $# > 0 )); then
|
||||
SIZES_USD=("$@")
|
||||
else
|
||||
SIZES_USD=(10 50 100 250 500 1000)
|
||||
fi
|
||||
|
||||
bal_of() {
|
||||
cast call "$1" 'balanceOf(address)(uint256)' "$SIGNER" --rpc-url "$RPC_URL" | awk '{print $1}'
|
||||
}
|
||||
|
||||
fmt_amount() {
|
||||
python3 - "$1" "$2" <<'PY'
|
||||
from decimal import Decimal
|
||||
import sys
|
||||
raw = Decimal(sys.argv[1])
|
||||
decimals = Decimal(10) ** Decimal(sys.argv[2])
|
||||
print(raw / decimals)
|
||||
PY
|
||||
}
|
||||
|
||||
ensure_allowance() {
|
||||
local current
|
||||
current="$(cast call "$USDT" 'allowance(address,address)(uint256)' "$SIGNER" "$ROUTER" --rpc-url "$RPC_URL" | awk '{print $1}')"
|
||||
if [[ "$current" =~ ^[0-9]+$ ]] && (( current >= APPROVE_TOTAL_RAW )); then
|
||||
echo "[ok] router allowance already sufficient: $current"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "[info] approving router for ${APPROVE_TOTAL_RAW} raw USDT"
|
||||
cast send "$USDT" \
|
||||
'approve(address,uint256)' \
|
||||
"$ROUTER" "$APPROVE_TOTAL_RAW" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY" \
|
||||
--legacy \
|
||||
--json | jq -r '.transactionHash'
|
||||
}
|
||||
|
||||
build_plan() {
|
||||
local amount_in_raw="$1"
|
||||
curl -fsS \
|
||||
-H 'content-type: application/json' \
|
||||
-X POST \
|
||||
--data "{\"sourceChainId\":138,\"tokenIn\":\"${USDT}\",\"tokenOut\":\"${WETH}\",\"amountIn\":\"${amount_in_raw}\"}" \
|
||||
"${BASE_URL}/api/v2/routes/internal-execution-plan"
|
||||
}
|
||||
|
||||
run_swap() {
|
||||
local usd="$1"
|
||||
local amount_in_raw=$(( usd * 1000000 ))
|
||||
local plan provider contract calldata estimated min_out pre_usdt pre_weth tx_hash status post_usdt post_weth actual_usdt actual_weth
|
||||
|
||||
echo
|
||||
echo "== ${usd} USDT -> WETH =="
|
||||
|
||||
plan="$(build_plan "$amount_in_raw")"
|
||||
provider="$(printf '%s\n' "$plan" | jq -r '.plannerResponse.legs[0].provider // .plannerResponse.routePlan.legs[0].provider // "unknown"')"
|
||||
contract="$(printf '%s\n' "$plan" | jq -r '.execution.contractAddress // .execution.target')"
|
||||
calldata="$(printf '%s\n' "$plan" | jq -r '.execution.encodedCalldata')"
|
||||
estimated="$(printf '%s\n' "$plan" | jq -r '.plannerResponse.estimatedAmountOut')"
|
||||
min_out="$(printf '%s\n' "$plan" | jq -r '.plannerResponse.minAmountOut')"
|
||||
|
||||
[[ -n "$contract" && "$contract" != "null" ]] || {
|
||||
echo "[fail] planner did not return contract address" >&2
|
||||
exit 1
|
||||
}
|
||||
[[ -n "$calldata" && "$calldata" != "null" ]] || {
|
||||
echo "[fail] planner did not return executable calldata" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
pre_usdt="$(bal_of "$USDT")"
|
||||
pre_weth="$(bal_of "$WETH")"
|
||||
|
||||
tx_hash="$(
|
||||
cast send "$contract" \
|
||||
"$calldata" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--private-key "$PRIVATE_KEY" \
|
||||
--legacy \
|
||||
--json | jq -r '.transactionHash'
|
||||
)"
|
||||
|
||||
status="$(cast receipt "$tx_hash" --rpc-url "$RPC_URL" --json | jq -r '.status')"
|
||||
if [[ "$status" != "1" && "$status" != "0x1" ]]; then
|
||||
echo "[fail] tx reverted: $tx_hash" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
post_usdt="$(bal_of "$USDT")"
|
||||
post_weth="$(bal_of "$WETH")"
|
||||
actual_usdt=$(( pre_usdt - post_usdt ))
|
||||
actual_weth=$(( post_weth - pre_weth ))
|
||||
|
||||
jq -n \
|
||||
--arg usd "$usd" \
|
||||
--arg provider "$provider" \
|
||||
--arg contract "$contract" \
|
||||
--arg tx_hash "$tx_hash" \
|
||||
--arg amount_in_raw "$amount_in_raw" \
|
||||
--arg estimated "$estimated" \
|
||||
--arg min_out "$min_out" \
|
||||
--arg actual_usdt "$actual_usdt" \
|
||||
--arg actual_weth "$actual_weth" \
|
||||
--arg actual_usdt_fmt "$(fmt_amount "$actual_usdt" 6)" \
|
||||
--arg actual_weth_fmt "$(fmt_amount "$actual_weth" 18)" \
|
||||
'{
|
||||
usdNotional: ($usd | tonumber),
|
||||
provider: $provider,
|
||||
contract: $contract,
|
||||
txHash: $tx_hash,
|
||||
amountInRaw: $amount_in_raw,
|
||||
estimatedAmountOutRaw: $estimated,
|
||||
minAmountOutRaw: $min_out,
|
||||
actualAmountInRaw: $actual_usdt,
|
||||
actualAmountOutRaw: $actual_weth,
|
||||
actualAmountIn: $actual_usdt_fmt,
|
||||
actualAmountOut: $actual_weth_fmt
|
||||
}'
|
||||
}
|
||||
|
||||
echo "Signer: ${SIGNER}"
|
||||
echo "RPC: ${RPC_URL}"
|
||||
echo "Base: ${BASE_URL}"
|
||||
|
||||
ensure_allowance
|
||||
|
||||
for usd in "${SIZES_USD[@]}"; do
|
||||
run_swap "$usd"
|
||||
done
|
||||
@@ -5,7 +5,8 @@
|
||||
# Addresses are from docs/11-references/CONTRACT_ADDRESSES_REFERENCE.md and TOKENS_AND_NETWORKS_MINTABLE_TO_DEPLOYER.md.
|
||||
# Usage: ./scripts/deployment/set-dotenv-c-tokens-and-register-gru.sh [--no-register] [--register-v2]
|
||||
# --no-register Only update .env; do not run RegisterGRUCompliantTokens.
|
||||
# --register-v2 After V1 registration, also register staged V2 cUSDT/cUSDC using RegisterGRUCompliantTokensV2.
|
||||
# --register-v2 After V1 registration, also register or recover the deployed forward-canonical V2 cUSDT/cUSDC
|
||||
# addresses using RegisterGRUCompliantTokensV2.
|
||||
#
|
||||
# Note: RegisterGRUCompliantTokens requires (1) broadcast account has REGISTRAR_ROLE, and (2) the
|
||||
# UniversalAssetRegistry *implementation* (not just proxy) exposes registerGRUCompliantAsset.
|
||||
@@ -50,10 +51,10 @@ set_env_var "COMPLIANT_USDT" "0x93E66202A11B1772E55407B32B44e5Cd8eda7f22"
|
||||
set_env_var "COMPLIANT_USDC" "0xf22258f57794CC8E06237084b353Ab30fFfa640b"
|
||||
set_env_var "CUSDT_ADDRESS_138" "0x93E66202A11B1772E55407B32B44e5Cd8eda7f22"
|
||||
set_env_var "CUSDC_ADDRESS_138" "0xf22258f57794CC8E06237084b353Ab30fFfa640b"
|
||||
set_env_var "COMPLIANT_USDT_V2" "0x8d342d321DdEe97D0c5011DAF8ca0B59DA617D29"
|
||||
set_env_var "COMPLIANT_USDC_V2" "0x1ac3F4942a71E86A9682D91837E1E71b7BACdF99"
|
||||
set_env_var "CUSDT_V2_ADDRESS_138" "0x8d342d321DdEe97D0c5011DAF8ca0B59DA617D29"
|
||||
set_env_var "CUSDC_V2_ADDRESS_138" "0x1ac3F4942a71E86A9682D91837E1E71b7BACdF99"
|
||||
set_env_var "COMPLIANT_USDT_V2" "0x9FBfab33882Efe0038DAa608185718b772EE5660"
|
||||
set_env_var "COMPLIANT_USDC_V2" "0x219522c60e83dEe01FC5b0329d6fA8fD84b9D13d"
|
||||
set_env_var "CUSDT_V2_ADDRESS_138" "0x9FBfab33882Efe0038DAa608185718b772EE5660"
|
||||
set_env_var "CUSDC_V2_ADDRESS_138" "0x219522c60e83dEe01FC5b0329d6fA8fD84b9D13d"
|
||||
# cEURC (TOKENS_AND_NETWORKS_MINTABLE_TO_DEPLOYER)
|
||||
set_env_var "CEURC_ADDRESS_138" "0x8085961F9cF02b4d800A3c6d386D31da4B34266a"
|
||||
|
||||
@@ -77,7 +78,7 @@ set_env_var "WETH10" "0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f"
|
||||
set_env_var "LINK_TOKEN" "0xb7721dD53A8c629d9f1Ba31a5819AFe250002b03"
|
||||
set_env_var "CCIP_FEE_TOKEN" "0xb7721dD53A8c629d9f1Ba31a5819AFe250002b03"
|
||||
|
||||
echo "Done. Set: COMPLIANT_USDT, COMPLIANT_USDC, COMPLIANT_USDT_V2, COMPLIANT_USDC_V2, all C*_ADDRESS_138 (including staged V2 addresses), UNIVERSAL_ASSET_REGISTRY, WETH9, WETH10, LINK_TOKEN, CCIP_FEE_TOKEN."
|
||||
echo "Done. Set: COMPLIANT_USDT, COMPLIANT_USDC, COMPLIANT_USDT_V2, COMPLIANT_USDC_V2, all C*_ADDRESS_138 (including forward-canonical V2 addresses), UNIVERSAL_ASSET_REGISTRY, WETH9, WETH10, LINK_TOKEN, CCIP_FEE_TOKEN."
|
||||
echo "All c* on explorer.d-bis.org/tokens must be GRU-registered. See docs/04-configuration/EXPLORER_TOKENS_GRU_POLICY.md."
|
||||
|
||||
if [[ "$RUN_REGISTER" -eq 0 ]]; then
|
||||
@@ -97,12 +98,12 @@ export RPC_URL_138="$RPC"
|
||||
|
||||
if [[ "$RUN_REGISTER_V2" -eq 1 ]]; then
|
||||
echo ""
|
||||
echo "=== Registering staged c* V2 inventory as GRU (RegisterGRUCompliantTokensV2) ==="
|
||||
echo "=== Registering deployed c* V2 inventory as GRU (RegisterGRUCompliantTokensV2) ==="
|
||||
(cd "$SMOM" && forge script script/deploy/RegisterGRUCompliantTokensV2.s.sol \
|
||||
--rpc-url "$RPC_URL_138" --broadcast --private-key "$PRIVATE_KEY" --with-gas-price 1000000000)
|
||||
else
|
||||
echo ""
|
||||
echo "V2 note: COMPLIANT_USDT_V2 / COMPLIANT_USDC_V2 are staged in .env but not auto-registered."
|
||||
echo "Use --register-v2 once downstream registry consumers are ready for version-aware symbols."
|
||||
echo "V2 note: COMPLIANT_USDT_V2 / COMPLIANT_USDC_V2 are now the forward-canonical GRU V2 addresses."
|
||||
echo "Use --register-v2 only for explicit recovery or if a fresh deployment has not already staged itself through DeployAndStageCompliantFiatTokensV2ForChain."
|
||||
fi
|
||||
echo "=== Done. ==="
|
||||
|
||||
@@ -27,12 +27,12 @@ echo "=== Set missing dotenv (Chain 138 / DODO PMM) ==="
|
||||
echo " Target: $ENV_FILE"
|
||||
echo ""
|
||||
|
||||
append_if_missing "DODO_PMM_PROVIDER_ADDRESS" "0x5CAe6Ce155b7f08D3a956F5Dc82fC9945f29B381"
|
||||
append_if_missing "DODO_PMM_INTEGRATION_ADDRESS" "0x5BDc62f1ae7D630c37A8B363a1d49845356Ee72d"
|
||||
append_if_missing "POOL_CUSDTCUSDC" "0x9fcB06Aa1FD5215DC0E91Fd098aeff4B62fEa5C8"
|
||||
append_if_missing "POOL_CUSDTUSDT" "0x6fc60DEDc92a2047062294488539992710b99D71"
|
||||
append_if_missing "POOL_CUSDCUSDC" "0x9f74Be42725f2Aa072a9E0CdCce0E7203C510263"
|
||||
append_if_missing "CHAIN_138_DODO_PMM_INTEGRATION" "0x5BDc62f1ae7D630c37A8B363a1d49845356Ee72d"
|
||||
append_if_missing "DODO_PMM_PROVIDER_ADDRESS" "0x3f729632E9553EBacCdE2e9b4c8F2B285b014F2e"
|
||||
append_if_missing "DODO_PMM_INTEGRATION_ADDRESS" "0x86ADA6Ef91A3B450F89f2b751e93B1b7A3218895"
|
||||
append_if_missing "POOL_CUSDTCUSDC" "0x9e89bAe009adf128782E19e8341996c596ac40dC"
|
||||
append_if_missing "POOL_CUSDTUSDT" "0x866Cb44b59303d8dc5f4F9E3E7A8e8b0bf238d66"
|
||||
append_if_missing "POOL_CUSDCUSDC" "0xc39B7D0F40838cbFb54649d327f49a6DAC964062"
|
||||
append_if_missing "CHAIN_138_DODO_PMM_INTEGRATION" "0x86ADA6Ef91A3B450F89f2b751e93B1b7A3218895"
|
||||
|
||||
echo ""
|
||||
echo "Done. Verify: grep -E 'DODO_PMM|POOL_' $ENV_FILE"
|
||||
|
||||
213
scripts/deployment/swap-random-registered-pmm-pools-chain138.sh
Executable file
213
scripts/deployment/swap-random-registered-pmm-pools-chain138.sh
Executable file
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env bash
|
||||
# Randomized PMM swaps on Chain 138 against official DODO DVM pools (two-step pattern).
|
||||
#
|
||||
# Official DVM uses sellBase(address)/sellQuote(address) — excess token balance vs reserve is
|
||||
# swapped and sent to `to`. Flow: ERC20.transfer(pool, amountIn) then pool.sellBase(signer)
|
||||
# or sellQuote(signer). The deployed DODOPMMIntegration at 0x86ADA6… historically called the
|
||||
# wrong ABI (sellBase(uint256)) until fixed in smom-dbis-138; this script talks to pools
|
||||
# directly so swaps work without waiting for integration redeploy.
|
||||
#
|
||||
# Scope (default): three public stable DVM pools (cUSDT/cUSDC, cUSDT/USDT, cUSDC/USDC).
|
||||
#
|
||||
# Optional: TRY_XAU=1 — also iterates six funded XAU PMM pools from ADDRESS_MATRIX. On current
|
||||
# chain, those addresses typically revert querySellBase/Quote (legacy / non-DVM PMM). The script
|
||||
# probes each pool and skips with a clear message if quotes fail. Probe manually:
|
||||
# ./scripts/verify/probe-pmm-pool-dvm-chain138.sh <pool> [RPC]
|
||||
#
|
||||
# After fixing DODOPMMIntegration in smom-dbis-138, redeploy and register pools:
|
||||
# cd smom-dbis-138 && forge script script/dex/DeployDODOPMMIntegration.s.sol --rpc-url $RPC_URL_138 --broadcast
|
||||
# Then call importExistingPool(...) per pool on the new integration (admin).
|
||||
#
|
||||
# Constraints: USD_CAP (default 1000) for 6-dec stable notionals; amount <= balance; random size.
|
||||
# GOLD_USD_EST (default 4000) used only if TRY_XAU hits a DVM-compatible XAU pool later.
|
||||
#
|
||||
# Usage:
|
||||
# RPC_URL_138=https://rpc-http-pub.d-bis.org ./scripts/deployment/swap-random-registered-pmm-pools-chain138.sh --dry-run
|
||||
# TRY_XAU=1 ./scripts/deployment/swap-random-registered-pmm-pools-chain138.sh --dry-run
|
||||
# ./scripts/deployment/swap-random-registered-pmm-pools-chain138.sh --execute
|
||||
#
|
||||
# Requires: cast, python3, PRIVATE_KEY in env (smom-dbis-138/.env via load-project-env).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
_SAVED_RPC_URL_138="${RPC_URL_138:-}"
|
||||
# shellcheck disable=SC1091
|
||||
source "$PROJECT_ROOT/scripts/lib/load-project-env.sh"
|
||||
[[ -n "$_SAVED_RPC_URL_138" ]] && RPC_URL_138="$_SAVED_RPC_URL_138"
|
||||
export RPC_URL_138
|
||||
|
||||
MODE="${1:-}"
|
||||
if [[ "$MODE" != "--dry-run" && "$MODE" != "--execute" ]]; then
|
||||
echo "Usage: $0 --dry-run | --execute" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RPC="${RPC_URL_138:-https://rpc-http-pub.d-bis.org}"
|
||||
USD_CAP="${USD_CAP:-1000}"
|
||||
TRY_XAU="${TRY_XAU:-0}"
|
||||
GOLD_USD_EST="${GOLD_USD_EST:-4000}"
|
||||
MAX_STABLE_RAW=$((USD_CAP * 1000000))
|
||||
|
||||
if [[ -z "${PRIVATE_KEY:-}" ]]; then
|
||||
if [[ "$MODE" == "--dry-run" ]]; then
|
||||
FROM="${DEPLOYER_ADDRESS:-0x4A666F96fC8764181194447A7dFdb7d471b301C8}"
|
||||
echo "WARN: PRIVATE_KEY unset — dry-run only; FROM=$FROM" >&2
|
||||
else
|
||||
echo "ERROR: PRIVATE_KEY required for --execute" >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
FROM=$(cast wallet address --private-key "$PRIVATE_KEY")
|
||||
fi
|
||||
|
||||
cast_u1() { cast "$@" 2>/dev/null | awk '{print $1; exit}'; }
|
||||
|
||||
bal_of() { cast_u1 call "$1" "balanceOf(address)(uint256)" "$FROM" --rpc-url "$RPC"; }
|
||||
|
||||
echo "Signer: $FROM"
|
||||
echo "RPC: $RPC"
|
||||
echo "Mode: $MODE (USD_CAP=$USD_CAP TRY_XAU=$TRY_XAU)"
|
||||
echo ""
|
||||
|
||||
POOL_ROWS=(
|
||||
"0x9e89bAe009adf128782E19e8341996c596ac40dC|0x93E66202A11B1772E55407B32B44e5Cd8eda7f22|0xf22258f57794CC8E06237084b353Ab30fFfa640b|cUSDT/cUSDC"
|
||||
"0x866Cb44b59303d8dc5f4F9E3E7A8e8b0bf238d66|0x93E66202A11B1772E55407B32B44e5Cd8eda7f22|0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1|cUSDT/USDT"
|
||||
"0xc39B7D0F40838cbFb54649d327f49a6DAC964062|0xf22258f57794CC8E06237084b353Ab30fFfa640b|0x71D6687F38b93CCad569Fa6352c876eea967201b|cUSDC/USDC"
|
||||
)
|
||||
|
||||
CXAUC="0x290E52a8819A4fbD0714E517225429aA2B70EC6b"
|
||||
CEURT="0xdf4b71c61E5912712C1Bdd451416B9aC26949d72"
|
||||
|
||||
XAU_POOL_ROWS=(
|
||||
"0x1AA55E2001E5651349AfF5A63FD7A7Ae44f0F1b0|0x93E66202A11B1772E55407B32B44e5Cd8eda7f22|${CXAUC}|cUSDT/cXAUC public"
|
||||
"0xEA9Ac6357CaCB42a83b9082B870610363B177cBa|0xf22258f57794CC8E06237084b353Ab30fFfa640b|${CXAUC}|cUSDC/cXAUC public"
|
||||
"0xbA99bc1eAAC164569d5AcA96C806934DDaF970Cf|${CEURT}|${CXAUC}|cEURT/cXAUC public"
|
||||
"0x94316511621430423a2cff0C036902BAB4aA70c2|0x93E66202A11B1772E55407B32B44e5Cd8eda7f22|${CXAUC}|cUSDT/cXAUC private"
|
||||
"0x7867D58567948e5b9908F1057055Ee4440de0851|0xf22258f57794CC8E06237084b353Ab30fFfa640b|${CXAUC}|cUSDC/cXAUC private"
|
||||
"0x505403093826D494983A93b43Aa0B8601078A44e|${CEURT}|${CXAUC}|cEURT/cXAUC private"
|
||||
)
|
||||
|
||||
pick_amount() {
|
||||
local max_in="$1"
|
||||
local min_lo="${2:-1000000}"
|
||||
python3 -c "
|
||||
import random
|
||||
max_in = int('$max_in')
|
||||
min_lo = int('$min_lo')
|
||||
if max_in < min_lo:
|
||||
print(0)
|
||||
else:
|
||||
lo = min(min_lo, max(min_lo, max_in // 500))
|
||||
hi = max(lo, max_in)
|
||||
print(random.randint(lo, hi))
|
||||
"
|
||||
}
|
||||
|
||||
max_notional_raw() {
|
||||
local token="$1"
|
||||
if [[ "${token,,}" == "${CXAUC,,}" ]]; then
|
||||
python3 -c "print(int(float('$USD_CAP') / float('$GOLD_USD_EST') * 10**6))"
|
||||
return
|
||||
fi
|
||||
echo "$MAX_STABLE_RAW"
|
||||
}
|
||||
|
||||
dvm_quotes_ok() {
|
||||
local pool="$1"
|
||||
cast call "$pool" "querySellBase(address,uint256)(uint256,uint256)" "$FROM" 1000000 --rpc-url "$RPC" >/dev/null 2>&1 \
|
||||
|| cast call "$pool" "querySellQuote(address,uint256)(uint256,uint256)" "$FROM" 1000000 --rpc-url "$RPC" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
run_pool_swap() {
|
||||
local pool="$1" base="$2" quote="$3" label="$4"
|
||||
echo "----------------------------------------"
|
||||
echo "Pool: $label"
|
||||
echo " $pool"
|
||||
|
||||
if ! dvm_quotes_ok "$pool"; then
|
||||
echo " SKIP: DVM quote probe failed (non-DVM pool or incompatible PMM). See scripts/verify/probe-pmm-pool-dvm-chain138.sh"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local dir=$((RANDOM % 2))
|
||||
local token_in dir_lab
|
||||
if [[ "$dir" -eq 0 ]]; then
|
||||
token_in="$base"
|
||||
dir_lab="sellBase (pay base → receive quote)"
|
||||
else
|
||||
token_in="$quote"
|
||||
dir_lab="sellQuote (pay quote → receive base)"
|
||||
fi
|
||||
|
||||
local cap_raw min_lo
|
||||
cap_raw=$(max_notional_raw "$token_in")
|
||||
[[ "${token_in,,}" == "${CXAUC,,}" ]] && min_lo=10000 || min_lo=1000000
|
||||
|
||||
local bal
|
||||
bal=$(bal_of "$token_in")
|
||||
local max_in
|
||||
max_in=$(python3 -c "print(min(int('$bal'), int('$cap_raw')))")
|
||||
if [[ "$max_in" -lt "$min_lo" ]]; then
|
||||
echo " SKIP: balance or cap too low (raw bal=$bal cap=$cap_raw min_lo=$min_lo)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local amt
|
||||
amt=$(pick_amount "$max_in" "$min_lo")
|
||||
if [[ -z "$amt" || "$amt" == "0" ]]; then
|
||||
echo " SKIP: could not pick amount"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local exp
|
||||
if [[ "$dir" -eq 0 ]]; then
|
||||
exp=$(cast_u1 call "$pool" "querySellBase(address,uint256)(uint256,uint256)" "$FROM" "$amt" --rpc-url "$RPC")
|
||||
else
|
||||
exp=$(cast_u1 call "$pool" "querySellQuote(address,uint256)(uint256,uint256)" "$FROM" "$amt" --rpc-url "$RPC")
|
||||
fi
|
||||
|
||||
echo " $dir_lab"
|
||||
echo " tokenIn: $token_in amount: $amt expectedOut: $exp"
|
||||
|
||||
if [[ "$MODE" == "--dry-run" ]]; then
|
||||
echo " DRY-RUN: would transfer then sell"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo " tx1: transfer → pool"
|
||||
cast send --rpc-url "$RPC" --private-key "$PRIVATE_KEY" "$token_in" \
|
||||
"transfer(address,uint256)" "$pool" "$amt" 2>&1 | tail -n 4
|
||||
|
||||
if [[ "$dir" -eq 0 ]]; then
|
||||
echo " tx2: sellBase($FROM)"
|
||||
cast send --rpc-url "$RPC" --private-key "$PRIVATE_KEY" "$pool" \
|
||||
"sellBase(address)" "$FROM" 2>&1 | tail -n 4
|
||||
else
|
||||
echo " tx2: sellQuote($FROM)"
|
||||
cast send --rpc-url "$RPC" --private-key "$PRIVATE_KEY" "$pool" \
|
||||
"sellQuote(address)" "$FROM" 2>&1 | tail -n 4
|
||||
fi
|
||||
}
|
||||
|
||||
echo "=== Stable DVM pools ==="
|
||||
echo ""
|
||||
|
||||
for row in "${POOL_ROWS[@]}"; do
|
||||
IFS='|' read -r pool base quote label <<< "$row"
|
||||
run_pool_swap "$pool" "$base" "$quote" "$label"
|
||||
done
|
||||
|
||||
if [[ "$TRY_XAU" == "1" ]]; then
|
||||
echo ""
|
||||
echo "=== XAU pools (TRY_XAU=1; skipped unless DVM quote probe passes) ==="
|
||||
echo ""
|
||||
for row in "${XAU_POOL_ROWS[@]}"; do
|
||||
IFS='|' read -r pool base quote label <<< "$row"
|
||||
run_pool_swap "$pool" "$base" "$quote" "$label"
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Done."
|
||||
99
scripts/deployment/sync-info-defi-oracle-to-vmid2400.sh
Normal file
99
scripts/deployment/sync-info-defi-oracle-to-vmid2400.sh
Normal file
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build the info.defi-oracle.io SPA locally and sync it into the dedicated web LXC.
|
||||
#
|
||||
# Default target: VMID 2410 on r630-01 (nginx-only CT). Do not deploy to VMID 2400 — that is ThirdWeb RPC.
|
||||
# Provision once: scripts/deployment/provision-info-defi-oracle-web-lxc.sh
|
||||
#
|
||||
# Override: PROXMOX_HOST, INFO_DEFI_ORACLE_VMID (or INFO_DEFI_ORACLE_WEB_VMID from ip-addresses.conf).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true
|
||||
|
||||
PROXMOX_HOST="${PROXMOX_HOST:-${PROXMOX_HOST_R630_01:-192.168.11.11}}"
|
||||
VMID="${INFO_DEFI_ORACLE_VMID:-${INFO_DEFI_ORACLE_WEB_VMID:-2410}}"
|
||||
APP_DIR="${INFO_DEFI_ORACLE_WEB_ROOT:-/var/www/info.defi-oracle.io/html}"
|
||||
SITE_FILE="${INFO_DEFI_ORACLE_NGINX_SITE:-/etc/nginx/sites-available/info-defi-oracle}"
|
||||
NGINX_SRC="${PROJECT_ROOT}/config/nginx/info-defi-oracle-io.site.conf"
|
||||
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
|
||||
DRY_RUN=false
|
||||
|
||||
if [[ "${1:-}" == "--dry-run" ]]; then
|
||||
DRY_RUN=true
|
||||
fi
|
||||
|
||||
if ! command -v pnpm >/dev/null 2>&1; then
|
||||
echo "pnpm is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v tar >/dev/null 2>&1; then
|
||||
echo "tar is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$NGINX_SRC" ]]; then
|
||||
echo "ERROR: Missing $NGINX_SRC" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TMP_TGZ="${TMPDIR:-/tmp}/info-defi-oracle-sync-$$.tgz"
|
||||
REMOTE_TGZ="/tmp/info-defi-oracle-sync-$$.tgz"
|
||||
REMOTE_NGINX="/tmp/info-defi-oracle-io-site-$$.conf"
|
||||
CT_TGZ="/tmp/info-defi-oracle-sync.tgz"
|
||||
cleanup() { rm -f "$TMP_TGZ"; }
|
||||
trap cleanup EXIT
|
||||
|
||||
if $DRY_RUN; then
|
||||
echo "[DRY-RUN] pnpm --filter info-defi-oracle-138 build"
|
||||
echo "[DRY-RUN] tar info-defi-oracle-138/dist -> $TMP_TGZ"
|
||||
echo "[DRY-RUN] scp dist tgz + nginx site -> root@${PROXMOX_HOST}"
|
||||
echo "[DRY-RUN] pct push ${VMID} -> ${CT_TGZ} + site to ${SITE_FILE}"
|
||||
echo "[DRY-RUN] extract to ${APP_DIR}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Building info-defi-oracle-138..."
|
||||
pnpm --filter info-defi-oracle-138 build
|
||||
|
||||
echo "Packaging dist/..."
|
||||
tar czf "$TMP_TGZ" -C "$PROJECT_ROOT/info-defi-oracle-138/dist" .
|
||||
|
||||
echo "Copying package and nginx site to Proxmox host ${PROXMOX_HOST}..."
|
||||
scp $SSH_OPTS "$TMP_TGZ" "root@${PROXMOX_HOST}:${REMOTE_TGZ}"
|
||||
scp $SSH_OPTS "$NGINX_SRC" "root@${PROXMOX_HOST}:${REMOTE_NGINX}"
|
||||
|
||||
echo "Installing site into VMID ${VMID}..."
|
||||
ssh $SSH_OPTS "root@${PROXMOX_HOST}" bash -s "$VMID" "$REMOTE_TGZ" "$CT_TGZ" "$APP_DIR" "$SITE_FILE" "$REMOTE_NGINX" <<'REMOTE'
|
||||
set -euo pipefail
|
||||
VMID="$1"
|
||||
REMOTE_TGZ="$2"
|
||||
CT_TGZ="$3"
|
||||
APP_DIR="$4"
|
||||
SITE_FILE="$5"
|
||||
REMOTE_NGINX="$6"
|
||||
|
||||
pct push "$VMID" "$REMOTE_TGZ" "$CT_TGZ"
|
||||
rm -f "$REMOTE_TGZ"
|
||||
|
||||
pct exec "$VMID" -- bash -lc "mkdir -p '$APP_DIR' && rm -rf '$APP_DIR'/* && tar xzf '$CT_TGZ' -C '$APP_DIR' && rm -f '$CT_TGZ'"
|
||||
|
||||
pct push "$VMID" "$REMOTE_NGINX" "$SITE_FILE"
|
||||
rm -f "$REMOTE_NGINX"
|
||||
|
||||
pct exec "$VMID" -- bash -lc "ln -sf '$SITE_FILE' /etc/nginx/sites-enabled/info-defi-oracle"
|
||||
pct exec "$VMID" -- nginx -t
|
||||
pct exec "$VMID" -- systemctl reload nginx
|
||||
sleep 1
|
||||
pct exec "$VMID" -- curl -fsS -H 'Host: info.defi-oracle.io' http://127.0.0.1/health >/dev/null
|
||||
pct exec "$VMID" -- bash -lc "curl -fsS -H 'Host: info.defi-oracle.io' -o /tmp/info-ta-networks.json 'http://127.0.0.1/token-aggregation/api/v1/networks?refresh=1' && grep -q '\"networks\"' /tmp/info-ta-networks.json && rm -f /tmp/info-ta-networks.json"
|
||||
REMOTE
|
||||
|
||||
echo "Done. Target: VMID ${VMID} (${IP_INFO_DEFI_ORACLE_WEB:-LAN IP from provision script}). Next steps:"
|
||||
echo " 1. NPMplus: point info.defi-oracle.io → http://${IP_INFO_DEFI_ORACLE_WEB:-192.168.11.218}:80 (scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh)"
|
||||
echo " 2. Optional tunnel/DNS: scripts/cloudflare/set-info-defi-oracle-dns-to-vmid2400-tunnel.sh"
|
||||
echo " 3. pnpm run verify:info-defi-oracle-public"
|
||||
3
scripts/deployment/sync-info-defi-oracle-web.sh
Executable file
3
scripts/deployment/sync-info-defi-oracle-web.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
# Alias for discoverability — syncs the info.defi-oracle.io SPA to the dedicated web LXC (default VMID 2410).
|
||||
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/sync-info-defi-oracle-to-vmid2400.sh" "$@"
|
||||
79
scripts/deployment/sync-omdnl-org-static-to-ct.sh
Executable file
79
scripts/deployment/sync-omdnl-org-static-to-ct.sh
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
# Sync sites/omdnl-org/public into the dedicated nginx LXC (default VMID 10203).
|
||||
#
|
||||
# Provision once: scripts/deployment/provision-omdnl-org-web-lxc.sh
|
||||
#
|
||||
# Usage: bash scripts/deployment/sync-omdnl-org-static-to-ct.sh [--dry-run]
|
||||
# Env: PROXMOX_HOST, OMDNL_ORG_WEB_VMID, IP_OMDNL_ORG_WEB (from config/ip-addresses.conf)
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
# shellcheck source=/dev/null
|
||||
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
|
||||
|
||||
PROXMOX_HOST="${PROXMOX_HOST:-${PROXMOX_HOST_R630_01:-192.168.11.11}}"
|
||||
VMID="${OMDNL_ORG_WEB_VMID:-10203}"
|
||||
APP_DIR="${OMDNL_ORG_WEB_ROOT:-/var/www/omdnl.org/html}"
|
||||
SITE_FILE="${OMDNL_ORG_NGINX_SITE:-/etc/nginx/sites-available/omdnl-org}"
|
||||
NGINX_SRC="${PROJECT_ROOT}/config/nginx/omdnl-org.site.conf"
|
||||
STATIC_SRC="${PROJECT_ROOT}/sites/omdnl-org/public"
|
||||
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
|
||||
DRY_RUN=false
|
||||
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
|
||||
|
||||
if [[ ! -d "$STATIC_SRC" ]]; then
|
||||
echo "ERROR: Missing static directory $STATIC_SRC" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "$NGINX_SRC" ]]; then
|
||||
echo "ERROR: Missing $NGINX_SRC" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TMP_TGZ="${TMPDIR:-/tmp}/omdnl-org-sync-$$.tgz"
|
||||
REMOTE_TGZ="/tmp/omdnl-org-sync-$$.tgz"
|
||||
REMOTE_NGINX="/tmp/omdnl-org-site-$$.conf"
|
||||
CT_TGZ="/tmp/omdnl-org-sync.tgz"
|
||||
cleanup() { rm -f "$TMP_TGZ"; }
|
||||
trap cleanup EXIT
|
||||
|
||||
if $DRY_RUN; then
|
||||
echo "[DRY-RUN] tar $STATIC_SRC -> push VMID $VMID $APP_DIR"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Packaging static files..."
|
||||
tar czf "$TMP_TGZ" -C "$STATIC_SRC" .
|
||||
|
||||
echo "Copying to Proxmox host ${PROXMOX_HOST}..."
|
||||
scp $SSH_OPTS "$TMP_TGZ" "root@${PROXMOX_HOST}:${REMOTE_TGZ}"
|
||||
scp $SSH_OPTS "$NGINX_SRC" "root@${PROXMOX_HOST}:${REMOTE_NGINX}"
|
||||
|
||||
echo "Installing into VMID ${VMID}..."
|
||||
ssh $SSH_OPTS "root@${PROXMOX_HOST}" bash -s "$VMID" "$REMOTE_TGZ" "$CT_TGZ" "$APP_DIR" "$SITE_FILE" "$REMOTE_NGINX" <<'REMOTE'
|
||||
set -euo pipefail
|
||||
VMID="$1"
|
||||
REMOTE_TGZ="$2"
|
||||
CT_TGZ="$3"
|
||||
APP_DIR="$4"
|
||||
SITE_FILE="$5"
|
||||
REMOTE_NGINX="$6"
|
||||
|
||||
pct push "$VMID" "$REMOTE_TGZ" "$CT_TGZ"
|
||||
rm -f "$REMOTE_TGZ"
|
||||
|
||||
pct exec "$VMID" -- bash -lc "mkdir -p '$APP_DIR' && rm -rf '$APP_DIR'/* && tar xzf '$CT_TGZ' -C '$APP_DIR' && rm -f '$CT_TGZ'"
|
||||
|
||||
pct push "$VMID" "$REMOTE_NGINX" "$SITE_FILE"
|
||||
rm -f "$REMOTE_NGINX"
|
||||
|
||||
pct exec "$VMID" -- bash -lc "ln -sf '$SITE_FILE' /etc/nginx/sites-enabled/omdnl-org"
|
||||
pct exec "$VMID" -- nginx -t
|
||||
pct exec "$VMID" -- systemctl reload nginx
|
||||
sleep 1
|
||||
pct exec "$VMID" -- curl -fsS -H 'Host: omdnl.org' http://127.0.0.1/health >/dev/null
|
||||
REMOTE
|
||||
|
||||
echo "Done. LAN: http://${IP_OMDNL_ORG_WEB:-192.168.11.222}/ (Host: omdnl.org)"
|
||||
echo "Next: Cloudflare DNS + NPM — see scripts/cloudflare/configure-omdnl-org-dns.sh and upsert-omdnl-org-proxy-host.sh"
|
||||
24
scripts/deployment/test-one-usdt-flash-chain138.sh
Executable file
24
scripts/deployment/test-one-usdt-flash-chain138.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run TestOneUSDTFlash forge script (broadcast): minimal borrower + 1 USDT flash against FLASH_VAULT.
|
||||
#
|
||||
# Env: PRIVATE_KEY, FLASH_VAULT, RPC_URL_138
|
||||
# Optional: FLASH_VAULT_TOKEN, FLASH_TEST_AMOUNT (raw units)
|
||||
#
|
||||
# Usage:
|
||||
# source scripts/lib/load-project-env.sh
|
||||
# FLASH_VAULT=0x... ./scripts/deployment/test-one-usdt-flash-chain138.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
|
||||
RPC="${RPC_URL_138:?Set RPC_URL_138}"
|
||||
: "${PRIVATE_KEY:?Set PRIVATE_KEY}"
|
||||
: "${FLASH_VAULT:?Set FLASH_VAULT (deployed SimpleERC3156FlashVault)}"
|
||||
|
||||
cd "$PROJECT_ROOT/smom-dbis-138"
|
||||
exec forge script script/flash/TestOneUSDTFlash.s.sol:TestOneUSDTFlash --rpc-url "$RPC" --broadcast -vvvv
|
||||
13
scripts/deployment/test-scaled-flash-chain138.sh
Executable file
13
scripts/deployment/test-scaled-flash-chain138.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
# Sequential 1 / 10 / 100 / 1000 USDT flashes (6 dp) against FLASH_VAULT.
|
||||
#
|
||||
# Env: RPC_URL_138, PRIVATE_KEY, FLASH_VAULT
|
||||
#
|
||||
# shellcheck disable=SC1091
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
||||
: "${RPC_URL_138:?}" "${PRIVATE_KEY:?}" "${FLASH_VAULT:?}"
|
||||
cd "$PROJECT_ROOT/smom-dbis-138"
|
||||
exec forge script script/flash/TestScaledFlash.s.sol:TestScaledFlash --rpc-url "$RPC_URL_138" --broadcast -vvvv
|
||||
62
scripts/deployment/unpause-mainnet-weth-relay-lane.sh
Normal file
62
scripts/deployment/unpause-mainnet-weth-relay-lane.sh
Normal file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env bash
|
||||
# Default: plan-only. Use --apply only after bridge inventory is restored.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
APPLY=0
|
||||
MIN_BRIDGE_WEI="${MIN_BRIDGE_WETH_WEI:-10000000000000000}"
|
||||
PROFILE_ENV="${RELAY_PROFILE_ENV_MAINNET_WETH:-${PROJECT_ROOT}/smom-dbis-138/services/relay/.env.mainnet-weth}"
|
||||
SERVICE_NAME="${MAINNET_WETH_RELAY_SERVICE:-ccip-relay}"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--apply) APPLY=1; shift ;;
|
||||
--dry-run) APPLY=0; shift ;;
|
||||
--min-bridge-weth-wei) MIN_BRIDGE_WEI="${2:-}"; shift 2 ;;
|
||||
*)
|
||||
echo "Usage: $0 [--apply|--dry-run] [--min-bridge-weth-wei WEI]" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -f "$PROFILE_ENV" ]] || { echo "Profile env not found: $PROFILE_ENV" >&2; exit 1; }
|
||||
|
||||
if [[ "$APPLY" != "1" ]]; then
|
||||
echo "Dry run only. This will NOT modify ${PROFILE_ENV}."
|
||||
echo "Planned changes:"
|
||||
echo " - set RELAY_SHEDDING=0"
|
||||
echo " - ensure RELAY_DELIVERY_ENABLED=1"
|
||||
echo " - restart service: $SERVICE_NAME"
|
||||
echo
|
||||
echo "Before apply, verify readiness:"
|
||||
echo " bash scripts/verify/check-mainnet-weth-relay-lane.sh"
|
||||
echo " bash scripts/bridge/fund-mainnet-relay-bridge.sh --target-bridge-balance-wei $MIN_BRIDGE_WEI --dry-run"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
tmp_file="$(mktemp)"
|
||||
trap 'rm -f "$tmp_file"' EXIT
|
||||
cp "$PROFILE_ENV" "$tmp_file"
|
||||
|
||||
if grep -q '^RELAY_SHEDDING=' "$tmp_file"; then
|
||||
sed -i 's/^RELAY_SHEDDING=.*/RELAY_SHEDDING=0/' "$tmp_file"
|
||||
else
|
||||
printf '\nRELAY_SHEDDING=0\n' >> "$tmp_file"
|
||||
fi
|
||||
|
||||
if grep -q '^RELAY_DELIVERY_ENABLED=' "$tmp_file"; then
|
||||
sed -i 's/^RELAY_DELIVERY_ENABLED=.*/RELAY_DELIVERY_ENABLED=1/' "$tmp_file"
|
||||
else
|
||||
printf 'RELAY_DELIVERY_ENABLED=1\n' >> "$tmp_file"
|
||||
fi
|
||||
|
||||
cp "$tmp_file" "$PROFILE_ENV"
|
||||
|
||||
echo "Updated $PROFILE_ENV"
|
||||
echo "Next step on the relay host:"
|
||||
echo " sudo systemctl restart $SERVICE_NAME"
|
||||
echo "Then verify:"
|
||||
echo " bash scripts/verify/check-mainnet-weth-relay-lane.sh"
|
||||
Reference in New Issue
Block a user