> 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/products/oracle-gateway.md).

# Oracle Gateway

Pyth and Switchboard price feeds behind Chainlink's AggregatorV3Interface.

The Oracle Gateway exposes Solana-native price feeds (Pyth, Switchboard V3) through Chainlink's `AggregatorV3Interface`, so Ethereum protocols porting to Rome keep their existing oracle integration code. **Oracle Gateway V2 is deployed on Hadrian and Rubicon**, though the registry lists feed addresses only for Hadrian, and a customer portal handles registration. A Rome-run keeper refreshes the cached and book feeds, but not every feed is fresh at every moment — [which feeds answer today](#which-feeds-answer-today) is spelled out below. The adapters, factory, and keeper model are covered in the [rome-oracle-gateway](https://github.com/rome-protocol/rome-oracle-gateway) repo.

> **Preview.** The Oracle Gateway has not been audited. The portal says the same: do not use it in production value flows.

## The problem

Ethereum DeFi expects Chainlink's `AggregatorV3Interface`:

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

Solana's oracle providers (Pyth, Switchboard) have different data formats. Without adaptation, every ported protocol would need custom oracle code.

## The solution

Oracle Gateway V2 deploys lightweight adapter contracts that:

1. Read price data directly from Pyth or Switchboard accounts on Solana (cross-state reads, not CPI)
2. Parse the on-chain data
3. Normalize prices to 8 decimals
4. Expose the standard Chainlink `AggregatorV3Interface`

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

// Same interface as Chainlink on Ethereum
(, int256 price,,,) = IAggregatorV3Interface(ADAPTER).latestRoundData();
// price = SOL/USD at 8 decimals (e.g. 15000000000 = $150.00)
```

## Consume a feed in three steps

1. **Resolve the adapter address from the registry** — never hardcode it:

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

The registry is installed from GitHub (`npm install github:rome-protocol/rome-registry#v0.2.0`). The feed keys carry a suffix that says which adapter kind sits behind them; see [Feed keys in the registry](#feed-keys-in-the-registry) before you pick one.

2. **Read it** with the interface your protocol already uses — `IAggregatorV3Interface(solUsd).latestRoundData()`. A stale or uninitialized feed **reverts** rather than serving a frozen price.
3. **Several feeds at once?** `BatchReader.getLatestPrices(adapters[])` (Hadrian, registry v0.2.0: `0x2869f0a96572e7fb15036656e9a32fd17346499b`) — and `getFeedHealth(adapters[])` before you depend on them. Cached adapters make each consumer read a plain `SLOAD`, so multi-feed transactions (a multi-collateral borrow, a portfolio view) stay cheap.

