Web3中的抢跑交易与理解MEV

本文深入解释了区块链中的抢跑交易(Frontrunning)和最大可提取价值(MEV)现象。通过公开内存池(mempool)机制,分析了恶意机器人如何利用用户交易信息进行三明治攻击,导致用户损失。文章提供了易受攻击的智能合约代码示例及修复方法(强制滑点检查),并介绍了更高级的MEV策略如尾随交易和即时流动性(JIT Liquidity),后者虽可改善交易执行但损害被动流动性提供者收益。结论强调开发者应在智能合约中实施滑点保护,以应对公共内存池带来的风险。

引言

想象一位交易者在市场中发现了有利可图的交易机会。他们签署交易,点击确认,然后等待。但当交易最终确认时,他们发现买入代币的价格高于预期。在去中心化交易所(DEX)交易时,这并不罕见;他们可能被 MEV 机器人盯上了。

区块链交易的公开性带来了一些挑战。虽然透明度确保了可信度,但用户的意图在最终执行前就暴露了。从签署交易到交易被纳入区块之间的时间差,正是抢先交易可能发生的窗口。

作为智能合约安全研究人员,我们亲身见证了抢先交易的影响,尤其是在自动做市商(AMM)中。抢先其他用户的交易是一种复杂的经济操作,每年从用户和协议中榨取数百万美元。

抢先交易解析:内存池(Mempool)

要理解抢先交易的含义,首先必须了解内存池。当一笔交易被发送到网络(例如,在 Uniswap 这样的 DEX 上进行一笔兑换交易),该交易并不会立即执行。它进入一个称为内存池的"等待区",直到被验证节点拾取并确认。

验证者受到经济激励,会优先处理支付最高 gas 费用的交易。

什么是抢先交易?

抢先交易是指发现内存池中待处理的交易(例如代币兑换交易),并提交自己的交易,支付更高的 gas 费用,以确保它在原始交易之前被处理。

由于交易数据是可见的,复杂的机器人可以针对 AMM 的当前状态模拟交易。如果它们发现盈利机会,就会复制交易逻辑,支付更高的"贿赂"(gas 费用),并在原始交易之前让它们的交易被处理。

加密货币中的最大可提取价值(MEV)

抢先交易只是更大现象——MEV——的一个方面。最初,这个缩写代表矿工可提取价值(Miners Extractable Value),指矿工通过处理区块链交易可以提取的利润。

加密货币中的 MEV 已经发展成为一种工业规模的运作,涉及"搜索者"(运行机器人的实体)和"构建者"(打包交易的实体)。虽然有些 MEV 是无害的,但"有毒"的 MEV,例如三明治攻击,会直接损害用户利益。

攻击向量:三明治攻击与滑点

在 AMM 中最臭名昭著的抢先交易形式是三明治攻击。当 MEV 机器人检测到内存池中有一笔待处理的大额兑换订单时,就会发生这种情况。

在一个依赖恒定乘积公式 x * y = k 的 AMM 中,一笔大额买入订单必然会导致价格上涨。为了演示攻击如何展开,让我们看看一些有漏洞的代码。

AMM 漏洞代码示例

抢先交易经常利用设计不良的滑点检查。考虑这个忽略了最小输出金额的简单兑换函数:

// 有漏洞的代码示例
function naiveSwap(address tokenIn, uint256 amountIn) external {
// 假设在实际兑换前进行了一些检查

    // 与 AMM 的交互
    IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
    // 没有指定最小输出金额 -> 没有滑点保护
    AMM.swap(tokenIn, amountIn, 0);
}

在上述代码中,swap 函数中的 0 代表 100% 的滑点容忍度。抢先交易机器人看到这一点后就知道,它们可以在该交易执行前操纵价格,而交易仍然会成功,迫使受害者在极其不利的价格下进行兑换。

"三明治攻击"详解

假设 Alice 调用上述 naiveSwap 函数,用 10,000 USDC 兑换 ETH。

  • 当前市场价格:1 ETH = 2,000 USDC。
  • Alice 的预期:她期望收到大约 5 ETH。

图 1: 三明治攻击通过三笔有序交易执行。机器人的抢跑交易抬高价格;受害者的交易在该抬高价格上以无滑点底线完成结算;尾随交易让机器人获利。

攻击在内存池中是这样展开的:

  1. 设置(抢跑交易): 机器人发现 Alice 的交易。在她的交易执行之前,机器人支付更高的 gas 费用从池中大量买入 ETH。机器人的买入行为耗尽了池中的 ETH 供应,暂时造成不平衡的兑换比率,使得新的价格为 1 ETH = 10,000 USDC。
  2. 受害者执行: Alice 的 naiveSwap 交易随后执行。
    • 输入:10,000 USDC。
    • 问题:因为最小输出金额设置为 0,合约接受任何价格作为有效价格。
    • 执行:她以新的抬高价格 10,000 USDC/ETH 买入 ETH。
    • Alice 收到:1 ETH(而不是她预期的 5 ETH)。
  3. 获利(尾随交易): 在 Alice 买入之后(进一步推高价格),机器人立即将所有 ETH 卖回给池子。
    • 机器人利润:机器人实际上以 400% 的加价将 ETH 卖给了 Alice。
    • Alice 的损失:Alice 损失了 80% 的价值(价值 8,000 美元的 ETH),因为 naiveSwap 函数缺少滑点检查。

修复漏洞:强制执行滑点

为了防止这种情况,智能合约必须强制执行严格的滑点参数。

// 安全的代码示例
function secureSwap(
    address tokenIn,
    uint256 amountIn,
    uint256 minAmountOut, // 滑点保护
) external {
// 假设在实际兑换前进行了一些检查
    IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);

    // 如果抢跑交易将返回金额推低至 minAmountOut 以下,该交易将回滚
    uint256 actualAmountOut = AMM.swap(tokenIn, amountIn, minAmountOut);
    require(actualAmountOut >= minAmountOut, "输出金额不足");
}

