bolt-cw-sdk 1.0.0

SDK for the BOLT protocol, providing utilities for interacting with the BOLT contracts.
Documentation
use std::collections::BTreeMap;
use std::str::FromStr;

use cosmrs::tendermint::abci::Event;
use thiserror::Error;
use tonic::Status;

pub const BOLT_UPDATE_PRICE_EVENT_NAME: &str = "wasm-bolt_update_price";
pub const BOLT_UPDATE_PRICE_TRADING_PAIR: &str = "trading_pair";
pub const BOLT_UPDATE_PRICE_NEW_PRICE: &str = "new_price";
pub const BOLT_UPDATE_PRICE_EXPIRY_TIME: &str = "expiry_time";

pub const BOLT_SWAP_EVENT_NAME: &str = "wasm-bolt_swap";
pub const BOLT_SWAP_EVENT_AMOUNT_IN: &str = "amount_in";
pub const BOLT_SWAP_EVENT_DENOM_IN: &str = "denom_in";
pub const BOLT_SWAP_EVENT_AMOUNT_OUT: &str = "amount_out";
pub const BOLT_SWAP_EVENT_DENOM_OUT: &str = "denom_out";
pub const BOLT_SWAP_EVENT_PROTOCOL_FEE_AMOUNT: &str = "protocol_fee_amount";
pub const BOLT_SWAP_EVENT_LP_FEE_AMOUNT: &str = "lp_fee_amount";
pub const BOLT_SWAP_EVENT_PRICE: &str = "price";
pub const BOLT_SWAP_EVENT_CONTRACT_ADDRESS: &str = "_contract_address";
pub const BOLT_SWAP_EVENT_SENDER: &str = "sender";

pub const BOLT_WITHDRAW_BASE_EVENT_NAME: &str = "wasm-bolt_withdraw_base";
pub const BOLT_WITHDRAW_BASE_EVENT_WITHDRAWN_AMOUNT: &str = "withdrawn_amount";
pub const WASM_EVENT_CONTRACT_ADDRESS: &str = "_contract_address";

pub const BOLT_WITHDRAW_QUOTES_EVENT_NAME: &str = "wasm-bolt_withdraw_quotes";
pub const BOLT_WITHDRAW_QUOTES_EVENT_CONTRACT_ADDRESS: &str = "_contract_address";
pub const BOLT_WITHDRAW_QUOTES_EVENT_WITHDRAWN_QUOTES: &str = "withdrawn_quotes";

#[derive(Error, Debug)]
pub enum EventError {
    #[error("Failed to find bolt event attribute")]
    FailedToFindBoltEventAttribute,
    #[error("Failed to parse bold event value")]
    FailedToParseBoltEventValue,
}

pub fn event_key(event: &str, key: &str) -> String {
    format!("{event}.{key}")
}

#[allow(clippy::result_large_err)]
pub fn get_events<'a>(
    event: &str,
    key: &str,
    events: &'a BTreeMap<String, Vec<String>>,
) -> Result<&'a Vec<String>, Status> {
    let event_key = event_key(event, key);

    events
        .get(&event_key)
        .ok_or(Status::not_found(format!("Could not find {event_key}")))
}

#[allow(clippy::result_large_err)]
pub fn get_event<'a>(
    event: &str,
    key: &str,
    events: &'a BTreeMap<String, Vec<String>>,
) -> Result<&'a str, Status> {
    get_events(event, key, events).and_then(|events| {
        if events.len() > 1 {
            return Err(Status::internal(format!(
                "Expected one event, got {}",
                events.len()
            )));
        }

        events
            .first()
            .map(|a| a.as_str())
            .ok_or(Status::not_found("Event was empty"))
    })
}

/// Useful filter function for contracts
pub fn expected_contract(
    event: &str,
    contracts: &[String],
    events: &BTreeMap<String, Vec<String>>,
) -> bool {
    get_event(event, "_contract_address", events)
        .ok()
        .map(|contract| contracts.iter().any(|expected| expected == contract))
        // Default to false if the event doesnt exist
        .unwrap_or(false)
}

pub fn get_attr_value_as_str<'a>(event: &'a Event, key: &str) -> Result<&'a str, EventError> {
    let attribute = event
        .attributes
        .iter()
        .find(|attr| attr.key_str().map(|k| k == key).unwrap_or(false))
        .ok_or(EventError::FailedToFindBoltEventAttribute)?;
    attribute
        .value_str()
        .map_err(|_| EventError::FailedToParseBoltEventValue)
}

pub fn get_attr_value_as_u128(event: &Event, key: &str) -> Result<u128, EventError> {
    let value = get_attr_value_as_str(event, key)?;
    value
        .parse::<u128>()
        .map_err(|_| EventError::FailedToParseBoltEventValue)
}

pub fn get_attr_value_as_coin(event: &Event, key: &str) -> Result<cosmwasm_std::Coin, EventError> {
    let value = get_attr_value_as_str(event, key)?;
    cosmwasm_std::Coin::from_str(value).map_err(|_| EventError::FailedToParseBoltEventValue)
}

pub fn get_attr_value_as_coins(
    event: &Event,
    key: &str,
) -> Result<Vec<cosmwasm_std::Coin>, EventError> {
    let value = get_attr_value_as_str(event, key)?;
    value
        .split(',')
        .map(|coin_value| {
            cosmwasm_std::Coin::from_str(coin_value)
                .map_err(|_| EventError::FailedToParseBoltEventValue)
        })
        .collect::<Result<Vec<_>, _>>()
}