永续合约 07 - 永续合约中的 MEV

永续合约因为有杠杆和清算机制, MEV 的类型和收益都比现货 DeFi 多. 本文梳理永续合约特有的六类 MEV (清算抢跑, Oracle 抢跑, 三明治攻击, funding rate 套利, 跨市场套利, 清算级联), 对比 GMX/dYdX/Hyperliquid 各自的防护方案, 最后给出清算监控 Bot 的 Go 实现思路.

一、术语表

术语 英文 含义
清算 MEV Liquidation MEV 通过抢先执行清算交易获取的清算奖励
清算人 Liquidator / Keeper 监控并执行清算的链上参与者
清算罚金 Liquidation Penalty 被清算者额外损失的保证金比例, 部分给清算人
Oracle 抢跑 Oracle Front-running 在 oracle 价格更新前抢先交易, 利用已知的价格变动获利
三明治攻击 Sandwich Attack 在目标交易前后各插一笔交易, 利用价格影响获利
清算级联 Liquidation Cascade 大额清算引发价格下跌, 触发更多清算的连锁反应
基差交易 Basis Trade 利用现货与永续价格差异的套利策略
虚拟 AMM vAMM (Virtual AMM) 不持有真实流动性, 用虚拟储备计算价格的永续 DEX 模型
优先费 Priority Fee / Tip 用户给验证者的小费, 决定交易在区块中的排序
搜索者 Searcher 扫描 mempool/链上状态, 寻找并提取 MEV 的角色

二、永续 MEV 概述

2.1 Spot MEV vs Perp MEV

现货 MEV 和永续 MEV 的来源和规模有本质区别:

Spot MEV vs Perp MEV Spot MEV 主要类型: - DEX 套利 (价格差) - 三明治攻击 (swap 滑点) - 借贷清算 (Aave/Compound) 特点: - 1:1 资产交换, 无杠杆 - 利润 = 价差 x 数量 - 清算涉及实际资产转移 影响范围: 单笔交易本身 典型利润: $10 - $10K per tx 受限于 swap 金额和价差 Perp MEV 主要类型: - 清算 MEV (keeper 竞赛) - Oracle 抢跑 - Funding rate 套利 - 跨市场套利 (basis) 特点: - 杠杆放大一切 (10-50x) - 利润 = 价差 x 数量 x 杠杆 - 可触发清算级联 影响范围: 可波及整个市场 典型利润: $100 - $1M+ per event 杠杆 + 级联效应放大收益

2.2 为什么永续 MEV 更复杂

三个核心因素让永续 MEV 远比现货复杂:

杠杆放大效应: 10x 杠杆意味着 1% 的价格操纵可以造成 10% 的仓位盈亏. 搜索者可以用较小的资金撬动巨大的利润, 同时也能造成巨大的损害.

清算机制: 永续合约独有. 当保证金不足时必须强制平仓, 这创造了一个巨大的 MEV 市场: 清算人竞相抢夺清算奖励. 而清算的执行又会进一步影响市场价格.

Funding rate: 每 8h (或更频繁) 的资金费率结算创造了跨市场套利机会. 不同协议、CEX vs DEX 之间的 funding rate 差异, 是 searcher 持续关注的信号.

2.3 永续 MEV 的分类

永续 MEV 分类 永续 MEV 清算 MEV 最大来源, keeper 竞赛 Oracle 抢跑 利用 oracle 延迟, 适用于 GMX 等 三明治攻击 适用于 vAMM 型协议 Funding rate 套利 跨交易所套利 跨市场套利 basis trade, DEX-CEX 套利 清算级联利用 极端行情下的连锁反应

三、清算 MEV (Liquidation MEV)

3.1 最大的永续 MEV 来源

清算 MEV 是永续合约中最主要的 MEV 类型. 当交易者的保证金率低于维持保证金要求时, 任何人都可以调用 liquidate() 来强制平仓该头寸, 并获得一部分清算罚金作为奖励.

1
2
3
4
5
6
清算奖励 = 被清算头寸的 liquidation penalty (清算罚金) 的一部分

例: 100,000 USDC 的头寸, 5% liquidation penalty
penalty (罚金) = 5,000 USDC
→ keeper (清算人) 奖励 = 2,500 USDC (各协议比例不同)
insurance fund (保险基金) = 2,500 USDC