通过添加 minAmountOut 参数并强制执行 actualAmountOut >= minAmountOut,用户定义了其愿意接受的"最差"价格。如果三明治机器人试图抢先该交易以改变价格,最后的 require 语句会导致受害者的交易回滚,从而保护其资金。攻击者的策略失败,MEV 机器人会浪费 gas 费用。

高级 MEV:尾随交易与即时流动性

并非所有 MEV 策略都涉及抢先交易。有些发生在目标交易之后

尾随交易是指提交一笔旨在目标交易之后立即执行的交易。常见的例子是套利。

  • 场景: 用户在 Uniswap 上进行了一笔大额交易,改变了 ETH/USDC 交易对的价格。
  • 尾随交易: 机器人看到后,立即在 Uniswap 上买入 ETH,并在 Sushiswap 上卖出,以重新平衡价格。这通常被认为是"良性"MEV,因为它使市场更有效率。

即时流动性(JIT Liquidity)

在集中流动性 AMM(如 Uniswap V3)中,更复杂的 MEV 形式是即时流动性。

  1. LP 机器人发现内存池中有一笔待处理的大额交易。
  2. 它们通过抢跑交易,在即将执行交易的价格区间内集中添加大量流动性(Uniswap 的上下刻度)。
  3. 机器人从该大额兑换中赚取大部分交易费用。
  4. 机器人立即通过尾随交易移除该流动性。

虽然 JIT 流动性实际上为交易者提供了更好的执行价格(由于流动性更深,价格影响更小),但它通过窃取收益来打击被动流动性提供者(LP),因为该笔特定兑换的大部分费用将流向 MEV 机器人,而不是长期 LP 头寸持有者。

JIT 流动性:数值示例

假设一个 USDC/ETH 池,兑换费率为 0.3%。

初始状态:

  • LP 提供者 1(Bob):在活跃价格范围内提供了价值 50,000 美元的流动性。
  • LP 提供者 2(Charlie):在同一活跃范围内提供了价值 50,000 美元的流动性。
  • 总活跃流动性:100,000 美元。

如果此时发生一笔交易,Bob 和 Charlie 将按 50/50 平分兑换费,因为他们各拥有 50% 的活跃流动性。

图 2: 一笔 100 万美元的兑换交易即将执行前,JIT 机器人存入 990 万美元流动性,从而捕获该笔交易产生的 3,000 美元费用中的 99%。池中各有 5 万美元的 Bob 和 Charlie 仅各获得 15 美元,而非在没有干预时本应获得的 1,500 美元。

主要交易:

  1. 用户提交一笔交易,将价值 1,000,000 美元的 USDC 兑换为 ETH。
    • 产生的总费用:1,000,000 美元 * 0.3% = 3,000 美元。
  2. 一个 MEV 机器人发现了该交易。
  3. Alice 的 MEV 机器人在内存池中发现了这笔 100 万美元的交易。它执行 JIT 流动性策略。
    • 抢跑交易:在主要交易之前,向活跃流动性范围内添加流动性:Alice 在兑换即将发生的精确刻度范围内添加了 9,900,000(990 万)美元的流动性,恰好在该兑换执行之前。
      • 新的总流动性:100,000 美元(Bob+Charlie)+ 9,900,000 美元(Alice)= 10,000,000 美元。
      • Alice 的份额:Alice 现在拥有池中 99% 的活跃流动性。Bob 和 Charlie 的份额被稀释到仅占 1%。
    • 主要交易执行:
      • 用户的 100 万美元交易针对这个新的 1000 万美元刻度范围执行。
      • 3,000 美元的费用基于该时刻的流动性所有权按比例分配。
    • 费用分配:
      • Alice(MEV 机器人):获得 99% 的费用 -> 2,970 美元。
      • Bob 和 Charlie(被动 LP):分享剩余的 1% -> 总共 30 美元(每人 15 美元)。
    • 尾随交易:在主要交易之后,从活跃流动性范围中移除流动性:
      • Alice 立即撤回其 990 万美元的流动性以及获得的 2,970 美元费用。

最终结果如何?

如果没有 Alice,Bob 和 Charlie 本应平分 3,000 美元的费用。由于 JIT 流动性"攻击",他们每人只赚了 15 美元。Alice 获得了该笔兑换 99% 的费用,影响了其他流动性提供者的收益。

结论

只要验证者有权选择哪些交易被包含以及以何种顺序包含,MEV 就会存在。其范围从良性到有害:套利机器人在市场间平衡价格,JIT 流动性在侵蚀被动 LP 收益的同时改善了交易者的执行价格,而三明治攻击则直接从不设防的用户那里窃取资金。区分这些结果的不是 MEV 基础设施本身,而是目标智能合约是否给了攻击者可乘之机。

内存池是公开的,每一笔待处理的交易都是意图的声明,任何运行机器人的人都可以读取。这种不对称性不会消失,但开发者可以控制的是,这种可见性对用户造成的成本有多大。一个简单的 minAmountOut 检查,就能区分一笔安全回滚的交易和一笔将用户 80% 价值拱手让给机器人的交易。假设每一笔交易都在被监视,并据此进行构建。

需要进行智能合约代码安全审查? 立即联系我们的安全团队,进行全面审计。

作者


Andrei Popa
专注于 EVM、StarkNet 和 Solana 生态系统的智能合约安全研究员,拥有审计高价值去中心化金融和基础设施协议的经验。

  • 原文链接: nethermind.io/blog/front...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
nethermind
nethermind
江湖只有他的大名,没有他的介绍。