[cardo](https://github.com/rome-protocol/cardo) reads these feeds on Hadrian. Because the surface is exactly `AggregatorV3Interface`, Compound- and Aave-class protocols can take these feeds without oracle code changes — point their price-feed configuration at the adapter addresses.

## Adapter types

* **PythPullAdapter** — reads Pyth price accounts (price, confidence, EMA, publish time).
* **SwitchboardV3Adapter** — reads Switchboard aggregator accounts (price, timestamp; no EMA).
* **CachedPyth / CachedFeed adapters** — cached-track variants for CU-efficient reads.
* **BookFeedAdapter** — a read-only Chainlink-compatible view over one entry of a `PriceBook` contract. The book holds the prices for all of its registered feeds and is refreshed in batches (the refresh is permissionless); each feed's adapter keeps no cache of its own. A paused entry reverts straight away with `AdapterPaused`, instead of waiting to go stale.

Adapters are deployed as EIP-1167 minimal-proxy clones: the `OracleAdapterFactory` clones the Pyth, Switchboard and cached adapters, and each `PriceBook` clones its own `BookFeedAdapter` when a feed is registered. They expose an extended interface alongside `AggregatorV3Interface` (`latestPriceData`, `maxStaleness`, `oracleType`, and a `metadata()` surface used for health checks).

## Feed keys in the registry

`getOracle(chainId).feeds` has several keys per asset. The suffix on the key names the adapter kind:

| Key                  | Adapter kind                         | How it reads                                                                              |
| -------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------- |
| `SOL/USD`            | Direct Pyth (`PythPullAdapter`)      | Parses the Pyth account on every call.                                                    |
| `SOL/USD-BOOK`       | `BookFeedAdapter`                    | Reads a `PriceBook` entry that the keeper refreshes.                                      |
| `SOL/USD-CACHED`     | Cached (wraps another feed)          | Reads a stored price that the keeper refreshes.                                           |
| `SOL/USD-CACHEDPYTH` | Cached Pyth (`CachedPythAdapter`)    | Reads a stored price that the keeper refreshes.                                           |
| `SOL/USD-SB`         | Switchboard (`SwitchboardV3Adapter`) | Parses the Switchboard account on every call. The registry marks these entries `retired`. |

Which one to consume depends on the chain. Check the next section first.

### Which feeds answer today

These are results of read-only `latestRoundData()` calls on the feeds listed in registry v0.2.0, made while this page was written. Feed freshness changes from minute to minute on devnet, so treat it as a snapshot and run the same call on the feed you plan to use.

* **Hadrian.** The direct feeds are often stale: in repeated checks most of them (BONK, BTC, ETH, JITOSOL, JTO, JUP, MSOL, USDC, USDT) reverted with `StalePriceFeed` every time, and `SOL/USD` answered on some calls and not others. The `-BOOK` feeds were the most reliable and answered on every call for every asset except JTO, whose `-BOOK` feed reverted as stale. Every `-CACHED` and `-CACHEDPYTH` feed reverted with `UninitializedPriceFeed` (nothing has refreshed them), and `SOL/USD-SB` reverted with `StalePriceFeed`. On Hadrian, prefer the `-BOOK` feed, and run `latestRoundData()` on the feed you choose before you depend on it.
* **Rubicon.** The registry does not list oracle feeds for Rubicon, so this page does not cover them. The factory is deployed there (see [Deployed addresses](#deployed-addresses)) and its first adapters answer, but look up the feed addresses from the factory (`adapterCount()` and `allAdapters(i)`) rather than from the registry.

A revert here is the designed behaviour: a stale or uninitialized feed fails loudly instead of returning an old price. Handle it in your contract.

## Freshness — the oracle-keeper

Adapters enforce a `maxStaleness` window: if `block.timestamp - publishTime > maxStaleness`, reads revert. On Solana mainnet, Pyth runs production keepers. On Solana devnet (where Hadrian lives), Pyth's pushers are best-effort, so Rome runs its own **oracle-keeper** sidecar to keep the underlying Pyth accounts fresh and to refresh the cached and book feeds. The keeper does not cover every feed on every chain at every moment (see [which feeds answer today](#which-feeds-answer-today)).

Check feed health with `BatchReader.getFeedHealth` (which reads each adapter's `metadata()`), not just `latestRoundData` — a feed can be reachable but stale.

## Customer portal

Register feeds and configure consumers per chain through the [Oracle Gateway portal](https://oracle.testnet.romeprotocol.xyz/). See the [portal page](/apps-on-rome/oracle-gateway.md) for what it covers.

## Deployed addresses

Canonical per-chain addresses (factories, adapter implementations, and the live feeds) live in the [registry](https://github.com/rome-protocol/rome-registry/tree/main/chains). Headline factories, from the `live` entries of registry v0.2.0 (version 5.0.0 on Hadrian; the Rubicon addresses are the ones on the [Contract Addresses](/reference/contract-addresses.md) page); see [Reading the contracts list](/reference/contract-addresses.md#reading-the-contracts-list):

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

## Constraints

* **No historical round data** — `getRoundData(roundId)` reverts; only `latestRoundData()` is supported.
* **Switchboard EMA not supported** — EMA data is Pyth-only.
* **Per-chain staleness** — `maxStaleness` is set per environment at bring-up: direct (Pyth-read) adapters default to \~60 seconds; cached adapters default to 3600 seconds (when the keeper is refreshing them, it does so far more often than that).
* **Parser offsets** are validated against current Pyth/Switchboard layouts; layout changes require re-validation.

## What's Next

* [Oracle Gateway portal](/apps-on-rome/oracle-gateway.md) — register feeds, configure consumers
* [Contract Addresses](/reference/contract-addresses.md) — precompiles and per-chain addresses


---

# 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/products/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.
