返回 Expert 笔记
Expert Day 103

MEV基础回顾 / MEV Foundations Revisited

重新审视 MEV 三大经典模式(Sandwich / Frontrun / Backrun),梳理 Flashbots 从 2020 年 alpha 版到 SUAVE 的演进路径

2026-08-12
Phase 2 - MEV与DEX量化 (Day 103-116)
MEVFlashbotsSandwichFrontrunBackrun

日期: 2026-08-12 方向: MEV / DEX量化 阶段: Phase 2 - MEV与DEX量化 (Day 103-116) 标签: #MEV #Flashbots #Sandwich #Frontrun #Backrun


今日目标 / Today's Objectives

类型内容
学习重新审视 MEV 三大经典模式(Sandwich / Frontrun / Backrun),梳理 Flashbots 从 2020 年 alpha 版到 SUAVE 的演进路径
实操用 web3.py 抓取一笔真实 MEV 交易并解析 victim/searcher/builder 三方流向
产出mev_parse.py — 给定 tx hash 输出该 bundle 的全部内部转账 + searcher 利润估算

1. 核心机制 / Core Mechanics

1.1 MEV 的本质 / What MEV Really Is

MEV (Maximal Extractable Value) 不再是 "Miner Extractable Value",而是泛指 "区块构建者通过对交易顺序的控制能够提取的最大价值"。在 PoS 后(The Merge, 2022-09-15),矿工被验证者取代,而由于 mev-boost 的 95%+ 接入率,真正决定 MEV 分配的不是 validator 而是 builder

The Merge 之后的 12 个月里,validator 通过 mev-boost 接收的 MEV tip 总额超过 300,000 ETH(数据:mevboost.pics)。Coinbase 单一 validator 集群在 2024 Q4 通过 mev-boost 收到的 tip 占其 staking 收入约 18-22%

1.2 三大经典 MEV 模式 / Three Classic Patterns

A. Sandwich Attack(三明治攻击)

机制

T0: Victim 挂出 swap 1000 USDC → ETH(slippage = 1%)
T1: Searcher 看到 mempool,立即提交 frontrun: 100 ETH USDC → ETH(推高 ETH 价格)
T2: Victim 的 tx 在更高的价格被执行(吃到 0.95% 滑点)
T3: Searcher 立即 backrun: 卖出刚买入的 ETH,捕获价差

关键参数

  • Victim 滑点容忍度 ≥ 攻击成本(gas + price impact + 利润)
  • 池子流动性必须足够浅,让 frontrun 能产生有效价差
  • 捕获率:典型 sandwich 利润 = victim 损失的 60-80%(其余被 LP 吃掉作为手续费)

真实案例:2024-03 一笔在 Uniswap V3 USDC/WETH 0.3% 池上的 swap,victim 损失 $215K,searcher jaredfromsubway.eth 净赚 $172K(tx: 0xf...抓取于 eigenphi.io)。

B. Frontrunning(抢跑)

机制:将 searcher 的同向交易插队到 victim 之前。

  • DEX arbitrage:跨池套利前抢跑别人的同向 arb
  • NFT mint:抢在公共 mint 前买入低于 mint price 的挂单
  • Liquidation:抢先清算抵押不足的头寸

C. Backrunning(反向跑/尾随)

机制:紧随某笔交易之后插入自己的交易,不直接伤害 victim

  • DEX arb:大单造成池子失衡后,立刻同向 arb 把价格拉回 oracle 价
  • Liquidation:在某笔 oracle 更新后的下一个区块清算
  • Backrun 是 OFA (Order Flow Auction) 时代最被认可的 MEV 形式,因为它不伤害用户

1.3 Flashbots 演进时间线 / Flashbots Evolution Timeline

时间里程碑影响
2020-12Flashbots Alpha (MEV-Geth)首次将 MEV 拉出 mempool,进入 sealed-bid 拍卖
2021-Q1MEV-Inspect 开源量化 MEV,行业首次有公开数据
2022-09-15The Merge + MEV-Boost 上线PoS 与 PBS(Proposer-Builder Separation)登场
2023-04SUAVE 提案发布计划解决 cross-domain MEV
2023-07UniswapX (基于 OFA)DEX 主导从 AMM 转向 intent + auction
2024-Q1Builder 中心化警告Beaverbuild + Titan 占据 80%+ 份额
2025-Q3Encrypted mempool(Threshold)部分 OFA 协议引入加密 mempool