3.2 Keeper 竞赛

清算 MEV 的核心是一场速度竞赛: 谁先调用 liquidate() 谁拿到奖励:

Keeper 清算竞赛 ETH 价格下跌 → 某用户 margin ratio < maintenance margin 可清算! 多个 Keeper 同时检测到可清算头寸 通过监控链上状态 / 订阅事件 / 模拟计算 margin ratio Keeper A priority fee: 50 gwei latency: 120ms Keeper B priority fee: 200 gwei latency: 80ms Keeper C priority fee: 150 gwei latency: 200ms Keeper B 赢得清算 最高 priority fee + 最低延迟 → 第一个被打包 其他 keeper 的清算交易 revert (头寸已被清算), gas 白花 这就是为什么清算竞赛会推高 gas: "赢者通吃"

竞赛的关键因素:

因素 说明
Priority fee 出价越高, 在区块中排序越靠前
延迟 离验证者/builder 越近, 交易到达越快
检测速度 更快识别可清算头寸 (监控 + 计算)
Gas 估算 精确估算 gas limit, 避免失败
利润计算 确保 清算奖励 > gas 成本, 否则亏本

3.3 各协议的清算机制

不同协议对清算 MEV 的处理方式完全不同:

GMX: 外部 Keeper

1
2
3
4
5
6
7
8
GMX 清算流程:
1. 任何人都可以调用 liquidatePosition()
2. 链上检查: margin ratio (保证金率) < maintenance margin (维持保证金)?
3. 是 → 强制平仓, 扣除 liquidation fee (清算费)
4. Keeper (清算人) 获得固定清算费 (如 5 USD)
5. 剩余 penalty (罚金) 进入保险基金 (fee reserve)

清算奖励相对固定 → MEV 竞争集中在 "谁先提交"

dYdX v4: 验证者执行

1
2
3
4
5
6
7
8
dYdX v4 (Cosmos appchain) 清算流程:
1. validator (验证者) 节点自动检测可清算头寸
2. 验证者在出块时将清算作为 protocol operation (协议操作) 执行
3. 无需外部 keeper (清算人) 提交交易
4. 没有 mempool (交易池) 竞争 → 没有传统意义的清算 MEV

关键: 验证者有动力执行清算 (维持系统健康)
但验证者本身可能优先清算对自己有利的头寸

Hyperliquid: 协议自动执行

1
2
3
4
5
6
7
Hyperliquid 清算流程:
1. validator (验证者) 自动检测并执行清算
2. 被清算头寸进入 "liquidation auction (清算拍卖)"
3. HLP vault (协议 LP 金库) 作为清算头寸的接收方
4. 清算的 profit/loss (利润/损失) 由 HLP vault 承担

没有外部 keeper (清算人) 竞赛, MEV 被内化到协议中

四、Oracle 抢跑 (Oracle Front-running)

4.1 攻击原理

Oracle 抢跑主要针对使用链外 Oracle 定价的协议 (如 GMX). 核心思路: 在 oracle 价格更新上链之前, 利用已知的价格变动方向抢先开仓.

Oracle 抢跑攻击 (针对 GMX) 1 攻击者监控 CEX 价格 ETH 在 Binance 已经涨到 $2050, 但 GMX 的 Chainlink oracle 还是 $2000 价差 = 2.5% 2 抢先在 GMX 开 10x long 以旧 oracle 价格 $2000 开仓, 10x 杠杆, 成本: 入场费 + gas 3 Chainlink oracle 更新价格到 $2050 Oracle 有延迟 (heartbeat ~1s, deviation threshold ~0.5%) 4 立即平仓获利 PnL = 2.5% x 10x = 25% 利润 (减去手续费) 几乎无风险利润 因为攻击者已经知道价格要涨, 只需要在 oracle 更新前后完成一个来回

4.2 GMX 的防御: Two-Step Execution

GMX v2 引入了两步执行机制来对抗 oracle 抢跑:

1
2
3
4
5
6
7
8
9
10
传统 (v1, 易被抢跑):
用户提交开仓交易 → 立即以当前 oracle price (预言机价格) 执行

