Using Data Feeds on Stellar
Stellar is a Layer 1 blockchain that uses Soroban smart contracts written in Rust. Chainlink Data Feeds on Stellar are delivered through Chainlink Runtime Environment (CRE), which publishes data onchain using a decentralized oracle network.
Chainlink Data Feeds on Stellar follow the same pattern as Data Feeds on Aptos: a single proxy contract serves every feed, and consumers select a feed by its 32-byte data_id rather than by a per-feed contract address. This contrasts with Chainlink's integration on EVM blockchains, where each price feed has a separate contract address.
How Stellar Data Feeds work
Data Feeds on Stellar use a proxy and cache model:
- Proxy contract: A single proxy contract is the only contract that consumers interact with. The proxy address stays stable across cache replacements, so your application does not need to change when the underlying data is upgraded.
- Cache contract: The proxy delegates reads to a cache contract that the Chainlink oracle network writes into. When the cache is replaced, the proxy continues to point to the new cache, and the proxy address remains unchanged.
Consumers select a feed by passing its 32-byte data_id to the proxy. You can find the data_id for each feed on the Price Feed Contract Addresses page with Stellar selected.
Supported networks
Chainlink Data Feeds are available on the following Stellar networks:
Proxy contract addresses
The proxy contract address is the same for every feed on a given network. You only ever interact with the proxy.
- Stellar Testnet:
<TESTNET_PROXY_ADDRESS> - Stellar Mainnet (Pubnet):
<MAINNET_PROXY_ADDRESS>
Reader interface
Consumers call the following functions on the proxy contract. The interface is defined in the chainlink-stellar repository.
| Function | Returns |
|---|---|
latest_round(data_id, decimals) | The most recent round for that feed, scaled to the requested decimals |
get_round(data_id, round_id, decimals) | A specific historical round, scaled to the requested decimals |
decimals(data_id) | The storage precision of the feed, currently always 18 |
description(data_id) | Human-readable pair name |
Round structure
A round returned by the proxy carries the following fields:
| Field | Description |
|---|---|
round_id | The unique identifier of the round |
answer | The answer for the feed, represented as an I256 |
timestamp | The Unix timestamp in seconds when the round was recorded |
Error handling
When you call the proxy, the contract can return a ProxyReadError. The following cases describe what each error means for a consumer:
| Error | Meaning |
|---|---|
NoDataPresent | No round is available for the requested data_id or round_id. This can happen if the feed has not been written yet or the round has expired. |
InvalidDecimals | The decimals you requested is outside the supported range for the feed. The valid range is [min_decimals(feed), 18]. |
RoundsToZero | A non-zero answer would truncate to zero at the requested decimals. Request fewer decimal places. |
How long a round is available
A round is stored in the cache contract's temporary storage with a retention period of 3,110,400 ledgers. After this period, a round can no longer be read from the proxy.
Getting started
You can read Chainlink Data Feeds on Stellar either onchain or offchain:
- Onchain: Deploy a Soroban contract in Rust that calls the proxy and reads a price, deployed with
stellar-clito the Stellar Testnet. - Offchain: Read a feed via Soroban RPC simulation using
stellar-cliand the Stellar JavaScript SDK.
Answer precision
The cache stores every answer at a fixed 18 decimal places (DECIMALS). Feeds sourced at a lower precision, such as 8 decimals, are scaled up to 18 on write, so the stored value is always 18-decimal regardless of the feed's native precision. As a result, decimals(data_id) on the proxy currently always returns 18; it describes the storage precision, not the originating feed.
Decimal conversion is applied at the proxy level. When you call latest_round or get_round, you pass the precision you want the answer returned at. The valid range is [min_decimals(feed), 18]:
- A feed's minimum precision is owner-configurable and defaults to
18, which locks the feed to full precision unless the owner explicitly opens it up. - Downscaling truncates toward zero, matching the behavior of Solidity integer division that EVM consumers already use.
- A non-zero answer that would truncate to zero at the requested precision fails with a distinct error rather than silently returning zero. An answer that is genuinely zero returns zero at any precision.