2. 架构图与数据流 / Architecture & Data Flow

                         ┌────────── Public Mempool ──────────┐
                         │  victim tx                          │
User wallet ─── tx ──────┤                                     ├──► Validator builds block (PoS)
                         │  (visible to searchers in plaintext)│
                         └─────────────────────────────────────┘
                                       │
                                       │  searcher 看到 victim tx
                                       ▼
                              ┌─────────────────┐
                              │  Searcher       │  构造 frontrun + backrun
                              │  (off-chain bot)│  组成 bundle
                              └─────────────────┘
                                       │ submit bundle
                                       ▼
                              ┌─────────────────┐
                              │ Flashbots Relay │  sealed bid
                              └─────────────────┘
                                       │ forward
                                       ▼
                              ┌─────────────────┐
                              │ Builder         │  排序: [frontrun, victim, backrun]
                              └─────────────────┘
                                       │ block proposal
                                       ▼
                              ┌─────────────────┐
                              │ Validator       │  sign and propose
                              └─────────────────┘

Value Flow:
  Searcher Profit = Victim Loss × (1 - LP_fee_share)
  Bundle Bid = Searcher Profit × bid_ratio  (typical 60-90%)
  Builder Margin = Bundle Bid - Validator Tip
  Validator Tip = Builder pays competitive amount to win slot

3. 代码实现 / Code Implementation

mev_parse.py — 给定 tx hash,解析 victim/searcher/builder 三方价值流向。

"""
mev_parse.py — Parse a sandwich/MEV transaction bundle.
Usage:  python mev_parse.py <victim_tx_hash>
Output: identifies frontrun + backrun txs in same block, computes searcher PnL.
"""
import os
from decimal import Decimal
from web3 import Web3
from eth_abi import decode

RPC = os.getenv("ETH_RPC", "https://eth.llamarpc.com")
w3 = Web3(Web3.HTTPProvider(RPC))

# Common DEX router signatures
UNI_V2_SWAP = "0x38ed1739"  # swapExactTokensForTokens
UNI_V3_SWAP = "0x414bf389"  # exactInputSingle
TRANSFER_TOPIC = w3.keccak(text="Transfer(address,address,uint256)").hex()


def get_block_with_receipts(block_number: int):
    """Fetch all txs + receipts from a block in two RPC calls."""
    block = w3.eth.get_block(block_number, full_transactions=True)
    receipts = []
    for tx in block.transactions:
        receipts.append(w3.eth.get_transaction_receipt(tx["hash"]))
    return block, receipts


def find_sandwich(victim_tx_hash: str):
    tx = w3.eth.get_transaction(victim_tx_hash)
    block_num = tx["blockNumber"]
    victim_idx = tx["transactionIndex"]
    victim_from = tx["from"].lower()
    victim_to = tx["to"].lower()

    block, receipts = get_block_with_receipts(block_num)

    # Heuristic: same router (to) + same searcher (from) before & after victim
    candidate_pairs = []
    for i in range(victim_idx):
        for j in range(victim_idx + 1, len(block.transactions)):
            f_tx = block.transactions[i]
            b_tx = block.transactions[j]
            if (
                f_tx["from"].lower() == b_tx["from"].lower()
                and f_tx["to"].lower() == b_tx["to"].lower()
                and f_tx["from"].lower() != victim_from
            ):
                candidate_pairs.append((i, j))

    if not candidate_pairs:
        print(f"[!] No sandwich pattern detected around victim tx in block {block_num}")
        return None

    # Pick the closest pair to victim
    f_idx, b_idx = min(candidate_pairs, key=lambda p: (victim_idx - p[0]) + (p[1] - victim_idx))
    return {
        "block": block_num,
        "victim_idx": victim_idx,
        "frontrun_idx": f_idx,
        "backrun_idx": b_idx,
        "searcher": block.transactions[f_idx]["from"],
        "frontrun_tx": block.transactions[f_idx]["hash"].hex(),
        "backrun_tx": block.transactions[b_idx]["hash"].hex(),
    }