两步执行 (v2, 抗抢跑):
Step 1: 用户提交 "开仓请求" (createOrder)
↓ 等待 keeper (执行者) 执行 (至少 1 个 block delay (区块延迟))
Step 2: Keeper 调用 executeOrder(), 使用该时刻最新的 oracle price

关键: execution price (执行价格) 是 Step 2 时的价格, 不是 Step 1 时的
→ 攻击者无法锁定旧价格

即使攻击者在 Step 1 看到 oracle 即将更新, 到 Step 2 执行时 oracle 已经更新了, 价格优势消失.

额外防护:

机制 说明
Execution fee 用户预付 keeper 执行的 gas 费, 增加攻击成本
Position fee 开仓/平仓手续费 (0.05-0.1%), 侵蚀套利空间
Price impact 大单有额外的价格影响, 模拟滑点
Min block delay 开仓请求和执行之间至少间隔 N 个区块

即使有 two-step execution, Chainlink oracle 本身也有延迟:

1
2
3
4
5
6
7
8
Chainlink oracle 更新条件 (两者满足其一):
1. Heartbeat (心跳): 每 T 秒强制更新一次 (如 ETH/USD heartbeat = 3600s)
2. Deviation (偏差): 价格偏离超过阈值时更新 (如 0.5%)

在 heartbeat 间隔内, 如果价格缓慢移动且未触发 deviation threshold (偏差阈值),
oracle price (预言机价格) 可能落后于真实价格.

这个窗口虽然很小, 但高频交易者仍然可以利用.

五、三明治攻击 (Sandwich Attack on Perps)

5.1 vAMM 三明治攻击

三明治攻击主要影响使用 vAMM 定价的永续协议 (如早期 Perpetual Protocol). 原理与现货 AMM 三明治相同, 但因为涉及杠杆, 影响被放大:

vAMM 永续三明治攻击 vAMM 虚拟池: vETH / vUSDC | 当前虚拟价格: $2000 1 攻击者抢先开多 (front-run) 10x long, 大单推高 vAMM 虚拟价格: $2000 → $2020 2 受害者交易执行 以更高的虚拟价格 $2020 开多, 继续推高: $2020 → $2035 3 攻击者平仓 (back-run) 在 $2035 平仓, 虚拟价格回落到 $2015 攻击者利润 入场 $2000, 出场 $2035 10x leverage: +17.5% 利润 受害者损失 以 $2020 开仓, 实际价值 ~$2015 即时浮亏 -0.25% (杠杆放大)

5.2 哪些协议容易被三明治?

协议类型 三明治风险 原因
vAMM (Perpetual Protocol) 交易直接影响虚拟价格, 和现货 AMM 一样
Oracle 型 (GMX) 价格由 oracle 决定, 交易不影响 oracle 价格
订单簿 (dYdX, Hyperliquid) Maker/taker 模型, 无 AMM 滑点曲线

Oracle 型协议对三明治免疫, 因为无论你交易量多大, GMX 的价格都来自 Chainlink, 不会被你的交易推动. 但 GMX v2 引入了 price impact 机制 (模拟滑点), 大单仍会有额外成本.

订单簿型协议没有共享的流动性池, 每笔交易只和 maker 对手方成交, 没有可以被 “推动” 的价格曲线.


六、Funding Rate 套利

6.1 跨交易所 Funding Rate 差异

不同交易所的 funding rate 几乎不可能完全一致, 因为每个市场的多空比例不同:

1
2
3
4
5
6
7
8
9
Binance ETH-PERP  funding rate (资金费率): +0.03%  (多头付给空头)
dYdX ETH-PERP funding rate: +0.08% (多头付给空头)
GMX ETH-PERP funding rate: +0.01% (多头付给空头)

套利策略:
- 在 dYdX 开空 (收取 0.08% funding)
- 在 GMX 开多 (支付 0.01% funding)
- net profit (净收益): 0.08% - 0.01% = 0.07% per funding interval (资金费率结算周期)
- 价格风险对冲 (一多一空), 只赚 funding spread (费率差)

6.2 CEX vs DEX Funding Rate

CEX (如 Binance) 和 DEX 的 funding rate 经常存在显著差异:

