bins: Fix restarting on new or changed listings (#802)
- Don't just restart on new listings, but also on significant changes to
old listings such as oracle changes.
- Cover the liquidator and settler in addition to the keeper.
(cherry picked from commit ce16d79b13
)
This commit is contained in:
parent
5cfbb8386d
commit
9ba0004760
|
@ -31,14 +31,9 @@ pub async fn save_snapshot(
|
||||||
let oracles_and_vaults = group_context
|
let oracles_and_vaults = group_context
|
||||||
.tokens
|
.tokens
|
||||||
.values()
|
.values()
|
||||||
.map(|value| value.mint_info.oracle)
|
.map(|value| value.oracle)
|
||||||
.chain(group_context.perp_markets.values().map(|p| p.market.oracle))
|
.chain(group_context.perp_markets.values().map(|p| p.oracle))
|
||||||
.chain(
|
.chain(group_context.tokens.values().flat_map(|value| value.vaults))
|
||||||
group_context
|
|
||||||
.tokens
|
|
||||||
.values()
|
|
||||||
.flat_map(|value| value.mint_info.vaults),
|
|
||||||
)
|
|
||||||
.unique()
|
.unique()
|
||||||
.filter(|pk| *pk != Pubkey::default())
|
.filter(|pk| *pk != Pubkey::default())
|
||||||
.collect::<Vec<Pubkey>>();
|
.collect::<Vec<Pubkey>>();
|
||||||
|
@ -46,7 +41,7 @@ pub async fn save_snapshot(
|
||||||
let serum_programs = group_context
|
let serum_programs = group_context
|
||||||
.serum3_markets
|
.serum3_markets
|
||||||
.values()
|
.values()
|
||||||
.map(|s3| s3.market.serum_program)
|
.map(|s3| s3.serum_program)
|
||||||
.unique()
|
.unique()
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use mango_v4::accounts_zerocopy::KeyedAccount;
|
use mango_v4::accounts_zerocopy::KeyedAccount;
|
||||||
use mango_v4_client::{Client, MangoGroupContext};
|
use mango_v4_client::{Client, MangoGroupContext};
|
||||||
|
@ -11,11 +13,23 @@ pub async fn run(client: &Client, group: Pubkey) -> anyhow::Result<()> {
|
||||||
let oracles = context
|
let oracles = context
|
||||||
.tokens
|
.tokens
|
||||||
.values()
|
.values()
|
||||||
.map(|t| t.mint_info.oracle)
|
.map(|t| t.oracle)
|
||||||
.chain(context.perp_markets.values().map(|p| p.market.oracle))
|
.chain(context.perp_markets.values().map(|p| p.oracle))
|
||||||
.unique()
|
.unique()
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
|
let banks: HashMap<_, _> = mango_v4_client::gpa::fetch_banks(&rpc_async, mango_v4::id(), group)
|
||||||
|
.await?
|
||||||
|
.iter()
|
||||||
|
.map(|(_, b)| (b.oracle, *b))
|
||||||
|
.collect();
|
||||||
|
let perp_markets: HashMap<_, _> =
|
||||||
|
mango_v4_client::gpa::fetch_perp_markets(&rpc_async, mango_v4::id(), group)
|
||||||
|
.await?
|
||||||
|
.iter()
|
||||||
|
.map(|(_, p)| (p.oracle, *p))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut interval = tokio::time::interval(std::time::Duration::from_secs(5));
|
let mut interval = tokio::time::interval(std::time::Duration::from_secs(5));
|
||||||
loop {
|
loop {
|
||||||
interval.tick().await;
|
interval.tick().await;
|
||||||
|
@ -41,25 +55,19 @@ pub async fn run(client: &Client, group: Pubkey) -> anyhow::Result<()> {
|
||||||
account: account_opt.unwrap(),
|
account: account_opt.unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let tc_opt = context
|
let bank_opt = banks.get(pubkey);
|
||||||
.tokens
|
let perp_opt = perp_markets.get(pubkey);
|
||||||
.values()
|
|
||||||
.find(|t| t.mint_info.oracle == *pubkey);
|
|
||||||
let pc_opt = context
|
|
||||||
.perp_markets
|
|
||||||
.values()
|
|
||||||
.find(|p| p.market.oracle == *pubkey);
|
|
||||||
let mut price = None;
|
let mut price = None;
|
||||||
if let Some(tc) = tc_opt {
|
if let Some(bank) = bank_opt {
|
||||||
match tc.bank.oracle_price(&keyed_account, Some(slot)) {
|
match bank.oracle_price(&keyed_account, Some(slot)) {
|
||||||
Ok(p) => price = Some(p),
|
Ok(p) => price = Some(p),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("could not read bank oracle {}: {e:?}", keyed_account.key);
|
error!("could not read bank oracle {}: {e:?}", keyed_account.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(pc) = pc_opt {
|
if let Some(perp) = perp_opt {
|
||||||
match pc.market.oracle_price(&keyed_account, Some(slot)) {
|
match perp.oracle_price(&keyed_account, Some(slot)) {
|
||||||
Ok(p) => price = Some(p),
|
Ok(p) => price = Some(p),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("could not read perp oracle {}: {e:?}", keyed_account.key);
|
error!("could not read perp oracle {}: {e:?}", keyed_account.key);
|
||||||
|
|
|
@ -5,7 +5,8 @@ use itertools::Itertools;
|
||||||
|
|
||||||
use anchor_lang::{__private::bytemuck::cast_ref, solana_program};
|
use anchor_lang::{__private::bytemuck::cast_ref, solana_program};
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use mango_v4::state::{EventQueue, EventType, FillEvent, OutEvent, PerpMarket, TokenIndex};
|
use mango_v4::state::{EventQueue, EventType, FillEvent, OutEvent, TokenIndex};
|
||||||
|
use mango_v4_client::PerpMarketContext;
|
||||||
use prometheus::{register_histogram, Encoder, Histogram, IntCounter, Registry};
|
use prometheus::{register_histogram, Encoder, Histogram, IntCounter, Registry};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
instruction::{AccountMeta, Instruction},
|
instruction::{AccountMeta, Instruction},
|
||||||
|
@ -79,7 +80,7 @@ pub async fn runner(
|
||||||
interval_update_banks: u64,
|
interval_update_banks: u64,
|
||||||
interval_consume_events: u64,
|
interval_consume_events: u64,
|
||||||
interval_update_funding: u64,
|
interval_update_funding: u64,
|
||||||
interval_check_new_listings_and_abort: u64,
|
interval_check_for_changes_and_abort: u64,
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
let handles1 = mango_client
|
let handles1 = mango_client
|
||||||
.context
|
.context
|
||||||
|
@ -106,12 +107,12 @@ pub async fn runner(
|
||||||
.values()
|
.values()
|
||||||
.filter(|perp|
|
.filter(|perp|
|
||||||
// MNGO-PERP-OLD
|
// MNGO-PERP-OLD
|
||||||
perp.market.perp_market_index != 1)
|
perp.perp_market_index != 1)
|
||||||
.map(|perp| {
|
.map(|perp| {
|
||||||
loop_consume_events(
|
loop_consume_events(
|
||||||
mango_client.clone(),
|
mango_client.clone(),
|
||||||
perp.address,
|
perp.address,
|
||||||
perp.market,
|
perp,
|
||||||
interval_consume_events,
|
interval_consume_events,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -123,12 +124,12 @@ pub async fn runner(
|
||||||
.values()
|
.values()
|
||||||
.filter(|perp|
|
.filter(|perp|
|
||||||
// MNGO-PERP-OLD
|
// MNGO-PERP-OLD
|
||||||
perp.market.perp_market_index != 1)
|
perp.perp_market_index != 1)
|
||||||
.map(|perp| {
|
.map(|perp| {
|
||||||
loop_update_funding(
|
loop_update_funding(
|
||||||
mango_client.clone(),
|
mango_client.clone(),
|
||||||
perp.address,
|
perp.address,
|
||||||
perp.market,
|
perp,
|
||||||
interval_update_funding,
|
interval_update_funding,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -138,9 +139,9 @@ pub async fn runner(
|
||||||
futures::future::join_all(handles1),
|
futures::future::join_all(handles1),
|
||||||
futures::future::join_all(handles2),
|
futures::future::join_all(handles2),
|
||||||
futures::future::join_all(handles3),
|
futures::future::join_all(handles3),
|
||||||
loop_check_new_listings_and_abort(
|
MangoClient::loop_check_for_context_changes_and_abort(
|
||||||
mango_client.clone(),
|
mango_client.clone(),
|
||||||
interval_check_new_listings_and_abort
|
Duration::from_secs(interval_check_for_changes_and_abort),
|
||||||
),
|
),
|
||||||
serve_metrics(),
|
serve_metrics(),
|
||||||
debugging_handle,
|
debugging_handle,
|
||||||
|
@ -149,27 +150,6 @@ pub async fn runner(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn loop_check_new_listings_and_abort(mango_client: Arc<MangoClient>, interval: u64) {
|
|
||||||
let mut interval = time::interval(Duration::from_secs(interval));
|
|
||||||
loop {
|
|
||||||
if mango_client
|
|
||||||
.context
|
|
||||||
.new_tokens_listed(&mango_client.client.rpc_async())
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
|| mango_client
|
|
||||||
.context
|
|
||||||
.new_perp_markets_listed(&mango_client.client.rpc_async())
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
{
|
|
||||||
std::process::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
interval.tick().await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn loop_update_index_and_rate(
|
pub async fn loop_update_index_and_rate(
|
||||||
mango_client: Arc<MangoClient>,
|
mango_client: Arc<MangoClient>,
|
||||||
token_indices: Vec<TokenIndex>,
|
token_indices: Vec<TokenIndex>,
|
||||||
|
@ -191,14 +171,14 @@ pub async fn loop_update_index_and_rate(
|
||||||
let mut instructions = vec![];
|
let mut instructions = vec![];
|
||||||
for token_index in token_indices_clone.iter() {
|
for token_index in token_indices_clone.iter() {
|
||||||
let token = client.context.token(*token_index);
|
let token = client.context.token(*token_index);
|
||||||
let banks_for_a_token = token.mint_info.banks();
|
let banks_for_a_token = token.banks();
|
||||||
let oracle = token.mint_info.oracle;
|
let oracle = token.oracle;
|
||||||
|
|
||||||
let mut ix = Instruction {
|
let mut ix = Instruction {
|
||||||
program_id: mango_v4::id(),
|
program_id: mango_v4::id(),
|
||||||
accounts: anchor_lang::ToAccountMetas::to_account_metas(
|
accounts: anchor_lang::ToAccountMetas::to_account_metas(
|
||||||
&mango_v4::accounts::TokenUpdateIndexAndRate {
|
&mango_v4::accounts::TokenUpdateIndexAndRate {
|
||||||
group: token.mint_info.group,
|
group: token.group,
|
||||||
mint_info: token.mint_info_address,
|
mint_info: token.mint_info_address,
|
||||||
oracle,
|
oracle,
|
||||||
instructions: solana_program::sysvar::instructions::id(),
|
instructions: solana_program::sysvar::instructions::id(),
|
||||||
|
@ -264,7 +244,7 @@ pub async fn loop_update_index_and_rate(
|
||||||
pub async fn loop_consume_events(
|
pub async fn loop_consume_events(
|
||||||
mango_client: Arc<MangoClient>,
|
mango_client: Arc<MangoClient>,
|
||||||
pk: Pubkey,
|
pk: Pubkey,
|
||||||
perp_market: PerpMarket,
|
perp_market: &PerpMarketContext,
|
||||||
interval: u64,
|
interval: u64,
|
||||||
) {
|
) {
|
||||||
let mut interval = time::interval(Duration::from_secs(interval));
|
let mut interval = time::interval(Duration::from_secs(interval));
|
||||||
|
@ -362,7 +342,7 @@ pub async fn loop_consume_events(
|
||||||
METRIC_CONSUME_EVENTS_FAILURE.inc();
|
METRIC_CONSUME_EVENTS_FAILURE.inc();
|
||||||
info!(
|
info!(
|
||||||
"metricName=ConsumeEventsV4Failure market={} durationMs={} consumed={} error={}",
|
"metricName=ConsumeEventsV4Failure market={} durationMs={} consumed={} error={}",
|
||||||
perp_market.name(),
|
perp_market.name,
|
||||||
confirmation_time,
|
confirmation_time,
|
||||||
num_of_events,
|
num_of_events,
|
||||||
e.to_string()
|
e.to_string()
|
||||||
|
@ -372,9 +352,7 @@ pub async fn loop_consume_events(
|
||||||
METRIC_CONSUME_EVENTS_SUCCESS.inc();
|
METRIC_CONSUME_EVENTS_SUCCESS.inc();
|
||||||
info!(
|
info!(
|
||||||
"metricName=ConsumeEventsV4Success market={} durationMs={} consumed={}",
|
"metricName=ConsumeEventsV4Success market={} durationMs={} consumed={}",
|
||||||
perp_market.name(),
|
perp_market.name, confirmation_time, num_of_events,
|
||||||
confirmation_time,
|
|
||||||
num_of_events,
|
|
||||||
);
|
);
|
||||||
info!("{:?}", sig_result);
|
info!("{:?}", sig_result);
|
||||||
}
|
}
|
||||||
|
@ -384,7 +362,7 @@ pub async fn loop_consume_events(
|
||||||
pub async fn loop_update_funding(
|
pub async fn loop_update_funding(
|
||||||
mango_client: Arc<MangoClient>,
|
mango_client: Arc<MangoClient>,
|
||||||
pk: Pubkey,
|
pk: Pubkey,
|
||||||
perp_market: PerpMarket,
|
perp_market: &PerpMarketContext,
|
||||||
interval: u64,
|
interval: u64,
|
||||||
) {
|
) {
|
||||||
let mut interval = time::interval(Duration::from_secs(interval));
|
let mut interval = time::interval(Duration::from_secs(interval));
|
||||||
|
@ -417,7 +395,7 @@ pub async fn loop_update_funding(
|
||||||
METRIC_UPDATE_FUNDING_FAILURE.inc();
|
METRIC_UPDATE_FUNDING_FAILURE.inc();
|
||||||
error!(
|
error!(
|
||||||
"metricName=UpdateFundingV4Error market={} durationMs={} error={}",
|
"metricName=UpdateFundingV4Error market={} durationMs={} error={}",
|
||||||
perp_market.name(),
|
perp_market.name,
|
||||||
confirmation_time,
|
confirmation_time,
|
||||||
e.to_string()
|
e.to_string()
|
||||||
);
|
);
|
||||||
|
@ -426,8 +404,7 @@ pub async fn loop_update_funding(
|
||||||
METRIC_UPDATE_FUNDING_SUCCESS.inc();
|
METRIC_UPDATE_FUNDING_SUCCESS.inc();
|
||||||
info!(
|
info!(
|
||||||
"metricName=UpdateFundingV4Success market={} durationMs={}",
|
"metricName=UpdateFundingV4Success market={} durationMs={}",
|
||||||
perp_market.name(),
|
perp_market.name, confirmation_time,
|
||||||
confirmation_time,
|
|
||||||
);
|
);
|
||||||
info!("{:?}", sig_result);
|
info!("{:?}", sig_result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ pub async fn runner(
|
||||||
|
|
||||||
let mut price_arcs = HashMap::new();
|
let mut price_arcs = HashMap::new();
|
||||||
for s3_market in mango_client.context.serum3_markets.values() {
|
for s3_market in mango_client.context.serum3_markets.values() {
|
||||||
let base_token_index = s3_market.market.base_token_index;
|
let base_token_index = s3_market.base_token_index;
|
||||||
let price = mango_client
|
let price = mango_client
|
||||||
.bank_oracle_price(base_token_index)
|
.bank_oracle_price(base_token_index)
|
||||||
.await
|
.await
|
||||||
|
@ -47,11 +47,8 @@ pub async fn runner(
|
||||||
.map(|s3_market| {
|
.map(|s3_market| {
|
||||||
loop_blocking_orders(
|
loop_blocking_orders(
|
||||||
mango_client.clone(),
|
mango_client.clone(),
|
||||||
s3_market.market.name().to_string(),
|
s3_market.name.clone(),
|
||||||
price_arcs
|
price_arcs.get(&s3_market.base_token_index).unwrap().clone(),
|
||||||
.get(&s3_market.market.base_token_index)
|
|
||||||
.unwrap()
|
|
||||||
.clone(),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -70,7 +67,7 @@ async fn ensure_oo(mango_client: &Arc<MangoClient>) -> Result<(), anyhow::Error>
|
||||||
for (market_index, serum3_market) in mango_client.context.serum3_markets.iter() {
|
for (market_index, serum3_market) in mango_client.context.serum3_markets.iter() {
|
||||||
if account.serum3_orders(*market_index).is_err() {
|
if account.serum3_orders(*market_index).is_err() {
|
||||||
mango_client
|
mango_client
|
||||||
.serum3_create_open_orders(serum3_market.market.name())
|
.serum3_create_open_orders(&serum3_market.name)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,7 +178,6 @@ impl<'a> LiquidateHelper<'a> {
|
||||||
let max_perp_unsettled_leverage = I80F48::from_num(0.95);
|
let max_perp_unsettled_leverage = I80F48::from_num(0.95);
|
||||||
let perp_unsettled_cost = I80F48::ONE
|
let perp_unsettled_cost = I80F48::ONE
|
||||||
- perp
|
- perp
|
||||||
.market
|
|
||||||
.init_overall_asset_weight
|
.init_overall_asset_weight
|
||||||
.min(max_perp_unsettled_leverage);
|
.min(max_perp_unsettled_leverage);
|
||||||
let max_pnl_transfer = allowed_usdc_borrow / perp_unsettled_cost;
|
let max_pnl_transfer = allowed_usdc_borrow / perp_unsettled_cost;
|
||||||
|
@ -334,7 +333,7 @@ impl<'a> LiquidateHelper<'a> {
|
||||||
asset_usdc_equivalent.is_positive()
|
asset_usdc_equivalent.is_positive()
|
||||||
&& self
|
&& self
|
||||||
.allowed_asset_tokens
|
.allowed_asset_tokens
|
||||||
.contains(&self.client.context.token(*asset_token_index).mint_info.mint)
|
.contains(&self.client.context.token(*asset_token_index).mint)
|
||||||
})
|
})
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
anyhow::anyhow!(
|
anyhow::anyhow!(
|
||||||
|
@ -350,7 +349,7 @@ impl<'a> LiquidateHelper<'a> {
|
||||||
liab_usdc_equivalent.is_negative()
|
liab_usdc_equivalent.is_negative()
|
||||||
&& self
|
&& self
|
||||||
.allowed_liab_tokens
|
.allowed_liab_tokens
|
||||||
.contains(&self.client.context.token(*liab_token_index).mint_info.mint)
|
.contains(&self.client.context.token(*liab_token_index).mint)
|
||||||
})
|
})
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
anyhow::anyhow!(
|
anyhow::anyhow!(
|
||||||
|
@ -414,7 +413,7 @@ impl<'a> LiquidateHelper<'a> {
|
||||||
liab_usdc_equivalent.is_negative()
|
liab_usdc_equivalent.is_negative()
|
||||||
&& self
|
&& self
|
||||||
.allowed_liab_tokens
|
.allowed_liab_tokens
|
||||||
.contains(&self.client.context.token(*liab_token_index).mint_info.mint)
|
.contains(&self.client.context.token(*liab_token_index).mint)
|
||||||
})
|
})
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
anyhow::anyhow!(
|
anyhow::anyhow!(
|
||||||
|
@ -569,13 +568,7 @@ pub async fn maybe_liquidate_account(
|
||||||
|
|
||||||
let maint_health = health_cache.health(HealthType::Maint);
|
let maint_health = health_cache.health(HealthType::Maint);
|
||||||
|
|
||||||
let all_token_mints = HashSet::from_iter(
|
let all_token_mints = HashSet::from_iter(mango_client.context.tokens.values().map(|c| c.mint));
|
||||||
mango_client
|
|
||||||
.context
|
|
||||||
.tokens
|
|
||||||
.values()
|
|
||||||
.map(|c| c.mint_info.mint),
|
|
||||||
);
|
|
||||||
|
|
||||||
// try liquidating
|
// try liquidating
|
||||||
let maybe_txsig = LiquidateHelper {
|
let maybe_txsig = LiquidateHelper {
|
||||||
|
|
|
@ -198,15 +198,15 @@ async fn main() -> anyhow::Result<()> {
|
||||||
let mango_oracles = group_context
|
let mango_oracles = group_context
|
||||||
.tokens
|
.tokens
|
||||||
.values()
|
.values()
|
||||||
.map(|value| value.mint_info.oracle)
|
.map(|value| value.oracle)
|
||||||
.chain(group_context.perp_markets.values().map(|p| p.market.oracle))
|
.chain(group_context.perp_markets.values().map(|p| p.oracle))
|
||||||
.unique()
|
.unique()
|
||||||
.collect::<Vec<Pubkey>>();
|
.collect::<Vec<Pubkey>>();
|
||||||
|
|
||||||
let serum_programs = group_context
|
let serum_programs = group_context
|
||||||
.serum3_markets
|
.serum3_markets
|
||||||
.values()
|
.values()
|
||||||
.map(|s3| s3.market.serum_program)
|
.map(|s3| s3.serum_program)
|
||||||
.unique()
|
.unique()
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
|
@ -533,6 +533,12 @@ async fn main() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let check_changes_for_abort_job =
|
||||||
|
tokio::spawn(MangoClient::loop_check_for_context_changes_and_abort(
|
||||||
|
mango_client.clone(),
|
||||||
|
Duration::from_secs(300),
|
||||||
|
));
|
||||||
|
|
||||||
if cli.telemetry == BoolArg::True {
|
if cli.telemetry == BoolArg::True {
|
||||||
tokio::spawn(telemetry::report_regularly(
|
tokio::spawn(telemetry::report_regularly(
|
||||||
mango_client,
|
mango_client,
|
||||||
|
@ -546,6 +552,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
rebalance_job,
|
rebalance_job,
|
||||||
liquidation_job,
|
liquidation_job,
|
||||||
token_swap_info_job,
|
token_swap_info_job,
|
||||||
|
check_changes_for_abort_job,
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use mango_v4::accounts_zerocopy::KeyedAccountSharedData;
|
use mango_v4::accounts_zerocopy::KeyedAccountSharedData;
|
||||||
use mango_v4::state::{
|
use mango_v4::state::{
|
||||||
Bank, BookSide, MangoAccountValue, PerpPosition, PlaceOrderType, Side, TokenIndex,
|
Bank, BookSide, MangoAccountValue, PerpMarket, PerpPosition, PlaceOrderType, Side, TokenIndex,
|
||||||
QUOTE_TOKEN_INDEX,
|
QUOTE_TOKEN_INDEX,
|
||||||
};
|
};
|
||||||
use mango_v4_client::{
|
use mango_v4_client::{
|
||||||
|
@ -34,7 +34,7 @@ fn token_bank(
|
||||||
token: &TokenContext,
|
token: &TokenContext,
|
||||||
account_fetcher: &chain_data::AccountFetcher,
|
account_fetcher: &chain_data::AccountFetcher,
|
||||||
) -> anyhow::Result<Bank> {
|
) -> anyhow::Result<Bank> {
|
||||||
account_fetcher.fetch::<Bank>(&token.mint_info.first_bank())
|
account_fetcher.fetch::<Bank>(&token.first_bank())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Rebalancer {
|
pub struct Rebalancer {
|
||||||
|
@ -121,8 +121,8 @@ impl Rebalancer {
|
||||||
.get("SOL") // TODO: better use mint
|
.get("SOL") // TODO: better use mint
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
let quote_mint = quote_token.mint_info.mint;
|
let quote_mint = quote_token.mint;
|
||||||
let sol_mint = sol_token.mint_info.mint;
|
let sol_mint = sol_token.mint;
|
||||||
let jupiter_version = self.config.jupiter_version;
|
let jupiter_version = self.config.jupiter_version;
|
||||||
|
|
||||||
let full_route_job = self.jupiter_quote(
|
let full_route_job = self.jupiter_quote(
|
||||||
|
@ -143,7 +143,7 @@ impl Rebalancer {
|
||||||
// For the SOL -> output route we need to adjust the in amount by the SOL price
|
// For the SOL -> output route we need to adjust the in amount by the SOL price
|
||||||
let sol_price = self
|
let sol_price = self
|
||||||
.account_fetcher
|
.account_fetcher
|
||||||
.fetch_bank_price(&sol_token.mint_info.first_bank())?;
|
.fetch_bank_price(&sol_token.first_bank())?;
|
||||||
let in_amount_sol = (I80F48::from(in_amount_quote) / sol_price)
|
let in_amount_sol = (I80F48::from(in_amount_quote) / sol_price)
|
||||||
.ceil()
|
.ceil()
|
||||||
.to_num::<u64>();
|
.to_num::<u64>();
|
||||||
|
@ -199,8 +199,8 @@ impl Rebalancer {
|
||||||
.get("SOL") // TODO: better use mint
|
.get("SOL") // TODO: better use mint
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
let quote_mint = quote_token.mint_info.mint;
|
let quote_mint = quote_token.mint;
|
||||||
let sol_mint = sol_token.mint_info.mint;
|
let sol_mint = sol_token.mint;
|
||||||
let jupiter_version = self.config.jupiter_version;
|
let jupiter_version = self.config.jupiter_version;
|
||||||
|
|
||||||
let full_route_job =
|
let full_route_job =
|
||||||
|
@ -305,10 +305,8 @@ impl Rebalancer {
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let token_mint = token.mint_info.mint;
|
let token_mint = token.mint;
|
||||||
let token_price = self
|
let token_price = self.account_fetcher.fetch_bank_price(&token.first_bank())?;
|
||||||
.account_fetcher
|
|
||||||
.fetch_bank_price(&token.mint_info.first_bank())?;
|
|
||||||
|
|
||||||
// It's not always possible to bring the native balance to 0 through swaps:
|
// It's not always possible to bring the native balance to 0 through swaps:
|
||||||
// Consider a price <1. You need to sell a bunch of tokens to get 1 USDC native and
|
// Consider a price <1. You need to sell a bunch of tokens to get 1 USDC native and
|
||||||
|
@ -419,7 +417,7 @@ impl Rebalancer {
|
||||||
#[instrument(
|
#[instrument(
|
||||||
skip_all,
|
skip_all,
|
||||||
fields(
|
fields(
|
||||||
perp_market_name = perp.market.name(),
|
perp_market_name = perp.name,
|
||||||
base_lots = perp_position.base_position_lots(),
|
base_lots = perp_position.base_position_lots(),
|
||||||
effective_lots = perp_position.effective_base_position_lots(),
|
effective_lots = perp_position.effective_base_position_lots(),
|
||||||
quote_native = %perp_position.quote_position_native()
|
quote_native = %perp_position.quote_position_native()
|
||||||
|
@ -438,28 +436,28 @@ impl Rebalancer {
|
||||||
let base_lots = perp_position.base_position_lots();
|
let base_lots = perp_position.base_position_lots();
|
||||||
let effective_lots = perp_position.effective_base_position_lots();
|
let effective_lots = perp_position.effective_base_position_lots();
|
||||||
let quote_native = perp_position.quote_position_native();
|
let quote_native = perp_position.quote_position_native();
|
||||||
|
let perp_market: PerpMarket = self.account_fetcher.fetch(&perp.address)?;
|
||||||
|
|
||||||
if effective_lots != 0 {
|
if effective_lots != 0 {
|
||||||
// send an ioc order to reduce the base position
|
// send an ioc order to reduce the base position
|
||||||
let oracle_account_data = self.account_fetcher.fetch_raw(&perp.market.oracle)?;
|
let oracle_account_data = self.account_fetcher.fetch_raw(&perp.oracle)?;
|
||||||
let oracle_account =
|
let oracle_account = KeyedAccountSharedData::new(perp.oracle, oracle_account_data);
|
||||||
KeyedAccountSharedData::new(perp.market.oracle, oracle_account_data);
|
let oracle_price = perp_market.oracle_price(&oracle_account, None)?;
|
||||||
let oracle_price = perp.market.oracle_price(&oracle_account, None)?;
|
let oracle_price_lots = perp_market.native_price_to_lot(oracle_price);
|
||||||
let oracle_price_lots = perp.market.native_price_to_lot(oracle_price);
|
|
||||||
let (side, order_price, oo_lots) = if effective_lots > 0 {
|
let (side, order_price, oo_lots) = if effective_lots > 0 {
|
||||||
(
|
(
|
||||||
Side::Ask,
|
Side::Ask,
|
||||||
oracle_price * (I80F48::ONE - perp.market.base_liquidation_fee),
|
oracle_price * (I80F48::ONE - perp_market.base_liquidation_fee),
|
||||||
perp_position.asks_base_lots,
|
perp_position.asks_base_lots,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
Side::Bid,
|
Side::Bid,
|
||||||
oracle_price * (I80F48::ONE + perp.market.base_liquidation_fee),
|
oracle_price * (I80F48::ONE + perp_market.base_liquidation_fee),
|
||||||
perp_position.bids_base_lots,
|
perp_position.bids_base_lots,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let price_lots = perp.market.native_price_to_lot(order_price);
|
let price_lots = perp_market.native_price_to_lot(order_price);
|
||||||
let max_base_lots = effective_lots.abs() - oo_lots;
|
let max_base_lots = effective_lots.abs() - oo_lots;
|
||||||
if max_base_lots <= 0 {
|
if max_base_lots <= 0 {
|
||||||
warn!(?side, oo_lots, "cannot place reduce-only order",);
|
warn!(?side, oo_lots, "cannot place reduce-only order",);
|
||||||
|
@ -470,8 +468,8 @@ impl Rebalancer {
|
||||||
// even match anything. That way we don't need to pay the tx fee and
|
// even match anything. That way we don't need to pay the tx fee and
|
||||||
// ioc penalty fee unnecessarily.
|
// ioc penalty fee unnecessarily.
|
||||||
let opposite_side_key = match side.invert_side() {
|
let opposite_side_key = match side.invert_side() {
|
||||||
Side::Bid => perp.market.bids,
|
Side::Bid => perp.bids,
|
||||||
Side::Ask => perp.market.asks,
|
Side::Ask => perp.asks,
|
||||||
};
|
};
|
||||||
let bookside = Box::new(self.account_fetcher.fetch::<BookSide>(&opposite_side_key)?);
|
let bookside = Box::new(self.account_fetcher.fetch::<BookSide>(&opposite_side_key)?);
|
||||||
if bookside.quantity_at_price(price_lots, now_ts, oracle_price_lots) <= 0 {
|
if bookside.quantity_at_price(price_lots, now_ts, oracle_price_lots) <= 0 {
|
||||||
|
|
|
@ -94,8 +94,8 @@ impl TokenSwapInfoUpdater {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let token_mint = self.mango_client.context.mint_info(token_index).mint;
|
let token_mint = self.mango_client.context.token(token_index).mint;
|
||||||
let quote_mint = self.mango_client.context.mint_info(quote_index).mint;
|
let quote_mint = self.mango_client.context.token(quote_index).mint;
|
||||||
|
|
||||||
// these prices are in USD, which doesn't exist on chain
|
// these prices are in USD, which doesn't exist on chain
|
||||||
let token_price = self
|
let token_price = self
|
||||||
|
|
|
@ -364,7 +364,7 @@ impl Context {
|
||||||
&self,
|
&self,
|
||||||
token_index: TokenIndex,
|
token_index: TokenIndex,
|
||||||
) -> anyhow::Result<(Bank, I80F48, Pubkey)> {
|
) -> anyhow::Result<(Bank, I80F48, Pubkey)> {
|
||||||
let info = self.mango_client.context.mint_info(token_index);
|
let info = self.mango_client.context.token(token_index);
|
||||||
let (bank, price) = self
|
let (bank, price) = self
|
||||||
.account_fetcher
|
.account_fetcher
|
||||||
.fetch_bank_and_price(&info.first_bank())?;
|
.fetch_bank_and_price(&info.first_bank())?;
|
||||||
|
|
|
@ -60,10 +60,8 @@ pub fn max_swap_source_with_limits(
|
||||||
mango_v4_client::health_cache::new_sync(&client.context, account_fetcher, &account)
|
mango_v4_client::health_cache::new_sync(&client.context, account_fetcher, &account)
|
||||||
.expect("always ok");
|
.expect("always ok");
|
||||||
|
|
||||||
let source_bank: Bank =
|
let source_bank: Bank = account_fetcher.fetch(&client.context.token(source).first_bank())?;
|
||||||
account_fetcher.fetch(&client.context.mint_info(source).first_bank())?;
|
let target_bank: Bank = account_fetcher.fetch(&client.context.token(target).first_bank())?;
|
||||||
let target_bank: Bank =
|
|
||||||
account_fetcher.fetch(&client.context.mint_info(target).first_bank())?;
|
|
||||||
|
|
||||||
let source_price = health_cache.token_info(source).unwrap().prices.oracle;
|
let source_price = health_cache.token_info(source).unwrap().prices.oracle;
|
||||||
|
|
||||||
|
@ -104,10 +102,8 @@ pub fn max_swap_source_ignoring_limits(
|
||||||
mango_v4_client::health_cache::new_sync(&client.context, account_fetcher, &account)
|
mango_v4_client::health_cache::new_sync(&client.context, account_fetcher, &account)
|
||||||
.expect("always ok");
|
.expect("always ok");
|
||||||
|
|
||||||
let source_bank: Bank =
|
let source_bank: Bank = account_fetcher.fetch(&client.context.token(source).first_bank())?;
|
||||||
account_fetcher.fetch(&client.context.mint_info(source).first_bank())?;
|
let target_bank: Bank = account_fetcher.fetch(&client.context.token(target).first_bank())?;
|
||||||
let target_bank: Bank =
|
|
||||||
account_fetcher.fetch(&client.context.mint_info(target).first_bank())?;
|
|
||||||
|
|
||||||
let source_price = health_cache.token_info(source).unwrap().prices.oracle;
|
let source_price = health_cache.token_info(source).unwrap().prices.oracle;
|
||||||
|
|
||||||
|
|
|
@ -83,14 +83,14 @@ async fn main() -> anyhow::Result<()> {
|
||||||
let perp_queue_pks: Vec<_> = group_context
|
let perp_queue_pks: Vec<_> = group_context
|
||||||
.perp_markets
|
.perp_markets
|
||||||
.values()
|
.values()
|
||||||
.map(|context| (context.address, context.market.event_queue))
|
.map(|context| (context.address, context.event_queue))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// fetch all serum/openbook markets to find their event queues
|
// fetch all serum/openbook markets to find their event queues
|
||||||
let serum_market_pks: Vec<_> = group_context
|
let serum_market_pks: Vec<_> = group_context
|
||||||
.serum3_markets
|
.serum3_markets
|
||||||
.values()
|
.values()
|
||||||
.map(|context| context.market.serum_market_external)
|
.map(|context| context.serum_market_external)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let serum_market_ais = client
|
let serum_market_ais = client
|
||||||
|
|
|
@ -384,23 +384,22 @@ async fn main() -> anyhow::Result<()> {
|
||||||
.perp_markets
|
.perp_markets
|
||||||
.values()
|
.values()
|
||||||
.map(|context| {
|
.map(|context| {
|
||||||
let quote_decimals = match group_context.tokens.get(&context.market.settle_token_index)
|
let quote_decimals = match group_context.tokens.get(&context.settle_token_index) {
|
||||||
{
|
|
||||||
Some(token) => token.decimals,
|
Some(token) => token.decimals,
|
||||||
None => panic!("token not found for market"), // todo: default to 6 for usdc?
|
None => panic!("token not found for market"), // todo: default to 6 for usdc?
|
||||||
};
|
};
|
||||||
(
|
(
|
||||||
context.address,
|
context.address,
|
||||||
MarketConfig {
|
MarketConfig {
|
||||||
name: context.market.name().to_owned(),
|
name: context.name.clone(),
|
||||||
bids: context.market.bids,
|
bids: context.bids,
|
||||||
asks: context.market.asks,
|
asks: context.asks,
|
||||||
event_queue: context.market.event_queue,
|
event_queue: context.event_queue,
|
||||||
oracle: context.market.oracle,
|
oracle: context.oracle,
|
||||||
base_decimals: context.market.base_decimals,
|
base_decimals: context.base_decimals,
|
||||||
quote_decimals,
|
quote_decimals,
|
||||||
base_lot_size: context.market.base_lot_size,
|
base_lot_size: context.base_lot_size,
|
||||||
quote_lot_size: context.market.quote_lot_size,
|
quote_lot_size: context.quote_lot_size,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -410,18 +409,18 @@ async fn main() -> anyhow::Result<()> {
|
||||||
.serum3_markets
|
.serum3_markets
|
||||||
.values()
|
.values()
|
||||||
.map(|context| {
|
.map(|context| {
|
||||||
let base_decimals = match group_context.tokens.get(&context.market.base_token_index) {
|
let base_decimals = match group_context.tokens.get(&context.base_token_index) {
|
||||||
Some(token) => token.decimals,
|
Some(token) => token.decimals,
|
||||||
None => panic!("token not found for market"), // todo: default?
|
None => panic!("token not found for market"), // todo: default?
|
||||||
};
|
};
|
||||||
let quote_decimals = match group_context.tokens.get(&context.market.quote_token_index) {
|
let quote_decimals = match group_context.tokens.get(&context.quote_token_index) {
|
||||||
Some(token) => token.decimals,
|
Some(token) => token.decimals,
|
||||||
None => panic!("token not found for market"), // todo: default to 6 for usdc?
|
None => panic!("token not found for market"), // todo: default to 6 for usdc?
|
||||||
};
|
};
|
||||||
(
|
(
|
||||||
context.market.serum_market_external,
|
context.serum_market_external,
|
||||||
MarketConfig {
|
MarketConfig {
|
||||||
name: context.market.name().to_owned(),
|
name: context.name.clone(),
|
||||||
bids: context.bids,
|
bids: context.bids,
|
||||||
asks: context.asks,
|
asks: context.asks,
|
||||||
event_queue: context.event_q,
|
event_queue: context.event_q,
|
||||||
|
@ -438,7 +437,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
let perp_queue_pks: Vec<(Pubkey, Pubkey)> = group_context
|
let perp_queue_pks: Vec<(Pubkey, Pubkey)> = group_context
|
||||||
.perp_markets
|
.perp_markets
|
||||||
.values()
|
.values()
|
||||||
.map(|context| (context.address, context.market.event_queue))
|
.map(|context| (context.address, context.event_queue))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let _a: Vec<(String, String)> = group_context
|
let _a: Vec<(String, String)> = group_context
|
||||||
|
@ -446,20 +445,15 @@ async fn main() -> anyhow::Result<()> {
|
||||||
.values()
|
.values()
|
||||||
.map(|context| {
|
.map(|context| {
|
||||||
(
|
(
|
||||||
context.market.serum_market_external.to_string(),
|
context.serum_market_external.to_string(),
|
||||||
context.market.name().to_owned(),
|
context.name.clone(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let b: Vec<(String, String)> = group_context
|
let b: Vec<(String, String)> = group_context
|
||||||
.perp_markets
|
.perp_markets
|
||||||
.values()
|
.values()
|
||||||
.map(|context| {
|
.map(|context| (context.address.to_string(), context.name.clone()))
|
||||||
(
|
|
||||||
context.address.to_string(),
|
|
||||||
context.market.name().to_owned(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
let market_pubkey_strings: HashMap<String, String> = [b].concat().into_iter().collect();
|
let market_pubkey_strings: HashMap<String, String> = [b].concat().into_iter().collect();
|
||||||
|
|
||||||
|
|
|
@ -368,23 +368,22 @@ async fn main() -> anyhow::Result<()> {
|
||||||
.perp_markets
|
.perp_markets
|
||||||
.values()
|
.values()
|
||||||
.map(|context| {
|
.map(|context| {
|
||||||
let quote_decimals = match group_context.tokens.get(&context.market.settle_token_index)
|
let quote_decimals = match group_context.tokens.get(&context.settle_token_index) {
|
||||||
{
|
|
||||||
Some(token) => token.decimals,
|
Some(token) => token.decimals,
|
||||||
None => panic!("token not found for market"), // todo: default to 6 for usdc?
|
None => panic!("token not found for market"), // todo: default to 6 for usdc?
|
||||||
};
|
};
|
||||||
(
|
(
|
||||||
context.address,
|
context.address,
|
||||||
MarketConfig {
|
MarketConfig {
|
||||||
name: context.market.name().to_owned(),
|
name: context.name.clone(),
|
||||||
bids: context.market.bids,
|
bids: context.bids,
|
||||||
asks: context.market.asks,
|
asks: context.asks,
|
||||||
event_queue: context.market.event_queue,
|
event_queue: context.event_queue,
|
||||||
oracle: context.market.oracle,
|
oracle: context.oracle,
|
||||||
base_decimals: context.market.base_decimals,
|
base_decimals: context.base_decimals,
|
||||||
quote_decimals,
|
quote_decimals,
|
||||||
base_lot_size: context.market.base_lot_size,
|
base_lot_size: context.base_lot_size,
|
||||||
quote_lot_size: context.market.quote_lot_size,
|
quote_lot_size: context.quote_lot_size,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -394,18 +393,18 @@ async fn main() -> anyhow::Result<()> {
|
||||||
.serum3_markets
|
.serum3_markets
|
||||||
.values()
|
.values()
|
||||||
.map(|context| {
|
.map(|context| {
|
||||||
let base_decimals = match group_context.tokens.get(&context.market.base_token_index) {
|
let base_decimals = match group_context.tokens.get(&context.base_token_index) {
|
||||||
Some(token) => token.decimals,
|
Some(token) => token.decimals,
|
||||||
None => panic!("token not found for market"), // todo: default?
|
None => panic!("token not found for market"), // todo: default?
|
||||||
};
|
};
|
||||||
let quote_decimals = match group_context.tokens.get(&context.market.quote_token_index) {
|
let quote_decimals = match group_context.tokens.get(&context.quote_token_index) {
|
||||||
Some(token) => token.decimals,
|
Some(token) => token.decimals,
|
||||||
None => panic!("token not found for market"), // todo: default to 6 for usdc?
|
None => panic!("token not found for market"), // todo: default to 6 for usdc?
|
||||||
};
|
};
|
||||||
(
|
(
|
||||||
context.market.serum_market_external,
|
context.serum_market_external,
|
||||||
MarketConfig {
|
MarketConfig {
|
||||||
name: context.market.name().to_owned(),
|
name: context.name.clone(),
|
||||||
bids: context.bids,
|
bids: context.bids,
|
||||||
asks: context.asks,
|
asks: context.asks,
|
||||||
event_queue: context.event_q,
|
event_queue: context.event_q,
|
||||||
|
|
|
@ -65,7 +65,6 @@ async fn compute_pnl(
|
||||||
.perp_markets
|
.perp_markets
|
||||||
.get(&pp.market_index)
|
.get(&pp.market_index)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.market
|
|
||||||
.settle_token_index;
|
.settle_token_index;
|
||||||
let perp_settle_health = health_cache.perp_max_settle(settle_token_index).unwrap();
|
let perp_settle_health = health_cache.perp_max_settle(settle_token_index).unwrap();
|
||||||
let settleable_pnl = if pnl > 0 {
|
let settleable_pnl = if pnl > 0 {
|
||||||
|
|
|
@ -125,15 +125,15 @@ async fn main() -> anyhow::Result<()> {
|
||||||
let mango_oracles = group_context
|
let mango_oracles = group_context
|
||||||
.tokens
|
.tokens
|
||||||
.values()
|
.values()
|
||||||
.map(|value| value.mint_info.oracle)
|
.map(|value| value.oracle)
|
||||||
.chain(group_context.perp_markets.values().map(|p| p.market.oracle))
|
.chain(group_context.perp_markets.values().map(|p| p.oracle))
|
||||||
.unique()
|
.unique()
|
||||||
.collect::<Vec<Pubkey>>();
|
.collect::<Vec<Pubkey>>();
|
||||||
|
|
||||||
let serum_programs = group_context
|
let serum_programs = group_context
|
||||||
.serum3_markets
|
.serum3_markets
|
||||||
.values()
|
.values()
|
||||||
.map(|s3| s3.market.serum_program)
|
.map(|s3| s3.serum_program)
|
||||||
.unique()
|
.unique()
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
|
@ -337,10 +337,21 @@ async fn main() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let check_changes_for_abort_job =
|
||||||
|
tokio::spawn(MangoClient::loop_check_for_context_changes_and_abort(
|
||||||
|
mango_client.clone(),
|
||||||
|
Duration::from_secs(300),
|
||||||
|
));
|
||||||
|
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
let mut jobs: futures::stream::FuturesUnordered<_> = vec![data_job, settle_job, tcs_start_job]
|
let mut jobs: futures::stream::FuturesUnordered<_> = vec![
|
||||||
.into_iter()
|
data_job,
|
||||||
.collect();
|
settle_job,
|
||||||
|
tcs_start_job,
|
||||||
|
check_changes_for_abort_job,
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
jobs.next().await;
|
jobs.next().await;
|
||||||
|
|
||||||
error!("a critical job aborted, exiting");
|
error!("a critical job aborted, exiting");
|
||||||
|
|
|
@ -42,7 +42,7 @@ fn perp_markets_and_prices(
|
||||||
|
|
||||||
let settle_token = mango_client.context.token(perp_market.settle_token_index);
|
let settle_token = mango_client.context.token(perp_market.settle_token_index);
|
||||||
let settle_token_price =
|
let settle_token_price =
|
||||||
account_fetcher.fetch_bank_price(&settle_token.mint_info.first_bank())?;
|
account_fetcher.fetch_bank_price(&settle_token.first_bank())?;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
*market_index,
|
*market_index,
|
||||||
|
|
|
@ -112,7 +112,7 @@ impl State {
|
||||||
|
|
||||||
// Clear newly created token positions, so the liqor account is mostly empty
|
// Clear newly created token positions, so the liqor account is mostly empty
|
||||||
for token_index in startable_chunk.iter().map(|(_, _, ti)| *ti).unique() {
|
for token_index in startable_chunk.iter().map(|(_, _, ti)| *ti).unique() {
|
||||||
let mint = mango_client.context.token(token_index).mint_info.mint;
|
let mint = mango_client.context.token(token_index).mint;
|
||||||
instructions.append(mango_client.token_withdraw_instructions(
|
instructions.append(mango_client.token_withdraw_instructions(
|
||||||
&liqor_account,
|
&liqor_account,
|
||||||
mint,
|
mint,
|
||||||
|
@ -166,12 +166,7 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn oracle_for_token(&self, token_index: TokenIndex) -> anyhow::Result<I80F48> {
|
fn oracle_for_token(&self, token_index: TokenIndex) -> anyhow::Result<I80F48> {
|
||||||
let bank_pk = self
|
let bank_pk = self.mango_client.context.token(token_index).first_bank();
|
||||||
.mango_client
|
|
||||||
.context
|
|
||||||
.token(token_index)
|
|
||||||
.mint_info
|
|
||||||
.first_bank();
|
|
||||||
self.account_fetcher.fetch_bank_price(&bank_pk)
|
self.account_fetcher.fetch_bank_price(&bank_pk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +196,6 @@ impl State {
|
||||||
.mango_client
|
.mango_client
|
||||||
.context
|
.context
|
||||||
.token(tcs.sell_token_index)
|
.token(tcs.sell_token_index)
|
||||||
.mint_info
|
|
||||||
.first_bank();
|
.first_bank();
|
||||||
let mut sell_bank: Bank = self.account_fetcher.fetch(&sell_bank_pk)?;
|
let mut sell_bank: Bank = self.account_fetcher.fetch(&sell_bank_pk)?;
|
||||||
let sell_pos = account.token_position(tcs.sell_token_index)?;
|
let sell_pos = account.token_position(tcs.sell_token_index)?;
|
||||||
|
|
|
@ -18,7 +18,7 @@ use itertools::Itertools;
|
||||||
use mango_v4::accounts_ix::{Serum3OrderType, Serum3SelfTradeBehavior, Serum3Side};
|
use mango_v4::accounts_ix::{Serum3OrderType, Serum3SelfTradeBehavior, Serum3Side};
|
||||||
use mango_v4::accounts_zerocopy::KeyedAccountSharedData;
|
use mango_v4::accounts_zerocopy::KeyedAccountSharedData;
|
||||||
use mango_v4::state::{
|
use mango_v4::state::{
|
||||||
Bank, Group, MangoAccountValue, PerpMarketIndex, PlaceOrderType, SelfTradeBehavior,
|
Bank, Group, MangoAccountValue, PerpMarket, PerpMarketIndex, PlaceOrderType, SelfTradeBehavior,
|
||||||
Serum3MarketIndex, Side, TokenIndex, INSURANCE_TOKEN_INDEX,
|
Serum3MarketIndex, Side, TokenIndex, INSURANCE_TOKEN_INDEX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -295,7 +295,7 @@ impl MangoClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn first_bank(&self, token_index: TokenIndex) -> anyhow::Result<Bank> {
|
pub async fn first_bank(&self, token_index: TokenIndex) -> anyhow::Result<Bank> {
|
||||||
let bank_address = self.context.mint_info(token_index).first_bank();
|
let bank_address = self.context.token(token_index).first_bank();
|
||||||
account_fetcher_fetch_anchor_account(&*self.account_fetcher, &bank_address).await
|
account_fetcher_fetch_anchor_account(&*self.account_fetcher, &bank_address).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,7 +338,6 @@ impl MangoClient {
|
||||||
) -> anyhow::Result<Signature> {
|
) -> anyhow::Result<Signature> {
|
||||||
let token = self.context.token_by_mint(&mint)?;
|
let token = self.context.token_by_mint(&mint)?;
|
||||||
let token_index = token.token_index;
|
let token_index = token.token_index;
|
||||||
let mint_info = token.mint_info;
|
|
||||||
|
|
||||||
let (health_check_metas, health_cu) = self
|
let (health_check_metas, health_cu) = self
|
||||||
.derive_health_check_remaining_account_metas(vec![token_index], vec![], vec![])
|
.derive_health_check_remaining_account_metas(vec![token_index], vec![], vec![])
|
||||||
|
@ -353,13 +352,10 @@ impl MangoClient {
|
||||||
group: self.group(),
|
group: self.group(),
|
||||||
account: self.mango_account_address,
|
account: self.mango_account_address,
|
||||||
owner: self.owner(),
|
owner: self.owner(),
|
||||||
bank: mint_info.first_bank(),
|
bank: token.first_bank(),
|
||||||
vault: mint_info.first_vault(),
|
vault: token.first_vault(),
|
||||||
oracle: mint_info.oracle,
|
oracle: token.oracle,
|
||||||
token_account: get_associated_token_address(
|
token_account: get_associated_token_address(&self.owner(), &token.mint),
|
||||||
&self.owner(),
|
|
||||||
&mint_info.mint,
|
|
||||||
),
|
|
||||||
token_authority: self.owner(),
|
token_authority: self.owner(),
|
||||||
token_program: Token::id(),
|
token_program: Token::id(),
|
||||||
},
|
},
|
||||||
|
@ -390,7 +386,6 @@ impl MangoClient {
|
||||||
) -> anyhow::Result<PreparedInstructions> {
|
) -> anyhow::Result<PreparedInstructions> {
|
||||||
let token = self.context.token_by_mint(&mint)?;
|
let token = self.context.token_by_mint(&mint)?;
|
||||||
let token_index = token.token_index;
|
let token_index = token.token_index;
|
||||||
let mint_info = token.mint_info;
|
|
||||||
|
|
||||||
let (health_check_metas, health_cu) =
|
let (health_check_metas, health_cu) =
|
||||||
self.context.derive_health_check_remaining_account_metas(
|
self.context.derive_health_check_remaining_account_metas(
|
||||||
|
@ -416,12 +411,12 @@ impl MangoClient {
|
||||||
group: self.group(),
|
group: self.group(),
|
||||||
account: self.mango_account_address,
|
account: self.mango_account_address,
|
||||||
owner: self.owner(),
|
owner: self.owner(),
|
||||||
bank: mint_info.first_bank(),
|
bank: token.first_bank(),
|
||||||
vault: mint_info.first_vault(),
|
vault: token.first_vault(),
|
||||||
oracle: mint_info.oracle,
|
oracle: token.oracle,
|
||||||
token_account: get_associated_token_address(
|
token_account: get_associated_token_address(
|
||||||
&self.owner(),
|
&self.owner(),
|
||||||
&mint_info.mint,
|
&token.mint,
|
||||||
),
|
),
|
||||||
token_program: Token::id(),
|
token_program: Token::id(),
|
||||||
},
|
},
|
||||||
|
@ -454,7 +449,7 @@ impl MangoClient {
|
||||||
|
|
||||||
pub async fn bank_oracle_price(&self, token_index: TokenIndex) -> anyhow::Result<I80F48> {
|
pub async fn bank_oracle_price(&self, token_index: TokenIndex) -> anyhow::Result<I80F48> {
|
||||||
let bank = self.first_bank(token_index).await?;
|
let bank = self.first_bank(token_index).await?;
|
||||||
let mint_info = self.context.mint_info(token_index);
|
let mint_info = self.context.token(token_index);
|
||||||
let oracle = self
|
let oracle = self
|
||||||
.account_fetcher
|
.account_fetcher
|
||||||
.fetch_raw_account(&mint_info.oracle)
|
.fetch_raw_account(&mint_info.oracle)
|
||||||
|
@ -471,12 +466,11 @@ impl MangoClient {
|
||||||
perp_market_index: PerpMarketIndex,
|
perp_market_index: PerpMarketIndex,
|
||||||
) -> anyhow::Result<I80F48> {
|
) -> anyhow::Result<I80F48> {
|
||||||
let perp = self.context.perp(perp_market_index);
|
let perp = self.context.perp(perp_market_index);
|
||||||
let oracle = self
|
let perp_market: PerpMarket =
|
||||||
.account_fetcher
|
account_fetcher_fetch_anchor_account(&*self.account_fetcher, &perp.address).await?;
|
||||||
.fetch_raw_account(&perp.market.oracle)
|
let oracle = self.account_fetcher.fetch_raw_account(&perp.oracle).await?;
|
||||||
.await?;
|
let price = perp_market.oracle_price(
|
||||||
let price = perp.market.oracle_price(
|
&KeyedAccountSharedData::new(perp.oracle, oracle.into()),
|
||||||
&KeyedAccountSharedData::new(perp.market.oracle, oracle.into()),
|
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
Ok(price)
|
Ok(price)
|
||||||
|
@ -510,8 +504,8 @@ impl MangoClient {
|
||||||
group: self.group(),
|
group: self.group(),
|
||||||
account: account_pubkey,
|
account: account_pubkey,
|
||||||
serum_market: s3.address,
|
serum_market: s3.address,
|
||||||
serum_program: s3.market.serum_program,
|
serum_program: s3.serum_program,
|
||||||
serum_market_external: s3.market.serum_market_external,
|
serum_market_external: s3.serum_market_external,
|
||||||
open_orders,
|
open_orders,
|
||||||
owner: self.owner(),
|
owner: self.owner(),
|
||||||
payer: self.owner(),
|
payer: self.owner(),
|
||||||
|
@ -558,9 +552,9 @@ impl MangoClient {
|
||||||
.context
|
.context
|
||||||
.derive_health_check_remaining_account_metas(account, vec![], vec![], vec![])?;
|
.derive_health_check_remaining_account_metas(account, vec![], vec![], vec![])?;
|
||||||
|
|
||||||
let payer_mint_info = match side {
|
let payer_token = match side {
|
||||||
Serum3Side::Bid => quote.mint_info,
|
Serum3Side::Bid => "e,
|
||||||
Serum3Side::Ask => base.mint_info,
|
Serum3Side::Ask => &base,
|
||||||
};
|
};
|
||||||
|
|
||||||
let ixs = PreparedInstructions::from_single(
|
let ixs = PreparedInstructions::from_single(
|
||||||
|
@ -572,12 +566,12 @@ impl MangoClient {
|
||||||
group: self.group(),
|
group: self.group(),
|
||||||
account: self.mango_account_address,
|
account: self.mango_account_address,
|
||||||
open_orders,
|
open_orders,
|
||||||
payer_bank: payer_mint_info.first_bank(),
|
payer_bank: payer_token.first_bank(),
|
||||||
payer_vault: payer_mint_info.first_vault(),
|
payer_vault: payer_token.first_vault(),
|
||||||
payer_oracle: payer_mint_info.oracle,
|
payer_oracle: payer_token.oracle,
|
||||||
serum_market: s3.address,
|
serum_market: s3.address,
|
||||||
serum_program: s3.market.serum_program,
|
serum_program: s3.serum_program,
|
||||||
serum_market_external: s3.market.serum_market_external,
|
serum_market_external: s3.serum_market_external,
|
||||||
market_bids: s3.bids,
|
market_bids: s3.bids,
|
||||||
market_asks: s3.asks,
|
market_asks: s3.asks,
|
||||||
market_event_queue: s3.event_q,
|
market_event_queue: s3.event_q,
|
||||||
|
@ -660,13 +654,13 @@ impl MangoClient {
|
||||||
group: self.group(),
|
group: self.group(),
|
||||||
account: self.mango_account_address,
|
account: self.mango_account_address,
|
||||||
open_orders,
|
open_orders,
|
||||||
quote_bank: quote.mint_info.first_bank(),
|
quote_bank: quote.first_bank(),
|
||||||
quote_vault: quote.mint_info.first_vault(),
|
quote_vault: quote.first_vault(),
|
||||||
base_bank: base.mint_info.first_bank(),
|
base_bank: base.first_bank(),
|
||||||
base_vault: base.mint_info.first_vault(),
|
base_vault: base.first_vault(),
|
||||||
serum_market: s3.address,
|
serum_market: s3.address,
|
||||||
serum_program: s3.market.serum_program,
|
serum_program: s3.serum_program,
|
||||||
serum_market_external: s3.market.serum_market_external,
|
serum_market_external: s3.serum_market_external,
|
||||||
market_base_vault: s3.coin_vault,
|
market_base_vault: s3.coin_vault,
|
||||||
market_quote_vault: s3.pc_vault,
|
market_quote_vault: s3.pc_vault,
|
||||||
market_vault_signer: s3.vault_signer,
|
market_vault_signer: s3.vault_signer,
|
||||||
|
@ -674,8 +668,8 @@ impl MangoClient {
|
||||||
token_program: Token::id(),
|
token_program: Token::id(),
|
||||||
},
|
},
|
||||||
v2: mango_v4::accounts::Serum3SettleFundsV2Extra {
|
v2: mango_v4::accounts::Serum3SettleFundsV2Extra {
|
||||||
quote_oracle: quote.mint_info.oracle,
|
quote_oracle: quote.oracle,
|
||||||
base_oracle: base.mint_info.oracle,
|
base_oracle: base.oracle,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
|
@ -708,8 +702,8 @@ impl MangoClient {
|
||||||
market_asks: s3.asks,
|
market_asks: s3.asks,
|
||||||
market_event_queue: s3.event_q,
|
market_event_queue: s3.event_q,
|
||||||
serum_market: s3.address,
|
serum_market: s3.address,
|
||||||
serum_program: s3.market.serum_program,
|
serum_program: s3.serum_program,
|
||||||
serum_market_external: s3.market.serum_market_external,
|
serum_market_external: s3.serum_market_external,
|
||||||
owner: self.owner(),
|
owner: self.owner(),
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
|
@ -782,18 +776,18 @@ impl MangoClient {
|
||||||
account: *liqee.0,
|
account: *liqee.0,
|
||||||
open_orders: *open_orders,
|
open_orders: *open_orders,
|
||||||
serum_market: s3.address,
|
serum_market: s3.address,
|
||||||
serum_program: s3.market.serum_program,
|
serum_program: s3.serum_program,
|
||||||
serum_market_external: s3.market.serum_market_external,
|
serum_market_external: s3.serum_market_external,
|
||||||
market_bids: s3.bids,
|
market_bids: s3.bids,
|
||||||
market_asks: s3.asks,
|
market_asks: s3.asks,
|
||||||
market_event_queue: s3.event_q,
|
market_event_queue: s3.event_q,
|
||||||
market_base_vault: s3.coin_vault,
|
market_base_vault: s3.coin_vault,
|
||||||
market_quote_vault: s3.pc_vault,
|
market_quote_vault: s3.pc_vault,
|
||||||
market_vault_signer: s3.vault_signer,
|
market_vault_signer: s3.vault_signer,
|
||||||
quote_bank: quote.mint_info.first_bank(),
|
quote_bank: quote.first_bank(),
|
||||||
quote_vault: quote.mint_info.first_vault(),
|
quote_vault: quote.first_vault(),
|
||||||
base_bank: base.mint_info.first_bank(),
|
base_bank: base.first_bank(),
|
||||||
base_vault: base.mint_info.first_vault(),
|
base_vault: base.first_vault(),
|
||||||
token_program: Token::id(),
|
token_program: Token::id(),
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
|
@ -832,8 +826,8 @@ impl MangoClient {
|
||||||
group: self.group(),
|
group: self.group(),
|
||||||
account: self.mango_account_address,
|
account: self.mango_account_address,
|
||||||
serum_market: s3.address,
|
serum_market: s3.address,
|
||||||
serum_program: s3.market.serum_program,
|
serum_program: s3.serum_program,
|
||||||
serum_market_external: s3.market.serum_market_external,
|
serum_market_external: s3.serum_market_external,
|
||||||
open_orders,
|
open_orders,
|
||||||
market_bids: s3.bids,
|
market_bids: s3.bids,
|
||||||
market_asks: s3.asks,
|
market_asks: s3.asks,
|
||||||
|
@ -890,10 +884,10 @@ impl MangoClient {
|
||||||
account: self.mango_account_address,
|
account: self.mango_account_address,
|
||||||
owner: self.owner(),
|
owner: self.owner(),
|
||||||
perp_market: perp.address,
|
perp_market: perp.address,
|
||||||
bids: perp.market.bids,
|
bids: perp.bids,
|
||||||
asks: perp.market.asks,
|
asks: perp.asks,
|
||||||
event_queue: perp.market.event_queue,
|
event_queue: perp.event_queue,
|
||||||
oracle: perp.market.oracle,
|
oracle: perp.oracle,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
@ -972,8 +966,8 @@ impl MangoClient {
|
||||||
account: self.mango_account_address,
|
account: self.mango_account_address,
|
||||||
owner: self.owner(),
|
owner: self.owner(),
|
||||||
perp_market: perp.address,
|
perp_market: perp.address,
|
||||||
bids: perp.market.bids,
|
bids: perp.bids,
|
||||||
asks: perp.market.asks,
|
asks: perp.asks,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
@ -1030,7 +1024,7 @@ impl MangoClient {
|
||||||
account_b: (&Pubkey, &MangoAccountValue),
|
account_b: (&Pubkey, &MangoAccountValue),
|
||||||
) -> anyhow::Result<PreparedInstructions> {
|
) -> anyhow::Result<PreparedInstructions> {
|
||||||
let perp = self.context.perp(market_index);
|
let perp = self.context.perp(market_index);
|
||||||
let settlement_token = self.context.token(perp.market.settle_token_index);
|
let settlement_token = self.context.token(perp.settle_token_index);
|
||||||
|
|
||||||
let (health_remaining_ams, health_cu) = self
|
let (health_remaining_ams, health_cu) = self
|
||||||
.context
|
.context
|
||||||
|
@ -1054,9 +1048,9 @@ impl MangoClient {
|
||||||
perp_market: perp.address,
|
perp_market: perp.address,
|
||||||
account_a: *account_a.0,
|
account_a: *account_a.0,
|
||||||
account_b: *account_b.0,
|
account_b: *account_b.0,
|
||||||
oracle: perp.market.oracle,
|
oracle: perp.oracle,
|
||||||
settle_bank: settlement_token.mint_info.first_bank(),
|
settle_bank: settlement_token.first_bank(),
|
||||||
settle_oracle: settlement_token.mint_info.oracle,
|
settle_oracle: settlement_token.oracle,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
@ -1103,8 +1097,8 @@ impl MangoClient {
|
||||||
group: self.group(),
|
group: self.group(),
|
||||||
account: *liqee.0,
|
account: *liqee.0,
|
||||||
perp_market: perp.address,
|
perp_market: perp.address,
|
||||||
bids: perp.market.bids,
|
bids: perp.bids,
|
||||||
asks: perp.market.asks,
|
asks: perp.asks,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
@ -1130,7 +1124,7 @@ impl MangoClient {
|
||||||
max_pnl_transfer: u64,
|
max_pnl_transfer: u64,
|
||||||
) -> anyhow::Result<PreparedInstructions> {
|
) -> anyhow::Result<PreparedInstructions> {
|
||||||
let perp = self.context.perp(market_index);
|
let perp = self.context.perp(market_index);
|
||||||
let settle_token_info = self.context.token(perp.market.settle_token_index);
|
let settle_token_info = self.context.token(perp.settle_token_index);
|
||||||
|
|
||||||
let (health_remaining_ams, health_cu) = self
|
let (health_remaining_ams, health_cu) = self
|
||||||
.derive_liquidation_health_check_remaining_account_metas(liqee.1, &[], &[])
|
.derive_liquidation_health_check_remaining_account_metas(liqee.1, &[], &[])
|
||||||
|
@ -1144,13 +1138,13 @@ impl MangoClient {
|
||||||
&mango_v4::accounts::PerpLiqBaseOrPositivePnl {
|
&mango_v4::accounts::PerpLiqBaseOrPositivePnl {
|
||||||
group: self.group(),
|
group: self.group(),
|
||||||
perp_market: perp.address,
|
perp_market: perp.address,
|
||||||
oracle: perp.market.oracle,
|
oracle: perp.oracle,
|
||||||
liqor: self.mango_account_address,
|
liqor: self.mango_account_address,
|
||||||
liqor_owner: self.owner(),
|
liqor_owner: self.owner(),
|
||||||
liqee: *liqee.0,
|
liqee: *liqee.0,
|
||||||
settle_bank: settle_token_info.mint_info.first_bank(),
|
settle_bank: settle_token_info.first_bank(),
|
||||||
settle_vault: settle_token_info.mint_info.first_vault(),
|
settle_vault: settle_token_info.first_vault(),
|
||||||
settle_oracle: settle_token_info.mint_info.oracle,
|
settle_oracle: settle_token_info.oracle,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
@ -1183,7 +1177,7 @@ impl MangoClient {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let perp = self.context.perp(market_index);
|
let perp = self.context.perp(market_index);
|
||||||
let settle_token_info = self.context.token(perp.market.settle_token_index);
|
let settle_token_info = self.context.token(perp.settle_token_index);
|
||||||
let insurance_token_info = self.context.token(INSURANCE_TOKEN_INDEX);
|
let insurance_token_info = self.context.token(INSURANCE_TOKEN_INDEX);
|
||||||
|
|
||||||
let (health_remaining_ams, health_cu) = self
|
let (health_remaining_ams, health_cu) = self
|
||||||
|
@ -1202,17 +1196,17 @@ impl MangoClient {
|
||||||
&mango_v4::accounts::PerpLiqNegativePnlOrBankruptcyV2 {
|
&mango_v4::accounts::PerpLiqNegativePnlOrBankruptcyV2 {
|
||||||
group: self.group(),
|
group: self.group(),
|
||||||
perp_market: perp.address,
|
perp_market: perp.address,
|
||||||
oracle: perp.market.oracle,
|
oracle: perp.oracle,
|
||||||
liqor: self.mango_account_address,
|
liqor: self.mango_account_address,
|
||||||
liqor_owner: self.owner(),
|
liqor_owner: self.owner(),
|
||||||
liqee: *liqee.0,
|
liqee: *liqee.0,
|
||||||
settle_bank: settle_token_info.mint_info.first_bank(),
|
settle_bank: settle_token_info.first_bank(),
|
||||||
settle_vault: settle_token_info.mint_info.first_vault(),
|
settle_vault: settle_token_info.first_vault(),
|
||||||
settle_oracle: settle_token_info.mint_info.oracle,
|
settle_oracle: settle_token_info.oracle,
|
||||||
insurance_vault: group.insurance_vault,
|
insurance_vault: group.insurance_vault,
|
||||||
insurance_bank: insurance_token_info.mint_info.first_bank(),
|
insurance_bank: insurance_token_info.first_bank(),
|
||||||
insurance_bank_vault: insurance_token_info.mint_info.first_vault(),
|
insurance_bank_vault: insurance_token_info.first_vault(),
|
||||||
insurance_oracle: insurance_token_info.mint_info.oracle,
|
insurance_oracle: insurance_token_info.oracle,
|
||||||
token_program: Token::id(),
|
token_program: Token::id(),
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
|
@ -1289,7 +1283,6 @@ impl MangoClient {
|
||||||
let liab_info = self.context.token(liab_token_index);
|
let liab_info = self.context.token(liab_token_index);
|
||||||
|
|
||||||
let bank_remaining_ams = liab_info
|
let bank_remaining_ams = liab_info
|
||||||
.mint_info
|
|
||||||
.banks()
|
.banks()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|bank_pubkey| util::to_writable_account_meta(*bank_pubkey))
|
.map(|bank_pubkey| util::to_writable_account_meta(*bank_pubkey))
|
||||||
|
@ -1320,7 +1313,7 @@ impl MangoClient {
|
||||||
liqor: self.mango_account_address,
|
liqor: self.mango_account_address,
|
||||||
liqor_owner: self.owner(),
|
liqor_owner: self.owner(),
|
||||||
liab_mint_info: liab_info.mint_info_address,
|
liab_mint_info: liab_info.mint_info_address,
|
||||||
quote_vault: quote_info.mint_info.first_vault(),
|
quote_vault: quote_info.first_vault(),
|
||||||
insurance_vault: group.insurance_vault,
|
insurance_vault: group.insurance_vault,
|
||||||
token_program: Token::id(),
|
token_program: Token::id(),
|
||||||
},
|
},
|
||||||
|
@ -1664,6 +1657,30 @@ impl MangoClient {
|
||||||
.simulate(&self.client)
|
.simulate(&self.client)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn loop_check_for_context_changes_and_abort(
|
||||||
|
mango_client: Arc<MangoClient>,
|
||||||
|
interval: Duration,
|
||||||
|
) {
|
||||||
|
let mut delay = tokio::time::interval(interval);
|
||||||
|
let rpc_async = mango_client.client.rpc_async();
|
||||||
|
loop {
|
||||||
|
delay.tick().await;
|
||||||
|
|
||||||
|
let new_context =
|
||||||
|
match MangoGroupContext::new_from_rpc(&rpc_async, mango_client.group()).await {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!("could not fetch context to check for changes: {e:?}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if mango_client.context.changed_significantly(&new_context) {
|
||||||
|
std::process::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
|
@ -2,11 +2,10 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use anchor_client::ClientError;
|
use anchor_client::ClientError;
|
||||||
|
|
||||||
use anchor_lang::__private::bytemuck::{self, Zeroable};
|
use anchor_lang::__private::bytemuck;
|
||||||
|
|
||||||
use mango_v4::state::{
|
use mango_v4::state::{
|
||||||
Bank, Group, MangoAccountValue, MintInfo, PerpMarket, PerpMarketIndex, Serum3Market,
|
Group, MangoAccountValue, PerpMarketIndex, Serum3MarketIndex, TokenIndex, MAX_BANKS,
|
||||||
Serum3MarketIndex, TokenIndex,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use fixed::types::I80F48;
|
use fixed::types::I80F48;
|
||||||
|
@ -20,26 +19,51 @@ use solana_sdk::account::Account;
|
||||||
use solana_sdk::instruction::AccountMeta;
|
use solana_sdk::instruction::AccountMeta;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub struct TokenContext {
|
pub struct TokenContext {
|
||||||
|
pub group: Pubkey,
|
||||||
pub token_index: TokenIndex,
|
pub token_index: TokenIndex,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub mint_info: MintInfo,
|
pub mint: Pubkey,
|
||||||
|
pub oracle: Pubkey,
|
||||||
|
pub banks: [Pubkey; MAX_BANKS],
|
||||||
|
pub vaults: [Pubkey; MAX_BANKS],
|
||||||
|
pub fallback_oracle: Pubkey,
|
||||||
pub mint_info_address: Pubkey,
|
pub mint_info_address: Pubkey,
|
||||||
pub decimals: u8,
|
pub decimals: u8,
|
||||||
/// Bank snapshot is never updated, only use static parts!
|
|
||||||
pub bank: Bank,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenContext {
|
impl TokenContext {
|
||||||
pub fn native_to_ui(&self, native: I80F48) -> f64 {
|
pub fn native_to_ui(&self, native: I80F48) -> f64 {
|
||||||
(native / I80F48::from(10u64.pow(self.decimals.into()))).to_num()
|
(native / I80F48::from(10u64.pow(self.decimals.into()))).to_num()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn first_bank(&self) -> Pubkey {
|
||||||
|
self.banks[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn first_vault(&self) -> Pubkey {
|
||||||
|
self.vaults[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn banks(&self) -> &[Pubkey] {
|
||||||
|
let n_banks = self
|
||||||
|
.banks
|
||||||
|
.iter()
|
||||||
|
.position(|&b| b == Pubkey::default())
|
||||||
|
.unwrap_or(MAX_BANKS);
|
||||||
|
&self.banks[..n_banks]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub struct Serum3MarketContext {
|
pub struct Serum3MarketContext {
|
||||||
pub address: Pubkey,
|
pub address: Pubkey,
|
||||||
pub market: Serum3Market,
|
pub name: String,
|
||||||
|
pub serum_program: Pubkey,
|
||||||
|
pub serum_market_external: Pubkey,
|
||||||
|
pub base_token_index: TokenIndex,
|
||||||
|
pub quote_token_index: TokenIndex,
|
||||||
pub bids: Pubkey,
|
pub bids: Pubkey,
|
||||||
pub asks: Pubkey,
|
pub asks: Pubkey,
|
||||||
pub event_q: Pubkey,
|
pub event_q: Pubkey,
|
||||||
|
@ -51,10 +75,21 @@ pub struct Serum3MarketContext {
|
||||||
pub pc_lot_size: u64,
|
pub pc_lot_size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub struct PerpMarketContext {
|
pub struct PerpMarketContext {
|
||||||
|
pub group: Pubkey,
|
||||||
|
pub perp_market_index: PerpMarketIndex,
|
||||||
|
pub settle_token_index: TokenIndex,
|
||||||
pub address: Pubkey,
|
pub address: Pubkey,
|
||||||
/// PerpMarket snapshot is never updated, only use static parts!
|
pub name: String,
|
||||||
pub market: PerpMarket,
|
pub bids: Pubkey,
|
||||||
|
pub asks: Pubkey,
|
||||||
|
pub event_queue: Pubkey,
|
||||||
|
pub oracle: Pubkey,
|
||||||
|
pub base_lot_size: i64,
|
||||||
|
pub quote_lot_size: i64,
|
||||||
|
pub base_decimals: u8,
|
||||||
|
pub init_overall_asset_weight: I80F48,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ComputeEstimates {
|
pub struct ComputeEstimates {
|
||||||
|
@ -128,10 +163,6 @@ impl MangoGroupContext {
|
||||||
self.token(token_index).mint_info_address
|
self.token(token_index).mint_info_address
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mint_info(&self, token_index: TokenIndex) -> MintInfo {
|
|
||||||
self.token(token_index).mint_info
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn perp(&self, perp_market_index: PerpMarketIndex) -> &PerpMarketContext {
|
pub fn perp(&self, perp_market_index: PerpMarketIndex) -> &PerpMarketContext {
|
||||||
self.perp_markets.get(&perp_market_index).unwrap()
|
self.perp_markets.get(&perp_market_index).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -149,11 +180,11 @@ impl MangoGroupContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serum3_base_token(&self, market_index: Serum3MarketIndex) -> &TokenContext {
|
pub fn serum3_base_token(&self, market_index: Serum3MarketIndex) -> &TokenContext {
|
||||||
self.token(self.serum3(market_index).market.base_token_index)
|
self.token(self.serum3(market_index).base_token_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serum3_quote_token(&self, market_index: Serum3MarketIndex) -> &TokenContext {
|
pub fn serum3_quote_token(&self, market_index: Serum3MarketIndex) -> &TokenContext {
|
||||||
self.token(self.serum3(market_index).market.quote_token_index)
|
self.token(self.serum3(market_index).quote_token_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn token(&self, token_index: TokenIndex) -> &TokenContext {
|
pub fn token(&self, token_index: TokenIndex) -> &TokenContext {
|
||||||
|
@ -163,7 +194,7 @@ impl MangoGroupContext {
|
||||||
pub fn token_by_mint(&self, mint: &Pubkey) -> anyhow::Result<&TokenContext> {
|
pub fn token_by_mint(&self, mint: &Pubkey) -> anyhow::Result<&TokenContext> {
|
||||||
self.tokens
|
self.tokens
|
||||||
.values()
|
.values()
|
||||||
.find(|tc| tc.mint_info.mint == *mint)
|
.find(|tc| tc.mint == *mint)
|
||||||
.ok_or_else(|| anyhow::anyhow!("no token for mint {}", mint))
|
.ok_or_else(|| anyhow::anyhow!("no token for mint {}", mint))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,10 +223,14 @@ impl MangoGroupContext {
|
||||||
TokenContext {
|
TokenContext {
|
||||||
token_index: mi.token_index,
|
token_index: mi.token_index,
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
mint_info: *mi,
|
|
||||||
mint_info_address: *pk,
|
mint_info_address: *pk,
|
||||||
decimals: u8::MAX,
|
decimals: u8::MAX,
|
||||||
bank: Bank::zeroed(),
|
banks: mi.banks,
|
||||||
|
vaults: mi.vaults,
|
||||||
|
fallback_oracle: Pubkey::default(), // coming in v0.22
|
||||||
|
oracle: mi.oracle,
|
||||||
|
group: mi.group,
|
||||||
|
mint: mi.mint,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -209,7 +244,6 @@ impl MangoGroupContext {
|
||||||
let token = tokens.get_mut(&bank.token_index).unwrap();
|
let token = tokens.get_mut(&bank.token_index).unwrap();
|
||||||
token.name = bank.name().into();
|
token.name = bank.name().into();
|
||||||
token.decimals = bank.mint_decimals;
|
token.decimals = bank.mint_decimals;
|
||||||
token.bank = bank.clone();
|
|
||||||
}
|
}
|
||||||
assert!(tokens.values().all(|t| t.decimals != u8::MAX));
|
assert!(tokens.values().all(|t| t.decimals != u8::MAX));
|
||||||
|
|
||||||
|
@ -237,7 +271,11 @@ impl MangoGroupContext {
|
||||||
s.market_index,
|
s.market_index,
|
||||||
Serum3MarketContext {
|
Serum3MarketContext {
|
||||||
address: *pk,
|
address: *pk,
|
||||||
market: *s,
|
base_token_index: s.base_token_index,
|
||||||
|
quote_token_index: s.quote_token_index,
|
||||||
|
name: s.name().to_string(),
|
||||||
|
serum_program: s.serum_program,
|
||||||
|
serum_market_external: s.serum_market_external,
|
||||||
bids: from_serum_style_pubkey(market_external.bids),
|
bids: from_serum_style_pubkey(market_external.bids),
|
||||||
asks: from_serum_style_pubkey(market_external.asks),
|
asks: from_serum_style_pubkey(market_external.asks),
|
||||||
event_q: from_serum_style_pubkey(market_external.event_q),
|
event_q: from_serum_style_pubkey(market_external.event_q),
|
||||||
|
@ -261,7 +299,18 @@ impl MangoGroupContext {
|
||||||
pm.perp_market_index,
|
pm.perp_market_index,
|
||||||
PerpMarketContext {
|
PerpMarketContext {
|
||||||
address: *pk,
|
address: *pk,
|
||||||
market: *pm,
|
group: pm.group,
|
||||||
|
oracle: pm.oracle,
|
||||||
|
perp_market_index: pm.perp_market_index,
|
||||||
|
settle_token_index: pm.settle_token_index,
|
||||||
|
asks: pm.asks,
|
||||||
|
bids: pm.bids,
|
||||||
|
event_queue: pm.event_queue,
|
||||||
|
base_decimals: pm.base_decimals,
|
||||||
|
base_lot_size: pm.base_lot_size,
|
||||||
|
quote_lot_size: pm.quote_lot_size,
|
||||||
|
init_overall_asset_weight: pm.init_overall_asset_weight,
|
||||||
|
name: pm.name().to_string(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -274,11 +323,11 @@ impl MangoGroupContext {
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
let serum3_market_indexes_by_name = serum3_markets
|
let serum3_market_indexes_by_name = serum3_markets
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(i, s)| (s.market.name().to_string(), *i))
|
.map(|(i, s)| (s.name.clone(), *i))
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
let perp_market_indexes_by_name = perp_markets
|
let perp_market_indexes_by_name = perp_markets
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(i, p)| (p.market.name().to_string(), *i))
|
.map(|(i, p)| (p.name.clone(), *i))
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
let group_data = fetch_anchor_account::<Group>(rpc, &group).await?;
|
let group_data = fetch_anchor_account::<Group>(rpc, &group).await?;
|
||||||
|
@ -314,10 +363,7 @@ impl MangoGroupContext {
|
||||||
account.ensure_token_position(*affected_token_index)?;
|
account.ensure_token_position(*affected_token_index)?;
|
||||||
}
|
}
|
||||||
for affected_perp_market_index in affected_perp_markets {
|
for affected_perp_market_index in affected_perp_markets {
|
||||||
let settle_token_index = self
|
let settle_token_index = self.perp(affected_perp_market_index).settle_token_index;
|
||||||
.perp(affected_perp_market_index)
|
|
||||||
.market
|
|
||||||
.settle_token_index;
|
|
||||||
account.ensure_perp_position(affected_perp_market_index, settle_token_index)?;
|
account.ensure_perp_position(affected_perp_market_index, settle_token_index)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,12 +371,12 @@ impl MangoGroupContext {
|
||||||
let mut banks = vec![];
|
let mut banks = vec![];
|
||||||
let mut oracles = vec![];
|
let mut oracles = vec![];
|
||||||
for position in account.active_token_positions() {
|
for position in account.active_token_positions() {
|
||||||
let mint_info = self.mint_info(position.token_index);
|
let token = self.token(position.token_index);
|
||||||
banks.push((
|
banks.push((
|
||||||
mint_info.first_bank(),
|
token.first_bank(),
|
||||||
writable_banks.iter().any(|&ti| ti == position.token_index),
|
writable_banks.iter().any(|&ti| ti == position.token_index),
|
||||||
));
|
));
|
||||||
oracles.push(mint_info.oracle);
|
oracles.push(token.oracle);
|
||||||
}
|
}
|
||||||
|
|
||||||
let serum_oos = account.active_serum3_orders().map(|&s| s.open_orders);
|
let serum_oos = account.active_serum3_orders().map(|&s| s.open_orders);
|
||||||
|
@ -339,7 +385,7 @@ impl MangoGroupContext {
|
||||||
.map(|&pa| self.perp_market_address(pa.market_index));
|
.map(|&pa| self.perp_market_address(pa.market_index));
|
||||||
let perp_oracles = account
|
let perp_oracles = account
|
||||||
.active_perp_positions()
|
.active_perp_positions()
|
||||||
.map(|&pa| self.perp(pa.market_index).market.oracle);
|
.map(|&pa| self.perp(pa.market_index).oracle);
|
||||||
|
|
||||||
let to_account_meta = |pubkey| AccountMeta {
|
let to_account_meta = |pubkey| AccountMeta {
|
||||||
pubkey,
|
pubkey,
|
||||||
|
@ -384,10 +430,10 @@ impl MangoGroupContext {
|
||||||
.unique();
|
.unique();
|
||||||
|
|
||||||
for token_index in token_indexes {
|
for token_index in token_indexes {
|
||||||
let mint_info = self.mint_info(token_index);
|
let token = self.token(token_index);
|
||||||
let writable_bank = writable_banks.iter().contains(&token_index);
|
let writable_bank = writable_banks.iter().contains(&token_index);
|
||||||
banks.push((mint_info.first_bank(), writable_bank));
|
banks.push((token.first_bank(), writable_bank));
|
||||||
oracles.push(mint_info.oracle);
|
oracles.push(token.oracle);
|
||||||
}
|
}
|
||||||
|
|
||||||
let serum_oos = account2
|
let serum_oos = account2
|
||||||
|
@ -405,7 +451,7 @@ impl MangoGroupContext {
|
||||||
.map(|&index| self.perp_market_address(index));
|
.map(|&index| self.perp_market_address(index));
|
||||||
let perp_oracles = perp_market_indexes
|
let perp_oracles = perp_market_indexes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&index| self.perp(index).market.oracle);
|
.map(|&index| self.perp(index).oracle);
|
||||||
|
|
||||||
let to_account_meta = |pubkey| AccountMeta {
|
let to_account_meta = |pubkey| AccountMeta {
|
||||||
pubkey,
|
pubkey,
|
||||||
|
@ -453,6 +499,47 @@ impl MangoGroupContext {
|
||||||
Ok((accounts, cu))
|
Ok((accounts, cu))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the on-chain context changed significantly, this currently means:
|
||||||
|
/// - new listings (token, serum, perp)
|
||||||
|
/// - oracle pubkey or config changes
|
||||||
|
/// - other config changes visible through the context
|
||||||
|
/// This is done because those would affect the pubkeys the websocket streams need to listen to,
|
||||||
|
/// or change limits, oracle staleness or other relevant configuration.
|
||||||
|
pub fn changed_significantly(&self, other: &Self) -> bool {
|
||||||
|
if other.tokens.len() != self.tokens.len() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (&ti, old) in self.tokens.iter() {
|
||||||
|
if old != other.token(ti) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.serum3_markets.len() != self.serum3_markets.len() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (&mi, old) in self.serum3_markets.iter() {
|
||||||
|
if old != other.serum3(mi) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.perp_markets.len() != self.perp_markets.len() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (&pi, old) in self.perp_markets.iter() {
|
||||||
|
if old != other.perp(pi) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.address_lookup_tables != self.address_lookup_tables {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn new_tokens_listed(&self, rpc: &RpcClientAsync) -> anyhow::Result<bool> {
|
pub async fn new_tokens_listed(&self, rpc: &RpcClientAsync) -> anyhow::Result<bool> {
|
||||||
let mint_infos = fetch_mint_infos(rpc, mango_v4::id(), self.group).await?;
|
let mint_infos = fetch_mint_infos(rpc, mango_v4::id(), self.group).await?;
|
||||||
Ok(mint_infos.len() > self.tokens.len())
|
Ok(mint_infos.len() > self.tokens.len())
|
||||||
|
|
|
@ -211,25 +211,19 @@ impl<'a> JupiterV4<'a> {
|
||||||
.position(|ix| !is_setup_ix(ix.program_id))
|
.position(|ix| !is_setup_ix(ix.program_id))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let bank_ams = [
|
let bank_ams = [source_token.first_bank(), target_token.first_bank()]
|
||||||
source_token.mint_info.first_bank(),
|
.into_iter()
|
||||||
target_token.mint_info.first_bank(),
|
.map(util::to_writable_account_meta)
|
||||||
]
|
.collect::<Vec<_>>();
|
||||||
.into_iter()
|
|
||||||
.map(util::to_writable_account_meta)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let vault_ams = [
|
let vault_ams = [source_token.first_vault(), target_token.first_vault()]
|
||||||
source_token.mint_info.first_vault(),
|
.into_iter()
|
||||||
target_token.mint_info.first_vault(),
|
.map(util::to_writable_account_meta)
|
||||||
]
|
.collect::<Vec<_>>();
|
||||||
.into_iter()
|
|
||||||
.map(util::to_writable_account_meta)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let owner = self.mango_client.owner();
|
let owner = self.mango_client.owner();
|
||||||
|
|
||||||
let token_ams = [source_token.mint_info.mint, target_token.mint_info.mint]
|
let token_ams = [source_token.mint, target_token.mint]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mint| {
|
.map(|mint| {
|
||||||
util::to_writable_account_meta(
|
util::to_writable_account_meta(
|
||||||
|
@ -270,7 +264,7 @@ impl<'a> JupiterV4<'a> {
|
||||||
spl_associated_token_account::instruction::create_associated_token_account_idempotent(
|
spl_associated_token_account::instruction::create_associated_token_account_idempotent(
|
||||||
&owner,
|
&owner,
|
||||||
&owner,
|
&owner,
|
||||||
&source_token.mint_info.mint,
|
&source_token.mint,
|
||||||
&Token::id(),
|
&Token::id(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -220,25 +220,19 @@ impl<'a> JupiterV6<'a> {
|
||||||
let source_token = self.mango_client.context.token_by_mint(&input_mint)?;
|
let source_token = self.mango_client.context.token_by_mint(&input_mint)?;
|
||||||
let target_token = self.mango_client.context.token_by_mint(&output_mint)?;
|
let target_token = self.mango_client.context.token_by_mint(&output_mint)?;
|
||||||
|
|
||||||
let bank_ams = [
|
let bank_ams = [source_token.first_bank(), target_token.first_bank()]
|
||||||
source_token.mint_info.first_bank(),
|
.into_iter()
|
||||||
target_token.mint_info.first_bank(),
|
.map(util::to_writable_account_meta)
|
||||||
]
|
.collect::<Vec<_>>();
|
||||||
.into_iter()
|
|
||||||
.map(util::to_writable_account_meta)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let vault_ams = [
|
let vault_ams = [source_token.first_vault(), target_token.first_vault()]
|
||||||
source_token.mint_info.first_vault(),
|
.into_iter()
|
||||||
target_token.mint_info.first_vault(),
|
.map(util::to_writable_account_meta)
|
||||||
]
|
.collect::<Vec<_>>();
|
||||||
.into_iter()
|
|
||||||
.map(util::to_writable_account_meta)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let owner = self.mango_client.owner();
|
let owner = self.mango_client.owner();
|
||||||
|
|
||||||
let token_ams = [source_token.mint_info.mint, target_token.mint_info.mint]
|
let token_ams = [source_token.mint, target_token.mint]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mint| {
|
.map(|mint| {
|
||||||
util::to_writable_account_meta(
|
util::to_writable_account_meta(
|
||||||
|
@ -303,7 +297,7 @@ impl<'a> JupiterV6<'a> {
|
||||||
spl_associated_token_account::instruction::create_associated_token_account_idempotent(
|
spl_associated_token_account::instruction::create_associated_token_account_idempotent(
|
||||||
&owner,
|
&owner,
|
||||||
&owner,
|
&owner,
|
||||||
&source_token.mint_info.mint,
|
&source_token.mint,
|
||||||
&Token::id(),
|
&Token::id(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,7 +10,7 @@ mod chain_data_fetcher;
|
||||||
mod client;
|
mod client;
|
||||||
mod context;
|
mod context;
|
||||||
pub mod error_tracking;
|
pub mod error_tracking;
|
||||||
mod gpa;
|
pub mod gpa;
|
||||||
pub mod health_cache;
|
pub mod health_cache;
|
||||||
pub mod jupiter;
|
pub mod jupiter;
|
||||||
pub mod perp_pnl;
|
pub mod perp_pnl;
|
||||||
|
|
Loading…
Reference in New Issue