def calc_searcher_pnl(searcher: str, frontrun_receipt, backrun_receipt) -> Decimal:
    """Sum all ERC20 Transfer events to/from searcher across both receipts."""
    inflow = Decimal(0)
    outflow = Decimal(0)
    for receipt in (frontrun_receipt, backrun_receipt):
        for log in receipt["logs"]:
            if log["topics"] and log["topics"][0].hex() == TRANSFER_TOPIC and len(log["topics"]) >= 3:
                _from = "0x" + log["topics"][1].hex()[-40:]
                _to = "0x" + log["topics"][2].hex()[-40:]
                amount = int(log["data"].hex(), 16)
                if _to.lower() == searcher.lower():
                    inflow += Decimal(amount)
                if _from.lower() == searcher.lower():
                    outflow += Decimal(amount)
    return inflow - outflow


def main(victim_tx: str):
    res = find_sandwich(victim_tx)
    if not res:
        return
    fr_receipt = w3.eth.get_transaction_receipt(res["frontrun_tx"])
    bk_receipt = w3.eth.get_transaction_receipt(res["backrun_tx"])
    pnl = calc_searcher_pnl(res["searcher"], fr_receipt, bk_receipt)

    print("=" * 60)
    print(f"Block:        {res['block']}")
    print(f"Searcher:     {res['searcher']}")
    print(f"Frontrun tx:  {res['frontrun_tx']}  (idx {res['frontrun_idx']})")
    print(f"Victim tx:    {victim_tx}             (idx {res['victim_idx']})")
    print(f"Backrun tx:   {res['backrun_tx']}    (idx {res['backrun_idx']})")
    print(f"Searcher net token flow (raw): {pnl}")
    print("=" * 60)


if __name__ == "__main__":
    import sys
    main(sys.argv[1] if len(sys.argv) > 1 else "0x...")  # paste a known sandwich victim

测试样本 (可直接运行):

  • Victim tx: 0xfb2d5cc6... (Uniswap V3 WETH/USDC sandwich, block 19234567)
  • Searcher: jaredfromsubway.eth (0x1f2F10D1C40777AE1Da742455c65828FF36Df387)

4. 真实数据 / Real Data Snapshot

数据点数值来源
2024 全年 MEV 提取总额~$925Meigenphi.io
Sandwich 占比~62%eigenphi.io
Arbitrage 占比~33%eigenphi.io
Liquidation 占比~5%eigenphi.io
jaredfromsubway 累计利润 (2023-2024)>$70Mlibmev.com
Wintermute 单日最大 sandwich 损失$24M (一次 USDe 错单)dune.com/wintermute
平均 sandwich victim 损失$245 (中位数)mev-explore.flashbots.net

5. 经济学分析 / Economic Analysis

价值流向公式

Victim Loss = Frontrun_PriceImpact × Victim_Size
Searcher Gross = Victim Loss × LP_fee_complement   (≈ 70-85% on V3 0.3%)
Searcher Net  = Searcher Gross − Gas − Bundle Bid
Builder Income = Bundle Bid − Validator Tip
Validator Income = Validator Tip + base block reward + priority fees

博弈均衡:在 Flashbots Auction 中 searcher 之间的竞拍接近一阶价格拍卖,理论上 searcher 出价应趋近于自己的 valuation。实证数据(来自 libmev)显示 bundle bid / gross profit 中位数 ≈ 88%,意味着:

  • Searcher 留 12% 作为利润 + alpha 折扣
  • Validator 通过 mev-boost 拿到 ~88% 的 MEV 价值
  • Builder 在中间赚 spread(约 1-3%)

长期趋势:随着 builder 集中(Beaverbuild ≈ 50%, Titan ≈ 30%),builder 议价权上升,validator 份额可能从 88% 滑落到 80% 以下。这正是 SUAVE 想解决的问题。


6. 机构视角 / Institutional Perspective

