值得信赖的区块链资讯!
价格预言机的使用总结(一):Chainlink篇
前言
价格预言机已经成为了 DeFi 中不可获取的基础设施,很多 DeFi 应用都需要从价格预言机来获取稳定可信的价格数据,包括借贷协议 Compound、AAVE、Liquity,也包括衍生品交易所 dYdX、PERP 等等。
目前最主流的价格预言机主要有 Chainlink、UniswapV2、UniswapV3,这几种价格预言机的接入方式和适用场景都不太一样,可以单独使用,也可以结合使用。鉴于不少同学还不知道这些预言机具体有哪些接入方式,也不了解背后的机制,更不清楚如何才能做到保证安全性的同时又能以最小的成本接入。下面,我将分享下我的经验总结,以供参考。
Chainlink
先从 Chainlink 的价格预言机开始聊起,这应该是使用最广泛的价格预言机了。
其实,Chainlink 提供的产品不只是价格预言机,还有其他产品,包括 Verifiable Random Numbers (VRF)、Call External APIs、Chainlink Keepers。当然,使用最广泛的还是价格预言机,叫 Data Feeds。
Chainlink Data Feeds 目前已经支持了多条链,主要还是 EVM 链,包括 Ethereum、BSC、Heco、Avalanche 等,也包括 Arbitrum、Optimism、Polygon 等 L2 的链。另外,也支持了非 EVM 链,目前支持了 Solana 和 Terra。不过,我对非 EVM 链并不熟悉,所以只讲 EVM 链的使用。
DeFi 应用接入使用 Chainlink Data Feeds 其实很简单,而且还有不同的使用方式,下面就来看看最常用的使用方式。
Price Feed
第一种使用方式,官方给的示例代码是这样的:
// SPDX-License-Identifier: MITpragma solidity ^0.8.7;import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";contract PriceConsumerV3 { AggregatorV3Interface internal priceFeed; /** * Network: Kovan * Aggregator: ETH/USD * Address: 0x9326BFA02ADD2366b30bacB125260Af641031331 */ constructor() { priceFeed = AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331); } /** * Returns the latest price */ function getLatestPrice() public view returns (int) { ( uint80 roundID, int price, uint startedAt, uint timeStamp, uint80 answeredInRound ) = priceFeed.latestRoundData(); return price; }}
首先,每个交易对都有一个单独的 Price Feed,也叫 Aggregator,其实就是一个个 AggregatorProxy,像下面这样:
可以看到,每个 Pair 都有一个对应的 Proxy,读取价格其实就是从 Proxy 提供的方法读取的。Proxy 的具体实现稍微有点复杂,但 DeFi 应用要接入的话,只要知道 Interface 就够了,这个 Interface 则很简单,就是示例代码中所引入的 AggregatorV3Interface,其代码如下:
// SPDX-License-Identifier: MITpragma solidity ^0.8.0;interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound );}
就 5 个查询方法而已,简单介绍下这几个方法:
-
decimals():返回的价格数据的精度位数,一般为 8 或 18
-
description():一般为交易对名称,比如 ETH / USD
-
version():主要用来标识 Proxy 所指向的 Aggregator 类型
-
getRoundData(_roundId):根据 round ID 获取当时的价格数据
-
latestRoundData():获取最新的价格数据
大部分应用场景可能只需要读取最新价格,即调用最后一个方法,其返回参数中,answer 就是最新价格。
另外,大部分应用读取 token 的价格都是统一以 USD 为计价单位的,若如此,你会发现,以 USD 为计价单位的 Pair,精度位数都是统一为 8 位的,所以一般情况下也无需根据不同 token 处理不同精度的问题。
当然,在实际应用中,肯定不会只读取一个固定 Token 的价格,更多场景是根据 Token 读取该 Token 的 USD 价格,因此可以将前面的示例合约升级为如下:
// SPDX-License-Identifier: MITpragma solidity ^0.8.7;import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";import "@openzeppelin/contracts/access/Ownable.sol";contract PriceConsumerV3 is Ownable { mapping(address => AggregatorV3Interface) internal priceFeedMap; function setPriceFeed(address token, address priceFeed) external onlyOwner { priceFeedMap[token] = AggregatorV3Interface(priceFeed); } /** * Returns the latest price */ function getLatestPrice(address token) public view returns (int) { ( uint80 roundID, int price, uint startedAt, uint timeStamp, uint80 answeredInRound ) = priceFeedMap[token].latestRoundData(); return price; }}
原先的示例只能读取 ETH/USD 一个 Pair 的价格,而现在则可以设置和读取多个不同 token 的价格。比如,现在想要读取 UNI 的 USD 价格,就可以先查出 UNI/USD 的 priceFeed,查出其 Proxy 为 0x553303d460EE0afB37EdFf9bE42922D8FF63220e ,而 UNI token 地址为 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984,那就可以调用:
setPriceFeed("0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", "0x553303d460EE0afB37EdFf9bE42922D8FF63220e");
如此,UNI 所使用的 priceFeed 就设置好了,想读取 UNI 的最新价格时,调用 getLatestPrice() 就可读取到结果了,如下:
int price = getLatestPrice("0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984");
另外,mapping 所使用的 key 也可以不用 token address,改用 token symbol 或其它具有唯一标识性的属性也是可以的。
虽然该示例比较简单,很多实际应用中,可能比这复杂,但基本核心功能是差不多的了。
Feed Registry
第一种接入方式虽然已经很简单,但每个 token 都需要 owner 执行 setPriceFeed,治理成本其实有点高,对某些场景来说就不太灵活。这时候,就可以考虑使用第二种方式来接入 Chainlink Data Feeds 了,通过使用 Feed Registry 的方式来接入。
Feed Registry 可以简单理解为 PriceFeeds 的聚合器,已经聚合了多个 priceFeed,有了它,使用者就无需自己去设置 priceFeed 了,可直接通过 Feed Registry 读取价格数据,如下图:
官方给的使用示例代码则如下:
// SPDX-License-Identifier: MITpragma solidity ^0.8.7;import "@chainlink/contracts/src/v0.8/interfaces/FeedRegistryInterface.sol";import "@chainlink/contracts/src/v0.8/Denominations.sol";contract PriceConsumer { FeedRegistryInterface internal registry; /** * Network: Ethereum Kovan * Feed Registry: 0xAa7F6f7f507457a1EE157fE97F6c7DB2BEec5cD0 */ constructor(address _registry) { registry = FeedRegistryInterface(_registry); } /** * Returns the ETH / USD price */ function getEthUsdPrice() public view returns (int) { ( uint80 roundID, int price, uint startedAt, uint timeStamp, uint80 answeredInRound ) = registry.latestRoundData(Denominations.ETH, Denominations.USD); return price; } /** * Returns the latest price */ function getPrice(address base, address quote) public view returns (int) { ( uint80 roundID, int price, uint startedAt, uint timeStamp, uint80 answeredInRound ) = registry.latestRoundData(base, quote); return price; }}
可看到,开头引入了两个 sol 文件,FeedRegistryInterface 和 Denominations。Denominations 是一个很简单的 library,主要定义了各种货币的地址,如下:
// SPDX-License-Identifier: MITpragma solidity ^0.8.7;library Denominations { address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; address public constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB; // Fiat currencies follow https://en.wikipedia.org/wiki/ISO_4217 address public constant USD = address(840); address public constant GBP = address(826); address public constant EUR = address(978); // … other fiat currencies}
FeedRegistryInterface 则定义了不少函数,包括和 AggregatorV3Interface 一样的几个函数,只是每个函数相比 AggregatorV3Interface 多了两个参数:base 和 quote,如下:
interface FeedRegistryInterface { // V3 AggregatorV3Interface function decimals(address base, address quote) external view returns (uint8); function description(address base, address quote) external view returns (string memory); function version(address base, address quote) external view returns (uint256); function latestRoundData(address base, address quote) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function getRoundData(address base, address quote, uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); // … other functions}
假设交易对为 UNI/USD,那 base 为 UNI 的 token 地址,quote 则为 USD 的地址,即为 Denominations.USD;假设交易对为 ETH/BTC,那 base 则为 Denominations.ETH,quote 为 Denominations.BTC。
另外,也可以通过 getFeed(address base, address quote) 直接读取到 priceFeed。
可以发现,使用 Feed Registry 的方式,主要都是用 base/quote 的方式进行查询。
FeedRegistry 里的每个 priceFeed 则是通过先后调用 proposeFeed() 和 confirmFeed() 两个函数设置的,不过这两个函数只有 FeedRegistry 的 owner 才可以调用。
喂价机制
至此,我们已经知道如何接入 Chainlink Data Feeds 来获取价格信息了,但还不够,我们还要了解背后的喂价机制,也要了解价格数据多久更新一次的,如此才能更好地判定 Chainlink 的价格预言机是否能满足具体的场景需求。
首先,Price Feed 的价格是通过多个层级的数据聚合得到的。实际上有三个数据聚合层:数据源聚合、节点运营商聚合、预言机网络聚合。
最原始的价格数据主要来源于币安、火币、Coinbase 等中心化交易平台,以及 Uniswap、Sushi 等去中心化交易平台。存在一些专门做数据聚合的服务商(比如 amberdata、CoinGecko),会从这些交易平台收集原始的价格数据,并对这些数据源进行加工整合,比如根据交易量、流动性和时差等进行加权计算。
这就是第一个层面的聚合,对数据源的聚合。拥有可靠的价格数据源的关键是要有全面的市场覆盖,才能保证一个价格点能代表所有交易环境的精确聚合,而不是单个交易所或少数交易所的价格,以防止数据被人为操纵和出现价格偏差。也因此,为了确保数据具有高度的防篡改和可靠性,Chainlink Data Feeds 只会从优质的数据聚合服务商获取数据,这意味着每个数据源都代表一个从所有中心化和去中心化交易所聚合的经过交易量调整的精细价格点,也因此可以有效抵抗闪电贷或价格异常偏差等攻击。
第二层则是 Chainlink Node Operators 所做的聚合。每个 Chainlink Node Operator 主要负责运行用于在区块链上获取和广播外部市场数据的 Chainlink 核心软件。Node Operators 会从多个独立的数据聚合服务商获取价格数据,并获取它们之间的中值,剔除掉异常值和 API 停机时间。比如,从 A 数据聚合服务商获取到价格点为 7.0,从 B 服务商获取到价格点为 7.2,那取中值后的价格点为 7.1。这意味着不仅每个单独的数据源反映了来自所有交易环境的聚合价格点,而且每个单独的节点的响应代表了来自多个数据源的聚合,进一步防止任何单一来源成为故障点,即避免了单点故障。
最后一层则是整个预言机网络的聚合,其聚合的方式有多种,但最常见的聚合方式是当响应节点数量达到预设值时对数据取中值。比如总共有 31 个节点,预设值为 21,即收到了 21 个节点的响应后,就取这些节点的价格数据的中值作为最终的价格。不过,并非每一轮的价格结果都会更新到链上,只有满足两个触发参数之一的时候才会更新:偏差阈值(Deviation Threshold)和心跳阈值(Heartbeat Threshold)。而且,不同 PriceFeed 的这两个参数的值可能会不一样。
比如,ETH/USD 的偏差阈值为 0.5%,即表示新一轮的价格点跟上一次更新的价格偏差超过 0.5% 的时候才会更新链上价格;而心跳阈值为 3600 秒,即表示上一次价格更新后过了 1 小时后才会更新链上价格。另外,因为每一轮的数据聚合都不是实时的,也需要时间,再加上偏差阈值的限制,所以,有时候,要隔几十分钟才会有价格更新,这点比较关键,需要清楚。有些 Price Feed 的偏差阈值比较大,会高达十几个小时才会有价格更新,比如下面这个:
可看到,其偏差阈值高达 5%,且已经长达 11 个小时没有价格更新了,而它的心跳阈值其实也比较高,长达 24 小时。
高达 5% 的偏差阈值,且这么长时间都没有更新价格,这如果是应用到一些高杠杆的交易产品,可能就不太合适了。
总结
总而言之,Chainlink 价格预言机接入方便,且安全性还是比较高的,但因为其价格更新机制存在偏差阈值,导致价格更新比较慢,短则几分钟或几十分钟更新一次,长则可能达 24 小时才更新一次,因此,一般只适用于对价格更新不太敏感的应用。这也是 Chainlink 价格预言机的局限性,并无法适用所有场景的应用。
比推快讯
更多 >>- 沃尔玛将允许顾客直接使用 Gemini 完成购物
- 中文 Meme 代币热度退潮,我踏马来了、老子等市值大幅回撤
- Michael Saylor 再次发布比特币 Tracker 信息,或暗示再次增持 BTC
- BNB 链上部分中文 meme 代币下跌,"老子" 6 小时下跌 34.6%
- 麻吉大哥 25 倍杠杆 ETH 多单浮亏 28.7 万美元,较高点浮盈已回撤超 200 万美元
- 数据:1791.9 枚 PAXG 从匿名地址转出,经中转后流入 0x
- VERTEXS.AI 发布一站式聚合交易平台规划,聚焦跨链与 AI 交易基础设施
- 以太坊基金会联席执行董事:以太坊未来取决于零知识证明
- Solana:将与 X 合作并内置 X 应用
- 能否加密货币支付养老金成为俄罗斯社会基金热门非标准问询
- 委内瑞拉国家石油公司利用 USDT 规避制裁
- 比特币突破 91,000 美元,24 小时涨幅 0.32%
- 特朗普佩戴快乐特朗普胸针,却坚称美国再次伟大前从不快乐
- 委内瑞拉石油出口大量使用 USDT,Tether 强调严格遵守国际制裁规定
- 某个与 Polygon 关联地址向币安和 GSR Markets 存入 2000 万枚 POL
- 分析:亚洲资金当前对 BTC 价格主导性更强,市场静待美区资金跟进
- 麻吉做多 ZEC 仓位快速平仓,获利仅约 2544 美元
- TRADOOR 出现大额提币,价值 210 万美元代币被分流至 10 个新钱包
- CZ:无法预测超级周期是否即将到来,继续积累
- Solana 链上 Meme 代币 SOL 市值突破 860 万美元,24 小时涨超 70%
- X 正开发智能资产标签,显示关联资产实时价格与合约信息
- 贵州茅台成立数字科技公司,涉区块链技术相关软件和服务
- 麻吉大哥新开 10 倍杠杆 ZEC 多单,现整体仓位浮亏约 41 万美元
- CZ 转推:BNB 为加密圈最强金铲子,2025 年单枚收益为 71.5 美元
- OneBullEx 今日正式开启 OBE 积分兑换活动
- 数据:MetaMask 预测市场上线近一个月,名义交易量仅为 70 万美元,不足 Phantom 钱包交易量 1/10
- 24 小时现货资金流入/流出榜:ETH 净流入 1.13 亿美元,SOL 净流入 2340 万美元
- CZ:阿联酋利用政府资源开采比特币已持续一段时间
- 数据:TornadoCash 业务地址 2025 年共收入 693,412 枚 ETH,价值约 25 亿美元;净流入资金约 14 亿美元
- 币安 2025 年为 540 万用户避免潜在损失近 67 亿美元,协助没收涉案资金 1.31 亿美元
- 0G 登上 YTN 《最强企业》节目,探讨当前 AI 技术的发展
- Meme 币 FISH 今日反弹超 68%,市值现报 750 万美元
- 0G Starboard 第一季奖励现已上线
- Santiment:以太坊社媒情绪低迷,接近以往行情启动前情形
- 数据:过去 24 小时全网爆仓 6,277.93 万美元,多单爆仓 3,907.41 万美元,空单爆仓 2,370.52 万美元
- 去年比特币网络共有 36 个区块由独立矿工挖出
- 特朗普任期内再遭弹劾的概率升至 57%
- Vitalik:行业需要更好的去中心化稳定币,目前仍存三个问题待解决
- Willy Woo:看好比特币 1-2 月表现,但对 2026 年持谨慎态度
- meme 币我踏马来了榜一地址于 6 天前低位买入,现已浮盈 43 万美元
- Peter Brandt 评 Tom Lee 唱多比特币:不能信任执迷于自己职位的人
- 数据:ETH 跌破 3100 美元
- 稳定币总市值本周微增 0.13%,仍维持历史高位
- DWF Labs 联创:已对某 DeFi 项目完成 100 万美元种子轮投资
- 今日为哈尔·芬尼著名比特币推文Running bitcoin发表 17 周年
- ETH 突破 3100 USDT,24H 涨幅 0.43%
- 某中本聪时代矿工今日转移 2000 枚比特币
- BNB Chain 增长总监:希望 BSC 有原生的,真正来自社区的草根文化,本人不参与任何代币的发售
- 美国参议院银行委员会将于 15 日就《CLARITY 法案》进行投票
- BSC 预测市场单日名义交易量超 4 亿美元,持仓量达 1.5 亿美元创历史新高
比推专栏
更多 >>观点
比推热门文章
- 沃尔玛将允许顾客直接使用 Gemini 完成购物
- 中文 Meme 代币热度退潮,我踏马来了、老子等市值大幅回撤
- Michael Saylor 再次发布比特币 Tracker 信息,或暗示再次增持 BTC
- BNB 链上部分中文 meme 代币下跌,”老子” 6 小时下跌 34.6%
- 麻吉大哥 25 倍杠杆 ETH 多单浮亏 28.7 万美元,较高点浮盈已回撤超 200 万美元
- 数据:1791.9 枚 PAXG 从匿名地址转出,经中转后流入 0x
- VERTEXS.AI 发布一站式聚合交易平台规划,聚焦跨链与 AI 交易基础设施
- 以太坊基金会联席执行董事:以太坊未来取决于零知识证明
- Solana:将与 X 合作并内置 X 应用
- 能否加密货币支付养老金成为俄罗斯社会基金热门非标准问询
比推 APP



