使用 Jito DontFront 实现 MEV 防护
最大可提取价值(MEV)是指通过对区块内交易进行重新排序、纳入或排除所能获取的价值。MEV 并非某条链所独有,而是任何由区块生产者控制交易排序的区块链的基本属性。某些形式的 MEV(如套利)通过纠正各 DEX 之间的价格差异,有助于维持市场效率;而另一些形式(如夹击攻击)则直接从用户身上提取价值。对于构建 DeFi 应用的开发者而言,这可能意味着更差的交易执行价格和利润损失。
本指南重点介绍夹击攻击——一种常见的有害 MEV 形式,以及如何利用 Jito 的 dontfront 功能加以缓解。
推荐预读: Jito Bundles
Solana 上的 MEV
与拥有公共内存池的链相比,Solana 的架构本身已大幅缩小了 MEV 的攻击面。交易直接转发给即将到来的出块者,而非停留在共享内存池中,未处理的交易在约 150 个区块(约 1 分钟)后过期。这极大地压缩了搜索者观察并利用待处理交易的时间窗口。
尽管如此,MEV 提取仍然存在。拥有优化基础设施和直接 validator 连接的搜索者可以观察交易流并作出响应。Solana 上的 MEV 活动大多为原子套利——即机器人在单笔交易中纠正各 DEX 之间的价格差异。这类 MEV 通常是有益的,因为它能提升各市场之间的价格一致性。
而有害的那种——夹击攻击,才是开发者应当主动防范的。
什么是夹击攻击?
当搜索者检测到您的待处理交换并利用交易排序进行攻击时,夹击攻击便会发生:
- 抢先交易:搜索者在您的交易之前买入代币,推高价格
- 您的交易执行时价格比预期更差
- 尾随交易:搜索者在您的交易之后卖出代币,从中套利
在 Solana 上,这可以通过
Jito bundles 实现。
搜索者提交一个包含如下交易的捆绑包:
[frontrun_tx, your_tx, backrun_tx],区块引擎按顺序原子化执行它们。受害者获得更差的执行价格,而搜索者从价差中获利。
DontFront 的工作原理
DontFront 是 Jito 区块引擎的一项功能,可防止搜索者在捆绑包中将交易排在您的交易之前。
其机制为:将任意以 jitodontfront 开头的有效 Solana 公钥添加到您交易的任意指令中。区块引擎识别此前缀并强制执行一条规则:任何包含您交易的捆绑包必须将其置于索引 0 处。
Without dontfront: [frontrun_tx, your_tx, backrun_tx] ← sandwich possibleWith dontfront: [your_tx, ...] ← your tx must be first
分步说明
-
选取一个有效地址,以
jitodontfront开头(例如jitodontfront111111111111111111111111111111)。该账户无需在链上存在,它永远不会被读取或写入。您可以使用 @solana/kit 中的assertIsAddress函数验证某字符串是否为有效地址,也可以在 Solana Explorer 中输入该地址查看是否能解析。以下是一个 无效地址的示例。 -
将其作为只读非签名账户添加到您交易的任意指令中。标记为只读可获得最优的上链速度。大多数程序(包括 System Program)会忽略其预期之外的额外账户。
-
通过 Jito 区块引擎提交:
sendTransaction:https://mainnet.block-engine.jito.wtf/api/v1/transactionssendBundle:https://mainnet.block-engine.jito.wtf/api/v1/bundles
文档: sendTransaction 和 sendBundle
-
区块引擎检测到
jitodontfront前缀并强制执行排序规则。
区块引擎的处理逻辑
当区块引擎检测到包含 jitodontfront 账户的交易时:
- 通过
sendTransaction:该交易受到保护,任何捆绑包均不能将其他交易置于其前 - 通过
sendBundle:该交易必须出现在索引 0 处,否则整个捆绑包将被拒绝
不包含 jitodontfront 账户的交易和捆绑包不受影响。
捆绑包排序规则
这些规则适用于捆绑包中出现 jitodontfront 交易的情况。
允许的模式
[tx_with_dontfront, tip][tx_with_dontfront, arbitrage, tip][tx_with_dontfront_signer1, tx_with_dontfront_signer1_and_signer2, tip]
单个捆绑包中的多个 DontFront 交易
当同时满足以下两个条件时,单个捆绑包中允许包含多个 dontfront 交易:
- 所有 dontfront 交易连续排列在捆绑包的最前面
- 每个 dontfront 交易与第一个 dontfront 交易至少共享一个签名者
✅ [txA_df, txB_df, txC_df, arbitrage, tip](contiguous at front, overlapping signers)✅ [txA_df_signer1_signer2, txB_df_signer1_signer3, txC_df_signer2_signer4](each shares a signer with txA)
被拒绝的模式
❌ [tip, tx_with_dontfront]→ dontfront tx is not at index 0❌ [txA_df_signer1, txB_df_signer2]→ no overlapping signer between txA and txB❌ [trade, tx_with_dontfront, arbitrage, tip]→ dontfront tx is not at the front
集成示例
如需快速代码示例,请参阅 MEV 防护 Cookbook 条目。
TypeScript(@solana/kit)
核心思路是编写一个将 dontfront 账户追加到任意指令的辅助函数:
import {address,AccountRole,type Instruction,type Address} from "@solana/kit";const DONT_FRONT: Address = address("jitodontfront111111111111111111111111111111");function withDontFront(ix: Instruction): Instruction {return {...ix,accounts: [...(ix.accounts ?? []),{ address: DONT_FRONT, role: AccountRole.READONLY }]};}
然后将已签名的交易提交到 Jito 区块引擎,而非标准 RPC 节点。请始终使用 base64 编码。Base58 在 Jito 的 API 中已被废弃。
const response = await fetch("https://mainnet.block-engine.jito.wtf/api/v1/transactions",{method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify({jsonrpc: "2.0",id: 1,method: "sendTransaction",params: [base64Tx, { encoding: "base64" }]})});
最佳实践
DontFront 是防护的一个层面。一套健全的 MEV 缓解策略需要综合运用多种方法:
交易层面的防护
-
设置严格的滑点容忍度。 防范夹击攻击最有效的单一手段是限制交换的可接受滑点。更小的滑点窗口会压缩攻击者的利润空间,使您的交易对夹击攻击的吸引力降低。
-
设置合适的小费和优先费。 在网络拥堵期间,更高的小费和优先费会提高您的交易/捆绑包快速上链的可能性,从而缩短搜索者的行动窗口。有关如何设置合适小费的更多详情,请参阅 Jito 文档中的 小费金额。
-
优化计算单元用量。 每个区块的计算单元数量有限(账户在每个区块内的计算单元数量同样有限)。为最大化您的交易/捆绑包被纳入区块的概率,您应优化计算单元用量。详情请参阅 如何优化 Solana 上的计算用量 指南。
DontFront 专项建议
-
将 dontfront 账户标记为只读。 标记为可写也可正常工作,但标记为只读可 优化上链速度。
-
每个应用使用唯一的 dontfront pubkey。 任何以
jitodontfront开头的 pubkey 均可使用。使用唯一变体(例如jitodontfront111111111111111111111111111123)可让您在检查链上数据时区分各应用的使用情况。 -
DontFront 支持地址查询表(ALT)。 dontfront 账户可通过 ALT 纳入。但请勿将 ALT 用于小费账户。
局限性
-
仅限区块引擎。 DontFront 由 Jito 区块引擎强制执行。直接提交给 validator(绕过 Jito)的交易不受保护。
-
不提供保证。 引用 Jito 文档:"此功能可能有助于减少夹击攻击,但不保证能做到,也不是解决所有交易排序变化的方案,包括第三方实施的任何排序。"
-
仅限主网/测试网。 这是 Jito 区块引擎的功能,在 devnet 或本地环境中不可用。
参考资料
Is this page helpful?