> For the complete documentation index, see [llms.txt](https://docs.rome.builders/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rome.builders/zh/chan-pin/oracle-gateway.md).

# Oracle Gateway

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

Oracle Gateway 通过 Chainlink 的接口公开 Solana 原生价格源（Pyth、Switchboard V3） `AggregatorV3Interface`，因此迁移到 Rome 的以太坊协议可以保留其现有的预言机集成代码。 **Oracle Gateway V2 已部署在 Hadrian 和 Rubicon 上**，不过，注册表只列出了 Hadrian 的价格源地址，而注册由客户门户处理。由 Rome 运行的 keeper 会刷新缓存型和 book 型价格源，但并非每个价格源在每一时刻都保持最新—— [今天哪些价格源可用](#which-feeds-answer-today) 下面会详细说明。适配器、工厂和 keeper 模型在 [rome-oracle-gateway](https://github.com/rome-protocol/rome-oracle-gateway) 仓库中有说明。

> **预览。** Oracle Gateway 尚未经过审计。门户中也有同样说明：不要将其用于生产环境中的资金流转。

## 问题

以太坊 DeFi 期望 Chainlink 的 `AggregatorV3Interface`:

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

Solana 的预言机提供方（Pyth、Switchboard）数据格式不同。如果不进行适配，每个迁移过来的协议都需要定制的预言机代码。

## 解决方案

Oracle Gateway V2 部署了轻量级适配器合约，用于：

1. 直接从 Solana 上的 Pyth 或 Switchboard 账户读取价格数据（跨状态读取，而非 CPI）
2. 解析链上数据
3. 将价格统一标准化为 8 位小数
4. 暴露标准的 Chainlink `AggregatorV3Interface`

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

// 与以太坊上的 Chainlink 使用相同的接口
(, int256 price,,,) = IAggregatorV3Interface(ADAPTER).latestRoundData();
// price = SOL/USD，精度为 8 位小数（例如 15000000000 = 150.00 美元）
```

## 三步消费一个价格源

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

```ts
import { getOracle } from "@rome-protocol/registry";
const solUsd = getOracle(200010).feeds["SOL/USD"].address;
// Hadrian，registry v0.2.0: 0x567384ee000ca0418c386A73eC2b7466f9f99D6b
```

注册表从 GitHub 安装（`npm install github:rome-protocol/rome-registry#v0.2.0`）。价格源键名带有一个后缀，用于说明其背后是哪种适配器；请参见 [注册表中的价格源键名](#feed-keys-in-the-registry) 选择之前先查看。

2. **读取它** 使用你的协议已经在用的接口—— `IAggregatorV3Interface(solUsd).latestRoundData()`。过期或未初始化的价格源 **回退** 而不是返回一个冻结的价格。
3. **一次获取多个价格源？** `BatchReader.getLatestPrices(adapters[])` （Hadrian，registry v0.2.0： `0x2869f0a96572e7fb15036656e9a32fd17346499b`）——以及 `getFeedHealth(adapters[])` 在依赖它们之前先检查。缓存型适配器让每个消费者只需读取一个普通的 `SLOAD`，因此多价格源交易（多抵押借款、投资组合视图）仍然保持低成本。

[cardo](https://github.com/rome-protocol/cardo) 在 Hadrian 上读取这些价格源。由于其接口完全与 `AggregatorV3Interface`，因此 Compound 和 Aave 级别的协议无需更改预言机代码即可接入这些价格源——只需将其价格源配置指向适配器地址即可。

## 适配器类型

* **PythPullAdapter** — 读取 Pyth 价格账户（价格、置信区间、EMA、发布时刻）。
* **SwitchboardV3Adapter** — 读取 Switchboard 聚合器账户（价格、时间戳；不含 EMA）。
* **CachedPyth / CachedFeed 适配器** — 用于高效 CU 读取的缓存轨道变体。
* **BookFeedAdapter** — 针对一个 `PriceBook` 合约条目的只读、兼容 Chainlink 的视图。该 book 保存其所有已注册价格源的价格，并以批量方式刷新（刷新是无权限的）；每个价格源的适配器本身不保留缓存。处于暂停状态的条目会立即回滚，并返回 `AdapterPaused`，而不是等到过期。

适配器以 EIP-1167 最小代理克隆的形式部署：由 `OracleAdapterFactory` 克隆 Pyth、Switchboard 以及缓存型适配器，并且每个 `PriceBook` 都会克隆其自身的 `BookFeedAdapter` 在注册价格源时。它们在 `AggregatorV3Interface` (`latestPriceData`, `maxStaleness`, `oracleType`，以及一个 `metadata()` 接口旁提供扩展接口（用于健康检查）。

## 注册表中的价格源键名

`getOracle(chainId).feeds` 每个资产在其中对应多个键。键名后的后缀用于标识适配器类型：

| 键                    | 适配器类型                               | 读取方式                                       |
| -------------------- | ----------------------------------- | ------------------------------------------ |
| `SOL/USD`            | 直接 Pyth（`PythPullAdapter`)          | 每次调用都会解析 Pyth 账户。                          |
| `SOL/USD-BOOK`       | `BookFeedAdapter`                   | 读取一个 `PriceBook` 由 keeper 刷新的条目。           |
| `SOL/USD-CACHED`     | 缓存型（封装另一个价格源）                       | 读取由 keeper 刷新的已存储价格。                       |
| `SOL/USD-CACHEDPYTH` | 缓存型 Pyth（`CachedPythAdapter`)       | 读取由 keeper 刷新的已存储价格。                       |
| `SOL/USD-SB`         | Switchboard（`SwitchboardV3Adapter`) | 每次调用都会解析 Switchboard 账户。注册表将这些条目标记为 `已退役`. |

具体选择哪一个取决于链。请先查看下一节。

### 今天哪些价格源可用

这些是只读 `latestRoundData()` 调用在本页面撰写时对 registry v0.2.0 中列出的价格源所得到的结果。devnet 上价格源的新鲜度每分钟都会变化，因此请将其视为快照，并在你计划使用的价格源上执行同样的调用。

* **Hadrian。** 直接价格源通常过期：在重复检查中，其中大多数（BONK、BTC、ETH、JITOSOL、JTO、JUP、MSOL、USDC、USDT）都会回滚并返回 `StalePriceFeed` 每次都如此，而且 `SOL/USD` 在某些调用中有响应，而在其他调用中则没有。该 `-BOOK` 价格源最可靠，除 JTO 外，所有资产在每次调用时都能得到响应，而 JTO 的 `-BOOK` 价格源会因过期而回滚。每个 `-CACHED` 和 `-CACHEDPYTH` 价格源都会回滚并返回 `UninitializedPriceFeed` （没有任何内容刷新过它们），而且 `SOL/USD-SB` 回滚并返回 `StalePriceFeed`。在 Hadrian 上，优先选择 `-BOOK` 价格源，并运行 `latestRoundData()` 在你依赖它之前，在你选择的价格源上。
* **Rubicon。** 注册表未列出 Rubicon 的预言机价格源，因此本页不予涵盖。工厂已部署在那里（见 [已部署地址](#deployed-addresses)），其首批适配器已可响应，但请从工厂中查询价格源地址（`adapterCount()` 和 `allAdapters(i)`），而不是从注册表中获取。

这里的回滚是预期行为：过期或未初始化的价格源会明确失败，而不是返回旧价格。请在你的合约中处理这一情况。

## 新鲜度——oracle-keeper

适配器强制执行一个 `maxStaleness` 窗口：如果 `block.timestamp - publishTime > maxStaleness`，读取会回滚。在 Solana 主网上，Pyth 运行生产 keeper。在 Solana devnet（Hadrian 所在之处）上，Pyth 的推送是尽力而为，因此 Rome 运行自己的 **oracle-keeper** 旁路进程，以保持底层 Pyth 账户的最新状态，并刷新缓存型和 book 型价格源。keeper 并不会在每个时刻覆盖每条链上的每个价格源（见 [今天哪些价格源可用](#which-feeds-answer-today)).

使用以下方式检查价格源健康状态： `BatchReader.getFeedHealth` （它会读取每个适配器的 `metadata()`），而不只是 `latestRoundData` ——一个价格源可能可达，但仍然过期。

## 客户门户

通过 [Oracle Gateway 门户](https://oracle.testnet.romeprotocol.xyz/)。参见 [门户页面](/zh/rome-shang-de-ying-yong/oracle-gateway.md) 了解其涵盖内容。

## 已部署地址

各链的规范地址（工厂、适配器实现以及在线价格源）存放在 [registry](https://github.com/rome-protocol/rome-registry/tree/main/chains)。主要工厂来自 `生效` registry v0.2.0 的条目（Hadrian 上为 5.0.0 版本；Rubicon 地址对应的是 [合约地址](/zh/can-kao/contract-addresses.md) 页面）；参见 [读取合约列表](/zh/can-kao/contract-addresses.md#reading-the-contracts-list):

| 合约                   | Hadrian                                      | Rubicon                                      |
| -------------------- | -------------------------------------------- | -------------------------------------------- |
| OracleAdapterFactory | `0x52b008771fc95b4674a4f3533570b3d223f27f68` | `0x6a1340dd86c3951b6f13d9001b3951d56a2fe581` |
| BatchReader          | `0x2869f0a96572e7fb15036656e9a32fd17346499b` | `0x580dc0ecb226530ee8a90b57e0f0cd2555fb4f0f` |

## 约束

* **没有历史轮次数据** — `getRoundData(roundId)` 会回滚；仅 `latestRoundData()` 支持。
* **不支持 Switchboard EMA** — EMA 数据仅适用于 Pyth。
* **各链的过期阈值** — `maxStaleness` 在启动时按环境设置：直接（读取 Pyth）适配器默认为约 60 秒；缓存型适配器默认为 3600 秒（当 keeper 刷新它们时，刷新频率会远高于此）。
* **解析器偏移** 会根据当前的 Pyth/Switchboard 布局进行验证；布局变更需要重新验证。

## 接下来

* [Oracle Gateway 门户](/zh/rome-shang-de-ying-yong/oracle-gateway.md) — 注册价格源，配置消费者
* [合约地址](/zh/can-kao/contract-addresses.md) — 预编译和每链地址


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.rome.builders/zh/chan-pin/oracle-gateway.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
