第三层第四篇——DeFi 收官。我们项目 4 份合约每一份都用闪电贷做引擎(07 §8 数据: FlashLoanHelloWorld 359k / Arbitrageur 487k / MultiPoolArbitrageur viaIR / LiquidationBot 897k gas)。 这一篇答:"无抵押借走百万美金"这件听起来违反金融常识的事为什么在链上不仅成立、 还成了 DeFi 最被复用的"原语"——它不靠魔法,靠的就是 07 §4.4 那条原子性 + 07 §4.5 require revert 的状态全回滚。一旦你看懂这一篇,前面 06–12 的几乎所有"为什么这么设计"都串起来了。 结构见 README.md §1。


0. 一句话本质

闪电贷 = 把"原子性"当抵押品。EVM 单笔 tx 要么完整成功要么状态全回滚(07 §4.4), 所以借方不必先押钱:你借走 N、必须在同一 tx 内还回 N + premium,否则整笔 tx revert → 资金从未离开过 Pool。这不是"信任"也不是"魔法"——是 EVM 原子性 + 合约回调 + 协议简单的 require(repaid) 三件套合成的奇迹。


1. 怎么产生的:它解决什么问题

01–12 把基础设施全装完了,最后一块拼图是"杠杆 / 套利 / 清算 怎么不要本金也能做":

  1. 传统借贷必须先押抵押品(11),是不是就没办法"零本金"做套利? ⇒ 用 Aave 闪电贷——单笔 tx 内借走、用、还,根本不需要抵押
  2. 既然不要抵押,那 Pool 为什么愿意把钱借给我?谁担风险? ⇒ Pool 一点风险都不担。因为 Pool 借给你之后,同一笔 tx 里它会调一个 require: require(balanceOf(self) >= principal + premium, "FLASH_NOT_REPAID")。 你不还 → require 失败 → 07 §4.4 整笔 tx 状态全回滚 → 钱从未离开过 Pool。
  3. 流程具体怎么走?我看 11 §6 用了 5 步原子流程,闪电贷在第几步? ⇒ Aave V3 flashLoanSimple 的 6 步:
    1. 你的合约调 POOL.flashLoanSimple(receiver=self, asset, amount, params, referralCode=0)
    2. Pool 把 amount transfer 给你(你瞬间有钱了)
    3. Pool 回调 receiver.executeOperation(asset, amount, premium, initiator, params)(这是你写的 callback)
    4. callback 内做你的事:套利 / 清算 / debt refinance
    5. callback 末尾 IERC20(asset).approve(POOL, amount + premium) + return true
    6. Pool 在 callback 返回后 transferFrom 你 amount + premium;require 通过 → flashLoanSimple 成功
  4. 5 bps premium 给谁? ⇒ 全部进 Pool(间接给所有 LP / 部分 reserve factor 给治理金库)。 V2 时代是 9 bps,V3 降到 5 bps;这数字是"协议风险溢价 + 摩擦费",对成功的套利者来说基本忽略不计
  5. 闪电贷被滥用做攻击的故事都不少——它本身有漏洞吗?闪电贷本身没漏洞——它做的事是合规的:你借/还都 OK 才让你过。 问题在被借的钱拿去打谁:如果某个协议的 oracle / 价格机制可被砸偏(12 §1.5), 闪电贷就是放大器——把"需要 $10M 本金才能攻击"降到"任何人 0 本金就能攻击"。
  6. 闪电贷以外,DyDx / Uniswap V3 也有自己的闪电贷? ⇒ 有,但 API 不同:
    • Aave V3 flashLoanSimple:单资产、5bps、最简(我们项目用的)
    • Aave V3 flashLoan:多资产
    • DyDx operate:2 wei 名义费(基本免费),但 API 复杂
    • Uniswap V3 pool.swap with callback:借任一边代币、无 fee、复杂度高
    • Balancer V2:免 fee(仅燃 gas),需还到原 pool
  7. 闪电贷的真正限制是什么? ⇒ 必须单 tx 完成全部逻辑——你不能"今天借明天还"。所有获利路径必须能压缩到一次 EVM 执行内。 这就是为什么"flash loan + 跨链套利"不存在(跨链非原子,03/17 待写);闪电贷限定在一条链的一笔 tx

→ 这一篇答全部。


2. 它本质在做的"那一件事"

