use crate::events::{
get_attr_value_as_coin, get_attr_value_as_str, BOLT_WITHDRAW_BASE_EVENT_NAME,
BOLT_WITHDRAW_BASE_EVENT_WITHDRAWN_AMOUNT, WASM_EVENT_CONTRACT_ADDRESS,
};
use crate::market::client::MarketAdminClient;
use crate::market::error::MarketError;
use crate::tx_builder::TxBuilder;
use cosmrs::cosmwasm::MsgExecuteContract;
use cosmrs::tendermint::abci::types::ExecTxResult;
use cosmrs::tx::Msg;
use cosmrs::AccountId;
use cosmwasm_std::Addr;
use serde::Serialize;
impl MarketAdminClient {
pub fn append_withdraw_base_msg(
&self,
tx_builder: &mut TxBuilder,
market_address: AccountId,
receiver: Option<String>,
) -> Result<(), MarketError> {
let receiver = receiver.map(Addr::unchecked);
let msg = ExecuteMsg {
withdraw_base: WithdrawBase { receiver },
};
let contract_msg = serde_json::to_vec(&msg).map_err(MarketError::SerdeJsonError)?;
let msg = MsgExecuteContract {
sender: tx_builder.account_id.clone(),
contract: market_address.clone(),
msg: contract_msg,
funds: vec![],
};
let msg = msg.to_any().map_err(MarketError::EyreError)?;
tx_builder.add_msg(msg);
Ok(())
}
}
pub fn parse_withdraw_base_tx_for_bolt_event(
tx_result: &ExecTxResult,
) -> Result<Vec<WithdrawBaseEvent>, MarketError> {
let bolt_events = tx_result
.events
.iter()
.filter(|event| event.kind == BOLT_WITHDRAW_BASE_EVENT_NAME)
.map(|event| {
let market_address_value = get_attr_value_as_str(event, WASM_EVENT_CONTRACT_ADDRESS)?;
let withdrawn_amount =
get_attr_value_as_coin(event, BOLT_WITHDRAW_BASE_EVENT_WITHDRAWN_AMOUNT)?;
Ok(WithdrawBaseEvent {
withdrawn_amount,
market_address: market_address_value.to_string(),
})
})
.collect::<Result<Vec<_>, MarketError>>()?;
Ok(bolt_events)
}
#[derive(Debug, Clone, PartialEq)]
pub struct WithdrawBaseEvent {
pub withdrawn_amount: cosmwasm_std::Coin,
pub market_address: String,
}
#[derive(Serialize)]
struct WithdrawBase {
pub receiver: Option<Addr>,
}
#[derive(Serialize)]
struct ExecuteMsg {
pub withdraw_base: WithdrawBase,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::market::client::MarketAdminClient;
use crate::oracle::client::OracleAdminClient;
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, Event, EventAttribute};
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_append_deposit_base_msg() {
let mut 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 price_oracle_contract = test_scenario
.instantiate_oracle_contract(price_threshold_ratio, price_expire_millis)
.await;
let client = OracleAdminClient::from_scenario(&test_scenario, &price_oracle_contract)
.expect("Failed to create oracle admin client");
test_scenario.set_default_assets(&client).await;
let price_expiry_time = SystemTime::now().add(Duration::from_secs(7200)); 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;
let protocol_fee_recipient =
Addr::unchecked("archway1706lywddqxu24ff6426puftkfaw20tyhdkun30pljux7e9alv2fql0q0ca");
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(
Addr::unchecked(price_oracle_contract),
protocol_fee_recipient,
protocol_fee,
lp_fee,
TEST_ASSET_ARCH_SYMBOL.to_owned(),
vec![TEST_ASSET_USDT_SYMBOL.to_owned()],
min_base_out,
)
.await;
let market_client = MarketAdminClient::from_scenario(&test_scenario)
.expect("Failed to create market admin client");
let admin_account = test_scenario.admin_account();
test_scenario
.deposit_base(
&market_client,
AccountId::from_str(market_contract_addr.as_ref()).unwrap(),
&admin_account,
Coin::new(50, TEST_ASSET_ARCH_SYMBOL).unwrap(),
)
.await;
let account = market_client
.account(test_scenario.admin_address.clone())
.await
.expect("Failed to get account info");
let mut tx_builder = TxBuilder::new(
test_scenario.admin_mnemonic.clone(),
test_scenario.chain_prefix,
test_scenario.chain_id,
test_scenario.derivation_path,
account.sequence,
account.account_number,
)
.expect("Failed to create tx builder");
market_client
.append_withdraw_base_msg(
&mut tx_builder,
market_contract_addr.clone().parse().unwrap(),
None,
)
.expect("Failed to append withdraw base msg");
tx_builder.set_memo("From test_append_withdraw_base_msg".to_string());
let gas = 400_000u64;
let amount = 70_000_000_000_000_000u128;
tx_builder.set_fee(amount, &test_scenario.chain_denom, gas);
let signed_bytes = tx_builder
.get_signed_bytes()
.expect("Failed to get signed bytes");
let response = market_client
.broadcast_tx(signed_bytes)
.await
.expect("Failed to broadcast tx");
match response.tx_result.code {
Code::Ok => {
println!("Transaction successful: {:?}", response.hash);
println!("Transaction response: {:?}", response);
let withdraw_base_event = response
.tx_result
.events
.iter()
.find(|ev| ev.kind == "wasm-bolt_withdraw_base")
.expect("Failed to find withdraw event");
assert_event_attribute(
withdraw_base_event,
"withdrawn_amount",
format!("50{}", TEST_ASSET_ARCH_SYMBOL).as_str(),
);
assert_event_attribute(withdraw_base_event, "lp", &test_scenario.admin_address);
assert_event_attribute(withdraw_base_event, "liquidity_amount", "0");
}
Code::Err(code) => {
panic!(
"Transaction failed with code: {:?} response: {:?}",
code, response
);
}
}
}
#[test]
fn tx_result_no_withdraw_tx_event() {
let tx_result = super::ExecTxResult {
code: Code::Ok,
data: vec![].into(),
log: String::new(),
events: vec![],
gas_wanted: 0,
gas_used: 0,
info: String::new(),
codespace: String::new(),
};
let result = super::parse_withdraw_base_tx_for_bolt_event(&tx_result);
assert!(result.is_ok());
assert!(result.unwrap().is_empty());
}
#[test]
fn tx_result_multiple_withdraw_tx_events() {
let tx_result = super::ExecTxResult {
code: Code::Ok,
data: vec![].into(),
log: String::new(),
events: vec![
Event {
kind: super::BOLT_WITHDRAW_BASE_EVENT_NAME.to_string(),
attributes: vec![
EventAttribute::V037(cosmrs::tendermint::abci::v0_37::EventAttribute {
key: super::BOLT_WITHDRAW_BASE_EVENT_WITHDRAWN_AMOUNT.to_string(),
value: "500usdt".to_string(),
index: false,
}),
EventAttribute::V037(cosmrs::tendermint::abci::v0_37::EventAttribute {
key: super::WASM_EVENT_CONTRACT_ADDRESS.to_string(),
value: "aaa".to_string(),
index: false,
}),
],
},
Event {
kind: "foo".to_string(),
attributes: vec![
EventAttribute::V037(cosmrs::tendermint::abci::v0_37::EventAttribute {
key: super::BOLT_WITHDRAW_BASE_EVENT_WITHDRAWN_AMOUNT.to_string(),
value: "400eth".to_string(),
index: false,
}),
EventAttribute::V037(cosmrs::tendermint::abci::v0_37::EventAttribute {
key: super::WASM_EVENT_CONTRACT_ADDRESS.to_string(),
value: "bbb".to_string(),
index: false,
}),
],
},
Event {
kind: super::BOLT_WITHDRAW_BASE_EVENT_NAME.to_string(),
attributes: vec![
EventAttribute::V037(cosmrs::tendermint::abci::v0_37::EventAttribute {
key: super::BOLT_WITHDRAW_BASE_EVENT_WITHDRAWN_AMOUNT.to_string(),
value: "300arch".to_string(),
index: false,
}),
EventAttribute::V037(cosmrs::tendermint::abci::v0_37::EventAttribute {
key: super::WASM_EVENT_CONTRACT_ADDRESS.to_string(),
value: "ccc".to_string(),
index: false,
}),
],
},
],
gas_wanted: 0,
gas_used: 0,
info: String::new(),
codespace: String::new(),
};
let result = super::parse_withdraw_base_tx_for_bolt_event(&tx_result)
.expect("Failed to parse events");
assert_eq!(
result,
[
WithdrawBaseEvent {
withdrawn_amount: cosmwasm_std::coin(500, "usdt"),
market_address: "aaa".to_string(),
},
WithdrawBaseEvent {
withdrawn_amount: cosmwasm_std::coin(300, "arch"),
market_address: "ccc".to_string(),
}
]
);
}
}