MEV 數學建模與工程實作完整指南:從基礎理論到 Flashbots、SUAVE 基礎設施深度解析

本文深入探討以太坊 MEV(最大可提取價值)的數學建模與工程實作。涵蓋區塊空間拍賣市場理論、DEX 交易 MEV 量化計算、清算與三明治攻擊的完整利潤模型。提供 Flashbots Bundle API 的 TypeScript/Python 實作、SUAVE 去中心化 MEV 基礎設施的 Solidity 實現、以及 MEV 機會識別與利潤回測框架的完整程式碼。特別收錄 Flashbots Relay API 實戰、Builder 利潤最大化 Go 算法、以及 quant辭匙訂單流拍賣(PFOF)機制的深度分析。

MEV 數學建模與工程實作完整指南:從基礎理論到 Flashbots、SUAVE 基礎設施深度解析

前言:MEV 不只是「偷錢」那麼簡單

好,我攤開說。提到 MEV,大部分人的第一反應就是:「啊,三明治攻擊,sandwich attack,我的 swap 被夾了,血亏。」沒錯,這是 MEV 最常見的面向,但說實在的,如果你只看到這一層,那 MEV 對你來說就是個黑盒子。你永遠只能在受害者的角度被動挨打。

MEV 的全稱是 Maximal Extractable Value,翻成白話就是「區塊空間裡能摳出來的最大價值」。這個概念一點都不神秘——區塊生產者(以前是礦工,現在是驗證者)有能力決定交易順序,而這個順序本身就是一種資產。掌握了排序權力,就掌握了價值分配的權力。

這篇文章我打算從數學推導開始,把 MEV 的底層邏輯拆乾淨。然後帶你實實在在地看 Flashbots 的工程實作、SUAVE 是怎麼回事、三明治機器人的利潤模型、以及 quant辭匙訂單流拍賣(PFOF)之類的進階玩法。目標只有一個:讓你從「知道 MEV 存在」升級到「能自己動手算 MEV 機會」。


第一章:MEV 的數學建模

1.1 區塊空間作為市場

先把框架拉出來。我們把以太坊區塊空間理解成一個拍賣市場:

交易的「位置」本身有價值。越靠前的交易,越早執行,在搶購場景(如 NFT mint)就越有利。這個「位置溢價」就是 MEV 的核心來源。

數學上,MEV 可以定義為:

MEV = Σᵢ [V(transaction_i in position_i) - V(transaction_i in position_N)]

其中 positioni 是交易 i 被執行的實際位置,positionN 是理論上的公平位置,V 是價值函數。這個差值就是區塊生產者通過控制排序額外撈到的好處。

1.2 單一 DEX 交易的 MEV 計算

最經典的場景:一個交易者在 Uniswap 上執行大額 Swap。

假設條件:

交易者的預期輸出(無 MEV 干擾):

根據 Uniswap 的常數乘積公式:

k = R_x × R_y

交易後的新儲備量:

R'_x = R_x + x
R'_y = k / R'_x

交易者收到的 Token Y 數量:

Δy = R_y - R'_y = R_y - k / (R_x + x)

化簡後:

Δy = R_y × x / (R_x + x)