把"原子性"这条 EVM 内置的硬保证(07 §4.4)货币化:因为 EVM 保证"全成或全败", Pool 可以把巨额资金借给完全陌生的合约——不用做信用评估、不用押抵押、不用催收, 只要它写一个 require(repaid) 在 callback 之后就够了。一旦借方不还,整笔 tx 像没发生过; 一旦还了,Pool 反而收 5bps 净利。这是"链上独有"的金融原语—— 现实世界里没有任何机制能在 12 秒内做相同的事。

→ 闪电贷不是"违反金融常识",是 EVM 原子性给金融常识开了一个全新维度。 传统金融的"信用风险"在这里不存在——只有"原子性是否成立",而它总是成立(除非链分叉,几乎可忽略)。


3. 类比:村庄"原子化金库"

01–12 的村庄已经有:账本、代金券、自动找零桶(AMM)、无人当铺(Aave)、邮差(Oracle)。 13 加一种新机制——不是新设施,而是Aave 当铺多开的一种特殊业务:

┌── 无人当铺新增窗口:闪电贷 ─────────────────────────────────┐
│                                                              │
│  柜员对你说:"你能用我们金库里的钱,但有 3 条规矩:           │
│    1. 你拿走多少,**这一秒之内**必须还多少 + 0.05%            │
│    2. 还不上 → **从开始那一刻就当作没发生过**                │
│       (你的钱包恢复原状、金库的钱恢复原状、所有改动一律撤销)│
│    3. 你拿钱去干什么我不管——但还不上你白付 gas                │
│  "                                                          │
│                                                              │
│  这"一秒之内"= 一笔 EVM tx                                  │
│  这"当作没发生过"= 07 §4.4 状态全回滚                       │
│  这"还不上你白付 gas"= 07 §4.4 失败只亏 gas                 │
│                                                              │
│  柜员怎么知道你还了?                                        │
│    柜员在你 callback 返回后,调一次 require:                │
│      "你的余额 ≥ amount + 0.05%?" → ✅ 通过 / ❌ revert      │
│    require 失败 = tx revert = 整一切回到开始                 │
│                                                              │
│  所以柜员**不承担风险**:从他视角看,要么你"无中生有"地变出   │
│  了 0.05% 的额外钱给他赚 → 他高兴;要么你 tx 整个失败 →       │
│  钱根本没出柜门 → 他也高兴。                                │
└─────────────────────────────────────────────────────────────┘

→ 这就是闪电贷"无抵押"的真相:抵押品是整笔 tx 本身——你违约了,时间倒流,违约消失。 现实里没人能做这种生意;链上能,因为 EVM 让"时间倒流"是免费且必然的。


