use std::collections::HashMap;
use cosmwasm_schema::{cw_serde, QueryResponses};
use crate::asset::{Asset, AssetExchangeRate, AssetInfo};
use crate::vault::{PoolType, SwapType, NativeAssetPrecisionInfo};
use cosmwasm_std::{Addr, Binary, Decimal256, DepsMut, Env, Event, MessageInfo, Response, StdError, StdResult, Uint128};
use std::fmt::{Display, Formatter, Result};
use cw_storage_plus::{Item, Map};
use crate::helper::EventExt;
pub const DEFAULT_SPREAD: &str = "0.005";
#[cw_serde]
pub struct FeeStructs {
pub total_fee_bps: u16,
}
impl Display for FeeStructs {
fn fmt(&self, fmt: &mut Formatter) -> Result {
fmt.write_str(format!("total_fee_bps : {}", self.total_fee_bps).as_str())
}
}
#[cw_serde]
pub struct Config {
pub pool_id: Uint128,
pub lp_token_addr: Addr,
pub vault_addr: Addr,
pub assets: Vec<Asset>,
pub pool_type: PoolType,
pub fee_info: FeeStructs,
pub block_time_last: u64,
}
#[cw_serde]
pub struct Trade {
pub amount_in: Uint128,
pub amount_out: Uint128,
pub spread: Uint128,
}
#[cw_serde]
pub enum ResponseType {
Success {},
Failure(String),
}
impl Display for ResponseType {
fn fmt(&self, fmt: &mut Formatter) -> Result {
match self {
ResponseType::Success {} => fmt.write_str("success"),
ResponseType::Failure(error) => fmt.write_str(format!("error : {}", error).as_str()),
}
}
}
impl ResponseType {
pub fn is_success(&self) -> bool {
match self {
ResponseType::Success {} => true,
ResponseType::Failure(_) => false,
}
}
}
#[cw_serde]
pub struct InstantiateMsg {
pub pool_id: Uint128,
pub pool_type: PoolType,
pub vault_addr: Addr,
pub lp_token_addr: Addr,
pub asset_infos: Vec<AssetInfo>,
pub native_asset_precisions: Vec<NativeAssetPrecisionInfo>,
pub fee_info: FeeStructs,
pub init_params: Option<Binary>,
}
#[cw_serde]
pub enum ExecuteMsg {
UpdateConfig { params: Binary },
UpdateFee { total_fee_bps: u16 },
UpdateLiquidity { assets: Vec<Asset> },
}
#[cw_serde]
#[derive(QueryResponses)]
pub enum QueryMsg {
#[returns(ConfigResponse)]
Config {},
#[returns(FeeResponse)]
FeeParams {},
#[returns(Uint128)]
PoolId {},
#[returns(AfterJoinResponse)]
OnJoinPool {
assets_in: Option<Vec<Asset>>,
mint_amount: Option<Uint128>,
},
#[returns(AfterExitResponse)]
OnExitPool {
exit_type: ExitType,
},
#[returns(SwapResponse)]
OnSwap {
swap_type: SwapType,
offer_asset: AssetInfo,
ask_asset: AssetInfo,
amount: Uint128
},
#[returns(SpotPrice)]
SpotPrice {
offer_asset: AssetInfo,
ask_asset: AssetInfo,
},
#[returns(CumulativePriceResponse)]
CumulativePrice {
offer_asset: AssetInfo,
ask_asset: AssetInfo,
},
#[returns(CumulativePricesResponse)]
CumulativePrices {},
}
#[cw_serde]
pub enum ExitType {
ExactLpBurn(Uint128),
ExactAssetsOut(Vec<Asset>),
}
#[cw_serde]
pub enum MigrateMsg {
V1_1 {}
}
#[cw_serde]
pub struct ConfigResponse {
pub pool_id: Uint128,
pub lp_token_addr: Addr,
pub vault_addr: Addr,
pub assets: Vec<Asset>,
pub pool_type: PoolType,
pub fee_info: FeeStructs,
pub block_time_last: u64,
pub math_params: Option<Binary>,
pub additional_params: Option<Binary>,
}
#[cw_serde]
pub struct AfterJoinResponse {
pub provided_assets: Vec<Asset>,
pub new_shares: Uint128,
pub response: ResponseType,
pub fee: Option<Vec<Asset>>,
}
#[cw_serde]
pub struct AfterExitResponse {
pub assets_out: Vec<Asset>,
pub burn_shares: Uint128,
pub response: ResponseType,
pub fee: Option<Vec<Asset>>,
}
#[cw_serde]
pub struct FeeResponse {
pub total_fee_bps: u16,
}
#[cw_serde]
pub struct SwapResponse {
pub trade_params: Trade,
pub response: ResponseType,
pub fee: Option<Asset>,
}
#[cw_serde]
pub struct CumulativePriceResponse {
pub exchange_info: AssetExchangeRate,
pub total_share: Uint128,
}
#[cw_serde]
pub struct CumulativePricesResponse {
pub exchange_infos: Vec<AssetExchangeRate>,
pub total_share: Uint128,
}
#[cw_serde]
pub struct SpotPrice {
pub from: AssetInfo,
pub to: AssetInfo,
pub price: Decimal256,
pub price_including_fee: Decimal256
}
pub fn update_fee(
deps: DepsMut,
_env: Env,
info: MessageInfo,
total_fee_bps: u16,
config_item: Item<Config>,
contract_name: impl Into<String>,
) -> StdResult<Response> {
let mut config = config_item.load(deps.storage)?;
if info.sender != config.vault_addr {
return Err(StdError::generic_err("Unauthorized"));
}
config.fee_info.total_fee_bps = total_fee_bps;
config_item.save(deps.storage, &config)?;
Ok(Response::new().add_event(
Event::from_info(contract_name.into() + "::update_fee", &info)
.add_attribute("total_fee_bps", config.fee_info.total_fee_bps.to_string())
))
}
pub fn return_join_failure(error: String) -> AfterJoinResponse {
AfterJoinResponse {
provided_assets: vec![],
new_shares: Uint128::zero(),
response: ResponseType::Failure(error),
fee: None,
}
}
pub fn return_exit_failure(error: String) -> AfterExitResponse {
AfterExitResponse {
assets_out: vec![],
burn_shares: Uint128::zero(),
response: ResponseType::Failure(error),
fee: None,
}
}
pub fn return_swap_failure(error: String) -> SwapResponse {
SwapResponse {
trade_params: Trade {
amount_in: Uint128::zero(),
amount_out: Uint128::zero(),
spread: Uint128::zero(),
},
response: ResponseType::Failure(error),
fee: None,
}
}
pub fn store_precisions(
deps: DepsMut,
native_asset_precisions: &Vec<NativeAssetPrecisionInfo>,
asset_infos: &[AssetInfo],
precisions: Map<String, u8>,
) -> StdResult<u8> {
let mut max = 0u8;
let native_asset_precisions = native_asset_precisions
.into_iter()
.map(|info| (info.denom.clone(), info.precision))
.collect::<HashMap<String, u8>>();
for asset_info in asset_infos {
let precision = match asset_info {
AssetInfo::NativeToken { denom } => {
native_asset_precisions.get(&denom.clone()).cloned().unwrap()
},
AssetInfo::Token { contract_addr } => {
let res: cw20::TokenInfoResponse =
deps.querier.query_wasm_smart(contract_addr, &cw20::Cw20QueryMsg::TokenInfo {})?;
res.decimals
}
};
max = max.max(precision);
precisions.save(deps.storage, asset_info.to_string(), &precision)?;
}
Ok(max)
}