场景 CEX Funding DEX Funding 策略
CEX 多头拥挤 +0.1% +0.02% CEX 开空 (收 0.1%) + DEX 开多 (付 0.02%)
DEX 空头拥挤 +0.01% -0.05% DEX 开多 (收 0.05%) + CEX 开空 (收 0.01%)

这种策略严格来说不是传统 MEV (不需要操纵交易排序), 但它利用了链上信息的可见性优势:

  • DEX 的 funding rate 可以实时从链上计算
  • CEX 的 funding rate 也是公开的
  • Searcher 可以在 funding 结算前精确计算套利空间

6.3 风险

Funding rate 套利不是无风险的:

风险 说明
执行风险 多空两侧交易不一定同时成交, 单侧成交期间暴露方向性风险
Funding 反转 Rate 可能在下次结算前改变方向
清算风险 价格剧烈波动时, 一侧可能被清算
手续费 开仓/平仓手续费可能吃掉利润
资金效率 需要在多个协议分别质押保证金

七、跨市场套利

7.1 现货-永续套利 (Basis Trade)

Basis trade 是利用现货与永续价格差 (基差) 的经典策略:

Basis Trade (现货-永续套利) 场景: 正基差 (永续溢价, perp > spot) 现货市场 买入 1 ETH @ $2000 做多现货 (持有资产) 永续市场 开空 1 ETH @ $2050 做空永续 (对冲方向风险) 基差收敛: perp 价格 → spot 价格 Funding rate 会持续把溢价压回去 利润来源 (两部分) 1. 基差收敛: $50/ETH | 2. 收取 funding payment (空头收钱)

7.2 DEX-CEX 永续套利

1
2
3
4
5
6
7
8
9
10
11
12
dYdX ETH-PERP: $2010
Binance ETH-PERP: $2000
spread (价差) = $10

套利:
- Binance 开多 @ $2000
- dYdX 开空 @ $2010
- 等 price convergence (价格收敛), 双边平仓
- profit (利润) ≈ $10/ETH (减 fee (手续费))

链上优势: dYdX 的 order book (订单簿) 状态公开透明,
searcher (搜索者) 可以看到所有挂单, 精确计算套利空间.

7.3 DEX-DEX 永续套利

不同 DEX 永续协议之间也存在价差:

1
2
3
4
5
6
7
8
9
10
11
GMX  ETH-PERP (oracle price (预言机价格)): $2000 (Chainlink)
dYdX ETH-PERP (order book (订单簿)): $2015
basis (基差) = $15

如果 GMX 还有 open interest (未平仓量) 额度:
- GMX 开多 @ $2000 (零 slippage (滑点), oracle 价)
- dYdX 开空 @ $2015
- 锁定 $15 差价

注意: GMX 有 position fee (持仓费) 和 borrow fee (借贷费),
实际 profit (利润) = basis - GMX 费用 - dYdX 费用

八、清算级联 (Liquidation Cascade)

8.1 级联机制

清算级联是永续合约市场最危险的现象之一: 一个大额清算可以触发连锁反应:

清算级联 (Liquidation Cascade) 触发事件 ETH 从 $2000 跌到 $1950 (-2.5%) 第一波清算 高杠杆多头 (20-50x) 被清算, 头寸被市价平仓 清算量: $50M 多头平仓 = 大量卖压 价格进一步下跌 清算卖压 → ETH 跌到 $1880 (-6%) 原本安全的 10x 多头现在也到了清算线 第二波清算 10x 多头被清算, 更多卖压 → ETH 跌到 $1750 (-12.5%) 连锁反应持续, 清算总量可达数亿美元 MEV Searcher 如何利用级联 1. 监控即将被清算的头寸列表 | 2. 预判清算会导致的价格影响 3. 提前开空获利 | 4. 同时竞争清算奖励 (双重收益)

8.2 历史案例

2022 年 LUNA/UST 崩盘: LUNA 价格螺旋下跌期间, 多个 DeFi 永续协议出现大规模清算级联. 部分协议的保险基金被完全耗尽, 导致系统性亏损 (socialized loss).

2023 年 3 月 SVB 事件: USDC 脱锚期间, 使用 USDC 作为保证金的永续协议出现异常. 保证金价值下降触发清算, 但被清算者并非因为交易方向错误, 而是因为抵押品贬值.