4. 核心关键点(5 条)

  1. 闪电贷的"无抵押"= 原子性当抵押

    • 借出和归还都在同一笔 tx 内发生
    • tx 末尾 Pool 做 require(repaid);不过 → revert → 状态全回滚 → 借款这件事像从未发生
    • 借方实际承担的最大损失 = gas 费用(即使逻辑 broken,至少 200k+ gas 还是要付)
    • Pool 实际承担风险 = 0(除非 EVM 原子性本身被破坏,而这意味着以太坊整个完蛋了,与闪电贷无关)
  2. Aave V3 flashLoanSimple 完整 6 步流程(接 11 §6 / 09 §6):

    step 1: 调用方   → POOL.flashLoanSimple(receiver, asset, amount, params, 0)
    step 2: POOL    → receiver: transfer(amount)       (受款方瞬时持有 amount)
    step 3: POOL    → receiver.executeOperation(asset, amount, premium, initiator, params)
                      ↑↑↑ 你的 callback ↑↑↑ 在这里干所有事
    step 4: receiver 内: ... 套利 / 清算 / refinance ...
    step 5: receiver 内: IERC20(asset).approve(POOL, amount+premium)
                         return true        (告诉 Pool: "我准备好了,来拉回")
    step 6: POOL    → transferFrom(receiver, POOL, amount+premium)
                      require(balanceCheck OK)  → emit FlashLoan(...) → success
    
    • 第 3 步是回调:Pool 主动调你写的 executeOperation(接口 selector 0x920f5c84,08 §6.4)
    • 第 5 步必须 approve:09 §6 同款机制——你不 approve,Pool 第 6 步 transferFrom 就 revert
    • 6 步全在同一 tx 内——这是闪电贷的全部魔法
  3. 5 bps premium 是什么 + 算法:

    • 公式:premium = amount * 5 / 10000
    • 对 26,869 USDC 借款:26869e6 * 5 / 10000 = 13434500 base units ≈ $13.43
    • 一年内成功执行 1000 次:$13,430 总成本,对成功套利完全可忽略
    • 5bps 是 V3 配置参数 FLASHLOAN_PREMIUM_TOTAL,可被治理调整(V2 时是 9bps)
    • 资金流向:90% 给 Pool(间接给 LPs),10% 给 protocol treasury(reserve factor)
  4. 闪电贷的三种主流用法(DeFi 工具书必背):

    用法 干什么 我们项目里 注意
    套利 借 A → 跨池 swap → 还 A + premium → 利润落袋 Arbitrageur.sol (487k gas) 价差必须 > 双边手续费 + 5bps + gas
    清算 借 B → 替他人还债 → 拿抵押 → swap → 还 B + premium LiquidationBot.sol (897k gas) HF < 1 + liqBonus > 总成本
    抵押 / 债务重构 借 X → 还旧债 → 取出抵押 → 新协议存抵押 → 借 X → 还闪电贷 我们没做 高级 DeFi 用户跨协议优化利率
  5. 闪电贷攻击不是闪电贷的锅:

    • bZx 2020-02:闪电贷 + Kyber spot price 操纵 → $630k(首例闪电贷攻击)
    • Harvest Finance 2020-10:闪电贷 + Curve spot price 操纵 → $24M
    • PancakeBunny 2021-05:闪电贷砸偏 BNB-BUNNY 池 → $200M+
    • Beanstalk 2022-04:闪电贷买治理代币立刻投票通过恶意提案 → $182M
    • 共同点:受害协议自身机制设计有问题(spot oracle / 治理无延迟 / 抵押估值漏洞)
    • 闪电贷把"门票 $10M"变成了"门票 $200 gas",但漏洞是被找出来才能被利用的
    • 正确防御方式:修协议漏洞(用 TWAP / 治理时间锁 / 多源 oracle),不是封禁闪电贷

5. 必须先认识的前置名词(小词典)

名词 一句话 类比
flash loan 单 tx 内借/还的无抵押贷款 当铺的"时间倒流"特别窗口
flashLoanSimple Aave V3 单资产闪电贷入口,最常用 单币特快窗
flashLoan (multi) Aave V3 多资产入口,一次借多种 一篮子借
executeOperation 闪电贷 callback;接口 selector 0x920f5c84 借方必须实现的"我会还"承诺函数
premium 借贷费(5bps) 借一次的服务费
FLASHLOAN_PREMIUM_TOTAL Aave 配置参数 = 5 (单位 bps) 服务费率
receiver 闪电贷的"借方合约"地址 业务执行人
initiator 调 flashLoanSimple 的最初地址(一般 = receiver) 操作发起人
referralCode Aave 早期分销用,现在写 0 推荐码
原子性 (atomicity) 单 tx 全成或全败(07 §4.4) 时间倒流保证
IFlashLoanSimpleReceiver Aave 接口;你必须 is 它(08 §6.1) 借方资格证
flash swap (Uni V2/V3) 用 swap 的 callback 借(实际无须先付) 桶的赊账 swap
flash mint 临时铸造再销毁(如 DAI、FRAX) 印钱再撕
flash loan attack 利用闪电贷放大对其他协议漏洞的攻击 借大炮去攻有 bug 的城门
MEV searcher 搜寻链上机会的实体;闪电贷是其最常用工具 战场扫荡兵

6. 一个完整过程走一遍:我们 Arbitrageur 的 11 阶段细节

pnpm demo 这一笔从 EVM 视角放慢到逐条状态变更——前面 09/11 已经走过部分,这里只放大闪电贷那一段:

设定

  • amount = 26,869 USDC = 26869 * 10^6 base units
  • premium = 26869 * 5 / 10000 * 10^6 = 13,434,500 base units ≈ $13.43
  • owed = 26869013434500

阶段 1:调用 + 资金借出

─── T0: msg.sender(EOA) calls Arbitrageur.arb(routers, USDC, 26869e6) ───
─── Arbitrageur.arb 内第一行 ───
  POOL.flashLoanSimple(
    receiverAddress = address(this),     // self
    asset           = USDC,
    amount          = 26869e6,
    params          = bytes(""),         // 我们不用 params 字段
    referralCode    = 0
  )

─── POOL 内部 ───
  USDC.transfer(Arbitrageur, 26869e6)         (Pool's USDC balance ↓; Arbitrageur's ↑)
  POOL 记录: pendingFlashLoanRepay[Arbitrageur][USDC] = owed

