bolt-cw-sdk 1.0.0

SDK for the BOLT protocol, providing utilities for interacting with the BOLT contracts.
Documentation
use crate::market::Allowance;
use crate::tx_builder::TxBuilder;
use cosmrs::cosmwasm::MsgExecuteContract;
use cosmrs::tx::Msg;
use cosmwasm_std::{Addr, Decimal256, Uint128};
use serde::Serialize;

use super::client::RouterAdminClient;
use super::error::RouterError;

impl RouterAdminClient {
    pub fn append_update_market_config(
        &self,
        tx_builder: &mut TxBuilder,
        config: UpdateMarketConfig,
    ) -> Result<(), RouterError> {
        let msg = ExecuteMsg {
            update_market_config: UpdateMarketConfigMsg {
                base_asset: config.base_asset,
                price_oracle_contract: config.price_oracle_contract,
                protocol_fee_recipient: config.protocol_fee_recipient,
                protocol_fee: config.protocol_fee,
                lp_fee: config.lp_fee,
                allowance: config.allowance,
                min_base_out: None, // will update next PR
            },
        };
        let contract_msg = serde_json::to_vec(&msg).map_err(RouterError::SerdeJsonError)?;
        let msg = MsgExecuteContract {
            sender: tx_builder.account_id.clone(),
            contract: self.public_router_client.router_contract_address.clone(),
            msg: contract_msg,
            funds: vec![],
        };
        let msg = msg.to_any().map_err(RouterError::EyreError)?;
        tx_builder.add_msg(msg);
        Ok(())
    }
}

#[derive(Serialize)]
struct UpdateMarketConfigMsg {
    pub base_asset: String,
    pub price_oracle_contract: Option<Addr>,
    pub protocol_fee_recipient: Option<Addr>,
    pub protocol_fee: Option<Decimal256>,
    pub lp_fee: Option<Decimal256>,
    pub allowance: Option<Allowance>,
    /// The minimum amount of base asset that can be swapped from the pool e.g 1aconst
    pub min_base_out: Option<Uint128>,
}

#[derive(Serialize)]
struct ExecuteMsg {
    pub update_market_config: UpdateMarketConfigMsg,
}

pub struct UpdateMarketConfig {
    pub base_asset: String,
    pub price_oracle_contract: Option<Addr>,
    pub protocol_fee_recipient: Option<Addr>,
    pub protocol_fee: Option<Decimal256>,
    pub lp_fee: Option<Decimal256>,
    pub allowance: Option<Allowance>,
}

#[cfg(test)]
mod tests {

    use crate::market::Allowance;
    use crate::oracle::client::OracleAdminClient;
    use crate::router::client::RouterAdminClient;
    use crate::router::update_market_config::UpdateMarketConfig;
    use crate::test_utils::helpers::{
        assert_event_attribute, TEST_ASSET_ARCH_SYMBOL, TEST_ASSET_USDT_SYMBOL,
    };
    use crate::test_utils::test_scenario::TestScenario;
    use crate::tx_builder::TxBuilder;
    use cosmrs::tendermint::abci::Code;

    use cosmwasm_std::{Addr, Decimal256};
    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_append_update_market_config() {
        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).unwrap();
        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).unwrap();
        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(),
        )
        .unwrap();
        test_scenario
            .create_market(
                &router_client,
                TEST_ASSET_ARCH_SYMBOL,
                &[TEST_ASSET_USDT_SYMBOL],
                10u128,
            )
            .await;

        // Update arch market config
        let account = router_client
            .account(test_scenario.admin_address.clone())
            .await
            .unwrap();
        let mut tx_builder = TxBuilder::new(
            test_scenario.admin_mnemonic.clone(),
            test_scenario.chain_prefix.clone(),
            test_scenario.chain_id.clone(),
            test_scenario.derivation_path,
            account.sequence,
            account.account_number,
        )
        .unwrap();

        let new_price_oracle_contract = Addr::unchecked(oracle_contract_address.clone());
        let new_protocol_fee_recipient = Addr::unchecked(oracle_contract_address);
        let new_protocol_fee = Decimal256::percent(5);
        let new_lp_fee = Decimal256::percent(5);
        let config = UpdateMarketConfig {
            base_asset: TEST_ASSET_ARCH_SYMBOL.to_string(),
            price_oracle_contract: Some(new_price_oracle_contract),
            protocol_fee_recipient: Some(new_protocol_fee_recipient),
            protocol_fee: Some(new_protocol_fee),
            lp_fee: Some(new_lp_fee),
            allowance: Some(Allowance::AllowedLps(vec![test_scenario.admin_address])),
        };

        router_client
            .append_update_market_config(&mut tx_builder, config)
            .unwrap();
        tx_builder.set_memo("From test_append_update_market_config".to_string());
        let gas = 500_000u64;
        let amount = 80_000_000_000_000_000u128;
        tx_builder.set_fee(amount, &test_scenario.chain_denom, gas);
        let signed_bytes = tx_builder.get_signed_bytes().unwrap();
        let response = router_client.broadcast_tx(signed_bytes).await.unwrap();
        match response.tx_result.code {
            Code::Ok => {
                println!("Transaction successful: {:?}", response.hash);
                println!("Transaction response: {:?}", response);
                let event = response
                    .tx_result
                    .events
                    .iter()
                    .find(|ev| ev.kind == "wasm")
                    .expect("Failed to find update swap exact in event");
                assert_event_attribute(event, "action", "update_market_config");
                assert_event_attribute(event, "base_asset", TEST_ASSET_ARCH_SYMBOL);
                let markets = router_client.public_router_client.markets().await.unwrap();
                let market = markets.first().unwrap();
                assert_event_attribute(event, "market_address", market.market_address.as_str());
            }
            Code::Err(code) => {
                panic!(
                    "Transaction failed with code: {:?} response: {:?}",
                    code, response
                );
            }
        }
    }
}