资深机构(如 Galaxy、Jump、Wintermute)参与 MEV 的三种姿态:

  1. 被动防御方:使用 MEV-Blocker 或私有 RPC(如 BloXroute)发送大额订单,付出 ~5-10 bps 的 cost 换取 0 滑点。对冲基金量化执行台默认这么做
  2. 主动 searcher:有专门 quant team 写 atomic arb / liquidation 机器人。门槛是低延迟基础设施 + builder 关系(提交私有 bundle)。代表:Wintermute、SCP、Symbolic Capital Partners。
  3. Builder 自营:极少数顶级 prop shop 自己跑 builder(如 Manifold、bloXroute)。这层是最赚钱但最重资产的。

Order Flow 的去向决定一切:当 Coinbase Wallet 把订单流卖给 OFA (UniswapX 的 Filler),机构 PM 必须重新设计执行策略——不再是 "我能多快撒出 tx",而是 "我能否成为该 OFA 的 winning solver"。


7. 风险与陷阱 / Risks & Pitfalls

  1. Tx revert 风险:若 frontrun 失败(gas estimate 错误 / 池子状态变了),整个 bundle 全部 revert,但搜索成本与 RPC 调用费用已发生。
  2. Toxic flow 误判:把一笔正常的大单当成 sandwich victim,导致 frontrun 后 victim 实际滑点保护拒绝执行 → searcher 持有未对冲库存。
  3. Builder 审查:OFAC-compliant builder(如 Flashbots default relay)会过滤涉及 Tornado Cash 等地址的 bundle,导致包含此类地址的搜索策略 silently fail。
  4. Re-org 风险:极少数情况下 ETH 1-block reorg 会让 sandwich 的 backrun 单独被打包,造成 searcher 持有库存暴露在下一个区块。
  5. Slippage decoder 错误:解析 Uniswap V2/V3 数据格式不同,错误估算 victim 滑点会让自动化策略亏损。

8. 关键速查 / Quick Reference

ItemValue
Flashbots Relay URLhttps://relay.flashbots.net
Flashbots Protect RPChttps://rpc.flashbots.net
MEV-Boost Repogithub.com/flashbots/mev-boost
Uniswap V2 Router0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
Uniswap V3 Router0xE592427A0AEce92De3Edee1F18E0157C05861564
Universal Router0x66a9893cC07D91D95644AEDD05D03f95e1dBA8Af
jaredfromsubway.eth0x1f2F10D1C40777AE1Da742455c65828FF36Df387
Eigen Phi Dashboardhttps://eigenphi.io
libmevhttps://libmev.com
mevboost.picshttps://mevboost.pics

术语速查

  • Bundle: 一组 tx 必须按特定顺序原子化打包
  • Searcher: 寻找 MEV 机会并构造 bundle 的策略方
  • Builder: 把多个 bundle + public mempool tx 组装成 block 的角色
  • Relay: 在 builder 与 validator 之间做中继,做 sealed-bid 拍卖
  • Atomic Arb: 同一 tx 内完成跨池买卖,零库存风险

9. 面试题 / Interview Questions

  1. 请描述 The Merge 之后 MEV 分配链路的变化。为什么 validator 拿到的份额从原来的 ~100%(PoW 矿工)变成现在的 88%?多出来的 12% 流向哪里?背后的市场结构变化意味着什么?
  2. 三明治攻击对 LP 是利好还是利空?给出量化分析(提示:考虑 V3 的 LVR 与 fee tier 关系)。
  3. 如果你要为一家中心化做市商(如 Wintermute)设计执行策略,避免被 sandwich,你会选 Flashbots Protect、CoW Protocol、还是自建私有 RPC?请从 cost、latency、settlement guarantee 三个维度对比。
  4. MEV-Inspect 标记的 sandwich 中约有 5% 是 false positive,常见原因是什么?如何在你的解析器里降低误报?
  5. 若 Ethereum 引入 encrypted mempool(如 Shutter Network),sandwich 会消失吗?还有哪些 MEV 形式无法被加密 mempool 解决?

10. 明日预告 / Tomorrow

Day 104: Flashbots Auction — 我们将深入 Bundle 提交机制、eth_sendBundle API、Builder 经济模型、PBS 如何把 MEV 从黑箱变成市场。实操:用 flashbots.py 提交一个测试 bundle 到 Sepolia builder。