use crate::bid::{calculate_liquidated_collateral, calculate_remaining_bid};
use crate::querier::query_collateral_whitelist_info;
use crate::state::{
read_bid, read_bid_pool, read_bid_pools, read_bids_by_user, read_collateral_info, read_config,
read_total_bids, Bid, BidPool, CollateralInfo, Config,
};
use cosmwasm_std::{Decimal256, Deps, StdResult, Uint128, Uint256};
use solid_moneymarket::liquidation_queue::{
BidPoolResponse, BidPoolsResponse, BidResponse, BidsResponse, CollateralInfoResponse,
ConfigResponse, LiquidationAmountResponse,
};
use solid_moneymarket::tokens::TokensHuman;
pub fn query_config(deps: Deps) -> StdResult<ConfigResponse> {
let config = read_config(deps.storage)?;
let resp = ConfigResponse {
owner: config.owner.to_string(),
oracle_contract: config.oracle_contract.to_string(),
stable_contract: config.stable_contract.to_string(),
safe_ratio: config.safe_ratio,
bid_fee: config.bid_fee,
liquidator_fee: config.liquidator_fee,
liquidation_threshold: config.liquidation_threshold,
price_timeframe: config.price_timeframe,
waiting_period: config.waiting_period,
overseer: config.overseer.to_string(),
};
Ok(resp)
}
pub fn query_liquidation_amount(
deps: Deps,
borrow_amount: Uint256,
borrow_limit: Uint256,
collaterals: TokensHuman,
collateral_prices: Vec<Decimal256>,
) -> StdResult<LiquidationAmountResponse> {
let config: Config = read_config(deps.storage)?;
let overseer: String = config.overseer.to_string();
if borrow_amount <= borrow_limit {
return Ok(LiquidationAmountResponse {
collaterals: vec![],
});
}
let (collaterals_value, total_weight, collateral_weights, max_ltvs) =
compute_collateral_weights(deps, overseer, &collaterals, &collateral_prices)?;
let safe_ratio = if collaterals_value <= config.liquidation_threshold {
Decimal256::zero()
} else {
config.safe_ratio
};
let base_fee_deductor =
(Decimal256::one() - config.bid_fee) * (Decimal256::one() - config.liquidator_fee);
let mut result: Vec<(String, Uint256)> = vec![];
for (i, collateral) in collaterals.iter().enumerate() {
let (price, weight, max_ltv) = (collateral_prices[i], collateral_weights[i], max_ltvs[i]);
let collateral_token_validated = deps.api.addr_validate(&collateral.0)?;
let collateral_info = read_collateral_info(deps.storage, &collateral_token_validated)?;
let position_portion =
Decimal256::from_ratio(weight, total_weight);
let collateral_borrow_amount = borrow_amount * position_portion;
let collateral_borrow_limit = borrow_limit * position_portion;
let mut x = Uint256::zero();
let mut g_x = Uint256::zero();
let mut intersected = false;
for slot in 0..collateral_info.max_slot + 1 {
let (slot_available_bids, premium_rate) =
match read_bid_pool(deps.storage, &collateral_token_validated, slot) {
Ok(bid_pool) => (bid_pool.total_bid_amount, bid_pool.premium_rate),
Err(_) => continue,
};
if slot_available_bids.is_zero() {
continue;
};
let prev_x = x;
let prev_g_x = g_x;
let discounted_price = price * (Decimal256::one() - premium_rate) * base_fee_deductor;
x += slot_available_bids.multiply_ratio(Decimal256::one().atomics(), discounted_price.atomics());
let safe_borrow = safe_ratio * collateral_borrow_limit;
let f_x = ((safe_ratio * max_ltv * price) * x) + collateral_borrow_amount - safe_borrow;
g_x += slot_available_bids;
if g_x > f_x {
let nominator =
collateral_borrow_amount - safe_borrow + (discounted_price * prev_x) - prev_g_x;
let denominator = price
* (((Decimal256::one() - premium_rate) * base_fee_deductor)
- (safe_ratio * max_ltv));
let liquidation_amount = nominator.multiply_ratio(
Decimal256::one().atomics(),
denominator.atomics(),
) + Uint256::one();
result.push((
collateral.0.to_string(),
liquidation_amount.min(collateral.1),
));
intersected = true;
break;
}
}
if !intersected {
result.push((collateral.0.to_string(), x)); }
}
Ok(LiquidationAmountResponse {
collaterals: result,
})
}
#[allow(clippy::ptr_arg)]
fn compute_collateral_weights(
deps: Deps,
overseer: String,
collaterals: &TokensHuman,
collateral_prices: &Vec<Decimal256>,
) -> StdResult<(Uint256, Uint256, Vec<Uint256>, Vec<Decimal256>)> {
let mut collaterals_value = Uint256::zero();
let mut total_weight = Uint256::zero();
let mut collateral_weights: Vec<Uint256> = vec![];
let mut max_ltvs: Vec<Decimal256> = vec![];
for (collateral, price) in collaterals.iter().zip(collateral_prices.iter()) {
let collateral_available_bids =
read_total_bids(deps.storage, &deps.api.addr_validate(&collateral.0)?)
.unwrap_or_default();
let max_ltv = query_collateral_whitelist_info(
&deps.querier,
overseer.to_string(),
collateral.0.to_string(),
)?
.max_ltv;
let collateral_value = collateral.1 * *price;
let weigth = collateral_value
.min(collateral_available_bids)
.multiply_ratio(Decimal256::one().atomics(), max_ltv.atomics());
total_weight += weigth;
collaterals_value += collateral_value;
collateral_weights.push(weigth);
max_ltvs.push(max_ltv);
}
Ok((
collaterals_value,
total_weight,
collateral_weights,
max_ltvs,
))
}
pub fn query_bid(deps: Deps, bid_idx: Uint128) -> StdResult<BidResponse> {
let bid: Bid = read_bid(deps.storage, bid_idx)?;
let bid_pool: BidPool = read_bid_pool(deps.storage, &bid.collateral_token, bid.premium_slot)?;
let (bid_amount, bid_pending_liquidated_collateral) = if bid.wait_end.is_some() {
(bid.amount, bid.pending_liquidated_collateral)
} else {
let (remaining_bid, _) = calculate_remaining_bid(&bid, &bid_pool)?;
let (liquidated_collateral, _) = calculate_liquidated_collateral(deps.storage, &bid)?;
(
remaining_bid,
bid.pending_liquidated_collateral + liquidated_collateral,
)
};
Ok(BidResponse {
idx: bid.idx,
collateral_token: bid.collateral_token.to_string(),
bidder: bid.bidder.to_string(),
amount: bid_amount,
premium_slot: bid.premium_slot,
pending_liquidated_collateral: bid_pending_liquidated_collateral,
product_snapshot: bid.product_snapshot,
sum_snapshot: bid.sum_snapshot,
wait_end: bid.wait_end,
epoch_snapshot: bid.epoch_snapshot,
scale_snapshot: bid.scale_snapshot,
})
}
pub fn query_bids_by_user(
deps: Deps,
collateral_token: String,
bidder: String,
start_after: Option<Uint128>,
limit: Option<u8>,
) -> StdResult<BidsResponse> {
let collateral_token_validated = deps.api.addr_validate(&collateral_token)?;
let bidder_validated = deps.api.addr_validate(&bidder)?;
let bids: Vec<BidResponse> = read_bids_by_user(
deps.storage,
&collateral_token_validated,
&bidder_validated,
start_after,
limit,
)?
.iter()
.map(|bid| {
let bid_pool: BidPool =
read_bid_pool(deps.storage, &bid.collateral_token, bid.premium_slot)?;
let (bid_amount, bid_pending_liquidated_collateral) = if bid.wait_end.is_some() {
(bid.amount, bid.pending_liquidated_collateral)
} else {
let (remaining_bid, _) = calculate_remaining_bid(bid, &bid_pool)?;
let (liquidated_collateral, _) = calculate_liquidated_collateral(deps.storage, bid)?;
(
remaining_bid,
bid.pending_liquidated_collateral + liquidated_collateral,
)
};
let res = BidResponse {
idx: bid.idx,
collateral_token: bid.collateral_token.to_string(),
bidder: bid.bidder.to_string(),
amount: bid_amount,
premium_slot: bid.premium_slot,
pending_liquidated_collateral: bid_pending_liquidated_collateral,
product_snapshot: bid.product_snapshot,
sum_snapshot: bid.sum_snapshot,
wait_end: bid.wait_end,
epoch_snapshot: bid.epoch_snapshot,
scale_snapshot: bid.scale_snapshot,
};
Ok(res)
})
.collect::<StdResult<Vec<BidResponse>>>()?;
Ok(BidsResponse { bids })
}
pub fn query_bid_pool(
deps: Deps,
collateral_token: String,
bid_slot: u8,
) -> StdResult<BidPoolResponse> {
let collateral_token_validated = deps.api.addr_validate(&collateral_token)?;
let bid_pool: BidPool = read_bid_pool(deps.storage, &collateral_token_validated, bid_slot)?;
Ok(BidPoolResponse {
sum_snapshot: bid_pool.sum_snapshot,
product_snapshot: bid_pool.product_snapshot,
total_bid_amount: bid_pool.total_bid_amount,
premium_rate: bid_pool.premium_rate,
current_epoch: bid_pool.current_epoch,
current_scale: bid_pool.current_scale,
})
}
pub fn query_bid_pools(
deps: Deps,
collateral_token: String,
start_after: Option<u8>,
limit: Option<u8>,
) -> StdResult<BidPoolsResponse> {
let collateral_token_validated = deps.api.addr_validate(&collateral_token)?;
let bid_pools: Vec<BidPoolResponse> = read_bid_pools(
deps.storage,
&collateral_token_validated,
start_after,
limit,
)?
.iter()
.map(|bid_pool| BidPoolResponse {
sum_snapshot: bid_pool.sum_snapshot,
product_snapshot: bid_pool.product_snapshot,
total_bid_amount: bid_pool.total_bid_amount,
premium_rate: bid_pool.premium_rate,
current_epoch: bid_pool.current_epoch,
current_scale: bid_pool.current_scale,
})
.collect();
Ok(BidPoolsResponse { bid_pools })
}
pub fn query_collateral_info(
deps: Deps,
collateral_token: String,
) -> StdResult<CollateralInfoResponse> {
let collateral_token_validated = deps.api.addr_validate(&collateral_token)?;
let collateral_info: CollateralInfo =
read_collateral_info(deps.storage, &collateral_token_validated)?;
Ok(CollateralInfoResponse {
collateral_token: collateral_token_validated.to_string(),
bid_threshold: collateral_info.bid_threshold,
max_slot: collateral_info.max_slot,
premium_rate_per_slot: collateral_info.premium_rate_per_slot,
})
}