### Domains

| Description | Testnet URL | Mainnet URL |

| --------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------- |

| REST Endpoint to query specific reports | [https://live-api-test.apro.com](http://live-api-test.apro.com) | [https://live-api.apro.com](http://live-api.apro.com) |

### Program IDs

| Description | Devnet | Mainnet |

| ----------------- | -------------------------------------------- | -------------------------------------------- |

| Apro\_SVM\_Oracle | 4Mvy4RKRyJMf4PHavvGUuTj9agoddUZ9atQoFma1tyMY | 4Mvy4RKRyJMf4PHavvGUuTj9agoddUZ9atQoFma1tyMY |

| Sample\_Client | HUJ8ouH6fVonhF1hPV6ENoLid5nbHfyZSpvfujw6X6Hm | HUJ8ouH6fVonhF1hPV6ENoLid5nbHfyZSpvfujw6X6Hm |

### Authentication

#### Headers

All routes require the following two headers for user authentication:

| Header | Description |

| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

| Authorization | The user’s unique identifier, provided as a UUID (Universally Unique IDentifier). |

| X-Authorization-Timestamp | The current timestamp, with precision up to milliseconds. The timestamp must closely synchronize with the server time, allowing a maximum discrepancy of 5 seconds (by default). For a value of 10 digits, it is multiplied directly by 1000 to milliseconds. |

To get authorization, please contact our BD Team:

* Email: <bd@apro.com>

* Telegram:[ Head of Business Development](https://t.me/Annie_LLLEEE)

### API endpoints

1. #### Return a single report at a given timestamp

Endpoint

> /api/svmbnb/reports

| Type | Description | Parameter(s) |

| -------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

| HTTP GET | Returns a single report for a given timestamp. | <ul><li><strong><code>feedID</code></strong>: A Data Streams feed ID.</li><li><strong><code>timestamp</code></strong>: The Unix timestamp for the report.</li></ul> |

Sample request

> GET /api/svmbnb/reports?feedID=\<feedID>\&timestamp=\<timestamp>

Sample response

<figure><img src="https://227137242-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUIpyIkxGAPXFuivFcvwB%2Fuploads%2F82jOz5vLcbC7GXfB9DZB%2Fimage.png?alt=media&#x26;token=554c2d2a-9abe-48ff-8578-ed0c5f6f3a4b" alt=""><figcaption></figcaption></figure>

2. #### Return a single report with the latest timestamp

Endpoint

> /api/svmbnb/reports/latest

| Type | Parameter(s) |

| -------- | ------------------------------------- |

| HTTP GET | `feedID`: A Data Streams feed ID. |

Sample request

> GET /api/svmbnb/reports/latest?feedID=\<feedID>

Sample response

<figure><img src="https://227137242-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUIpyIkxGAPXFuivFcvwB%2Fuploads%2Fhw8SQ3na2ec6Vzuss0t8%2Fimage.png?alt=media&#x26;token=e9f70fd5-d6d2-4001-b2cd-ea955e4b9391" alt=""><figcaption></figcaption></figure>

3. #### Return a report for multiple FeedIDs at a given timestamp

Endpoint

> /api/svmbnb/reports/bulk

| Type | Description | Parameter(s) |

| -------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

| HTTP GET | Return a report for multiple FeedIDs at a given timestamp. | <ul><li><strong><code>feedIDs</code></strong>: A comma-separated list of Data Streams feed IDs.</li><li><strong><code>timestamp</code></strong>: The Unix timestamp for the first report or the string 'latest' for getting the latest report.</li></ul> |

Sample request

> GET /api/svmbnb/reports/bulk?feedIDs=\<FeedID1>,\<FeedID2>,...\&timestamp=\<timestamp>

>

> GET /api/svmbnb/reports/bulk?feedIDs=\<FeedID1>,\<FeedID2>,...\&timestamp=latest

Sample response

<figure><img src="https://227137242-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUIpyIkxGAPXFuivFcvwB%2Fuploads%2F7G4o7z1f7SBUHLuqdQuR%2Fimage.png?alt=media&#x26;token=bfe0daf8-842d-4b39-b7ad-8e2eee583b25" alt=""><figcaption></figcaption></figure>

<figure><img src="https://227137242-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUIpyIkxGAPXFuivFcvwB%2Fuploads%2FVGqh1dZ0Brvy32hfvAez%2Fimage.png?alt=media&#x26;token=d02c1afd-03ff-4e15-802f-3408b13413dd" alt=""><figcaption></figcaption></figure>

4. #### Return multiple sequential reports for a single FeedID, starting at a given timestamp

Endpoint

> /api/svmbnb/reports/page

| Type | Description | Parameter(s) |

| -------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

| HTTP GET | Return multiple sequential reports for a single FeedID, starting at a given timestamp. | <ul><li><strong><code>feedID</code></strong>: A Data Streams feed ID.</li><li><strong><code>startTimestamp</code></strong>: The Unix timestamp for the first report.</li><li><strong><code>limit</code></strong> (optional): The number of reports to return.</li></ul> |

Sample request

> GET /api/svmbnb/reports/page?feedID=\<FeedID>\&startTimestamp=\<StartTimestamp>\&limit=\<Limit>

Sample response

<figure><img src="https://227137242-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUIpyIkxGAPXFuivFcvwB%2Fuploads%2Fa8rLwxM03aTkgPa4ZZLN%2Fimage.png?alt=media&#x26;token=6ae690ba-0cbc-4f04-a77f-39820b9fdc2e" alt=""><figcaption></figcaption></figure>

5. #### WebSocket Connection

Establish a streaming WebSocket connection that sends reports for the given feedID(s) after they are verified.

Endpoint

> /api/svmbnb/ws

| Type | Parameter(s) |

| --------- | --------------------------------------------------------------- |

| WebSocket | `feedIDs`: A comma-separated list of Data Streams feed IDs. |

Sample request

> GET /api/svmbnb/ws?feedIDs=\<feedID1>,\<feedID2>,...

Sample response

<figure><img src="https://227137242-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUIpyIkxGAPXFuivFcvwB%2Fuploads%2FxCN973z1S4JSGn2jhcAg%2Fimage.png?alt=media&#x26;token=1ce860c4-d917-4a80-99f7-5b39a497f4f3" alt=""><figcaption></figcaption></figure>

### Price Feed ID

Devnet:

<table><thead><tr><th width="188">Feed</th><th>Information</th></tr></thead><tbody><tr><td><strong>ETH/USD</strong></td><td><ul><li>I<strong>D:</strong> 0x0003555ace6b39aae1b894097d0a9fc17f504c62fea598fa206cc6f5088e6e45</li><li><strong>Decimals:</strong> 18</li><li><strong>oracle_state_ID:</strong> 2</li></ul></td></tr><tr><td><strong>SOL/USD</strong></td><td><ul><li><strong>ID:</strong> 0x000343ec7f6691d6bf679978bab5c093fa45ee74c0baac6cc75649dc59cc21d3</li><li><strong>Decimals:</strong> 18</li><li><strong>oracle_state_ID:</strong> 4</li></ul></td></tr><tr><td><strong>USDC/USD</strong></td><td><ul><li><strong>ID:</strong> 0x00034b881a0c0fff844177f881a313ff894bfc6093d33b5514e34d7faa41b7ef</li><li><strong>Decimals:</strong> 18</li><li><strong>oracle_state_ID:</strong> 6</li></ul></td></tr><tr><td><strong>BNB/USD</strong></td><td><ul><li><strong>ID:</strong> 0x00039630e8b9343f7fccdecf67bf1a59fe8d2b58e4e8d3246a74f5cd6d6fed5d</li><li><strong>Decimals:</strong> 18</li><li><strong>oracle_state_ID:</strong> 8</li></ul></td></tr></tbody></table>

Mainnet:

<table><thead><tr><th width="188">Feed</th><th>Information</th></tr></thead><tbody><tr><td><strong>ETH/USD</strong></td><td><ul><li>I<strong>D:</strong> 0x0003555ace6b39aae1b894097d0a9fc17f504c62fea598fa206cc6f5088e6e45</li><li><strong>Decimals:</strong> 18</li><li><strong>oracle_state_ID:</strong> 2</li></ul></td></tr><tr><td><strong>USDC/USD</strong></td><td><ul><li><strong>ID:</strong> 0x00034b881a0c0fff844177f881a313ff894bfc6093d33b5514e34d7faa41b7ef</li><li><strong>Decimals:</strong> 18</li><li><strong>oracle_state_ID:</strong> 5</li></ul></td></tr><tr><td><strong>BNB/USD</strong></td><td><ul><li><strong>ID:</strong> 0x00039630e8b9343f7fccdecf67bf1a59fe8d2b58e4e8d3246a74f5cd6d6fed5d</li><li><strong>Decimals:</strong> 18</li><li><strong>oracle_state_ID:</strong> 6</li></ul></td></tr><tr><td><strong>SOL/USD</strong></td><td><ul><li><strong>ID:</strong> 0x000343ec7f6691d6bf679978bab5c093fa45ee74c0baac6cc75649dc59cc21d3</li><li><strong>Decimals:</strong> 18</li><li><strong>oracle_state_ID:</strong> 7</li></ul></td></tr></tbody></table>

### Error response codes

| Status Code | Description |

| -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

| 400 Bad Request | <p>This error is triggered when:</p><ul><li>There is any missing/malformed query argument.</li><li>Required headers are missing or provided with incorrect values.</li></ul> |

| 401 Unauthorized User | <p>This error is triggered when:</p><ul><li>Authentication fails, typically because the HMAC signature provided by the client doesn't match the one expected by the server.</li><li>A user requests access to a feed without the appropriate permission or that does not exist.</li></ul> |

| 500 Internal Server | Indicates an unexpected condition encountered by the server, preventing it from fulfilling the request. This error typically points to issues on the server side. |

| 206 Missing data (**`/bulk`** endpoint only) | Indicates that at least one feed ID data is missing from the report. E.g., you requested a report for feed IDs `<feedID1>`, `<feedID2>`, and `<feedID3>` at a given timestamp. If data for `<feedID2>` is missing from the report (not available yet at the specified timestamp), you get `[<feedID1 data>, <feedID3 data>]` and a 206 response. |

### Sample Integration Program

#### oracle\_sdk:

```rust

use anchor_lang::prelude::*;

use anchor_lang::solana_program::hash::hash;

use anchor_lang::solana_program::instruction::Instruction;

use anchor_lang::solana_program::program::invoke;

pub const APRO_SVM_PROGRAM_ID: &str = "4Mvy4RKRyJMf4PHavvGUuTj9agoddUZ9atQoFma1tyMY";

pub fn load_price_feed_from_account_info(price_account_info: &AccountInfo) -> Result<PriceFeed> {

let data = price_account_info.try_borrow_data()?;

let mut price_feed_data = &data[8..];

let price_feed = PriceFeed::deserialize(&mut price_feed_data)?;

Ok(price_feed)

}

pub fn update_price<'info>(

oracle_state: &AccountInfo<'info>,

price_feed: &AccountInfo<'info>,

payer: &AccountInfo<'info>,

admin: &AccountInfo<'info>,

system_program: &AccountInfo<'info>,

oracle_program: &AccountInfo<'info>,

feed_id: [u8; 32],

valid_time_stamp: u128,

observe_time_stamp: u128,

native_fee: u128,

apro_token_fee: u128,

expire_at: u128,

benchmark_price: u128,

ask_price: u128,

bid_price: u128,

config_digest: [u8; 32],

epoch_and_round: u128,

extra_hash: [u8; 32],

signatures: Vec<[u8; 64]>,

recovery_ids: Vec<u8>,

) -> Result<()> {

let ix = Instruction {

program_id: *oracle_program.key,

accounts: vec![

AccountMeta::new_readonly(*oracle_state.key, false),

AccountMeta::new(*price_feed.key, false),

AccountMeta::new(*payer.key, true),

AccountMeta::new(*admin.key, false),

AccountMeta::new_readonly(*system_program.key, false),

],

data: UpdatePriceArgs {

feed_id,

valid_time_stamp,

observe_time_stamp,

native_fee,

apro_token_fee,

expire_at,

benchmark_price,

ask_price,

bid_price,

config_digest,

epoch_and_round,

extra_hash,

signatures,

recovery_ids,

}

.data(),

};

invoke(

&ix,

&[

oracle_state.clone(),

price_feed.clone(),

payer.clone(),

admin.clone(),

system_program.clone(),

oracle_program.clone(),

],

)?;

Ok(())

}

#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)]

pub struct PriceFeed {

pub feed_id: [u8; 32],

pub valid_time_stamp: u128,

pub observe_time_stamp: u128,

pub native_fee: u128,

pub apro_token_fee: u128,

pub expire_at: u128,

pub benchmark_price: u128,

pub ask_price: u128,

pub bid_price: u128,

pub config_digest: [u8; 32],

pub epoch_and_round: u128,

pub extra_hash: [u8; 32],

}

#[derive(AnchorSerialize, AnchorDeserialize)]

struct UpdatePriceArgs {

feed_id: [u8; 32],

valid_time_stamp: u128,

observe_time_stamp: u128,

native_fee: u128,

apro_token_fee: u128,

expire_at: u128,

benchmark_price: u128,

ask_price: u128,

bid_price: u128,

config_digest: [u8; 32],

epoch_and_round: u128,

extra_hash: [u8; 32],

signatures: Vec<[u8; 64]>,

recovery_ids: Vec<u8>,

}

impl UpdatePriceArgs {

fn data(&self) -> Vec<u8> {

let mut data = Vec::new();

let preimage = "global:update_price".as_bytes();

let hash = hash(preimage);

let discriminator = &hash.to_bytes()[..8];

data.extend_from_slice(discriminator);

data.extend_from_slice(&AnchorSerialize::try_to_vec(self).unwrap());

data

}

}

```

#### Clients code:

```rust

use anchor_lang::prelude::*;

use oracle_sdk::{load_price_feed_from_account_info, update_price};

declare_id!("GzWu85MdbZtVBDcC4Xrmp1dWix7Qo52nUAkD2FjraJ6f");//modify by solana address -k

#[program]

pub mod oracle_client_example {

use super::*;

pub fn fetch_price(ctx: Context<FetchPrice>) -> Result<()> {

//fetch price feed from oracle

let price_feed = load_price_feed_from_account_info(&ctx.accounts.price_account)?;

msg!("Price Feed Details:");

msg!("Feed ID: {:?}", price_feed.feed_id);

msg!("Valid Timestamp: {}", price_feed.valid_time_stamp);

msg!("Observe Timestamp: {}", price_feed.observe_time_stamp);

msg!("Benchmark Price: {}", price_feed.benchmark_price);

msg!("Ask Price: {}", price_feed.ask_price);

msg!("Bid Price: {}", price_feed.bid_price);

let current_timestamp = Clock::get()?.unix_timestamp as u128;

let staleness_threshold = 3600u128;

//store the price in the price_result account

let price_result = if current_timestamp - price_feed.valid_time_stamp <= staleness_threshold

{

PriceResult {

price: price_feed.benchmark_price,

is_valid: true,

}

} else {

PriceResult {

price: 0,

is_valid: false,

}

};

ctx.accounts.price_result.set_inner(price_result);

Ok(())

}

pub fn update_oracle_price(

ctx: Context<UpdateOraclePrice>,

feed_id: [u8; 32],

valid_time_stamp: u128,

observe_time_stamp: u128,

native_fee: u128,

apro_token_fee: u128,

expire_at: u128,

benchmark_price: u128,

ask_price: u128,

bid_price: u128,

config_digest: [u8; 32],

epoch_and_round: u128,

extra_hash: [u8; 32],

signatures: Vec<[u8; 64]>,

recovery_ids: Vec<u8>,

) -> Result<()> {

//update the price feed through CPI

update_price(

&ctx.accounts.oracle_state.to_account_info(),

&ctx.accounts.price_feed.to_account_info(),

&ctx.accounts.payer.to_account_info(),

&ctx.accounts.admin.to_account_info(),

&ctx.accounts.system_program.to_account_info(),

&ctx.accounts.oracle_program.to_account_info(),

feed_id,

valid_time_stamp,

observe_time_stamp,

native_fee,

apro_token_fee,

expire_at,

benchmark_price,

ask_price,

bid_price,

config_digest,

epoch_and_round,

extra_hash,

signatures,

recovery_ids,

)?;

Ok(())

}

}

#[derive(Accounts)]

pub struct FetchPrice<'info> {

#[account(mut)]

pub payer: Signer<'info>,

/// CHECK: This account is owned by the Oracle program

pub price_account: UncheckedAccount<'info>,

#[account(

init_if_needed,

payer = payer,

space = 8 + 16 + 1, // discriminator + price (u128) + is_valid

seeds = [b"price_result", price_account.key().as_ref()],

bump

)]

pub price_result: Account<'info, PriceResult>,

pub system_program: Program<'info, System>,

}

#[derive(Accounts)]

pub struct UpdateOraclePrice<'info> {

/// CHECK: This account is verified in the update_price function

pub oracle_state: UncheckedAccount<'info>,

/// CHECK: This account is verified in the update_price function

#[account(mut)]

pub price_feed: UncheckedAccount<'info>,

#[account(mut)]

pub payer: Signer<'info>,

/// CHECK: This account is verified in the update_price function

#[account(mut)]

pub admin: UncheckedAccount<'info>,

pub system_program: Program<'info, System>,

/// CHECK: This account is verified in the update_price function

pub oracle_program: UncheckedAccount<'info>,

}

#[account]

pub struct PriceResult {

pub price: u128,

pub is_valid: bool,

}

#[error_code]

pub enum ErrorCode {

#[msg("Invalid account data length")]

InvalidAccountDataLength,

}

```

<br>

#USNonFarmPayrollReport #CPIWatch