bolt-cw-sdk 1.0.0

SDK for the BOLT protocol, providing utilities for interacting with the BOLT contracts.
Documentation
use std::time::Duration;

use crate::oracle::add_asset_pairs::AssetPair;
use crate::oracle::client::OracleAdminClient;
use crate::oracle::post_prices::PriceUpdate;
use crate::test_utils::helpers::{
    get_test_arch_usdt_pair, TEST_ASSET_ARCH_SYMBOL, TEST_ASSET_USDT_SYMBOL,
};
use crate::test_utils::test_scenario::TestScenario;
use crate::tx_builder::TxBuilder;
use cosmwasm_std::{Addr, Decimal256, Timestamp};
use serde::Serialize;
use tendermint_rpc::endpoint::broadcast::tx_commit::Response;

use super::helpers::{get_test_arch_eth_pair, get_test_eth_usdt_pair, TEST_ASSET_ETH_SYMBOL};

impl TestScenario {
    pub async fn instantiate_oracle_contract(
        &self,
        price_threshold_ratio: Decimal256,
        price_expire_millis: Option<u64>,
    ) -> String {
        let init_msg = InstantiateMsg {
            admin: Addr::unchecked(self.admin_address.clone()),
            price_feeder: Addr::unchecked(self.price_feeder_account.account_id.to_string()),
            price_threshold_ratio,
            price_expire_millis,
        };
        let msg = serde_json::to_vec(&init_msg).expect("Failed to serialize init msg");

        self.instantiate_contract(msg, self.oracle_code_id, "oracle")
            .await
    }

    pub async fn instantiate_and_setup_oracle(
        &self,
        price_threshold_ratio: Decimal256,
        price_expire_millis: Option<u64>,
        asset_pairs: Vec<AssetPair>,
        memo: &str,
    ) -> String {
        let oracle_contract_address = self
            .instantiate_oracle_contract(price_threshold_ratio, price_expire_millis)
            .await;

        let client = OracleAdminClient::from_scenario(self, &oracle_contract_address)
            .expect("Failed to create oracle admin client");
        let account = client
            .account(self.admin_address.clone())
            .await
            .expect("Failed to get account from oracle client");
        let mut tx_builder = TxBuilder::new(
            self.admin_mnemonic.clone(),
            self.chain_prefix.clone(),
            self.chain_id.clone(),
            self.derivation_path,
            account.sequence,
            account.account_number,
        )
        .expect("Failed to create tx builder");
        client
            .append_add_asset_pairs_msg(&mut tx_builder, asset_pairs)
            .expect("Failed to append add asset pairs msg");
        tx_builder.set_memo(memo.to_string());
        tx_builder.set_fee(45_000_000_000_000_000, &self.chain_denom, 400_000u64);
        let signed_bytes = tx_builder
            .get_signed_bytes()
            .expect("Failed to get signed bytes");
        let response = client
            .broadcast_tx(signed_bytes)
            .await
            .expect("Failed to broadcast tx");
        println!(
            "Oracle contract added assets with hash: {:?}",
            response.hash.to_string()
        );

        oracle_contract_address
    }

    pub async fn set_default_assets(&self, client: &OracleAdminClient) -> Response {
        let account = client
            .public_oracle_client
            .account(self.admin_address.clone())
            .await
            .expect("Failed to get account from public oracle client");
        let mut tx_builder = TxBuilder::new(
            self.admin_mnemonic.clone(),
            self.chain_prefix.clone(),
            self.chain_id.clone(),
            self.derivation_path,
            account.sequence,
            account.account_number,
        )
        .expect("Failed to create tx builder");

        let asset_pairs = vec![
            get_test_arch_usdt_pair(),
            get_test_eth_usdt_pair(),
            get_test_arch_eth_pair(),
        ];
        client
            .append_add_asset_pairs_msg(&mut tx_builder, asset_pairs)
            .expect("Failed to append add asset pairs msg");

        tx_builder.set_memo("From test_default_assets".to_string());
        let gas = 500_000u64;
        tx_builder.set_fee(70_000_000_000_000_000u128, &self.chain_denom, gas);
        let signed_bytes = tx_builder
            .get_signed_bytes()
            .expect("Failed to get signed bytes");

        client
            .broadcast_tx(signed_bytes)
            .await
            .expect("Failed to broadcast tx")
    }

