大家好,我是 Lucifer。之前写RWA的时候,我把预言机比作”信使”:链下世界的玉米价格、黄金报价,得靠一位信使穿过链上链下那堵墙,把消息递进来。当时一笔带过,今天把这位信使请出来,当面问三个问题:你带来的消息是哪来的?可信吗?有没有人塞你红包?
你可能觉得这是技术宅的闲磕牙。还真不是。DeFi 里几乎所有要命的时刻,背后都挂着同一个问题——“现在价格是多少”。借贷协议靠价格算抵押品价值、触发清算;稳定币靠价格守锚定;永续合约靠价格结算资金费率。价格对,天下太平;价格被污染 10 分钟,几个亿的真金白银就会换主人。2022 年 Mango Markets 一夜之间被搬走约 1.14 亿美元——攻击者没黑一行合约代码,他只是把”价格”这个数换掉了。
这篇文章我们就把预言机从头捋一遍:链上为什么看不到外面的价格、为什么不能随便抄个现货价、Chainlink 和 Pyth 各自在忙什么、TWAP 是怎么把操纵变成烧钱大战的,最后照例用两段 Python 模拟把原理跑给你看。放心,全程初中数学。
链上是一间没有窗户的会议室
先说一个让很多新朋友愣住的事实:智能合约上不了网。
不是”不太方便”,是根本做不到。以太坊虚拟机(EVM)的指令表里,压根没有”发个 HTTP 请求”这一项。你想在 Solidity 里写 fetch("https://api.binance.com/price")?编译器直接把代码打回来。
为什么这么绝情?你把链上想象成一间没有窗户、没有电话的会议室:外面发生什么,屋里的人一概不知。想递消息,只有一种办法——把消息写成纸条,从门缝塞进去。这张纸条,就是一笔交易。
这么设计是故意的,因为区块链的命根子是确定性:全网每个节点都要把每笔交易重放一遍,抄出来的结果必须一字不差。假如合约真能实时调 API,A 节点请求时 Binance 报 2000,B 节点晚 3 秒请求报 2001,两个节点算出来的账本从此对不上——共识当场崩盘。所以设计者一刀切:合约只准看屋里的东西,账户余额、合约状态、区块头,仅此而已。
麻烦来了。ETH 现在值多少钱,答案明明在屋外的交易所里,屋里的人却两眼一抹黑。想让他知道,就必须有人把价格写成纸条塞进门缝。干这件事的机器和人,就叫预言机(Oracle)。

