教程区块链区块链技术ch1919.4 DEX 安全:闪电贷、预言机操纵与重入

本页目录

DEX 是 DeFi 攻击的头号目标。闪电贷把攻击者的资金约束降到了零——一场攻击可以在一个区块内完成贷款、操纵、套现三步。本节剖析最常见的攻击面与防御。


19.4.1 威胁全景图

graph TD
    ATK[攻击者] --> FL[闪电贷 Flash Loan]
    FL --> Mani[单池价格操纵]
    Mani --> Target1[预言机喂价/清算协议]
    Mani --> Target2[依赖瞬价的做市/借贷]
    ATK --> Sandwich[三明治攻击]
    Sandwich --> Victim[普通交易者]
    ATK --> Re[重入攻击]
    Re --> Pool[流动性池]
攻击手法危害
闪电贷 + 价格操纵借巨资单池买爆,扭曲瞬价,再于下游清算/套利借贷协议坏账、清算失真
三明治攻击在受害者交易前后各插入一笔,吃滑点普通用户被 MEV 收割
重入回调重复取走资产资金丢失
精度/舍入薅羊毛利用整数除法向零舍入反复小额度操作微小但批量放大
假币/假池诱导用户交易伪劣代币用户资金损失

19.4.2 为什么不能信"单池瞬价"

内部价格可操纵的本质:AMM 的即时价格 P=y/xP = y/x 随储备变化,而闪电贷可以让攻击者在一个区块内无成本地y/xy/x 扭到任意值。

攻击案例(经典 BZX 事件):

  1. 攻击者用闪电贷借入巨量 ETH;
  2. 在某个小池用 ETH 买爆 sUSD,池价被推高数倍;
  3. 同一区块内在 BZX 借贷协议以扭曲的 sUSD 价格作为抵押品价值,贷出 ETH;
  4. 还款闪电贷后净赚。

根因:下游协议读了 单点瞬时价格 作为可信输入。任何依赖单一 AMM 瞬价的协议都可被闪电贷瞬间操纵。


19.4.3 防御第一原则:时间加权平均价(TWAP)

TWAP(Time-Weighted Average Price) 用一段时间的价格序列加权,让单区块内的操纵被"摊薄"。Uniswap V2 在每次 _update 时记录累计价格:

price0Cumulative+=P0×Δt,Δt=tnowtlast\text{price0Cumulative} \mathrel{+}= P_0 \times \Delta t, \qquad \Delta t = t_{\text{now}} - t_{\text{last}}

在区间 [t1,t2][t_1, t_2] 上的时间加权平均价为:

TWAP(t1,t2)=price0Cumulative(t2)price0Cumulative(t1)t2t1\text{TWAP}(t_1, t_2) = \frac{\text{price0Cumulative}(t_2) - \text{price0Cumulative}(t_1)}{t_2 - t_1}

为什么有效:攻击者如果想操纵 30 分钟 TWAP 到 3 倍,必须让池价在整个 30 分钟内保持高位——这意味着要承受套利者持续反向交易与资金占用,成本极高,闪电贷的单区块优势失效。


19.4.4 TWAP 从零实现(纯 TS)

typescript
// TWAP 累计价引擎:从零实现(纯 TS + BigInt,无外部库)
class TwapOracle {
  price0Cumulative: bigint; // 累计价积分 Σ P(t)*dt
  lastTimestamp: bigint;    // 上次更新时刻(秒)
  lastPrice0: bigint;       // 上个区间采用的即时价(1e18 定点)

  constructor(now: bigint) {
    this.price0Cumulative = 0n;
    this.lastTimestamp = now;
    this.lastPrice0 = 0n;
  }

  // 每次 swap 后调用:先积分上一区间,再记录新价
  update(newPrice0: bigint, now: bigint): void {
    const dt = now - this.lastTimestamp; // 区间长度
    if (dt > 0n) {
      this.price0Cumulative += this.lastPrice0 * dt; // 用上一区间价格积分
      this.lastTimestamp = now;
    }
    this.lastPrice0 = newPrice0; // 记录新价供下一区间
  }

  // 窗口 TWAP = 累计差 / 时长
  twap(cumulativeAtStart: bigint, cumulativeAtEnd: bigint, dt: bigint): bigint {
    if (dt <= 0n) throw new Error("时间区间非法");
    return (cumulativeAtEnd - cumulativeAtStart) / dt;
  }
}

// 验证:窗口 [0,30] 内价格恒为 1,攻击在 t=31 才跳到 3 —— TWAP 抗瞬时操纵
const WEI = 10n ** 18n;
const o = new TwapOracle(0n);
o.update(1n * WEI, 0n);  // 设首价 1(t=0,dt=0 不积分)
o.update(1n * WEI, 30n); // 积分 [0,30]:30*1 = 30
const cumAt30 = o.price0Cumulative; // 窗口终点累计 = 30
o.update(3n * WEI, 31n);            // 攻击:t=31 瞬价跳到 3(窗口外)
const avg = o.twap(0n, cumAt30, 30n); // (30-0)/30 = 1
console.log("前30s TWAP =", Number(avg) / Number(WEI), "(未被 3 拉高)");

运行验证:输出 前30s TWAP = 1(未被 3 拉高)。攻击者想让 30 分钟 TWAP 失真,必须让价格在高位持续整个窗口而非瞬时——这正是 TWAP 抵抗闪电贷操纵的关键。


19.4.5 其它防护清单

风险防护手段
三明治攻击提高滑点容忍度会受伤;前端可试 "MIN_OUT + deadline" 并教育用户;协议侧无解(MEV 是开放市场的必然)
重入遵循 checks-effects-interactions;使用非重入锁;ERC-777 回调警惕
闪电贷操纵只用 TWAP/外部预言机(Chainlink 等)定价,拒绝单池瞬价
舍入薅羊毛输入≥阈值、向「对协议有利」方向舍入
流动性抽干可暂停开关、速率限制、黑白名单(需平衡去中心化)
假池钓鱼前端显示 verified 合约 + 锁定 Router 白名单

19.4.6 关键认知

① 闪电贷让"资金约束"归零,单池瞬价必然可被操纵——这是 DEX 安全的第一课;② TWAP 通过时间加权让单区块操纵失效,是 DeFi 定价的主流基线;③ 三明治攻击难以根除(MEV 是市场结构问题),务实策略是降低用户可感知滑点 + 加强引导。


, 前往 → 19.5 DEX 经济/无常损失 |*

评论

0

评论加载中…

发表评论

0/2000