# 预言机网关

Oracle Gateway 通过 Chainlink 的方式公开 Solana 原生价格馈送（Pyth Network、Switchboard V3） `AggregatorV3Interface`. 迁移到 Rome 的以太坊协议可以无需改动地使用其现有的预言机集成代码。

## 问题

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

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

Solana 具有不同的预言机提供方（Pyth、Switchboard）和不同的数据格式。若不加适配，每个以太坊协议都需要定制预言机集成。

## 解决方案

Oracle Gateway V2 部署轻量级适配器合约，它们：

1. 通过 CPI 从 Solana 上的 Pyth 或 Switchboard 账户读取价格数据
2. 解析链上 Borsh 编码数据
3. 将价格统一为 8 位小数
4. 公开标准的 Chainlink `AggregatorV3Interface`

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

// 与以太坊上的 Chainlink 接口相同
IAggregatorV3 priceFeed = IAggregatorV3(ORACLE_ADAPTER_ADDRESS);
(, int256 price,,,) = priceFeed.latestRoundData();
// 价格 = SOL/USD，保留 8 位小数（例如，15000000000 = $150.00）
```

## 架构

### OracleAdapterFactory

部署并管理预言机适配器：

```solidity
OracleAdapterFactory factory = OracleAdapterFactory(0xa4647955a16b72d15f13b51b5277036755d297be);

// 部署一个 Pyth 价格馈送适配器
address adapter = factory.createPythFeed(
    pythAccountPubkey,    // Solana 上的 Pyth 价格账户
    "SOL/USD",            // 描述
    60                    // 最大陈旧时间（秒）
);

// 部署一个 Switchboard 价格馈送适配器
address adapter = factory.createSwitchboardFeed(
    sbAccountPubkey,      // Solana 上的 Switchboard 聚合器
    "SOL/USD",
    60
);
```

工厂在部署前会验证该账户确实由 Pyth/Switchboard 程序拥有。

### 适配器类型

**PythPullAdapter** — 读取 Pyth `PriceUpdateV2` 账户。支持价格、置信区间、EMA 价格和发布时间。

**SwitchboardV3Adapter** — 读取 Switchboard `AggregatorAccountData` 账户。支持价格和时间戳。不支持 EMA。

两个适配器都使用 **EIP-1167 最小代理克隆** 以实现更节省 gas 的部署。

## 接口

### 标准 Chainlink 接口

```solidity
interface IAggregatorV3Interface {
    function decimals() external view returns (uint8);          // 始终为 8
    function description() external view returns (string memory);
    function version() external view returns (uint256);
    function latestRoundData() external view returns (
        uint80 roundId,
        int256 answer,        // 归一化到 8 位小数的价格
        uint256 startedAt,
        uint256 updatedAt,
        uint80 answeredInRound
    );
}
```

### 扩展接口

```solidity
interface IExtendedOracleAdapter {
    function latestPriceData() external view returns (
        int64 price, uint64 conf, int32 expo, uint64 publishTime
    );
    function latestEMAData() external view returns (        // 仅限 Pyth
        int64 emaPrice, uint64 emaConf, int32 expo, uint64 publishTime
    );
    function priceStatus() external view returns (uint8);   // 0=交易中，1=陈旧，2=已暂停
    function maxStaleness() external view returns (uint256);
    function oracleType() external view returns (uint8);    // 0=PythPull, 1=Switchboard
}
```

### 批量读取器

一次调用读取多个馈送：

```solidity
BatchReader reader = BatchReader(0x70da375e5680f84032f5b15d35ba0e6f9871d3fd);

address[] memory adapters = new address[](3);
adapters[0] = solUsdAdapter;
adapters[1] = btcUsdAdapter;
adapters[2] = ethUsdAdapter;

// 返回 (adapter, answer, updatedAt, success) 数组
// 单个陈旧馈送不会导致整个批次回滚
reader.getLatestPrices(adapters);
```

## 陈旧性保护

适配器会强制执行一个 `maxStaleness` 参数。若 `block.timestamp - publishTime > maxStaleness`，调用将回滚并抛出 `StalePriceFeed()`。默认值：60 秒。

工厂所有者可按每个适配器或全局调整陈旧时间：

```solidity
factory.setDefaultMaxStaleness(120); // 2 分钟
```

## 已部署地址（Devnet）

| 合约                       | 地址                                           |
| ------------------------ | -------------------------------------------- |
| OracleAdapterFactory     | `0xa4647955a16b72d15f13b51b5277036755d297be` |
| PythPullAdapter（实现）      | `0x4fd11aed44ee5f71df22fb804cfcbb4c50535db9` |
| SwitchboardV3Adapter（实现） | `0xb57e3589b880aa3f6b66ce2df6aa42cd9c36925e` |
| BatchReader              | `0x70da375e5680f84032f5b15d35ba0e6f9871d3fd` |
| SOL/USD（Switchboard）     | `0xF0864572019c295407CF2ed46e6FD3615e10E19d` |

查看 [合约地址](/zh/can-kao/contract-addresses.md) 以获取完整列表。

## 约束

* **不支持历史轮次数据** — `getRoundData(roundId)` 会回滚并抛出 `HistoricalRoundsNotSupported()`
* **不支持 Switchboard EMA** — `latestEMAData()` 在 Switchboard 适配器上会回滚
* **解析器偏移量已通过实证验证** — 在针对新的 Pyth/Switchboard 版本重新部署前，必须先使用验证脚本重新验证
* **价格归一化** — Pyth 价格归一化为 `price * 10^(expo - (-8))`；Switchboard 归一化为 `(mantissa * 10^8) / 10^scale`

## 状态

**V1 已发布** （2026-04-01）— V2 正在推进中，包含陈旧性保护、批量读取、Switchboard 适配器、EIP-1167 克隆。

## 下一步

* [合约地址](/zh/can-kao/contract-addresses.md) — 所有已部署的预言机地址
* [部署 Solidity](/zh/kai-fa-zhe-zhi-nan/deploy-solidity.md) — 部署使用预言机馈送的合约


---

# Agent Instructions: Querying This Documentation

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

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

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

The question should be specific, self-contained, and written in natural language.
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.
