security admin can bring markets to reduce only (#394)

* security admin can bring markets to reduce only

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>

* fixes from review

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>

* fix from review; security admin can only turn on reduce only, security admin can only reduce init asset weight

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>

* --wip-- [skip ci]

* fixes from review

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>

* fixes from review

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
microwavedcola1 2023-01-23 15:02:48 +01:00 committed by GitHub
parent 64dda20cb5
commit 80f4bd8048
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 130 additions and 68 deletions

View File

@ -83,6 +83,8 @@ pub enum MangoError {
HasLiquidatableTrustedPerpPnl,
#[msg("account is frozen")]
AccountIsFrozen,
#[msg("Init Asset Weight can't be negative")]
InitAssetWeightCantBeNegative,
#[msg("has open perp taker fills")]
HasOpenPerpTakerFills,
#[msg("deposit crosses the current group deposit limit")]

View File

@ -1,4 +1,4 @@
use crate::{accounts_zerocopy::AccountInfoRef, state::*};
use crate::{accounts_zerocopy::AccountInfoRef, error::MangoError, state::*};
use anchor_lang::prelude::*;
use fixed::types::I80F48;
@ -6,10 +6,8 @@ use crate::logs::PerpMarketMetaDataLog;
#[derive(Accounts)]
pub struct PerpEditMarket<'info> {
#[account(
has_one = admin
)]
pub group: AccountLoader<'info, Group>,
// group <-> admin relation is checked at #1
pub admin: Signer<'info>,
#[account(
@ -55,17 +53,15 @@ pub fn perp_edit_market(
reduce_only_opt: Option<bool>,
reset_stable_price: bool,
) -> Result<()> {
let group = ctx.accounts.group.load()?;
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
// note: unchanged fields are inline, and match exact definition in perp_register_market
// please maintain, and don't remove, makes it easy to reason about which support admin modification
// unchanged -
// name
// group
let mut require_group_admin = false;
if let Some(oracle_config) = oracle_config_opt {
perp_market.oracle_config = oracle_config.to_oracle_config();
require_group_admin = true;
};
if let Some(oracle) = oracle_opt {
perp_market.oracle = oracle;
@ -78,112 +74,144 @@ pub fn perp_edit_market(
oracle_price.to_num(),
Clock::get()?.unix_timestamp.try_into().unwrap(),
);
require_group_admin = true;
}
// unchanged -
// bids
// asks
// event_queue
// quote_lot_size
// base_lot_size
if let Some(maint_base_asset_weight) = maint_base_asset_weight_opt {
perp_market.maint_base_asset_weight = I80F48::from_num(maint_base_asset_weight);
require_group_admin = true;
}
if let Some(init_base_asset_weight) = init_base_asset_weight_opt {
require_gte!(
init_base_asset_weight,
0.0,
MangoError::InitAssetWeightCantBeNegative
);
let old_init_base_asset_weight = perp_market.init_base_asset_weight;
perp_market.init_base_asset_weight = I80F48::from_num(init_base_asset_weight);
// security admin can only reduce init_base_asset_weight
if old_init_base_asset_weight < perp_market.init_base_asset_weight {
require_group_admin = true;
}
}
if let Some(maint_base_liab_weight) = maint_base_liab_weight_opt {
perp_market.maint_base_liab_weight = I80F48::from_num(maint_base_liab_weight);
require_group_admin = true;
}
if let Some(init_base_liab_weight) = init_base_liab_weight_opt {
perp_market.init_base_liab_weight = I80F48::from_num(init_base_liab_weight);
require_group_admin = true;
}
if let Some(maint_pnl_asset_weight) = maint_pnl_asset_weight_opt {
perp_market.maint_pnl_asset_weight = I80F48::from_num(maint_pnl_asset_weight);
require_group_admin = true;
}
if let Some(init_pnl_asset_weight) = init_pnl_asset_weight_opt {
perp_market.init_pnl_asset_weight = I80F48::from_num(init_pnl_asset_weight);
require_group_admin = true;
}
if let Some(liquidation_fee) = liquidation_fee_opt {
perp_market.liquidation_fee = I80F48::from_num(liquidation_fee);
require_group_admin = true;
}
if let Some(maker_fee) = maker_fee_opt {
perp_market.maker_fee = I80F48::from_num(maker_fee);
require_group_admin = true;
}
if let Some(taker_fee) = taker_fee_opt {
perp_market.taker_fee = I80F48::from_num(taker_fee);
require_group_admin = true;
}
if let Some(min_funding) = min_funding_opt {
perp_market.min_funding = I80F48::from_num(min_funding);
require_group_admin = true;
}
if let Some(max_funding) = max_funding_opt {
perp_market.max_funding = I80F48::from_num(max_funding);
require_group_admin = true;
}
if let Some(impact_quantity) = impact_quantity_opt {
perp_market.impact_quantity = impact_quantity;
require_group_admin = true;
}
if let Some(fee_penalty) = fee_penalty_opt {
perp_market.fee_penalty = fee_penalty;
require_group_admin = true;
}
// unchanged -
// long_funding
// short_funding
// funding_last_updated
// open_interest
// seq_num
// fees_accrued
// bump
if let Some(base_decimals) = base_decimals_opt {
perp_market.base_decimals = base_decimals;
require_group_admin = true;
}
// unchanged -
// perp_market_index
// unchanged -
// registration_time
if let Some(group_insurance_fund) = group_insurance_fund_opt {
perp_market.set_elligible_for_group_insurance_fund(group_insurance_fund);
require_group_admin = true;
}
if let Some(settle_fee_flat) = settle_fee_flat_opt {
perp_market.settle_fee_flat = settle_fee_flat;
require_group_admin = true;
}
if let Some(settle_fee_amount_threshold) = settle_fee_amount_threshold_opt {
perp_market.settle_fee_amount_threshold = settle_fee_amount_threshold;
require_group_admin = true;
}
if let Some(settle_fee_fraction_low_health) = settle_fee_fraction_low_health_opt {
perp_market.settle_fee_fraction_low_health = settle_fee_fraction_low_health;
require_group_admin = true;
}
if let Some(stable_price_delay_interval_seconds) = stable_price_delay_interval_seconds_opt {
// Updating this makes the old delay values slightly inconsistent
perp_market.stable_price_model.delay_interval_seconds = stable_price_delay_interval_seconds;
require_group_admin = true;
}
if let Some(stable_price_delay_growth_limit) = stable_price_delay_growth_limit_opt {
perp_market.stable_price_model.delay_growth_limit = stable_price_delay_growth_limit;
require_group_admin = true;
}
if let Some(stable_price_growth_limit) = stable_price_growth_limit_opt {
perp_market.stable_price_model.stable_growth_limit = stable_price_growth_limit;
require_group_admin = true;
}
if let Some(settle_pnl_limit_factor_opt) = settle_pnl_limit_factor_opt {
perp_market.settle_pnl_limit_factor = settle_pnl_limit_factor_opt;
require_group_admin = true;
}
if let Some(settle_pnl_limit_window_size_ts) = settle_pnl_limit_window_size_ts_opt {
perp_market.settle_pnl_limit_window_size_ts = settle_pnl_limit_window_size_ts;
require_group_admin = true;
}
if let Some(reduce_only) = reduce_only_opt {
perp_market.reduce_only = u8::from(reduce_only);
// security admin can only enable reduce_only
if !reduce_only {
require_group_admin = true;
}
};
// account constraint #1
if require_group_admin {
require!(
group.admin == ctx.accounts.admin.key(),
MangoError::SomeError
);
} else {
require!(
group.admin == ctx.accounts.admin.key()
|| group.security_admin == ctx.accounts.admin.key(),
MangoError::SomeError
);
}
emit!(PerpMarketMetaDataLog {
mango_group: ctx.accounts.group.key(),
perp_market: ctx.accounts.perp_market.key(),

View File

@ -5,6 +5,7 @@ use fixed::types::I80F48;
use super::InterestRateParams;
use crate::accounts_zerocopy::{AccountInfoRef, LoadMutZeroCopyRef};
use crate::error::MangoError;
use crate::state::*;
use crate::logs::TokenMetaDataLog;
@ -15,10 +16,8 @@ use crate::logs::TokenMetaDataLog;
/// in MintInfo order.
#[derive(Accounts)]
pub struct TokenEdit<'info> {
#[account(
has_one = admin
)]
pub group: AccountLoader<'info, Group>,
// group <-> admin relation is checked at #1
pub admin: Signer<'info>,
#[account(
@ -60,27 +59,23 @@ pub fn token_edit(
reset_net_borrow_limit: bool,
reduce_only_opt: Option<bool>,
) -> Result<()> {
let group = ctx.accounts.group.load()?;
let mut mint_info = ctx.accounts.mint_info.load_mut()?;
mint_info.verify_banks_ais(ctx.remaining_accounts)?;
let mut require_group_admin = false;
for ai in ctx.remaining_accounts.iter() {
let mut bank = ai.load_mut::<Bank>()?;
// note: unchanged fields are inline, and match exact definition in register_token
// please maintain, and don't remove, makes it easy to reason about which support admin modification
// unchanged -
// name
// group
// mint
// vault
if let Some(oracle_config) = oracle_config_opt.as_ref() {
bank.oracle_config = oracle_config.to_oracle_config();
require_group_admin = true;
};
if let Some(oracle) = oracle_opt {
bank.oracle = oracle;
mint_info.oracle = oracle;
require_group_admin = true;
}
if reset_stable_price {
require_keys_eq!(bank.oracle, ctx.accounts.oracle.key());
@ -90,21 +85,14 @@ pub fn token_edit(
oracle_price.to_num(),
Clock::get()?.unix_timestamp.try_into().unwrap(),
);
require_group_admin = true;
}
if let Some(group_insurance_fund) = group_insurance_fund_opt {
mint_info.group_insurance_fund = u8::from(group_insurance_fund);
require_group_admin = true;
};
// unchanged -
// deposit_index
// borrow_index
// cached_indexed_total_deposits
// cached_indexed_total_borrows
// indexed_deposits
// indexed_borrows
// last_updated
if let Some(ref interest_rate_params) = interest_rate_params_opt {
// TODO: add a require! verifying relation between the parameters
bank.adjustment_factor = I80F48::from_num(interest_rate_params.adjustment_factor);
@ -113,79 +101,113 @@ pub fn token_edit(
bank.util1 = I80F48::from_num(interest_rate_params.util1);
bank.rate1 = I80F48::from_num(interest_rate_params.rate1);
bank.max_rate = I80F48::from_num(interest_rate_params.max_rate);
require_group_admin = true;
}
// unchanged -
// collected_fees_native
if let Some(loan_origination_fee_rate) = loan_origination_fee_rate_opt {
bank.loan_origination_fee_rate = I80F48::from_num(loan_origination_fee_rate);
require_group_admin = true;
}
if let Some(loan_fee_rate) = loan_fee_rate_opt {
bank.loan_fee_rate = I80F48::from_num(loan_fee_rate);
require_group_admin = true;
}
if let Some(maint_asset_weight) = maint_asset_weight_opt {
bank.maint_asset_weight = I80F48::from_num(maint_asset_weight);
require_group_admin = true;
}
if let Some(init_asset_weight) = init_asset_weight_opt {
require_gte!(
init_asset_weight,
0.0,
MangoError::InitAssetWeightCantBeNegative
);
let old_init_asset_weight = bank.init_asset_weight;
bank.init_asset_weight = I80F48::from_num(init_asset_weight);
// security admin can only reduce init_base_asset_weight
if old_init_asset_weight < bank.init_asset_weight {
require_group_admin = true;
}
}
if let Some(maint_liab_weight) = maint_liab_weight_opt {
bank.maint_liab_weight = I80F48::from_num(maint_liab_weight);
require_group_admin = true;
}
if let Some(init_liab_weight) = init_liab_weight_opt {
bank.init_liab_weight = I80F48::from_num(init_liab_weight);
require_group_admin = true;
}
if let Some(liquidation_fee) = liquidation_fee_opt {
bank.liquidation_fee = I80F48::from_num(liquidation_fee);
require_group_admin = true;
}
if let Some(stable_price_delay_interval_seconds) = stable_price_delay_interval_seconds_opt {
// Updating this makes the old delay values slightly inconsistent
bank.stable_price_model.delay_interval_seconds = stable_price_delay_interval_seconds;
require_group_admin = true;
}
if let Some(stable_price_delay_growth_limit) = stable_price_delay_growth_limit_opt {
bank.stable_price_model.delay_growth_limit = stable_price_delay_growth_limit;
require_group_admin = true;
}
if let Some(stable_price_growth_limit) = stable_price_growth_limit_opt {
bank.stable_price_model.stable_growth_limit = stable_price_growth_limit;
require_group_admin = true;
}
if let Some(min_vault_to_deposits_ratio) = min_vault_to_deposits_ratio_opt {
bank.min_vault_to_deposits_ratio = min_vault_to_deposits_ratio;
require_group_admin = true;
}
if let Some(net_borrow_limit_per_window_quote) = net_borrow_limit_per_window_quote_opt {
bank.net_borrow_limit_per_window_quote = net_borrow_limit_per_window_quote;
require_group_admin = true;
}
if let Some(net_borrow_limit_window_size_ts) = net_borrow_limit_window_size_ts_opt {
bank.net_borrow_limit_window_size_ts = net_borrow_limit_window_size_ts;
require_group_admin = true;
}
if reset_net_borrow_limit {
bank.net_borrows_in_window = 0;
bank.last_net_borrows_window_start_ts = 0;
require_group_admin = true;
}
if let Some(borrow_weight_scale_start_quote) = borrow_weight_scale_start_quote_opt {
bank.borrow_weight_scale_start_quote = borrow_weight_scale_start_quote;
require_group_admin = true;
}
if let Some(deposit_weight_scale_start_quote) = deposit_weight_scale_start_quote_opt {
bank.deposit_weight_scale_start_quote = deposit_weight_scale_start_quote;
require_group_admin = true;
}
if let Some(reduce_only) = reduce_only_opt {
bank.reduce_only = u8::from(reduce_only);
};
// unchanged -
// dust
// flash_loan_token_account_initial
// flash_loan_approved_amount
// token_index
// bump
// mint_decimals
// bank_num
// reserved
// security admin can only enable reduce_only
if !reduce_only {
require_group_admin = true;
}
};
}
// account constraint #1
if require_group_admin {
require!(
group.admin == ctx.accounts.admin.key(),
MangoError::SomeError
);
} else {
require!(
group.admin == ctx.accounts.admin.key()
|| group.security_admin == ctx.accounts.admin.key(),
MangoError::SomeError
);
}
// Assumes that there is at least one bank

View File

@ -7927,11 +7927,16 @@ export type MangoV4 = {
},
{
"code": 6039,
"name": "InitAssetWeightCantBeNegative",
"msg": "Init Asset Weight can't be negative"
},
{
"code": 6040,
"name": "HasOpenPerpTakerFills",
"msg": "has open perp taker fills"
},
{
"code": 6040,
"code": 6041,
"name": "DepositLimit",
"msg": "deposit crosses the current group deposit limit"
}
@ -15867,11 +15872,16 @@ export const IDL: MangoV4 = {
},
{
"code": 6039,
"name": "InitAssetWeightCantBeNegative",
"msg": "Init Asset Weight can't be negative"
},
{
"code": 6040,
"name": "HasOpenPerpTakerFills",
"msg": "has open perp taker fills"
},
{
"code": 6040,
"code": 6041,
"name": "DepositLimit",
"msg": "deposit crosses the current group deposit limit"
}