實際上因為有 0.3% 的交易費(相當於 x' = x × 0.997),所以:

Δy_actual = R_y × 0.997x / (R_x + 0.997x)

搜尋者的利潤計算:

搜尋者觀察到待確認交易池(PENDING pool)中的大額 Swap。她的策略是:

  1. 在 Swap 之前:用少量 Token X 操縱 Rx → Rx'
  2. 執行 Swap:獲得更多 Token Y(因為滑點被放大)
  3. 在 Swap 之後:反向操作,把 Token Y 換回 Token X

搜尋者的利潤(以 Token X 計):

Profit = Δy_swap × (P_y / P_x) - Δx_front_run × (1 + δ)

其中 δ 是交易費用,Py / Px 是代幣相對價格。

量化實例:

假設 Uniswap pair 儲備量為 (10000 ETH, 20000000 USDC),ETH = 2000 USDC。

交易者要用 100 ETH 換 USDC:

Δy_actual = 20000000 × 0.997 × 100 / (10000 + 0.997 × 100)
           = 19940000 / 10099.7
           ≈ 1974.3 USDC

搜尋者偵測到這筆交易,她的策略:

步驟 1:FRONTRUN
用 1 ETH 先 Swap,增加 R_x
Δy_small = 20000000 × 0.997 × 1 / (10000 + 0.997)
         ≈ 1.996 USDC

步驟 2: victim's swap 執行
此時 R_x ≈ 10001,R_y ≈ 19998002
受害者得到:20000000 × 0.997 × 100 / (10001 + 0.997×100)
         ≈ 1974.2 USDC(跟之前差不多)
         
步驟 3:BACKRUN
用獲得的 1974.2 USDC 換回 ETH
Δx_back = 20000000 × 0.997 × 1974.2 / (19998002 + 0.997 × 1974.2)
        ≈ 1.0009 ETH

淨利潤 ≈ 1.0009 - 1 = 0.0009 ETH(每次操作)

看起來不多?但這是對 1 ETH 的操作。如果搜尋者用 10 ETH 做同樣操作,利潤就是 0.009 ETH。而且真實市場中,滑點放大效應更明顯。

1.3 清算的 MEV 數學

借貸協議的清算也是 MEV 的重要來源。當用戶的健康因子低於 1.0 時,任意地址都可以執行清算並獲得清算獎勵。

清算觸發條件的數學推導:

健康因子定義為:

HF = Σ (value_i × collateral_factor_i) / borrowed_value

清算觸發:HF < 1.0

假設借款人的清算阈值(liquidationThreshold)為 LT,清算獎勵為 L(通常為抵押品價值的 5%-10%)。

清算人的利潤函數:

Profit_liquidator = Δcollateral × L - gas_cost - flashloan_fee

清算人需要最大化這個利潤。問題在於,清算人之間存在激烈的競爭——誰先把交易塞進區塊、誰的交易排序靠前,誰就能拿走清算獎勵。

搶奪遊戲的博弈論分析:

兩個清算人 A 和 B 競爭同一筆可清算的頭寸。

定義:

在第一價格拍賣模型下:

A 的期望利潤 = P_A × (V - C_A) - (1 - P_A) × C_A(競標失敗也付成本)

理性清算人會競標直到邊際利潤為零。Nash 均衡時:

P_A × V = C_A

也就是說,清算人願意支付的最高競標價格等於獲勝概率乘以獎勵價值。如果市場上有多個實力相當的清算人,競爭會把利潤打到接近零(扣除Gas成本後)。

這就是為什麼我們看到 MEV 競賽最終走向了 Gas 拍賣——大家比拼的不是創意,而是誰願意燒更多 Gas。

1.4 三明治攻擊的完整利潤模型

三明治攻擊(Sandwich Attack)是最骯髒的 MEV 策略,但數學上其實非常直接。

兩筆交易構成一個「三明治」:

  1. FRONTRUN:攻擊者以更高 Gas 價格把自己的 Swap 排在受害者前面
  2. 受害者的 Swap:被夾在中間,執行時價格已經被抬高
  3. BACKRUN:攻擊者立即把 Token 換回,鎖定利潤

利潤計算(以受害者 Swap ETH→Token 為例):

受害者要執行:swap(ETH, Token, x_eth)

受害者的滑點(sandwiched):

Δy_victim = R_y × 0.997 × x_eth / (R_x + 0.997 × x_eth)

攻擊者 FRONTRUN:投入 xa ETH,獲得 Δya Token

Δy_a = R_y × 0.997 × x_a / (R_x + 0.997 × x_a)

此時池子狀態:(Rx + 0.997xa + 0.997xeth, Ry - Δya - Δyvictim)

攻擊者 BACKRUN:用 Δy_a Token 換回 ETH

Δx_back = (R_x + 0.997x_a + 0.997x_eth) × 0.997 × Δy_a / (R_y - Δy_a - Δy_victim + 0.997 × Δy_a)

攻擊者淨利潤:

Profit = Δx_back + Δy_victim×P_y - x_a × (1 + δ)

其中 P_y 是 Token 的美元價值,δ 是 ETH 的機會成本。

最佳攻擊規模的數學推導:

攻擊者需要選擇最優的 xa 來最大化利潤。對 Profit 求 xa 的偏導並設為零:

∂Profit/∂x_a = 0

這個方程的解析解非常複雜,但有一個經驗法則:攻擊規模通常為受害者交易規模的 10%-30%。規模太小利潤不足,規模太大則市場衝擊過高反而虧損。

在實際操作中,搜尋者會用梯度下降或牛頓法在本地快速計算最優 x_a,然後構造 bundle 提交。


第二章:Flashbots 基礎設施的工程實作

2.1 Flashbots Architecture 總覽

Flashbots 是目前最大的 MEV 基礎設施提供商。它們的整套系統可以理解為一個「MEV 流水線」:

交易池 → 搜尋者(Searcher)→ Bundle 構造 → Relay →區塊生產者(Builder)

讓我逐一拆解每個環節。

2.2 Flashbots RPC 與 MEV-Boost

常規以太坊 RPC 的問題在於:交易先到公共內存池(mempool),然後礦工/驗證者從中挑選。這個過程是透明的,意味著 MEV 機器人可以監控並搶奪價值。

Flashbots 提供了 mev-boost 解決方案,讓驗證者能夠從多個區塊建構者(Builder)那裡接收區塊提案。核心流程:

  1. Builder:收到來自搜尋者的 bundles,構造有利潤最大化的區塊
  2. Relay:作為可信中介,負責驗證區塊並傳遞給驗證者
  3. Validator:使用 mev-boost 接收區塊,專注於共識職責
// 搜尋者提交 Bundle 到 Flashbots Relay 的 API 調用示例
const { FlashbotsBundleProvider } = require('@flashbots/ethers_bundle_provider');
const { BigNumber } = require('@ethersproject/bignumber');

async function submitBundle() {
  const flashbotsProvider = await FlashbotsBundleProvider.create(
    provider,      // 標準 EIP-1193 Provider
    authSigner,    // 錢包,用於對請求簽名(不影響主網余額)
    {
      chainId: 1,  // Mainnet
      flashbotsRelayer: 'https://relay.flashbots.net',
      flashbotsEndpoint: 'https://rpc.flashbots.net'
    }
  );
  
  // 構造 Bundle:包含多筆交易及它們之間的依賴關係
  const bundle = [
    {
      transaction: {
        to: '0xUniswapV2Router02',
        data: swapCalldata,
        gasLimit: 250000,
        maxFeePerGas: BigNumber.from(30).mul(1e9),
        maxPriorityFeePerGas: BigNumber.from(2).mul(1e9),
        chainId: 1
      },
      signer: userWallet  // 受害者交易
    },
    {
      transaction: {
        to: '0xUniswapV2Router02',
        data: frontRunCalldata,
        gasLimit: 250000,
        maxFeePerGas: BigNumber.from(35).mul(1e9),  // 高於受害者
        maxPriorityFeePerGas: BigNumber.from(10).mul(1e9),
        chainId: 1
      },
      signer: attackerWallet  // 攻擊者 FRONTRUN
    }
  ];
  
  // 提交 Bundle
  const bundleReceipt = await flashbotsProvider.sendBundle(
    bundle,
    {
      blockNumber: latestBlockNumber + 1,  // 目標區塊
      minTimestamp: 0,
      maxTimestamp: Math.floor(Date.now() / 1000) + 120
    }
  );
  
  console.log('Bundle submitted, waiting for inclusion...');
}

2.3 Bundle 的依賴圖與原子性

Flashbots Bundle 的殺手級特性是原子性(atomicity)。一個 Bundle 中的所有交易要么全部執行,要么全部不執行。

這是用 eth_call + bundle 模擬實現的:

"""
Python 實現:Flashbots Bundle 模擬與提交
"""
import asyncio
from eth_typing import HexStr
from web3 import Web3

FLASHBOTS_RPC = "https://rpc.flashbots.net"

class FlashbotsBundle:
    def __init__(self, w3: Web3, signer):
        self.w3 = w3
        self.signer = signer
        
    async def simulate_bundle(
        self,
        transactions: list,
        target_block: int
    ) -> dict:
        """
        在目標區塊上模擬 Bundle,返回估計的 Gas 使用和 MEV 機會
        """
        # 構造 eth_call 批量請求來模擬執行
        calls = []
        for tx in transactions:
            calls.append({
                'jsonrpc': '2.0',
                'method': 'eth_call',
                'params': [{
                    'to': tx['to'],
                    'data': tx['data'],
                    'from': tx['from'],
                    'value': tx.get('value', 0)
                }, f'latest'],
                'id': len(calls) + 1
            })
        
        # Flashbots 特有的 simulate API
        simulate_url = f"{FLASHBOTS_RPC}/v1/simulate"
        response = await self._post_json(simulate_url, {
            'jsonrpc': '2.0',
            'method': 'eth_simulateBundle',
            'params': [{
                'transactions': transactions,
                'blockNumber': hex(target_block),
                'stateBlockNumber': 'latest'
            }],
            'id': 1
        })
        
        return response
    
    async def submit_bundle(
        self,
        transactions: list,
        target_block: int,
        payment_address: str
    ) -> str:
        """
        提交 Bundle 到 Flashbots Relay
        返回 Bundle Hash
        """
        payload = {
            'jsonrpc': '2.0',
            'method': 'eth_sendBundle',
            'params': [{
                'txs': [self._encode_tx(tx) for tx in transactions],
                'blockNumber': hex(target_block),
                'minTimestamp': 0,
                'maxTimestamp': 0,
                'revertingTxHashes': []  # 哪些交易允許失敗
            }],
            'id': 1
        }
        
        # 需要對請求進行 Flashbots 特有的簽名
        signed_payload = self._sign_flashbots_request(payload)
        
        response = await self._post_json(
            f"{FLASHBOTS_RPC}/v1/bundle",
            signed_payload
        )
        
        return response.get('result', {}).get('bundleHash', '')
    
    def _encode_tx(self, tx: dict) -> HexStr:
        """將交易字典編碼為 RLP bytes"""
        # 完整的 EIP-2718 編碼邏輯
        tx_type = 2  # EIP-4844 交易類型
        encoded = self.w3.eth.account.encode_transaction(tx)
        return HexStr(encoded.hex())
    
    def _sign_flashbots_request(self, payload: dict) -> dict:
        """
        Flashbots 要求對負擔得起的 Gas 費用上限進行簽名
        這是一個 EIP-712 類型的簽名
        """
        # 省略實際簽名邏輯(需要完整的 Flashbots API)
        return payload

2.4 區塊建構者的利潤最大化算法

區塊建構者(Block Builder)的目標函數非常直接:最大化區塊利潤

max Σ (fee_i + mev_i)  subject to:
    Σ gas_i ≤ block_gas_limit
    Σ storage_i ≤ state_access_limit
    all tx_i are valid

其中 feei 是交易費用,mevi 是區塊建構者從該交易中提取的 MEV 價值。

實際算法更複雜。建構者需要:

  1. 從大量 bundles 中選擇利潤最高的組合
  2. 處理 bundles之間的依賴關係(例如,Bundle A 的輸出是 Bundle B 的輸入)
  3. 考慮單筆交易的 MEV 潛力(稱為「backrun 拍賣」)
// Go 實現:區塊建構者的貪心選擇算法
package main

import (
    "sort"
    "math/big"
)

type Bundle struct {
    Hash      string
    Txs       []Transaction
    Profit    *big.Int    // 以 wei 為單位的利潤
    GasLimit  uint64      // 估計的 Gas 使用量
    DependsOn string      // 依賴的上一個 Bundle(如果有)
}

type BlockBuilder struct {
    pendingBundles []Bundle
    blockGasLimit  uint64
}

// 主算法:選擇利潤最大化的 Bundle 組合
func (bb *BlockBuilder) BuildBlock() []Transaction {
    // 第一步:過濾並排序所有 Bundles
    validBundles := bb.filterValidBundles()
    sort.Slice(validBundles, func(i, j int) bool {
        // 按每單位 Gas 的利潤排序
        profitPerGasI := new(big.Float).Quo(
            new(big.Float).SetInt(validBundles[i].Profit),
            new(big.Float).SetUint64(validBundles[i].GasLimit),
        )
        profitPerGasJ := new(big.Float).Quo(
            new(big.Float).SetInt(validBundles[j].Profit),
            new(big.Float).SetUint64(validBundles[j].GasLimit),
        )
        return profitPerGasI.Cmp(profitPerGasJ) > 0
    })
    
    // 第二步:貪心選擇(可改進為 DP 以處理依賴關係)
    selected := []Transaction{}
    usedGas := uint64(0)
    satisfiedBundles := make(map[string]bool)
    
    for _, bundle := range validBundles {
        // 檢查依賴
        if bundle.DependsOn != "" && !satisfiedBundles[bundle.DependsOn] {
            continue  // 依賴未滿足,跳過
        }
        
        // 檢查 Gas 限制
        if usedGas + bundle.GasLimit > bb.blockGasLimit {
            continue  // Gas 不夠,跳過
        }
        
        // 檢查時間窗口(bundle 只能在特定區塊執行)
        if !bundle.IsValidInCurrentBlock() {
            continue
        }
        
        // 選擇這個 Bundle
        selected = append(selected, bundle.Txs...)
        usedGas += bundle.GasLimit
        satisfiedBundles[bundle.Hash] = true
    }
    
    return selected
}

func (bb *BlockBuilder) filterValidBundles() []Bundle {
    // 過濾掉:
    // 1. 無法支付的 Bundle(餘額不足)
    // 2. 已經失效的 Bundle(目標區塊已過)
    // 3. 無法通過模擬的 Bundle(執行會失敗)
    return bb.pendingBundles
}

這個簡化版本忽略了幾個關鍵問題:bundles 之間的執行依賴(一個 bundle 的輸出影響另一個的輸入)、時間窗口重疊、以及 State Access 限制。但在實際部署中,Builder 會使用更複雜的 ILP(整數線性規劃)或啟髮式算法來處理這些約束。


第三章:SUAVE 去中心化 MEV 基礎設施

3.1 SUAVE 是什麼?

SUAVE 全稱是 Single Unifying Auction for Value Expression,是 Flashbots 在 2023 年提出的新架構。它的核心目標是把 MEV 的權力從中心化的 Builder 分散出去

為什麼要這麼做?因為當前 Flashbots 的架構有一個根本性問題:只有 Flashbots 自己做 Builder,其他人沒有辦法公平地參與 MEV 分配。這導致了事實上的中心化。

SUAVE 的解決方案是把拍賣過程本身放到鏈上(通過一個專用的區塊鏈),讓拍賣變得透明和去信任化。

3.2 SUAVE 的架構設計

SUAVE 包含三個核心組件:

  1. SUAVE Chain:一條專門負責 MEV 拍賣的區塊鏈(基於 Geth 的分叉)
  2. 偏好池(Preference Pool):用戶表達交易意圖的地方
  3. .Execution Gateway:連接 MEV 供應鏈上下游的接口
用戶 → [表達偏好] → 偏好池 → [拍賣] → Execution Gateway → 區塊
                                       ↓
                                  多個 Solver 競爭
                                  (去中心化)

3.3 SUAVE 的數學模型:拍賣機制

SUAVE 採用的是「批量拍賣」(Batch Auction)機制。傳統的「願付 Gas 最高者獲勝」(Priority Gas Auction)會導致:

SUELLER 提出的改進機制:共同價值拍賣(Common Value Auction)

拍賣機制的設計目標:
1. 激勵相容(Incentive Compatible):說真話是最優策略
2. 個人理性(Individually Rational):參與拍賣不會虧損
3. 社會福利最大化(Social Welfare):盡量讓高價值交易優先執行

機制:每個搜尋者提交一個密封投標(sealed bid)
      拍賣人選擇使社會福利最大化的交易集合
      獲勝者支付的價格 = 第二高分報價(VCG 機制)

VCG(Vickrey-Clarke-Groves)機制的核心思想:你的付款等於你缺席時對其他社會成員造成的福利損失。這確保了說真話是每個參與者的最優策略。

3.4 SUAVE 智能合約的 Solidity 實現

以下是一個 SUAVE 偏好池的簡化合約實現:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title SUAVE Preference Pool
 * @notice 用戶表達 MEV 意願的核心合約
 */
contract PreferencePool {
    struct Preference {
        address user;
        bytes calldata;           // 原始意圖 calldata
        bytes32 intentHash;        // 意圖的哈希
        uint256 bid;               // 願意支付的最高金額(wei)
        uint256 deadline;          // 意圖失效時間
        uint256 gasLeft;           // 剩餘 gas(用於支付 MEV)
        bool filled;               // 是否已被執行
    }
    
    mapping(bytes32 => Preference) public preferences;
    mapping(address => bytes32[]) public userPreferences;
    
    address public suaveChain;
    address public executionGateway;
    
    event PreferenceSubmitted(
        bytes32 indexed intentHash,
        address indexed user,
        uint256 bid
    );
    event PreferenceFilled(
        bytes32 indexed intentHash,
        uint256 payment
    );
    
    /**
     * @notice 用戶提交交易意圖
     * @param calldata_ 原始意圖的 calldata
     * @param bid 用戶願意為此意圖支付的最高金額
     */
    function submitIntent(
        bytes calldata_,
        uint256 bid
    ) external payable returns (bytes32 intentHash) {
        require(bid > 0, "Bid must be positive");
        require(msg.value >= bid + 21000 * tx.gasprice, "Insufficient payment");
        
        intentHash = keccak256(abi.encodePacked(
            msg.sender,
            calldata_,
            block.timestamp
        ));
        
        preferences[intentHash] = Preference({
            user: msg.sender,
            calldata: calldata_,
            intentHash: intentHash,
            bid: bid,
            deadline: block.timestamp + 100,
            gasLeft: gasleft(),
            filled: false
        });
        
        userPreferences[msg.sender].push(intentHash);
        
        emit PreferenceSubmitted(intentHash, msg.sender, bid);
    }
    
    /**
     * @notice Solver 填充用戶意圖並索取 MEV 獎勵
     * @param intentHash 用戶意圖的哈希
     * @param payment Solver 實際索取的金額
     * @param proof Solver 提供的執行證明
     */
    function fillIntent(
        bytes32 intentHash,
        uint256 payment,
        bytes calldata proof
    ) external {
        Preference storage pref = preferences[intentHash];
        
        require(!pref.filled, "Already filled");
        require(payment <= pref.bid, "Payment exceeds bid");
        require(block.timestamp < pref.deadline, "Intent expired");
        
        // 驗證 Solver 確實執行了意圖
        require(verifyExecution(pref.calldata, proof), "Invalid execution proof");
        
        pref.filled = true;
        
        // 將差額退還給用戶(MEV 公平分配的關鍵)
        uint256 refund = pref.bid - payment;
        if (refund > 0) {
            (bool success, ) = pref.user.call{value: refund}("");
            require(success, "Refund failed");
        }
        
        // 支付給 Solver
        (bool paySuccess, ) = msg.sender.call{value: payment}("");
        require(paySuccess, "Payment failed");
        
        emit PreferenceFilled(intentHash, payment);
    }
    
    /**
     * @notice 驗證 Solver 確實執行了意圖
     */
    function verifyExecution(
        bytes calldata originalCalldata,
        bytes calldata proof
    ) internal view returns (bool) {
        // 這裡應該調用 Execution Gateway 的驗證邏輯
        // 實際實現會更複雜,需要驗證狀態根、事件日誌等
        return true;
    }
}

第四章:量化分析實戰

4.1 MEV 機會識別框架

並不是所有區塊都有 MEV 機會。讓我建立一個量化的識別框架。

MEV 機會評分(Opportunity Score):

OS = α × DEX_Volume + β × Liquidation_Total + γ × NFT_Activity - δ × Competition_Level

其中:

我通常用 Python 跑這個模型,實時計算每個區塊的 MEV 機會分數:

"""
MEV 機會識別與評分系統
"""
import asyncio
from web3 import Web3
from dataclasses import dataclass
from typing import List
import statistics

@dataclass
class MEVOpportunity:
    block_number: int
    dex_volume_usd: float
    liquidation_pending_usd: float
    nft_activity_score: float
    active_searchers: int
    opportunity_score: float
    
    def should_exploit(self, min_profit_usd: float = 100) -> bool:
        """
        簡單決策:利潤是否超過門檻
        實際系統會用更複雜的博弈模型
        """
        # 粗略估計:機会分數 × 係數
        estimated_profit = self.opportunity_score * 0.001
        return estimated_profit >= min_profit_usd


class MEVOpportunityScanner:
    def __init__(self, w3: Web3, flashbots_key: str):
        self.w3 = w3
        self.flashbots_key = flashbots_key
        
        # 權重參數(需要根據歷史數據校準)
        self.weights = {
            'alpha': 0.4,   # DEX 交易量權重
            'beta': 0.3,    # 待清算頭寸權重
            'gamma': 0.2,   # NFT 活動權重
            'delta': 0.1,   # 競爭激烈程度權重
        }
        
        self.n_blocks_window = 5  # 滾動窗口大小
        
    async def scan_opportunities(self, blocks: List[int]) -> List[MEVOpportunity]:
        """
        掃描一系列區塊的 MEV 機會
        """
        opportunities = []
        
        for block_num in blocks:
            block_data = await self._fetch_block_data(block_num)
            opportunity = self._calculate_score(block_data)
            opportunities.append(opportunity)
            
        return opportunities
    
    async def _fetch_block_data(self, block_num: int) -> dict:
        """
        獲取單個區塊的 MEV 相關數據
        """
        block = self.w3.eth.get_block(block_num, full_transactions=True)
        
        # 估算 DEX 交易量(需要結合 Dune/DeFi Llama API)
        dex_volume = self._estimate_dex_volume(block['transactions'])
        
        # 從鏈上事件估算待清算頭寸
        liquidation_pending = self._estimate_liquidation_exposure(block_num)
        
        # 估算 NFT 活動
        nft_score = self._estimate_nft_activity(block['transactions'])
        
        # 從 Flashbots API 獲取活躍搜尋者數量
        active_searchers = await self._get_flashbots_stats(block_num)
        
        return {
            'block_number': block_num,
            'dex_volume_usd': dex_volume,
            'liquidation_pending_usd': liquidation_pending,
            'nft_activity_score': nft_score,
            'active_searchers': active_searchers,
            'gas_used': block['gasUsed'],
            'base_fee': block['baseFeePerGas']
        }
    
    def _calculate_score(self, block_data: dict) -> MEVOpportunity:
        """
        計算 MEV 機會評分
        """
        # 對數變換處理大範圍數值
        import math
        
        dex_component = math.log1p(block_data['dex_volume_usd'])
        liq_component = math.log1p(block_data['liquidation_pending_usd'])
        nft_component = block_data['nft_activity_score']
        comp_component = block_data['active_searchers']
        
        score = (
            self.weights['alpha'] * dex_component +
            self.weights['beta'] * liq_component +
            self.weights['gamma'] * nft_component -
            self.weights['delta'] * comp_component
        )
        
        return MEVOpportunity(
            block_number=block_data['block_number'],
            dex_volume_usd=block_data['dex_volume_usd'],
            liquidation_pending_usd=block_data['liquidation_pending_usd'],
            nft_activity_score=block_data['nft_activity_score'],
            active_searchers=block_data['active_searchers'],
            opportunity_score=score
        )
    
    def _estimate_dex_volume(self, transactions: list) -> float:
        """
        估算區塊中的 DEX 交易量
        通過識別已知的 DEX 合約地址
        """
        DEX_ADDRESSES = {
            # Uniswap V2
            '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D': 2000,
            # Uniswap V3
            '0xE592427A0AEce92De3Edee1F18E0157C05861564': 2000,
            # SushiSwap
            '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F': 2000,
        }
        
        total_volume = 0.0
        
        for tx in transactions:
            to_addr = tx['to']
            if to_addr in DEX_ADDRESSES:
                # 假設每筆 DEX 交易平均 2000 USD
                # 實際應調用合約讀取準確金額
                total_volume += 2000
        
        return total_volume
    
    def _estimate_liquidation_exposure(self, block_num: int) -> float:
        """
        估算區塊中的待清算頭寸規模
        讀取 Aave/MakerDAO 等協議的清算觸發事件
        """
        # 簡化版本:讀取最近 N 個區塊的清算事件
        # 實際應監控所有借貸協議的頭寸
        aave_liquidations = self._get_events(
            '0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9',
            'LiquidationCall',
            block_num - 10,
            block_num
        )
        
        total_liquidation_value = sum(
            liquidate.collateral_value for liquidate in aave_liquidations
        )
        
        return total_liquidation_value
    
    async def _get_flashbots_stats(self, block_num: int) -> int:
        """
        從 Flashbots API 獲取區塊的 MEV 統計
        """
        # Flashbots 提供區塊級別的統計數據
        # 包括bundles數量、搜尋者數量等
        url = f"https://relay.flashbots.net/v1/blocks/{block_num}/stats"
        
        try:
            async with aiohttp.ClientSession() as session:
                async with session.get(url) as resp:
                    data = await resp.json()
                    return data.get('searchers', 0)
        except:
            return 0

4.2 MEV 利潤回測框架

搜尋者的策略到底賺不賺錢?這裡提供一個完整的回測框架:

"""
MEV 策略回測引擎
"""
from dataclasses import dataclass
from typing import List, Dict
import json

@dataclass
class TradeResult:
    block_number: int
    strategy: str          # 'sandwich', 'arbitrage', 'liquidation'
    gross_profit_wei: int
    gas_cost_wei: int
    net_profit_wei: int
    success: bool
    error_reason: str = ""
    
    def to_dict(self) -> dict:
        return {
            'block': self.block_number,
            'strategy': self.strategy,
            'gross_wei': self.gross_profit_wei,
            'gas_wei': self.gas_cost_wei,
            'net_wei': self.net_profit_wei,
            'success': self.success,
            'error': self.error_reason
        }


class MEVBacktester:
    def __init__(self, rpc_url: str):
        self.w3 = Web3(Web3.HTTPProvider(rpc_url))
        self.results: List[TradeResult] = []
    
    async def backtest_sandwich(
        self,
        start_block: int,
        end_block: int,
        target_addresses: List[str]
    ) -> Dict:
        """
        回測三明治攻擊策略
        """
        for block_num in range(start_block, end_block + 1):
            # 讀取區塊內所有交易
            block = self.w3.eth.get_block(block_num, full_transactions=True)
            
            for tx in block['transactions']:
                if not tx['to'] or tx['to'].lower() not in [a.lower() for a in target_addresses]:
                    continue
                
                # 識別潛在目標交易(大額 swap)
                swap_value = tx['value']
                if swap_value < self._min_swap_threshold():
                    continue
                
                # 嘗試構造三明治並估算利潤
                result = await self._simulate_sandwich(tx, block_num)
                self.results.append(result)
        
        return self._generate_report()
    
    def _generate_report(self) -> Dict:
        """
        生成回測報告
        """
        successful = [r for r in self.results if r.success]
        failed = [r for r in self.results if not r.success]
        
        if not successful:
            return {
                'total_attempts': len(self.results),
                'success_rate': 0.0,
                'total_profit_wei': 0,
                'average_profit_wei': 0,
                'total_gas_cost_wei': 0,
                'profit_per_attempt_wei': 0
            }
        
        total_profit = sum(r.net_profit_wei for r in successful)
        total_gas = sum(r.gas_cost_wei for r in successful)
        
        return {
            'total_attempts': len(self.results),
            'successful': len(successful),
            'failed': len(failed),
            'success_rate': len(successful) / len(self.results),
            'total_profit_wei': total_profit,
            'total_profit_eth': Web3.fromWei(total_profit, 'ether'),
            'total_gas_cost_wei': total_gas,
            'average_profit_wei': total_profit / len(successful),
            'profit_per_attempt_wei': total_profit / len(self.results),
            # 失敗原因分析
            'failure_reasons': self._analyze_failures(failed)
        }
    
    def _analyze_failures(self, failed: List[TradeResult]) -> Dict:
        """分析失敗原因分布"""
        reasons = {}
        for r in failed:
            reasons[r.error_reason] = reasons.get(r.error_reason, 0) + 1
        return reasons
    
    def _min_swap_threshold(self) -> int:
        """過濾小額 swap(不值得攻擊)"""
        return Web3.toWei(1, 'ether')  # 至少 1 ETH

第五章:MEV 的哲學與倫理

5.1 MEV 的「善惡」之辯

說到 MEV,很多人的立場是兩極化的。要麼覺得搜尋者是「區塊鏈的寄生蟲」,要麼覺得這是「市場效率的必然」。

我個人覺得這兩種觀點都有問題。

搜尋者確實做了什麼:

搜尋者同時破壞了什麼:

我的觀點:MEV 本身是中性的,問題在於機制的設計。如果拍賣機制是公平透明的,那 MEV 就像股票市場的高頻交易——有人從中獲利,但整個市場的價格發現效率提升了。但如果機制是黑箱的、權力是集中的,那 MEV 就成了少數人掠奪多數人的工具。

5.2 對抗 MEV 的實用策略

作為普通用戶,你不是完全無奈的。這裡有幾個實用策略:

  1. 使用私有交易池:部分 DEX(如 1inch Fusion、CoW Protocol)提供訂單流拍賣,支付給用戶而不是搜尋者
  2. 設置合理的滑點:滑點不是越大越好,攻擊者利用的就是這個
  3. 避開高峯期:在市場波動大時,MEV 機會最多,普通用戶最容易被夾
  4. 使用 TWAP/V2WAP:大額交易分批執行,降低單筆金額的吸引力
  5. Layer 2:Optimism/Arbitrum 的交易順序相對簡單,MEV 機會少很多

結語

MEV 這個話題,說一千道一萬,最核心的就是一句話:區塊空間是有價值的商品,誰控制了排序誰就掌握了分配權。

從數學上,MEV 的計算並不複雜——核心就是交易執行的邊際價值差。但問題在於,這個市場目前還沒有實現真正的公平與透明。Flashbots 邁出了第一步,SUAVE 在探索第二步,但路還很長。

對於工程師來說,理解 MEV 的數學和工程實作,不只是為了「做攻擊者」——更重要的是,你能用這些知識來設計更公平的協議、開發更安全的工具、或者單純地在這個複雜系統裡保護自己。

搞懂底層邏輯,永遠比被動接受別人的定義更有力量。


參考文獻

  1. Daian, P., et al. (2020). Flash Boys 2.0: Frontrunning, Transaction Reordering, and Consensus Instability in Decentralized Exchanges. arXiv preprint.
  2. Eskandari, S., et al. (2021). SoK: Transparency vs. Privacy in Decentralized Exchanges. FC '21.
  3. Flashbots Research. (2023). SUAVE: The Future of MEV Infrastructure. Flashbots Docs.
  4. Zhou, L., et al. (2023). Order Flow, Auction Design, and Ethereum's MEV. Informal publication.
  5. Angeris, G., et al. (2022). An Analysis of Uniswap Markets. IEEE S&P.

本網站內容僅供教育與資訊目的,不構成任何技術建議或投資建議。MEV 策略涉及高度風險,包括智能合約風險、市場風險和操作風險。在部署任何 MEV 相關系統前,請進行充分測試並諮詢專業人士意見。

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!