Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ members = [
"contracts/mock-pyth",
"contracts/mock-red-bank",
"contracts/mock-vault",
"contracts/mock-dao-staking",

# packages
"packages/chains/*",
Expand Down Expand Up @@ -151,6 +152,7 @@ mars-mock-oracle = { path = "./contracts/mock-oracle" }
mars-mock-red-bank = { path = "./contracts/mock-red-bank" }
mars-mock-vault = { path = "./contracts/mock-vault" }
mars-mock-rover-health = { path = "./contracts/mock-health" }
mars-mock-dao-staking = { path = "./contracts/mock-dao-staking" }
mars-swapper-mock = { path = "./contracts/swapper/mock" }
mars-zapper-mock = { path = "./contracts/v2-zapper/mock" }

Expand Down
15 changes: 11 additions & 4 deletions contracts/credit-manager/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ use crate::{
migrations,
perp::update_balance_after_deleverage,
query::{
query_accounts, query_all_coin_balances, query_all_debt_shares,
query_all_total_debt_shares, query_all_trigger_orders,
query_account_tier_and_discount, query_accounts, query_all_coin_balances,
query_all_debt_shares, query_all_total_debt_shares, query_all_trigger_orders,
query_all_trigger_orders_for_account, query_all_vault_positions,
query_all_vault_utilizations, query_config, query_positions, query_swap_fee,
query_total_debt_shares, query_vault_bindings, query_vault_position_value,
query_vault_utilization,
query_total_debt_shares, query_trading_fee, query_vault_bindings,
query_vault_position_value, query_vault_utilization,
},
repay::repay_from_wallet,
state::NEXT_TRIGGER_ID,
Expand Down Expand Up @@ -173,6 +173,13 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> ContractResult<Binary> {
start_after,
limit,
} => to_json_binary(&query_vault_bindings(deps, start_after, limit)?),
QueryMsg::GetAccountTierAndDiscount {
account_id,
} => to_json_binary(&query_account_tier_and_discount(deps, &account_id)?),
QueryMsg::TradingFee {
account_id,
market_type,
} => to_json_binary(&query_trading_fee(deps, &account_id, &market_type)?),
QueryMsg::SwapFeeRate {} => to_json_binary(&query_swap_fee(deps)?),
};
res.map_err(Into::into)
Expand Down
1 change: 1 addition & 0 deletions contracts/credit-manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub mod reclaim;
pub mod refund;
pub mod repay;
pub mod stake_astro_lp;
pub mod staking;
pub mod state;
pub mod swap;
pub mod trigger;
Expand Down
64 changes: 56 additions & 8 deletions contracts/credit-manager/src/perp.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::cmp::min;

use cosmwasm_std::{
coin, ensure_eq, BankMsg, Coin, CosmosMsg, DepsMut, Env, Int128, MessageInfo, Response, Uint128,
coin, ensure_eq, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, Env, Int128, MessageInfo,
Response, Uint128,
};
use mars_types::{
adapters::perps::Perps,
Expand All @@ -12,6 +13,7 @@ use mars_types::{
use crate::{
borrow,
error::{ContractError, ContractResult},
staking::get_account_tier_and_discount,
state::{COIN_BALANCES, PERPS, RED_BANK},
trigger::remove_invalid_trigger_orders,
utils::{decrement_coin_balance, increment_coin_balance},
Expand Down Expand Up @@ -94,6 +96,10 @@ pub fn execute_perp_order(

let mut response = Response::new();

// Get staking tier discount for this account
let (tier, discount_pct, voting_power) =
get_account_tier_and_discount(deps.as_ref(), account_id)?;

// Query the perp position PnL so that we know whether funds needs to be
// sent to the perps contract
//
Expand All @@ -114,10 +120,14 @@ pub fn execute_perp_order(
position,
order_size,
reduce_only,
discount_pct,
)?,
None => {
// Open new position
let opening_fee = perps.query_opening_fee(&deps.querier, denom, order_size)?;
let base_opening_fee =
perps.query_opening_fee(&deps.querier, denom, order_size, None)?;
let opening_fee =
perps.query_opening_fee(&deps.querier, denom, order_size, Some(discount_pct))?;
let fee = opening_fee.fee;

let funds = if !fee.amount.is_zero() {
Expand All @@ -127,8 +137,14 @@ pub fn execute_perp_order(
vec![]
};

let msg =
perps.execute_perp_order(account_id, denom, order_size, reduce_only, funds)?;
let msg = perps.execute_perp_order(
account_id,
denom,
order_size,
reduce_only,
funds,
Some(discount_pct),
)?;

response
.add_message(msg)
Expand All @@ -138,6 +154,10 @@ pub fn execute_perp_order(
.add_attribute("reduce_only", reduce_only.unwrap_or(false).to_string())
.add_attribute("new_size", order_size.to_string())
.add_attribute("opening_fee", fee.to_string())
.add_attribute("base_opening_fee", base_opening_fee.fee.to_string())
.add_attribute("voting_power", voting_power.to_string())
.add_attribute("tier_id", tier.id)
.add_attribute("discount_pct", discount_pct.to_string())
}
})
}
Expand All @@ -149,6 +169,9 @@ pub fn close_perp_position(
) -> ContractResult<Response> {
let perps = PERPS.load(deps.storage)?;

// Get staking tier discount for this account
let (_, discount_pct, _) = get_account_tier_and_discount(deps.as_ref(), account_id)?;

// Query the perp position PnL so that we know whether funds needs to be
// sent to the perps contract
//
Expand All @@ -174,6 +197,7 @@ pub fn close_perp_position(
position,
order_size,
Some(true),
discount_pct,
)?)
}
None => Err(ContractError::NoPerpPosition {
Expand Down Expand Up @@ -214,15 +238,23 @@ pub fn close_all_perps(
let (funds, response) =
update_state_based_on_pnl(&mut deps, account_id, pnl, Some(action.clone()), response)?;
let funds = funds.map_or_else(Vec::new, |c| vec![c]);
println!("funds : {:?}", funds);

// Get staking tier discount for this account
let (tier, discount_pct, voting_power) =
get_account_tier_and_discount(deps.as_ref(), account_id)?;

// Close all perp positions at once
let close_msg = perps.close_all_msg(account_id, funds, action)?;
let close_msg = perps.close_all_msg(account_id, funds, action, Some(discount_pct))?;

Ok(response
.add_message(close_msg)
.add_attribute("action", "close_all_perps")
.add_attribute("account_id", account_id)
.add_attribute("number_of_positions", perp_positions.len().to_string()))
.add_attribute("number_of_positions", perp_positions.len().to_string())
.add_attribute("voting_power", voting_power.to_string())
.add_attribute("tier_id", tier.id)
.add_attribute("discount_pct", discount_pct.to_string()))
}

fn modify_existing_position(
Expand All @@ -234,13 +266,26 @@ fn modify_existing_position(
position: PerpPosition,
order_size: Int128,
reduce_only: Option<bool>,
discount_pct: Decimal,
) -> ContractResult<Response> {
let pnl = position.unrealized_pnl.to_coins(&position.base_denom).pnl;
let pnl_string = position.unrealized_pnl.pnl.to_string();
let (funds, response) = update_state_based_on_pnl(&mut deps, account_id, pnl, None, response)?;
let funds = funds.map_or_else(Vec::new, |c| vec![c]);

let msg = perps.execute_perp_order(account_id, denom, order_size, reduce_only, funds)?;
// Get base and effective fees for logging
let base_opening_fee = perps.query_opening_fee(&deps.querier, denom, order_size, None)?;
let effective_opening_fee =
perps.query_opening_fee(&deps.querier, denom, order_size, Some(discount_pct))?;

let msg = perps.execute_perp_order(
account_id,
denom,
order_size,
reduce_only,
funds,
Some(discount_pct),
)?;

let new_size = position.size.checked_add(order_size)?;

Expand All @@ -258,7 +303,10 @@ fn modify_existing_position(
.add_attribute("realized_pnl", pnl_string)
.add_attribute("reduce_only", reduce_only.unwrap_or(false).to_string())
.add_attribute("order_size", order_size.to_string())
.add_attribute("new_size", new_size.to_string()))
.add_attribute("new_size", new_size.to_string())
.add_attribute("base_opening_fee", base_opening_fee.fee.to_string())
.add_attribute("effective_opening_fee", effective_opening_fee.fee.to_string())
.add_attribute("discount_pct", discount_pct.to_string()))
}

/// Prepare the necessary messages and funds to be sent to the perps contract based on the PnL.
Expand Down
65 changes: 65 additions & 0 deletions contracts/credit-manager/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,68 @@ pub fn query_vault_bindings(
})
})
}

