MEV(最大可提取价值)是指通过选择哪些交易进入区块、以何种顺序进入以及排除哪些交易,在标准区块奖励和 Gas 费之外可捕获的利润。在加密货币中,它不是单一攻击,而是叠加在区块生产之上的经济层:搜索者发现机会,构建者组装区块,验证者或排序器最终确定区块。本文解释其机制,演示一种可复现的方法来衡量你自己 RPC 流量上的排序效应,并将有文档记录的协议行为与你必须自行运行的测量区分开来。文末包含故障排查以及不同缓解方案的权衡。
加密货币中 MEV 的含义及其来源
最大可提取价值(MEV)是指通过在一个区块内包含、排除或重新排序交易,在标准区块奖励和 Gas 费之外从区块生产中可提取的最大价值。该术语最初在 PoW 下称为“矿工可提取价值”,当时矿工控制交易包含和排序。网络转向 PoS 后,验证者接管了这些角色,因此更宽泛的术语现在成为标准。底层机制没有改变:谁控制排序,谁就控制了对执行的一个虽小但具有经济意义的自由度。
这种自由度之所以存在,是因为区块空间稀缺且交易不可交换。一笔改变池价格的兑换、一笔翻转健康因子的清算,或一笔售罄供应上限的铸造,都会因前后执行内容不同而产生不同结果。MEV 就是这种不可交换性的货币化。它本身并非恶意;弥合不同场所之间价差的套利也是 MEV,它在竞争相同排序权的同时提高了市场效率。
- 排序:将一笔交易置于另一笔之前或之后,以改变其经济结果。
- 包含:决定一笔盈利或亏损交易是否进入区块。
- 排除:审查或延迟交易,这本身也可能有利可图。
- 区块空间稀缺:使排序具有价值的根本约束。
本指南的假设与前提条件
本文假设你了解交易如何进入内存池、Gas 费或优先费如何影响包含,以及至少一条 EVM 链上的区块如何产生。你应能熟练调用 JSON-RPC 方法并阅读原始交易和收据对象。测量过程使用标准以太坊兼容 JSON-RPC 接口,因此适用于任何暴露 eth_getBlockByNumber 并返回完整交易体的端点。
有两条边界很重要。第一,有文档记录的协议行为(客户端或规范保证的内容)与特定时间在特定网络上观察到的行为是分开的;后者必须测量。第二,本页不推荐特定端点或提供商。关于 RPC 端点的具体选择,请使用 RPC Assistant,它直接涵盖端点与提供商选择。
MEV 供应链:搜索者、构建者和验证者
理论上,所有 MEV 都归区块生产者,因为只有生产者能保证一笔有利可图的交易被打包。实践中,大多数检测和执行由称为搜索者的独立参与者完成,他们运行算法处理待处理和 historial 链上数据以发现机会,并提交捕获这些机会的交易或捆绑包。搜索者在速度和愿意为包含支付的利润比例上竞争,因此大部分总 MEV 以优先费或构建者支付的形式向上传递。
在采用提议者-构建者分离(PBS)设计的网络上,构建者从捆绑包组装候选区块,并竞标获得提议其区块的权利。验证者(或 rollup 上的排序器)在出价中选择。这种分离使 MEV 可作为市场来衡量:出价、捆绑包和支付是可观察的产物,而非私密矿工决策。在没有 PBS 的网络上,生产者同时承担两个角色,市场透明度较低。
- 搜索者:发现机会并提交交易或捆绑包。
- 构建者:将捆绑包合并为候选区块并竞标包含。
- 提议者/验证者/排序器:选择并最终确定区块。
- 用户:提供创造机会的订单流,并承担大部分成本。
常见 MEV 策略及其对用户的影响
DEX 套利是最良性且竞争最激烈的类别:搜索者针对价格已偏离另一场所的池进行交易并捕获价差。清算结构类似,奖励第一个偿还不健康贷款的 keeper。广义抢跑复制任何有利可图的待处理交易,并以更高费用重新提交。三明治攻击是用户感受最直接的策略:搜索者在一笔大额兑换前买入,让受害者的交易推动价格,然后立即卖出,捕获受害者愿意容忍的滑点。
三明治攻击对用户的成本受兑换设置的滑点容忍度限制。更紧的容忍度减少可提取金额,但增加失败概率。这是真实的权衡,不是可以优化掉的设置,这也是滑点和 MEV 通常一起讨论的原因。
心智模型:MEV 作为排序拍卖
推理 MEV 最清晰的方式是将每个区块视为对交易顺序的小型拍卖。每个能影响顺序的参与者以费用或捆绑包支付的形式提交出价。获胜顺序最大化生产者收入,而不一定是最大化用户福利的顺序。因此,MEV 是收入最大化顺序与用户本会选择顺序之间的差距。
该模型解释了若干观察到的模式。竞争将搜索者利润率推向零,而向生产者的总支付保持高位。容易发现的机会吸引许多搜索者,因此费用竞争激烈。需要专有数据或资本的机会吸引较少参与者并保留更多利润。当你衡量 MEV 时,你实际上是在衡量你所观察流量上这场拍卖的规模。
衡量你自己 RPC 流量上的排序效应
你无法从单笔交易推断你流量上的 MEV。你需要区块上下文:哪些交易在你之前和之后执行,以及每一步的池状态。以下过程拉取包含完整交易体的近期区块,提取针对目标池的兑换,并记录每笔交易在其区块内的顺序。针对你控制或信任的端点运行它,并记录区块范围和时戳,以便结果可复现。
脚本仅使用标准 JSON-RPC 方法。将 RPC_URL 替换为你的端点,将 POOL 替换为你关心的池合约。它并不声称检测所有 MEV 模式;它生成分析这些模式所需的排序数据。
import os, json, urllib.request
RPC_URL = os.environ.get("RPC_URL", "RPC_URL")
POOL = os.environ.get("POOL", "0x0000000000000000000000000000000000000000")
SWAP_TOPIC = "0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822" # Uniswap V2 Swap
def rpc(method, params):
req = urllib.request.Request(
RPC_URL,
data=json.dumps({"jsonrpc": "2.0", "id": 1, "method": method, "params": params}).encode(),
headers={"Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=30) as resp:
body = json.loads(resp.read())
if "error" in body:
raise RuntimeError(body["error"])
return body["result"]
latest = int(rpc("eth_blockNumber", []), 16)
start = latest - 20
rows = []
for n in range(start, latest + 1):
block = rpc("eth_getBlockByNumber", [hex(n), True])
if not block or not block.get("transactions"):
continue
for idx, tx in enumerate(block["transactions"]):
to = (tx.get("to") or "").lower()
if to != POOL.lower():
continue
rows.append({
"block": n,
"tx_index": idx,
"tx_hash": tx["hash"],
"from": tx["from"],
"gas_price": int(tx.get("gasPrice", "0x0"), 16),
})
print(json.dumps(rows[:50], indent=2))
print(f"blocks_scanned={latest - start + 1} pool_txs={len(rows)}")验证测量并记录结果
成功运行会打印一个 JSON 数组,包含池交易及其区块号和区块内索引,随后是一行摘要。对照区块浏览器验证一两个区块的输出:交易哈希和索引应完全匹配。如果不匹配,你的端点返回的是链的不同视图,这本身就是一个值得记录的发现。
将结果记录在表格中,以便跨端点和时间窗口比较运行。不要填写你未测量的数字;在运行产生结果之前留空单元格。下面是一个有用的模板。
- | 运行 | 端点标签 | 区块范围 | 池交易数 | 区块内索引中位数 | 备注 |
- |-----|----------------|-------------|----------|--------------------------|-------|
- | 1 | RPC_URL (A) | latest-20..latest | (已测量) | (已测量) | 基线 |
- | 2 | RPC_URL (B) | 相同范围 | (已测量) | (已测量) | 比较一致性 |
诊断过程:比较两个端点
如果你怀疑端点对待处理或已确认交易给出陈旧或重排序的视图,对两个端点运行相同区块范围并比较结果。以下脚本比较每个区块的交易哈希和索引。已确认区块上的分歧表明一致性问题;待处理数据上的分歧是预期内的,不应视为故障。
这是诊断,不是基准测试。它告诉你两个端点是否对已确认状态达成一致,而不是哪个更快或在负载下更可靠。对于性能问题,请在受控窗口内分别测量延迟和错误率。
const RPC_A = process.env.RPC_A || "RPC_URL";
const RPC_B = process.env.RPC_B || "RPC_URL";
async function call(url, method, params) {
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
});
const body = await res.json();
if (body.error) throw new Error(JSON.stringify(body.error));
return body.result;
}
async function compareBlock(n) {
const [a, b] = await Promise.all([
call(RPC_A, "eth_getBlockByNumber", ["0x" + n.toString(16), false]),
call(RPC_B, "eth_getBlockByNumber", ["0x" + n.toString(16), false]),
]);
const ha = a.transactions.join(",");
const hb = b.transactions.join(",");
return { block: n, match: ha === hb, countA: a.transactions.length, countB: b.transactions.length };
}
(async () => {
const latest = parseInt(await call(RPC_A, "eth_blockNumber", []), 16);
const results = [];
for (let n = latest - 10; n <= latest; n++) results.push(await compareBlock(n));
console.table(results);
})();排查常见测量问题
大多数失败的 MEV 测量源于四个原因:端点不返回完整交易体,区块范围太小不包含任何相关活动,池地址或事件主题错误,或端点在视图略有不同的节点间负载均衡。每种都有不同的症状和修复方法。
如果 eth_getBlockByNumber 仅返回交易哈希,将第二个参数设为 true。如果扫描返回零笔池交易,扩大范围并对照已知交易确认池地址和事件主题。如果两个端点在已确认区块上不一致,将其视为一致性事件,并在端点重新同步后重跑。如果同一范围多次运行结果不同,你的端点可能路由到不同后端。
- 交易数组为空:检查布尔参数和区块号编码。
- 零匹配:验证池地址校验和与事件主题。
- 结果不一致:固定单个端点或使用专用节点。
- 超时:减少区块范围并添加带退避的重试。
局限性、权衡与缓解方法
仅凭排序数据不能证明意图。一笔看似被三明治攻击的交易可能是恰好夹住另一笔交易的合法套利。归因需要捆绑包级数据、构建者 API 或标准 JSON-RPC 之外的启发式方法。将任何来自原始区块数据的 MEV 估计视为带有假阳性的下界。
缓解措施有不同的权衡。更紧的滑点降低三明治攻击盈利能力,但增加失败兑换。私有交易中继减少抢跑暴露,但将信任集中于中继运营商。加密内存池和公平排序旨在协议层消除排序优势,但改变延迟和包含保证。批量拍卖和基于意图的路由将问题从排序转移到求解器竞争。这些都没有消除 MEV;每一种都改变了 MEV 被捕获的位置和谁承担成本。
下一步:插桩与基础设施选择
要进一步深入,将测量扩展到网络暴露的捆绑包级数据,跟踪每个区块的优先费分布,并将其与池活动关联。为每次运行保留固定区块范围和端点标签,以便结果保持可比。如果你需要一致、低方差的已确认和待处理数据访问,专用节点可消除混淆端点比较的负载均衡器方差;在确定方案前,请参阅专用节点选项和 RPC 定价概览。
关于端点与提供商选择,请使用 RPC Assistant 而非猜测。关于节点和 RPC 层如何配合的背景,区块链节点托管指南和 RPC 端点指南是有用的补充。如果你跨多条链工作,多链 RPC 端点指南涵盖影响 MEV 测量的运营差异。
- 每次测量运行固定一个端点并记录其标签。
- 将原始 JSON 输出与摘要表一起存储以便复现。
- 任何端点变更后重跑相同区块范围。
- 将一致性检查与延迟和吞吐量测试分开。