Hyperliquid 2025 年 3 月 JELLY 事件: 攻击者操纵 JELLY token 价格, 在 Hyperliquid 上开巨额空头后自我清算, 迫使 HLP vault 接管空头头寸. 随后在现货市场拉升 JELLY 价格, 使 HLP vault 面临巨额浮亏. 最终 Hyperliquid 验证者投票以有利价格强制平仓, 引发社区对去中心化的讨论.

8.3 协议的级联防护

防护机制 说明 采用协议
保险基金 清算不足时由保险基金补贴, 避免穿仓 标配: 所有永续合约平台
OI 上限 限制单边未平仓量, 降低清算冲击 标配: 所有永续合约平台
ADL (Auto-Deleveraging) 保险基金耗尽时, 强制减仓盈利最多的对手方; 极端情况下可能失效 (无盈利对手方可减) 标配: 所有永续合约平台
动态维持保证金 头寸越大, 要求的维持保证金率越高 标配: 所有永续合约平台
阶梯清算 不一次性清算全部头寸, 分批清算减少价格冲击 dYdX (每区块限制清算量), Hyperliquid, Binance
最终兜底 ADL 之外的额外安全网 Hyperliquid: HLP vault 接管; GMX: GLP/GM 池承担; dYdX: 坏账挂账 + 治理介入

九、各协议 MEV 防护措施对比

MEV 类型 GMX v2 dYdX v4 Hyperliquid vAMM (Perp Protocol)
清算 MEV 外部 keeper 执行, 赚执行费 协议清算引擎生成订单匹配订单簿, 盈亏归保险基金 协议清算引擎先匹配订单簿, 无人接则 HLP 兜底 外部 keeper, 竞争奖励
Oracle 抢跑 Two-step execution + price impact Slinky sidecar + VoteExtensions 共识聚合, 每区块更新 验证者从 8 个 CEX 加权中位数, 每 3s 更新 依赖 Chainlink, 有延迟风险
三明治 免疫 (oracle 定价) 低风险 (订单簿) 低风险 (订单簿) 高风险 (vAMM 价格曲线)
Funding 套利 存在 (rate 差异) 存在 (rate 差异) 存在 (rate 差异) 存在 (rate 差异)
跨市场套利 存在 (oracle vs 市场价差) 较少 (实时撮合) 较少 (实时撮合) 存在 (vAMM 偏移)
清算级联防护 保险基金 + ADL + OI 上限 保险基金 + ADL + 每区块清算量限制 保险基金 + ADL + HLP vault 兜底 保险基金 (早期不足)

总结:

  • dYdX v4 MEV 防护最强: 验证者内化了大部分 MEV (清算 + oracle), 但验证者本身的行为需要治理约束
  • GMX v2 通过 two-step execution 有效抵御 oracle 抢跑, 但清算仍是外部竞争
  • Hyperliquid 将清算内化到 HLP vault, 减少了 keeper 竞赛, 但 HLP 持有者承担风险
  • vAMM 协议 MEV 暴露面最大, 这也是 vAMM 模型逐渐被淘汰的原因之一

十、Go: 清算监控 Bot 架构

一个基本的永续合约清算 Bot 需要: 监听链上头寸状态, 实时计算 margin ratio, 判断可清算, 提交清算交易.

以下是核心模块的实现思路:

10.1 数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package liquidator

import (
"math/big"
"sync"
)

// Position 表示链上读取的永续头寸 (perpetual position)
type Position struct {
Account string // 持仓者地址
Market string // 交易对 (trading pair), 如 "ETH-USD"
IsLong bool // true = long (多), false = short (空)
Size *big.Int // 头寸大小 (position size, scaled, 如 1e30)
Collateral *big.Int // 抵押品金额 (collateral amount, scaled)
EntryPrice *big.Int // 开仓均价 (entry price, scaled)
LastUpdated uint64 // 上次更新的区块号
}

// MarketConfig 协议的市场参数
type MarketConfig struct {
MaintenanceMarginRate *big.Int // 维持保证金率 (maintenance margin rate), 如 0.005 = 0.5%
LiquidationFee *big.Int // 清算费率 (liquidation fee rate)
MaxLeverage uint64 // 最大杠杆 (max leverage)
}

