bolt-cw-sdk 1.0.0

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

use super::client::MarketClient;

impl MarketClient {
    pub async fn quotes_for_lp(
        &self,
        market_address: String,
        lp_address: String,
    ) -> Result<Vec<Coin>, MarketError> {
        let query = Query {
            quotes_for_lp: QuotesForLpRequest { lp_address },
        };
        let response: QuotesForLpResponse = self
            .cosmos_client
            .query_contract(market_address, &query, None)
            .await?
            .data;

        response
            .quotes
            .into_iter()
            .map(|coin| Coin::new(coin.amount.u128(), &coin.denom))
            .collect::<Result<Vec<_>, _>>()
            .map_err(MarketError::EyreError)
    }
}

#[derive(Serialize)]
struct Query {
    pub quotes_for_lp: QuotesForLpRequest,
}
#[derive(Serialize)]
pub struct QuotesForLpRequest {
    lp_address: String,
}

#[derive(Deserialize, Debug)]
pub struct QuotesForLpResponse {
    pub quotes: Vec<cosmwasm_std::Coin>,
}

#[cfg(test)]
mod tests {
    use crate::market::client::{MarketAdminClient, MarketClient};
    use crate::market::error::MarketError;
    use crate::oracle::client::OracleAdminClient;
    use crate::test_utils::helpers::{
        TEST_ASSET_ARCH_SYMBOL, TEST_ASSET_ETH_SYMBOL, TEST_ASSET_USDT_SYMBOL,
    };
    use crate::test_utils::test_scenario::TestScenario;
    use cosmrs::{AccountId, Coin};
    use cosmwasm_std::{Addr, 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_quotes_for_lp() {
        let mut test_scenario = TestScenario::new_from_config("config.json".to_string()).await;

        // Initialize oracle contract
        let price_threshold_ratio = Decimal256::from_str("0.1").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");

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

        // Instantiate market contract
        let new_account = test_scenario.create_new_account().await;
        let protocol_fee = Decimal256::percent(10);
        let lp_fee = Decimal256::percent(10);
        let min_base_out = Uint128::new(10);

        let market_contract_addr = test_scenario
            .instantiate_settlement_contract(
                oracle_contract_address.clone(),
                new_account.account_id.to_string(),
                protocol_fee,
                lp_fee,
                TEST_ASSET_ARCH_SYMBOL.to_owned(),
                vec![
                    TEST_ASSET_USDT_SYMBOL.to_owned(),
                    TEST_ASSET_ETH_SYMBOL.to_owned(),
                ],
                min_base_out,
            )
            .await;

        // Deposit base assets
        let market_client = MarketAdminClient::from_scenario(&test_scenario).unwrap();

        let quotes_err = market_client
            .public_market_client
            .quotes_for_lp(
                oracle_contract_address.clone(),
                new_account.account_id.to_string(),
            )
            .await
            .expect_err("Should fail to get quotes for LP as we have given oracle address instead of market address");
        assert!(matches!(quotes_err, MarketError::CosmosClientError(_)));

        let quotes = market_client
            .public_market_client
            .quotes_for_lp(
                market_contract_addr.clone(),
                new_account.account_id.to_string(),
            )
            .await
            .expect("Failed to get quotes for LP as LP hasnt deposited any assets yet");
        assert_eq!(quotes.len(), 0);

        test_scenario
            .deposit_base(
                &market_client,
                AccountId::from_str(&market_contract_addr).unwrap(),
                &new_account,
                Coin::new(10_000_000, TEST_ASSET_ARCH_SYMBOL).unwrap(),
            )
            .await;

        let quotes = market_client
            .public_market_client
            .quotes_for_lp(
                market_contract_addr.clone(),
                new_account.account_id.to_string(),
            )
            .await
            .expect("Failed to get quotes for LP");

        assert_eq!(quotes.len(), 0);

        // Swap usdt and eth with base
        let transfer_amount = 5000000;
        let minimum_base_out = Some(Uint128::new(10));
        let receiver = Some(Addr::unchecked(new_account.account_id.clone()));
        let market_contract_account_id: AccountId =
            market_contract_addr.to_string().parse().unwrap();

        for asset in [TEST_ASSET_USDT_SYMBOL, TEST_ASSET_ETH_SYMBOL] {
            let coin = Coin::new(transfer_amount, asset).unwrap();
            test_scenario
                .swap(
                    &market_client,
                    market_contract_account_id.clone(),
                    &new_account,
                    coin,
                    minimum_base_out,
                    receiver.clone(),
                )
                .await;
        }

        // Query quotes for lp
        let market_client =
            MarketClient::new(test_scenario.cosmos_client).expect("Failed to create market client");
        let quotes = market_client
            .quotes_for_lp(
                market_contract_addr.clone(),
                new_account.account_id.to_string(),
            )
            .await
            .unwrap();

        let expected = vec![
            Coin::new(4_500_000, TEST_ASSET_USDT_SYMBOL).unwrap(),
            Coin::new(4_500_000, TEST_ASSET_ETH_SYMBOL).unwrap(),
        ];
        assert_eq!(quotes, expected);
    }
}