For the complete documentation index, see llms.txt. This page is also available as Markdown.

Oracle Gateway

Chainlink 的 AggregatorV3Interface 背后的 Pyth 和 Switchboard 价格数据源。

Oracle Gateway 通过 Chainlink 的接口公开 Solana 原生价格源(Pyth、Switchboard V3) AggregatorV3Interface,因此迁移到 Rome 的以太坊协议可以保留其现有的预言机集成代码。 Oracle Gateway V2 已部署在 Hadrian、Martius 和 Rubicon 上,尽管注册表仅列出了 Hadrian 和 Martius 的 feed 地址,而且由客户门户负责注册。Rome 运行的 keeper 会刷新缓存和 book feed,但并非每一时刻每个 feed 都是新鲜的—— 今天哪些 feed 会响应 如下所述。适配器、工厂和 keeper 模型在以下内容中有说明: rome-oracle-gateway 代码仓库。

预览。 Oracle Gateway 尚未经过审计。门户页面也明确说明:不要在生产价值流中使用它。

问题

以太坊 DeFi 期望使用 Chainlink 的 AggregatorV3Interface:

(, int256 price,,,) = priceFeed.latestRoundData();

Solana 的预言机提供方(Pyth、Switchboard)具有不同的数据格式。若不做适配,每个迁移过来的协议都需要自定义预言机代码。

解决方案

Oracle Gateway V2 部署轻量级适配器合约,它们:

  1. 直接从 Solana 上的 Pyth 或 Switchboard 账户读取价格数据(跨状态读取,而非 CPI)

  2. 解析链上数据

  3. 将价格标准化为 8 位小数

  4. 暴露标准的 Chainlink AggregatorV3Interface

import {IAggregatorV3Interface} from "@rome-protocol/rome-solidity/contracts/oracle/IAggregatorV3Interface.sol";

// 与以太坊上的 Chainlink 接口相同
(, int256 price,,,) = IAggregatorV3Interface(ADAPTER).latestRoundData();
// price = SOL/USD,保留 8 位小数(例如 15000000000 = $150.00)

分三步使用一个 feed

  1. 从注册表中解析适配器地址 —— 切勿将其硬编码:

