#!/usr/bin/env bash set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" CONTRACTS_DIR="$ROOT/MEV_Bot/mev-platform/contracts" CONFIG_PATH_DEFAULT="$ROOT/MEV_Bot/mev-platform/config.toml" OUTPUT_DEFAULT="$ROOT/reports/status/mev_execution_deploy_$(date +%Y%m%d_%H%M%S).json" RPC_URL="${RPC_URL:-}" PRIVATE_KEY="${PRIVATE_KEY:-}" FLASH_LOAN_PROVIDER="${FLASH_LOAN_PROVIDER:-}" AAVE_POOL="${AAVE_POOL:-}" TREASURY="${TREASURY:-}" EXECUTOR_OWNER="${EXECUTOR_OWNER:-}" PAUSE_ON_DEPLOY="${PAUSE_ON_DEPLOY:-1}" CONFIG_PATH="${MEV_CONFIG_PATH:-$CONFIG_PATH_DEFAULT}" OUTPUT_PATH="${MEV_EXECUTION_DEPLOY_OUTPUT:-$OUTPUT_DEFAULT}" DRY_RUN=0 usage() { cat <<'EOF' Usage: deploy-mev-execution-contracts.sh [options] Deploys the MEV ArbitrageExecutor and UniswapV2Adapter using the Foundry script already present in the MEV contracts workspace, then captures the deployed addresses into a JSON artifact and prints the exact config values that should be updated afterward. Required env or options: RPC_URL Target chain RPC URL PRIVATE_KEY Deployer private key FLASH_LOAN_PROVIDER Non-zero generic provider address compatible with the executor AAVE_POOL Aave V3 pool address; deploys the wrapper and uses it as provider Optional env or options: TREASURY Treasury address; defaults to deployer in Foundry script EXECUTOR_OWNER Optional final owner; deploy initiates 2-step ownership transfer PAUSE_ON_DEPLOY 1/true to pause immediately after deploy (default: 1) MEV_CONFIG_PATH Config file to inspect for current values MEV_EXECUTION_DEPLOY_OUTPUT Output JSON path Options: --rpc-url URL --private-key KEY --flash-loan-provider ADDRESS --aave-pool ADDRESS --treasury ADDRESS --executor-owner ADDRESS --pause-on-deploy BOOL --config PATH --output PATH --dry-run -h, --help EOF } while [[ $# -gt 0 ]]; do case "$1" in --rpc-url) RPC_URL="$2" shift 2 ;; --private-key) PRIVATE_KEY="$2" shift 2 ;; --flash-loan-provider) FLASH_LOAN_PROVIDER="$2" shift 2 ;; --aave-pool) AAVE_POOL="$2" shift 2 ;; --treasury) TREASURY="$2" shift 2 ;; --executor-owner) EXECUTOR_OWNER="$2" shift 2 ;; --pause-on-deploy) PAUSE_ON_DEPLOY="$2" shift 2 ;; --config) CONFIG_PATH="$2" shift 2 ;; --output) OUTPUT_PATH="$2" shift 2 ;; --dry-run) DRY_RUN=1 shift ;; -h|--help) usage exit 0 ;; *) echo "Unknown argument: $1" >&2 usage >&2 exit 2 ;; esac done require_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "Required command missing: $1" >&2 exit 2 } } require_cmd forge require_cmd jq require_cmd python3 require_cmd cast if [[ -z "$RPC_URL" ]]; then echo "RPC_URL is required" >&2 exit 2 fi if [[ -z "$PRIVATE_KEY" && "$DRY_RUN" -eq 0 ]]; then echo "PRIVATE_KEY is required unless --dry-run is used" >&2 exit 2 fi if [[ -z "$FLASH_LOAN_PROVIDER" && -z "$AAVE_POOL" ]]; then echo "Either FLASH_LOAN_PROVIDER or AAVE_POOL is required" >&2 exit 2 fi if [[ ! -d "$CONTRACTS_DIR" ]]; then echo "Contracts directory not found: $CONTRACTS_DIR" >&2 exit 2 fi mkdir -p "$(dirname "$OUTPUT_PATH")" CHAIN_ID="$(cast chain-id --rpc-url "$RPC_URL")" BROADCAST_PATH="$CONTRACTS_DIR/broadcast/Deploy.s.sol/$CHAIN_ID/run-latest.json" DEPLOYER_ADDRESS="" if [[ -n "$PRIVATE_KEY" ]]; then DEPLOYER_ADDRESS="$(cast wallet address --private-key "$PRIVATE_KEY")" fi cat <&2 exit 1 fi EXECUTOR_CONTRACT="$(jq -r '.transactions[] | select(.contractName=="ArbitrageExecutor") | .contractAddress' "$BROADCAST_PATH" | tail -1)" UNISWAP_V2_ADAPTER="$(jq -r '.transactions[] | select(.contractName=="UniswapV2Adapter") | .contractAddress' "$BROADCAST_PATH" | tail -1)" AAVE_WRAPPER="$(jq -r '.transactions[] | select(.contractName=="AaveV3FlashLoanProviderAdapter") | .contractAddress' "$BROADCAST_PATH" | tail -1)" if [[ -z "$EXECUTOR_CONTRACT" || "$EXECUTOR_CONTRACT" == "null" || -z "$UNISWAP_V2_ADAPTER" || "$UNISWAP_V2_ADAPTER" == "null" ]]; then echo "Could not extract deployed contract addresses from broadcast artifact" >&2 exit 1 fi if [[ "$AAVE_WRAPPER" == "null" ]]; then AAVE_WRAPPER="" fi if [[ -n "$AAVE_POOL" ]]; then EFFECTIVE_PROVIDER="$AAVE_WRAPPER" else EFFECTIVE_PROVIDER="$FLASH_LOAN_PROVIDER" fi ONCHAIN_OWNER="$(cast call "$EXECUTOR_CONTRACT" "owner()(address)" --rpc-url "$RPC_URL" 2>/dev/null || true)" ONCHAIN_PENDING_OWNER="$(cast call "$EXECUTOR_CONTRACT" "pendingOwner()(address)" --rpc-url "$RPC_URL" 2>/dev/null || true)" ONCHAIN_PAUSED="$(cast call "$EXECUTOR_CONTRACT" "paused()(bool)" --rpc-url "$RPC_URL" 2>/dev/null || true)" ONCHAIN_PROVIDER="$(cast call "$EXECUTOR_CONTRACT" "flashLoanProvider()(address)" --rpc-url "$RPC_URL" 2>/dev/null || true)" ONCHAIN_TREASURY="$(cast call "$EXECUTOR_CONTRACT" "treasury()(address)" --rpc-url "$RPC_URL" 2>/dev/null || true)" python3 - "$OUTPUT_PATH" "$CONFIG_PATH" "$BROADCAST_PATH" "$CHAIN_ID" "$EXECUTOR_CONTRACT" "$UNISWAP_V2_ADAPTER" "$EFFECTIVE_PROVIDER" "$TREASURY" "$DEPLOYER_ADDRESS" "$EXECUTOR_OWNER" "$PAUSE_ON_DEPLOY" "$ONCHAIN_OWNER" "$ONCHAIN_PENDING_OWNER" "$ONCHAIN_PAUSED" "$ONCHAIN_PROVIDER" "$ONCHAIN_TREASURY" "$AAVE_POOL" "$AAVE_WRAPPER" <<'PY' import json import sys from pathlib import Path output_path = Path(sys.argv[1]) config_path = Path(sys.argv[2]) broadcast_path = Path(sys.argv[3]) chain_id = int(sys.argv[4]) executor_contract = sys.argv[5] uniswap_v2_adapter = sys.argv[6] flash_loan_provider = sys.argv[7] treasury = sys.argv[8] or None deployer_address = sys.argv[9] or None executor_owner = sys.argv[10] or None pause_on_deploy = sys.argv[11] onchain_owner = sys.argv[12] or None onchain_pending_owner = sys.argv[13] or None onchain_paused = sys.argv[14] or None onchain_provider = sys.argv[15] or None onchain_treasury = sys.argv[16] or None aave_pool = sys.argv[17] or None aave_wrapper = sys.argv[18] or None artifact = { "chain_id": chain_id, "broadcast_artifact": str(broadcast_path), "config_path": str(config_path), "executor_contract": executor_contract, "uniswap_v2_adapter": uniswap_v2_adapter, "flash_loan_provider": flash_loan_provider, "treasury": treasury, "deployer_address": deployer_address, "requested_executor_owner": executor_owner, "pause_on_deploy": pause_on_deploy, "aave_pool": aave_pool, "aave_wrapper": aave_wrapper, "onchain": { "owner": onchain_owner, "pending_owner": onchain_pending_owner, "paused": onchain_paused, "flash_loan_provider": onchain_provider, "treasury": onchain_treasury, }, } output_path.write_text(json.dumps(artifact, indent=2) + "\n") print("") print("captured deployment artifact:") print(output_path) print("") print(json.dumps(artifact, indent=2)) print("") print("next config values:") print(f'chains.{chain_id}.execution.executor_contract = "{executor_contract}"') print(f'chains.{chain_id}.execution.flash_loan_provider = "{flash_loan_provider}"') if aave_pool: print(f'# Aave pool behind wrapper: {aave_pool}') print("") print("next operator checks:") print(f"- verify code exists at {executor_contract}") if aave_wrapper: print(f"- verify code exists at provider wrapper {aave_wrapper}") print(f"- verify owner(): {onchain_owner}") if onchain_pending_owner and onchain_pending_owner.lower() != "0x0000000000000000000000000000000000000000": print(f"- pendingOwner() is set to {onchain_pending_owner}; that address must call acceptOwnership()") print(f"- verify paused(): {onchain_paused}") print("- set router addresses for every V2-style dex used by execution") print("- keep MEV_SUBMIT_DISABLED=1 until signer/config readiness is green") PY