bolt-cw-sdk 1.0.0

SDK for the BOLT protocol, providing utilities for interacting with the BOLT contracts.
Documentation
use crate::market::client::MarketClient;
use crate::market::error::MarketError;
use cosmwasm_std::Uint128;
use serde::{Deserialize, Serialize};

impl MarketClient {
    pub async fn base_liquidity(
        &self,
        market_address: String,
    ) -> Result<BaseLiquidityResponse, MarketError> {
        let query = Query {
            base_liquidity: BaseLiquidityRequest {},
        };
        let response: BaseLiquidityResponse = self
            .cosmos_client
            .query_contract(market_address, &query, None)
            .await?
            .data;

        Ok(response)
    }
}

#[derive(Serialize)]
struct Query {
    pub base_liquidity: BaseLiquidityRequest,
}

#[derive(Serialize)]
pub struct BaseLiquidityRequest {}

#[derive(Deserialize, Debug)]
pub struct BaseLiquidityResponse {
    pub base_liquidity: cosmwasm_std::Coin,
    pub total_shares: Uint128,
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::market::client::MarketAdminClient;
    use crate::oracle::client::OracleAdminClient;
    use crate::router::client::RouterAdminClient;
    use crate::test_utils::helpers::{TEST_ASSET_ARCH_SYMBOL, TEST_ASSET_USDT_SYMBOL};
    use crate::test_utils::test_scenario::TestScenario;
    use cosmrs::{AccountId, Coin};
    use cosmwasm_std::{Decimal256, Uint128};
    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_base_liquidity() {
        let mut test_scenario = TestScenario::new_from_config("config.json".to_string()).await;

        // Initialize oracle contract
        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;

        // Initialize router contract
        let default_protocol_fee = Decimal256::from_str("0.1").unwrap();
        let default_lp_fee = Decimal256::from_str("0.1").unwrap();
        let router_contract_address = test_scenario
            .instantiate_router_contract(
                oracle_contract_address.clone(),
                oracle_contract_address.clone(),
                default_protocol_fee,
                default_lp_fee,
            )
            .await;

        // Set default assets and prices
        let oracle_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");
        test_scenario.set_default_assets(&oracle_client).await;
        test_scenario
            .set_default_prices(&oracle_client, price_expiry_timestamp, "50000")
            .await;

        // Create market
        let router_client = RouterAdminClient::from_scenario(
            &test_scenario,
            router_contract_address.parse().unwrap(),
        )
        .expect("Failed to create router admin client");
        test_scenario
            .create_market(
                &router_client,
                TEST_ASSET_ARCH_SYMBOL,
                &[TEST_ASSET_USDT_SYMBOL],
                Uint128::new(10),
            )
            .await;

        // Query the only created market
        let markets = router_client
            .public_router_client
            .markets()
            .await
            .expect("Failed to get markets");
        let market_addr = &markets.first().unwrap().market_address;

        let market_client = MarketAdminClient::from_scenario(&test_scenario).unwrap();

        // Querying an invalid market address should return an error
        let liquidity = market_client
            .public_market_client
            .base_liquidity(oracle_contract_address.to_string())
            .await
            .expect_err("Querying an invalid market address should return an error");
        assert!(matches!(liquidity, MarketError::CosmosClientError(_)));

        // No liquidity should be present initially
        let liquidity = market_client
            .public_market_client
            .base_liquidity(market_addr.to_string())
            .await
            .expect("Failed to get base liquidity");

        assert_eq!(liquidity.base_liquidity.amount.u128(), 0u128);
        assert_eq!(
            liquidity.base_liquidity.denom,
            TEST_ASSET_ARCH_SYMBOL.to_string()
        );
        assert_eq!(liquidity.total_shares.u128(), 0u128);

        // Deposit some liquidity
        let admin_account = test_scenario.admin_account();
        test_scenario
            .deposit_base(
                &market_client,
                AccountId::from_str(market_addr.as_ref()).unwrap(),
                &admin_account,
                Coin::new(42, TEST_ASSET_ARCH_SYMBOL).unwrap(),
            )
            .await;

        // Query base liquidities
        let market_client = MarketClient::new(test_scenario.cosmos_client.clone())
            .expect("Failed to create market client");
        let liquidity = market_client
            .base_liquidity(market_addr.to_string())
            .await
            .expect("Failed to get base liquidity");

        assert_eq!(liquidity.base_liquidity.amount.u128(), 42u128);
        assert_eq!(
            liquidity.base_liquidity.denom,
            TEST_ASSET_ARCH_SYMBOL.to_string()
        );
        assert_eq!(liquidity.total_shares.u128(), 42u128);
    }
}