Compare commits
No commits in common. "7e3a72048ed1ba1c0f87de4d6a5be797f8dc8a79" and "ec2d10af6e822a9caf74bdc8dc5e635578ca9552" have entirely different histories.
7e3a72048e
...
ec2d10af6e
|
@ -193,7 +193,7 @@ pub async fn loop_update_index_and_rate(
|
|||
.map(|token_index| client.context.token(*token_index).name.to_owned())
|
||||
.join(",");
|
||||
|
||||
let mut instructions = PreparedInstructions::new();
|
||||
let mut instructions = vec![];
|
||||
for token_index in token_indices_clone.iter() {
|
||||
let token = client.context.token(*token_index);
|
||||
let banks_for_a_token = token.banks();
|
||||
|
@ -225,14 +225,7 @@ pub async fn loop_update_index_and_rate(
|
|||
|
||||
ix.accounts.append(&mut banks);
|
||||
|
||||
let pix = PreparedInstructions::from_single(
|
||||
ix,
|
||||
client
|
||||
.context
|
||||
.compute_estimates
|
||||
.cu_token_update_index_and_rates,
|
||||
);
|
||||
let sim_result = match client.simulate(pix.clone().to_instructions()).await {
|
||||
let sim_result = match client.simulate(vec![ix.clone()]).await {
|
||||
Ok(response) => response.value,
|
||||
Err(e) => {
|
||||
error!(token.name, "simulation request error: {e:?}");
|
||||
|
@ -245,29 +238,28 @@ pub async fn loop_update_index_and_rate(
|
|||
continue;
|
||||
}
|
||||
|
||||
instructions.append(pix);
|
||||
instructions.push(ix);
|
||||
}
|
||||
|
||||
let pre = Instant::now();
|
||||
let sig_result = client
|
||||
.send_and_confirm_permissionless_tx(instructions.to_instructions())
|
||||
.send_and_confirm_permissionless_tx(instructions)
|
||||
.await;
|
||||
|
||||
let duration_ms = pre.elapsed().as_millis();
|
||||
let confirmation_time = pre.elapsed().as_millis();
|
||||
METRIC_CONFIRMATION_TIMES.observe(confirmation_time as f64);
|
||||
|
||||
if let Err(e) = sig_result {
|
||||
METRIC_UPDATE_TOKENS_FAILURE.inc();
|
||||
info!(
|
||||
"metricName=UpdateTokensV4Failure tokens={} durationMs={} error={}",
|
||||
token_names, duration_ms, e
|
||||
token_names, confirmation_time, e
|
||||
);
|
||||
error!("{:?}", e)
|
||||
} else {
|
||||
METRIC_UPDATE_TOKENS_SUCCESS.inc();
|
||||
METRIC_CONFIRMATION_TIMES.observe(duration_ms as f64);
|
||||
info!(
|
||||
"metricName=UpdateTokensV4Success tokens={} durationMs={}",
|
||||
token_names, duration_ms,
|
||||
token_names, confirmation_time,
|
||||
);
|
||||
info!("{:?}", sig_result);
|
||||
}
|
||||
|
@ -366,37 +358,26 @@ pub async fn loop_consume_events(
|
|||
}),
|
||||
};
|
||||
|
||||
let ixs = PreparedInstructions::from_single(
|
||||
ix,
|
||||
client.context.compute_estimates.cu_perp_consume_events_base
|
||||
+ num_of_events
|
||||
* client
|
||||
.context
|
||||
.compute_estimates
|
||||
.cu_perp_consume_events_per_event,
|
||||
);
|
||||
let sig_result = client
|
||||
.send_and_confirm_permissionless_tx(ixs.to_instructions())
|
||||
.await;
|
||||
let sig_result = client.send_and_confirm_permissionless_tx(vec![ix]).await;
|
||||
|
||||
let duration_ms = pre.elapsed().as_millis();
|
||||
let confirmation_time = pre.elapsed().as_millis();
|
||||
METRIC_CONFIRMATION_TIMES.observe(confirmation_time as f64);
|
||||
|
||||
if let Err(e) = sig_result {
|
||||
METRIC_CONSUME_EVENTS_FAILURE.inc();
|
||||
info!(
|
||||
"metricName=ConsumeEventsV4Failure market={} durationMs={} consumed={} error={}",
|
||||
perp_market.name,
|
||||
duration_ms,
|
||||
confirmation_time,
|
||||
num_of_events,
|
||||
e.to_string()
|
||||
);
|
||||
error!("{:?}", e)
|
||||
} else {
|
||||
METRIC_CONSUME_EVENTS_SUCCESS.inc();
|
||||
METRIC_CONFIRMATION_TIMES.observe(duration_ms as f64);
|
||||
info!(
|
||||
"metricName=ConsumeEventsV4Success market={} durationMs={} consumed={}",
|
||||
perp_market.name, duration_ms, num_of_events,
|
||||
perp_market.name, confirmation_time, num_of_events,
|
||||
);
|
||||
info!("{:?}", sig_result);
|
||||
}
|
||||
|
@ -430,31 +411,25 @@ pub async fn loop_update_funding(
|
|||
),
|
||||
data: anchor_lang::InstructionData::data(&mango_v4::instruction::PerpUpdateFunding {}),
|
||||
};
|
||||
let ixs = PreparedInstructions::from_single(
|
||||
ix,
|
||||
client.context.compute_estimates.cu_perp_update_funding,
|
||||
);
|
||||
let sig_result = client
|
||||
.send_and_confirm_permissionless_tx(ixs.to_instructions())
|
||||
.await;
|
||||
let sig_result = client.send_and_confirm_permissionless_tx(vec![ix]).await;
|
||||
|
||||
let duration_ms = pre.elapsed().as_millis();
|
||||
let confirmation_time = pre.elapsed().as_millis();
|
||||
METRIC_CONFIRMATION_TIMES.observe(confirmation_time as f64);
|
||||
|
||||
if let Err(e) = sig_result {
|
||||
METRIC_UPDATE_FUNDING_FAILURE.inc();
|
||||
error!(
|
||||
"metricName=UpdateFundingV4Error market={} durationMs={} error={}",
|
||||
perp_market.name,
|
||||
duration_ms,
|
||||
confirmation_time,
|
||||
e.to_string()
|
||||
);
|
||||
error!("{:?}", e)
|
||||
} else {
|
||||
METRIC_UPDATE_FUNDING_SUCCESS.inc();
|
||||
METRIC_CONFIRMATION_TIMES.observe(duration_ms as f64);
|
||||
info!(
|
||||
"metricName=UpdateFundingV4Success market={} durationMs={}",
|
||||
perp_market.name, duration_ms,
|
||||
perp_market.name, confirmation_time,
|
||||
);
|
||||
info!("{:?}", sig_result);
|
||||
}
|
||||
|
|
|
@ -324,7 +324,7 @@ impl<'a> LiquidateHelper<'a> {
|
|||
|
||||
let txsig = self
|
||||
.client
|
||||
.send_and_confirm_authority_tx(liq_ixs.to_instructions())
|
||||
.send_and_confirm_owner_tx(liq_ixs.to_instructions())
|
||||
.await
|
||||
.context("sending perp_liq_base_or_positive_pnl_instruction")?;
|
||||
info!(
|
||||
|
@ -370,7 +370,7 @@ impl<'a> LiquidateHelper<'a> {
|
|||
liq_ixs.cu = liq_ixs.cu.max(self.config.compute_limit_for_liq_ix);
|
||||
let txsig = self
|
||||
.client
|
||||
.send_and_confirm_authority_tx(liq_ixs.to_instructions())
|
||||
.send_and_confirm_owner_tx(liq_ixs.to_instructions())
|
||||
.await
|
||||
.context("sending perp_liq_negative_pnl_or_bankruptcy_instruction")?;
|
||||
info!(
|
||||
|
@ -578,7 +578,7 @@ impl<'a> LiquidateHelper<'a> {
|
|||
|
||||
let txsig = self
|
||||
.client
|
||||
.send_and_confirm_authority_tx(liq_ixs.to_instructions())
|
||||
.send_and_confirm_owner_tx(liq_ixs.to_instructions())
|
||||
.await
|
||||
.context("sending liq_token_with_token")?;
|
||||
info!(
|
||||
|
@ -635,7 +635,7 @@ impl<'a> LiquidateHelper<'a> {
|
|||
liq_ixs.cu = liq_ixs.cu.max(self.config.compute_limit_for_liq_ix);
|
||||
let txsig = self
|
||||
.client
|
||||
.send_and_confirm_authority_tx(liq_ixs.to_instructions())
|
||||
.send_and_confirm_owner_tx(liq_ixs.to_instructions())
|
||||
.await
|
||||
.context("sending liq_token_with_token")?;
|
||||
info!(
|
||||
|
|
|
@ -126,16 +126,9 @@ async fn main() -> anyhow::Result<()> {
|
|||
let mango_oracles = group_context
|
||||
.tokens
|
||||
.values()
|
||||
.flat_map(|value| {
|
||||
[
|
||||
value.oracle,
|
||||
value.fallback_context.key,
|
||||
value.fallback_context.quote_key,
|
||||
]
|
||||
})
|
||||
.map(|value| value.oracle)
|
||||
.chain(group_context.perp_markets.values().map(|p| p.oracle))
|
||||
.unique()
|
||||
.filter(|&k| k != Pubkey::default())
|
||||
.collect::<Vec<Pubkey>>();
|
||||
|
||||
let serum_programs = group_context
|
||||
|
|
|
@ -472,7 +472,7 @@ impl Rebalancer {
|
|||
// Imagine SOL at 0.04 USDC-native per SOL-native: Any amounts below 25 SOL-native
|
||||
// would not be worth a single USDC-native.
|
||||
//
|
||||
// To avoid errors, we consider all amounts below 1000 * (1/oracle) dust and don't try
|
||||
// To avoid errors, we consider all amounts below 2 * (1/oracle) dust and don't try
|
||||
// to sell them. Instead they will be withdrawn at the end.
|
||||
// Purchases will aim to purchase slightly more than is needed, such that we can
|
||||
// again withdraw the dust at the end.
|
||||
|
@ -693,7 +693,7 @@ impl Rebalancer {
|
|||
|
||||
let txsig = self
|
||||
.mango_client
|
||||
.send_and_confirm_authority_tx(ixs.to_instructions())
|
||||
.send_and_confirm_owner_tx(ixs.to_instructions())
|
||||
.await?;
|
||||
|
||||
info!(
|
||||
|
@ -765,7 +765,7 @@ impl Rebalancer {
|
|||
|
||||
let txsig = self
|
||||
.mango_client
|
||||
.send_and_confirm_authority_tx(ixs.to_instructions())
|
||||
.send_and_confirm_owner_tx(ixs.to_instructions())
|
||||
.await?;
|
||||
|
||||
info!(
|
||||
|
@ -937,7 +937,7 @@ impl Rebalancer {
|
|||
|
||||
let tx_builder = TransactionBuilder {
|
||||
instructions: ixs.to_instructions(),
|
||||
signers: vec![self.mango_client.authority.clone()],
|
||||
signers: vec![self.mango_client.owner.clone()],
|
||||
..self.mango_client.transaction_builder().await?
|
||||
};
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ async fn report(client: &MangoClient, min_health_ratio: f64) -> anyhow::Result<(
|
|||
})
|
||||
.to_string();
|
||||
|
||||
let signature = client.authority.sign_message(message.as_bytes());
|
||||
let signature = client.owner.sign_message(message.as_bytes());
|
||||
let payload = serde_json::json!({
|
||||
"wallet_pk": client.authority.pubkey().to_string(),
|
||||
"wallet_pk": client.owner.pubkey().to_string(),
|
||||
"message": message,
|
||||
"signature": signature.to_string(),
|
||||
});
|
||||
|
|
|
@ -1211,7 +1211,7 @@ impl Context {
|
|||
let fee_payer = self.mango_client.client.fee_payer();
|
||||
TransactionBuilder {
|
||||
instructions: vec![compute_ix],
|
||||
signers: vec![self.mango_client.authority.clone(), fee_payer],
|
||||
signers: vec![self.mango_client.owner.clone(), fee_payer],
|
||||
..self.mango_client.transaction_builder().await?
|
||||
}
|
||||
};
|
||||
|
|
|
@ -142,7 +142,7 @@ impl State {
|
|||
}
|
||||
|
||||
let txsig = match mango_client
|
||||
.send_and_confirm_authority_tx(instructions.to_instructions())
|
||||
.send_and_confirm_owner_tx(instructions.to_instructions())
|
||||
.await
|
||||
{
|
||||
Ok(v) => v,
|
||||
|
|
|
@ -273,7 +273,7 @@ pub struct MangoClient {
|
|||
// call to refresh banks etc -- if it's backed by websockets, these could just do nothing
|
||||
pub account_fetcher: Arc<dyn AccountFetcher>,
|
||||
|
||||
pub authority: Arc<Keypair>,
|
||||
pub owner: Arc<Keypair>,
|
||||
pub mango_account_address: Pubkey,
|
||||
|
||||
pub context: MangoGroupContext,
|
||||
|
@ -406,7 +406,7 @@ impl MangoClient {
|
|||
pub async fn new_for_existing_account(
|
||||
client: Client,
|
||||
account: Pubkey,
|
||||
authority: Arc<Keypair>,
|
||||
owner: Arc<Keypair>,
|
||||
) -> anyhow::Result<Self> {
|
||||
let rpc = client.new_rpc_async();
|
||||
let account_fetcher = Arc::new(CachedAccountFetcher::new(Arc::new(RpcAccountFetcher {
|
||||
|
@ -415,25 +415,25 @@ impl MangoClient {
|
|||
let mango_account =
|
||||
account_fetcher_fetch_mango_account(&*account_fetcher, &account).await?;
|
||||
let group = mango_account.fixed.group;
|
||||
if mango_account.fixed.owner != authority.pubkey() {
|
||||
if mango_account.fixed.owner != owner.pubkey() {
|
||||
anyhow::bail!(
|
||||
"bad owner for account: expected {} got {}",
|
||||
mango_account.fixed.owner,
|
||||
authority.pubkey()
|
||||
owner.pubkey()
|
||||
);
|
||||
}
|
||||
|
||||
let rpc = client.rpc_async();
|
||||
let group_context = MangoGroupContext::new_from_rpc(&rpc, group).await?;
|
||||
|
||||
Self::new_detail(client, account, authority, group_context, account_fetcher)
|
||||
Self::new_detail(client, account, owner, group_context, account_fetcher)
|
||||
}
|
||||
|
||||
/// Allows control of AccountFetcher and externally created MangoGroupContext
|
||||
pub fn new_detail(
|
||||
client: Client,
|
||||
account: Pubkey,
|
||||
authority: Arc<Keypair>,
|
||||
owner: Arc<Keypair>,
|
||||
// future: maybe pass Arc<MangoGroupContext>, so it can be extenally updated?
|
||||
group_context: MangoGroupContext,
|
||||
account_fetcher: Arc<dyn AccountFetcher>,
|
||||
|
@ -441,15 +441,15 @@ impl MangoClient {
|
|||
Ok(Self {
|
||||
client,
|
||||
account_fetcher,
|
||||
authority,
|
||||
owner,
|
||||
mango_account_address: account,
|
||||
context: group_context,
|
||||
http_client: reqwest::Client::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn authority(&self) -> Pubkey {
|
||||
self.authority.pubkey()
|
||||
pub fn owner(&self) -> Pubkey {
|
||||
self.owner.pubkey()
|
||||
}
|
||||
|
||||
pub fn group(&self) -> Pubkey {
|
||||
|
@ -554,15 +554,12 @@ impl MangoClient {
|
|||
&mango_v4::accounts::TokenDeposit {
|
||||
group: self.group(),
|
||||
account: self.mango_account_address,
|
||||
owner: self.authority(),
|
||||
owner: self.owner(),
|
||||
bank: token.first_bank(),
|
||||
vault: token.first_vault(),
|
||||
oracle: token.oracle,
|
||||
token_account: get_associated_token_address(
|
||||
&self.authority(),
|
||||
&token.mint,
|
||||
),
|
||||
token_authority: self.authority(),
|
||||
token_account: get_associated_token_address(&self.owner(), &token.mint),
|
||||
token_authority: self.owner(),
|
||||
token_program: Token::id(),
|
||||
},
|
||||
None,
|
||||
|
@ -577,8 +574,7 @@ impl MangoClient {
|
|||
},
|
||||
self.instruction_cu(health_cu),
|
||||
);
|
||||
self.send_and_confirm_authority_tx(ixs.to_instructions())
|
||||
.await
|
||||
self.send_and_confirm_owner_tx(ixs.to_instructions()).await
|
||||
}
|
||||
|
||||
/// Assert that health of account is > N
|
||||
|
@ -671,8 +667,8 @@ impl MangoClient {
|
|||
let ixs = PreparedInstructions::from_vec(
|
||||
vec![
|
||||
spl_associated_token_account::instruction::create_associated_token_account_idempotent(
|
||||
&self.authority(),
|
||||
&account.fixed.owner,
|
||||
&self.owner(),
|
||||
&self.owner(),
|
||||
&mint,
|
||||
&Token::id(),
|
||||
),
|
||||
|
@ -683,12 +679,12 @@ impl MangoClient {
|
|||
&mango_v4::accounts::TokenWithdraw {
|
||||
group: self.group(),
|
||||
account: self.mango_account_address,
|
||||
owner: self.authority(),
|
||||
owner: self.owner(),
|
||||
bank: token.first_bank(),
|
||||
vault: token.first_vault(),
|
||||
oracle: token.oracle,
|
||||
token_account: get_associated_token_address(
|
||||
&account.fixed.owner,
|
||||
&self.owner(),
|
||||
&token.mint,
|
||||
),
|
||||
token_program: Token::id(),
|
||||
|
@ -719,8 +715,7 @@ impl MangoClient {
|
|||
let ixs = self
|
||||
.token_withdraw_instructions(&account, mint, amount, allow_borrow)
|
||||
.await?;
|
||||
self.send_and_confirm_authority_tx(ixs.to_instructions())
|
||||
.await
|
||||
self.send_and_confirm_owner_tx(ixs.to_instructions()).await
|
||||
}
|
||||
|
||||
pub async fn bank_oracle_price(&self, token_index: TokenIndex) -> anyhow::Result<I80F48> {
|
||||
|
@ -772,8 +767,8 @@ impl MangoClient {
|
|||
serum_program: s3.serum_program,
|
||||
serum_market_external: s3.serum_market_external,
|
||||
open_orders,
|
||||
owner: self.authority(),
|
||||
sol_destination: self.authority(),
|
||||
owner: self.owner(),
|
||||
sol_destination: self.owner(),
|
||||
},
|
||||
None,
|
||||
),
|
||||
|
@ -788,8 +783,7 @@ impl MangoClient {
|
|||
pub async fn serum3_close_open_orders(&self, name: &str) -> anyhow::Result<Signature> {
|
||||
let market_index = self.context.serum3_market_index(name);
|
||||
let ix = self.serum3_close_open_orders_instruction(market_index);
|
||||
self.send_and_confirm_authority_tx(ix.to_instructions())
|
||||
.await
|
||||
self.send_and_confirm_owner_tx(ix.to_instructions()).await
|
||||
}
|
||||
|
||||
pub fn serum3_create_open_orders_instruction(
|
||||
|
@ -811,8 +805,8 @@ impl MangoClient {
|
|||
serum_program: s3.serum_program,
|
||||
serum_market_external: s3.serum_market_external,
|
||||
open_orders,
|
||||
owner: self.authority(),
|
||||
payer: self.authority(),
|
||||
owner: self.owner(),
|
||||
payer: self.owner(),
|
||||
system_program: System::id(),
|
||||
rent: sysvar::rent::id(),
|
||||
},
|
||||
|
@ -844,7 +838,7 @@ impl MangoClient {
|
|||
pub async fn serum3_create_open_orders(&self, name: &str) -> anyhow::Result<Signature> {
|
||||
let market_index = self.context.serum3_market_index(name);
|
||||
let ix = self.serum3_create_open_orders_instruction(market_index);
|
||||
self.send_and_confirm_authority_tx(vec![ix]).await
|
||||
self.send_and_confirm_owner_tx(vec![ix]).await
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -902,7 +896,7 @@ impl MangoClient {
|
|||
market_base_vault: s3.coin_vault,
|
||||
market_quote_vault: s3.pc_vault,
|
||||
market_vault_signer: s3.vault_signer,
|
||||
owner: self.authority(),
|
||||
owner: self.owner(),
|
||||
token_program: Token::id(),
|
||||
},
|
||||
None,
|
||||
|
@ -1165,8 +1159,7 @@ impl MangoClient {
|
|||
let mut ixs = PreparedInstructions::new();
|
||||
ixs.append(create_or_replace_ixs);
|
||||
ixs.append(place_order_ixs);
|
||||
self.send_and_confirm_authority_tx(ixs.to_instructions())
|
||||
.await
|
||||
self.send_and_confirm_owner_tx(ixs.to_instructions()).await
|
||||
}
|
||||
|
||||
pub async fn serum3_settle_funds(&self, name: &str) -> anyhow::Result<Signature> {
|
||||
|
@ -1179,8 +1172,7 @@ impl MangoClient {
|
|||
let open_orders = account.serum3_orders(market_index).unwrap().open_orders;
|
||||
|
||||
let ix = self.serum3_settle_funds_instruction(s3, base, quote, open_orders);
|
||||
self.send_and_confirm_authority_tx(ix.to_instructions())
|
||||
.await
|
||||
self.send_and_confirm_owner_tx(ix.to_instructions()).await
|
||||
}
|
||||
|
||||
pub fn serum3_settle_funds_instruction(
|
||||
|
@ -1208,7 +1200,7 @@ impl MangoClient {
|
|||
market_base_vault: s3.coin_vault,
|
||||
market_quote_vault: s3.pc_vault,
|
||||
market_vault_signer: s3.vault_signer,
|
||||
owner: self.authority(),
|
||||
owner: self.owner(),
|
||||
token_program: Token::id(),
|
||||
},
|
||||
v2: mango_v4::accounts::Serum3SettleFundsV2Extra {
|
||||
|
@ -1252,7 +1244,7 @@ impl MangoClient {
|
|||
serum_market: s3.address,
|
||||
serum_program: s3.serum_program,
|
||||
serum_market_external: s3.serum_market_external,
|
||||
owner: self.authority(),
|
||||
owner: self.owner(),
|
||||
},
|
||||
None,
|
||||
),
|
||||
|
@ -1373,7 +1365,7 @@ impl MangoClient {
|
|||
accounts: {
|
||||
let mut ams = anchor_lang::ToAccountMetas::to_account_metas(
|
||||
&mango_v4::accounts::OpenbookV2LiqForceCancelOrders {
|
||||
payer: self.authority(),
|
||||
payer: self.owner(),
|
||||
group: self.group(),
|
||||
account: *liqee.0,
|
||||
open_orders: *open_orders,
|
||||
|
@ -1447,7 +1439,7 @@ impl MangoClient {
|
|||
market_bids: s3.bids,
|
||||
market_asks: s3.asks,
|
||||
market_event_queue: s3.event_q,
|
||||
owner: self.authority(),
|
||||
owner: self.owner(),
|
||||
},
|
||||
None,
|
||||
)
|
||||
|
@ -1457,7 +1449,7 @@ impl MangoClient {
|
|||
order_id,
|
||||
}),
|
||||
};
|
||||
self.send_and_confirm_authority_tx(vec![ix]).await
|
||||
self.send_and_confirm_owner_tx(vec![ix]).await
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -1510,7 +1502,7 @@ impl MangoClient {
|
|||
&mango_v4::accounts::PerpPlaceOrder {
|
||||
group: self.group(),
|
||||
account: self.mango_account_address,
|
||||
owner: self.authority(),
|
||||
owner: self.owner(),
|
||||
perp_market: perp.address,
|
||||
bids: perp.bids,
|
||||
asks: perp.asks,
|
||||
|
@ -1615,8 +1607,7 @@ impl MangoClient {
|
|||
self_trade_behavior,
|
||||
)
|
||||
.await?;
|
||||
self.send_and_confirm_authority_tx(ixs.to_instructions())
|
||||
.await
|
||||
self.send_and_confirm_owner_tx(ixs.to_instructions()).await
|
||||
}
|
||||
|
||||
pub fn perp_cancel_all_orders_instruction(
|
||||
|
@ -1634,7 +1625,7 @@ impl MangoClient {
|
|||
&mango_v4::accounts::PerpCancelAllOrders {
|
||||
group: self.group(),
|
||||
account: self.mango_account_address,
|
||||
owner: self.authority(),
|
||||
owner: self.owner(),
|
||||
perp_market: perp.address,
|
||||
bids: perp.bids,
|
||||
asks: perp.asks,
|
||||
|
@ -1659,8 +1650,7 @@ impl MangoClient {
|
|||
let ixs = self
|
||||
.perp_deactivate_position_instruction(market_index)
|
||||
.await?;
|
||||
self.send_and_confirm_authority_tx(ixs.to_instructions())
|
||||
.await
|
||||
self.send_and_confirm_owner_tx(ixs.to_instructions()).await
|
||||
}
|
||||
|
||||
async fn perp_deactivate_position_instruction(
|
||||
|
@ -1677,7 +1667,7 @@ impl MangoClient {
|
|||
&mango_v4::accounts::PerpDeactivatePosition {
|
||||
group: self.group(),
|
||||
account: self.mango_account_address,
|
||||
owner: self.authority(),
|
||||
owner: self.owner(),
|
||||
perp_market: perp.address,
|
||||
},
|
||||
None,
|
||||
|
@ -1720,7 +1710,7 @@ impl MangoClient {
|
|||
&mango_v4::accounts::PerpSettlePnl {
|
||||
group: self.group(),
|
||||
settler: self.mango_account_address,
|
||||
settler_owner: self.authority(),
|
||||
settler_owner: self.owner(),
|
||||
perp_market: perp.address,
|
||||
account_a: *account_a.0,
|
||||
account_b: *account_b.0,
|
||||
|
@ -1824,7 +1814,7 @@ impl MangoClient {
|
|||
perp_market: perp.address,
|
||||
oracle: perp.oracle,
|
||||
liqor: self.mango_account_address,
|
||||
liqor_owner: self.authority(),
|
||||
liqor_owner: self.owner(),
|
||||
liqee: *liqee.0,
|
||||
settle_bank: settle_token_info.first_bank(),
|
||||
settle_vault: settle_token_info.first_vault(),
|
||||
|
@ -1884,7 +1874,7 @@ impl MangoClient {
|
|||
perp_market: perp.address,
|
||||
oracle: perp.oracle,
|
||||
liqor: self.mango_account_address,
|
||||
liqor_owner: self.authority(),
|
||||
liqor_owner: self.owner(),
|
||||
liqee: *liqee.0,
|
||||
settle_bank: settle_token_info.first_bank(),
|
||||
settle_vault: settle_token_info.first_vault(),
|
||||
|
@ -1992,7 +1982,7 @@ impl MangoClient {
|
|||
group: self.group(),
|
||||
liqee: *liqee.0,
|
||||
liqor: self.mango_account_address,
|
||||
liqor_owner: self.authority(),
|
||||
liqor_owner: self.owner(),
|
||||
},
|
||||
None,
|
||||
);
|
||||
|
@ -2052,7 +2042,7 @@ impl MangoClient {
|
|||
group: self.group(),
|
||||
liqee: *liqee.0,
|
||||
liqor: self.mango_account_address,
|
||||
liqor_owner: self.authority(),
|
||||
liqor_owner: self.owner(),
|
||||
liab_mint_info: liab_info.mint_info_address,
|
||||
quote_vault: insurance_info.first_vault(),
|
||||
insurance_vault: group.insurance_vault,
|
||||
|
@ -2112,7 +2102,7 @@ impl MangoClient {
|
|||
group: self.group(),
|
||||
liqee: *liqee.0,
|
||||
liqor: self.mango_account_address,
|
||||
liqor_authority: self.authority(),
|
||||
liqor_authority: self.owner(),
|
||||
},
|
||||
None,
|
||||
);
|
||||
|
@ -2159,7 +2149,7 @@ impl MangoClient {
|
|||
group: self.group(),
|
||||
liqee: *account.0,
|
||||
liqor: self.mango_account_address,
|
||||
liqor_authority: self.authority(),
|
||||
liqor_authority: self.owner(),
|
||||
},
|
||||
None,
|
||||
);
|
||||
|
@ -2363,7 +2353,7 @@ impl MangoClient {
|
|||
self.context.compute_estimates.cu_per_mango_instruction + health_cu
|
||||
}
|
||||
|
||||
pub async fn send_and_confirm_authority_tx(
|
||||
pub async fn send_and_confirm_owner_tx(
|
||||
&self,
|
||||
instructions: Vec<Instruction>,
|
||||
) -> anyhow::Result<Signature> {
|
||||
|
@ -2371,7 +2361,7 @@ impl MangoClient {
|
|||
instructions,
|
||||
..self.transaction_builder().await?
|
||||
};
|
||||
tx_builder.signers.push(self.authority.clone());
|
||||
tx_builder.signers.push(self.owner.clone());
|
||||
tx_builder.send_and_confirm(&self.client).await
|
||||
}
|
||||
|
||||
|
|
|
@ -145,10 +145,6 @@ pub struct ComputeEstimates {
|
|||
pub cu_per_charge_collateral_fees_token: u32,
|
||||
pub cu_for_sequence_check: u32,
|
||||
pub cu_per_associated_token_account_creation: u32,
|
||||
pub cu_perp_update_funding: u32,
|
||||
pub cu_perp_consume_events_base: u32,
|
||||
pub cu_perp_consume_events_per_event: u32,
|
||||
pub cu_token_update_index_and_rates: u32,
|
||||
}
|
||||
|
||||
impl Default for ComputeEstimates {
|
||||
|
@ -177,10 +173,6 @@ impl Default for ComputeEstimates {
|
|||
// measured around 8k, see test_basics
|
||||
cu_for_sequence_check: 10_000,
|
||||
cu_per_associated_token_account_creation: 21_000,
|
||||
cu_perp_update_funding: 40_000,
|
||||
cu_perp_consume_events_base: 10_000,
|
||||
cu_perp_consume_events_per_event: 18_000,
|
||||
cu_token_update_index_and_rates: 90_000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -233,14 +233,14 @@ impl<'a> JupiterV6<'a> {
|
|||
.map(util::to_writable_account_meta)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let authority = self.mango_client.authority();
|
||||
let owner = self.mango_client.owner();
|
||||
let account = &self.mango_client.mango_account().await?;
|
||||
|
||||
let token_ams = [source_token.mint, target_token.mint]
|
||||
.into_iter()
|
||||
.map(|mint| {
|
||||
util::to_writable_account_meta(
|
||||
anchor_spl::associated_token::get_associated_token_address(&authority, &mint),
|
||||
anchor_spl::associated_token::get_associated_token_address(&owner, &mint),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
@ -277,7 +277,7 @@ impl<'a> JupiterV6<'a> {
|
|||
.post(format!("{}/swap-instructions", config.jupiter_v6_url))
|
||||
.query(&query_args)
|
||||
.json(&SwapRequest {
|
||||
user_public_key: authority.to_string(),
|
||||
user_public_key: owner.to_string(),
|
||||
wrap_and_unwrap_sol: false,
|
||||
use_shared_accounts: true,
|
||||
fee_account: None,
|
||||
|
@ -308,8 +308,8 @@ impl<'a> JupiterV6<'a> {
|
|||
// Ensure the source token account is created (jupiter takes care of the output account)
|
||||
instructions.push(
|
||||
spl_associated_token_account::instruction::create_associated_token_account_idempotent(
|
||||
&authority,
|
||||
&authority,
|
||||
&owner,
|
||||
&owner,
|
||||
&source_token.mint,
|
||||
&Token::id(),
|
||||
),
|
||||
|
@ -321,7 +321,7 @@ impl<'a> JupiterV6<'a> {
|
|||
let mut ams = anchor_lang::ToAccountMetas::to_account_metas(
|
||||
&mango_v4::accounts::FlashLoanBegin {
|
||||
account: self.mango_client.mango_account_address,
|
||||
owner: authority,
|
||||
owner,
|
||||
token_program: Token::id(),
|
||||
instructions: solana_sdk::sysvar::instructions::id(),
|
||||
},
|
||||
|
@ -344,7 +344,7 @@ impl<'a> JupiterV6<'a> {
|
|||
let mut ams = anchor_lang::ToAccountMetas::to_account_metas(
|
||||
&mango_v4::accounts::FlashLoanEnd {
|
||||
account: self.mango_client.mango_account_address,
|
||||
owner: authority,
|
||||
owner,
|
||||
token_program: Token::id(),
|
||||
},
|
||||
None,
|
||||
|
@ -379,13 +379,13 @@ impl<'a> JupiterV6<'a> {
|
|||
.await?;
|
||||
address_lookup_tables.extend(jup_alts.into_iter());
|
||||
|
||||
let payer = authority; // maybe use fee_payer? but usually it's the same
|
||||
let payer = owner; // maybe use fee_payer? but usually it's the same
|
||||
|
||||
Ok(TransactionBuilder {
|
||||
instructions,
|
||||
address_lookup_tables,
|
||||
payer,
|
||||
signers: vec![self.mango_client.authority.clone()],
|
||||
signers: vec![self.mango_client.owner.clone()],
|
||||
config: self
|
||||
.mango_client
|
||||
.client
|
||||
|
|
|
@ -124,14 +124,14 @@ impl<'a> Sanctum<'a> {
|
|||
.map(util::to_writable_account_meta)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let authority = self.mango_client.authority();
|
||||
let owner = self.mango_client.owner();
|
||||
let account = &self.mango_client.mango_account().await?;
|
||||
|
||||
let token_ams = [source_token.mint, target_token.mint]
|
||||
.into_iter()
|
||||
.map(|mint| {
|
||||
util::to_writable_account_meta(
|
||||
anchor_spl::associated_token::get_associated_token_address(&authority, &mint),
|
||||
anchor_spl::associated_token::get_associated_token_address(&owner, &mint),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
@ -176,7 +176,7 @@ impl<'a> Sanctum<'a> {
|
|||
input: input_mint.to_string(),
|
||||
mode: "ExactIn".to_string(),
|
||||
output_lst_mint: output_mint.to_string(),
|
||||
signer: authority.to_string(),
|
||||
signer: owner.to_string(),
|
||||
swap_src: quote.swap_src.clone(),
|
||||
})
|
||||
.timeout(self.timeout_duration)
|
||||
|
@ -244,8 +244,8 @@ impl<'a> Sanctum<'a> {
|
|||
// Ensure the source token account is created (sanctum takes care of the output account)
|
||||
instructions.push(
|
||||
spl_associated_token_account::instruction::create_associated_token_account_idempotent(
|
||||
&authority,
|
||||
&authority,
|
||||
&owner,
|
||||
&owner,
|
||||
&source_token.mint,
|
||||
&Token::id(),
|
||||
),
|
||||
|
@ -257,7 +257,7 @@ impl<'a> Sanctum<'a> {
|
|||
let mut ams = anchor_lang::ToAccountMetas::to_account_metas(
|
||||
&mango_v4::accounts::FlashLoanBegin {
|
||||
account: self.mango_client.mango_account_address,
|
||||
owner: authority,
|
||||
owner,
|
||||
token_program: Token::id(),
|
||||
instructions: solana_sdk::sysvar::instructions::id(),
|
||||
},
|
||||
|
@ -284,7 +284,7 @@ impl<'a> Sanctum<'a> {
|
|||
let mut ams = anchor_lang::ToAccountMetas::to_account_metas(
|
||||
&mango_v4::accounts::FlashLoanEnd {
|
||||
account: self.mango_client.mango_account_address,
|
||||
owner: authority,
|
||||
owner,
|
||||
token_program: Token::id(),
|
||||
},
|
||||
None,
|
||||
|
@ -308,13 +308,13 @@ impl<'a> Sanctum<'a> {
|
|||
let mut address_lookup_tables = self.mango_client.mango_address_lookup_tables().await?;
|
||||
address_lookup_tables.extend(sanctum_alts.into_iter());
|
||||
|
||||
let payer = authority; // maybe use fee_payer? but usually it's the same
|
||||
let payer = owner; // maybe use fee_payer? but usually it's the same
|
||||
|
||||
Ok(TransactionBuilder {
|
||||
instructions,
|
||||
address_lookup_tables,
|
||||
payer,
|
||||
signers: vec![self.mango_client.authority.clone()],
|
||||
signers: vec![self.mango_client.owner.clone()],
|
||||
config: self
|
||||
.mango_client
|
||||
.client
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@blockworks-foundation/mango-v4",
|
||||
"version": "0.24.0",
|
||||
"version": "0.23.1",
|
||||
"description": "Typescript Client for mango-v4 program.",
|
||||
"repository": "https://github.com/blockworks-foundation/mango-v4",
|
||||
"author": {
|
||||
|
@ -27,8 +27,8 @@
|
|||
"lint": "eslint ./ts/client/src --ext ts --ext tsx --ext js --quiet",
|
||||
"typecheck": "tsc --noEmit --pretty",
|
||||
"prepublishOnly": "yarn validate && yarn build",
|
||||
"deduplicate": "npx yarn-deduplicate --list --fail",
|
||||
"validate": "yarn lint && yarn format",
|
||||
"deduplicate": "npx yarn-deduplicate --list --fail",
|
||||
"prepare": "yarn build"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -64,7 +64,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@blockworks-foundation/mango-v4-settings": "0.14.15",
|
||||
"@blockworks-foundation/mangolana": "0.0.15",
|
||||
"@blockworks-foundation/mangolana": "0.0.14",
|
||||
"@coral-xyz/anchor": "^0.29.0",
|
||||
"@openbook-dex/openbook-v2": "^0.1.2",
|
||||
"@project-serum/serum": "0.13.65",
|
||||
|
|
|
@ -89,7 +89,7 @@ impl OpenbookV2Cookie {
|
|||
pub async fn consume_spot_events(&self, spot_market_cookie: &OpenbookMarketCookie, limit: u8) {
|
||||
let event_heap = self
|
||||
.solana
|
||||
.get_account_boxed::<EventHeap>(spot_market_cookie.event_heap)
|
||||
.get_account::<EventHeap>(spot_market_cookie.event_heap)
|
||||
.await;
|
||||
let to_consume = event_heap
|
||||
.iter()
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@coral-xyz/anchor';
|
||||
import { Commitment, Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import * as fs from 'fs';
|
||||
import { MangoAccount, MangoClient, TokenIndex } from '../src';
|
||||
|
||||
const outputCsvName = 'mango-boost-switchboard-snapshot.csv';
|
||||
|
||||
const script = async () => {
|
||||
const connection = new Connection(
|
||||
process.env.MB_CLUSTER_URL!,
|
||||
'confirmed' as Commitment,
|
||||
);
|
||||
const kp = Keypair.generate();
|
||||
const wallet = new Wallet(kp);
|
||||
const provider = new AnchorProvider(connection, wallet, {});
|
||||
|
||||
const CLUSTER = 'mainnet-beta';
|
||||
const client = MangoClient.connect(
|
||||
provider as any,
|
||||
CLUSTER,
|
||||
new PublicKey('zF2vSz6V9g1YHGmfrzsY497NJzbRr84QUrPry4bLQ25'),
|
||||
{ idsSource: 'get-program-accounts' },
|
||||
);
|
||||
|
||||
const groupKey = new PublicKey(
|
||||
'AKeMSYiJekyKfwCc3CUfVNDVAiqk9FfbQVMY3G7RUZUf',
|
||||
);
|
||||
const group = await client.getGroup(groupKey);
|
||||
const mangoAccounts = await client.getAllMangoAccounts(group, false);
|
||||
|
||||
const sbTokenIndexes = [
|
||||
1, // JLP
|
||||
];
|
||||
|
||||
const sbBanks = await Promise.all(
|
||||
sbTokenIndexes.map((i) => group.getFirstBankByTokenIndex(i as TokenIndex)),
|
||||
);
|
||||
|
||||
function accountHasSbTokens(m: MangoAccount): boolean {
|
||||
for (const bank of sbBanks) {
|
||||
if (m.getTokenBalanceUi(bank) !== 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const sbAccounts = mangoAccounts.filter((m) => accountHasSbTokens(m));
|
||||
|
||||
const resultMap = new Map<string, [number, number]>();
|
||||
for (const account of sbAccounts) {
|
||||
let deposits = 0;
|
||||
let borrows = 0;
|
||||
|
||||
for (const bank of sbBanks) {
|
||||
deposits += account.getTokenDepositsUi(bank) * bank.uiPrice;
|
||||
borrows += account.getTokenBorrowsUi(bank) * bank.uiPrice;
|
||||
}
|
||||
|
||||
const wallet = account.owner.toBase58();
|
||||
|
||||
const existingValue = resultMap.get(account.owner.toBase58());
|
||||
if (existingValue) {
|
||||
const [d, b] = existingValue;
|
||||
resultMap.set(wallet, [d + deposits, b + borrows]);
|
||||
} else {
|
||||
resultMap.set(wallet, [deposits, borrows]);
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(resultMap.entries())
|
||||
writeMapToCsv(resultMap, outputCsvName);
|
||||
};
|
||||
|
||||
script();
|
||||
|
||||
function writeMapToCsv(
|
||||
map: Map<string, [number, number]>,
|
||||
filename: string,
|
||||
): void {
|
||||
let csv = 'wallet,deposits_value,borrows_value\n';
|
||||
for (let [wallet, [deposits, borrows]] of map) {
|
||||
csv += `${wallet},${deposits},${borrows}\n`;
|
||||
}
|
||||
|
||||
fs.writeFileSync(filename, csv);
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@coral-xyz/anchor';
|
||||
import { Commitment, Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import * as fs from 'fs';
|
||||
import { MANGO_V4_ID, MangoAccount, MangoClient, TokenIndex } from '../src';
|
||||
|
||||
const outputCsvName = 'mango-switchboard-snapshot.csv';
|
||||
|
||||
const script = async () => {
|
||||
const connection = new Connection(
|
||||
process.env.MB_CLUSTER_URL!,
|
||||
'confirmed' as Commitment,
|
||||
);
|
||||
const kp = Keypair.generate();
|
||||
const wallet = new Wallet(kp);
|
||||
const provider = new AnchorProvider(connection, wallet, {});
|
||||
|
||||
const CLUSTER = 'mainnet-beta';
|
||||
const client = MangoClient.connect(
|
||||
provider as any,
|
||||
CLUSTER,
|
||||
MANGO_V4_ID[CLUSTER],
|
||||
{ idsSource: 'api' },
|
||||
);
|
||||
|
||||
const groupKey = new PublicKey(
|
||||
'78b8f4cGCwmZ9ysPFMWLaLTkkaYnUjwMJYStWe5RTSSX',
|
||||
);
|
||||
const group = await client.getGroup(groupKey);
|
||||
const mangoAccounts = await client.getAllMangoAccounts(group, false);
|
||||
|
||||
const sbTokenIndexes = [
|
||||
// https://api.mngo.cloud/data/v4/group-metadata
|
||||
791, // NOS
|
||||
916, // STEP
|
||||
881, // GECKO
|
||||
889, // Moutai
|
||||
743, // JLP
|
||||
669, // GUAC
|
||||
455, // DUAL
|
||||
616, // ALL
|
||||
848, // WEN
|
||||
];
|
||||
|
||||
const sbBanks = await Promise.all(
|
||||
sbTokenIndexes.map((i) => group.getFirstBankByTokenIndex(i as TokenIndex)),
|
||||
);
|
||||
|
||||
function accountHasSbTokens(m: MangoAccount): boolean {
|
||||
for (const bank of sbBanks) {
|
||||
if (m.getTokenBalanceUi(bank) !== 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const sbAccounts = mangoAccounts.filter((m) => accountHasSbTokens(m));
|
||||
|
||||
const resultMap = new Map<string, [number, number]>();
|
||||
for (const account of sbAccounts) {
|
||||
let deposits = 0;
|
||||
let borrows = 0;
|
||||
|
||||
for (const bank of sbBanks) {
|
||||
deposits += account.getTokenDepositsUi(bank) * bank.uiPrice;
|
||||
borrows += account.getTokenBorrowsUi(bank) * bank.uiPrice;
|
||||
}
|
||||
|
||||
const wallet = account.owner.toBase58();
|
||||
|
||||
const existingValue = resultMap.get(account.owner.toBase58());
|
||||
if (existingValue) {
|
||||
const [d, b] = existingValue;
|
||||
resultMap.set(wallet, [d + deposits, b + borrows]);
|
||||
} else {
|
||||
resultMap.set(wallet, [deposits, borrows]);
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(resultMap.entries())
|
||||
writeMapToCsv(resultMap, outputCsvName);
|
||||
};
|
||||
|
||||
script();
|
||||
|
||||
function writeMapToCsv(
|
||||
map: Map<string, [number, number]>,
|
||||
filename: string,
|
||||
): void {
|
||||
let csv = 'wallet,deposits_value,borrows_value\n';
|
||||
for (let [wallet, [deposits, borrows]] of map) {
|
||||
csv += `${wallet},${deposits},${borrows}\n`;
|
||||
}
|
||||
|
||||
fs.writeFileSync(filename, csv);
|
||||
}
|
|
@ -248,10 +248,7 @@ async function fullMarketMaker() {
|
|||
CLUSTER,
|
||||
MANGO_V4_ID[CLUSTER],
|
||||
{
|
||||
idsSource: 'api',
|
||||
fallbackOracleConfig: [
|
||||
new PublicKey('Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD'), // USDC pyth oracle
|
||||
],
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@coral-xyz/anchor';
|
||||
import { Cluster, Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { TokenIndex } from '../src/accounts/bank';
|
||||
import { MangoClient } from '../src/client';
|
||||
import { MANGO_V4_ID } from '../src/constants';
|
||||
|
||||
const CLUSTER: Cluster =
|
||||
(process.env.CLUSTER_OVERRIDE as Cluster) || 'mainnet-beta';
|
||||
const CLUSTER_URL =
|
||||
process.env.CLUSTER_URL_OVERRIDE || process.env.MB_CLUSTER_URL;
|
||||
const USER_KEYPAIR =
|
||||
process.env.USER_KEYPAIR_OVERRIDE || process.env.MB_PAYER_KEYPAIR;
|
||||
const MANGO_ACCOUNT_PK = process.env.MANGO_ACCOUNT_PK;
|
||||
const TOKEN_INDEX = Number(process.env.TOKEN_INDEX) as TokenIndex;
|
||||
|
||||
async function tokenDeposit(): Promise<void> {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(CLUSTER_URL!, options);
|
||||
const user = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(
|
||||
process.env.KEYPAIR || fs.readFileSync(USER_KEYPAIR!, 'utf-8'),
|
||||
),
|
||||
),
|
||||
);
|
||||
const userWallet = new Wallet(user);
|
||||
const userProvider = new AnchorProvider(connection, userWallet, options);
|
||||
const client = await MangoClient.connect(
|
||||
userProvider,
|
||||
CLUSTER,
|
||||
MANGO_V4_ID[CLUSTER],
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
prioritizationFee: 80000,
|
||||
},
|
||||
);
|
||||
|
||||
const liqor = await client.getMangoAccount(new PublicKey(MANGO_ACCOUNT_PK!));
|
||||
const group = await client.getGroup(liqor.group);
|
||||
|
||||
const forceCloseTokenBank = group.getFirstBankByTokenIndex(TOKEN_INDEX);
|
||||
|
||||
const mangoAccountsWithBorrows = (
|
||||
await client.getAllMangoAccounts(group)
|
||||
).filter((a) => a.getTokenBalanceUi(forceCloseTokenBank) < 0);
|
||||
|
||||
for (const liqee of mangoAccountsWithBorrows) {
|
||||
console.log(
|
||||
`liqee ${liqee.publicKey}, ${liqee.getTokenBalanceUi(
|
||||
forceCloseTokenBank,
|
||||
)}`,
|
||||
);
|
||||
|
||||
const sig = await client.tokenDeposit(
|
||||
group,
|
||||
liqee,
|
||||
forceCloseTokenBank.mint,
|
||||
-liqee.getTokenBalanceUi(forceCloseTokenBank),
|
||||
);
|
||||
|
||||
console.log(
|
||||
` - tokendeposit, sig https://explorer.solana.com/tx/${
|
||||
sig.signature
|
||||
}?cluster=${CLUSTER == 'devnet' ? 'devnet' : ''}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
tokenDeposit();
|
|
@ -30,10 +30,12 @@ import { MANGO_V4_MAIN_GROUP as MANGO_V4_PRIMARY_GROUP } from '../src/constants'
|
|||
import {
|
||||
LiqorPriceImpact,
|
||||
buildGroupGrid,
|
||||
findLargestAssetBatchUi,
|
||||
getEquityForMangoAccounts,
|
||||
} from '../src/risk';
|
||||
import {
|
||||
buildFetch,
|
||||
toNative,
|
||||
toNativeI80F48ForQuote,
|
||||
toUiDecimalsForQuote,
|
||||
} from '../src/utils';
|
||||
|
@ -165,20 +167,20 @@ async function updateTokenParams(): Promise<void> {
|
|||
}
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
// if (false) {
|
||||
// // Deposit limits header
|
||||
// console.log(
|
||||
// `${'name'.padStart(20)} ${'maxLiqBatchUi'.padStart(
|
||||
// 15,
|
||||
// )} ${'maxLiqBatchUi'.padStart(15)} ${'sellImpact'.padStart(
|
||||
// 12,
|
||||
// )}$ ${'pi %'.padStart(12)}% ${'aNDUi'.padStart(
|
||||
// 20,
|
||||
// )}${'aNDQuoteUi'.padStart(20)} ${'uiDeposits'.padStart(
|
||||
// 12,
|
||||
// )} ${'uiDeposits'.padStart(12)} ${'depositLimitsUi'.padStart(12)}`,
|
||||
// );
|
||||
// }
|
||||
if (false) {
|
||||
// Deposit limits header
|
||||
console.log(
|
||||
`${'name'.padStart(20)} ${'maxLiqBatchUi'.padStart(
|
||||
15,
|
||||
)} ${'maxLiqBatchUi'.padStart(15)} ${'sellImpact'.padStart(
|
||||
12,
|
||||
)}$ ${'pi %'.padStart(12)}% ${'aNDUi'.padStart(
|
||||
20,
|
||||
)}${'aNDQuoteUi'.padStart(20)} ${'uiDeposits'.padStart(
|
||||
12,
|
||||
)} ${'uiDeposits'.padStart(12)} ${'depositLimitsUi'.padStart(12)}`,
|
||||
);
|
||||
}
|
||||
|
||||
Array.from(group.banksMapByTokenIndex.values())
|
||||
.map((banks) => banks[0])
|
||||
|
@ -187,270 +189,266 @@ async function updateTokenParams(): Promise<void> {
|
|||
const builder = Builder(NullTokenEditParams);
|
||||
let change = false;
|
||||
|
||||
const tier = Object.values(LISTING_PRESETS).find((x) =>
|
||||
x.initLiabWeight.toFixed(1) === '1.8'
|
||||
? x.initLiabWeight.toFixed(1) ===
|
||||
bank?.initLiabWeight.toNumber().toFixed(1) &&
|
||||
x.reduceOnly === bank.reduceOnly
|
||||
: x.initLiabWeight.toFixed(1) ===
|
||||
bank?.initLiabWeight.toNumber().toFixed(1),
|
||||
);
|
||||
|
||||
if (!tier) {
|
||||
console.log(`Cant estimate tier for ${bank.name}!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
// if (true) {
|
||||
// if (
|
||||
// bank.uiBorrows() == 0 &&
|
||||
// bank.reduceOnly == 2 &&
|
||||
// bank.initAssetWeight.toNumber() == 0 &&
|
||||
// bank.maintAssetWeight.toNumber() == 0
|
||||
// ) {
|
||||
// builder.disableAssetLiquidation(true);
|
||||
// builder.oracleConfig({
|
||||
// confFilter: 1000,
|
||||
// maxStalenessSlots: -1,
|
||||
// });
|
||||
// change = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // eslint-disable-next-line no-constant-condition
|
||||
// if (true) {
|
||||
// if (bank.uiBorrows() == 0 && bank.reduceOnly == 1) {
|
||||
// builder.disableAssetLiquidation(true);
|
||||
// builder.forceWithdraw(true);
|
||||
// change = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // eslint-disable-next-line no-constant-condition
|
||||
// if (true) {
|
||||
// if (!tier) {
|
||||
// console.log(`${bank.name}, no tier found`);
|
||||
// } else if (tier.preset_name != 'C') {
|
||||
// if (tier.preset_name.includes('A')) {
|
||||
// builder.liquidationFee(bank.liquidationFee.toNumber() * 0.2);
|
||||
// builder.platformLiquidationFee(
|
||||
// bank.liquidationFee.toNumber() * 0.8,
|
||||
// );
|
||||
// } else if (tier.preset_name.includes('B')) {
|
||||
// builder.liquidationFee(bank.liquidationFee.toNumber() * 0.4);
|
||||
// builder.platformLiquidationFee(
|
||||
// bank.liquidationFee.toNumber() * 0.6,
|
||||
// );
|
||||
// }
|
||||
// change = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
// if (true) {
|
||||
// if (!tier) {
|
||||
// console.log(`${bank.name}, no tier found`);
|
||||
// } else {
|
||||
// console.log(
|
||||
// `${bank.name.padStart(10)}, ${bank.loanFeeRate
|
||||
// .mul(I80F48.fromNumber(100))
|
||||
// .toFixed(2)}, ${bank.loanOriginationFeeRate
|
||||
// .mul(I80F48.fromNumber(100))
|
||||
// .toFixed(2)}, ${tier?.preset_name.padStart(5)}, ${(
|
||||
// tier.loanFeeRate * 100
|
||||
// ).toFixed(2)}, ${(tier!.loanOriginationFeeRate * 100).toFixed(2)}`,
|
||||
// );
|
||||
|
||||
// builder.loanFeeRate(tier!.loanFeeRate);
|
||||
// builder.loanOriginationFeeRate(tier!.loanOriginationFeeRate);
|
||||
// builder.flashLoanSwapFeeRate(tier!.loanOriginationFeeRate);
|
||||
// change = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// formulas are sourced from here
|
||||
// https://www.notion.so/mango-markets/Mango-v4-Risk-parameter-recommendations-d309cdf5faac4aeea7560356e68532ab
|
||||
|
||||
// const priceImpact = getPriceImpactForBank(midPriceImpacts, bank);
|
||||
// const scaleStartQuoteUi = Math.min(
|
||||
// 50 * ttlLiqorEquityUi,
|
||||
// 4 * priceImpact.target_amount,
|
||||
// );
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
if (!bank.areBorrowsReduceOnly()) {
|
||||
// Net borrow limits
|
||||
let netBorrowLimitPerWindowQuote = Math.max(
|
||||
toUiDecimalsForQuote(tier!.netBorrowLimitPerWindowQuote),
|
||||
Math.min(bank.uiDeposits() * bank.uiPrice, 300_000) / 3 +
|
||||
Math.max(0, bank.uiDeposits() * bank.uiPrice - 300_000) / 5,
|
||||
try {
|
||||
const tier = Object.values(LISTING_PRESETS).find((x) =>
|
||||
x.initLiabWeight.toFixed(1) === '1.8'
|
||||
? x.initLiabWeight.toFixed(1) ===
|
||||
bank?.initLiabWeight.toNumber().toFixed(1) &&
|
||||
x.reduceOnly === bank.reduceOnly
|
||||
: x.initLiabWeight.toFixed(1) ===
|
||||
bank?.initLiabWeight.toNumber().toFixed(1),
|
||||
);
|
||||
netBorrowLimitPerWindowQuote =
|
||||
Math.round(netBorrowLimitPerWindowQuote / 10_000) * 10_000;
|
||||
builder.netBorrowLimitPerWindowQuote(
|
||||
toNativeI80F48ForQuote(netBorrowLimitPerWindowQuote).toNumber(),
|
||||
);
|
||||
change = true;
|
||||
if (
|
||||
netBorrowLimitPerWindowQuote !=
|
||||
toUiDecimalsForQuote(bank.netBorrowLimitPerWindowQuote)
|
||||
) {
|
||||
console.log(
|
||||
`${bank.name}, ${bank.uiDeposits() * bank.uiPrice}$ (${
|
||||
Math.min(bank.uiDeposits() * bank.uiPrice, 300_000) / 3
|
||||
}, ${
|
||||
Math.max(0, bank.uiDeposits() * bank.uiPrice - 300_000) / 5
|
||||
}), ${
|
||||
tier?.netBorrowLimitPerWindowQuote
|
||||
} , new - ${netBorrowLimitPerWindowQuote}, old - ${toUiDecimalsForQuote(
|
||||
bank.netBorrowLimitPerWindowQuote,
|
||||
)}`,
|
||||
);
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
if (true) {
|
||||
if (
|
||||
bank.uiBorrows() == 0 &&
|
||||
bank.reduceOnly == 2 &&
|
||||
bank.initAssetWeight.toNumber() == 0 &&
|
||||
bank.maintAssetWeight.toNumber() == 0
|
||||
) {
|
||||
builder.disableAssetLiquidation(true);
|
||||
builder.oracleConfig({
|
||||
confFilter: 1000,
|
||||
maxStalenessSlots: -1,
|
||||
});
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deposit limits
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
// if (false) {
|
||||
// if (bank.maintAssetWeight.toNumber() > 0) {
|
||||
// {
|
||||
// // Find asset's largest batch in $ we would need to liquidate, batches are extreme points of a range of price drop,
|
||||
// // range is constrained by leverage provided
|
||||
// // i.e. how much volatility we expect
|
||||
// const r = findLargestAssetBatchUi(
|
||||
// pisForLiqor,
|
||||
// bank.name,
|
||||
// Math.round(bank.maintAssetWeight.toNumber() * 100),
|
||||
// 100 - Math.round(bank.maintAssetWeight.toNumber() * 100),
|
||||
// stepSize,
|
||||
// );
|
||||
// // eslint-disable-next-line no-constant-condition
|
||||
// if (true) {
|
||||
// if (bank.uiBorrows() == 0 && bank.reduceOnly == 1) {
|
||||
// builder.disableAssetLiquidation(true);
|
||||
// builder.forceWithdraw(true);
|
||||
// change = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// const maxLiqBatchQuoteUi = r[0];
|
||||
// const maxLiqBatchUi = r[1];
|
||||
// // eslint-disable-next-line no-constant-condition
|
||||
// if (true) {
|
||||
// if (!tier) {
|
||||
// console.log(`${bank.name}, no tier found`);
|
||||
// } else if (tier.preset_name != 'C') {
|
||||
// if (tier.preset_name.includes('A')) {
|
||||
// builder.liquidationFee(bank.liquidationFee.toNumber() * 0.2);
|
||||
// builder.platformLiquidationFee(
|
||||
// bank.liquidationFee.toNumber() * 0.8,
|
||||
// );
|
||||
// } else if (tier.preset_name.includes('B')) {
|
||||
// builder.liquidationFee(bank.liquidationFee.toNumber() * 0.4);
|
||||
// builder.platformLiquidationFee(
|
||||
// bank.liquidationFee.toNumber() * 0.6,
|
||||
// );
|
||||
// }
|
||||
// change = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// const sellImpact = getPriceImpactForBank(
|
||||
// midPriceImpacts,
|
||||
// bank,
|
||||
// (bank.liquidationFee.toNumber() * 100) / 2,
|
||||
// );
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
// if (true) {
|
||||
// if (!tier) {
|
||||
// console.log(`${bank.name}, no tier found`);
|
||||
// } else {
|
||||
// console.log(
|
||||
// `${bank.name.padStart(10)}, ${bank.loanFeeRate
|
||||
// .mul(I80F48.fromNumber(100))
|
||||
// .toFixed(2)}, ${bank.loanOriginationFeeRate
|
||||
// .mul(I80F48.fromNumber(100))
|
||||
// .toFixed(2)}, ${tier?.preset_name.padStart(5)}, ${(
|
||||
// tier.loanFeeRate * 100
|
||||
// ).toFixed(2)}, ${(tier!.loanOriginationFeeRate * 100).toFixed(2)}`,
|
||||
// );
|
||||
|
||||
// // Deposit limit = sell impact - largest batch
|
||||
// const allowedNewDepositsQuoteUi =
|
||||
// sellImpact.target_amount - maxLiqBatchQuoteUi;
|
||||
// const allowedNewDepositsUi =
|
||||
// sellImpact.target_amount / bank.uiPrice -
|
||||
// maxLiqBatchQuoteUi / bank.uiPrice;
|
||||
// builder.loanFeeRate(tier!.loanFeeRate);
|
||||
// builder.loanOriginationFeeRate(tier!.loanOriginationFeeRate);
|
||||
// builder.flashLoanSwapFeeRate(tier!.loanOriginationFeeRate);
|
||||
// change = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// const depositLimitUi = bank.uiDeposits() + allowedNewDepositsUi;
|
||||
// formulas are sourced from here
|
||||
// https://www.notion.so/mango-markets/Mango-v4-Risk-parameter-recommendations-d309cdf5faac4aeea7560356e68532ab
|
||||
|
||||
// // LOG
|
||||
// // console.log(
|
||||
// // `${bank.name.padStart(20)} ${maxLiqBatchUi
|
||||
// // .toLocaleString()
|
||||
// // .padStart(15)} ${maxLiqBatchQuoteUi
|
||||
// // .toLocaleString()
|
||||
// // .padStart(15)}$ ${sellImpact.target_amount
|
||||
// // .toLocaleString()
|
||||
// // .padStart(12)}$ ${sellImpact.avg_price_impact_percent
|
||||
// // .toLocaleString()
|
||||
// // .padStart(12)}% ${allowedNewDepositsUi
|
||||
// // .toLocaleString()
|
||||
// // .padStart(20)}${allowedNewDepositsQuoteUi
|
||||
// // .toLocaleString()
|
||||
// // .padStart(20)}$ ${bank
|
||||
// // .uiDeposits()
|
||||
// // .toLocaleString()
|
||||
// // .padStart(12)} ${(bank.uiDeposits() * bank.uiPrice)
|
||||
// // .toLocaleString()
|
||||
// // .padStart(12)}$ ${depositLimitUi
|
||||
// // .toLocaleString()
|
||||
// // .padStart(12)}`,
|
||||
// // );
|
||||
// const priceImpact = getPriceImpactForBank(midPriceImpacts, bank);
|
||||
// const scaleStartQuoteUi = Math.min(
|
||||
// 50 * ttlLiqorEquityUi,
|
||||
// 4 * priceImpact.target_amount,
|
||||
// );
|
||||
|
||||
// builder.depositLimit(toNative(depositLimitUi, bank.mintDecimals));
|
||||
// change = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
if (false) {
|
||||
// Net borrow limits
|
||||
const netBorrowLimitPerWindowQuote = Math.max(
|
||||
10_000,
|
||||
Math.min(bank.uiDeposits() * bank.uiPrice, 300_000) / 3 +
|
||||
Math.max(0, bank.uiDeposits() * bank.uiPrice - 300_000) / 5,
|
||||
);
|
||||
builder.netBorrowLimitPerWindowQuote(
|
||||
toNativeI80F48ForQuote(netBorrowLimitPerWindowQuote).toNumber(),
|
||||
);
|
||||
change = true;
|
||||
if (
|
||||
netBorrowLimitPerWindowQuote !=
|
||||
toUiDecimalsForQuote(bank.netBorrowLimitPerWindowQuote)
|
||||
) {
|
||||
console.log(
|
||||
`${
|
||||
bank.name
|
||||
} new - ${netBorrowLimitPerWindowQuote.toLocaleString()}, old - ${toUiDecimalsForQuote(
|
||||
bank.netBorrowLimitPerWindowQuote,
|
||||
).toLocaleString()}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const params = builder.build();
|
||||
// Deposit limits
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
if (false) {
|
||||
if (bank.maintAssetWeight.toNumber() > 0) {
|
||||
{
|
||||
// Find asset's largest batch in $ we would need to liquidate, batches are extreme points of a range of price drop,
|
||||
// range is constrained by leverage provided
|
||||
// i.e. how much volatility we expect
|
||||
const r = findLargestAssetBatchUi(
|
||||
pisForLiqor,
|
||||
bank.name,
|
||||
Math.round(bank.maintAssetWeight.toNumber() * 100),
|
||||
100 - Math.round(bank.maintAssetWeight.toNumber() * 100),
|
||||
stepSize,
|
||||
);
|
||||
|
||||
const ix = await client.program.methods
|
||||
.tokenEdit(
|
||||
params.oracle,
|
||||
params.oracleConfig,
|
||||
params.groupInsuranceFund,
|
||||
params.interestRateParams,
|
||||
params.loanFeeRate,
|
||||
params.loanOriginationFeeRate,
|
||||
params.maintAssetWeight,
|
||||
params.initAssetWeight,
|
||||
params.maintLiabWeight,
|
||||
params.initLiabWeight,
|
||||
params.liquidationFee,
|
||||
params.stablePriceDelayIntervalSeconds,
|
||||
params.stablePriceDelayGrowthLimit,
|
||||
params.stablePriceGrowthLimit,
|
||||
params.minVaultToDepositsRatio,
|
||||
params.netBorrowLimitPerWindowQuote !== null
|
||||
? new BN(params.netBorrowLimitPerWindowQuote)
|
||||
: null,
|
||||
params.netBorrowLimitWindowSizeTs !== null
|
||||
? new BN(params.netBorrowLimitWindowSizeTs)
|
||||
: null,
|
||||
params.borrowWeightScaleStartQuote,
|
||||
params.depositWeightScaleStartQuote,
|
||||
params.resetStablePrice ?? false,
|
||||
params.resetNetBorrowLimit ?? false,
|
||||
params.reduceOnly,
|
||||
params.name,
|
||||
params.forceClose,
|
||||
params.tokenConditionalSwapTakerFeeRate,
|
||||
params.tokenConditionalSwapMakerFeeRate,
|
||||
params.flashLoanSwapFeeRate,
|
||||
params.interestCurveScaling,
|
||||
params.interestTargetUtilization,
|
||||
params.maintWeightShiftStart,
|
||||
params.maintWeightShiftEnd,
|
||||
params.maintWeightShiftAssetTarget,
|
||||
params.maintWeightShiftLiabTarget,
|
||||
params.maintWeightShiftAbort ?? false,
|
||||
false, // setFallbackOracle, unused
|
||||
params.depositLimit,
|
||||
params.zeroUtilRate,
|
||||
params.platformLiquidationFee,
|
||||
params.disableAssetLiquidation,
|
||||
params.collateralFeePerDay,
|
||||
params.forceWithdraw,
|
||||
)
|
||||
.accounts({
|
||||
group: group.publicKey,
|
||||
oracle: bank.oracle,
|
||||
admin: group.admin,
|
||||
mintInfo: group.mintInfosMapByTokenIndex.get(bank.tokenIndex)
|
||||
?.publicKey,
|
||||
fallbackOracle: PublicKey.default,
|
||||
})
|
||||
.remainingAccounts([
|
||||
{
|
||||
pubkey: bank.publicKey,
|
||||
isWritable: true,
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
])
|
||||
.instruction();
|
||||
const maxLiqBatchQuoteUi = r[0];
|
||||
const maxLiqBatchUi = r[1];
|
||||
|
||||
const tx = new Transaction({ feePayer: wallet.publicKey }).add(ix);
|
||||
const simulated = await client.connection.simulateTransaction(tx);
|
||||
const sellImpact = getPriceImpactForBank(
|
||||
midPriceImpacts,
|
||||
bank,
|
||||
(bank.liquidationFee.toNumber() * 100) / 2,
|
||||
);
|
||||
|
||||
if (simulated.value.err) {
|
||||
console.log('error', simulated.value.logs);
|
||||
throw simulated.value.logs;
|
||||
}
|
||||
// Deposit limit = sell impact - largest batch
|
||||
const allowedNewDepositsQuoteUi =
|
||||
sellImpact.target_amount - maxLiqBatchQuoteUi;
|
||||
const allowedNewDepositsUi =
|
||||
sellImpact.target_amount / bank.uiPrice -
|
||||
maxLiqBatchQuoteUi / bank.uiPrice;
|
||||
|
||||
if (change) {
|
||||
instructions.push(ix);
|
||||
const depositLimitUi = bank.uiDeposits() + allowedNewDepositsUi;
|
||||
|
||||
// LOG
|
||||
// console.log(
|
||||
// `${bank.name.padStart(20)} ${maxLiqBatchUi
|
||||
// .toLocaleString()
|
||||
// .padStart(15)} ${maxLiqBatchQuoteUi
|
||||
// .toLocaleString()
|
||||
// .padStart(15)}$ ${sellImpact.target_amount
|
||||
// .toLocaleString()
|
||||
// .padStart(12)}$ ${sellImpact.avg_price_impact_percent
|
||||
// .toLocaleString()
|
||||
// .padStart(12)}% ${allowedNewDepositsUi
|
||||
// .toLocaleString()
|
||||
// .padStart(20)}${allowedNewDepositsQuoteUi
|
||||
// .toLocaleString()
|
||||
// .padStart(20)}$ ${bank
|
||||
// .uiDeposits()
|
||||
// .toLocaleString()
|
||||
// .padStart(12)} ${(bank.uiDeposits() * bank.uiPrice)
|
||||
// .toLocaleString()
|
||||
// .padStart(12)}$ ${depositLimitUi
|
||||
// .toLocaleString()
|
||||
// .padStart(12)}`,
|
||||
// );
|
||||
|
||||
builder.depositLimit(toNative(depositLimitUi, bank.mintDecimals));
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const params = builder.build();
|
||||
console.log(
|
||||
`${bank.name}, ${params.disableAssetLiquidation} ${params.oracleConfig?.maxStalenessSlots} ${params.oracleConfig?.confFilter}`,
|
||||
);
|
||||
|
||||
const ix = await client.program.methods
|
||||
.tokenEdit(
|
||||
params.oracle,
|
||||
params.oracleConfig,
|
||||
params.groupInsuranceFund,
|
||||
params.interestRateParams,
|
||||
params.loanFeeRate,
|
||||
params.loanOriginationFeeRate,
|
||||
params.maintAssetWeight,
|
||||
params.initAssetWeight,
|
||||
params.maintLiabWeight,
|
||||
params.initLiabWeight,
|
||||
params.liquidationFee,
|
||||
params.stablePriceDelayIntervalSeconds,
|
||||
params.stablePriceDelayGrowthLimit,
|
||||
params.stablePriceGrowthLimit,
|
||||
params.minVaultToDepositsRatio,
|
||||
params.netBorrowLimitPerWindowQuote !== null
|
||||
? new BN(params.netBorrowLimitPerWindowQuote)
|
||||
: null,
|
||||
params.netBorrowLimitWindowSizeTs !== null
|
||||
? new BN(params.netBorrowLimitWindowSizeTs)
|
||||
: null,
|
||||
params.borrowWeightScaleStartQuote,
|
||||
params.depositWeightScaleStartQuote,
|
||||
params.resetStablePrice ?? false,
|
||||
params.resetNetBorrowLimit ?? false,
|
||||
params.reduceOnly,
|
||||
params.name,
|
||||
params.forceClose,
|
||||
params.tokenConditionalSwapTakerFeeRate,
|
||||
params.tokenConditionalSwapMakerFeeRate,
|
||||
params.flashLoanSwapFeeRate,
|
||||
params.interestCurveScaling,
|
||||
params.interestTargetUtilization,
|
||||
params.maintWeightShiftStart,
|
||||
params.maintWeightShiftEnd,
|
||||
params.maintWeightShiftAssetTarget,
|
||||
params.maintWeightShiftLiabTarget,
|
||||
params.maintWeightShiftAbort ?? false,
|
||||
false, // setFallbackOracle, unused
|
||||
params.depositLimit,
|
||||
params.zeroUtilRate,
|
||||
params.platformLiquidationFee,
|
||||
params.disableAssetLiquidation,
|
||||
params.collateralFeePerDay,
|
||||
params.forceWithdraw,
|
||||
)
|
||||
.accounts({
|
||||
group: group.publicKey,
|
||||
oracle: bank.oracle,
|
||||
admin: group.admin,
|
||||
mintInfo: group.mintInfosMapByTokenIndex.get(bank.tokenIndex)
|
||||
?.publicKey,
|
||||
fallbackOracle: PublicKey.default,
|
||||
})
|
||||
.remainingAccounts([
|
||||
{
|
||||
pubkey: bank.publicKey,
|
||||
isWritable: true,
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
])
|
||||
.instruction();
|
||||
|
||||
const tx = new Transaction({ feePayer: wallet.publicKey }).add(ix);
|
||||
const simulated = await client.connection.simulateTransaction(tx);
|
||||
|
||||
if (simulated.value.err) {
|
||||
console.log('error', simulated.value.logs);
|
||||
throw simulated.value.logs;
|
||||
}
|
||||
|
||||
if (change) {
|
||||
instructions.push(ix);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`....Skipping ${bank.name}, ${error}`);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -472,6 +470,8 @@ async function updateTokenParams(): Promise<void> {
|
|||
|
||||
const walletSigner = wallet as never;
|
||||
|
||||
console.log(DRY_RUN);
|
||||
|
||||
if (!DRY_RUN) {
|
||||
const proposalAddress = await createProposal(
|
||||
client.connection,
|
||||
|
@ -480,7 +480,7 @@ async function updateTokenParams(): Promise<void> {
|
|||
tokenOwnerRecord,
|
||||
PROPOSAL_TITLE
|
||||
? PROPOSAL_TITLE
|
||||
: 'Update net borrow limits for tokens in mango-v4',
|
||||
: 'Disable asset liquidation for C tier tokens in mango-v4, part 2',
|
||||
PROPOSAL_LINK ?? '',
|
||||
Object.values(proposals).length,
|
||||
instructions,
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
import { PublicKey } from '@solana/web3.js';
|
||||
import {
|
||||
HealthType,
|
||||
MangoAccount,
|
||||
TokenPosition,
|
||||
TokenPositionDto,
|
||||
} from './mangoAccount';
|
||||
import BN from 'bn.js';
|
||||
import { expect } from 'chai';
|
||||
import { assert } from 'console';
|
||||
import { I80F48, ONE_I80F48, ZERO_I80F48 } from '../numbers/I80F48';
|
||||
import { deepClone, toNative, toUiDecimals } from '../utils';
|
||||
import { Bank, TokenIndex } from './bank';
|
||||
import { deepClone, toNative, toUiDecimals } from '../utils';
|
||||
import { expect } from 'chai';
|
||||
import { I80F48, I80F48Dto, ONE_I80F48, ZERO_I80F48 } from '../numbers/I80F48';
|
||||
import { Group } from './group';
|
||||
import { MangoAccount, TokenPosition } from './mangoAccount';
|
||||
import { HealthCache } from './healthCache';
|
||||
import { assert } from 'console';
|
||||
|
||||
describe('Mango Account', () => {
|
||||
const mangoAccount = new MangoAccount(
|
||||
|
@ -27,7 +33,6 @@ describe('Mango Account', () => {
|
|||
new BN(0),
|
||||
0,
|
||||
0,
|
||||
new BN(0),
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
|
@ -104,7 +109,6 @@ describe('maxWithdraw', () => {
|
|||
new BN(0),
|
||||
0,
|
||||
0,
|
||||
new BN(0),
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
|
|
|
@ -62,7 +62,6 @@ export class MangoAccount {
|
|||
perps: unknown;
|
||||
perpOpenOrders: unknown;
|
||||
tokenConditionalSwaps: unknown;
|
||||
lastCollateralFeeCharge: BN;
|
||||
},
|
||||
): MangoAccount {
|
||||
return new MangoAccount(
|
||||
|
@ -83,7 +82,6 @@ export class MangoAccount {
|
|||
obj.buybackFeesExpiryTimestamp,
|
||||
obj.sequenceNumber,
|
||||
obj.headerVersion,
|
||||
obj.lastCollateralFeeCharge,
|
||||
obj.tokens as TokenPositionDto[],
|
||||
obj.serum3 as Serum3PositionDto[],
|
||||
obj.openbookV2 as OpenbookV2PositionDto[],
|
||||
|
@ -113,7 +111,6 @@ export class MangoAccount {
|
|||
public buybackFeesExpiryTimestamp: BN,
|
||||
public sequenceNumber: number,
|
||||
public headerVersion: number,
|
||||
public lastCollateralFeeCharge: BN,
|
||||
tokens: TokenPositionDto[],
|
||||
serum3: Serum3PositionDto[],
|
||||
openbookV2: OpenbookV2PositionDto[],
|
||||
|
@ -1765,10 +1762,12 @@ export class PerpPosition {
|
|||
throw new Error("PerpPosition doesn't belong to the given market!");
|
||||
}
|
||||
const cumulativeFunding = this.getCumulativeFunding(perpMarket);
|
||||
return (
|
||||
-1 * toUiDecimalsForQuote(cumulativeFunding.cumulativeLongFunding) +
|
||||
toUiDecimalsForQuote(cumulativeFunding.cumulativeShortFunding)
|
||||
);
|
||||
// can't be long and short at the same time
|
||||
if (cumulativeFunding.cumulativeLongFunding !== 0) {
|
||||
return -1 * toUiDecimalsForQuote(cumulativeFunding.cumulativeLongFunding);
|
||||
} else {
|
||||
return toUiDecimalsForQuote(cumulativeFunding.cumulativeShortFunding);
|
||||
}
|
||||
}
|
||||
|
||||
public getEquity(perpMarket: PerpMarket): I80F48 {
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import { expect } from 'chai';
|
||||
|
||||
import {
|
||||
USDC_MINT_MAINNET,
|
||||
deriveFallbackOracleQuoteKey,
|
||||
isOrcaOracle,
|
||||
isRaydiumOracle,
|
||||
} from './oracle';
|
||||
import { MangoClient } from '../client';
|
||||
import { MANGO_V4_ID, MANGO_V4_MAIN_GROUP } from '../constants';
|
||||
import { AnchorProvider, Provider, Wallet } from '@coral-xyz/anchor';
|
||||
import * as fs from 'fs';
|
||||
import { Group } from './group';
|
||||
|
||||
function getProvider(connection: Connection): Provider {
|
||||
const secretKey = JSON.parse(
|
||||
fs.readFileSync(process.env.KEYPAIR_PATH as string, 'utf-8'),
|
||||
);
|
||||
const kp = Keypair.fromSecretKey(Uint8Array.from(secretKey));
|
||||
const wallet = new Wallet(kp);
|
||||
const provider = new AnchorProvider(connection, wallet, {});
|
||||
return provider;
|
||||
}
|
||||
|
||||
describe.only('Oracle', () => {
|
||||
const connection = new Connection('https://api.mainnet-beta.solana.com/');
|
||||
const CLUSTER = 'mainnet-beta';
|
||||
|
||||
const Orca_SOL_USDC_Whirlpool = new PublicKey(
|
||||
'83v8iPyZihDEjDdY8RdZddyZNyUtXngz69Lgo9Kt5d6d',
|
||||
);
|
||||
const Raydium_SOL_USDC_Whirlpool = new PublicKey(
|
||||
'Ds33rQ1d4AXwxqyeXX6Pc3G4pFNr6iWb3dd8YfBBQMPr',
|
||||
);
|
||||
|
||||
it('can decode Orca CLMM oracles', async () => {
|
||||
const accInfo = await connection.getAccountInfo(Orca_SOL_USDC_Whirlpool);
|
||||
expect(accInfo).not.to.be.null;
|
||||
expect(isOrcaOracle(accInfo!)).to.be.true;
|
||||
|
||||
const other = await connection.getAccountInfo(Raydium_SOL_USDC_Whirlpool);
|
||||
expect(isOrcaOracle(other!)).to.be.false;
|
||||
|
||||
const quoteKey = deriveFallbackOracleQuoteKey(accInfo!);
|
||||
expect(quoteKey.equals(USDC_MINT_MAINNET)).to.be.true;
|
||||
});
|
||||
|
||||
it('can decode Raydium CLMM oracles', async () => {
|
||||
const accInfo = await connection.getAccountInfo(Raydium_SOL_USDC_Whirlpool);
|
||||
expect(accInfo).not.to.be.null;
|
||||
expect(isRaydiumOracle(accInfo!)).to.be.true;
|
||||
|
||||
const other = await connection.getAccountInfo(Orca_SOL_USDC_Whirlpool);
|
||||
expect(isRaydiumOracle(other!)).to.be.false;
|
||||
|
||||
const quoteKey = deriveFallbackOracleQuoteKey(accInfo!);
|
||||
expect(quoteKey.equals(USDC_MINT_MAINNET)).to.be.true;
|
||||
});
|
||||
|
||||
it.skip('can generate fixed fallback oracles', async () => {
|
||||
const provider = getProvider(connection);
|
||||
const client = MangoClient.connect(
|
||||
provider,
|
||||
CLUSTER,
|
||||
MANGO_V4_ID[CLUSTER],
|
||||
{
|
||||
fallbackOracleConfig: [
|
||||
new PublicKey('H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG'),
|
||||
],
|
||||
},
|
||||
); // SOL
|
||||
|
||||
const groupAccount = await client.program.account.group.fetch(
|
||||
MANGO_V4_MAIN_GROUP,
|
||||
);
|
||||
const GROUP = Group.from(MANGO_V4_MAIN_GROUP, groupAccount);
|
||||
await GROUP.reloadBanks(client);
|
||||
const fbs = await client.deriveFallbackOracleContexts(GROUP);
|
||||
expect(fbs.size).to.equal(1);
|
||||
});
|
||||
|
||||
it.skip('can generate all fallback oracles', async () => {
|
||||
const provider = getProvider(connection);
|
||||
const client = MangoClient.connect(
|
||||
provider,
|
||||
CLUSTER,
|
||||
MANGO_V4_ID[CLUSTER],
|
||||
{ fallbackOracleConfig: 'all' },
|
||||
);
|
||||
|
||||
const groupAccount = await client.program.account.group.fetch(
|
||||
MANGO_V4_MAIN_GROUP,
|
||||
);
|
||||
const GROUP = Group.from(MANGO_V4_MAIN_GROUP, groupAccount);
|
||||
await GROUP.reloadBanks(client);
|
||||
const fbs = await client.deriveFallbackOracleContexts(GROUP);
|
||||
expect(fbs.size).to.be.greaterThan(1);
|
||||
});
|
||||
|
||||
it.skip('can generate dynamic fallback oracles', async () => {
|
||||
const provider = getProvider(connection);
|
||||
const client = MangoClient.connect(
|
||||
provider,
|
||||
CLUSTER,
|
||||
MANGO_V4_ID[CLUSTER],
|
||||
{ fallbackOracleConfig: 'dynamic' },
|
||||
);
|
||||
|
||||
const groupAccount = await client.program.account.group.fetch(
|
||||
MANGO_V4_MAIN_GROUP,
|
||||
);
|
||||
const GROUP = Group.from(MANGO_V4_MAIN_GROUP, groupAccount);
|
||||
await GROUP.reloadBanks(client);
|
||||
const fbs = await client.deriveFallbackOracleContexts(GROUP);
|
||||
console.log(fbs.size);
|
||||
});
|
||||
});
|
|
@ -11,26 +11,6 @@ const SBV1_DEVNET_PID = new PublicKey(
|
|||
const SBV1_MAINNET_PID = new PublicKey(
|
||||
'DtmE9D2CSB4L5D6A15mraeEjrGMm6auWVzgaD8hK2tZM',
|
||||
);
|
||||
|
||||
const ORCA_MAINNET_PID = new PublicKey(
|
||||
'whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc',
|
||||
);
|
||||
const ORCA_WHIRLPOOL_LEN = 653;
|
||||
const ORCA_WHIRLPOOL_DISCRIMINATOR = [63, 149, 209, 12, 225, 128, 99, 9];
|
||||
|
||||
const RAYDIUM_MAINNET_PID = new PublicKey(
|
||||
'CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK',
|
||||
);
|
||||
const RAYDIUM_POOL_LEN = 1544;
|
||||
const RAYDIUM_POOL_DISCRIMINATOR = [247, 237, 227, 245, 215, 195, 222, 70];
|
||||
|
||||
export const USDC_MINT_MAINNET = new PublicKey(
|
||||
'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
||||
);
|
||||
export const SOL_MINT_MAINNET = new PublicKey(
|
||||
'So11111111111111111111111111111111111111112',
|
||||
);
|
||||
|
||||
let sbv2DevnetProgram;
|
||||
let sbv2MainnetProgram;
|
||||
|
||||
|
@ -184,36 +164,6 @@ export function isPythOracle(accountInfo: AccountInfo<Buffer>): boolean {
|
|||
return accountInfo.data.readUInt32LE(0) === PythMagic;
|
||||
}
|
||||
|
||||
export function isOrcaOracle(accountInfo: AccountInfo<Buffer>): boolean {
|
||||
for (let i = 0; i < 8; i++) {
|
||||
if (accountInfo.data.at(i) !== ORCA_WHIRLPOOL_DISCRIMINATOR[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
accountInfo.owner.equals(ORCA_MAINNET_PID) &&
|
||||
accountInfo.data.length == ORCA_WHIRLPOOL_LEN
|
||||
);
|
||||
}
|
||||
|
||||
export function isRaydiumOracle(accountInfo: AccountInfo<Buffer>): boolean {
|
||||
for (let i = 0; i < 8; i++) {
|
||||
if (accountInfo.data.at(i) !== RAYDIUM_POOL_DISCRIMINATOR[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
accountInfo.owner.equals(RAYDIUM_MAINNET_PID) &&
|
||||
accountInfo.data.length == RAYDIUM_POOL_LEN
|
||||
);
|
||||
}
|
||||
|
||||
export function isClmmOracle(accountInfo: AccountInfo<Buffer>): boolean {
|
||||
return isOrcaOracle(accountInfo) || isRaydiumOracle(accountInfo);
|
||||
}
|
||||
|
||||
export function isOracleStaleOrUnconfident(
|
||||
nowSlot: number,
|
||||
maxStalenessSlots: number,
|
||||
|
@ -236,50 +186,3 @@ export function isOracleStaleOrUnconfident(
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function deriveFallbackOracleQuoteKey(
|
||||
accountInfo: AccountInfo<Buffer>,
|
||||
): PublicKey {
|
||||
if (isOrcaOracle(accountInfo)) {
|
||||
const tokenA = new PublicKey(accountInfo.data.subarray(101, 133));
|
||||
const tokenB = new PublicKey(accountInfo.data.subarray(181, 213));
|
||||
return clmmQuoteKey(tokenA, tokenB);
|
||||
} else if (isRaydiumOracle(accountInfo)) {
|
||||
const tokenA = new PublicKey(accountInfo.data.subarray(73, 105));
|
||||
const tokenB = new PublicKey(accountInfo.data.subarray(105, 137));
|
||||
return clmmQuoteKey(tokenA, tokenB);
|
||||
} else {
|
||||
return PublicKey.default;
|
||||
}
|
||||
}
|
||||
|
||||
function clmmQuoteKey(tokenA: PublicKey, tokenB: PublicKey): PublicKey {
|
||||
if (
|
||||
tokenA.equals(USDC_MINT_MAINNET) ||
|
||||
(tokenA.equals(SOL_MINT_MAINNET) && !tokenB.equals(USDC_MINT_MAINNET))
|
||||
) {
|
||||
return tokenA; // inverted
|
||||
} else {
|
||||
return tokenB;
|
||||
}
|
||||
}
|
||||
// Assumes oracles.length === fallbacks.length
|
||||
export async function createFallbackOracleMap(
|
||||
conn: Connection,
|
||||
oracles: PublicKey[],
|
||||
fallbacks: PublicKey[],
|
||||
): Promise<Map<string, [PublicKey, PublicKey]>> {
|
||||
const map: Map<string, [PublicKey, PublicKey]> = new Map();
|
||||
const accounts = await conn.getMultipleAccountsInfo(fallbacks);
|
||||
for (let i = 0; i < oracles.length; i++) {
|
||||
if (accounts[i] === null) {
|
||||
map.set(oracles[i].toBase58(), [fallbacks[i], PublicKey.default]);
|
||||
} else if (!isClmmOracle(accounts[i]!)) {
|
||||
map.set(oracles[i].toBase58(), [fallbacks[i], PublicKey.default]);
|
||||
} else {
|
||||
const quoteKey = deriveFallbackOracleQuoteKey(accounts[i]!);
|
||||
map.set(oracles[i].toBase58(), [fallbacks[i], quoteKey]);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ import {
|
|||
generateOpenbookV2MarketExternalVaultSignerAddress,
|
||||
priceNumberToLots,
|
||||
} from './accounts/openbookV2';
|
||||
import { StubOracle, createFallbackOracleMap } from './accounts/oracle';
|
||||
import { StubOracle } from './accounts/oracle';
|
||||
import {
|
||||
FillEvent,
|
||||
OutEvent,
|
||||
|
@ -123,7 +123,6 @@ export enum AccountRetriever {
|
|||
}
|
||||
|
||||
export type IdsSource = 'api' | 'static' | 'get-program-accounts';
|
||||
export type FallbackOracleConfig = 'never' | 'all' | 'dynamic' | PublicKey[]; // 'fixed'
|
||||
|
||||
export type MangoClientOptions = {
|
||||
idsSource?: IdsSource;
|
||||
|
@ -135,7 +134,6 @@ export type MangoClientOptions = {
|
|||
openbookFeesToDao?: boolean;
|
||||
prependedGlobalAdditionalInstructions?: TransactionInstruction[];
|
||||
multipleConnections?: Connection[];
|
||||
fallbackOracleConfig?: FallbackOracleConfig;
|
||||
};
|
||||
|
||||
export type TxCallbackOptions = {
|
||||
|
@ -152,8 +150,6 @@ export class MangoClient {
|
|||
private txConfirmationCommitment: Commitment;
|
||||
private openbookFeesToDao: boolean;
|
||||
private prependedGlobalAdditionalInstructions: TransactionInstruction[] = [];
|
||||
private fallbackOracleConfig: FallbackOracleConfig = 'never';
|
||||
private fixedFallbacks: Map<string, [PublicKey, PublicKey]> = new Map();
|
||||
multipleConnections: Connection[] = [];
|
||||
|
||||
constructor(
|
||||
|
@ -177,7 +173,6 @@ export class MangoClient {
|
|||
// TODO: evil side effect, but limited backtraces are a nightmare
|
||||
Error.stackTraceLimit = 1000;
|
||||
this.multipleConnections = opts?.multipleConnections ?? [];
|
||||
this.fallbackOracleConfig = opts?.fallbackOracleConfig ?? 'never';
|
||||
}
|
||||
|
||||
/// Convenience accessors
|
||||
|
@ -641,7 +636,7 @@ export class MangoClient {
|
|||
const assetBank = group.getFirstBankByTokenIndex(assetTokenIndex);
|
||||
const liabBank = group.getFirstBankByTokenIndex(liabTokenIndex);
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[liqor, liqee],
|
||||
[assetBank, liabBank],
|
||||
|
@ -1158,13 +1153,7 @@ export class MangoClient {
|
|||
checkKind: HealthCheckKind,
|
||||
): Promise<TransactionInstruction> {
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[mangoAccount],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
);
|
||||
this.buildHealthRemainingAccounts(group, [mangoAccount], [], [], []);
|
||||
|
||||
return await this.program.methods
|
||||
.healthCheck(minHealthValue, checkKind)
|
||||
|
@ -1719,12 +1708,7 @@ export class MangoClient {
|
|||
}
|
||||
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[mangoAccount],
|
||||
[bank],
|
||||
[],
|
||||
);
|
||||
this.buildHealthRemainingAccounts(group, [mangoAccount], [bank], []);
|
||||
|
||||
const sharedAccounts = {
|
||||
group: group.publicKey,
|
||||
|
@ -1880,13 +1864,7 @@ export class MangoClient {
|
|||
}
|
||||
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[mangoAccount],
|
||||
[bank],
|
||||
[],
|
||||
[],
|
||||
);
|
||||
this.buildHealthRemainingAccounts(group, [mangoAccount], [bank], [], []);
|
||||
|
||||
const ix = await this.program.methods
|
||||
.tokenWithdraw(new BN(nativeAmount), allowBorrow)
|
||||
|
@ -2210,7 +2188,7 @@ export class MangoClient {
|
|||
);
|
||||
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[mangoAccount],
|
||||
[],
|
||||
|
@ -2323,7 +2301,7 @@ export class MangoClient {
|
|||
}
|
||||
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[mangoAccount],
|
||||
banks,
|
||||
|
@ -2447,7 +2425,7 @@ export class MangoClient {
|
|||
}
|
||||
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[mangoAccount],
|
||||
banks,
|
||||
|
@ -3102,7 +3080,7 @@ export class MangoClient {
|
|||
}
|
||||
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[mangoAccount],
|
||||
[],
|
||||
|
@ -3188,7 +3166,7 @@ export class MangoClient {
|
|||
}
|
||||
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[mangoAccount],
|
||||
banks,
|
||||
|
@ -3775,7 +3753,7 @@ export class MangoClient {
|
|||
): Promise<TransactionInstruction> {
|
||||
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(group, [mangoAccount], [], []);
|
||||
this.buildHealthRemainingAccounts(group, [mangoAccount], [], []);
|
||||
return await this.program.methods
|
||||
.perpDeactivatePosition()
|
||||
.accounts({
|
||||
|
@ -3908,7 +3886,7 @@ export class MangoClient {
|
|||
): Promise<TransactionInstruction> {
|
||||
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[mangoAccount],
|
||||
// Settlement token bank, because a position for it may be created
|
||||
|
@ -3965,7 +3943,7 @@ export class MangoClient {
|
|||
): Promise<TransactionInstruction> {
|
||||
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[mangoAccount],
|
||||
// Settlement token bank, because a position for it may be created
|
||||
|
@ -4058,7 +4036,7 @@ export class MangoClient {
|
|||
): Promise<TransactionInstruction> {
|
||||
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[mangoAccount],
|
||||
// Settlement token bank, because a position for it may be created
|
||||
|
@ -4118,7 +4096,7 @@ export class MangoClient {
|
|||
): Promise<TransactionInstruction> {
|
||||
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[mangoAccount],
|
||||
// Settlement token bank, because a position for it may be created
|
||||
|
@ -4400,7 +4378,7 @@ export class MangoClient {
|
|||
): Promise<TransactionInstruction> {
|
||||
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[profitableAccount, unprofitableAccount],
|
||||
[group.getFirstBankForPerpSettlement()],
|
||||
|
@ -4454,7 +4432,7 @@ export class MangoClient {
|
|||
): Promise<TransactionInstruction> {
|
||||
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[account], // Account must be unprofitable
|
||||
[group.getFirstBankForPerpSettlement()],
|
||||
|
@ -4597,7 +4575,7 @@ export class MangoClient {
|
|||
const outputBank: Bank = group.getFirstBankByMint(outputMintPk);
|
||||
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[mangoAccount],
|
||||
[inputBank, outputBank],
|
||||
|
@ -4793,7 +4771,7 @@ export class MangoClient {
|
|||
const liabBank: Bank = group.getFirstBankByMint(liabMintPk);
|
||||
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[liqor, liqee],
|
||||
[assetBank, liabBank],
|
||||
|
@ -5787,7 +5765,7 @@ export class MangoClient {
|
|||
const sellBank = group.banksMapByTokenIndex.get(tcs.sellTokenIndex)![0];
|
||||
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[liqor, liqee],
|
||||
[buyBank, sellBank],
|
||||
|
@ -5866,7 +5844,7 @@ export class MangoClient {
|
|||
perpMarkets: PerpMarket[] = [],
|
||||
): Promise<TransactionInstruction> {
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[account],
|
||||
[...banks],
|
||||
|
@ -5899,7 +5877,7 @@ export class MangoClient {
|
|||
perpMarkets: PerpMarket[] = [],
|
||||
): Promise<TransactionInstruction> {
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
await this.buildHealthRemainingAccounts(
|
||||
this.buildHealthRemainingAccounts(
|
||||
group,
|
||||
[account],
|
||||
[...banks],
|
||||
|
@ -5930,7 +5908,6 @@ export class MangoClient {
|
|||
opts?: MangoClientOptions,
|
||||
): MangoClient {
|
||||
const idl = IDL;
|
||||
console.log(opts);
|
||||
|
||||
return new MangoClient(
|
||||
new Program<MangoV4>(idl as MangoV4, programId, provider),
|
||||
|
@ -6002,11 +5979,10 @@ export class MangoClient {
|
|||
* @param mangoAccounts
|
||||
* @param banks - banks in which new positions might be opened
|
||||
* @param perpMarkets - markets in which new positions might be opened
|
||||
* @param serumOpenOrdersForMarket - markets in which new positions might be opened (openbook v1)
|
||||
* @param openbookOpenOrdersForMarket - markets in which new positions might be opened (openbook v2)
|
||||
* @param openOrdersForMarket - markets in which new positions might be opened
|
||||
* @returns
|
||||
*/
|
||||
async buildHealthRemainingAccounts(
|
||||
buildHealthRemainingAccounts(
|
||||
group: Group,
|
||||
mangoAccounts: MangoAccount[],
|
||||
// Banks and markets for whom positions don't exist on mango account,
|
||||
|
@ -6015,7 +5991,7 @@ export class MangoClient {
|
|||
perpMarkets: PerpMarket[] = [],
|
||||
serumOpenOrdersForMarket: [Serum3Market, PublicKey][] = [],
|
||||
openbookOpenOrdersForMarket: [OpenbookV2Market, PublicKey][] = [],
|
||||
): Promise<PublicKey[]> {
|
||||
): PublicKey[] {
|
||||
const healthRemainingAccounts: PublicKey[] = [];
|
||||
|
||||
const tokenPositionIndices = mangoAccounts
|
||||
|
@ -6160,81 +6136,9 @@ export class MangoClient {
|
|||
.map((openbookPosition) => openbookPosition.openOrders),
|
||||
);
|
||||
|
||||
const fallbackMap = await this.deriveFallbackOracleContexts(group);
|
||||
const fallbacks: PublicKey[] = [];
|
||||
|
||||
for (const oracle of mintInfos.map((mintInfo) => mintInfo.oracle)) {
|
||||
if (fallbackMap.has(oracle.toBase58())) {
|
||||
fallbacks.push(...fallbackMap.get(oracle.toBase58())!);
|
||||
}
|
||||
}
|
||||
|
||||
for (const fallback of uniq(fallbacks)) {
|
||||
if (
|
||||
!healthRemainingAccounts.find((h) => h.equals(fallback)) &&
|
||||
!fallback.equals(PublicKey.default)
|
||||
) {
|
||||
healthRemainingAccounts.push(fallback);
|
||||
}
|
||||
}
|
||||
|
||||
return healthRemainingAccounts;
|
||||
}
|
||||
|
||||
/**This function assumes that the provided group has loaded banks*/
|
||||
public async deriveFallbackOracleContexts(
|
||||
group: Group,
|
||||
): Promise<Map<string, [PublicKey, PublicKey]>> {
|
||||
// fixed
|
||||
if (typeof this.fallbackOracleConfig !== 'string') {
|
||||
if (this.fixedFallbacks.size === 0) {
|
||||
const oracles: PublicKey[] = this.fallbackOracleConfig;
|
||||
const fallbacks: PublicKey[] = [];
|
||||
Array.from(group.banksMapByTokenIndex.values()).forEach((b) => {
|
||||
if (oracles.find((o) => o.toBase58() === b[0].oracle.toBase58())) {
|
||||
fallbacks.push(b[0].fallbackOracle);
|
||||
}
|
||||
});
|
||||
this.fixedFallbacks = await createFallbackOracleMap(
|
||||
this.connection,
|
||||
oracles,
|
||||
fallbacks,
|
||||
);
|
||||
}
|
||||
return this.fixedFallbacks;
|
||||
}
|
||||
|
||||
switch (this.fallbackOracleConfig) {
|
||||
case 'never':
|
||||
return new Map();
|
||||
case 'dynamic': {
|
||||
const nowSlot = await this.connection.getSlot();
|
||||
const oracles: PublicKey[] = [];
|
||||
const fallbacks: PublicKey[] = [];
|
||||
await group.reloadBankOraclePrices(this);
|
||||
Array.from(group.banksMapByTokenIndex.values())
|
||||
.filter((b) => b[0].isOracleStaleOrUnconfident(nowSlot))
|
||||
.forEach((b) => {
|
||||
oracles.push(b[0].oracle);
|
||||
fallbacks.push(b[0].fallbackOracle);
|
||||
});
|
||||
return createFallbackOracleMap(this.connection, oracles, fallbacks);
|
||||
}
|
||||
case 'all': {
|
||||
const oracles: PublicKey[] = [];
|
||||
const fallbacks: PublicKey[] = [];
|
||||
Array.from(group.banksMapByTokenIndex.values()).forEach((b) => {
|
||||
oracles.push(b[0].oracle);
|
||||
fallbacks.push(b[0].fallbackOracle);
|
||||
});
|
||||
return createFallbackOracleMap(this.connection, oracles, fallbacks);
|
||||
}
|
||||
default: {
|
||||
return new Map();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async modifyPerpOrder(
|
||||
group: Group,
|
||||
mangoAccount: MangoAccount,
|
||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -38,10 +38,10 @@
|
|||
bn.js "^5.2.1"
|
||||
eslint-config-prettier "^9.0.0"
|
||||
|
||||
"@blockworks-foundation/mangolana@0.0.15":
|
||||
version "0.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mangolana/-/mangolana-0.0.15.tgz#8a603675028f7d7ab400cc58bf3058d2e7514cde"
|
||||
integrity sha512-P31XW2w2lFBgzM+529H2f3Kz8rNC7+h0pYZub9kOcsACAt7TwLksg1fGuzSvekLn6M1JmVpPa9nISdAVfsZB5g==
|
||||
"@blockworks-foundation/mangolana@0.0.14":
|
||||
version "0.0.14"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mangolana/-/mangolana-0.0.14.tgz#f2a0e164f2cbe6e0a0db4fc10267e81ef5d2f7ec"
|
||||
integrity sha512-KuA2+GdeKoHCBmx2HZnVb8IPomUP1w0ZiwQ1F10SLIypYfrylvPa+HSK2ak/+nzZCb8erS9Oub45UPV7cOh5ng==
|
||||
dependencies:
|
||||
"@solana/web3.js" "^1.88.0"
|
||||
bs58 "^5.0.0"
|
||||
|
@ -173,7 +173,7 @@
|
|||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699"
|
||||
integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==
|
||||
|
||||
"@noble/hashes@^1.3.1", "@noble/hashes@^1.3.3":
|
||||
"@noble/hashes@^1.3.1", "@noble/hashes@^1.3.2", "@noble/hashes@^1.3.3":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426"
|
||||
integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==
|
||||
|
|
Loading…
Reference in New Issue