注册表从 GitHub 安装(npm install github:rome-protocol/rome-registry#v0.2.0)。feed 键带有一个后缀,用于说明其背后是哪种适配器;见 注册表中的 feed 键 然后再选择一个。

  1. 阅读它 ,使用你的协议已经在用的接口—— IAggregatorV3Interface(solUsd).latestRoundData()。一个过期或未初始化的 feed 回退 而不是提供冻结的价格。

  2. 一次使用多个 feed? BatchReader.getLatestPrices(adapters[]) (Hadrian,registry v0.2.0: 0x2869f0a96572e7fb15036656e9a32fd17346499b)—— 以及 getFeedHealth(adapters[]) 在依赖它们之前先检查。缓存适配器使每个消费者只需读取一个普通的 SLOAD,因此多 feed 事务(多抵押借款、组合视图)保持低成本。

cardo 在 Hadrian 上读取这些 feed。由于其接口完全是 AggregatorV3Interface,Compound 和 Aave 级别的协议无需修改预言机代码即可接入这些 feed——只需将其价格源配置指向适配器地址。

适配器类型

  • PythPullAdapter —— 读取 Pyth 价格账户(价格、置信度、EMA、发布时间)。

  • SwitchboardV3Adapter —— 读取 Switchboard 聚合器账户(价格、时间戳;无 EMA)。

  • CachedPyth / CachedFeed 适配器 —— 用于高 CU 效率读取的缓存轨道变体。

  • BookFeedAdapter —— 针对以下对象中某一条目提供只读、兼容 Chainlink 的视图: PriceBook 合约。该 book 保存其所有已注册 feed 的价格,并以批量方式刷新(刷新无需许可);每个 feed 的适配器本身不维护缓存。暂停的条目会立即回退并返回 AdapterPaused,而不是等到过期。

适配器以 EIP-1167 最小代理克隆的形式部署: OracleAdapterFactory 该工厂克隆 Pyth、Switchboard 和缓存适配器,而每个 PriceBook 都会克隆其自己的 BookFeedAdapter ,当某个 feed 注册时。它们在以下基础上还暴露一个扩展接口: AggregatorV3Interface (latestPriceData, maxStaleness, oracleType,以及一个 metadata() 用于健康检查的接口)。

注册表中的 feed 键

getOracle(chainId).feeds 每个资产在其中都有多个键。键名上的后缀表示适配器类型:

键
适配器类型
读取方式

SOL/USD

直接 Pyth(PythPullAdapter)

每次调用时都会解析 Pyth 账户。

SOL/USD-BOOK

BookFeedAdapter

读取一个 PriceBook 由 keeper 刷新的条目。

SOL/USD-CACHED

缓存(封装另一个 feed)

读取 keeper 刷新的存储价格。

SOL/USD-CACHEDPYTH

缓存 Pyth(CachedPythAdapter)

读取 keeper 刷新的存储价格。

SOL/USD-SB

Switchboard(SwitchboardV3Adapter)

每次调用时都会解析 Switchboard 账户。注册表将这些条目标记为 已弃用.

应使用哪一个取决于链。请先查看下一节。

今天哪些 feed 有响应

以下是只读 latestRoundData() 调用结果,针对本页面编写时 registry v0.2.0 中列出的 feed。Devnet 上的 feed 新鲜度会分钟级变化,因此请将其视为快照,并在你计划使用的 feed 上运行相同调用。

  • Hadrian。 直接 feed 往往已过期:在重复检查中,其中大多数(BONK、BTC、ETH、JITOSOL、JTO、JUP、MSOL、USDC、USDT)都会回退并返回 StalePriceFeed 每次都是如此,而 SOL/USD 则在某些调用中有响应,而在另一些调用中没有响应。 -BOOK feed 最可靠,对除 JTO 之外的所有资产每次调用都有响应,而 JTO 的 -BOOK feed 则因过期而回退。每个 -CACHED 和 -CACHEDPYTH feed 都会回退并返回 UninitializedPriceFeed (没有任何东西刷新过它们),而 SOL/USD-SB 则回退并返回 StalePriceFeed。 -BOOK feed,并先运行 latestRoundData() 你选择的 feed,再依赖它。

  • Martius。 直接的、 -CACHED, -CACHEDPYTH 和 -BOOK feed 对除 JTO 之外的每个资产都有响应,而 JTO 的直接和缓存 feed 都因过期而回退(Martius 上没有 -BOOK JTO 的 feed)。 SOL/USD-SB 则回退并返回 StalePriceFeed.

  • Rubicon。 注册表没有列出 Rubicon 的预言机 feed,因此本页不予覆盖。工厂已部署在那里(见 已部署地址),其首批适配器有响应,但 feed 地址应从工厂中查找(adapterCount() 和 allAdapters(i)),而不是从注册表中获取。

这里的回退是预期行为:过期或未初始化的 feed 会明确失败,而不是返回旧价格。请在你的合约中处理它。

新鲜度——oracle-keeper

适配器强制执行一个 maxStaleness 窗口:如果 block.timestamp - publishTime > maxStaleness,读取将回退。在 Solana 主网,Pyth 运行生产 keeper。在 Solana devnet(公共链所在之处),Pyth 的推送器只能尽力而为,因此 Rome 运行其自己的 oracle-keeper sidecar,以保持底层 Pyth 账户新鲜,并刷新缓存和 book feed。该 keeper 并不会在每一时刻覆盖每条链上的每个 feed(见 今天哪些 feed 会响应).

使用以下方式检查 feed 健康状况: BatchReader.getFeedHealth (它会读取每个适配器的 metadata()) latestRoundData —— 某个 feed 可能可达,但已经过期。

客户门户

通过以下方式按链注册 feed 并配置消费者: Oracle Gateway 门户。 门户页面 了解其覆盖范围。

已部署地址

各链的权威地址(工厂、适配器实现和当前生效的 feed)位于 registry。主要工厂来自 live registry v0.2.0 的条目(Hadrian 和 Martius 上为 5.0.0 版本;Rubicon 的地址则是 合约地址 页面上的地址);见 读取 contracts 列表:

合约
Hadrian
Martius
Rubicon

OracleAdapterFactory

0x52b008771fc95b4674a4f3533570b3d223f27f68

0x8c92fd70360df63246cd0c8247119c79234b26ae

0x6a1340dd86c3951b6f13d9001b3951d56a2fe581

BatchReader

0x2869f0a96572e7fb15036656e9a32fd17346499b

0xab10948dc8b5bdaea0b31266b663022e7492e9cd

0x580dc0ecb226530ee8a90b57e0f0cd2555fb4f0f

约束

  • 没有历史轮次数据 —— getRoundData(roundId) 会回退;仅 latestRoundData() 受支持。

  • 不支持 Switchboard EMA —— EMA 数据仅限 Pyth。

  • 按链设置的过期时间 —— maxStaleness 在启动时按环境设置:直接(Pyth 读取)适配器默认为约 60 秒;缓存适配器默认为 3600 秒(当 keeper 刷新它们时,频率远高于此)。

  • 解析器偏移量 会根据当前的 Pyth/Switchboard 布局进行验证;布局变更需要重新验证。

下一步

最后更新于

这有帮助吗?