Add optional Cosmos/Engine-X/act-runner templates, CWUSDC/EI-matrix tooling, non-EVM route planner in multi-chain-execution (tests passing), token list and extraction updates, and documentation (MetaMask matrix, GRU/CWUSDC packets). Ignore institutional evidence tarballs/sha256 under reports/status. Validated with: bash scripts/verify/run-all-validation.sh --skip-genesis Co-authored-by: Cursor <cursoragent@cursor.com>
170 lines
8.2 KiB
Bash
Executable File
170 lines
8.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
|
|
|
# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh
|
|
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh"
|
|
# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/mev-protection.sh
|
|
source "${PROJECT_ROOT}/scripts/lib/mev-protection.sh"
|
|
|
|
: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}"
|
|
|
|
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
|
USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}"
|
|
PAIR="${MAINNET_CWUSDC_USDC_UNIV2_PAIR:-0xC28706F899266b36BC43cc072b3a921BDf2C48D9}"
|
|
ROUTER="${CHAIN_1_UNISWAP_V2_ROUTER:-0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D}"
|
|
TARGET_USDC_OUT_RAW="${TARGET_USDC_OUT_RAW:-10000}"
|
|
SLIPPAGE_BPS="${SLIPPAGE_BPS:-100}"
|
|
DEADLINE_SECONDS="${DEADLINE_SECONDS:-900}"
|
|
EXECUTE="${EXECUTE:-0}"
|
|
STAMP="${ENGINE_X_UNIV2_LOOP_STAMP:-$(date -u +%Y%m%dT%H%M%SZ)}"
|
|
OUT_JSON="${OUT_JSON:-reports/status/engine-x-univ2-public-indexed-loop-${STAMP}.json}"
|
|
LATEST_JSON="${LATEST_JSON:-reports/status/engine-x-univ2-public-indexed-loop-latest.json}"
|
|
|
|
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
|
SIGNER="$(cast wallet address --private-key "${PRIVATE_KEY}")"
|
|
else
|
|
SIGNER="${DEPLOYER_ADDRESS:-}"
|
|
fi
|
|
if [[ -z "${SIGNER}" ]]; then
|
|
echo "Set PRIVATE_KEY or DEPLOYER_ADDRESS" >&2
|
|
exit 1
|
|
fi
|
|
if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then
|
|
echo "PRIVATE_KEY is required when EXECUTE=1" >&2
|
|
exit 1
|
|
fi
|
|
|
|
TOKEN0="$(cast call "${PAIR}" 'token0()(address)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)"
|
|
TOKEN1="$(cast call "${PAIR}" 'token1()(address)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)"
|
|
if [[ "${TOKEN0,,}" != "${CWUSDC,,}" || "${TOKEN1,,}" != "${USDC,,}" ]]; then
|
|
echo "Configured pair is not token0=cWUSDC/token1=USDC: ${PAIR}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
RESERVES_BEFORE="$(cast call "${PAIR}" 'getReserves()(uint112,uint112,uint32)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | tr '\n' ' ')"
|
|
CW_BAL_BEFORE="$(cast call "${CWUSDC}" 'balanceOf(address)(uint256)' "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
|
USDC_BAL_BEFORE="$(cast call "${USDC}" 'balanceOf(address)(uint256)' "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
|
CW_ALLOW_BEFORE="$(cast call "${CWUSDC}" 'allowance(address,address)(uint256)' "${SIGNER}" "${ROUTER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
|
USDC_ALLOW_BEFORE="$(cast call "${USDC}" 'allowance(address,address)(uint256)' "${SIGNER}" "${ROUTER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
|
ETH_BEFORE="$(cast balance "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}")"
|
|
GAS_PRICE_WEI="$(cast gas-price --rpc-url "${ETHEREUM_MAINNET_RPC}")"
|
|
BLOCK_BEFORE="$(cast block-number --rpc-url "${ETHEREUM_MAINNET_RPC}")"
|
|
|
|
CW_IN_RAW="$(cast call --json "${ROUTER}" 'getAmountsIn(uint256,address[])(uint256[])' "${TARGET_USDC_OUT_RAW}" "[${CWUSDC},${USDC}]" --rpc-url "${ETHEREUM_MAINNET_RPC}" | jq -r '.[0][0]')"
|
|
ROUNDTRIP_CW_OUT_RAW="$(cast call --json "${ROUTER}" 'getAmountsOut(uint256,address[])(uint256[])' "${TARGET_USDC_OUT_RAW}" "[${USDC},${CWUSDC}]" --rpc-url "${ETHEREUM_MAINNET_RPC}" | jq -r '.[0][-1]')"
|
|
MAX_CW_IN_RAW="$(( CW_IN_RAW * (10000 + SLIPPAGE_BPS) / 10000 + 1 ))"
|
|
MIN_CW_BACK_RAW="$(( ROUNDTRIP_CW_OUT_RAW * (10000 - SLIPPAGE_BPS) / 10000 ))"
|
|
|
|
if (( CW_BAL_BEFORE < MAX_CW_IN_RAW )); then
|
|
echo "Insufficient cWUSDC: need ${MAX_CW_IN_RAW}, have ${CW_BAL_BEFORE}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
cat <<EOF
|
|
Engine X UniV2 public indexed loop plan
|
|
mode: ${EXECUTE}
|
|
pair: ${PAIR}
|
|
router: ${ROUTER}
|
|
signer: ${SIGNER}
|
|
target USDC out raw: ${TARGET_USDC_OUT_RAW}
|
|
cWUSDC max input raw: ${MAX_CW_IN_RAW}
|
|
expected cWUSDC input raw: ${CW_IN_RAW}
|
|
expected cWUSDC back raw: ${ROUNDTRIP_CW_OUT_RAW}
|
|
min cWUSDC back raw: ${MIN_CW_BACK_RAW}
|
|
reserves before: ${RESERVES_BEFORE}
|
|
EOF
|
|
|
|
mkdir -p "$(dirname "${OUT_JSON}")"
|
|
python3 - "${OUT_JSON}" "${LATEST_JSON}" \
|
|
"${EXECUTE}" "${STAMP}" "${BLOCK_BEFORE}" "${GAS_PRICE_WEI}" "${SIGNER}" "${PAIR}" "${ROUTER}" \
|
|
"${CWUSDC}" "${USDC}" "${TARGET_USDC_OUT_RAW}" "${CW_IN_RAW}" "${MAX_CW_IN_RAW}" \
|
|
"${ROUNDTRIP_CW_OUT_RAW}" "${MIN_CW_BACK_RAW}" "${RESERVES_BEFORE}" "${CW_BAL_BEFORE}" "${USDC_BAL_BEFORE}" \
|
|
"${CW_ALLOW_BEFORE}" "${USDC_ALLOW_BEFORE}" "${ETH_BEFORE}" <<'PY'
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
(
|
|
out_json, latest_json, execute, stamp, block, gas_price, signer, pair, router, cw, usdc,
|
|
target, cw_in, max_cw_in, cw_back, min_cw_back, reserves, cw_bal, usdc_bal, cw_allow,
|
|
usdc_allow, eth,
|
|
) = sys.argv[1:]
|
|
payload = {
|
|
"schema": "engine-x-univ2-public-indexed-loop/v1",
|
|
"executed": execute == "1",
|
|
"stamp": stamp,
|
|
"blockBefore": block,
|
|
"gasPriceWei": gas_price,
|
|
"signer": signer,
|
|
"pair": pair,
|
|
"router": router,
|
|
"tokens": {"cwusdc": cw, "usdc": usdc},
|
|
"targetUsdcOutRaw": target,
|
|
"expectedCwusdcInputRaw": cw_in,
|
|
"maxCwusdcInputRaw": max_cw_in,
|
|
"expectedCwusdcBackRaw": cw_back,
|
|
"minCwusdcBackRaw": min_cw_back,
|
|
"reservesBefore": reserves,
|
|
"balancesBefore": {"ethWei": eth, "cwusdcRaw": cw_bal, "usdcRaw": usdc_bal},
|
|
"allowancesBefore": {"cwusdcRaw": cw_allow, "usdcRaw": usdc_allow},
|
|
"transactions": {},
|
|
}
|
|
Path(out_json).write_text(json.dumps(payload, indent=2) + "\n")
|
|
Path(latest_json).write_text(json.dumps(payload, indent=2) + "\n")
|
|
PY
|
|
|
|
if [[ "${EXECUTE}" != "1" ]]; then
|
|
cat <<EOF
|
|
|
|
Dry-run only. To broadcast this exact public indexed loop:
|
|
EXECUTE=1 TARGET_USDC_OUT_RAW=${TARGET_USDC_OUT_RAW} SLIPPAGE_BPS=${SLIPPAGE_BPS} \\
|
|
bash scripts/deployment/run-engine-x-univ2-public-indexed-loop.sh
|
|
EOF
|
|
exit 0
|
|
fi
|
|
|
|
mev_require_private_for_action "engine-x-univ2-public-indexed-loop"
|
|
|
|
DEADLINE="$(( $(date +%s) + DEADLINE_SECONDS ))"
|
|
CW_APPROVE_TX=""
|
|
USDC_APPROVE_TX=""
|
|
FORWARD_TX=""
|
|
REVERSE_TX=""
|
|
|
|
if (( CW_ALLOW_BEFORE < MAX_CW_IN_RAW )); then
|
|
CW_APPROVE_TX="$(mev_cast_send "${CWUSDC}" 'approve(address,uint256)(bool)' "${ROUTER}" "${MAX_CW_IN_RAW}" --json | jq -r '.transactionHash')"
|
|
fi
|
|
FORWARD_TX="$(mev_cast_send "${ROUTER}" 'swapTokensForExactTokens(uint256,uint256,address[],address,uint256)' "${TARGET_USDC_OUT_RAW}" "${MAX_CW_IN_RAW}" "[${CWUSDC},${USDC}]" "${SIGNER}" "${DEADLINE}" --json | jq -r '.transactionHash')"
|
|
|
|
USDC_ALLOW_AFTER_FORWARD="$(cast call "${USDC}" 'allowance(address,address)(uint256)' "${SIGNER}" "${ROUTER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
|
if (( USDC_ALLOW_AFTER_FORWARD < TARGET_USDC_OUT_RAW )); then
|
|
USDC_APPROVE_TX="$(mev_cast_send "${USDC}" 'approve(address,uint256)(bool)' "${ROUTER}" "${TARGET_USDC_OUT_RAW}" --json | jq -r '.transactionHash')"
|
|
fi
|
|
REVERSE_TX="$(mev_cast_send "${ROUTER}" 'swapExactTokensForTokens(uint256,uint256,address[],address,uint256)' "${TARGET_USDC_OUT_RAW}" "${MIN_CW_BACK_RAW}" "[${USDC},${CWUSDC}]" "${SIGNER}" "${DEADLINE}" --json | jq -r '.transactionHash')"
|
|
|
|
RESERVES_AFTER="$(cast call "${PAIR}" 'getReserves()(uint112,uint112,uint32)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | tr '\n' ' ')"
|
|
CW_BAL_AFTER="$(cast call "${CWUSDC}" 'balanceOf(address)(uint256)' "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
|
USDC_BAL_AFTER="$(cast call "${USDC}" 'balanceOf(address)(uint256)' "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
|
ETH_AFTER="$(cast balance "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}")"
|
|
python3 - "${OUT_JSON}" "${LATEST_JSON}" "${CW_APPROVE_TX}" "${FORWARD_TX}" "${USDC_APPROVE_TX}" "${REVERSE_TX}" "${RESERVES_AFTER}" "${CW_BAL_AFTER}" "${USDC_BAL_AFTER}" "${ETH_AFTER}" <<'PY'
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
out_json, latest_json, cw_approve, forward, usdc_approve, reverse, reserves, cw_bal, usdc_bal, eth = sys.argv[1:]
|
|
payload = json.loads(Path(out_json).read_text())
|
|
payload["executed"] = True
|
|
payload["transactions"] = {
|
|
"cwusdcApprove": cw_approve or None,
|
|
"forwardCwusdcToUsdc": forward,
|
|
"usdcApprove": usdc_approve or None,
|
|
"reverseUsdcToCwusdc": reverse,
|
|
}
|
|
payload["reservesAfter"] = reserves
|
|
payload["balancesAfter"] = {"ethWei": eth, "cwusdcRaw": cw_bal, "usdcRaw": usdc_bal}
|
|
Path(out_json).write_text(json.dumps(payload, indent=2) + "\n")
|
|
Path(latest_json).write_text(json.dumps(payload, indent=2) + "\n")
|
|
print(json.dumps(payload["transactions"], indent=2))
|
|
PY
|