# Example Crypto Price Feed

## Example Crypto Price Feed

Using Pyth Core or a public API to build and deploy a crypto feed on **SEDA Fast** or **SEDA Core**.

## Contents

* [Requirements](#requirements)
* [Getting Started](#getting-started)
* [Building the Oracle Program](#building-the-oracle-program)
* [Deploying & Querying](#deploying)

***

## Requirements

* **Bun**: Install [Bun](https://bun.sh/) for package management and building.
* **Rust**: Install [Rust](https://rustup.rs/) for development and building.
* **WASM**: Install the [`wasm32-wasip1`](https://doc.rust-lang.org/rustc/platform-support/wasm32-wasip1.html) target with `rustup target add wasm32-wasip1` for WASM compilation.

```rust
rustup target add wasm32-wasip1
```

Alternatively, use the [devcontainer](https://containers.dev/) for a pre-configured environment.

#### Additional Requirement for SEDA Fast

{% hint style="info" icon="magnifying-glass" %}

* **SEDA Fast** requires a FAST API key for querying.

* **SEDA Core** does not require a FAST API key, but instead requires submitting a Data Request onchain from a supported network or on SEDA Chain directly.
  {% endhint %}

* A **SEDA FAST developer key**. Visit the [Developer Page](https://seda.xyz/dev) and access a 7 day trial.

{% hint style="info" %}
If you need more than 7 days for your trial key, please reach out to our team on [Discord](https://discord.com/invite/uBrQJZ2nrB) for an extended free trial.
{% endhint %}

***

## Getting Started&#x20;

The easiest way to start building a SEDA Oracle Program is by using the `seda-starter-kit`. This starter kit offers a streamlined setup process and comes with the essential tools and structure to help you create and deploy your first Oracle program on the SEDA network.

Clone and checkout the repository:

```
git clone https://github.com/sedaprotocol/seda-starter-kit.git simple-price-feed
cd simple-price-feed
```

Install dependencies:

```
bun install
```

Copy and populate the `.env` file's MNEMONIC:

```
cp .env.example .env
```

{% hint style="info" %}
If you need a SEDA mnemonic this can most easily be achieved by downloading [Keplr wallet](https://keplr.app). You can then claim Testnet SEDA tokens at the [SEDA faucet](https://ping-api.testnet.seda.xyz/faucet)&#x20;
{% endhint %}

***

## Building the Oracle Program

The PriceFeed Oracle Program is divided into two phases:

Execution: Fetches the price of an asset pair (e.g., BTC-USDT or a Pyth asset ID) from an external source and processes the data.

Tally: Aggregates execution reports.

> 🔎 Difference:
>
> * **SEDA Fast**: Typically runs a single executor and forwards the result.
> * **SEDA Core**: Multiple Overlay Nodes execute the program, and the tally phase aggregates (e.g., median) results.

***

### Step 1: Entrypoint

The Oracle Program logic lives in the `src` folder, where `main.rs` serves as the entry point.

`src/main.rs`

```
use execution_phase::execution_phase;
use seda_sdk_rs::oracle_program;
use tally_phase::tally_phase;

mod execution_phase;
mod tally_phase;
mod fetch_pyth; // Required for Pyth-based Fast example

#[oracle_program]
impl PriceFeed {
    fn execute() {
        execution_phase().unwrap();
    }

    fn tally() {
        tally_phase().unwrap();
    }
}
```

This setup initializes the PriceFeed Oracle program and links the execution and tally phases that make up the data request.

***

### Step 2: Execution Phase

The execution phase is where the Oracle Program fetches data and performs computation on the fetched data.

### Pyth Core via SEDA Data Proxy

Start by defining inputs and outputs:

```rust
#[derive(Serialize, Deserialize)]
struct PriceFeedInput {
    base_asset_id: String,
}

#[derive(Serialize, Deserialize)]
pub struct PriceFeedResponse {
    price: String,
    price_timestamp: i64,
}
```

Parse input:

```rust
let price_feed_input = serde_json::from_slice::<PriceFeedInput>(&Process::get_inputs())?;
let base_asset_id = price_feed_input.base_asset_id;
```

#### Create `src/fetch_pyth.rs`

```rust
use seda_sdk_rs::{HttpFetchOptions, proxy_http_fetch};
use serde::Deserialize;

const TESTNET_PROXY_PUBLIC_KEY: &str =
    "033ed60cfdeb7e91f718bf28e514e5c2f2990400b4643e86856e530de9b46dfb47";
const TESTNET_PROXY_URL: &str = "http://pyth.proxy.testnet.seda.xyz/proxy/";
```

Define response structures and fetch logic:

```rust
pub fn fetch_hermes_pyth_price(ids: Vec<String>) -> Result<PythPriceData, serde_json::Error> {
    let ids_str = ids.join("&ids[]=");

    let response = proxy_http_fetch(
        [TESTNET_PROXY_URL, "price/latest?ids[]=", ids_str.as_str()].concat(),
        Some(TESTNET_PROXY_PUBLIC_KEY.to_string()),
        Some(HttpFetchOptions {
            method: seda_sdk_rs::HttpFetchMethod::Get,
            headers: Default::default(),
            body: None,
            timeout_ms: Some(2_000),
        }),
    );

    let parsed_pyth_response = serde_json::from_slice::<PythPriceResponse>(&response.bytes)?;

    let price_data =
        parsed_pyth_response
        .parsed
        .first()
        .expect("No price data found")
        .price.clone();

    Ok(price_data)
}
```

Update execution phase:

```rust
let price_response = fetch_hermes_pyth_price(vec![base_asset_id])?;

let response_placeholder = PriceFeedResponse {
    price: price_response.price.to_string(),
    price_timestamp: price_response.publish_time,
};

Process::success(serde_json::to_vec(&response_placeholder)?.as_slice());
```

{% hint style="info" %}

* This example uses `proxy_http_fetch` and a signed SEDA Data Proxy.
* Suitable for both Fast and Core.
* In **Fast**, the result is typically forwarded directly.
* In **Core**, results are revealed and aggregated in tally phase.
  {% endhint %}

***

### Calling a Public API via http\_fetch

```rust
let response = http_fetch(
    format!(
        "https://api.binance.com/api/v3/ticker/price?symbol={}{}",
        symbol_a.to_uppercase(),
        symbol_b.to_uppercase()
    ),
    None,
);
```

Check for errors:

```rust
if !response.is_ok() {
    elog!(
        "HTTP Response was rejected: {} - {}",
        response.status,
        String::from_utf8(response.bytes)?
    );

    Process::error("Error while fetching price feed".as_bytes());
    return Ok(());
}
```

Parse and compute:

```rust
let data = serde_json::from_slice::<PriceFeedResponse>(&response.bytes)?;
let price: f32 = data.price.parse()?;
let result = (price * 1000000f32) as u128;

Process::success(&result.to_le_bytes());
```

***

### Step 3: Tally Phase

***

### SEDA Fast Tally Phase (Single Executor Forwarding)

```
let reveals = get_reveals()?;

if reveals.is_empty() {
    Process::error("No consensus among revealed results".as_bytes());
}

let latest_response: PriceFeedResponse = reveals
    .into_iter()
    .map(|reveal| serde_json::from_slice(&reveal.body.reveal))
    .next()
    .ok_or_else(|| anyhow::anyhow!("No reveals to process"))??;

Process::success(&serde_json::to_vec(&latest_response)?)
```

{% hint style="info" %}

* SEDA Fast only has one executor.
* The first reveal is used and forwarded.
  {% endhint %}

***

### SEDA Core Tally Phase (Median Aggregation)

```
let reveals = get_reveals()?;
let mut prices: Vec<u128> = Vec::new();

for reveal in reveals {
    let price_bytes_slice: [u8; 16] = match reveal.body.reveal.try_into() {
        Ok(value) => value,
        Err(_err) => {
            elog!("Reveal body could not be casted to u128");
            continue;
        }
    };

    let price = u128::from_le_bytes(price_bytes_slice);
    prices.push(price);
}

if prices.is_empty() {
    Process::error("No consensus among revealed results".as_bytes());
    return Ok(());
}

let final_price = median(prices);

Process::success(&final_price.to_be_bytes());
```

{% hint style="info" %}
SEDA Core Behavior:

* Multiple Overlay Nodes execute.
* Tally phase aggregates (median).
* Big-endian encoding for EVM compatibility.
  {% endhint %}

***

## Building the Oracle Program

To build:

```
bun run build
```

***

## Deploying

### SEDA Fast Deployment

```
bun run deploy
```

Query:

```
curl -L -X POST 'https://fast-api.testnet.seda.xyz/execute?encoding=json&includeDebugInfo=true' \
-H 'Authorization: Bearer {{BEARER_TOKEN}}' \
-H 'Content-Type: application/json' \
--data-raw '{
    "execProgramId": "8cf7808cdb5d16e9fb328007968b31727f8e67ac3c83b655aa61c4ccd4077125",
    "execInputs": {
        "pyth_assets": [
            "0x61c4ca5b9731a79e285a01e24432d57d89f0ecdd4cd7828196ca8992d5eafef6",
            "0xb1073854ed24cbc755dc527418f52b7d271f6cc967bbf8d8129112b18860a593"
        ]
    }
}'
```

***

### SEDA Core Deployment

After building, you'll have the `.wasm` artifacts in the `build` directory.

These artifacts can be deployed to the SEDA network and referenced in onchain Data Requests from supported networks.

Differences between SEDA Core and Fast:

| Component             | SEDA Fast              | SEDA Core                          |
| --------------------- | ---------------------- | ---------------------------------- |
| Execution Replication | Single executor        | Multiple Overlay Nodes             |
| Tally Behavior        | Forward first reveal   | Aggregated consensus (e.g. median) |
| Access Method         | Authenticated REST API | Onchain Data Request               |
| Encoding              | JSON common            | Big-endian for EVM                 |

The Oracle Program logic remains portable between both delivery methods.


---

# 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.seda.xyz/home/for-developers/build-or-access-oracle-programs/example-crypto-price-feed.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.
