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 的即时价格 随储备变化,而闪电贷可以让攻击者在一个区块内无成本地把 扭到任意值。
攻击案例(经典 BZX 事件):
- 攻击者用闪电贷借入巨量 ETH;
- 在某个小池用 ETH 买爆 sUSD,池价被推高数倍;
- 同一区块内在 BZX 借贷协议以扭曲的 sUSD 价格作为抵押品价值,贷出 ETH;
- 还款闪电贷后净赚。
根因:下游协议读了 单点瞬时价格 作为可信输入。任何依赖单一 AMM 瞬价的协议都可被闪电贷瞬间操纵。
19.4.3 防御第一原则:时间加权平均价(TWAP)
TWAP(Time-Weighted Average Price) 用一段时间的价格序列加权,让单区块内的操纵被"摊薄"。Uniswap V2 在每次 _update 时记录累计价格:
在区间 上的时间加权平均价为:
为什么有效:攻击者如果想操纵 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评论加载中…