阶段 2:POOL 回调 executeOperation(Arbitrageur 的业务逻辑)

─── POOL 调 Arbitrageur.executeOperation(USDC, 26869e6, 13434500, msg.sender, "") ───
─── Arbitrageur.executeOperation 内 ───
  // 现在我有 26869e6 USDC、欠 owed
  // step 1: USDC → WETH on routers[0] (Uni V2)
  IERC20(USDC).approve(routers[0], 26869e6)
  amountOutWETH = router0.swapExactTokensForTokens(
    amountIn=26869e6, amountOutMin=0, [USDC, WETH], self, deadline)

  // step 2: WETH → USDC on routers[1] (Sushi)
  IERC20(WETH).approve(routers[1], amountOutWETH)
  amountOutUSDC = router1.swapExactTokensForTokens(
    amountIn=amountOutWETH, amountOutMin=0, [WETH, USDC], self, deadline)

  // step 3: 检查赢够还了
  require(amountOutUSDC >= owed, "PROFIT_FAIL")
                                                       // 08 §4.5 REVERT + Error(string)
                                                       // 失败这里就回滚整个 tx (07 §4.4)

  // step 4: approve POOL 收回本金 + premium
  IERC20(USDC).approve(POOL, owed)

  return true                                          // 告诉 POOL "成了"

阶段 3:POOL 验收 + 收回

─── POOL 内部继续 ───
  IERC20(USDC).transferFrom(Arbitrageur, POOL, owed)   (09 §6 流程)
  require(USDC.balanceOf(POOL) >= POOL_balance_before_flashloan,
          "FLASH_NOT_REPAID")                          // 这里是核心 require
                                                       // 没拿够 owed 就 revert
  emit FlashLoan(target, asset, amount, premium, mode=0, ref=0)
─── flashLoanSimple 成功返回 ───
─── tx 落账 receipt.status = 1, gasUsed = 487252 ───

"失败"路径(也走一遍)

─── 如果阶段 2 step 3 那个 require(amountOutUSDC >= owed) 失败 ───
  REVERT + "PROFIT_FAIL" Error data

  阶段 2 内所有状态变更(两次 swap、两次 approve)→ 全部回滚

  阶段 1 内 POOL.transfer(Arbitrageur, 26869e6) → 也回滚

  EOA 余额恢复原状 (gas 已花的不退,07 §4.4)

  tx receipt.status = 0, gasUsed = 271552 (07 §8)

  POOL 不变;攻击者 / 操作者亏 27 万 gas (~$10-50 取决 gas price)

→ 这就是"闪电贷的安全性 = EVM 原子性"的字节级演示。Pool 完全无风险,借方最大损失 = gas。


7. 常见误区(逐条拍死)

误区 实情
"闪电贷是免费钱" 不是。你必须有一个能在一笔 tx 内赚回 amount + premium 的策略,否则 revert 你亏 gas
"闪电贷靠对借方的信任" 完全不靠。靠 EVM 原子性 + require(repaid)。借方信不信用、是谁都无关
"闪电贷有信用风险" 0(除非以太坊本身故障)。这是金融史上极少见的真零风险贷款
"5 bps 太高" 对成功套利完全可忽略。$1M 闪电贷的 premium = $500,远低于一般套利毛利
"闪电贷不能跨链" 对。跨链不原子(17 待写)→ 不可能跨链闪电贷。闪电贷限定单链单 tx
"Aave 应禁闪电贷" 反对:闪电贷是 DeFi 价格发现 / 清算的核心工具;禁了反而让坏协议永远卡在错价上
"闪电贷攻击是闪电贷的责任" 不是。受害协议设计漏洞(spot oracle / 无 timelock / 弱抵押估值)才是问题
"我合约可以检测调用者是不是闪电贷" 几乎不可能。看 balance?借方拿到钱后就是有那么多 balance。看 origin?任何人都能搭个 EOA。任何 per-tx defense 都脆弱
"闪电贷 = 闪兑(flash swap)" 不一样。闪电贷是借 + 还,Pool 收 fee;闪兑是 Uni V2/V3 pool 直接给你币、callback 内你或还币、或还另一边币(赊账 swap)
"闪电贷必须用 Aave" 不。Balancer / DyDx / Uni V3 / dForce 都有。Aave 流动性最深,最常用
"params 字段没用" 有用——把额外参数从 arb() 入口传到 executeOperation 内(因为后者签名固定)。我们项目里 params 留空,因为路径已经硬编码进 routers 参数
"executeOperation 名字能改" 不能。Aave 硬编码调用选择器 0x920f5c84(08 §6.4)。名字一改 selector 变 → Aave 调不到 → revert

