16 · Flashbots / bundle / 私有 orderflow:把 MEV 真正上链的工具链
第四层第三篇。14 给了 mempool + PBS 通道(看见机会的窗口),15 给了五大策略目录(看见什么)。 16 是搜寻者把策略真正变成上链 tx 的具体协议——bundle 长什么样、submission API 怎么调、 5+ 个 relay 怎么选、simulator 怎么模拟、reputation 怎么积累。 我们
demo-flashbots.ts跑出的那条 structured error"insufficient funds for gas * price + value: have 0 want 1050000000000000"这一篇答清楚为什么是它而不是别的,以及它为什么是好消息。 沿用 01–15 的村庄画面。结构见README.md§1。
0. 一句话本质
Flashbots = "把 bundle(多 tx 原子组)从 searcher 经过 relay 安全送进 builder 候选块"的协议 + 一家跑这个协议的公司 + 一个 relay 服务 + 一个 builder。 Bundle 的关键性质:原子(要么全成要么全不进块)、私有(不经公共 mempool)、 simulation-verified(送进 builder 前 relay 已模拟一遍)。 它解决的核心问题:无 Flashbots 时代搜寻者用公共 mempool 高 gas 拼竞争 → 链拥堵 + 用户被害; 有了 Flashbots,竞争被压到一条私有通道里,由 priority fee + coinbase.transfer 出价。
1. 怎么产生的:它解决什么问题
01–15 留下"我们怎么把策略真送上链"的工程问题:
- Flashbots 出现之前(2018-2020)MEV 搜寻怎么做? ⇒ 全公开:搜寻者把套利 tx 以超高 gas price 发到公共 mempool,互相拼出价。 结果:链上 gas 价飙升、普通用户看到自己 tx 被 frontrun、网络拥堵成本由全网背。
- Flashbots 解决了什么?
⇒ 提供一条私有通道:searcher 把 bundle 签名 POST 到
relay.flashbots.net, 不经过公共 mempool → 公众看不到 → 减少链上 gas 战、减少用户被无意伤害。 MEV 不消失,但搬到台面下——公平性问题转移到 relay 信任问题。 - bundle 跟一组普通 tx 有什么不同?
⇒ 四个关键性质:
- 原子组合:全包含或全不包含——不会出现"第 1 笔进了第 2 笔没进"
- 指定块号:你告诉 relay"我这 bundle 要进第 N 块"
- 签名提交:bundle 顶层用 searcher 的 reputation key 签名
- simulation-verified:relay 在转发给 builder 前会先 fork 当前 state 跑一遍模拟
- Flashbots 是协议、是公司、是 relay、是 builder——到底是哪个? ⇒ 全是。Flashbots Inc. 是公司;他们设计了 bundle 协议(mev-relay-api);运营 relay.flashbots.net; 也运营一个 builder 参与块构造。现在还推 SUAVE(专门 MEV 链)和 mev-share(user-side privacy)。
block.coinbase.transfer(_eth)是什么?跟 priority fee 什么关系? ⇒ EIP-1559 之后 priority fee 给 proposer 但 base fee 被烧(07 §4.3)。 proposer 拿不到全部 gas fee → searcher 直接付钱给 proposer 的通道:block.coinbase.transfer(N)。 simple-arbitrage 的BundleExecutor.sol就是这个模式:套利获利后直接转一笔 ETH 给 proposer, 这笔钱完全不走 fee 机制——是 searcher 出价的核心手段。- 我们 demo-flashbots.ts 跑出的那条 structured error 是什么?为什么说它"是好消息"?
⇒ 错误:
"insufficient funds for gas * price + value: have 0 want 1050000000000000"1050000000000000wei = 0.00105 ETH =50 gwei × 21000(07 §8 / 14 §8)- 50 gwei =
maxFeePerGas,21000 = transfer 类 tx 最小 gas ⇒ 含义:Flashbots relay 拿到我们 bundle → 跑 simulation → 算出 tx 需要预扣 0.00105 ETH → 发现我们 EOA 余额 = 0 → 报这个 structured error ⇒ = 端到端证明 SDK 打包、HTTPS auth、bundle 签名、relay 路由、simulation 引擎全部正确—— 只差给我们 EOA 充 0.001 ETH 整条链路就能跑。这就是为什么我们 docs 记录"成功证明"。
- Flashbots 是唯一 relay 吗?
⇒ 不。现在主流 mainnet relay 有:
- Flashbots (默认 OFAC-compliant)
- BloXroute (商业化 / 部分 censorship)
- Agnostic (完全中立)
- Ultra Sound (中立 + 强调不审查)
- Eden (面向 staker 优化)
- Manifold (中性) ⇒ 实战搜寻者同时给所有 relay 发,反正每个 relay 只收 reputation 不收钱。
→ 这一篇答全部。
2. 它本质在做的"那一件事"
把搜寻者 → 提议者之间的"包装/验证/竞标/路由"协议化、私有化、可重复化: 一笔 bundle 经过签名 → relay 验证 → simulation → 路由到 builder → 进候选块 → 被 proposer 选中。 这条路径每一步都是工程问题:协议长什么样、relay 怎么模拟、builder 怎么排块、proposer 怎么选最高出价。 14 §3 的"装箱工 + 拍卖公证"在 16 这里有了字节级 API 协议。
→ 这是为什么 Phase 5 我们把 flashbots.ts 写出来 + 拿到 structured error 是项目里非常重要的一关:
端到端打通这条工程链,剩下的只是"真本金 + 真套利策略 + 真竞争"。
3. 类比:村庄"VIP 投递通道 + 公证拍卖"
01–15 的村庄已有所有设施。16 加的是"邮局升级":
┌── 公共投递(普通 RPC + mempool)──────────────────────────┐
│ 你寄信去邮筒(eth_sendRawTransaction) │
│ 全村邮差都能看到(公共 mempool) │
│ → 抢插队的人会买高优先级邮票(gas war) │
│ → 普通村民也被波及(链堵 + 用户被 frontrun) │
└─────────────────────────────────────────────────────────────┘
┌── VIP 通道(Flashbots 等私有 relay)─────────────────────┐
│ 你(searcher)把信交给"VIP 邮局"(relay) │
│ 邮局四步操作: │
│ 1. 拿签字本核对你的身份(reputation key) │
│ 2. 把信里的内容预演一遍(simulation) │
│ 3. 把信连同你的出价交给装箱工(builder) │
│ 4. 装箱工把信装到候选块里 + 出价给当 slot 的执笔人 │
│ │
│ 与公共投递相比: │
│ - 邮差看不到你的内容(隐私) │
│ - 没成功投递不收费(gas 只在 tx 上链时扣) │
│ - 预演失败你被告知 → 不浪费 block 空间 │
│ │
│ 邮局收什么? │
│ - 你的 reputation score(多次成功投递积累) │
│ - 你愿意付给执笔人的"加急小费"(priority fee + coinbase.transfer)│
└─────────────────────────────────────────────────────────────┘
┌── SUAVE(未来设想)────────────────────────────────────────┐
│ 邮局本身也分布式 + 加密 │
│ 邮差互相竞争用 MPC 协议保证不偷看 │
│ 目标:连邮局都不需要信任 │
└─────────────────────────────────────────────────────────────┘
→ 一图说尽 Flashbots 在 14 §3 PBS 流水线里的位置:它是 searcher 与 builder 之间那一条加密 + 验证 + 信誉的拍卖管道。
4. 核心关键点(5 条)
-
Bundle 的 4 个铁性质:
- 原子组合:整个 bundle 全成或全不进块(默认;
revertingTxHashes可显式放宽) - 指定块号:
targetBlockNumber告诉 relay 想进哪一块(一般是 next block) - 签名提交:HTTP header
X-Flashbots-Signature: 0x<addr>:<sig>,relay 验证你的 reputation key - simulation-verified:relay 收到 bundle 后用
eth_callBundle跑一遍模拟,确认 tx 不会无脑 revert + 算出实际 priority fee + coinbase.transfer 收益
- 原子组合:整个 bundle 全成或全不进块(默认;
-
bundle 提交 API(Flashbots 风格):
POST https://relay.flashbots.net Content-Type: application/json X-Flashbots-Signature: 0xYourSearcherAddr:0xSignatureOfBody { "jsonrpc": "2.0", "id": 1, "method": "eth_sendBundle", "params": [{ "txs": ["0xrawSignedTx1", "0xrawSignedTx2", ...], "blockNumber": "0x12345", "minTimestamp": 0, "maxTimestamp": 0, "revertingTxHashes": [], "replacementUuid": "uuid-for-cancellation" }] } -
块价值流向(接 14 §4.3):
searcher 收益 = swap/清算获利 ↓ (减) gas fee (base fee 烧 + priority fee 到 proposer) ↓ (减) block.coinbase.transfer(_amount) ← 这条是 EIP-1559 后 searcher 给 proposer 的直接通道 ↓ (减) relay 路由 fee(一般免费,靠 reputation 牟利) ↓ (减) builder 抽成(典型 5-15% 块价值) ↓ searcher 净利- 头部 searcher 把 95%+ 利润付给 builder / proposer,自己赚 1-5%——靠 量 + 频率
- 我们的 educational demo 不必拼这种 margin(也拼不过)
-
Simulation vs 真实执行的差异:
- relay simulation:fork 当前state,跑你 bundle → 看会不会 revert + 利润是多少
- 真实执行:你 bundle 进 block 的瞬间,别的 tx 可能已经改了 state—— 比如另一个 searcher 同时套同一对池 → 你的 bundle 跑到时 reserves 不同了 → revert
- 解决方案:bundle 内自带 state assertion(如 require 利润 ≥ X)→ 状态变了你 revert 不浪费 builder 资源
- 这就是为什么我们
Arbitrageur.sol里有require(finalBalance >= owed, "PROFIT_FAIL")—— 不只是逻辑校验,也是 MEV 竞争中的关键保险
-
失败模式 cheatsheet:
错误信息 真实含义 怎么修 insufficient funds for gas * price + valueEOA 余额不够预扣(我们的情况) 给 EOA 充 ETH nonce too lownonce 与链上不一致 getTransactionCount("latest")重取bundle below floor出价低于 builder 入门线 加 priority fee / 加 coinbase.transfer bundle simulation failed: ...bundle 在 fork 上 revert 看 revert reason 调逻辑 bundle not included上面都过了但 builder/proposer 没选你 加价 / 改时机 / 等下一块 unknown signerreputation key 没注册 检查 X-Flashbots-Signature header
5. 必须先认识的前置名词(小词典)
| 名词 | 一句话 | 类比 |
|---|---|---|
| Flashbots | MEV 协议 + 公司 + relay + builder | VIP 邮局集团 |
| bundle | 多 tx 原子组,签名后送 relay | 一封挂号信里的多页文件 |
| eth_sendBundle | 提交 bundle 的 JSON-RPC 方法 | 寄挂号信 |
| eth_callBundle | 让 relay 仿真跑一遍 bundle 不上链 | 拆开复印一份预演 |
| relay | 14 §5 复习;searcher 和 builder 间的中介 | 拍卖会公证 |
| reputation key | searcher 在 relay 那的身份签名密钥(≠ EOA send key) | 邮局的VIP 凭证 |
X-Flashbots-Signature |
HTTP header 携带 searcher 的签名 | VIP 凭证盖章 |
block.coinbase.transfer(N) |
合约里直接转 N ETH 给当前 proposer | 直接付给执笔人的小费 |
revertingTxHashes |
bundle 内允许 revert 的 tx hash 列表 | 部分内容允许涂改 |
targetBlockNumber |
想进的块号 | 期望寄到的"那一班车" |
replacementUuid |
取消/替换 bundle 用的 UUID | 挂号信回执号 |
| mev-share | 用户主动分享 tx hint 换分润 | 用户主动开窗共享 |
| Flashbots Protect | 用户端隐私 RPC(绕开公共 mempool) | 私人通道发信 |
| SUAVE | Flashbots 推的"MEV 专属链"+ 加密 mempool | 完全保密的邮政 |
| OFAC compliance | 14 §5 复习;制裁名单过滤 | 拒收某些地址 |
| Agnostic / Ultra Sound | 中立 relay 替代 | 不审查的邮政 |
| builder competition | 多 builder 用候选块出价 | 多装箱工拼活 |
| simulation drift | simulation 时 state 与真上链时不同导致 revert | 预演时桥还在,上桥时塌了 |
6. 一个完整过程走一遍:demo-flashbots 那条 structured error
照搬 demo-flashbots.ts 的 11 阶段,看 SDK / auth / transport / relay / simulator 完整链路:
─── T0: 我们的 EOA = 0xf39F...(anvil 默认 key 0)─────────
─── T1: EOA 余额 = 0 ETH(fresh fork,dealUsdc 给的是 USDC 不是 ETH)─
─── T2: 构造 arb tx ────────────────────────────────────────
arbTx = {
to: Arbitrageur 0xDda8...,
data: arb(routers=[UNI,SUSHI], USDC, 26869e6) 的 ABI 编码,
value: 0,
nonce: getTransactionCount(EOA, "latest"),
gasLimit: 1_500_000,
maxFeePerGas: 50 gwei,
maxPriorityFeePerGas: 2 gwei,
chainId: 1,
}
signedArbTx = wallet.signTransaction(arbTx) ← 02 §6 ECDSA
─── T3: 创建 reputation key + 包装 Flashbots provider ─────
searcherKey = ethers.Wallet.createRandom() ← 不必有 ETH 余额,只用作签名
fbProvider = await FlashbotsBundleProvider.create(
provider, searcherKey, "https://relay.flashbots.net", "mainnet"
)
─── T4: 准备 bundle ────────────────────────────────────────
bundle = [{ signedTransaction: signedArbTx }]
blockNumber = (await provider.getBlock("latest")).number + 1
─── T5: 模拟跑一遍 (relay.eth_callBundle) ──────────────────
simResult = await fbProvider.simulate(bundle, blockNumber)
─── T6: relay 收到 simulate 请求,做的事 ──────────────────
1. 验证 X-Flashbots-Signature → 通过 (我们 searcherKey 签的)
2. fork mainnet state at blockNumber-1
3. 尝试在 fork 上执行 arbTx:
- 加载 EOA 0xf39F... 的 state
- 计算 maxFeePerGas * gasLimit = 50e9 * 21000 ≈ 0.00105 ETH 预扣
- 检查 balance(EOA) >= 0.00105 ETH
- 0 < 0.00105 → 拒绝执行
- 报错: "insufficient funds for gas * price + value: have 0 want 1050000000000000"
─── T7: simResult 返回到 demo-flashbots.ts ────────────────
console output:
{
"results": [{
"error": "insufficient funds for gas * price + value: have 0 want 1050000000000000",
...
}],
...
}
为什么这条 error 是好消息?
| 这条 error 证明了什么 | 反之没证明的是 |
|---|---|
| SDK 打包 bundle 格式正确 | 我们 EOA 有钱 |
| HTTPS 到 relay.flashbots.net 走通 | bundle 真上链 |
X-Flashbots-Signature 签名验过 |
builder 选我们的块 |
eth_callBundle simulator 跑通 |
proposer accept |
| fork state 取到 | 网络 / mainnet 实时性 |
| 整条工程链路就剩 1 件事:给 EOA 充 0.001 ETH | 真本金竞争 |
→ 这条 error 是项目里一条关键的诚信证据:不假装上链成功,但精确地证明工具链的每一节都接通。 docs/学习路径方案.md "归档结论"中的第 N 条就是这条 evidence。
如果我们给 EOA 充 0.001 ETH 后会发生什么?
- simulate 通过 → 返回利润预估、gas 数据
- 提交
eth_sendBundle→ relay 收下 - relay 转给 Flashbots builder
- builder 把我们 bundle 排进候选块(可能——竞争激烈)
- proposer 选这个 builder 的块(可能——比拼出价)
- block 落账 → bundle 内 arbTx 真实执行
- 套利成功 → 利润 - gas - tip - 5bps premium = 净利
→ 每一步"可能"都是 MEV 真实战场:能在 simulate 通过后真上链是另一个量级的难度。 教学项目把"工具链跑通"作为里程碑,不强求"主网真盈利"——后者需要 05 §5 L3-L4 级基础设施。
7. 常见误区(逐条拍死)
| 误区 | 实情 |
|---|---|
| "Flashbots 是一条链" | 不是。它是协议 + relay + builder + 公司 + 数个相关产品;运行在以太坊之上 |
| "所有 MEV 走 Flashbots" | 不到 50%。还有 BloXroute / Agnostic / Ultra Sound / Eden / 直连 builder 等多通道 |
| "bundle 一定上链" | 不一定。能上链需:simulate 通过 + 价高 + builder 选 + proposer 选 |
| "bundle 内 tx revert 也能上链" | 默认 NOT——整个 bundle 被丢。要允许须把那条 hash 加进 revertingTxHashes |
| "Flashbots 是去中心化的" | 不。relay 由 Flashbots Inc. 运营。SUAVE 是要走向去中心化的尝试 |
| "Flashbots 不审查" | 自 2022 默认对 OFAC 制裁地址过滤。要绝对中立需 Agnostic / Ultra Sound |
| "bundle 跟 multicall 一样" | 不。multicall 是合约层多调用合一笔 tx;bundle 是 RPC 层多 tx 合一个原子组 |
| "我可以用合约钱包做 searcher" | reputation key 必须是 EOA 私钥(用于 EIP-712 签名 header)。被签的 tx 内容可来自合约钱包 |
| "Flashbots 收费" | bundle 入站 free。Flashbots 收的是 builder 那条线的 MEV 抽成 |
| "submit 后立刻就上链" | 一般 6-12 秒(等下个 slot)。如果价低或竞争激烈可能要等几块 |
"block.coinbase.transfer 是隐藏 tx" |
不。它是普通的 ETH 转账,receipt 里能看到。目的是 EIP-1559 后给 proposer 直接付费 |
| "MEV-Share 和 Flashbots Protect 一样" | 不一样。Protect 是绕开公共 mempool;MEV-Share 是发模糊化 hint 让 searcher 报价分润给用户 |
| "Flashbots 知道我 bundle 内容" | 是。relay 内部能完整看到 bundle。SUAVE 想用加密 mempool 解决这条 |
| "我家用机能跑 Flashbots 生产 searcher" | 网络层能 (POST HTTPS);但 simulator 准确性 + 抢入速度需要专业基础设施 |
8. 与本项目实践的对应
flashbots.ts 的工程结构(Phase 5 step 3)
- ethers v6 +
@flashbots/ethers-provider-bundleSDK (compatible since v1.0.0) - 一个 helper:
createFlashbotsProvider(provider, signerOrRpcUrl) - 一个 helper:
submitArbitrageBundle(arbCallData, blockNumber, options) - 不缺任何工程零件,只没有真本金 + 真竞争
demo-flashbots.ts 真实 relay 握手(这一篇的核心证据)
- 全流程见 §6 11 阶段
- 关键证据 =
"insufficient funds for gas * price + value: have 0 want 1050000000000000" 1050000000000000 wei = 0.00105 ETH = 50 gwei × 21000(07 §8 反复引用过的数字)- 意义:打通了从 SDK 到 relay simulator 的完整工程链路,是项目客观可验证的工程里程碑
BundleExecutor.sol 风格的 block.coinbase.transfer 模式
- 来自 simple-arbitrage(我们 Phase 6 精读过的)
- 用法:套利合约最后
payable(block.coinbase).transfer(_ethAmountToCoinbase) - 这是 EIP-1559 之后给 proposer 直接塞钱的标准做法
- 我们 Arbitrageur.sol 没用这模式(因为我们用 priority fee 已够教学说明); 生产 searcher 一般两者都用——priority fee 灵活 + coinbase.transfer 出价具体
项目客观状态(接 docs 归档 + 自我更正)
- 工具链:✅ 完整(07-16 各篇逐层证明)
- 真上链:❌(没有真本金参与主网拍卖竞争)
- 利润:N/A(教学场景从不目标)
- 教训:"能跑 demo" ≠ "能赚钱";二者之间隔着 05 §5 L0–L4 的 4 级阶梯
失败模式与教学映射
- 我们项目的
test_Arb_Reverts_When_NoNaturalOpportunity(07 §8)= 271,552 gas → 等价于 §4.5 "bundle simulation failed" 错误的链上版本 - 我们
predictBackrun.ts的 factory mismatch 修正(14 §8)= 类似 simulator drift 的早期版本——本地预测不准 - 每一个我们抓过的 bug 都对应一类生产场景的 failure mode——这是教学项目的真实价值
14 / 15 / 17 的承接
- 14 mempool:发现机会 + 看到 victim
- 15 MEV 策略目录:选哪种打法
- 16 Flashbots / bundle(本篇):把策略真送上链
- 17 跨链:把 MEV 从单链 12 秒扩到跨域 N 秒——失去原子性,新风险新机会
9. 自检问题(答得出才算过)
- Flashbots 同时是哪四种东西?它解决了 2018-2020 公共 mempool 时代的什么问题?
- bundle 的 4 个铁性质是什么?跟普通的"一组 tx 顺序提交"在哪些地方不同?
eth_sendBundle完整 API 长什么样?必填字段、可选字段、签名 header 各是什么?block.coinbase.transfer(_eth)为什么是 EIP-1559 后必要的工具?它跟 priority fee 是替代还是补充?- relay 的 simulator 怎么工作的?simulation 通过≠真上链,这条 gap 叫什么、靠什么补救?
- 我们 demo-flashbots 的 structured error
1050000000000000 wei怎么来的?它证明了哪些工程链路通了、哪些没? - 现在 mainnet 主流 relay 有哪些?为什么实战 searcher 会同时给所有 relay 发?
- Flashbots Protect / MEV-Share / SUAVE 三者各解决什么不同的问题?
── Q&A / 更正记录区(按日期追加)──
规范见
README.md§0。质疑/反馈/纠错按日期累积,不抹错误轨迹。
- (2026-05-21 建立。暂无 Q&A。)