Enhance .env configuration with Infura support and add new RPC endpoints for various networks. Update package.json with new deployment scripts for Engine X. Improve public LP compliance documentation in runbooks and scripts, including guidance for public pair repairs and funding strategies.
All checks were successful
Deploy to Phoenix / validate (push) Successful in 1m11s
Deploy to Phoenix / deploy (push) Successful in 43s
Deploy to Phoenix / deploy-atomic-swap-dapp (push) Successful in 1m32s
phoenix-deploy Deployed to cloudflare-sync
Deploy to Phoenix / cloudflare (push) Successful in 38s
All checks were successful
Deploy to Phoenix / validate (push) Successful in 1m11s
Deploy to Phoenix / deploy (push) Successful in 43s
Deploy to Phoenix / deploy-atomic-swap-dapp (push) Successful in 1m32s
phoenix-deploy Deployed to cloudflare-sync
Deploy to Phoenix / cloudflare (push) Successful in 38s
This commit is contained in:
@@ -40,26 +40,26 @@ DEFAULT_ENV = ROOT / "smom-dbis-138" / ".env"
|
||||
|
||||
ZERO = "0x0000000000000000000000000000000000000000"
|
||||
|
||||
# Defaults when .env has no RPC for a chain (prefer setting INFURA_PROJECT_ID + load-project-env, or per-chain *_RPC in .env).
|
||||
DEFAULT_RPC: dict[str, str] = {
|
||||
"1": "https://eth.llamarpc.com",
|
||||
"1": "https://ethereum.publicnode.com",
|
||||
"10": "https://mainnet.optimism.io",
|
||||
"25": "https://evm.cronos.org",
|
||||
"56": "https://bsc-dataseed.binance.org",
|
||||
"100": "https://rpc.gnosischain.com",
|
||||
"137": "https://polygon-rpc.com",
|
||||
"8453": "https://mainnet.base.org",
|
||||
"42161": "https://arbitrum-one.publicnode.com",
|
||||
"42161": "https://arb1.arbitrum.io/rpc",
|
||||
"42220": "https://forno.celo.org",
|
||||
"43114": "https://avalanche-c-chain.publicnode.com",
|
||||
"43114": "https://api.avax.network/ext/bc/C/rpc",
|
||||
"1111": "https://api.wemix.com",
|
||||
}
|
||||
|
||||
# Extra public RPCs (retry when primary fails — connection resets, rate limits).
|
||||
RPC_FALLBACKS: dict[str, list[str]] = {
|
||||
"1": [
|
||||
"https://ethereum.publicnode.com",
|
||||
"https://eth.llamarpc.com",
|
||||
"https://1rpc.io/eth",
|
||||
"https://rpc.ankr.com/eth",
|
||||
],
|
||||
"137": ["https://polygon-bor.publicnode.com", "https://1rpc.io/matic"],
|
||||
"42161": ["https://arbitrum.llamarpc.com"],
|
||||
@@ -85,6 +85,58 @@ RPC_KEYS: dict[str, list[str]] = {
|
||||
}
|
||||
|
||||
|
||||
def _rpc_env_key_set() -> set[str]:
|
||||
return {k for ks in RPC_KEYS.values() for k in ks} | {
|
||||
"INFURA_PROJECT_ID",
|
||||
"INFURA_API_KEY",
|
||||
"RPC_URL_MAINNET",
|
||||
}
|
||||
|
||||
|
||||
def merge_rpc_os_into(env: dict[str, str]) -> None:
|
||||
"""Process env overrides .env for RPC-related keys (so `source load-project-env.sh` applies)."""
|
||||
for k in _rpc_env_key_set():
|
||||
v = os.environ.get(k, "").strip()
|
||||
if v:
|
||||
env[k] = v
|
||||
|
||||
|
||||
def apply_eth_mainnet_rpc_alias(env: dict[str, str]) -> None:
|
||||
"""Match load-project-env.sh: dotenv often sets ETH_MAINNET_RPC_URL only."""
|
||||
if not resolve(env, "ETHEREUM_MAINNET_RPC"):
|
||||
alt = resolve(env, "ETH_MAINNET_RPC_URL", "")
|
||||
if alt:
|
||||
env["ETHEREUM_MAINNET_RPC"] = alt
|
||||
|
||||
|
||||
def apply_infura_rpc_defaults(env: dict[str, str]) -> None:
|
||||
"""When INFURA_PROJECT_ID or INFURA_API_KEY is set, fill first unset RPC var per supported chain."""
|
||||
pid = (env.get("INFURA_PROJECT_ID") or env.get("INFURA_API_KEY") or "").strip()
|
||||
if not pid:
|
||||
return
|
||||
|
||||
def chain_has_rpc(cid: str) -> bool:
|
||||
return any(resolve(env, k) for k in RPC_KEYS.get(cid, []))
|
||||
|
||||
infura_by_chain: dict[str, str] = {
|
||||
"1": f"https://mainnet.infura.io/v3/{pid}",
|
||||
"10": f"https://optimism-mainnet.infura.io/v3/{pid}",
|
||||
"56": f"https://bnb-mainnet.infura.io/v3/{pid}",
|
||||
"100": f"https://gnosis-mainnet.infura.io/v3/{pid}",
|
||||
"137": f"https://polygon-mainnet.infura.io/v3/{pid}",
|
||||
"8453": f"https://base-mainnet.infura.io/v3/{pid}",
|
||||
"42161": f"https://arbitrum-mainnet.infura.io/v3/{pid}",
|
||||
"42220": f"https://celo-mainnet.infura.io/v3/{pid}",
|
||||
"43114": f"https://avalanche-mainnet.infura.io/v3/{pid}",
|
||||
}
|
||||
for cid, url in infura_by_chain.items():
|
||||
if chain_has_rpc(cid):
|
||||
continue
|
||||
keys = RPC_KEYS.get(cid, [])
|
||||
if keys:
|
||||
env[keys[0]] = url
|
||||
|
||||
|
||||
def load_dotenv(path: Path) -> dict[str, str]:
|
||||
out: dict[str, str] = {}
|
||||
if not path.is_file():
|
||||
@@ -388,6 +440,9 @@ def main() -> int:
|
||||
args = ap.parse_args()
|
||||
|
||||
env = load_dotenv(args.env)
|
||||
merge_rpc_os_into(env)
|
||||
apply_eth_mainnet_rpc_alias(env)
|
||||
apply_infura_rpc_defaults(env)
|
||||
dep = deployer_address(env, args.deployer)
|
||||
if not dep:
|
||||
print("No deployer: set PRIVATE_KEY or DEPLOYER_ADDRESS in .env or pass --deployer", file=sys.stderr)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Resume mainnet cWUSDC EI matrix mints from ei-matrix-cwusdc-mint-last-idx.txt + 1.
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec "$SCRIPT_DIR/mint-cwusdc-ei-matrix-wallets.sh" --resume-next "$@"
|
||||
89
scripts/deployment/deploy-engine-x-indexed-liquidity-vault-mainnet.sh
Executable file
89
scripts/deployment/deploy-engine-x-indexed-liquidity-vault-mainnet.sh
Executable file
@@ -0,0 +1,89 @@
|
||||
#!/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"
|
||||
|
||||
: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}"
|
||||
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}"
|
||||
FACTORY="${CHAIN_1_UNISWAP_V3_FACTORY:-0x1F98431c8aD98523631AE4a59f267346ea31F984}"
|
||||
FEE="${ENGINE_X_UNIV3_FEE:-100}"
|
||||
MAX_ABS_TICK="${ENGINE_X_INDEXED_MAX_ABS_TICK:-100}"
|
||||
MIN_LIQUIDITY="${ENGINE_X_INDEXED_MIN_LIQUIDITY:-1}"
|
||||
MAX_PROOF_SWAP_AMOUNT="${ENGINE_X_INDEXED_MAX_PROOF_SWAP_AMOUNT:-1000000}"
|
||||
VERIFY="${VERIFY:-1}"
|
||||
EXECUTE="${EXECUTE:-0}"
|
||||
|
||||
OWNER="${ENGINE_X_INDEXED_OWNER:-${DEPLOYER_ADDRESS:-}}"
|
||||
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
||||
OWNER="$(cast wallet address --private-key "${PRIVATE_KEY}")"
|
||||
fi
|
||||
if [[ -z "${OWNER}" ]]; then
|
||||
echo "Set PRIVATE_KEY, DEPLOYER_ADDRESS, or ENGINE_X_INDEXED_OWNER" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then
|
||||
echo "PRIVATE_KEY is required when EXECUTE=1" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TOKEN0="$(printf '%s\n%s\n' "${CWUSDC}" "${USDC}" | tr '[:upper:]' '[:lower:]' | sort | sed -n '1p')"
|
||||
TOKEN1="$(printf '%s\n%s\n' "${CWUSDC}" "${USDC}" | tr '[:upper:]' '[:lower:]' | sort | sed -n '2p')"
|
||||
POOL="${ENGINE_X_UNIV3_POOL:-$(cast call "${FACTORY}" 'getPool(address,address,uint24)(address)' "${TOKEN0}" "${TOKEN1}" "${FEE}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)}"
|
||||
|
||||
if [[ "${POOL}" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
cat <<EOF
|
||||
Engine X indexed-liquidity vault deployment blocked
|
||||
UniV3 cWUSDC/USDC pool is not deployed for fee ${FEE}.
|
||||
Create/fund the public pool first, then rerun this script.
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
VERIFY_ARGS=()
|
||||
if [[ "${VERIFY}" == "1" ]]; then
|
||||
VERIFY_ARGS+=(--verify)
|
||||
fi
|
||||
|
||||
CREATE_CMD_EXEC=(
|
||||
forge create
|
||||
--broadcast
|
||||
--rpc-url "${ETHEREUM_MAINNET_RPC}"
|
||||
--private-key "${PRIVATE_KEY:-}"
|
||||
"${VERIFY_ARGS[@]}"
|
||||
contracts/flash/DBISEngineXIndexedLiquidityVault.sol:DBISEngineXIndexedLiquidityVault
|
||||
--constructor-args
|
||||
"${CWUSDC}" "${USDC}" "${POOL}" "${OWNER}" "${MAX_ABS_TICK}" "${MIN_LIQUIDITY}" "${MAX_PROOF_SWAP_AMOUNT}"
|
||||
)
|
||||
|
||||
cat <<EOF
|
||||
Engine X indexed-liquidity vault deployment plan
|
||||
mode: ${EXECUTE}
|
||||
owner: ${OWNER}
|
||||
cWUSDC: ${CWUSDC}
|
||||
USDC: ${USDC}
|
||||
UniV3 pool: ${POOL}
|
||||
fee: ${FEE}
|
||||
max abs tick: ${MAX_ABS_TICK}
|
||||
min liquidity: ${MIN_LIQUIDITY}
|
||||
max proof swap amount raw: ${MAX_PROOF_SWAP_AMOUNT}
|
||||
EOF
|
||||
|
||||
if [[ "${EXECUTE}" != "1" ]]; then
|
||||
cat <<EOF
|
||||
|
||||
Dry-run only. Review command:
|
||||
cd smom-dbis-138
|
||||
forge create --broadcast --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY" ${VERIFY_ARGS[*]} contracts/flash/DBISEngineXIndexedLiquidityVault.sol:DBISEngineXIndexedLiquidityVault --constructor-args "${CWUSDC}" "${USDC}" "${POOL}" "${OWNER}" "${MAX_ABS_TICK}" "${MIN_LIQUIDITY}" "${MAX_PROOF_SWAP_AMOUNT}"
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
pushd "${PROJECT_ROOT}/smom-dbis-138" >/dev/null
|
||||
"${CREATE_CMD_EXEC[@]}"
|
||||
popd >/dev/null
|
||||
118
scripts/deployment/deploy-engine-x-v2-mainnet.sh
Executable file
118
scripts/deployment/deploy-engine-x-v2-mainnet.sh
Executable file
@@ -0,0 +1,118 @@
|
||||
#!/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"
|
||||
|
||||
: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}"
|
||||
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}"
|
||||
XAUT="${XAUT_MAINNET:-0x68749665FF8D2d112Fa859AA293F07A622782F38}"
|
||||
XAUT_USD_PRICE6="${ENGINE_X_XAUT_USD_PRICE6:-3226640000}"
|
||||
LTV_BPS="${ENGINE_X_LTV_BPS:-8000}"
|
||||
MAX_ROUND_TRIP_LOSS_BPS="${ENGINE_X_MAX_ROUND_TRIP_LOSS_BPS:-100}"
|
||||
POOL_CWUSDC_RAW="${ENGINE_X_POOL_CWUSDC_RAW:-85763529}"
|
||||
POOL_USDC_RAW="${ENGINE_X_POOL_USDC_RAW:-85763529}"
|
||||
LENDER_USDC_RAW="${ENGINE_X_LENDER_USDC_RAW:-5000000}"
|
||||
FLASH_FEE_BPS="${ENGINE_X_FLASH_FEE_BPS:-5}"
|
||||
MAX_FLASH_LOAN_RAW="${ENGINE_X_MAX_FLASH_LOAN_RAW:-0}"
|
||||
SEED_AFTER_DEPLOY="${SEED_AFTER_DEPLOY:-0}"
|
||||
VERIFY="${VERIFY:-1}"
|
||||
EXECUTE="${EXECUTE:-0}"
|
||||
|
||||
OWNER="${ENGINE_X_OWNER:-${DEPLOYER_ADDRESS:-}}"
|
||||
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
||||
OWNER="$(cast wallet address --private-key "${PRIVATE_KEY}")"
|
||||
fi
|
||||
SURPLUS_RECEIVER="${ENGINE_X_SURPLUS_RECEIVER:-${OWNER}}"
|
||||
|
||||
if [[ -z "${OWNER}" || -z "${SURPLUS_RECEIVER}" ]]; then
|
||||
echo "Set PRIVATE_KEY, DEPLOYER_ADDRESS, ENGINE_X_OWNER, and/or ENGINE_X_SURPLUS_RECEIVER" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then
|
||||
echo "PRIVATE_KEY is required when EXECUTE=1" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERIFY_ARGS=()
|
||||
if [[ "${VERIFY}" == "1" ]]; then
|
||||
VERIFY_ARGS+=(--verify)
|
||||
fi
|
||||
|
||||
CREATE_CMD_EXEC=(
|
||||
forge create
|
||||
--broadcast
|
||||
--rpc-url "${ETHEREUM_MAINNET_RPC}"
|
||||
--private-key "${PRIVATE_KEY:-}"
|
||||
"${VERIFY_ARGS[@]}"
|
||||
contracts/flash/DBISEngineXVirtualBatchVault.sol:DBISEngineXVirtualBatchVault
|
||||
--constructor-args
|
||||
"${CWUSDC}" "${USDC}" "${XAUT}" "${OWNER}" "${SURPLUS_RECEIVER}"
|
||||
"${XAUT_USD_PRICE6}" "${LTV_BPS}" "${MAX_ROUND_TRIP_LOSS_BPS}"
|
||||
)
|
||||
|
||||
cat <<EOF
|
||||
Engine X v2 Mainnet deployment plan
|
||||
mode: ${EXECUTE}
|
||||
owner: ${OWNER}
|
||||
surplus receiver: ${SURPLUS_RECEIVER}
|
||||
cWUSDC: ${CWUSDC}
|
||||
USDC: ${USDC}
|
||||
XAUt: ${XAUT}
|
||||
XAUt price6: ${XAUT_USD_PRICE6}
|
||||
LTV bps: ${LTV_BPS}
|
||||
max round-trip loss bps: ${MAX_ROUND_TRIP_LOSS_BPS}
|
||||
default seed: ${POOL_CWUSDC_RAW} cWUSDC raw / ${POOL_USDC_RAW} USDC raw
|
||||
lender bucket: ${LENDER_USDC_RAW} USDC raw
|
||||
flash fee bps: ${FLASH_FEE_BPS}
|
||||
max flash loan raw: ${MAX_FLASH_LOAN_RAW}
|
||||
EOF
|
||||
|
||||
if [[ "${EXECUTE}" != "1" ]]; then
|
||||
cat <<EOF
|
||||
|
||||
Dry-run only. Review commands:
|
||||
cd smom-dbis-138
|
||||
forge create --broadcast --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY" ${VERIFY_ARGS[*]} contracts/flash/DBISEngineXVirtualBatchVault.sol:DBISEngineXVirtualBatchVault --constructor-args "${CWUSDC}" "${USDC}" "${XAUT}" "${OWNER}" "${SURPLUS_RECEIVER}" "${XAUT_USD_PRICE6}" "${LTV_BPS}" "${MAX_ROUND_TRIP_LOSS_BPS}"
|
||||
|
||||
After deploy, export the new address:
|
||||
export DBIS_ENGINE_X_V2_VAULT=<deployed-vault-address>
|
||||
|
||||
Optional seed/fund/risk controls:
|
||||
cast send "\$DBIS_ENGINE_X_V2_VAULT" 'setFlashFeeBps(uint256)' "${FLASH_FEE_BPS}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
cast send "\$DBIS_ENGINE_X_V2_VAULT" 'setMaxFlashLoanAmount(uint256)' "${MAX_FLASH_LOAN_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
cast send "${CWUSDC}" 'approve(address,uint256)' "\$DBIS_ENGINE_X_V2_VAULT" "${POOL_CWUSDC_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
cast send "${USDC}" 'approve(address,uint256)' "\$DBIS_ENGINE_X_V2_VAULT" "$((POOL_USDC_RAW + LENDER_USDC_RAW))" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
cast send "\$DBIS_ENGINE_X_V2_VAULT" 'seedPool(uint256,uint256)' "${POOL_CWUSDC_RAW}" "${POOL_USDC_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
cast send "\$DBIS_ENGINE_X_V2_VAULT" 'fundLender(uint256)' "${LENDER_USDC_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
pushd "${PROJECT_ROOT}/smom-dbis-138" >/dev/null
|
||||
DEPLOY_OUT="$("${CREATE_CMD_EXEC[@]}")"
|
||||
popd >/dev/null
|
||||
printf '%s\n' "${DEPLOY_OUT}"
|
||||
|
||||
VAULT="$(printf '%s\n' "${DEPLOY_OUT}" | grep -oE 'Deployed to: 0x[a-fA-F0-9]{40}' | awk '{print $3}' | tail -1)"
|
||||
if [[ -z "${VAULT}" ]]; then
|
||||
echo "Could not parse deployed vault address" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cast send "${VAULT}" 'setFlashFeeBps(uint256)' "${FLASH_FEE_BPS}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
cast send "${VAULT}" 'setMaxFlashLoanAmount(uint256)' "${MAX_FLASH_LOAN_RAW}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
|
||||
if [[ "${SEED_AFTER_DEPLOY}" == "1" ]]; then
|
||||
cast send "${CWUSDC}" 'approve(address,uint256)' "${VAULT}" "${POOL_CWUSDC_RAW}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
cast send "${USDC}" 'approve(address,uint256)' "${VAULT}" "$((POOL_USDC_RAW + LENDER_USDC_RAW))" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
cast send "${VAULT}" 'seedPool(uint256,uint256)' "${POOL_CWUSDC_RAW}" "${POOL_USDC_RAW}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
cast send "${VAULT}" 'fundLender(uint256)' "${LENDER_USDC_RAW}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
fi
|
||||
|
||||
echo "DBIS_ENGINE_X_V2_VAULT=${VAULT}"
|
||||
99
scripts/deployment/deploy-engine-x-xaut-usdc-borrow-vault-mainnet.sh
Executable file
99
scripts/deployment/deploy-engine-x-xaut-usdc-borrow-vault-mainnet.sh
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/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"
|
||||
|
||||
: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}"
|
||||
|
||||
XAUT="${XAUT_MAINNET:-0x68749665FF8D2d112Fa859AA293F07A622782F38}"
|
||||
USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}"
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
XAUT_USD_PRICE6="${ENGINE_X_XAUT_USD_PRICE6:-3226640000}"
|
||||
LTV_BPS="${ENGINE_X_BORROW_LTV_BPS:-7500}"
|
||||
LIQUIDATION_THRESHOLD_BPS="${ENGINE_X_BORROW_LIQUIDATION_THRESHOLD_BPS:-8000}"
|
||||
MIN_HEALTH_FACTOR_BPS="${ENGINE_X_BORROW_MIN_HEALTH_FACTOR_BPS:-11000}"
|
||||
LIQUIDATION_BONUS_BPS="${ENGINE_X_BORROW_LIQUIDATION_BONUS_BPS:-500}"
|
||||
MAX_BORROW_USDC_RAW="${ENGINE_X_BORROW_MAX_USDC_RAW:-0}"
|
||||
PRICE_SOURCE_HASH="${ENGINE_X_XAUT_PRICE_SOURCE_HASH:-$(cast keccak "dbis-engine-x:xaut-usd-price6:${XAUT_USD_PRICE6}")}"
|
||||
VERIFY="${VERIFY:-1}"
|
||||
EXECUTE="${EXECUTE:-0}"
|
||||
|
||||
OWNER="${ENGINE_X_BORROW_OWNER:-${DEPLOYER_ADDRESS:-}}"
|
||||
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
||||
OWNER="$(cast wallet address --private-key "${PRIVATE_KEY}")"
|
||||
fi
|
||||
|
||||
if [[ -z "${OWNER}" ]]; then
|
||||
echo "Set PRIVATE_KEY, DEPLOYER_ADDRESS, or ENGINE_X_BORROW_OWNER" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then
|
||||
echo "PRIVATE_KEY is required when EXECUTE=1" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERIFY_ARGS=()
|
||||
if [[ "${VERIFY}" == "1" ]]; then
|
||||
VERIFY_ARGS+=(--verify)
|
||||
fi
|
||||
|
||||
CREATE_CMD_EXEC=(
|
||||
forge create
|
||||
--broadcast
|
||||
--rpc-url "${ETHEREUM_MAINNET_RPC}"
|
||||
--private-key "${PRIVATE_KEY:-}"
|
||||
"${VERIFY_ARGS[@]}"
|
||||
contracts/flash/DBISEngineXXautUsdcBorrowVault.sol:DBISEngineXXautUsdcBorrowVault
|
||||
--constructor-args
|
||||
"${XAUT}" "${USDC}" "${CWUSDC}" "${OWNER}"
|
||||
"${XAUT_USD_PRICE6}" "${LTV_BPS}" "${LIQUIDATION_THRESHOLD_BPS}" "${MIN_HEALTH_FACTOR_BPS}"
|
||||
"${LIQUIDATION_BONUS_BPS}" "${MAX_BORROW_USDC_RAW}" "${PRICE_SOURCE_HASH}"
|
||||
)
|
||||
|
||||
cat <<EOF
|
||||
Engine X XAUt/USDC borrow vault deployment plan
|
||||
mode: ${EXECUTE}
|
||||
owner: ${OWNER}
|
||||
XAUt: ${XAUT}
|
||||
USDC: ${USDC}
|
||||
cWUSDC proof token: ${CWUSDC}
|
||||
XAUt price6: ${XAUT_USD_PRICE6}
|
||||
price source hash: ${PRICE_SOURCE_HASH}
|
||||
LTV bps: ${LTV_BPS}
|
||||
liquidation threshold bps: ${LIQUIDATION_THRESHOLD_BPS}
|
||||
min health factor bps: ${MIN_HEALTH_FACTOR_BPS}
|
||||
liquidation bonus bps: ${LIQUIDATION_BONUS_BPS}
|
||||
max borrow USDC raw: ${MAX_BORROW_USDC_RAW}
|
||||
|
||||
Boundary:
|
||||
The vault lends only pre-funded official USDC. It does not create USDC and it
|
||||
does not accept cWUSDC as debt repayment. cWUSDC-sourced repayment proofs still
|
||||
settle with actual USDC and only attach public swap/audit/peg hashes.
|
||||
EOF
|
||||
|
||||
if [[ "${EXECUTE}" != "1" ]]; then
|
||||
cat <<EOF
|
||||
|
||||
Dry-run only. Review command:
|
||||
cd smom-dbis-138
|
||||
forge create --broadcast --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY" ${VERIFY_ARGS[*]} contracts/flash/DBISEngineXXautUsdcBorrowVault.sol:DBISEngineXXautUsdcBorrowVault --constructor-args "${XAUT}" "${USDC}" "${CWUSDC}" "${OWNER}" "${XAUT_USD_PRICE6}" "${LTV_BPS}" "${LIQUIDATION_THRESHOLD_BPS}" "${MIN_HEALTH_FACTOR_BPS}" "${LIQUIDATION_BONUS_BPS}" "${MAX_BORROW_USDC_RAW}" "${PRICE_SOURCE_HASH}"
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
pushd "${PROJECT_ROOT}/smom-dbis-138" >/dev/null
|
||||
DEPLOY_OUT="$("${CREATE_CMD_EXEC[@]}")"
|
||||
popd >/dev/null
|
||||
printf '%s\n' "${DEPLOY_OUT}"
|
||||
|
||||
VAULT="$(printf '%s\n' "${DEPLOY_OUT}" | grep -oE 'Deployed to: 0x[a-fA-F0-9]{40}' | awk '{print $3}' | tail -1)"
|
||||
if [[ -z "${VAULT}" ]]; then
|
||||
echo "Could not parse deployed borrow vault address" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "DBIS_ENGINE_X_XAUT_USDC_BORROW_VAULT=${VAULT}"
|
||||
238
scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh
Executable file
238
scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh
Executable file
@@ -0,0 +1,238 @@
|
||||
#!/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"
|
||||
|
||||
: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}"
|
||||
|
||||
VAULT="${ENGINE_X_VAULT:-0x9a22a3e272A364D64240dE6bda796FcA421cA7E9}"
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}"
|
||||
FACTORY="${CHAIN_1_UNISWAP_V3_FACTORY:-0x1F98431c8aD98523631AE4a59f267346ea31F984}"
|
||||
POSITION_MANAGER="${CHAIN_1_UNISWAP_V3_POSITION_MANAGER:-0xC36442b4a4522E871399CD717aBDD847Ab11FE88}"
|
||||
FEE="${ENGINE_X_UNIV3_FEE:-100}"
|
||||
TICK_LOWER="${ENGINE_X_UNIV3_TICK_LOWER:--100}"
|
||||
TICK_UPPER="${ENGINE_X_UNIV3_TICK_UPPER:-100}"
|
||||
SQRT_PRICE_X96="${ENGINE_X_UNIV3_SQRT_PRICE_X96:-79228162514264337593543950336}"
|
||||
DEADLINE_SECONDS="${DEADLINE_SECONDS:-1800}"
|
||||
EXECUTE="${EXECUTE:-0}"
|
||||
INCLUDE_LENDER_USDC="${INCLUDE_LENDER_USDC:-0}"
|
||||
MIGRATION_AMOUNT_RAW="${MIGRATION_AMOUNT_RAW:-}"
|
||||
|
||||
OWNER="$(cast call "${VAULT}" 'owner()(address)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)"
|
||||
SIGNER="${DEPLOYER_ADDRESS:-${OWNER}}"
|
||||
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
||||
SIGNER="$(cast wallet address --private-key "${PRIVATE_KEY}")"
|
||||
fi
|
||||
|
||||
if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then
|
||||
echo "PRIVATE_KEY is required when EXECUTE=1" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${EXECUTE}" == "1" && "${SIGNER,,}" != "${OWNER,,}" ]]; then
|
||||
echo "EXECUTE=1 signer must be the Engine X vault owner" >&2
|
||||
echo " signer: ${SIGNER}" >&2
|
||||
echo " owner: ${OWNER}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
POOL_CWUSDC_RAW="$(cast call "${VAULT}" 'poolCwusdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
POOL_USDC_RAW="$(cast call "${VAULT}" 'poolUsdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
LENDER_USDC_RAW="$(cast call "${VAULT}" 'lenderUsdcAvailable()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
VAULT_CWUSDC_RAW="$(cast call "${CWUSDC}" 'balanceOf(address)(uint256)' "${VAULT}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
VAULT_USDC_RAW="$(cast call "${USDC}" 'balanceOf(address)(uint256)' "${VAULT}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
FEE_TICK_SPACING="$(cast call "${FACTORY}" 'feeAmountTickSpacing(uint24)(int24)' "${FEE}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
ACCOUNTING_AWARE_SUPPORTED="0"
|
||||
if cast call "${VAULT}" 'maxFlashLoan(address)(uint256)' "${USDC}" --rpc-url "${ETHEREUM_MAINNET_RPC}" >/dev/null 2>&1; then
|
||||
ACCOUNTING_AWARE_SUPPORTED="1"
|
||||
fi
|
||||
|
||||
if [[ "${FEE_TICK_SPACING}" == "0" ]]; then
|
||||
echo "Uniswap v3 fee tier is not enabled on the configured factory: ${FEE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${EXECUTE}" == "1" && "${ACCOUNTING_AWARE_SUPPORTED}" != "1" ]]; then
|
||||
echo "EXECUTE=1 requires an upgraded accounting-aware Engine X vault" >&2
|
||||
echo " vault: ${VAULT}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
eval "$(
|
||||
python3 - "${CWUSDC}" "${USDC}" "${POOL_CWUSDC_RAW}" "${POOL_USDC_RAW}" \
|
||||
"${LENDER_USDC_RAW}" "${VAULT_CWUSDC_RAW}" "${VAULT_USDC_RAW}" \
|
||||
"${INCLUDE_LENDER_USDC}" "${MIGRATION_AMOUNT_RAW}" "${FEE_TICK_SPACING}" \
|
||||
"${TICK_LOWER}" "${TICK_UPPER}" <<'PY'
|
||||
from decimal import Decimal
|
||||
import sys
|
||||
|
||||
(
|
||||
cwusdc,
|
||||
usdc,
|
||||
pool_cw,
|
||||
pool_usdc,
|
||||
lender_usdc,
|
||||
vault_cw,
|
||||
vault_usdc,
|
||||
include_lender,
|
||||
override_amount,
|
||||
spacing,
|
||||
tick_lower,
|
||||
tick_upper,
|
||||
) = sys.argv[1:]
|
||||
|
||||
pool_cw = int(pool_cw)
|
||||
pool_usdc = int(pool_usdc)
|
||||
lender_usdc = int(lender_usdc)
|
||||
vault_cw = int(vault_cw)
|
||||
vault_usdc = int(vault_usdc)
|
||||
spacing = int(spacing)
|
||||
tick_lower = int(tick_lower)
|
||||
tick_upper = int(tick_upper)
|
||||
|
||||
if tick_lower >= tick_upper:
|
||||
raise SystemExit("tick lower must be less than tick upper")
|
||||
if tick_lower % spacing != 0 or tick_upper % spacing != 0:
|
||||
raise SystemExit(f"ticks must be multiples of tick spacing {spacing}")
|
||||
|
||||
available_usdc = vault_usdc if include_lender == "1" else max(vault_usdc - lender_usdc, 0)
|
||||
amount = min(pool_cw, pool_usdc, vault_cw, available_usdc)
|
||||
if override_amount:
|
||||
amount = int(override_amount)
|
||||
if amount <= 0:
|
||||
raise SystemExit("no balanced vault liquidity is available to migrate")
|
||||
if amount > vault_cw:
|
||||
raise SystemExit("migration amount exceeds vault cWUSDC balance")
|
||||
if amount > vault_usdc:
|
||||
raise SystemExit("migration amount exceeds vault USDC balance")
|
||||
if include_lender != "1" and amount > available_usdc:
|
||||
raise SystemExit("migration amount would consume lender USDC; set INCLUDE_LENDER_USDC=1 only if intentional")
|
||||
|
||||
addrs = sorted([cwusdc.lower(), usdc.lower()])
|
||||
token0, token1 = addrs
|
||||
amount0 = amount if token0 == cwusdc.lower() else amount
|
||||
amount1 = amount if token1 == usdc.lower() else amount
|
||||
|
||||
def units(raw: int) -> str:
|
||||
return f"{Decimal(raw) / Decimal(10**6):f}"
|
||||
|
||||
def emit(name, value):
|
||||
print(f"{name}='{value}'")
|
||||
|
||||
emit("TOKEN0", token0)
|
||||
emit("TOKEN1", token1)
|
||||
emit("AMOUNT0_RAW", amount0)
|
||||
emit("AMOUNT1_RAW", amount1)
|
||||
emit("MIGRATE_RAW", amount)
|
||||
emit("MIGRATE_UNITS", units(amount))
|
||||
emit("POOL_CWUSDC_UNITS", units(pool_cw))
|
||||
emit("POOL_USDC_UNITS", units(pool_usdc))
|
||||
emit("LENDER_USDC_UNITS", units(lender_usdc))
|
||||
emit("VAULT_CWUSDC_UNITS", units(vault_cw))
|
||||
emit("VAULT_USDC_UNITS", units(vault_usdc))
|
||||
emit("POOL_USDC_AVAILABLE_FOR_MIGRATION_RAW", available_usdc)
|
||||
emit("POOL_USDC_AVAILABLE_FOR_MIGRATION_UNITS", units(available_usdc))
|
||||
PY
|
||||
)"
|
||||
|
||||
POOL="$(cast call "${FACTORY}" 'getPool(address,address,uint24)(address)' "${TOKEN0}" "${TOKEN1}" "${FEE}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)"
|
||||
SIMULATED_POOL=""
|
||||
if [[ "${POOL}" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
SIMULATED_POOL="$(cast call "${POSITION_MANAGER}" 'createAndInitializePoolIfNecessary(address,address,uint24,uint160)(address)' \
|
||||
"${TOKEN0}" "${TOKEN1}" "${FEE}" "${SQRT_PRICE_X96}" --rpc-url "${ETHEREUM_MAINNET_RPC}" \
|
||||
| grep -oE '0x[a-fA-F0-9]{40}' | head -1 || true)"
|
||||
fi
|
||||
|
||||
POOL_SLOT0=""
|
||||
POOL_LIQUIDITY=""
|
||||
if [[ "${POOL}" != "0x0000000000000000000000000000000000000000" ]]; then
|
||||
POOL_SLOT0="$(cast call "${POOL}" 'slot0()(uint160,int24,uint16,uint16,uint16,uint8,bool)' --rpc-url "${ETHEREUM_MAINNET_RPC}")"
|
||||
POOL_LIQUIDITY="$(cast call "${POOL}" 'liquidity()(uint128)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
fi
|
||||
|
||||
DEADLINE="$(( $(date +%s) + DEADLINE_SECONDS ))"
|
||||
|
||||
cat <<EOF
|
||||
Engine X indexed-liquidity migration plan
|
||||
mode: ${EXECUTE}
|
||||
vault: ${VAULT}
|
||||
vault owner: ${OWNER}
|
||||
signer/recipient: ${SIGNER}
|
||||
|
||||
Engine X virtual vault state
|
||||
accounted pool: ${POOL_CWUSDC_UNITS} cWUSDC / ${POOL_USDC_UNITS} USDC
|
||||
lender bucket: ${LENDER_USDC_UNITS} USDC
|
||||
actual token balances: ${VAULT_CWUSDC_UNITS} cWUSDC / ${VAULT_USDC_UNITS} USDC
|
||||
USDC available without lender bucket: ${POOL_USDC_AVAILABLE_FOR_MIGRATION_UNITS}
|
||||
|
||||
Uniswap v3 public proof surface
|
||||
factory: ${FACTORY}
|
||||
position manager: ${POSITION_MANAGER}
|
||||
fee: ${FEE}
|
||||
tick spacing: ${FEE_TICK_SPACING}
|
||||
ticks: [${TICK_LOWER}, ${TICK_UPPER}]
|
||||
initial sqrtPriceX96: ${SQRT_PRICE_X96}
|
||||
existing pool: ${POOL}
|
||||
simulated pool if created: ${SIMULATED_POOL:-n/a}
|
||||
existing slot0: ${POOL_SLOT0:-n/a}
|
||||
existing liquidity: ${POOL_LIQUIDITY:-n/a}
|
||||
|
||||
Migration sizing
|
||||
migrate: ${MIGRATE_UNITS} cWUSDC + ${MIGRATE_UNITS} USDC
|
||||
token0: ${TOKEN0} amount0 raw: ${AMOUNT0_RAW}
|
||||
token1: ${TOKEN1} amount1 raw: ${AMOUNT1_RAW}
|
||||
|
||||
Boundary
|
||||
This migrates tiny Engine X proof liquidity into a public indexable UniV3 position.
|
||||
It does not meet the 2,500/2,500 policy floor or the 10,000/10,000 preferred evidence target.
|
||||
accounting-aware vault APIs detected: ${ACCOUNTING_AWARE_SUPPORTED}
|
||||
If this is 0, deploy the upgraded Engine X vault before broadcasting the migration.
|
||||
EOF
|
||||
|
||||
if [[ "${EXECUTE}" != "1" ]]; then
|
||||
if [[ "${ACCOUNTING_AWARE_SUPPORTED}" != "1" ]]; then
|
||||
cat <<EOF
|
||||
|
||||
Dry-run only. No broadcast commands were emitted for this vault because it does not expose
|
||||
the upgraded accounting-aware API. Deploy the upgraded Engine X vault, seed/migrate the
|
||||
balanced proof liquidity into that vault, then rerun with:
|
||||
ENGINE_X_VAULT=<upgraded-vault-address> EXECUTE=0 bash scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
cat <<EOF
|
||||
|
||||
Dry-run only. Review these commands before any broadcast:
|
||||
cast send "${VAULT}" 'withdrawPoolLiquidity(address,uint256,uint256)' "${SIGNER}" "${MIGRATE_RAW}" "${MIGRATE_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
cast send "${POSITION_MANAGER}" 'createAndInitializePoolIfNecessary(address,address,uint24,uint160)' "${TOKEN0}" "${TOKEN1}" "${FEE}" "${SQRT_PRICE_X96}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
cast send "${TOKEN0}" 'approve(address,uint256)' "${POSITION_MANAGER}" "${AMOUNT0_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
cast send "${TOKEN1}" 'approve(address,uint256)' "${POSITION_MANAGER}" "${AMOUNT1_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
cast send "${POSITION_MANAGER}" 'mint((address,address,uint24,int24,int24,uint256,uint256,uint256,uint256,address,uint256))' "(${TOKEN0},${TOKEN1},${FEE},${TICK_LOWER},${TICK_UPPER},${AMOUNT0_RAW},${AMOUNT1_RAW},0,0,${SIGNER},${DEADLINE})" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY" -vv
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cast send "${VAULT}" 'withdrawPoolLiquidity(address,uint256,uint256)' "${SIGNER}" "${MIGRATE_RAW}" "${MIGRATE_RAW}" \
|
||||
--rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
|
||||
cast send "${POSITION_MANAGER}" 'createAndInitializePoolIfNecessary(address,address,uint24,uint160)' \
|
||||
"${TOKEN0}" "${TOKEN1}" "${FEE}" "${SQRT_PRICE_X96}" \
|
||||
--rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
|
||||
cast send "${TOKEN0}" 'approve(address,uint256)' "${POSITION_MANAGER}" "${AMOUNT0_RAW}" \
|
||||
--rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
cast send "${TOKEN1}" 'approve(address,uint256)' "${POSITION_MANAGER}" "${AMOUNT1_RAW}" \
|
||||
--rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
|
||||
cast send "${POSITION_MANAGER}" 'mint((address,address,uint24,int24,int24,uint256,uint256,uint256,uint256,address,uint256))' \
|
||||
"(${TOKEN0},${TOKEN1},${FEE},${TICK_LOWER},${TICK_UPPER},${AMOUNT0_RAW},${AMOUNT1_RAW},0,0,${SIGNER},${DEADLINE})" \
|
||||
--rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}" -vv
|
||||
|
||||
NEW_POOL="$(cast call "${FACTORY}" 'getPool(address,address,uint24)(address)' "${TOKEN0}" "${TOKEN1}" "${FEE}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)"
|
||||
echo "Post-migration UniV3 pool: ${NEW_POOL}"
|
||||
cast call "${NEW_POOL}" 'slot0()(uint160,int24,uint16,uint16,uint16,uint8,bool)' --rpc-url "${ETHEREUM_MAINNET_RPC}"
|
||||
cast call "${NEW_POOL}" 'liquidity()(uint128)' --rpc-url "${ETHEREUM_MAINNET_RPC}"
|
||||
339
scripts/deployment/mint-cwusdc-ei-matrix-wallets.sh
Normal file
339
scripts/deployment/mint-cwusdc-ei-matrix-wallets.sh
Normal file
@@ -0,0 +1,339 @@
|
||||
#!/usr/bin/env bash
|
||||
# Mint Ethereum mainnet cWUSDC directly to each address in config/pmm-soak-wallet-grid.json
|
||||
# (EI matrix). Requires PRIVATE_KEY with MINTER_ROLE on the cWUSDC token.
|
||||
#
|
||||
# Modes (exactly one):
|
||||
# --mint-raw R Same raw units minted to every wallet in the slice.
|
||||
# --total-mint-raw B Total supply to mint across the slice, split with ±spread
|
||||
# then renormalized to B (same algorithm as transfer distribution).
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/deployment/mint-cwusdc-ei-matrix-wallets.sh [--dry-run] [--limit N] [--offset N|--resume-next]
|
||||
# (--mint-raw R | --total-mint-raw B [--spread-pct S])
|
||||
#
|
||||
# --quiet-dry-run With --dry-run, suppress per-wallet lines.
|
||||
# --legacy Pass --legacy to cast send.
|
||||
#
|
||||
# Env: ETHEREUM_MAINNET_RPC, CWUSDC_MAINNET, PRIVATE_KEY,
|
||||
# EI_MATRIX_MINT_GAS_EST (default 60000), EI_MATRIX_GAS_HEADROOM_BPS (default 10500),
|
||||
# EI_MATRIX_SKIP_GAS_CHECK=1 to bypass ETH preflight.
|
||||
#
|
||||
# Progress: reports/status/ei-matrix-cwusdc-mint-last-idx.txt
|
||||
# Failures: reports/status/ei-matrix-cwusdc-mint-failures.log
|
||||
# Lock: reports/status/ei-matrix-cwusdc-mint.lock
|
||||
#
|
||||
# On-chain: cWUSDC uses CompliantWrappedToken-style mint(address,uint256) for MINTER_ROLE.
|
||||
# If mint reverts (reserve policy, roles), fix on-chain state before retrying.
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
DRY_RUN=false
|
||||
LIMIT=""
|
||||
OFFSET="0"
|
||||
OFFSET_EXPLICIT=false
|
||||
RESUME_NEXT=false
|
||||
SPREAD_PCT="${EI_MATRIX_SPREAD_PCT:-15}"
|
||||
CAST_LEGACY=false
|
||||
QUIET_DRY_RUN=false
|
||||
MINT_RAW=""
|
||||
TOTAL_MINT_RAW=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--dry-run) DRY_RUN=true; shift ;;
|
||||
--quiet-dry-run) QUIET_DRY_RUN=true; shift ;;
|
||||
--limit) LIMIT="${2:?}"; shift 2 ;;
|
||||
--resume-next) RESUME_NEXT=true; shift ;;
|
||||
--offset) OFFSET="${2:?}"; OFFSET_EXPLICIT=true; shift 2 ;;
|
||||
--spread-pct) SPREAD_PCT="${2:?}"; shift 2 ;;
|
||||
--mint-raw) MINT_RAW="${2:?}"; shift 2 ;;
|
||||
--total-mint-raw) TOTAL_MINT_RAW="${2:?}"; shift 2 ;;
|
||||
--legacy) CAST_LEGACY=true; shift ;;
|
||||
*) echo "Unknown arg: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
LAST_IDX_FILE="${EI_MATRIX_CWUSDC_MINT_LAST_IDX_FILE:-${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-mint-last-idx.txt}"
|
||||
if $RESUME_NEXT && $OFFSET_EXPLICIT; then
|
||||
echo "Use only one of --offset or --resume-next." >&2
|
||||
exit 1
|
||||
fi
|
||||
if $RESUME_NEXT; then
|
||||
[[ -f "$LAST_IDX_FILE" ]] || { echo "Missing last-index file for --resume-next: $LAST_IDX_FILE" >&2; exit 1; }
|
||||
_last="$(tr -d '[:space:]' < "$LAST_IDX_FILE" || echo "")"
|
||||
[[ -n "$_last" ]] || { echo "Empty $LAST_IDX_FILE" >&2; exit 1; }
|
||||
OFFSET=$((_last + 1))
|
||||
echo "Resume-next (mint): last completed idx=$_last → offset=$OFFSET"
|
||||
fi
|
||||
|
||||
if [[ -n "$MINT_RAW" && -n "$TOTAL_MINT_RAW" ]]; then
|
||||
echo "Use only one of --mint-raw or --total-mint-raw." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "$MINT_RAW" && -z "$TOTAL_MINT_RAW" ]]; then
|
||||
echo "Set --mint-raw or --total-mint-raw." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "$PROJECT_ROOT/scripts/lib/load-project-env.sh"
|
||||
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
PUBLIC_ETH_RPC="${ETHEREUM_MAINNET_PUBLIC_RPC:-https://ethereum-rpc.publicnode.com}"
|
||||
RPC="${ETHEREUM_MAINNET_RPC:-${RPC_URL_1:-${ETH_MAINNET_RPC_URL:-$PUBLIC_ETH_RPC}}}"
|
||||
BALANCE_RPC="${EI_MATRIX_BALANCE_RPC:-$RPC}"
|
||||
|
||||
LOCK_FILE="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-mint.lock"
|
||||
MANIFEST_DIR="${PROJECT_ROOT}/reports/status"
|
||||
mkdir -p "$MANIFEST_DIR"
|
||||
exec 200>"$LOCK_FILE"
|
||||
if ! flock -n 200; then
|
||||
echo "Another mint-cwusdc-ei-matrix-wallets.sh is already running (lock: $LOCK_FILE)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GRID="$PROJECT_ROOT/config/pmm-soak-wallet-grid.json"
|
||||
DEPLOYER_CANONICAL="0x4A666F96fC8764181194447A7dFdb7d471b301C8"
|
||||
|
||||
[[ -f "$GRID" ]] || { echo "Missing $GRID" >&2; exit 1; }
|
||||
command -v cast &>/dev/null || { echo "cast required" >&2; exit 1; }
|
||||
command -v jq &>/dev/null || { echo "jq required" >&2; exit 1; }
|
||||
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY not set" >&2; exit 1; }
|
||||
|
||||
FROM_ADDR=$(cast wallet address --private-key "$PRIVATE_KEY")
|
||||
CHAIN_ID=$(cast chain-id --rpc-url "$RPC" 2>/dev/null | tr -d '[:space:]' || true)
|
||||
[[ -n "$CHAIN_ID" ]] || CHAIN_ID="1"
|
||||
if [[ "$CHAIN_ID" != "1" ]]; then
|
||||
echo "[WARN] chain-id=$CHAIN_ID (expected 1)." >&2
|
||||
fi
|
||||
|
||||
pending_nonce() {
|
||||
local resp hex
|
||||
resp=$(curl -sS -X POST "$RPC" -H "Content-Type: application/json" \
|
||||
-d "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionCount\",\"params\":[\"${FROM_ADDR}\",\"pending\"],\"id\":1}" 2>/dev/null) || return 1
|
||||
hex=$(echo "$resp" | jq -r '.result // empty')
|
||||
[[ -n "$hex" ]] || return 1
|
||||
cast to-dec "$hex"
|
||||
}
|
||||
|
||||
token_decimals() {
|
||||
cast call "$CWUSDC" 'decimals()(uint8)' --rpc-url "$BALANCE_RPC" 2>/dev/null | awk '{print $1}'
|
||||
}
|
||||
|
||||
generate_spread_amounts_raw() {
|
||||
local count="$1" budget="$2" spread="$3"
|
||||
python3 - "$count" "$budget" "$spread" <<'PY'
|
||||
import random
|
||||
import sys
|
||||
n = int(sys.argv[1])
|
||||
budget = int(sys.argv[2])
|
||||
spread = float(sys.argv[3])
|
||||
if n <= 0:
|
||||
sys.exit("count must be positive")
|
||||
if budget < 0:
|
||||
sys.exit("budget must be non-negative")
|
||||
if spread < 0 or spread > 100:
|
||||
sys.exit("spread-pct must be in [0, 100]")
|
||||
base = 10000
|
||||
low_w = max(1, (100 * base - int(spread * base)) // 100)
|
||||
high_w = (100 * base + int(spread * base)) // 100
|
||||
w = [random.randint(low_w, high_w) for _ in range(n)]
|
||||
s = sum(w)
|
||||
raw = [(budget * wi) // s for wi in w]
|
||||
rem = budget - sum(raw)
|
||||
for i in range(rem):
|
||||
raw[i % n] += 1
|
||||
for x in raw:
|
||||
print(x)
|
||||
PY
|
||||
}
|
||||
|
||||
stream_addresses() {
|
||||
if [[ -n "${LIMIT:-}" ]]; then
|
||||
jq -r --argjson o "$OFFSET" --argjson l "$LIMIT" '.wallets[$o:$o+$l][] | .address' "$GRID"
|
||||
else
|
||||
jq -r --argjson o "$OFFSET" '.wallets[$o:][] | .address' "$GRID"
|
||||
fi
|
||||
}
|
||||
|
||||
ERR_LOG="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-mint-failures.log"
|
||||
LAST_IDX="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-mint-last-idx.txt"
|
||||
|
||||
matrix_try_mint() {
|
||||
local addr="$1" raw_amt="$2" idx="$3"
|
||||
local dec human out tx attempt=1
|
||||
dec="${DECIMALS:-6}"
|
||||
if [[ "$raw_amt" == "0" ]]; then
|
||||
echo "[skip] idx=$idx $addr zero raw"
|
||||
return 0
|
||||
fi
|
||||
human=$(python3 -c "d=int('$dec'); a=int('$raw_amt'); print(f'{a / (10**d):.{min(d,8)}f}')" 2>/dev/null || echo "$raw_amt")
|
||||
if $DRY_RUN; then
|
||||
if ! $QUIET_DRY_RUN; then
|
||||
echo "[dry-run] idx=$idx $addr raw=$raw_amt (~$human)"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
local cast_extra=()
|
||||
$CAST_LEGACY && cast_extra+=(--legacy)
|
||||
while [[ "$attempt" -le 2 ]]; do
|
||||
if out=$(cast send "$CWUSDC" "mint(address,uint256)" "$addr" "$raw_amt" \
|
||||
--rpc-url "$RPC" --private-key "$PRIVATE_KEY" \
|
||||
--nonce "$NONCE" "${cast_extra[@]}" 2>&1); then
|
||||
tx=$(echo "$out" | tail -n1)
|
||||
echo "[ok] idx=$idx nonce=$NONCE $addr raw=$raw_amt (~$human) tx=$tx"
|
||||
sent=$((sent + 1))
|
||||
NONCE=$((NONCE + 1))
|
||||
echo "$idx" > "$LAST_IDX"
|
||||
return 0
|
||||
fi
|
||||
if [[ "$attempt" -eq 1 ]] && echo "$out" | grep -qi 'nonce too low'; then
|
||||
NONCE=$(pending_nonce) || true
|
||||
echo "[retry] idx=$idx nonce refreshed to $NONCE (nonce too low)" >&2
|
||||
attempt=$((attempt + 1))
|
||||
continue
|
||||
fi
|
||||
echo "[fail] idx=$idx nonce=$NONCE $addr $out" | tee -a "$ERR_LOG" >&2
|
||||
failed=$((failed + 1))
|
||||
NONCE=$(pending_nonce) || true
|
||||
return 0
|
||||
done
|
||||
}
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "EI matrix cWUSDC mint (mainnet)"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "RPC: $RPC"
|
||||
echo "Token: $CWUSDC"
|
||||
echo "Signer: $FROM_ADDR"
|
||||
echo "Grid: $GRID"
|
||||
echo "Dry-run: $DRY_RUN Quiet: $QUIET_DRY_RUN"
|
||||
echo "Offset: $OFFSET Limit: ${LIMIT:-all}"
|
||||
if [[ -n "$MINT_RAW" ]]; then
|
||||
echo "Mode: fixed --mint-raw $MINT_RAW per wallet"
|
||||
else
|
||||
echo "Mode: --total-mint-raw $TOTAL_MINT_RAW spread: ±${SPREAD_PCT}% normalized"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
if [[ "${FROM_ADDR,,}" != "${DEPLOYER_CANONICAL,,}" ]]; then
|
||||
echo "[WARN] Signer is not canonical deployer $DEPLOYER_CANONICAL — minter role may still be granted."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
DECIMALS=$(token_decimals || echo "6")
|
||||
|
||||
ADDR_TMP=$(mktemp)
|
||||
AMOUNTS_TMP=$(mktemp)
|
||||
cleanup_tmp() {
|
||||
[[ -f "$ADDR_TMP" ]] && rm -f "$ADDR_TMP"
|
||||
[[ -f "$AMOUNTS_TMP" ]] && rm -f "$AMOUNTS_TMP"
|
||||
}
|
||||
trap cleanup_tmp EXIT
|
||||
|
||||
stream_addresses > "$ADDR_TMP"
|
||||
WALLET_COUNT=$(wc -l < "$ADDR_TMP" | tr -d '[:space:]')
|
||||
if [[ -z "$WALLET_COUNT" || "$WALLET_COUNT" -eq 0 ]]; then
|
||||
echo "No wallets in range (offset=$OFFSET limit=${LIMIT:-all})." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$MINT_RAW" ]]; then
|
||||
awk -v r="$MINT_RAW" '{print r}' "$ADDR_TMP" > "$AMOUNTS_TMP"
|
||||
BUDGET_RAW=$((MINT_RAW * WALLET_COUNT))
|
||||
else
|
||||
BUDGET_RAW="$TOTAL_MINT_RAW"
|
||||
if [[ "$BUDGET_RAW" -le 0 ]]; then
|
||||
echo "total-mint-raw must be positive." >&2
|
||||
exit 1
|
||||
fi
|
||||
generate_spread_amounts_raw "$WALLET_COUNT" "$BUDGET_RAW" "$SPREAD_PCT" > "$AMOUNTS_TMP"
|
||||
fi
|
||||
|
||||
SUM_CHECK=$(awk '{s+=$1} END {print s}' "$AMOUNTS_TMP")
|
||||
if [[ "$SUM_CHECK" != "$BUDGET_RAW" ]]; then
|
||||
echo "INTERNAL: amount sum $SUM_CHECK != budget $BUDGET_RAW" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
AMOUNTS_SHA256=$(sha256sum "$AMOUNTS_TMP" | awk '{print $1}')
|
||||
TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
MANIFEST="$MANIFEST_DIR/ei-matrix-cwusdc-mint-manifest-${TS//:/-}.json"
|
||||
cat >"$MANIFEST" <<EOF
|
||||
{
|
||||
"version": 1,
|
||||
"kind": "ei-matrix-cwusdc-mint",
|
||||
"timestamp": "$TS",
|
||||
"chainId": 1,
|
||||
"token": "$CWUSDC",
|
||||
"signer": "$FROM_ADDR",
|
||||
"offset": $OFFSET,
|
||||
"limit": ${LIMIT:-null},
|
||||
"walletCount": $WALLET_COUNT,
|
||||
"budgetRaw": "$BUDGET_RAW",
|
||||
"fixedMintRaw": ${MINT_RAW:-null},
|
||||
"spreadPct": ${SPREAD_PCT},
|
||||
"amountsSha256": "$AMOUNTS_SHA256",
|
||||
"manifestPath": "$MANIFEST"
|
||||
}
|
||||
EOF
|
||||
echo "Manifest: $MANIFEST"
|
||||
echo "Amounts SHA256: $AMOUNTS_SHA256"
|
||||
echo ""
|
||||
|
||||
GAS_EST="${EI_MATRIX_MINT_GAS_EST:-60000}"
|
||||
HEADROOM_BPS="${EI_MATRIX_GAS_HEADROOM_BPS:-10500}"
|
||||
ETH_WEI=$(cast balance "$FROM_ADDR" --rpc-url "$BALANCE_RPC" 2>/dev/null | awk '{print $1}' || echo "0")
|
||||
ETH_HUMAN=$(python3 -c "print(f'{int(\"$ETH_WEI\") / 1e18:.6f}')" 2>/dev/null || echo "?")
|
||||
echo "Signer ETH (gas): ${ETH_WEI} wei (~$ETH_HUMAN ETH)"
|
||||
|
||||
if ! $DRY_RUN && [[ "${EI_MATRIX_SKIP_GAS_CHECK:-}" != "1" ]]; then
|
||||
GAS_PRICE_WEI=$(cast gas-price --rpc-url "$RPC" 2>/dev/null | awk '{print $1}' | head -1)
|
||||
[[ -n "$GAS_PRICE_WEI" ]] || GAS_PRICE_WEI=0
|
||||
MIN_WEI=$(python3 -c "c=int('$WALLET_COUNT'); g=int('$GAS_EST'); p=int('$GAS_PRICE_WEI'); b=int('$HEADROOM_BPS'); print(c*g*p*b//10000)")
|
||||
if python3 -c "import sys; sys.exit(0 if int('$ETH_WEI') >= int('$MIN_WEI') else 1)"; then
|
||||
echo "Gas preflight OK: est ${GAS_EST} gas/tx × $WALLET_COUNT × gasPrice $GAS_PRICE_WEI × (${HEADROOM_BPS}/10000) ≈ $MIN_WEI wei."
|
||||
else
|
||||
echo "Insufficient ETH for gas preflight. Need ≈ $MIN_WEI wei." >&2
|
||||
echo "Set EI_MATRIX_SKIP_GAS_CHECK=1 to override (operator risk)." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "Sample (first 3, last 3):"
|
||||
_s_idx=$OFFSET
|
||||
while IFS= read -r s_addr && IFS= read -r s_raw <&3; do
|
||||
h=$(python3 -c "d=int('$DECIMALS'); a=int('$s_raw'); print(f'{a / (10**d):.6f}')" 2>/dev/null || echo "$s_raw")
|
||||
echo " idx=$_s_idx $s_addr raw=$s_raw (~$h cWUSDC)"
|
||||
_s_idx=$((_s_idx + 1))
|
||||
done < <(head -3 "$ADDR_TMP") 3< <(head -3 "$AMOUNTS_TMP")
|
||||
_s_idx=$((OFFSET + WALLET_COUNT - 3))
|
||||
while IFS= read -r s_addr && IFS= read -r s_raw <&3; do
|
||||
h=$(python3 -c "d=int('$DECIMALS'); a=int('$s_raw'); print(f'{a / (10**d):.6f}')" 2>/dev/null || echo "$s_raw")
|
||||
echo " idx=$_s_idx $s_addr raw=$s_raw (~$h cWUSDC)"
|
||||
_s_idx=$((_s_idx + 1))
|
||||
done < <(tail -3 "$ADDR_TMP") 3< <(tail -3 "$AMOUNTS_TMP")
|
||||
echo ""
|
||||
|
||||
sent=0
|
||||
failed=0
|
||||
idx=$OFFSET
|
||||
NONCE=$(pending_nonce) || { echo "Could not read pending nonce" >&2; exit 1; }
|
||||
echo "Starting nonce (pending): $NONCE"
|
||||
echo ""
|
||||
|
||||
while IFS=$'\t' read -r addr raw_amt; do
|
||||
matrix_try_mint "$addr" "$raw_amt" "$idx"
|
||||
idx=$((idx + 1))
|
||||
done < <(paste -d $'\t' "$ADDR_TMP" "$AMOUNTS_TMP")
|
||||
|
||||
if $DRY_RUN; then
|
||||
echo "Dry-run complete. Indices covered: $OFFSET..$((idx - 1))."
|
||||
else
|
||||
echo "Done. Mint txs attempted: sent=$sent failed=$failed"
|
||||
fi
|
||||
68
scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh
Normal file
68
scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh
Normal file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
# Single operator pipeline: preflight + mint cWUSDC to every EI matrix wallet on Ethereum mainnet.
|
||||
#
|
||||
# This does NOT bridge from Chain 138 — it mints on mainnet via MINTER_ROLE on CWUSDC_MAINNET.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh --dry-run --mint-raw 1000000
|
||||
# ./scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh --total-mint-raw 1000000000000 --spread-pct 15
|
||||
# ./scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh --mint-raw 1000000 -- --limit 10 # extra args after --
|
||||
#
|
||||
# Env: loads scripts/lib/load-project-env.sh (PRIVATE_KEY, ETHEREUM_MAINNET_RPC, CWUSDC_MAINNET).
|
||||
#
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
PASS=()
|
||||
while [[ $# -gt 0 ]]; do
|
||||
if [[ "$1" == "--" ]]; then shift; PASS+=("$@"); break; fi
|
||||
PASS+=("$1")
|
||||
shift
|
||||
done
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "$PROJECT_ROOT/scripts/lib/load-project-env.sh"
|
||||
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
PUBLIC_ETH_RPC="${ETHEREUM_MAINNET_PUBLIC_RPC:-https://ethereum-rpc.publicnode.com}"
|
||||
RPC="${ETHEREUM_MAINNET_RPC:-${RPC_URL_1:-${ETH_MAINNET_RPC_URL:-$PUBLIC_ETH_RPC}}}"
|
||||
|
||||
echo "══════════════════════════════════════════════════════════════"
|
||||
echo "Pipeline: EI matrix mainnet cWUSDC mint"
|
||||
echo "══════════════════════════════════════════════════════════════"
|
||||
echo "Grid: config/pmm-soak-wallet-grid.json"
|
||||
echo "Token: $CWUSDC"
|
||||
echo "RPC: $RPC"
|
||||
echo ""
|
||||
|
||||
command -v cast &>/dev/null || { echo "cast required" >&2; exit 1; }
|
||||
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY not set" >&2; exit 1; }
|
||||
|
||||
FROM=$(cast wallet address --private-key "$PRIVATE_KEY")
|
||||
CID=$(cast chain-id --rpc-url "$RPC" 2>/dev/null | tr -d '[:space:]' || echo "?")
|
||||
echo "Signer: $FROM"
|
||||
echo "Chain ID: $CID (expected 1)"
|
||||
if [[ "$CID" != "1" ]]; then
|
||||
echo "[WARN] Not Ethereum mainnet — aborting." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ROLE=$(cast keccak "MINTER_ROLE()")
|
||||
# AccessControl MINTER_ROLE is keccak256 of the string "MINTER_ROLE" for OZ — use cast keccak
|
||||
ROLE=$(cast keccak "MINTER_ROLE")
|
||||
if HR=$(cast call "$CWUSDC" "hasRole(bytes32,address)(bool)" "$ROLE" "$FROM" --rpc-url "$RPC" 2>/dev/null); then
|
||||
if [[ "${HR,,}" != *true* ]]; then
|
||||
echo "[WARN] hasRole(MINTER_ROLE) returned false for signer — mints will likely revert." >&2
|
||||
else
|
||||
echo "Preflight: MINTER_ROLE on cWUSDC for signer — OK"
|
||||
fi
|
||||
else
|
||||
echo "[WARN] Could not call hasRole (ABI may differ) — continuing." >&2
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "→ Running mint-cwusdc-ei-matrix-wallets.sh ${PASS[*]}"
|
||||
echo ""
|
||||
exec "$SCRIPT_DIR/mint-cwusdc-ei-matrix-wallets.sh" "${PASS[@]}"
|
||||
93
scripts/deployment/record-engine-x-indexed-liquidity-proof.sh
Executable file
93
scripts/deployment/record-engine-x-indexed-liquidity-proof.sh
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/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"
|
||||
|
||||
: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}"
|
||||
|
||||
if [[ -z "${ENGINE_X_INDEXED_LIQUIDITY_VAULT:-}" ]]; then
|
||||
cat <<'EOF'
|
||||
Engine X indexed proof recording blocked
|
||||
ENGINE_X_INDEXED_LIQUIDITY_VAULT is not configured.
|
||||
Deploy the indexed-liquidity proof vault first:
|
||||
pnpm engine-x:indexed-vault-deploy
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
EXECUTE="${EXECUTE:-0}"
|
||||
RECIPIENT="${RECIPIENT:-${DEPLOYER_ADDRESS:-}}"
|
||||
EXACT_OUTPUT_RAW="${EXACT_OUTPUT_RAW:-10000}"
|
||||
PUBLIC_SWAP_TX_HASH_PROVIDED="${PUBLIC_SWAP_TX_HASH+x}"
|
||||
LIQUIDITY_TX_HASH_PROVIDED="${LIQUIDITY_TX_HASH+x}"
|
||||
ISO_HASH_PROVIDED="${ISO_HASH+x}"
|
||||
AUDIT_HASH_PROVIDED="${AUDIT_HASH+x}"
|
||||
PEG_HASH_PROVIDED="${PEG_HASH+x}"
|
||||
PROOF_ID="${PROOF_ID:-$(cast keccak "engine-x:indexed-proof:dry-run:${ENGINE_X_INDEXED_LIQUIDITY_VAULT}:${EXACT_OUTPUT_RAW}")}"
|
||||
PUBLIC_SWAP_TX_HASH="${PUBLIC_SWAP_TX_HASH:-$(cast keccak "engine-x:placeholder:public-swap")}"
|
||||
LIQUIDITY_TX_HASH="${LIQUIDITY_TX_HASH:-$(cast keccak "engine-x:placeholder:liquidity")}"
|
||||
ISO_HASH="${ISO_HASH:-$(cast keccak "engine-x:placeholder:iso")}"
|
||||
AUDIT_HASH="${AUDIT_HASH:-$(cast keccak "engine-x:placeholder:audit")}"
|
||||
PEG_HASH="${PEG_HASH:-$(cast keccak "engine-x:placeholder:peg")}"
|
||||
|
||||
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
||||
SIGNER="$(cast wallet address --private-key "${PRIVATE_KEY}")"
|
||||
RECIPIENT="${RECIPIENT:-${SIGNER}}"
|
||||
else
|
||||
SIGNER="${DEPLOYER_ADDRESS:-}"
|
||||
fi
|
||||
if [[ -z "${RECIPIENT}" || -z "${SIGNER}" ]]; then
|
||||
echo "Set RECIPIENT and DEPLOYER_ADDRESS or PRIVATE_KEY" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then
|
||||
echo "PRIVATE_KEY is required when EXECUTE=1" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PLACEHOLDER_USED="0"
|
||||
if [[ -z "${PUBLIC_SWAP_TX_HASH_PROVIDED}" || -z "${LIQUIDITY_TX_HASH_PROVIDED}" || -z "${ISO_HASH_PROVIDED}" || -z "${AUDIT_HASH_PROVIDED}" || -z "${PEG_HASH_PROVIDED}" ]]; then
|
||||
PLACEHOLDER_USED="1"
|
||||
fi
|
||||
|
||||
if [[ "${EXECUTE}" == "1" && "${PLACEHOLDER_USED}" == "1" ]]; then
|
||||
echo "EXECUTE=1 requires PUBLIC_SWAP_TX_HASH, LIQUIDITY_TX_HASH, ISO_HASH, AUDIT_HASH, and PEG_HASH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
STATE="$(cast call "${ENGINE_X_INDEXED_LIQUIDITY_VAULT}" 'currentPoolState()(uint160,int24,uint128,uint256,uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}")"
|
||||
|
||||
cat <<EOF
|
||||
Engine X indexed proof recording plan
|
||||
mode: ${EXECUTE}
|
||||
vault: ${ENGINE_X_INDEXED_LIQUIDITY_VAULT}
|
||||
signer: ${SIGNER}
|
||||
recipient: ${RECIPIENT}
|
||||
exact output raw: ${EXACT_OUTPUT_RAW}
|
||||
proof id: ${PROOF_ID}
|
||||
public swap tx hash: ${PUBLIC_SWAP_TX_HASH}
|
||||
liquidity tx hash: ${LIQUIDITY_TX_HASH}
|
||||
iso hash: ${ISO_HASH}
|
||||
audit hash: ${AUDIT_HASH}
|
||||
peg hash: ${PEG_HASH}
|
||||
current pool state: ${STATE}
|
||||
placeholder inputs used: ${PLACEHOLDER_USED}
|
||||
EOF
|
||||
|
||||
if [[ "${EXECUTE}" != "1" ]]; then
|
||||
cat <<EOF
|
||||
|
||||
Dry-run only. Use real tx/proof hashes before broadcasting:
|
||||
cast send "${ENGINE_X_INDEXED_LIQUIDITY_VAULT}" 'recordIndexedProof((bytes32,bytes32,bytes32,address,uint256,bytes32,bytes32,bytes32))' "(${PROOF_ID},${PUBLIC_SWAP_TX_HASH},${LIQUIDITY_TX_HASH},${RECIPIENT},${EXACT_OUTPUT_RAW},${ISO_HASH},${AUDIT_HASH},${PEG_HASH})" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cast send "${ENGINE_X_INDEXED_LIQUIDITY_VAULT}" \
|
||||
'recordIndexedProof((bytes32,bytes32,bytes32,address,uint256,bytes32,bytes32,bytes32))' \
|
||||
"(${PROOF_ID},${PUBLIC_SWAP_TX_HASH},${LIQUIDITY_TX_HASH},${RECIPIENT},${EXACT_OUTPUT_RAW},${ISO_HASH},${AUDIT_HASH},${PEG_HASH})" \
|
||||
--rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
155
scripts/deployment/repair-mainnet-cwusdc-usdc-univ2-canary.sh
Executable file
155
scripts/deployment/repair-mainnet-cwusdc-usdc-univ2-canary.sh
Executable file
@@ -0,0 +1,155 @@
|
||||
#!/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"
|
||||
|
||||
: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}"
|
||||
: "${PRIVATE_KEY:?PRIVATE_KEY is required}"
|
||||
|
||||
PAIR="${MAINNET_CWUSDC_USDC_UNIV2_PAIR:-0xC28706F899266b36BC43cc072b3a921BDf2C48D9}"
|
||||
ROUTER="${CHAIN_1_UNISWAP_V2_ROUTER:-0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D}"
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}"
|
||||
SLIPPAGE_BPS="${SLIPPAGE_BPS:-100}"
|
||||
USDC_KEEP_RAW="${MAINNET_PUBLIC_LP_USDC_KEEP_RAW:-10000}"
|
||||
EXECUTE="${EXECUTE:-0}"
|
||||
|
||||
SIGNER="$(cast wallet address --private-key "${PRIVATE_KEY}")"
|
||||
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)"
|
||||
RESERVES="$(cast call "${PAIR}" 'getReserves()(uint112,uint112,uint32)' --rpc-url "${ETHEREUM_MAINNET_RPC}")"
|
||||
WALLET_USDC_RAW="$(cast call "${USDC}" 'balanceOf(address)(uint256)' "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | sed -E 's/[[:space:]].*$//')"
|
||||
WALLET_CWUSDC_RAW="$(cast call "${CWUSDC}" 'balanceOf(address)(uint256)' "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | sed -E 's/[[:space:]].*$//')"
|
||||
|
||||
PLAN="$(
|
||||
python3 - "${CWUSDC}" "${USDC}" "${TOKEN0}" "${TOKEN1}" "${RESERVES}" "${WALLET_USDC_RAW}" "${WALLET_CWUSDC_RAW}" "${SLIPPAGE_BPS}" "${USDC_KEEP_RAW}" <<'PY'
|
||||
from decimal import Decimal, ROUND_FLOOR, ROUND_CEILING, getcontext
|
||||
import re
|
||||
import sys
|
||||
|
||||
getcontext().prec = 80
|
||||
cwusdc, usdc, token0, token1, reserves, wallet_usdc, wallet_cwusdc, slippage_bps, usdc_keep = sys.argv[1:]
|
||||
# Cast prints reserves as one uint112 per line; ignore bracket scientific notation.
|
||||
_lines = [ln.strip() for ln in reserves.strip().splitlines() if ln.strip()]
|
||||
_nums = []
|
||||
for ln in _lines:
|
||||
m = re.match(r"^(\d+)", ln)
|
||||
if m:
|
||||
_nums.append(int(m.group(1)))
|
||||
if len(_nums) < 2:
|
||||
raise SystemExit("could not parse pair reserves")
|
||||
|
||||
reserve0, reserve1 = _nums[:2]
|
||||
if token0.lower() == cwusdc.lower() and token1.lower() == usdc.lower():
|
||||
base_raw, quote_raw = reserve0, reserve1
|
||||
elif token1.lower() == cwusdc.lower() and token0.lower() == usdc.lower():
|
||||
base_raw, quote_raw = reserve1, reserve0
|
||||
else:
|
||||
raise SystemExit("pair tokens do not match cWUSDC/USDC")
|
||||
|
||||
wallet_usdc_raw = int(wallet_usdc)
|
||||
wallet_cwusdc_raw = int(wallet_cwusdc)
|
||||
slippage = int(slippage_bps)
|
||||
keep_raw = int(usdc_keep)
|
||||
fee = Decimal("0.997")
|
||||
x = Decimal(base_raw)
|
||||
y = Decimal(quote_raw)
|
||||
|
||||
if y <= 0 or x <= y:
|
||||
raise SystemExit("pair does not look quote-side short; aborting canary repair")
|
||||
|
||||
# Solve for quote-in q such that post-swap reserves are equal:
|
||||
# (y + q) * (y + 0.997q) = x * y
|
||||
a = fee
|
||||
b = y * (Decimal(1) + fee)
|
||||
c = y * y - x * y
|
||||
q = (-b + (b * b - Decimal(4) * a * c).sqrt()) / (Decimal(2) * a)
|
||||
quote_in_raw = int(q.to_integral_value(rounding=ROUND_CEILING))
|
||||
quote_in_fee = Decimal(quote_in_raw) * fee
|
||||
base_out = x * quote_in_fee / (y + quote_in_fee)
|
||||
min_base_out_raw = int((base_out * Decimal(10000 - slippage) / Decimal(10000)).to_integral_value(rounding=ROUND_FLOOR))
|
||||
|
||||
if wallet_usdc_raw < quote_in_raw + keep_raw:
|
||||
raise SystemExit(
|
||||
f"insufficient USDC for canary plus keep reserve: need {quote_in_raw + keep_raw}, have {wallet_usdc_raw}"
|
||||
)
|
||||
|
||||
balanced_quote_add_raw = max(wallet_usdc_raw - quote_in_raw - keep_raw, 0)
|
||||
balanced_base_add_raw = min(wallet_cwusdc_raw + int(base_out), balanced_quote_add_raw)
|
||||
min_balanced_raw = int((Decimal(balanced_base_add_raw) * Decimal(10000 - slippage) / Decimal(10000)).to_integral_value(rounding=ROUND_FLOOR))
|
||||
|
||||
def emit(name, value):
|
||||
print(f"{name}='{value}'")
|
||||
|
||||
emit("BASE_RESERVE_RAW", base_raw)
|
||||
emit("QUOTE_RESERVE_RAW", quote_raw)
|
||||
emit("QUOTE_IN_RAW", quote_in_raw)
|
||||
emit("EXPECTED_BASE_OUT_RAW", int(base_out.to_integral_value(rounding=ROUND_FLOOR)))
|
||||
emit("MIN_BASE_OUT_RAW", min_base_out_raw)
|
||||
emit("BALANCED_ADD_RAW", balanced_base_add_raw)
|
||||
emit("MIN_BALANCED_ADD_RAW", min_balanced_raw)
|
||||
emit("USDC_KEEP_RAW_EFFECTIVE", keep_raw)
|
||||
PY
|
||||
)" || { echo "Canary planner failed (likely insufficient USDC vs computed quote-in)." >&2; exit 1; }
|
||||
eval "${PLAN}"
|
||||
|
||||
# UniV2 router is source of truth for swap output; CP closed-form above can diverge on extreme reserves.
|
||||
ROUTER_AMOUNTS="$(cast call "${ROUTER}" "getAmountsOut(uint256,address[])(uint256[])" "${QUOTE_IN_RAW}" "[${USDC},${CWUSDC}]" --rpc-url "${ETHEREUM_MAINNET_RPC}")"
|
||||
# Second hop amount is the cWUSDC out; require wide digits so we do not confuse brackets with reserves.
|
||||
EXPECTED_BASE_OUT_RAW="$(python3 -c 'import re,sys; s=sys.argv[1]; nums=[int(x) for x in re.findall(r"\b\d{10,}\b", s)]; print(nums[-1] if nums else 0)' "${ROUTER_AMOUNTS}")"
|
||||
MIN_BASE_OUT_RAW=$(( EXPECTED_BASE_OUT_RAW * (10000 - SLIPPAGE_BPS) / 10000 ))
|
||||
if [[ "${EXPECTED_BASE_OUT_RAW}" -eq 0 ]]; then
|
||||
echo "router getAmountsOut returned 0; aborting" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
Mainnet cWUSDC/USDC UniV2 public canary plan
|
||||
mode: ${EXECUTE}
|
||||
pair: ${PAIR}
|
||||
signer: ${SIGNER}
|
||||
reserves raw before: ${BASE_RESERVE_RAW} cWUSDC / ${QUOTE_RESERVE_RAW} USDC
|
||||
quote repair swap: ${QUOTE_IN_RAW} USDC raw
|
||||
expected cWUSDC out (router): ${EXPECTED_BASE_OUT_RAW} raw
|
||||
min cWUSDC out (${SLIPPAGE_BPS} bps slip): ${MIN_BASE_OUT_RAW} raw
|
||||
balanced add after repair: ${BALANCED_ADD_RAW} raw per side
|
||||
USDC keep reserve: ${USDC_KEEP_RAW_EFFECTIVE} raw
|
||||
|
||||
This is a tiny public LP canary, not policy-floor liquidity.
|
||||
EOF
|
||||
|
||||
if [[ "${EXECUTE}" != "1" ]]; then
|
||||
cat <<EOF
|
||||
|
||||
Dry-run only. To broadcast after reviewing the numbers:
|
||||
EXECUTE=1 bash scripts/deployment/repair-mainnet-cwusdc-usdc-univ2-canary.sh
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
DEADLINE="$(( $(date +%s) + 1800 ))"
|
||||
|
||||
cast send "${USDC}" 'approve(address,uint256)(bool)' "${ROUTER}" "${QUOTE_IN_RAW}" \
|
||||
--private-key "${PRIVATE_KEY}" --rpc-url "${ETHEREUM_MAINNET_RPC}"
|
||||
|
||||
cast send "${ROUTER}" 'swapExactTokensForTokens(uint256,uint256,address[],address,uint256)' \
|
||||
"${QUOTE_IN_RAW}" "${MIN_BASE_OUT_RAW}" "[${USDC},${CWUSDC}]" "${SIGNER}" "${DEADLINE}" \
|
||||
--private-key "${PRIVATE_KEY}" --rpc-url "${ETHEREUM_MAINNET_RPC}"
|
||||
|
||||
if [[ "${BALANCED_ADD_RAW}" != "0" ]]; then
|
||||
cast send "${CWUSDC}" 'approve(address,uint256)(bool)' "${ROUTER}" "${BALANCED_ADD_RAW}" \
|
||||
--private-key "${PRIVATE_KEY}" --rpc-url "${ETHEREUM_MAINNET_RPC}"
|
||||
cast send "${USDC}" 'approve(address,uint256)(bool)' "${ROUTER}" "${BALANCED_ADD_RAW}" \
|
||||
--private-key "${PRIVATE_KEY}" --rpc-url "${ETHEREUM_MAINNET_RPC}"
|
||||
cast send "${ROUTER}" 'addLiquidity(address,address,uint256,uint256,uint256,uint256,address,uint256)' \
|
||||
"${CWUSDC}" "${USDC}" "${BALANCED_ADD_RAW}" "${BALANCED_ADD_RAW}" \
|
||||
"${MIN_BALANCED_ADD_RAW}" "${MIN_BALANCED_ADD_RAW}" "${SIGNER}" "${DEADLINE}" \
|
||||
--private-key "${PRIVATE_KEY}" --rpc-url "${ETHEREUM_MAINNET_RPC}"
|
||||
fi
|
||||
|
||||
bash "${PROJECT_ROOT}/scripts/verify/snapshot-mainnet-cwusdc-usdc-preflight.sh"
|
||||
bash "${PROJECT_ROOT}/scripts/verify/plan-mainnet-cwusdc-usdc-repeg.sh"
|
||||
124
scripts/deployment/retire-engine-x-legacy-vault.sh
Executable file
124
scripts/deployment/retire-engine-x-legacy-vault.sh
Executable file
@@ -0,0 +1,124 @@
|
||||
#!/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"
|
||||
|
||||
: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}"
|
||||
|
||||
VAULT="${ENGINE_X_VAULT:-0x9a22a3e272A364D64240dE6bda796FcA421cA7E9}"
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}"
|
||||
RECIPIENT="${RETIRE_RECIPIENT:-${DEPLOYER_ADDRESS:-}}"
|
||||
ALLOW_STALE_ACCOUNTING_RETIREMENT="${ALLOW_STALE_ACCOUNTING_RETIREMENT:-0}"
|
||||
EXECUTE="${EXECUTE:-0}"
|
||||
|
||||
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
||||
SIGNER="$(cast wallet address --private-key "${PRIVATE_KEY}")"
|
||||
RECIPIENT="${RETIRE_RECIPIENT:-${SIGNER}}"
|
||||
else
|
||||
SIGNER="${DEPLOYER_ADDRESS:-}"
|
||||
fi
|
||||
|
||||
if [[ -z "${RECIPIENT}" ]]; then
|
||||
echo "Set RETIRE_RECIPIENT 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
|
||||
|
||||
OWNER="$(cast call "${VAULT}" 'owner()(address)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)"
|
||||
POOL_CWUSDC_RAW="$(cast call "${VAULT}" 'poolCwusdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}' || echo 0)"
|
||||
POOL_USDC_RAW="$(cast call "${VAULT}" 'poolUsdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}' || echo 0)"
|
||||
LENDER_USDC_RAW="$(cast call "${VAULT}" 'lenderUsdcAvailable()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}' || echo 0)"
|
||||
VAULT_CWUSDC_RAW="$(cast call "${CWUSDC}" 'balanceOf(address)(uint256)' "${VAULT}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
VAULT_USDC_RAW="$(cast call "${USDC}" 'balanceOf(address)(uint256)' "${VAULT}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
|
||||
ACCOUNTING_AWARE_SUPPORTED="0"
|
||||
if cast call "${VAULT}" 'maxFlashLoan(address)(uint256)' "${USDC}" --rpc-url "${ETHEREUM_MAINNET_RPC}" >/dev/null 2>&1; then
|
||||
ACCOUNTING_AWARE_SUPPORTED="1"
|
||||
fi
|
||||
|
||||
if [[ "${EXECUTE}" == "1" && "${SIGNER,,}" != "${OWNER,,}" ]]; then
|
||||
echo "EXECUTE=1 signer must be the vault owner" >&2
|
||||
echo " signer: ${SIGNER}" >&2
|
||||
echo " owner: ${OWNER}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
eval "$(
|
||||
python3 - "${POOL_CWUSDC_RAW}" "${POOL_USDC_RAW}" "${LENDER_USDC_RAW}" "${VAULT_CWUSDC_RAW}" "${VAULT_USDC_RAW}" <<'PY'
|
||||
from decimal import Decimal
|
||||
import sys
|
||||
|
||||
pool_cw, pool_usdc, lender_usdc, vault_cw, vault_usdc = map(int, sys.argv[1:])
|
||||
|
||||
def units(raw):
|
||||
return f"{Decimal(raw) / Decimal(10**6):f}"
|
||||
|
||||
def emit(k, v):
|
||||
print(f"{k}='{v}'")
|
||||
|
||||
emit("POOL_CWUSDC_UNITS", units(pool_cw))
|
||||
emit("POOL_USDC_UNITS", units(pool_usdc))
|
||||
emit("LENDER_USDC_UNITS", units(lender_usdc))
|
||||
emit("VAULT_CWUSDC_UNITS", units(vault_cw))
|
||||
emit("VAULT_USDC_UNITS", units(vault_usdc))
|
||||
emit("BALANCED_POOL_WITHDRAW_RAW", min(pool_cw, pool_usdc, vault_cw, max(vault_usdc - lender_usdc, 0)))
|
||||
PY
|
||||
)"
|
||||
|
||||
cat <<EOF
|
||||
Engine X legacy vault retirement plan
|
||||
mode: ${EXECUTE}
|
||||
vault: ${VAULT}
|
||||
owner: ${OWNER}
|
||||
signer: ${SIGNER:-n/a}
|
||||
recipient: ${RECIPIENT}
|
||||
accounting-aware API: ${ACCOUNTING_AWARE_SUPPORTED}
|
||||
accounted pool: ${POOL_CWUSDC_UNITS} cWUSDC / ${POOL_USDC_UNITS} USDC
|
||||
lender bucket: ${LENDER_USDC_UNITS} USDC
|
||||
actual balances: ${VAULT_CWUSDC_UNITS} cWUSDC / ${VAULT_USDC_UNITS} USDC
|
||||
EOF
|
||||
|
||||
if [[ "${ACCOUNTING_AWARE_SUPPORTED}" == "1" ]]; then
|
||||
cat <<EOF
|
||||
|
||||
Accounting-aware retirement commands:
|
||||
cast send "${VAULT}" 'withdrawPoolLiquidity(address,uint256,uint256)' "${RECIPIENT}" "${BALANCED_POOL_WITHDRAW_RAW}" "${BALANCED_POOL_WITHDRAW_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
cast send "${VAULT}" 'withdrawLenderUsdc(address,uint256)' "${RECIPIENT}" "${LENDER_USDC_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
EOF
|
||||
if [[ "${EXECUTE}" == "1" ]]; then
|
||||
cast send "${VAULT}" 'withdrawPoolLiquidity(address,uint256,uint256)' "${RECIPIENT}" "${BALANCED_POOL_WITHDRAW_RAW}" "${BALANCED_POOL_WITHDRAW_RAW}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
cast send "${VAULT}" 'withdrawLenderUsdc(address,uint256)' "${RECIPIENT}" "${LENDER_USDC_RAW}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
|
||||
This vault does not expose accounting-aware retirement APIs. Default behavior is report-only.
|
||||
Set ALLOW_STALE_ACCOUNTING_RETIREMENT=1 only when the vault is being permanently retired
|
||||
and no future proof will rely on its internal accounting.
|
||||
EOF
|
||||
|
||||
if [[ "${ALLOW_STALE_ACCOUNTING_RETIREMENT}" != "1" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
|
||||
Legacy full-token retirement commands:
|
||||
cast send "${VAULT}" 'withdraw(address,address,uint256)' "${CWUSDC}" "${RECIPIENT}" "${VAULT_CWUSDC_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
cast send "${VAULT}" 'withdraw(address,address,uint256)' "${USDC}" "${RECIPIENT}" "${VAULT_USDC_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
EOF
|
||||
|
||||
if [[ "${EXECUTE}" == "1" ]]; then
|
||||
cast send "${VAULT}" 'withdraw(address,address,uint256)' "${CWUSDC}" "${RECIPIENT}" "${VAULT_CWUSDC_RAW}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
cast send "${VAULT}" 'withdraw(address,address,uint256)' "${USDC}" "${RECIPIENT}" "${VAULT_USDC_RAW}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
fi
|
||||
269
scripts/deployment/run-engine-x-loop-proof.sh
Executable file
269
scripts/deployment/run-engine-x-loop-proof.sh
Executable file
@@ -0,0 +1,269 @@
|
||||
#!/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"
|
||||
|
||||
: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}"
|
||||
|
||||
VAULT="${ENGINE_X_VAULT:-0x9a22a3e272A364D64240dE6bda796FcA421cA7E9}"
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
XAUT="${XAUT_MAINNET:-0x68749665FF8D2d112Fa859AA293F07A622782F38}"
|
||||
LOOPS="${LOOPS:-1}"
|
||||
EXACT_OUTPUT_PER_LOOP_RAW="${EXACT_OUTPUT_PER_LOOP_RAW:-10000}"
|
||||
DEBT_RAW="${DEBT_RAW:-}"
|
||||
ROUNDING_RECEIVER="${ROUNDING_RECEIVER:-}"
|
||||
RECIPIENT="${RECIPIENT:-}"
|
||||
EXECUTE="${EXECUTE:-0}"
|
||||
STAMP="${ENGINE_X_LOOP_PROOF_STAMP:-$(date -u +%Y%m%dT%H%M%SZ)}"
|
||||
OUT_JSON="${OUT_JSON:-reports/status/engine-x-loop-proof-${STAMP}.json}"
|
||||
OUT_MD="${OUT_MD:-reports/status/engine-x-loop-proof-${STAMP}.md}"
|
||||
LATEST_JSON="${LATEST_JSON:-reports/status/engine-x-loop-proof-latest.json}"
|
||||
LATEST_MD="${LATEST_MD:-reports/status/engine-x-loop-proof-latest.md}"
|
||||
|
||||
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
|
||||
|
||||
RECIPIENT="${RECIPIENT:-${SIGNER}}"
|
||||
ROUNDING_RECEIVER="${ROUNDING_RECEIVER:-${SIGNER}}"
|
||||
PROOF_ID="${PROOF_ID:-$(cast keccak "dbis-engine-x:loop-proof:${SIGNER}:${RECIPIENT}:${EXACT_OUTPUT_PER_LOOP_RAW}:${LOOPS}:${STAMP}")}"
|
||||
ISO_HASH="${ISO_HASH:-$(cast keccak "dbis-engine-x:loop-proof:iso:${PROOF_ID}")}"
|
||||
AUDIT_HASH="${AUDIT_HASH:-$(cast keccak "dbis-engine-x:loop-proof:audit:${PROOF_ID}")}"
|
||||
PEG_HASH="${PEG_HASH:-$(cast keccak "dbis-engine-x:loop-proof:peg:${PROOF_ID}")}"
|
||||
|
||||
POOL_CWUSDC_RAW="$(cast call "${VAULT}" 'poolCwusdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
POOL_USDC_RAW="$(cast call "${VAULT}" 'poolUsdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
LENDER_USDC_RAW="$(cast call "${VAULT}" 'lenderUsdcAvailable()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
|
||||
if [[ -z "${DEBT_RAW}" ]]; then
|
||||
DEBT_RAW="$(
|
||||
python3 - "${POOL_CWUSDC_RAW}" "${POOL_USDC_RAW}" "${EXACT_OUTPUT_PER_LOOP_RAW}" "${LENDER_USDC_RAW}" <<'PY'
|
||||
import sys
|
||||
|
||||
reserve_cw = int(sys.argv[1])
|
||||
reserve_usdc = int(sys.argv[2])
|
||||
target = int(sys.argv[3])
|
||||
max_debt = int(sys.argv[4])
|
||||
|
||||
def amount_in(amount_out, reserve_in, reserve_out):
|
||||
if amount_out <= 0 or reserve_in <= 0 or reserve_out <= amount_out:
|
||||
raise ValueError("bad getAmountIn inputs")
|
||||
return reserve_in * amount_out * 1000 // ((reserve_out - amount_out) * 997) + 1
|
||||
|
||||
def amount_out(amount_in, reserve_in, reserve_out):
|
||||
if amount_in <= 0 or reserve_in <= 0 or reserve_out <= 0:
|
||||
raise ValueError("bad getAmountOut inputs")
|
||||
return amount_in * 997 * reserve_out // (reserve_in * 1000 + amount_in * 997)
|
||||
|
||||
for debt in range(1, max_debt + 1):
|
||||
cw_in = amount_in(debt, reserve_cw, reserve_usdc)
|
||||
cw_reserve_after_in = reserve_cw + cw_in
|
||||
usdc_reserve_after_out = reserve_usdc - debt
|
||||
cw_out = amount_out(debt, usdc_reserve_after_out, cw_reserve_after_in)
|
||||
if cw_out >= target:
|
||||
print(debt)
|
||||
raise SystemExit(0)
|
||||
raise SystemExit("no debt size can reach exact output within lender bucket")
|
||||
PY
|
||||
)"
|
||||
fi
|
||||
|
||||
mapfile -t PREVIEW < <(
|
||||
cast call "${VAULT}" \
|
||||
'previewVirtualProof(uint256,uint256)(uint256,uint256,uint256,uint256,uint256,uint256,uint256)' \
|
||||
"${DEBT_RAW}" "${LOOPS}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}'
|
||||
)
|
||||
|
||||
COLLATERAL_XAUT_RAW="${PREVIEW[0]}"
|
||||
CWUSDC_IN_PER_LOOP_RAW="${PREVIEW[1]}"
|
||||
CWUSDC_OUT_PER_LOOP_RAW="${PREVIEW[2]}"
|
||||
CWUSDC_LOSS_PER_LOOP_RAW="${PREVIEW[3]}"
|
||||
TOTAL_CWUSDC_IN_RAW="${PREVIEW[4]}"
|
||||
TOTAL_CWUSDC_OUT_RAW="${PREVIEW[5]}"
|
||||
TOTAL_NEUTRALIZED_RAW="${PREVIEW[6]}"
|
||||
EXACT_OUTPUT_TOTAL_RAW="$((EXACT_OUTPUT_PER_LOOP_RAW * LOOPS))"
|
||||
OUTPUT_ROUNDING_RAW="$((TOTAL_CWUSDC_OUT_RAW - EXACT_OUTPUT_TOTAL_RAW))"
|
||||
|
||||
if (( OUTPUT_ROUNDING_RAW < 0 )); then
|
||||
echo "Preview output is lower than requested exact output" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CWUSDC_BAL_RAW="$(cast call "${CWUSDC}" 'balanceOf(address)(uint256)' "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
XAUT_BAL_RAW="$(cast call "${XAUT}" 'balanceOf(address)(uint256)' "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
CWUSDC_ALLOWANCE_RAW="$(cast call "${CWUSDC}" 'allowance(address,address)(uint256)' "${SIGNER}" "${VAULT}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
XAUT_ALLOWANCE_RAW="$(cast call "${XAUT}" 'allowance(address,address)(uint256)' "${SIGNER}" "${VAULT}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
PROOF_USED="$(cast call "${VAULT}" 'usedProofIds(bytes32)(bool)' "${PROOF_ID}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | tr -d '[:space:]')"
|
||||
|
||||
if [[ "${PROOF_USED}" == "true" ]]; then
|
||||
echo "Proof ID is already used: ${PROOF_ID}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if (( CWUSDC_BAL_RAW < TOTAL_CWUSDC_IN_RAW )); then
|
||||
echo "Insufficient cWUSDC balance" >&2
|
||||
exit 1
|
||||
fi
|
||||
if (( XAUT_BAL_RAW < COLLATERAL_XAUT_RAW )); then
|
||||
echo "Insufficient XAUt balance" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CWUSDC_APPROVE_NEEDED=0
|
||||
XAUT_APPROVE_NEEDED=0
|
||||
if (( CWUSDC_ALLOWANCE_RAW < TOTAL_CWUSDC_IN_RAW )); then
|
||||
CWUSDC_APPROVE_NEEDED=1
|
||||
fi
|
||||
if (( XAUT_ALLOWANCE_RAW < COLLATERAL_XAUT_RAW )); then
|
||||
XAUT_APPROVE_NEEDED=1
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "${OUT_JSON}")"
|
||||
python3 - "${OUT_JSON}" "${OUT_MD}" "${LATEST_JSON}" "${LATEST_MD}" \
|
||||
"${EXECUTE}" "${STAMP}" "${VAULT}" "${SIGNER}" "${RECIPIENT}" "${ROUNDING_RECEIVER}" \
|
||||
"${LOOPS}" "${EXACT_OUTPUT_PER_LOOP_RAW}" "${DEBT_RAW}" "${PROOF_ID}" "${ISO_HASH}" "${AUDIT_HASH}" "${PEG_HASH}" \
|
||||
"${COLLATERAL_XAUT_RAW}" "${CWUSDC_IN_PER_LOOP_RAW}" "${CWUSDC_OUT_PER_LOOP_RAW}" "${CWUSDC_LOSS_PER_LOOP_RAW}" \
|
||||
"${TOTAL_CWUSDC_IN_RAW}" "${TOTAL_CWUSDC_OUT_RAW}" "${EXACT_OUTPUT_TOTAL_RAW}" "${OUTPUT_ROUNDING_RAW}" "${TOTAL_NEUTRALIZED_RAW}" \
|
||||
"${CWUSDC_BAL_RAW}" "${XAUT_BAL_RAW}" "${CWUSDC_ALLOWANCE_RAW}" "${XAUT_ALLOWANCE_RAW}" "${CWUSDC_APPROVE_NEEDED}" "${XAUT_APPROVE_NEEDED}" <<'PY'
|
||||
from decimal import Decimal
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
(
|
||||
out_json, out_md, latest_json, latest_md, execute, stamp, vault, signer, recipient, rounding_receiver,
|
||||
loops, exact_per_loop, debt, proof_id, iso_hash, audit_hash, peg_hash, collateral, cw_in, cw_out, loss,
|
||||
total_in, total_out, exact_total, rounding, neutralized, cw_bal, xaut_bal, cw_allow, xaut_allow,
|
||||
cw_approve, xaut_approve,
|
||||
) = sys.argv[1:]
|
||||
|
||||
def units(raw):
|
||||
return f"{Decimal(int(raw)) / Decimal(10**6):f}"
|
||||
|
||||
payload = {
|
||||
"schema": "engine-x-loop-proof/v1",
|
||||
"executed": execute == "1",
|
||||
"stamp": stamp,
|
||||
"vault": vault,
|
||||
"signer": signer,
|
||||
"recipient": recipient,
|
||||
"roundingReceiver": rounding_receiver,
|
||||
"loops": int(loops),
|
||||
"debtUsdcPerLoopRaw": debt,
|
||||
"debtUsdcPerLoop": units(debt),
|
||||
"exactOutputPerLoopRaw": exact_per_loop,
|
||||
"exactOutputPerLoop": units(exact_per_loop),
|
||||
"proofId": proof_id,
|
||||
"isoHash": iso_hash,
|
||||
"auditHash": audit_hash,
|
||||
"pegHash": peg_hash,
|
||||
"preview": {
|
||||
"collateralXautRaw": collateral,
|
||||
"collateralXaut": units(collateral),
|
||||
"cwusdcInPerLoopRaw": cw_in,
|
||||
"cwusdcInPerLoop": units(cw_in),
|
||||
"cwusdcOutPerLoopRaw": cw_out,
|
||||
"cwusdcOutPerLoop": units(cw_out),
|
||||
"cwusdcLossPerLoopRaw": loss,
|
||||
"cwusdcLossPerLoop": units(loss),
|
||||
"totalCwusdcInRaw": total_in,
|
||||
"totalCwusdcIn": units(total_in),
|
||||
"totalCwusdcOutRaw": total_out,
|
||||
"totalCwusdcOut": units(total_out),
|
||||
"exactOutputTotalRaw": exact_total,
|
||||
"exactOutputTotal": units(exact_total),
|
||||
"outputRoundingRaw": rounding,
|
||||
"outputRounding": units(rounding),
|
||||
"totalNeutralizedRaw": neutralized,
|
||||
"totalNeutralized": units(neutralized),
|
||||
},
|
||||
"balances": {
|
||||
"cwusdcRaw": cw_bal,
|
||||
"cwusdc": units(cw_bal),
|
||||
"xautRaw": xaut_bal,
|
||||
"xaut": units(xaut_bal),
|
||||
"cwusdcAllowanceRaw": cw_allow,
|
||||
"xautAllowanceRaw": xaut_allow,
|
||||
"cwusdcApproveNeeded": cw_approve == "1",
|
||||
"xautApproveNeeded": xaut_approve == "1",
|
||||
},
|
||||
}
|
||||
Path(out_json).write_text(json.dumps(payload, indent=2) + "\n")
|
||||
Path(latest_json).write_text(json.dumps(payload, indent=2) + "\n")
|
||||
lines = [
|
||||
"# Engine X Loop Proof",
|
||||
"",
|
||||
f"- Executed: `{payload['executed']}`",
|
||||
f"- Vault: `{vault}`",
|
||||
f"- Loops: `{loops}`",
|
||||
f"- Exact cWUSDC output per loop: `{payload['exactOutputPerLoop']}`",
|
||||
f"- Debt USDC per loop: `{payload['debtUsdcPerLoop']}`",
|
||||
f"- cWUSDC in/out per loop: `{payload['preview']['cwusdcInPerLoop']} / {payload['preview']['cwusdcOutPerLoop']}`",
|
||||
f"- Neutralized per loop: `{payload['preview']['cwusdcLossPerLoop']}`",
|
||||
f"- Total exact output: `{payload['preview']['exactOutputTotal']}`",
|
||||
f"- Proof ID: `{proof_id}`",
|
||||
f"- cWUSDC approval needed: `{payload['balances']['cwusdcApproveNeeded']}`",
|
||||
f"- XAUt approval needed: `{payload['balances']['xautApproveNeeded']}`",
|
||||
]
|
||||
Path(out_md).write_text("\n".join(lines) + "\n")
|
||||
Path(latest_md).write_text("\n".join(lines) + "\n")
|
||||
PY
|
||||
|
||||
cat <<EOF
|
||||
Engine X loop proof plan
|
||||
mode: ${EXECUTE}
|
||||
vault: ${VAULT}
|
||||
signer: ${SIGNER}
|
||||
recipient: ${RECIPIENT}
|
||||
loops: ${LOOPS}
|
||||
exact cWUSDC output per loop raw: ${EXACT_OUTPUT_PER_LOOP_RAW}
|
||||
debt USDC per loop raw: ${DEBT_RAW}
|
||||
cWUSDC in/out/loss per loop raw: ${CWUSDC_IN_PER_LOOP_RAW} / ${CWUSDC_OUT_PER_LOOP_RAW} / ${CWUSDC_LOSS_PER_LOOP_RAW}
|
||||
total exact output raw: ${EXACT_OUTPUT_TOTAL_RAW}
|
||||
total cWUSDC input raw: ${TOTAL_CWUSDC_IN_RAW}
|
||||
total neutralized raw: ${TOTAL_NEUTRALIZED_RAW}
|
||||
XAUt collateral raw: ${COLLATERAL_XAUT_RAW}
|
||||
proof id: ${PROOF_ID}
|
||||
cWUSDC approve needed: ${CWUSDC_APPROVE_NEEDED}
|
||||
XAUt approve needed: ${XAUT_APPROVE_NEEDED}
|
||||
report: ${OUT_JSON}
|
||||
EOF
|
||||
|
||||
if [[ "${EXECUTE}" != "1" ]]; then
|
||||
cat <<EOF
|
||||
|
||||
Dry-run only. To broadcast this exact loop proof:
|
||||
EXECUTE=1 LOOPS=${LOOPS} EXACT_OUTPUT_PER_LOOP_RAW=${EXACT_OUTPUT_PER_LOOP_RAW} \\
|
||||
PROOF_ID=${PROOF_ID} ISO_HASH=${ISO_HASH} AUDIT_HASH=${AUDIT_HASH} PEG_HASH=${PEG_HASH} \\
|
||||
bash scripts/deployment/run-engine-x-loop-proof.sh
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${CWUSDC_APPROVE_NEEDED}" == "1" ]]; then
|
||||
cast send "${CWUSDC}" 'approve(address,uint256)' "${VAULT}" "${TOTAL_CWUSDC_IN_RAW}" \
|
||||
--rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
fi
|
||||
if [[ "${XAUT_APPROVE_NEEDED}" == "1" ]]; then
|
||||
cast send "${XAUT}" 'approve(address,uint256)' "${VAULT}" "${COLLATERAL_XAUT_RAW}" \
|
||||
--rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
fi
|
||||
|
||||
cast send "${VAULT}" \
|
||||
'runVirtualProofExactOutTo(bytes32,uint256,uint256,address,uint256,address,bytes32,bytes32,bytes32)' \
|
||||
"${PROOF_ID}" "${DEBT_RAW}" "${LOOPS}" "${RECIPIENT}" "${EXACT_OUTPUT_TOTAL_RAW}" "${ROUNDING_RECEIVER}" \
|
||||
"${ISO_HASH}" "${AUDIT_HASH}" "${PEG_HASH}" \
|
||||
--rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
113
scripts/deployment/run-engine-x-univ3-public-swap-proof.sh
Executable file
113
scripts/deployment/run-engine-x-univ3-public-swap-proof.sh
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/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"
|
||||
|
||||
: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}"
|
||||
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}"
|
||||
FACTORY="${CHAIN_1_UNISWAP_V3_FACTORY:-0x1F98431c8aD98523631AE4a59f267346ea31F984}"
|
||||
ROUTER="${CHAIN_1_UNISWAP_V3_ROUTER:-0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45}"
|
||||
QUOTER="${CHAIN_1_UNISWAP_V3_QUOTER:-0x61fFE014bA17989E743c5F6cB21bF9697530B21e}"
|
||||
FEE="${ENGINE_X_UNIV3_FEE:-100}"
|
||||
DIRECTION="${DIRECTION:-USDC_TO_CWUSDC}"
|
||||
AMOUNT_IN_RAW="${AMOUNT_IN_RAW:-10000}"
|
||||
SLIPPAGE_BPS="${SLIPPAGE_BPS:-100}"
|
||||
DEADLINE_SECONDS="${DEADLINE_SECONDS:-600}"
|
||||
EXECUTE="${EXECUTE:-0}"
|
||||
|
||||
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
|
||||
|
||||
case "${DIRECTION}" in
|
||||
USDC_TO_CWUSDC)
|
||||
TOKEN_IN="${USDC}"
|
||||
TOKEN_OUT="${CWUSDC}"
|
||||
;;
|
||||
CWUSDC_TO_USDC)
|
||||
TOKEN_IN="${CWUSDC}"
|
||||
TOKEN_OUT="${USDC}"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported DIRECTION: ${DIRECTION}; use USDC_TO_CWUSDC or CWUSDC_TO_USDC" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
TOKEN0="$(printf '%s\n%s\n' "${CWUSDC}" "${USDC}" | tr '[:upper:]' '[:lower:]' | sort | sed -n '1p')"
|
||||
TOKEN1="$(printf '%s\n%s\n' "${CWUSDC}" "${USDC}" | tr '[:upper:]' '[:lower:]' | sort | sed -n '2p')"
|
||||
POOL="$(cast call "${FACTORY}" 'getPool(address,address,uint24)(address)' "${TOKEN0}" "${TOKEN1}" "${FEE}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)"
|
||||
|
||||
if [[ "${POOL}" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
cat <<EOF
|
||||
Engine X UniV3 public swap proof blocked
|
||||
pool: not deployed
|
||||
fee: ${FEE}
|
||||
required first: pnpm engine-x:indexed-lp-migration against an upgraded vault
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
SLOT0="$(cast call "${POOL}" 'slot0()(uint160,int24,uint16,uint16,uint16,uint8,bool)' --rpc-url "${ETHEREUM_MAINNET_RPC}")"
|
||||
LIQUIDITY="$(cast call "${POOL}" 'liquidity()(uint128)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')"
|
||||
QUOTE_OUT_RAW="$(
|
||||
cast call "${QUOTER}" \
|
||||
'quoteExactInputSingle((address,address,uint256,uint24,uint160))(uint256,uint160,uint32,uint256)' \
|
||||
"(${TOKEN_IN},${TOKEN_OUT},${AMOUNT_IN_RAW},${FEE},0)" \
|
||||
--rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || true
|
||||
)"
|
||||
QUOTE_OUT_RAW="${QUOTE_OUT_RAW:-0}"
|
||||
MIN_OUT_RAW="$((QUOTE_OUT_RAW * (10000 - SLIPPAGE_BPS) / 10000))"
|
||||
DEADLINE="$(( $(date +%s) + DEADLINE_SECONDS ))"
|
||||
|
||||
cat <<EOF
|
||||
Engine X UniV3 public swap proof plan
|
||||
mode: ${EXECUTE}
|
||||
signer: ${SIGNER}
|
||||
direction: ${DIRECTION}
|
||||
pool: ${POOL}
|
||||
fee: ${FEE}
|
||||
slot0: ${SLOT0}
|
||||
liquidity: ${LIQUIDITY}
|
||||
token in: ${TOKEN_IN}
|
||||
token out: ${TOKEN_OUT}
|
||||
amount in raw: ${AMOUNT_IN_RAW}
|
||||
quote out raw: ${QUOTE_OUT_RAW}
|
||||
min out raw: ${MIN_OUT_RAW}
|
||||
EOF
|
||||
|
||||
if [[ "${QUOTE_OUT_RAW}" == "0" ]]; then
|
||||
echo "Quote is zero or unavailable; no broadcast command emitted."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${EXECUTE}" != "1" ]]; then
|
||||
cat <<EOF
|
||||
|
||||
Dry-run only. Review commands:
|
||||
cast send "${TOKEN_IN}" 'approve(address,uint256)' "${ROUTER}" "${AMOUNT_IN_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
cast send "${ROUTER}" 'exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160))(uint256)' "(${TOKEN_IN},${TOKEN_OUT},${FEE},${SIGNER},${DEADLINE},${AMOUNT_IN_RAW},${MIN_OUT_RAW},0)" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY"
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cast send "${TOKEN_IN}" 'approve(address,uint256)' "${ROUTER}" "${AMOUNT_IN_RAW}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
cast send "${ROUTER}" 'exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160))(uint256)' \
|
||||
"(${TOKEN_IN},${TOKEN_OUT},${FEE},${SIGNER},${DEADLINE},${AMOUNT_IN_RAW},${MIN_OUT_RAW},0)" \
|
||||
--rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}"
|
||||
@@ -85,7 +85,7 @@ _lpr_export_from_private_key
|
||||
# 4b. Strip trailing CR/LF from RPC URL vars (editor mistakes; breaks cast/curl)
|
||||
for _lpr_k in RPC_URL_138 RPC_URL CHAIN138_RPC CHAIN138_RPC_URL CHAIN_138_RPC_URL \
|
||||
TOKEN_AGG_CHAIN138_RPC_URL TOKEN_AGGREGATION_CHAIN138_RPC_URL TOKEN_AGGREGATION_PMM_RPC_URL \
|
||||
ETHEREUM_MAINNET_RPC \
|
||||
INFURA_PROJECT_ID INFURA_API_KEY INFURA_PROJECT_SECRET ETHEREUM_MAINNET_RPC ETH_MAINNET_RPC_URL \
|
||||
XDC_PARENTNET_URL PARENTNET_URL SUBNET_URL XDC_ZERO_PEER_RPC_URL \
|
||||
RPC_URL_138_PUBLIC GNOSIS_MAINNET_RPC GNOSIS_RPC CRONOS_RPC_URL CRONOS_RPC \
|
||||
CELO_MAINNET_RPC CELO_RPC WEMIX_RPC WEMIX_MAINNET_RPC BSC_RPC_URL \
|
||||
@@ -110,6 +110,27 @@ for _lpr_id in 1 10 25 56 100 137 138 1111 8453 42161 42220 43114 651940; do
|
||||
done
|
||||
unset _lpr_k _lpr_v _lpr_id 2>/dev/null || true
|
||||
|
||||
# 4d. Ethereum RPC alias (dotenv often sets ETH_MAINNET_RPC_URL only)
|
||||
export ETHEREUM_MAINNET_RPC="${ETHEREUM_MAINNET_RPC:-${ETH_MAINNET_RPC_URL:-}}"
|
||||
export RPC_URL_MAINNET="${RPC_URL_MAINNET:-${ETHEREUM_MAINNET_RPC:-}}"
|
||||
|
||||
# 4e. Optional Infura: when INFURA_PROJECT_ID or INFURA_API_KEY is set, fill unset public-chain RPCs
|
||||
# (Infura dashboard labels this value "API Key"; JSON-RPC path is always .../v3/<that_value>.)
|
||||
_lpr_inf="${INFURA_PROJECT_ID:-${INFURA_API_KEY:-}}"
|
||||
if [[ -n "$_lpr_inf" ]]; then
|
||||
[[ -z "${ETHEREUM_MAINNET_RPC:-}" ]] && export ETHEREUM_MAINNET_RPC="https://mainnet.infura.io/v3/${_lpr_inf}"
|
||||
export RPC_URL_MAINNET="${RPC_URL_MAINNET:-${ETHEREUM_MAINNET_RPC:-}}"
|
||||
[[ -z "${POLYGON_MAINNET_RPC:-}" ]] && [[ -z "${POLYGON_RPC_URL:-}" ]] && export POLYGON_MAINNET_RPC="https://polygon-mainnet.infura.io/v3/${_lpr_inf}"
|
||||
[[ -z "${ARBITRUM_MAINNET_RPC:-}" ]] && [[ -z "${ARBITRUM_RPC_URL:-}" ]] && export ARBITRUM_MAINNET_RPC="https://arbitrum-mainnet.infura.io/v3/${_lpr_inf}"
|
||||
[[ -z "${OPTIMISM_MAINNET_RPC:-}" ]] && [[ -z "${OPTIMISM_RPC_URL:-}" ]] && export OPTIMISM_MAINNET_RPC="https://optimism-mainnet.infura.io/v3/${_lpr_inf}"
|
||||
[[ -z "${BASE_MAINNET_RPC:-}" ]] && [[ -z "${BASE_RPC_URL:-}" ]] && export BASE_MAINNET_RPC="https://base-mainnet.infura.io/v3/${_lpr_inf}"
|
||||
[[ -z "${AVALANCHE_MAINNET_RPC:-}" ]] && [[ -z "${AVALANCHE_RPC_URL:-}" ]] && export AVALANCHE_MAINNET_RPC="https://avalanche-mainnet.infura.io/v3/${_lpr_inf}"
|
||||
[[ -z "${BSC_RPC_URL:-}" ]] && [[ -z "${BSC_MAINNET_RPC:-}" ]] && export BSC_RPC_URL="https://bnb-mainnet.infura.io/v3/${_lpr_inf}"
|
||||
[[ -z "${GNOSIS_MAINNET_RPC:-}" ]] && [[ -z "${GNOSIS_RPC_URL:-}" ]] && [[ -z "${GNOSIS_RPC:-}" ]] && export GNOSIS_MAINNET_RPC="https://gnosis-mainnet.infura.io/v3/${_lpr_inf}"
|
||||
[[ -z "${CELO_MAINNET_RPC:-}" ]] && [[ -z "${CELO_RPC_URL:-}" ]] && [[ -z "${CELO_RPC:-}" ]] && export CELO_MAINNET_RPC="https://celo-mainnet.infura.io/v3/${_lpr_inf}"
|
||||
fi
|
||||
unset _lpr_inf
|
||||
|
||||
# 5. Contract addresses from master JSON (config/smart-contracts-master.json) when not set by .env
|
||||
[[ -f "${PROJECT_ROOT}/scripts/lib/load-contract-addresses.sh" ]] && source "${PROJECT_ROOT}/scripts/lib/load-contract-addresses.sh" 2>/dev/null || true
|
||||
|
||||
|
||||
128
scripts/verify/build-engine-x-audit-manifest.sh
Executable file
128
scripts/verify/build-engine-x-audit-manifest.sh
Executable file
@@ -0,0 +1,128 @@
|
||||
#!/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"
|
||||
|
||||
OUT_DIR="${OUT_DIR:-reports/status}"
|
||||
MANIFEST_JSON="${MANIFEST_JSON:-${OUT_DIR}/engine-x-audit-manifest-latest.json}"
|
||||
MANIFEST_MD="${MANIFEST_MD:-${OUT_DIR}/engine-x-audit-manifest-latest.md}"
|
||||
mkdir -p "${PROJECT_ROOT}/${OUT_DIR}"
|
||||
|
||||
V2_ABI="${OUT_DIR}/engine-x-v2-vault-abi-latest.json"
|
||||
INDEXED_ABI="${OUT_DIR}/engine-x-indexed-liquidity-vault-abi-latest.json"
|
||||
BORROWER_ABI="${OUT_DIR}/engine-x-flash-proof-borrower-abi-latest.json"
|
||||
|
||||
pushd "${PROJECT_ROOT}/smom-dbis-138" >/dev/null
|
||||
forge inspect contracts/flash/DBISEngineXVirtualBatchVault.sol:DBISEngineXVirtualBatchVault abi >"${PROJECT_ROOT}/${V2_ABI}"
|
||||
forge inspect contracts/flash/DBISEngineXIndexedLiquidityVault.sol:DBISEngineXIndexedLiquidityVault abi >"${PROJECT_ROOT}/${INDEXED_ABI}"
|
||||
forge inspect contracts/flash/DBISEngineXFlashProofBorrower.sol:DBISEngineXFlashProofBorrower abi >"${PROJECT_ROOT}/${BORROWER_ABI}"
|
||||
popd >/dev/null
|
||||
|
||||
python3 - "${PROJECT_ROOT}" "${MANIFEST_JSON}" "${MANIFEST_MD}" "${V2_ABI}" "${INDEXED_ABI}" "${BORROWER_ABI}" <<'PY'
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
root = Path(sys.argv[1])
|
||||
manifest_json = root / sys.argv[2]
|
||||
manifest_md = root / sys.argv[3]
|
||||
abi_paths = [Path(p) for p in sys.argv[4:]]
|
||||
|
||||
def sha256(path: Path) -> str:
|
||||
return "0x" + hashlib.sha256((root / path).read_bytes()).hexdigest()
|
||||
|
||||
contracts = [
|
||||
{
|
||||
"name": "DBISEngineXVirtualBatchVault",
|
||||
"source": "smom-dbis-138/contracts/flash/DBISEngineXVirtualBatchVault.sol",
|
||||
"abi": str(abi_paths[0]),
|
||||
"abiSha256": sha256(abi_paths[0]),
|
||||
"constructorArgs": [
|
||||
"cWUSDC",
|
||||
"USDC",
|
||||
"XAUt",
|
||||
"owner",
|
||||
"surplusReceiver",
|
||||
"xautUsdPrice6",
|
||||
"ltvBps",
|
||||
"maxRoundTripLossBps",
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "DBISEngineXIndexedLiquidityVault",
|
||||
"source": "smom-dbis-138/contracts/flash/DBISEngineXIndexedLiquidityVault.sol",
|
||||
"abi": str(abi_paths[1]),
|
||||
"abiSha256": sha256(abi_paths[1]),
|
||||
"constructorArgs": [
|
||||
"cWUSDC",
|
||||
"USDC",
|
||||
"uniV3Pool",
|
||||
"owner",
|
||||
"maxAbsTick",
|
||||
"minLiquidity",
|
||||
"maxProofSwapAmount",
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "DBISEngineXFlashProofBorrower",
|
||||
"source": "smom-dbis-138/contracts/flash/DBISEngineXFlashProofBorrower.sol",
|
||||
"abi": str(abi_paths[2]),
|
||||
"abiSha256": sha256(abi_paths[2]),
|
||||
"constructorArgs": ["lender", "USDC", "owner"],
|
||||
},
|
||||
]
|
||||
|
||||
payload = {
|
||||
"schema": "engine-x-audit-manifest/v1",
|
||||
"generatedAt": datetime.now(timezone.utc).isoformat(),
|
||||
"contracts": contracts,
|
||||
"operatorScripts": [
|
||||
"scripts/deployment/deploy-engine-x-v2-mainnet.sh",
|
||||
"scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh",
|
||||
"scripts/deployment/deploy-engine-x-indexed-liquidity-vault-mainnet.sh",
|
||||
"scripts/deployment/run-engine-x-univ3-public-swap-proof.sh",
|
||||
"scripts/deployment/record-engine-x-indexed-liquidity-proof.sh",
|
||||
"scripts/deployment/retire-engine-x-legacy-vault.sh",
|
||||
"scripts/verify/check-engine-x-public-indexed-readiness.sh",
|
||||
],
|
||||
"reports": [
|
||||
"reports/status/mainnet-engine-x-indexed-liquidity-upgrade-plan-20260507.md",
|
||||
"reports/status/mainnet-cwusdc-cross-protocol-public-lp-proof-plan-20260507.md",
|
||||
"reports/status/engine-x-public-indexed-readiness-latest.json",
|
||||
],
|
||||
"proofRequirements": [
|
||||
"verified deployed source",
|
||||
"constructor args",
|
||||
"public UniV3 liquidity tx hash",
|
||||
"actual public cWUSDC/USDC swap tx hash",
|
||||
"pre/post pool slot0 and liquidity",
|
||||
"ISO 20022-style hash",
|
||||
"audit envelope hash",
|
||||
"peg proof hash",
|
||||
],
|
||||
}
|
||||
|
||||
manifest_json.write_text(json.dumps(payload, indent=2) + "\n")
|
||||
lines = [
|
||||
"# Engine X Audit Manifest",
|
||||
"",
|
||||
f"- Generated: `{payload['generatedAt']}`",
|
||||
"",
|
||||
"## Contracts",
|
||||
]
|
||||
for c in contracts:
|
||||
lines.append(f"- `{c['name']}`: `{c['source']}` ABI `{c['abi']}` hash `{c['abiSha256']}`")
|
||||
lines.extend(["", "## Operator Scripts"])
|
||||
lines.extend(f"- `{s}`" for s in payload["operatorScripts"])
|
||||
lines.extend(["", "## Proof Requirements"])
|
||||
lines.extend(f"- {r}" for r in payload["proofRequirements"])
|
||||
manifest_md.write_text("\n".join(lines) + "\n")
|
||||
print(manifest_json)
|
||||
print(manifest_md)
|
||||
PY
|
||||
169
scripts/verify/check-engine-x-public-indexed-readiness.sh
Executable file
169
scripts/verify/check-engine-x-public-indexed-readiness.sh
Executable file
@@ -0,0 +1,169 @@
|
||||
#!/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"
|
||||
|
||||
: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}"
|
||||
|
||||
OUT_JSON="${OUT_JSON:-reports/status/engine-x-public-indexed-readiness-latest.json}"
|
||||
OUT_MD="${OUT_MD:-reports/status/engine-x-public-indexed-readiness-latest.md}"
|
||||
CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}"
|
||||
USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}"
|
||||
V2_PAIR="${MAINNET_CWUSDC_USDC_UNIV2_PAIR:-0xC28706F899266b36BC43cc072b3a921BDf2C48D9}"
|
||||
V3_FACTORY="${CHAIN_1_UNISWAP_V3_FACTORY:-0x1F98431c8aD98523631AE4a59f267346ea31F984}"
|
||||
V3_FEE="${ENGINE_X_UNIV3_FEE:-100}"
|
||||
VAULT="${DBIS_ENGINE_X_V2_VAULT:-${ENGINE_X_VAULT:-0x9a22a3e272A364D64240dE6bda796FcA421cA7E9}}"
|
||||
INDEXED_VAULT="${ENGINE_X_INDEXED_LIQUIDITY_VAULT:-}"
|
||||
|
||||
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
||||
DEPLOYER="$(cast wallet address --private-key "${PRIVATE_KEY}")"
|
||||
else
|
||||
DEPLOYER="${DEPLOYER_ADDRESS:-}"
|
||||
fi
|
||||
|
||||
TOKEN0="$(printf '%s\n%s\n' "${CWUSDC}" "${USDC}" | tr '[:upper:]' '[:lower:]' | sort | sed -n '1p')"
|
||||
TOKEN1="$(printf '%s\n%s\n' "${CWUSDC}" "${USDC}" | tr '[:upper:]' '[:lower:]' | sort | sed -n '2p')"
|
||||
V3_POOL="$(cast call "${V3_FACTORY}" 'getPool(address,address,uint24)(address)' "${TOKEN0}" "${TOKEN1}" "${V3_FEE}" --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | grep -oE '0x[a-fA-F0-9]{40}' | head -1 || true)"
|
||||
V3_POOL="${V3_POOL:-0x0000000000000000000000000000000000000000}"
|
||||
|
||||
ACCOUNTING_AWARE="0"
|
||||
cast call "${VAULT}" 'maxFlashLoan(address)(uint256)' "${USDC}" --rpc-url "${ETHEREUM_MAINNET_RPC}" >/dev/null 2>&1 && ACCOUNTING_AWARE="1"
|
||||
|
||||
POOL_CWUSDC_RAW="$(cast call "${VAULT}" 'poolCwusdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)"
|
||||
POOL_USDC_RAW="$(cast call "${VAULT}" 'poolUsdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)"
|
||||
LENDER_USDC_RAW="$(cast call "${VAULT}" 'lenderUsdcAvailable()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)"
|
||||
V2_RESERVES="$(cast call "${V2_PAIR}" 'getReserves()(uint112,uint112,uint32)' --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null || true)"
|
||||
V3_SLOT0=""
|
||||
V3_LIQUIDITY="0"
|
||||
V3_TOKEN0_BALANCE="0"
|
||||
V3_TOKEN1_BALANCE="0"
|
||||
if [[ "${V3_POOL}" != "0x0000000000000000000000000000000000000000" ]]; then
|
||||
V3_SLOT0="$(cast call "${V3_POOL}" 'slot0()(uint160,int24,uint16,uint16,uint16,uint8,bool)' --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null || true)"
|
||||
V3_LIQUIDITY="$(cast call "${V3_POOL}" 'liquidity()(uint128)' --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)"
|
||||
V3_TOKEN0_BALANCE="$(cast call "${TOKEN0}" 'balanceOf(address)(uint256)' "${V3_POOL}" --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)"
|
||||
V3_TOKEN1_BALANCE="$(cast call "${TOKEN1}" 'balanceOf(address)(uint256)' "${V3_POOL}" --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)"
|
||||
fi
|
||||
WALLET_CWUSDC_RAW="0"
|
||||
WALLET_USDC_RAW="0"
|
||||
if [[ -n "${DEPLOYER}" ]]; then
|
||||
WALLET_CWUSDC_RAW="$(cast call "${CWUSDC}" 'balanceOf(address)(uint256)' "${DEPLOYER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)"
|
||||
WALLET_USDC_RAW="$(cast call "${USDC}" 'balanceOf(address)(uint256)' "${DEPLOYER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)"
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "${OUT_JSON}")"
|
||||
python3 - "${OUT_JSON}" "${OUT_MD}" \
|
||||
"${VAULT}" "${ACCOUNTING_AWARE}" "${POOL_CWUSDC_RAW}" "${POOL_USDC_RAW}" "${LENDER_USDC_RAW}" \
|
||||
"${INDEXED_VAULT}" "${V3_POOL}" "${V3_SLOT0}" "${V3_LIQUIDITY}" "${V3_TOKEN0_BALANCE}" "${V3_TOKEN1_BALANCE}" \
|
||||
"${V2_PAIR}" "${V2_RESERVES}" "${DEPLOYER:-}" "${WALLET_CWUSDC_RAW}" "${WALLET_USDC_RAW}" <<'PY'
|
||||
from decimal import Decimal
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
(
|
||||
out_json,
|
||||
out_md,
|
||||
vault,
|
||||
accounting_aware,
|
||||
pool_cw,
|
||||
pool_usdc,
|
||||
lender_usdc,
|
||||
indexed_vault,
|
||||
v3_pool,
|
||||
v3_slot0,
|
||||
v3_liquidity,
|
||||
v3_token0,
|
||||
v3_token1,
|
||||
v2_pair,
|
||||
v2_reserves,
|
||||
deployer,
|
||||
wallet_cw,
|
||||
wallet_usdc,
|
||||
) = sys.argv[1:]
|
||||
|
||||
def units(raw):
|
||||
return str(Decimal(int(raw or 0)) / Decimal(10**6))
|
||||
|
||||
v2_nums = [int(x) for x in re.findall(r"\b\d+\b", v2_reserves or "")]
|
||||
blockers = []
|
||||
if accounting_aware != "1":
|
||||
blockers.append("configured Engine X vault is not accounting-aware")
|
||||
if v3_pool.lower() == "0x0000000000000000000000000000000000000000":
|
||||
blockers.append("UniV3 cWUSDC/USDC public pool is not deployed")
|
||||
if int(v3_liquidity or 0) == 0:
|
||||
blockers.append("UniV3 public pool has zero active liquidity")
|
||||
if not indexed_vault:
|
||||
blockers.append("Engine X indexed-liquidity proof vault is not configured")
|
||||
if not deployer:
|
||||
blockers.append("deployer address unavailable")
|
||||
|
||||
payload = {
|
||||
"schema": "engine-x-public-indexed-readiness/v1",
|
||||
"generatedAt": datetime.now(timezone.utc).isoformat(),
|
||||
"summary": {
|
||||
"readyForPublicIndexedProof": not blockers,
|
||||
"blockers": blockers,
|
||||
},
|
||||
"engineXV2Vault": {
|
||||
"address": vault,
|
||||
"accountingAware": accounting_aware == "1",
|
||||
"poolCwusdcRaw": pool_cw,
|
||||
"poolCwusdc": units(pool_cw),
|
||||
"poolUsdcRaw": pool_usdc,
|
||||
"poolUsdc": units(pool_usdc),
|
||||
"lenderUsdcRaw": lender_usdc,
|
||||
"lenderUsdc": units(lender_usdc),
|
||||
},
|
||||
"indexedProofVault": {"address": indexed_vault or None},
|
||||
"uniV3": {
|
||||
"pool": v3_pool,
|
||||
"slot0": v3_slot0 or None,
|
||||
"liquidity": v3_liquidity,
|
||||
"token0BalanceRaw": v3_token0,
|
||||
"token1BalanceRaw": v3_token1,
|
||||
"token0Balance": units(v3_token0),
|
||||
"token1Balance": units(v3_token1),
|
||||
},
|
||||
"uniV2": {
|
||||
"pair": v2_pair,
|
||||
"rawReserves": v2_nums[:2],
|
||||
},
|
||||
"deployer": {
|
||||
"address": deployer or None,
|
||||
"cwusdcRaw": wallet_cw,
|
||||
"cwusdc": units(wallet_cw),
|
||||
"usdcRaw": wallet_usdc,
|
||||
"usdc": units(wallet_usdc),
|
||||
},
|
||||
}
|
||||
|
||||
Path(out_json).write_text(json.dumps(payload, indent=2) + "\n")
|
||||
lines = [
|
||||
"# Engine X Public Indexed Readiness",
|
||||
"",
|
||||
f"- Generated: `{payload['generatedAt']}`",
|
||||
f"- Ready: `{payload['summary']['readyForPublicIndexedProof']}`",
|
||||
f"- Engine X v2 vault: `{vault}`",
|
||||
f"- Accounting-aware: `{payload['engineXV2Vault']['accountingAware']}`",
|
||||
f"- UniV3 pool: `{v3_pool}`",
|
||||
f"- UniV3 liquidity: `{v3_liquidity}`",
|
||||
f"- Indexed proof vault: `{indexed_vault or 'not configured'}`",
|
||||
f"- Deployer USDC: `{payload['deployer']['usdc']}`",
|
||||
f"- Deployer cWUSDC: `{payload['deployer']['cwusdc']}`",
|
||||
"",
|
||||
"## Blockers",
|
||||
]
|
||||
if blockers:
|
||||
lines.extend(f"- {b}" for b in blockers)
|
||||
else:
|
||||
lines.append("- none")
|
||||
Path(out_md).write_text("\n".join(lines) + "\n")
|
||||
print(out_json)
|
||||
print(out_md)
|
||||
PY
|
||||
@@ -131,6 +131,23 @@ def funding_status(required_raw: int, available_raw: int, decimals: int = 6) ->
|
||||
}
|
||||
|
||||
|
||||
def public_reseed_status(
|
||||
base_shortfall_raw: int,
|
||||
quote_shortfall_raw: int,
|
||||
base_funding: dict,
|
||||
quote_funding: dict,
|
||||
) -> str:
|
||||
if base_shortfall_raw == 0 and quote_shortfall_raw == 0:
|
||||
return "ready"
|
||||
if quote_shortfall_raw > 0 and not quote_funding["covered"]:
|
||||
return "needs_usdc"
|
||||
if base_shortfall_raw > 0 and not base_funding["covered"]:
|
||||
return "needs_cwusdc"
|
||||
if quote_shortfall_raw > 0:
|
||||
return "needs_quote_side_repair"
|
||||
return "needs_base_side_reseed"
|
||||
|
||||
|
||||
def build_plan(snapshot: dict, policy: dict, env_values: dict[str, str], holder_override: str) -> dict:
|
||||
rpc_url = resolve_env_value("ETHEREUM_MAINNET_RPC", env_values)
|
||||
if not rpc_url:
|
||||
@@ -217,6 +234,11 @@ def build_plan(snapshot: dict, policy: dict, env_values: dict[str, str], holder_
|
||||
if any("defended quote query failed" in warning for warning in warnings):
|
||||
blockers.append("defended pool quote preview reverted; set MIN_BASE_OUT_RAW manually before any quote-in trade")
|
||||
|
||||
public_deviation_bps = Decimal(str(summary["publicPairDeviationBps"]))
|
||||
public_price_distorted = public_deviation_bps >= Decimal(str(policy["thresholds"]["warnDeviationBps"]))
|
||||
if public_price_distorted:
|
||||
warnings.append("public pair is materially asymmetric; plain addLiquidity follows the current reserve ratio and will not repair price")
|
||||
|
||||
operator_commands = {
|
||||
"rerunPreflight": "bash scripts/verify/snapshot-mainnet-cwusdc-usdc-preflight.sh",
|
||||
"rerunPlan": "bash scripts/verify/plan-mainnet-cwusdc-usdc-repeg.sh",
|
||||
@@ -272,7 +294,42 @@ def build_plan(snapshot: dict, policy: dict, env_values: dict[str, str], holder_
|
||||
]
|
||||
)
|
||||
|
||||
recommended_actions = [
|
||||
if (
|
||||
public_base_shortfall_raw > 0
|
||||
or public_quote_shortfall_raw > 0
|
||||
or public_price_distorted
|
||||
):
|
||||
operator_commands["publicPairRepairGuidance"] = command_block(
|
||||
[
|
||||
"# Public cWUSDC/USDC is one-sided or price-distorted.",
|
||||
"# Do not use addLiquidity by itself as a price repair when only one reserve side is short.",
|
||||
"# Use official Mainnet USDC for a quote-side repair swap first, then add balanced",
|
||||
"# liquidity only after the reserve-implied price is back inside policy gates.",
|
||||
"bash scripts/verify/snapshot-mainnet-cwusdc-usdc-preflight.sh",
|
||||
"bash scripts/verify/plan-mainnet-cwusdc-usdc-repeg.sh",
|
||||
]
|
||||
)
|
||||
|
||||
public_action_status = public_reseed_status(
|
||||
public_base_shortfall_raw,
|
||||
public_quote_shortfall_raw,
|
||||
public_base_funding,
|
||||
public_quote_funding,
|
||||
)
|
||||
needs_external_funding = (
|
||||
not defended_quote_funding["covered"]
|
||||
or not public_base_funding["covered"]
|
||||
or not public_quote_funding["covered"]
|
||||
)
|
||||
recommended_next_action = "execute_verified_repair"
|
||||
if public_quote_shortfall_raw > 0 and not public_quote_funding["covered"]:
|
||||
recommended_next_action = "acquire_official_mainnet_usdc"
|
||||
elif public_quote_shortfall_raw > 0 or public_price_distorted:
|
||||
recommended_next_action = "run_quote_side_public_pair_repair_before_balanced_liquidity"
|
||||
elif add_quote_raw > 0 and not defended_quote_funding["covered"]:
|
||||
recommended_next_action = "acquire_official_mainnet_usdc_for_defended_parity"
|
||||
|
||||
recommended_actions = [
|
||||
{
|
||||
"step": "fund_manager_for_one_max_cycle",
|
||||
"quoteAmountRaw": str(max_automated_raw),
|
||||
@@ -292,11 +349,11 @@ def build_plan(snapshot: dict, policy: dict, env_values: dict[str, str], holder_
|
||||
"baseAmountUnits": str(normalize_units(public_base_shortfall_raw)),
|
||||
"quoteAmountRaw": str(public_quote_shortfall_raw),
|
||||
"quoteAmountUnits": str(normalize_units(public_quote_shortfall_raw)),
|
||||
"status": (
|
||||
"ready"
|
||||
if public_base_shortfall_raw == 0
|
||||
or (public_base_funding["covered"] and public_quote_funding["covered"])
|
||||
else "needs_inventory"
|
||||
"status": public_action_status,
|
||||
"guidance": (
|
||||
"Use quote-side repair before balanced liquidity; plain addLiquidity cannot repair an asymmetric public pair."
|
||||
if public_quote_shortfall_raw > 0 or public_price_distorted
|
||||
else "Use addLiquidity only after price and reserve gates pass."
|
||||
),
|
||||
},
|
||||
]
|
||||
@@ -304,6 +361,23 @@ def build_plan(snapshot: dict, policy: dict, env_values: dict[str, str], holder_
|
||||
return {
|
||||
"generatedAt": datetime.now(timezone.utc).isoformat(),
|
||||
"mode": "read_only_repeg_plan",
|
||||
"summary": {
|
||||
"publicPairBaseReserveUnits": str(public_base_units),
|
||||
"publicPairQuoteReserveUnits": str(public_quote_units),
|
||||
"publicPairDeviationBps": str(summary["publicPairDeviationBps"]),
|
||||
"publicPolicyFloorBaseShortfallUnits": str(normalize_units(public_base_shortfall_raw)),
|
||||
"publicPolicyFloorQuoteShortfallUnits": str(normalize_units(public_quote_shortfall_raw)),
|
||||
"publicPairRepairRequiresQuoteSideAction": public_quote_shortfall_raw > 0 or public_price_distorted,
|
||||
"publicIndexedLpComplianceStatus": (
|
||||
"blocked_missing_quote_side_public_lp_evidence"
|
||||
if public_quote_shortfall_raw > 0 or public_price_distorted
|
||||
else "ready_for_evidence_review"
|
||||
),
|
||||
"defendedAddQuoteUnits": str(normalize_units(add_quote_raw)),
|
||||
"managerFundingShortfallUnits": manager_funding["shortfallUnits"],
|
||||
"needsExternalFunding": needs_external_funding,
|
||||
"recommendedNextAction": recommended_next_action,
|
||||
},
|
||||
"snapshotPath": str(LATEST_SNAPSHOT),
|
||||
"policyPath": str(POLICY_PATH),
|
||||
"inferenceNotes": [
|
||||
@@ -370,11 +444,7 @@ def build_plan(snapshot: dict, policy: dict, env_values: dict[str, str], holder_
|
||||
"blockers": holder_blockers + blockers,
|
||||
"status": {
|
||||
"canFullyReachSimple1To1WithCurrentHolder": len(holder_blockers + blockers) == 0,
|
||||
"needsExternalFunding": (
|
||||
not defended_quote_funding["covered"]
|
||||
or not public_base_funding["covered"]
|
||||
or not public_quote_funding["covered"]
|
||||
),
|
||||
"needsExternalFunding": needs_external_funding,
|
||||
"canFundManagerFromCurrentHolder": holder_usdc_raw >= max_automated_raw if holder_state else None,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user