于是问题从”怎么拿价格”,变成了一个更扎心的难题:递纸条的人,凭什么信他?他要是谎报一个数,屋里所有按这个数办事的协议全部遭殃。预言机的全部技术史,说白了就是一句话:怎么让说谎变得贵到没人干得起。下面按历史顺序,把几种方案挨个盘一遍。
最偷懒的方案:直接抄 DEX 现货价
链上既然没有价格,那链上的 Uniswap 池子不就是现成的市场吗?读一下储备比例,价格 = 储备B / 储备A,零信任、零外部依赖、一个合约调用搞定,看起来完美。
但这条路上埋着 DeFi 史上最经典的坑:现货价可以被一笔交易临时改写。
你琢磨一下 AMM 的定价:价格就是储备比。那”改价格”就等于”砸钱改储备比”——跟往天平一端加砝码是一个道理。你会说,加砝码得用自己的钱啊。是的,在闪电贷出现之前确实是这样。闪电贷允许你在同一笔交易里借出天量资金、用完即还。抵押物?没有。还不上,整笔交易自动回滚,等于什么都没发生过——像开了一家”一分钟租赁行”,卡车白借给你,货运完把车还回来就行。
于是教科书攻击流程诞生了,全程一个区块内完成:
- 闪电贷借出巨额 USDC;
- 全部砸进某个流动性很差的 ETH/USDC 小池子,现货价瞬间被拉高 3 倍;
- 借贷协议读这个池子的现货价给自己的抵押品估值——虚高的价格,虚增的抵押品;
- 凭着虚增的抵押品从国库借出真金白银;
- 反手砸回池子,归还闪电贷,留下一笔手续费和一地鸡毛。
注意攻击的精髓:协议的代码没有漏洞,被攻击的是”喂给协议的价格”这个输入。而”拉盘-借款-还贷”挤在同一个区块里完成,意味着没有任何人来得及拦——原子性本来是保护我们的特性,在这里成了攻击者的隐身衣。
更偷懒的方案:项目方自己喊价
另一条路的逻辑更朴素:我项目方自己写个脚本,每隔几分钟调一下交易所 API,把价格填进链上合约。很多小项目至今还这么干,毕竟实现成本约等于零。
但成本为零的东西,风险一般是满格的。这个方案的整套家当,都压在一个喇叭上:
- 喇叭会哑:脚本挂了、API 限流了、云服务器宕机了,链上价格就冻在上一次的数字里。行情剧烈波动时,这个冻结的旧价格会诱发一轮误清算;
- 喇叭会喊错:运维小哥手滑多敲一个零,2000 美元变成 20000,下一秒清算机器人倾巢而出;
- 喇叭会撒谎:这是最要命的。管喂价的私钥被盗,或者项目方自己起了歹念——把抵押品价格改高 10 倍,抵押人”集体暴富”,国库被凭空借空,项目方卷款跑路。
一句话总结:中心化喂价的潜台词是”请相信我,我是好人”。而在一个所有设计都围着”不信任任何人”转的行业里,这种架构本身就不及格。我们需要一个说谎代价高到不值的方案。
Chainlink:请一群互相不认识的报价员
Chainlink 的答案是:把”喂价”从项目方的私活,变成一群互相不认识的报价员共同出数。这套网络叫 DON(Decentralized Oracle Network)。以 ETH/USD 价格流(Data Feed)为例,流程是这样的:
- 一组相互独立的节点运营商(不少是知名基础设施公司)各自从多个数据源(各大中心化交易所、行情聚合商)取数;
- 每个节点对答案签名,提交到链上的聚合合约(Aggregator);
- 聚合合约把所有报价做一次聚合——取中位数,并按预设的上下限剔除明显离谱的值;
- 中位数成为这一轮(round)的官方答案,任何合约都可以无许可地读取。
为什么取中位数而不是平均数?想象 10 个报价员从低到高站成一排,取站正中间的那位。有人报了个 4 倍的离谱价?他只能被挤到队尾,站在中间的还是那 9 个老实人。所以想污染中位数,攻击者必须买通一半以上的报价员;而平均数不一样——收买一个人报个极端值,就能把平均值拖飞(文末代码模拟会跑给你看)。这就是统计学版本的防弹衣。
链上每个价格流还有两个关键参数,它俩决定了”链上的价格有多新鲜”,你可以在 data.chain.link 上逐个查看。你可以把 Chainlink 想成一位电台播报员,这两个参数就是他的工作守则:
- 偏差阈值(Deviation Threshold):价格相对上一次上链的值波动超过一定百分比,播报员才开口播一次新的。比如 ETH/USD 在以太坊主网是 0.5%——波动不到 0.5%,他就闭嘴不播,反正没什么可说的,播一次还得花一笔 gas 费。这是为了省钱;
- 心跳(Heartbeat):哪怕市场一整天风平浪静,最长每隔固定时间(ETH/USD 主网是 1 小时),播报员也必须定时播一次,证明自己还活着,防止”价格流假死没人管”。这是为了保命。
这两个参数是一场拉锯战:阈值越小、心跳越短,价格越新鲜,但上链越勤、gas 越贵。主流交易对定得小(波动大、需求高),长尾资产定得大。但也正因为有”整点报时”这回事,接入方要心里有数:你读到的”最新价”,可能已经是一小时前的旧闻了。行情突变的那一小时里,这个时间差足够让清算逻辑做出完全相反的决策。
Pyth:从”订报纸”改成”到店自取”
Chainlink 是”推”模型:报价员主动把价格送上链,链上常驻一个最新值,谁用谁读——像订报纸,每天准时塞进你家信箱,不管你今天看没看,油墨钱(gas 费)一分不少。Pyth 反过来,是个”拉”模型,而且它把数据源这件事直接做到了源头——publisher 就是交易所和顶级做市商自己(比如 Jump 这类),等于以前是二手贩子转述交易所报价,现在是交易所本人亲自到岗报数。
Pyth 的链上合约平时并不保存实时价格。当你的交易需要价格时,在交易里顺手夹一张价格更新证明(由中继器从 Pyth 网络同步过来),合约验证后即取即用,用完不存——像到店自取,要一次取一次,没人替你囤报纸。这个设计换来两个好处:不用为高频推送烧 gas,而且数据来自一手行情、更新达到亚秒级,比 Chainlink 的整点播报新鲜得多。代价是接入复杂度上升,且价格跟着交易走,适合永续合约这种对时效极度饥饿的场景——还记得永续合约的标记价格吗?多交易所现货取指数,那其实就是预言机最朴素的雏形,Pyth 等于把这个雏形交还给交易所本人来供电。
TWAP:一台骗不动的体重秤
上面两条路线都要”额外搭一套基础设施”。而 Uniswap V3 说:不用麻烦,我自己就能当预言机,办法叫 TWAP(Time-Weighted Average Price,时间加权平均价)。
原理直觉很简单。想象一台体重秤,天天自动帮你记体重,报的是一周平均值。某天晚上你暴饮暴食,重了三斤?没关系,摊到七天里,平均体重几乎纹丝不动。想骗过这台秤,你就得连着吃七天大餐——那可不是”一时嘴馋”,是”生活方式”了,成本完全不在一个量级。
放到合约里,它维护一个累积器,每个区块都做一件事:
1 | 累积器 += 当前现货价 × 距上个区块的时间 |
任意时刻想算”过去 N 分钟的均价”,只需要:
1 | TWAP = (累积器现在 - 累积器 N 分钟前) / N 分钟 |
这就是个朴素的积分再除以时间——初中学的”平均速度 = 路程 / 时间”,换了个马甲。但它有一个精妙的性质:短时间的价格尖刺,几乎推不动平均值。攻击者把一个区块的价格拉到 3 倍,这个尖刺只给累积器贡献了”1 个区块 × 3 倍价”的一点增量,摊进 30 分钟窗口,连水花都看不见。想让 TWAP 真的动起来,攻击者必须让虚高价格占住窗口的大部分时间——也就是持续砸钱扛住滑点、硬撑价格。池子越浅砸钱越少?不,池子越浅,维持虚高所需的持续买盘相对越明显,而且你是在跟全世界的套利机器人对线,人家会把你拉的价格瞬间搬平。操纵 TWAP 从”一笔交易的手续费”变成了”一场旷日持久的烧钱大战”,还得打得赢套利大军,性价比归零。
代价是什么?慢。TWAP 是后视镜:它看的是过去 30 分钟的路,不是眼前的悬崖。真实暴跌发生时,30 分钟窗口的均价还挂在半山腰。拿它当清算依据的协议,在瀑布行情里会”看错账”:抵押品实际已经跌成渣,TWAP 显示的估值还岁月静好,清算迟迟不触发,坏账越滚越大。2022 年 5 月 UST 崩盘那几天,不少依赖 TWAP 喂价的协议就吃足了迟钝的亏。安全性的代价从来不会消失,只会换个地方收钱:现货价新鲜但容易被拉,TWAP 抗拉但迟钝——选哪个,取决于你的协议更怕哪一种死法。
案发现场:一个年轻人搬走 1.14 亿美元
理论讲完,讲个真事,当故事听。时间拨回 2022 年 10 月 11 日的晚上,舞台是 Solana 上的借贷协议 Mango Markets,主角是个叫 Avraham Eisenberg 的年轻人。他的表演一共五幕:
第一幕,挑一台好骗的秤。Mango 给抵押品估值,用的是 MNGO——它自家治理代币的现货价。这个币的市场浅得像个水洼,几百美元的单子都能砸出明显水花。
第二幕,左右手互搏拉盘。他准备了两个自己控制的账户,在 MNGO 现货和永续市场里自买自卖:左手 100 美元卖给右手 110,右手再 120 卖回给左手……成交价就这么被”喊”上去了,几美分的币一路喊到接近一美元——十倍级的账面暴涨,实际动用的本金并不大。
第三幕,让秤认下这个数。Mango 的预言机忠实地读到了这个被拉爆的”市场价格”。于是他手里那一堆 MNGO,估值瞬间膨胀成一笔纸面巨款。
第四幕,拿纸面富贵换真金白银。他拿这些虚增的 MNGO 当抵押品,从 Mango 国库借出 USDC、BTC、SOL 等硬通货,总价值约 1.14 亿美元。
第五幕,谢幕离场。随后任由仓位爆掉,抵押品砸回市场,全剧终。
事后相当魔幻:双方谈判,约 6700 万美元被返还,攻击者一度留下约 4700 万,还公开宣称这是”合法利用了代码允许的规则”。三个月后他在波多黎各被捕,后被定罪——法律层面的拉扯比代码还精彩,但那是另一个故事了。
复盘时有一刀必须切准:Chainlink 没有被黑,Solana 也没有被黑。出事的是 Mango 选秤的眼光——把一个深度极浅的小币现货价,直接当成了金融真值。这也是我想划的重点:在预言机事故里,”秤选错了”远比”报价员集体叛变”更常见。攻破 Chainlink 的节点网络要买通多数节点,成本是天文数字;而选错数源,开两个账户对倒几下就够了。这也是为什么主流借贷协议上架新资产那么谨慎:抵押品规模必须远小于喂价源的流动性,小币要么限制借款上限,要么干脆不给额度。一句话教训:再先进的锁,也拦不住你把假钞当真钱收进金库——喂进系统的那个数,首先得配得上”价格”两个字。
把 Mango 案算成一笔账
五幕剧看完,把它摊开成一张账单。下面按公开报道与法庭文书的口径整理,个别中间量各来源略有出入,取量级看:
| 环节 | 数字 | 备注 |
|---|---|---|
| 初始投入 | 约 500 万美元 USDC | 分进两个自己控制的账户(公开指控文书口径) |
| 拉盘手法 | 现货与永续市场自买自卖 | 左手 100 卖给右手 110,几美分一路喊到接近一美元——账面十倍级,真实流通盘纹丝没动 |
| 抵押品账面 | 从约 500 万膨胀成足以覆盖约 1.14 亿借款的”纸面巨款” | Mango 的喂价忠实读下了这个被喊出来的价格 |
| 借出资产 | 约 1.14 亿美元 | USDC、BTC、SOL 等硬通货,从国库直接搬走 |
| 事后 | 归还约 6700 万,一度留下约 4700 万 | 2022 年 12 月被捕,2024 年 4 月被定罪,2025 年 5 月定罪又被推翻 |
算一笔最朴素的账:投入约 500 万,搬走约 1.14 亿——每投入 1 美元,搬走 20 美元开外。这几十倍的”杠杆”不是市场给的,是那台只认现货价的秤给的:秤认下了虚高的价格,纸面抵押立刻兑换成真借款额度。再看谁买单:谈判追回约 6700 万,净窟窿约 4700 万,由 Mango 的偿付能力兜底;MNGO 价格崩塌、协议从此一蹶不振,持有者与做市商一起陪葬。
一句话点题:这约 1.14 亿美元没有破解任何一行代码。喂价源选错——拿低流动性现货价当抵押品定价——等于把秤交给想骗保的人;攻击者没撬任何一把锁,他只是给坏秤喂了个好价钱。
防御清单
理论讲完,把实操层面的防御手段整理成清单,接入任何需要价格的协议时逐条打钩:
- 别用单一现货价:优先 TWAP(Uniswap V3 内置)或多源聚合,天然免疫单块操纵;
- 选对秤:喂价来源的流动性必须显著大于抵押品规模;小市值资产的借贷池要限制规模,甚至拒绝接入;
- 用带聚合的多节点网络:Chainlink 这类中位数聚合方案,并配好偏差阈值和心跳参数,同时清楚自己读到的价格最多”旧”多久;
- 盯住播报员:把链上喂价与外部参考价实时比对,偏差超阈值就告警甚至熔断。这也是参数化保险的天然触发器——DeFi 保险那篇里提过,有的保单直接写着”Chainlink 喂价偏差超过 10% 触发赔付”,把预言机故障变成可理赔的确定性事件;
- 交易内自校验:如果用 Pyth,检查返回的置信区间,价格置信度过宽就拒绝交易,宁可不做也不做错。
代码模拟
照例动手写代码加深理解,两段都是纯标准库,python3 直接跑。
模拟一:10 个报价员里混进一个叛徒
模拟 Chainlink 的聚合逻辑:10 个独立节点各自取数报价,正常报价在真实价格 2000 附近小幅抖动,然后我们让第 7 号节点叛变,报出一个 4 倍的极端价 8000。分别用平均数和中位数聚合,看看谁扛得住。
1 | # 模拟一:10 个预言机节点喂价,其中 1 个被操纵,平均数 vs 中位数 |
运行输出(python3 sim1_median.py):
1 | 10 个节点的报价: |