// LiquidationCandidate 可清算的候选头寸
type LiquidationCandidate struct {
Position Position
MarginRatio *big.Int // 当前保证金率
Profit *big.Int // 预估清算利润 (扣除 gas)
}

// PositionStore 维护所有已知头寸的本地缓存
type PositionStore struct {
mu sync.RWMutex
positions map[string]*Position // key: account + market
}

func NewPositionStore() *PositionStore {
return &PositionStore{
positions: make(map[string]*Position),
}
}

func (s *PositionStore) Upsert(p *Position) {
s.mu.Lock()
defer s.mu.Unlock()
key := p.Account + ":" + p.Market
s.positions[key] = p
}

func (s *PositionStore) All() []*Position {
s.mu.RLock()
defer s.mu.RUnlock()
result := make([]*Position, 0, len(s.positions))
for _, p := range s.positions {
result = append(result, p)
}
return result
}

10.2 保证金率计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package liquidator

import "math/big"

var (
precision = new(big.Int).Exp(big.NewInt(10), big.NewInt(30), nil) // 1e30
)

// CalcMarginRatio 计算头寸的当前保证金率
// marginRatio (保证金率) = (collateral (抵押品) + unrealizedPnL (未实现盈亏)) / positionValue (头寸价值)
// 返回值 scaled by 1e30
func CalcMarginRatio(pos *Position, markPrice *big.Int) *big.Int {
// positionValue = size * markPrice / precision
positionValue := new(big.Int).Mul(pos.Size, markPrice)
positionValue.Div(positionValue, precision)

if positionValue.Sign() == 0 {
return new(big.Int) // avoid division by zero
}

// unrealizedPnL (未实现盈亏) for long: (markPrice (标记价格) - entryPrice (开仓价)) * size (头寸规模) / precision
// unrealizedPnL for short: (entryPrice - markPrice) * size / precision
priceDiff := new(big.Int)
if pos.IsLong {
priceDiff.Sub(markPrice, pos.EntryPrice)
} else {
priceDiff.Sub(pos.EntryPrice, markPrice)
}
unrealizedPnL := new(big.Int).Mul(priceDiff, pos.Size)
unrealizedPnL.Div(unrealizedPnL, precision)

// effectiveMargin (有效保证金) = collateral + unrealizedPnL
effectiveMargin := new(big.Int).Add(pos.Collateral, unrealizedPnL)

// marginRatio = effectiveMargin * precision / positionValue
ratio := new(big.Int).Mul(effectiveMargin, precision)
ratio.Div(ratio, positionValue)

return ratio
}

// IsLiquidatable 判断头寸是否可被清算
func IsLiquidatable(pos *Position, markPrice *big.Int, config MarketConfig) bool {
ratio := CalcMarginRatio(pos, markPrice)
return ratio.Cmp(config.MaintenanceMarginRate) < 0
}

// EstimateLiquidationProfit 预估清算利润
func EstimateLiquidationProfit(pos *Position, config MarketConfig, gasCostUSD *big.Int) *big.Int {
// liquidationReward (清算奖励) = positionSize (头寸规模) * liquidationFee (清算费率) / precision
reward := new(big.Int).Mul(pos.Size, config.LiquidationFee)
reward.Div(reward, precision)

// profit (利润) = reward - gasCost (gas 成本)
profit := new(big.Int).Sub(reward, gasCostUSD)
return profit
}

10.3 监控循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package liquidator

import (
"context"
"log"
"math/big"
"time"
)

// PriceOracle 获取最新标记价格的接口
type PriceOracle interface {
GetMarkPrice(ctx context.Context, market string) (*big.Int, error)
}

// LiquidationExecutor 提交清算交易的接口
type LiquidationExecutor interface {
// SubmitLiquidation 提交清算交易, 返回 tx hash
SubmitLiquidation(ctx context.Context, pos *Position) (string, error)
// EstimateGas 估算清算交易的 gas 成本 (USD)
EstimateGas(ctx context.Context, pos *Position) (*big.Int, error)
}

// Monitor 清算监控器
type Monitor struct {
store *PositionStore
oracle PriceOracle
executor LiquidationExecutor
configs map[string]MarketConfig // market -> config
interval time.Duration
}