8. 与本项目实践的对应

闪电贷是我们项目的引擎——4 份合约全用。把每份合约里闪电贷的角色拆开:

FlashLoanHelloWorld.sol (359,816 gas, 07 §8)

  • 最简单形式:借 1 WETH → approve POOL → return true
  • 没有"业务逻辑",纯演示闪电贷协议本身
  • 这是为什么 gas 最低——只是 POOL + receiver 之间两次 transfer + 两次 approve

Arbitrageur.sol (487,252 gas, 07 §8)

  • 借 USDC → Uni V2 swap → Sushi swap → require 利润够 → approve POOL → return true
  • 上面 §6 完整走过
  • 487k 比 hello world 多了 ~127k = 两次 V2 swap 的 SSTORE + 第二次 approve

MultiPoolArbitrageur.sol(开 viaIR, 07 §1.5)

  • 借 USDC → 多池路由(pool[i] → pool[j] → pool[k])→ 验利润 → 还
  • 因为局部变量超 EVM stack 16-slot 窗口,必须开 via_ir = true 走 Yul IR
  • 闭式最优 amountIn 算法在 §6 外(详 10 §8)
  • 这是项目里最"算法密集"的合约

LiquidationBot.sol (897,176 gas, 07 §8 + 11 §8)

  • 借 USDC → liquidationCall(victim) → 拿 WETH 抵押 + 5% bonus → V2 swap WETH→USDC → 还 USDC + 5bps
  • 11 §6 完整走过
  • 897k 比 arb 多了 ~410k = liquidationCall 内部状态更新 (300k+) + 一次 WETH 抵押 transfer

Phase 5 Flashbots 阶段的承接

  • 单 tx 闪电贷套利 + Flashbots bundle = MEV 经典模式
  • 我们 demo-flashbots.ts 真实 relay 握手(structured error 证明 SDK/auth/transport 全通)= 把闪电贷套利打包成私 mempool 提交,避开公 mempool 被抢
  • 14 / 15 / 16 详细展开 mempool / MEV / Flashbots 的端到端

dealUsdc 在 setup 中的角色

  • 测试 setup 要先用 dealUsdc(Aave_USDC_aToken, 1_000_000_000e6) 给 Aave Pool 备足闪电贷可用流动性
  • 否则 Pool 自己 USDC 不够 → 闪电贷 transfer 那一步就 OOM revert
  • 这是为什么 06 §4.4 + 09 §8 反复提的 storage 第 9 槽 cheat 是项目所有测试的基石

第三层完结的串接

  • 10 DEX/AMM 给"换币"的水管
  • 11 借贷协议给"杠杆 / 清算"的水管
  • 12 预言机给"价格 = 多少"的水管
  • 13 闪电贷给"启动资金 = 0"的水管
  • 任何 MEV 搜寻者的工具箱本质就是这四样的组合 → 14/15 走的就是组合艺术

9. 自检问题(答得出才算过)

  1. 闪电贷为什么不需要抵押?精确到 EVM 原语回答(哪两条 opcode + 哪一条 require)。
  2. 写出 Aave V3 flashLoanSimple 的 6 步流程,标出哪一步在 receiver 合约内、哪一步在 Pool 内、哪一步是 callback。
  3. 5 bps premium 怎么算?对 1M USDC 借款是多少?premium 流向哪里?
  4. 闪电贷的三种主流用法是什么?我们项目分别对应哪几份合约?各自 gas 量级及差异来源?
  5. 闪电贷攻击(bZx / Harvest / PancakeBunny)的共同点是什么?责任在闪电贷本身吗?正确防御方式是?
  6. 为什么"detect caller is using flash loan"几乎不可能?这个不可能性的根源是什么?
  7. 跨链闪电贷为什么不存在?这跟原子性的哪条性质冲突?
  8. 我们 ArbitrageurexecuteOperation 选择器是固定的 0x920f5c84,能改吗?为什么?

── Q&A / 更正记录区(按日期追加)──

规范见 README.md §0。质疑/反馈/纠错按日期累积,不抹错误轨迹。

  • (2026-05-21 建立。暂无 Q&A。)