bolt-cw-sdk 1.0.0

SDK for the BOLT protocol, providing utilities for interacting with the BOLT contracts.
Documentation
use crate::oracle::client::OracleClient;
use crate::oracle::error::OracleError;
use cosmwasm_std::{Decimal256, Timestamp};
use serde::{Deserialize, Serialize};

impl OracleClient {
    pub async fn get_price(
        &self,
        base_token: String,
        quote_token: String,
    ) -> Result<Price, OracleError> {
        let query = Query {
            get_price: GetPriceRequest {
                base_asset_symbol: base_token,
                quote_asset_symbol: quote_token,
            },
        };
        let response: GetPriceResponse = self
            .query_contract(self.oracle_contract_address.to_string(), &query, None)
            .await?
            .data;
        response.pair_data.ok_or_else(|| {
            OracleError::PriceNotFound(
                query.get_price.base_asset_symbol,
                query.get_price.quote_asset_symbol,
            )
        })
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Price {
    pub price: Decimal256,
    pub expiry_time: Timestamp,
}

#[derive(Serialize)]
struct Query {
    pub get_price: GetPriceRequest,
}

#[derive(Serialize)]
struct GetPriceRequest {
    pub base_asset_symbol: String,
    pub quote_asset_symbol: String,
}

#[derive(Deserialize)]
struct GetPriceResponse {
    pub pair_data: Option<Price>,
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::oracle::client::OracleAdminClient;

    use crate::test_utils::helpers::{TEST_ASSET_ARCH_SYMBOL, TEST_ASSET_USDT_SYMBOL};
    use crate::test_utils::test_scenario::TestScenario;
    use serial_test::serial;
    use std::ops::Add;
    use std::str::FromStr;
    use std::time::{Duration, SystemTime, UNIX_EPOCH};

    #[tokio::test]
    #[serial]
    async fn test_get_price() {
        let test_scenario = TestScenario::new_from_config("config.json".to_string()).await;

        let price_threshold_ratio = Decimal256::from_str("0.5").unwrap();
        let price_expire_millis = Some(1000);
        let oracle_contract_address = test_scenario
            .instantiate_oracle_contract(price_threshold_ratio, price_expire_millis)
            .await;

        let client = OracleAdminClient::from_scenario(&test_scenario, &oracle_contract_address)
            .expect("Failed to create oracle admin client");
        let price_expiry_time = SystemTime::now().add(Duration::from_secs(7200)); // 2h from now
        let price_expiry_timestamp = price_expiry_time
            .duration_since(UNIX_EPOCH)
            .expect("Converting to timestamp failed");

        let err = client
            .public_oracle_client
            .get_price(
                TEST_ASSET_ARCH_SYMBOL.to_string(),
                TEST_ASSET_USDT_SYMBOL.to_string(),
            )
            .await
            .expect_err("Expected error as we haven't set any asset pairs yet");
        assert_eq!(
            err.to_string(),
            OracleError::PriceNotFound(
                TEST_ASSET_ARCH_SYMBOL.to_string(),
                TEST_ASSET_USDT_SYMBOL.to_string()
            )
            .to_string()
        );

        test_scenario.set_default_assets(&client).await;

        let err = client
            .public_oracle_client
            .get_price(
                TEST_ASSET_ARCH_SYMBOL.to_string(),
                TEST_ASSET_USDT_SYMBOL.to_string(),
            )
            .await
            .expect_err("Expected error as we haven't set any prices yet");
        assert!(matches!(err, OracleError::PriceNotFound(_, _)));

        test_scenario
            .set_default_prices(&client, price_expiry_timestamp, "50000")
            .await;

        let client = OracleClient::new(
            test_scenario.cosmos_client,
            oracle_contract_address
                .parse()
                .expect("Failed to parse account ID"),
        )
        .expect("Failed to create oracle client");
        let result = client
            .get_price(
                TEST_ASSET_ARCH_SYMBOL.to_string(),
                TEST_ASSET_USDT_SYMBOL.to_string(),
            )
            .await;
        assert!(result.is_ok());
        let price = result.unwrap();
        assert_eq!(price.price, Decimal256::from_str("50000").unwrap());
    }
}