func NewMonitor(
store *PositionStore,
oracle PriceOracle,
executor LiquidationExecutor,
configs map[string]MarketConfig,
interval time.Duration,
) *Monitor {
return &Monitor{
store: store,
oracle: oracle,
executor: executor,
configs: configs,
interval: interval,
}
}

// Run 启动监控循环
func (m *Monitor) Run(ctx context.Context) error {
ticker := time.NewTicker(m.interval)
defer ticker.Stop()

log.Printf("liquidation monitor started, interval=%v", m.interval)

for {
select {
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
m.scanAndLiquidate(ctx)
}
}
}

func (m *Monitor) scanAndLiquidate(ctx context.Context) {
positions := m.store.All()
candidates := make([]LiquidationCandidate, 0)

for _, pos := range positions {
config, ok := m.configs[pos.Market]
if !ok {
continue
}

markPrice, err := m.oracle.GetMarkPrice(ctx, pos.Market)
if err != nil {
log.Printf("failed to get mark price for %s: %v", pos.Market, err)
continue
}

if !IsLiquidatable(pos, markPrice, config) {
continue
}

gasCost, err := m.executor.EstimateGas(ctx, pos)
if err != nil {
log.Printf("failed to estimate gas for %s: %v", pos.Account, err)
continue
}

profit := EstimateLiquidationProfit(pos, config, gasCost)
if profit.Sign() <= 0 {
log.Printf("skip %s: profit %v <= 0 (gas too high)", pos.Account, profit)
continue
}

candidates = append(candidates, LiquidationCandidate{
Position: *pos,
MarginRatio: CalcMarginRatio(pos, markPrice),
Profit: profit,
})
}

// 按利润排序, 优先清算利润最高的
// (实际生产中应并发提交, 这里简化)
for _, c := range candidates {
log.Printf("liquidating %s %s, margin ratio=%v, est profit=%v",
c.Position.Account, c.Position.Market, c.MarginRatio, c.Profit)

txHash, err := m.executor.SubmitLiquidation(ctx, &c.Position)
if err != nil {
log.Printf("liquidation failed: %v", err)
continue
}
log.Printf("liquidation submitted: %s", txHash)
}
}

10.4 架构总结

Liquidation Bot (清算机器人) 架构 Position Store (头寸缓存) 本地缓存所有已知头寸, 通过事件监听实时更新 Event Listener 事件监听 Price Oracle 价格预言机 Margin Calculator 保证金计算 Liquidation Engine 清算引擎 profit > 0? gas 够吗? TX Sender 交易发送器 动态 gas / nonce 管理 / 重试逻辑 生产级优化 1. 用 WebSocket 订阅新区块, 不用 polling 2. 批量读取头寸状态 (multicall) 3. 使用 Flashbots 提交, 避免 revert 浪费 gas 4. 预计算 "接近清算" 的头寸, 优先监控 5. 多链并行监控 (Arbitrum, Optimism, etc.)

十一、小结

核心要点

  1. 清算 MEV 是永续合约最大的 MEV 来源: keeper 竞赛推高 gas, 但也保障了系统安全
  2. Oracle 抢跑 是 Oracle 型协议的核心挑战, two-step execution 是主要防御手段
  3. 三明治攻击 主要影响 vAMM 协议, Oracle 型和订单簿型天然免疫
  4. 清算级联 是系统性风险, ADL 和 OI 上限是主要防护
  5. 协议设计决定 MEV 暴露面: dYdX v4 通过验证者内化 MEV, GMX 通过 two-step execution 降低 MEV, vAMM 模型 MEV 暴露最大

MEV 的两面性

  • 正面: 清算 MEV 激励 keeper 维持系统健康, 套利 MEV 促进价格一致
  • 负面: 推高 gas 费, 增加用户成本, 清算级联可能加剧市场波动

下一步

《永续合约数据解析实战》将实现完整的链上交互:

  • 读取 GMX/dYdX 的真实头寸数据
  • 计算实时 funding rate
  • 构建可运行的清算/套利监控工具

永续合约 07 - 永续合约中的 MEV
https://mritd.com/2025/10/05/perp-mev/
作者
Kovacs
发布于
2025年10月5日
许可协议