pub fn query_account_tier_and_discount(
deps: Deps,
account_id: &str,
) -> ContractResult<mars_types::credit_manager::AccountTierAndDiscountResponse> {
use crate::staking::get_account_tier_and_discount;

let (tier, discount_pct, voting_power) = get_account_tier_and_discount(deps, account_id)?;

Ok(mars_types::credit_manager::AccountTierAndDiscountResponse {
tier_id: tier.id,
discount_pct,
voting_power,
})
}

/// Queries the trading fee for a specific account and market type.
/// For spot markets, returns a default fee structure.
/// For perp markets, calculates the opening fee based on the denom and applies any applicable discounts.
pub fn query_trading_fee(
deps: Deps,
account_id: &str,
market_type: &mars_types::credit_manager::MarketType,
) -> ContractResult<mars_types::credit_manager::TradingFeeResponse> {
use crate::staking::get_account_tier_and_discount;

// Get staking tier discount for this account
let (tier, discount_pct, _) = get_account_tier_and_discount(deps, account_id)?;

match market_type {
mars_types::credit_manager::MarketType::Spot => {
// For spot markets, use a default fee structure
// You can customize this based on your spot trading requirements
let base_fee_pct = cosmwasm_std::Decimal::percent(25); // 0.25% base fee
let effective_fee_pct =
base_fee_pct.checked_mul(cosmwasm_std::Decimal::one() - discount_pct)?;

Ok(mars_types::credit_manager::TradingFeeResponse {
base_fee_pct,
discount_pct,
effective_fee_pct,
tier_id: tier.id,
})
}
mars_types::credit_manager::MarketType::Perp {
denom,
} => {
// For perp markets, get the opening fee rate and apply discount
let params = crate::state::PARAMS.load(deps.storage)?;

// Query the params contract to get the opening fee rate for this denom
let perp_params = params.query_perp_params(&deps.querier, denom)?;
let base_fee_pct = perp_params.opening_fee_rate;
let effective_fee_pct =
base_fee_pct.checked_mul(cosmwasm_std::Decimal::one() - discount_pct)?;

Ok(mars_types::credit_manager::TradingFeeResponse {
base_fee_pct,
discount_pct,
effective_fee_pct,
tier_id: tier.id,
})
}
}
}
Loading
Loading