Skip to main content

bolt_cw_sdk/oracle/
post_prices.rs

1use crate::oracle::client::OracleAdminClient;
2use crate::oracle::error::OracleError;
3use crate::tx_builder::TxBuilder;
4use cosmrs::cosmwasm::MsgExecuteContract;
5use cosmrs::tx::Msg;
6use cosmwasm_std::Timestamp;
7use serde::Serialize;
8
9impl OracleAdminClient {
10    pub fn append_post_prices_msg(
11        &self,
12        tx_builder: &mut TxBuilder,
13        price_updates: Vec<PriceUpdate>,
14    ) -> Result<(), OracleError> {
15        let msgs = price_updates.into_iter().map(|price_update| {
16            let contract_msg = ExecuteMsg {
17                update_price: price_update,
18            };
19            let contract_msg =
20                serde_json::to_vec(&contract_msg).map_err(OracleError::SerdeJsonError)?;
21            let msg = MsgExecuteContract {
22                sender: tx_builder.account_id.clone(),
23                contract: self.chain_config.oracle_contract_address.clone(),
24                msg: contract_msg,
25                funds: vec![],
26            };
27            msg.to_any().map_err(OracleError::EyreError)
28        });
29        let msgs = msgs.collect::<Result<Vec<_>, _>>()?;
30        tx_builder.add_msgs(msgs);
31        Ok(())
32    }
33}
34
35#[derive(Serialize)]
36pub struct PriceUpdate {
37    pub base_asset_symbol: String,
38    pub quote_asset_symbol: String,
39    pub price: String,
40    pub price_expiry_time: Option<Timestamp>,
41}
42
43#[derive(Serialize)]
44struct ExecuteMsg {
45    pub update_price: PriceUpdate,
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51    use crate::events::BOLT_UPDATE_PRICE_EVENT_NAME;
52    use crate::test_utils::helpers::{
53        assert_event_attribute, get_test_arch_usdt_pair, TEST_ASSET_ARCH_SYMBOL,
54        TEST_ASSET_USDT_SYMBOL,
55    };
56    use crate::test_utils::test_scenario::TestScenario;
57    use cosmrs::tendermint::abci::Code;
58    use cosmwasm_std::Decimal256;
59    use serial_test::serial;
60    use std::ops::Add;
61    use std::str::FromStr;
62    use std::time::{Duration, SystemTime, UNIX_EPOCH};
63
64    #[tokio::test]
65    #[serial]
66    async fn test_post_prices() {
67        let test_scenario = TestScenario::new_from_config("config.json".to_string()).await;
68
69        let price_threshold_ratio = Decimal256::from_str("0.5").unwrap();
70        let price_expire_millis = Some(1000);
71        let oracle_contract_address = test_scenario
72            .instantiate_oracle_contract(price_threshold_ratio, price_expire_millis)
73            .await;
74
75        let client = OracleAdminClient::from_scenario(&test_scenario, &oracle_contract_address)
76            .expect("Failed to create oracle admin client");
77        let account = client
78            .public_oracle_client
79            .account(test_scenario.admin_address)
80            .await
81            .expect("Failed to get account from public oracle client");
82        let mut tx_builder = TxBuilder::new(
83            test_scenario.admin_mnemonic,
84            test_scenario.chain_prefix,
85            test_scenario.chain_id,
86            test_scenario.derivation_path,
87            account.sequence,
88            account.account_number,
89        )
90        .expect("Failed to create tx builder");
91        let asset_pairs = vec![get_test_arch_usdt_pair()];
92        client
93            .append_add_asset_pairs_msg(&mut tx_builder, asset_pairs)
94            .expect("Failed to append add asset pairs msg");
95
96        let price_expiry_time = SystemTime::now().add(Duration::from_secs(7200)); // 2h from now
97        let price_expiry_timestamp = price_expiry_time
98            .duration_since(UNIX_EPOCH)
99            .expect("Converting to timestamp failed");
100        let arch_usdt_price = Decimal256::from_str("50000").unwrap();
101        let price_expiry_time = Timestamp::from_seconds(price_expiry_timestamp.as_secs());
102        let price_updates = vec![PriceUpdate {
103            base_asset_symbol: TEST_ASSET_ARCH_SYMBOL.to_string(),
104            quote_asset_symbol: TEST_ASSET_USDT_SYMBOL.to_string(),
105            price: arch_usdt_price.to_string(),
106            price_expiry_time: Some(price_expiry_time),
107        }];
108        client
109            .append_post_prices_msg(&mut tx_builder, price_updates)
110            .expect("Append PostPrices message");
111
112        tx_builder.set_memo("From test_post_prices".to_string());
113
114        let response = client
115            .execute_tx(tx_builder)
116            .await
117            .expect("Failed to broadcast tx");
118
119        match response.tx_result.code {
120            Code::Ok => {
121                println!("Transaction successful: {:?}", response.hash);
122                dbg!(&response.tx_result.events);
123                let update_price_event = response
124                    .tx_result
125                    .events
126                    .iter()
127                    .find(|ev| ev.kind == BOLT_UPDATE_PRICE_EVENT_NAME)
128                    .expect("Failed to find update price event");
129                assert_event_attribute(
130                    update_price_event,
131                    "trading_pair",
132                    &format!("{}:{}", TEST_ASSET_ARCH_SYMBOL, TEST_ASSET_USDT_SYMBOL),
133                );
134                assert_event_attribute(update_price_event, "new_price", "50000");
135                assert_event_attribute(update_price_event, "forced_update", "true");
136            }
137            Code::Err(code) => {
138                panic!(
139                    "Transaction failed with code: {:?} response: {:?}",
140                    code, response
141                );
142            }
143        }
144    }
145}