    pub async fn set_prices(
        &self,
        client: &OracleAdminClient,
        prices: Vec<((String, String), String)>,
        price_expiry_timestamp: Duration,
    ) -> Response {
        let account = client
            .public_oracle_client
            .account(self.price_feeder_account.account_id.to_string())
            .await
            .expect("Failed to get account from public oracle client");
        let mut tx_builder = TxBuilder::new(
            self.price_feeder_account.mnemonic.clone(),
            self.chain_prefix.clone(),
            self.chain_id.clone(),
            self.derivation_path,
            account.sequence,
            account.account_number,
        )
        .expect("Failed to create tx builder");

        let price_updates = prices
            .iter()
            .map(|((base, quote), price)| PriceUpdate {
                base_asset_symbol: base.to_string(),
                quote_asset_symbol: quote.to_string(),
                price: price.clone(),
                price_expiry_time: Some(Timestamp::from_seconds(price_expiry_timestamp.as_secs())),
            })
            .collect();

        client
            .append_post_prices_msg(&mut tx_builder, price_updates)
            .expect("Append post prices message");

        tx_builder.set_memo("From test_post_prices".to_string());
        let gas = 500_000u64;
        tx_builder.set_fee(70_000_000_000_000_000u128, &self.chain_denom, gas);
        let signed_bytes = tx_builder
            .get_signed_bytes()
            .expect("Failed to get signed bytes");

        client
            .broadcast_tx(signed_bytes)
            .await
            .expect("Failed to broadcast tx")
    }

    pub async fn set_default_prices(
        &self,
        client: &OracleAdminClient,
        price_expiry_timestamp: Duration,
        price: &str,
    ) -> Response {
        self.set_prices(
            client,
            vec![
                (
                    (
                        TEST_ASSET_ARCH_SYMBOL.to_string(),
                        TEST_ASSET_USDT_SYMBOL.to_string(),
                    ),
                    price.to_string(),
                ),
                (
                    (
                        TEST_ASSET_ETH_SYMBOL.to_string(),
                        TEST_ASSET_USDT_SYMBOL.to_string(),
                    ),
                    price.to_string(),
                ),
                (
                    (
                        TEST_ASSET_ARCH_SYMBOL.to_string(),
                        TEST_ASSET_ETH_SYMBOL.to_string(),
                    ),
                    price.to_string(),
                ),
            ],
            price_expiry_timestamp,
        )
        .await
    }
}

#[derive(Serialize)]
struct InstantiateMsg {
    // admin is the address of the price feeder for this oracle
    pub admin: Addr,
    // price_feeder is the address of the price feeder contract that will be used to post prices
    pub price_feeder: Addr,
    // price_threshold_ratio is the rate at which the price of a pair is allowed to increase or decrease
    pub price_threshold_ratio: Decimal256,
    // price_expire_millis is the default value to use when explicit price expiry time is not provided by the price feeder
    pub price_expire_millis: Option<u64>,
}

#[cfg(test)]
mod tests {
    use serial_test::serial;

    use super::*;
    use crate::test_utils::test_scenario::TestScenario;
    use std::str::FromStr;

    #[tokio::test]
    #[serial]
    #[ignore]
    async fn test_instantiate_oracle_contract() {
        let test_scenario = TestScenario::new_from_config("config.json".to_string()).await;
        let price_threshold_ratio = Decimal256::from_str("0.1").unwrap();
        let price_expire_millis = Some(1000);
        let contract_addr = test_scenario
            .instantiate_oracle_contract(price_threshold_ratio, price_expire_millis)
            .await;
        println!("Contract address: {:?}", contract_addr);
    }
}