一目了然:同样是 1/10 的叛徒,平均数被拖偏近 30%(够触发一轮大规模误清算了),中位数只偏了 0.12%,几乎无感。原因在直觉上也说得通:把 8000 塞进排好序的队伍里,它只能站在队尾,中间位置的数还是那 9 个诚实的报价。中位数聚合把”收买一个报价员”的攻击收益打成了零,攻击者只剩一条路:收买一半以上的节点——而节点是相互独立的运营商,让它们合谋的成本,就是 Chainlink 安全性的定价。
模拟二:闪电贷拉盘,TWAP 装睡
模拟第二节讲的闪电贷攻击:一个 10000 ETH / 2000 万 USDC 的池子(现货价 2000),攻击者用闪电贷把价格拉到 3 倍。我们分别看攻击块的现货价和30 分钟窗口的 TWAP,并且做一个对攻击者相当慷慨的设定:让他拉出的 6000 在链上站稳整整一个区块(真实的闪电贷攻击里,拉盘和砸盘在同一笔交易内完成,连这个区块都留不下)。
1 | # 模拟二:闪电贷单块拉盘 3 倍,spot price vs 30 分钟窗口 TWAP |
运行输出(python3 sim2_twap.py):
1 | 拉盘前现货价: 2000.00 USDC/ETH |

