diff --git a/docs/04-configuration/MEV_EXECUTION_VALUE_SOURCES_AND_READINESS.md b/docs/04-configuration/MEV_EXECUTION_VALUE_SOURCES_AND_READINESS.md index 764542ed..22d0f420 100644 --- a/docs/04-configuration/MEV_EXECUTION_VALUE_SOURCES_AND_READINESS.md +++ b/docs/04-configuration/MEV_EXECUTION_VALUE_SOURCES_AND_READINESS.md @@ -126,6 +126,28 @@ The helper also records the deployed executor's on-chain: so you have an auditable deployment record instead of stdout-only notes. +## Cutover-ready config patching + +Once the contracts are actually broadcast, use the artifact to patch the MEV config deterministically instead of editing TOML by hand: + +```bash +bash scripts/deployment/apply-mev-execution-config-from-artifact.sh \ + --artifact reports/status/mev_execution_deploy_YYYYMMDD_HHMMSS.json \ + --config MEV_Bot/mev-platform/config.toml \ + --uniswap-v2-router 0x... \ + --sushiswap-router 0x... +``` + +That runs in dry-run mode by default and prints a unified diff. Add `--apply` when the diff is correct. + +This patch step updates: + +- `chains..execution.executor_contract` +- `chains..execution.flash_loan_provider` +- `chains..factories[].router` for `uniswap_v2` +- `chains..factories[].router` for `sushiswap` +- optional `relay_url` if `--relay-url` is supplied + ## Commit policy Safe to commit: diff --git a/scripts/deployment/apply-mev-execution-config-from-artifact.sh b/scripts/deployment/apply-mev-execution-config-from-artifact.sh new file mode 100755 index 00000000..3e8598a6 --- /dev/null +++ b/scripts/deployment/apply-mev-execution-config-from-artifact.sh @@ -0,0 +1,206 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +CONFIG_DEFAULT="$ROOT/MEV_Bot/mev-platform/config.toml" + +ARTIFACT_PATH="${MEV_EXECUTION_DEPLOY_ARTIFACT:-}" +CONFIG_PATH="${MEV_CONFIG_PATH:-$CONFIG_DEFAULT}" +CHAIN_ID_OVERRIDE="${MEV_CHAIN_ID:-}" +UNISWAP_V2_ROUTER="${MEV_UNISWAP_V2_ROUTER:-}" +SUSHISWAP_ROUTER="${MEV_SUSHISWAP_ROUTER:-}" +RELAY_URL_OVERRIDE="${MEV_RELAY_URL:-}" +APPLY=0 + +usage() { + cat <<'EOF' +Usage: apply-mev-execution-config-from-artifact.sh [options] + +Reads a deployment artifact created by deploy-mev-execution-contracts.sh and +patches the target MEV config with: +- chains..execution.executor_contract +- chains..execution.flash_loan_provider +- chains..factories[].router for uniswap_v2 and sushiswap +- optional relay_url override + +Defaults to dry-run and prints a unified diff. Use --apply to modify the file. + +Options: + --artifact PATH Required deployment artifact JSON + --config PATH Config TOML to patch (default: MEV_Bot/mev-platform/config.toml) + --chain ID Override chain id from artifact + --uniswap-v2-router ADR Router address for the uniswap_v2 factory entry + --sushiswap-router ADR Router address for the sushiswap factory entry + --relay-url URL Optional relay_url override + --apply Write changes in place + -h, --help Show this help +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --artifact) + ARTIFACT_PATH="$2" + shift 2 + ;; + --config) + CONFIG_PATH="$2" + shift 2 + ;; + --chain) + CHAIN_ID_OVERRIDE="$2" + shift 2 + ;; + --uniswap-v2-router) + UNISWAP_V2_ROUTER="$2" + shift 2 + ;; + --sushiswap-router) + SUSHISWAP_ROUTER="$2" + shift 2 + ;; + --relay-url) + RELAY_URL_OVERRIDE="$2" + shift 2 + ;; + --apply) + APPLY=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 python3 +require_cmd jq +require_cmd diff + +if [[ -z "$ARTIFACT_PATH" ]]; then + echo "--artifact is required" >&2 + exit 2 +fi + +if [[ ! -f "$ARTIFACT_PATH" ]]; then + echo "Artifact file not found: $ARTIFACT_PATH" >&2 + exit 2 +fi + +if [[ ! -f "$CONFIG_PATH" ]]; then + echo "Config file not found: $CONFIG_PATH" >&2 + exit 2 +fi + +if [[ -z "$UNISWAP_V2_ROUTER" || -z "$SUSHISWAP_ROUTER" ]]; then + echo "Both --uniswap-v2-router and --sushiswap-router are required" >&2 + exit 2 +fi + +TMP_OUT="$(mktemp)" +cleanup() { + rm -f "$TMP_OUT" +} +trap cleanup EXIT + +python3 - "$ARTIFACT_PATH" "$CONFIG_PATH" "$TMP_OUT" "$CHAIN_ID_OVERRIDE" "$UNISWAP_V2_ROUTER" "$SUSHISWAP_ROUTER" "$RELAY_URL_OVERRIDE" <<'PY' +import json +import re +import sys +from pathlib import Path + +artifact_path = Path(sys.argv[1]) +config_path = Path(sys.argv[2]) +output_path = Path(sys.argv[3]) +chain_override = sys.argv[4] +uniswap_router = sys.argv[5] +sushiswap_router = sys.argv[6] +relay_override = sys.argv[7] + +artifact = json.loads(artifact_path.read_text()) +text = config_path.read_text() + +chain_id = int(chain_override or artifact["chain_id"]) +executor_contract = artifact["executor_contract"] +flash_loan_provider = artifact["flash_loan_provider"] + +chain_marker = f"[chains.{chain_id}.execution]" +if chain_marker not in text: + raise SystemExit(f"Missing execution section for chain {chain_id} in {config_path}") + +def replace_line(pattern: str, replacement: str, content: str, *, required: bool = True) -> str: + updated, count = re.subn(pattern, replacement, content, flags=re.MULTILINE) + if required and count == 0: + raise SystemExit(f"Pattern not found: {pattern}") + return updated + +text = replace_line( + rf'(^\s*executor_contract\s*=\s*")[^"]*(")', + rf'\g<1>{executor_contract}\g<2>', + text, +) +text = replace_line( + rf'(^\s*flash_loan_provider\s*=\s*")[^"]*(")', + rf'\g<1>{flash_loan_provider}\g<2>', + text, +) + +if relay_override: + text = replace_line( + rf'(^\s*relay_url\s*=\s*")[^"]*(")', + rf'\g<1>{relay_override}\g<2>', + text, + ) + +def patch_factory_router(content: str, dex: str, router: str) -> str: + pattern = rf'(\{{\s*dex\s*=\s*"{re.escape(dex)}"\s*,\s*address\s*=\s*"[^"]*"\s*,)([^}}]*)\}}' + + def repl(match: re.Match[str]) -> str: + prefix = match.group(1) + middle = match.group(2) + if 'router =' in middle: + middle = re.sub(r'router\s*=\s*"[^"]*"', f'router = "{router}"', middle) + else: + middle = f' router = "{router}",' + middle + return f"{prefix}{middle}}}" + + updated, count = re.subn(pattern, repl, content) + if count == 0: + raise SystemExit(f"Factory entry for dex {dex} not found") + return updated + +text = patch_factory_router(text, "uniswap_v2", uniswap_router) +text = patch_factory_router(text, "sushiswap", sushiswap_router) + +output_path.write_text(text) +PY + +echo "Prepared MEV execution config patch" +echo "artifact: $ARTIFACT_PATH" +echo "config: $CONFIG_PATH" +echo "chain: ${CHAIN_ID_OVERRIDE:-$(jq -r '.chain_id' "$ARTIFACT_PATH")}" +echo "" + +if diff -u "$CONFIG_PATH" "$TMP_OUT"; then + echo "No changes needed." +fi + +if [[ "$APPLY" -eq 1 ]]; then + cp "$CONFIG_PATH" "${CONFIG_PATH}.bak.$(date +%Y%m%d_%H%M%S)" + cp "$TMP_OUT" "$CONFIG_PATH" + echo "" + echo "Applied changes to $CONFIG_PATH" +fi