Using Data Feeds Onchain (Stellar)

Chainlink Data Feeds are the quickest way to connect your smart contracts to the real-world market prices of assets. This guide demonstrates how to deploy a Soroban contract in Rust to the Stellar Testnet and read a price onchain by calling the Chainlink Data Feeds cache contract. To learn how to read price feed data using offchain applications, see the Using Data Feeds Offchain guide.

To get the full list of available Chainlink Data Feeds on Stellar, see the Price Feed Contract Addresses page with Stellar selected.

Available data feeds on Stellar

The following table shows all available data feeds on Stellar. Each feed is identified by its data_id (the Feed ID), which you pass to the cache contract to read that feed's data.

Networks

Stellar Mainnet

Unlike EVM chains, Stellar uses a single cache contract for all feeds. Instead of a per-feed contract address, you select a feed by its feed ID (data_id) shown below. See the Using Data Feeds on Stellar guide to learn how to read these feeds.

Prerequisites

Before you begin, you should have:

Requirements

To complete this guide, you'll need:

  • Stellar CLI: Install the Stellar CLI. Run stellar --version to verify your installation.

  • Rust toolchain: Install Rust using rustup. Run cargo --version to verify your installation.

  • Testnet XLM: You'll need testnet XLM to deploy your contract. Fund a Stellar Testnet account using the friendbot faucet. Testnet XLM has no real value.

Set up your Stellar testnet account

  1. Create a new directory for your project and navigate to it in your terminal:

    mkdir stellar-data-feeds && cd stellar-data-feeds
    
  2. Generate a keypair for your testnet account:

    stellar keys generate --network testnet my-account
    

    Expect an output similar to the following:

    Secret key already exists for key my-account
    
  3. Fund your account with testnet XLM using the friendbot faucet:

    stellar keys fund my-account --network testnet
    

    Expect an output similar to the following:

    Funded account <YOUR_PUBLIC_KEY> with 10000.0000000 XLM
    

Create the Soroban contract

  1. Initialize a Soroban contract project:

    stellar contract init consumer --name consumer
    

    This creates a consumer directory with a default Soroban contract scaffold.

  2. Open the consumer/contracts/consumer/src/lib.rs file and replace its contents with the following contract. This contract calls the Chainlink Data Feeds cache contract to read the latest price for a given data_id and returns the answer. The cache interface is defined in the chainlink-stellar repository:

    #![no_std]
    use soroban_sdk::{contract, contractimpl, contractclient, contracterror, contracttype, vec, Address, BytesN, Env, I256, Vec};
    
    #[contractclient(name = "DataFeedsCacheClient")]
    pub trait DataFeedsCache {
        fn latest_round(env: Env, data_ids: Vec<BytesN<32>>) -> Result<Vec<Option<RoundData>>, CacheError>;
    }
    
    #[contracttype]
    #[derive(Clone, Debug)]
    pub struct RoundData {
        pub round_id: u64,
        pub answer: I256,
        pub timestamp: u64,
        pub ledger_seq: u32,
        pub primary: bool,
    }
    
    #[contracterror]
    #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
    #[repr(u32)]
    pub enum CacheError {
        MalformedReport = 100,
        UnauthorizedCaller = 101,
        FeedNotConfigured = 102,
        EmptyConfig = 103,
        InvalidAddress = 104,
        InvalidWorkflowName = 105,
        DuplicatePermission = 106,
        InvalidDataId = 107,
        DuplicateFeedConfig = 108,
        FeedFrozen = 109,
        NoFeedState = 110,
    }
    
    #[contract]
    pub struct Consumer;
    
    #[contractimpl]
    impl Consumer {
        /// Read the latest round for a feed and return its answer.
        pub fn read_latest_price(env: Env, cache: Address, data_id: BytesN<32>) -> I256 {
            let client = DataFeedsCacheClient::new(&env, &cache);
            let rounds = client.latest_round(&vec![&env, data_id]);
            let round = rounds.get_unchecked(0).unwrap();
            round.answer
        }
    }
    

    This contract calls the cache's latest_round function with a vector containing a single data_id. The returned RoundData contains the round_id, answer, timestamp, ledger_seq, and primary fields for the feed. The answer is stored at 18 decimal places.

Build and deploy the contract

  1. Build the contract:

    stellar contract build
    

    Expect an output similar to the following:

    Compiling consumer...
    Finished `release` profile [optimized] target(s) in 5.00s
    
  2. Deploy the contract to the Stellar Testnet:

    stellar contract deploy \
    --wasm target/wasm32v1-none/release/consumer.wasm \
    --source my-account \
    --network testnet
    

    Expect an output similar to the following:

    <YOUR_CONTRACT_ADDRESS>
    

    Note the contract address that is printed. You use it to invoke the contract in the next step.

Invoke the contract

  1. Invoke the read_latest_price function on your deployed contract, passing the cache contract address and the data_id for the feed you want to read. The cache contract on Stellar Testnet is CAVLZXJDRGOS6UZ7BHYTYW7STQMZIOCUJIVRMT7JE7T5F6JIA3LPAOVW, and the BTC/USD data_id is 01a0b4d920000332000000000000000000000000000000000000000000000000. You can find the data_id for other assets on the Price Feed Contract Addresses page with Stellar selected.

    Because read_latest_price is a read-only function, use the --send=no flag to simulate the call without submitting a transaction:

    stellar contract invoke \
    --id <YOUR_CONTRACT_ADDRESS> \
    --source my-account \
    --network testnet \
    --send=no \
    -- read_latest_price \
    --cache CAVLZXJDRGOS6UZ7BHYTYW7STQMZIOCUJIVRMT7JE7T5F6JIA3LPAOVW \
    --data_id 01a0b4d920000332000000000000000000000000000000000000000000000000
    

    Expect an output similar to the following:

    "86400288421531180000000"
    

    Where the value is the latest BTC/USD price for the feed, stored at 18 decimal places. For example, 86400288421531180000000 represents a price of 86400.29.

What's next

Get the latest Chainlink content straight to your inbox.