同一个攻击动作,两种秤给出了两个世界:现货价飙升 200%,借贷协议的抵押品估值跟着原地起飞;TWAP 只动了 1.32%,攻击块那根尖刺被摊进 30 分钟的时间长河里,几乎没溅起水花。顺带注意那笔 1464 万美元的买入——这就是闪电贷存在的意义,攻击者不需要一分钱自有资金,只需要支付手续费。而如果他面对的是 TWAP,想让 30 分钟均价真的涨到 6000,就得让大部分区块都站在虚高价上,一边扛着滑点一边和套利机器人对线,这场战争的军费足够直接买下一个 Mango。
总结
把全文浓缩成三句话:
- 链上没有真值,只有别人塞进来的纸条。 智能合约是确定性孤岛,所有外部价格都必须经由预言机写入,而”谁写的、凭什么信”是预言机的核心命题;
- 任何单一价格源,要么能被买,要么能被拉。 项目方自己的喇叭可以谎报,小池子现货价可以被闪电贷单块改写,连中位数聚合也只是在提高作恶成本而非消除作恶可能;
- 预言机安全的本质,是让操纵的账单大于操纵的收益。 多节点中位数让收买变成”买下一半网络”,TWAP 让改价变成”长期烧钱大战”,多源交叉验证让谎报变得无处遁形——没有银弹,只有不断加高的作恶账单。
学习建议:打开 data.chain.link,挑一个你熟悉的价格流(比如 ETH/USD),盯着它的历史轮次看一会儿,看看偏差阈值和心跳写的是多少,再想想:如果明天市场上演 312 那样的瀑布,这组参数下,链上价格会落后现货多久?纸上得来终觉浅,价格流面前见真章。
最后留个钩子:预言机负责把价格送进来,但什么顺序用这个价格是另一门学问——清算机器人抢跑喂价更新的排序博弈,是 MEV 供应链里最肥美的一环,那片战场我们在《预言机的另一半:MEV 与出块拍卖》里再会。
相关阅读
- 《探索 DeFi 借贷与收益率交易的无限潜力》:清算正是靠预言机触发,闪电贷也是本文攻击手法的弹药库
- 《永续合约:无到期日的杠杆交易是如何运作的》:标记价格=多交易所现货指数,就是最朴素的预言机
- 《DeFi 保险:谁来保护你的链上资产?》:Mango 案例与”喂价偏差>10%”参数化保险
- 《当 RWA 遇上幸福小区玉米》:RWA 场景里预言机就是那个”信使”,本文是信使的全面审讯记录
- 《Uniswap V4:更灵活、更高效的去中心化交易所》:表格里点名的”自定义预言机” hook,喂价策略可以按池定制
- 《预言机的另一半:MEV 与出块拍卖》:清算 MEV 的排序博弈,价格进来之后的故事