2022-07-16 05:37:15 -07:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
use anchor_client::ClientError;
|
2022-07-16 05:37:15 -07:00
|
|
|
|
2023-07-05 03:58:42 -07:00
|
|
|
use anchor_lang::__private::bytemuck::{self, Zeroable};
|
2022-07-16 05:37:15 -07:00
|
|
|
|
|
|
|
use mango_v4::state::{
|
2023-07-05 03:58:42 -07:00
|
|
|
Bank, Group, MangoAccountValue, MintInfo, PerpMarket, PerpMarketIndex, Serum3Market,
|
2022-12-16 04:10:46 -08:00
|
|
|
Serum3MarketIndex, TokenIndex,
|
2022-07-16 05:37:15 -07:00
|
|
|
};
|
|
|
|
|
2022-08-04 08:01:00 -07:00
|
|
|
use fixed::types::I80F48;
|
2022-12-16 04:10:46 -08:00
|
|
|
use futures::{stream, StreamExt, TryStreamExt};
|
2022-09-29 03:59:55 -07:00
|
|
|
use itertools::Itertools;
|
2022-08-04 08:01:00 -07:00
|
|
|
|
2022-07-16 05:37:15 -07:00
|
|
|
use crate::gpa::*;
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
use solana_client::nonblocking::rpc_client::RpcClient as RpcClientAsync;
|
2022-07-16 05:37:15 -07:00
|
|
|
use solana_sdk::account::Account;
|
|
|
|
use solana_sdk::instruction::AccountMeta;
|
2022-12-16 04:10:46 -08:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
2022-07-16 05:37:15 -07:00
|
|
|
|
2022-08-04 08:01:00 -07:00
|
|
|
#[derive(Clone)]
|
2022-07-16 05:37:15 -07:00
|
|
|
pub struct TokenContext {
|
2022-08-04 08:01:00 -07:00
|
|
|
pub token_index: TokenIndex,
|
2022-07-16 05:37:15 -07:00
|
|
|
pub name: String,
|
|
|
|
pub mint_info: MintInfo,
|
|
|
|
pub mint_info_address: Pubkey,
|
|
|
|
pub decimals: u8,
|
2023-07-05 03:58:42 -07:00
|
|
|
/// Bank snapshot is never updated, only use static parts!
|
|
|
|
pub bank: Bank,
|
2022-07-16 05:37:15 -07:00
|
|
|
}
|
|
|
|
|
2022-08-04 08:01:00 -07:00
|
|
|
impl TokenContext {
|
|
|
|
pub fn native_to_ui(&self, native: I80F48) -> f64 {
|
|
|
|
(native / I80F48::from(10u64.pow(self.decimals.into()))).to_num()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-16 05:37:15 -07:00
|
|
|
pub struct Serum3MarketContext {
|
|
|
|
pub address: Pubkey,
|
|
|
|
pub market: Serum3Market,
|
|
|
|
pub bids: Pubkey,
|
|
|
|
pub asks: Pubkey,
|
|
|
|
pub event_q: Pubkey,
|
|
|
|
pub req_q: Pubkey,
|
|
|
|
pub coin_vault: Pubkey,
|
|
|
|
pub pc_vault: Pubkey,
|
|
|
|
pub vault_signer: Pubkey,
|
|
|
|
pub coin_lot_size: u64,
|
|
|
|
pub pc_lot_size: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct PerpMarketContext {
|
|
|
|
pub address: Pubkey,
|
2023-07-05 03:58:42 -07:00
|
|
|
/// PerpMarket snapshot is never updated, only use static parts!
|
2022-07-16 05:37:15 -07:00
|
|
|
pub market: PerpMarket,
|
|
|
|
}
|
|
|
|
|
2023-11-03 03:20:37 -07:00
|
|
|
pub struct ComputeEstimates {
|
|
|
|
pub cu_per_mango_instruction: u32,
|
|
|
|
pub health_cu_per_token: u32,
|
|
|
|
pub health_cu_per_perp: u32,
|
|
|
|
pub health_cu_per_serum: u32,
|
|
|
|
pub cu_per_serum3_order_match: u32,
|
|
|
|
pub cu_per_serum3_order_cancel: u32,
|
|
|
|
pub cu_per_perp_order_match: u32,
|
|
|
|
pub cu_per_perp_order_cancel: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for ComputeEstimates {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
cu_per_mango_instruction: 100_000,
|
|
|
|
health_cu_per_token: 5000,
|
|
|
|
health_cu_per_perp: 8000,
|
|
|
|
health_cu_per_serum: 6000,
|
|
|
|
// measured around 1.5k, see test_serum_compute
|
|
|
|
cu_per_serum3_order_match: 3_000,
|
|
|
|
// measured around 11k, see test_serum_compute
|
|
|
|
cu_per_serum3_order_cancel: 20_000,
|
|
|
|
// measured around 3.5k, see test_perp_compute
|
|
|
|
cu_per_perp_order_match: 7_000,
|
|
|
|
// measured around 3.5k, see test_perp_compute
|
|
|
|
cu_per_perp_order_cancel: 7_000,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ComputeEstimates {
|
|
|
|
pub fn health_for_counts(&self, tokens: usize, perps: usize, serums: usize) -> u32 {
|
|
|
|
let tokens: u32 = tokens.try_into().unwrap();
|
|
|
|
let perps: u32 = perps.try_into().unwrap();
|
|
|
|
let serums: u32 = serums.try_into().unwrap();
|
|
|
|
tokens * self.health_cu_per_token
|
|
|
|
+ perps * self.health_cu_per_perp
|
|
|
|
+ serums * self.health_cu_per_serum
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn health_for_account(&self, account: &MangoAccountValue) -> u32 {
|
|
|
|
self.health_for_counts(
|
|
|
|
account.active_token_positions().count(),
|
|
|
|
account.active_perp_positions().count(),
|
|
|
|
account.active_serum3_orders().count(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-16 05:37:15 -07:00
|
|
|
pub struct MangoGroupContext {
|
|
|
|
pub group: Pubkey,
|
|
|
|
|
|
|
|
pub tokens: HashMap<TokenIndex, TokenContext>,
|
|
|
|
pub token_indexes_by_name: HashMap<String, TokenIndex>,
|
|
|
|
|
|
|
|
pub serum3_markets: HashMap<Serum3MarketIndex, Serum3MarketContext>,
|
|
|
|
pub serum3_market_indexes_by_name: HashMap<String, Serum3MarketIndex>,
|
|
|
|
|
|
|
|
pub perp_markets: HashMap<PerpMarketIndex, PerpMarketContext>,
|
|
|
|
pub perp_market_indexes_by_name: HashMap<String, PerpMarketIndex>,
|
2022-12-16 04:10:46 -08:00
|
|
|
|
|
|
|
pub address_lookup_tables: Vec<Pubkey>,
|
2023-11-03 03:20:37 -07:00
|
|
|
|
|
|
|
pub compute_estimates: ComputeEstimates,
|
2022-07-16 05:37:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl MangoGroupContext {
|
|
|
|
pub fn mint_info_address(&self, token_index: TokenIndex) -> Pubkey {
|
|
|
|
self.token(token_index).mint_info_address
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn mint_info(&self, token_index: TokenIndex) -> MintInfo {
|
|
|
|
self.token(token_index).mint_info
|
|
|
|
}
|
|
|
|
|
2022-09-21 03:04:54 -07:00
|
|
|
pub fn perp(&self, perp_market_index: PerpMarketIndex) -> &PerpMarketContext {
|
|
|
|
self.perp_markets.get(&perp_market_index).unwrap()
|
|
|
|
}
|
|
|
|
|
2023-06-15 08:20:31 -07:00
|
|
|
pub fn perp_market_address(&self, perp_market_index: PerpMarketIndex) -> Pubkey {
|
|
|
|
self.perp(perp_market_index).address
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn serum3_market_index(&self, name: &str) -> Serum3MarketIndex {
|
|
|
|
*self.serum3_market_indexes_by_name.get(name).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn serum3(&self, market_index: Serum3MarketIndex) -> &Serum3MarketContext {
|
|
|
|
self.serum3_markets.get(&market_index).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn serum3_base_token(&self, market_index: Serum3MarketIndex) -> &TokenContext {
|
|
|
|
self.token(self.serum3(market_index).market.base_token_index)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn serum3_quote_token(&self, market_index: Serum3MarketIndex) -> &TokenContext {
|
|
|
|
self.token(self.serum3(market_index).market.quote_token_index)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn token(&self, token_index: TokenIndex) -> &TokenContext {
|
|
|
|
self.tokens.get(&token_index).unwrap()
|
|
|
|
}
|
|
|
|
|
2022-08-04 08:01:00 -07:00
|
|
|
pub fn token_by_mint(&self, mint: &Pubkey) -> anyhow::Result<&TokenContext> {
|
|
|
|
self.tokens
|
2023-11-07 23:51:41 -08:00
|
|
|
.values()
|
|
|
|
.find(|tc| tc.mint_info.mint == *mint)
|
2022-08-04 08:01:00 -07:00
|
|
|
.ok_or_else(|| anyhow::anyhow!("no token for mint {}", mint))
|
|
|
|
}
|
|
|
|
|
2023-11-07 23:51:41 -08:00
|
|
|
pub fn token_by_name(&self, name: &str) -> &TokenContext {
|
|
|
|
let mut tc_iter = self.tokens.values().filter(|tc| tc.name == name);
|
|
|
|
let tc = tc_iter.next();
|
|
|
|
assert!(
|
|
|
|
tc.is_some(),
|
|
|
|
"token {name} not found; names {:?}",
|
|
|
|
self.tokens.values().map(|tc| tc.name.clone()).collect_vec()
|
|
|
|
);
|
|
|
|
assert!(tc_iter.next().is_none(), "multiple token {name} found");
|
|
|
|
tc.unwrap()
|
|
|
|
}
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
pub async fn new_from_rpc(rpc: &RpcClientAsync, group: Pubkey) -> anyhow::Result<Self> {
|
|
|
|
let program = mango_v4::ID;
|
2022-07-16 05:37:15 -07:00
|
|
|
|
|
|
|
// tokens
|
2022-12-16 04:10:46 -08:00
|
|
|
let mint_info_tuples = fetch_mint_infos(rpc, program, group).await?;
|
2022-07-16 05:37:15 -07:00
|
|
|
let mut tokens = mint_info_tuples
|
|
|
|
.iter()
|
|
|
|
.map(|(pk, mi)| {
|
|
|
|
(
|
|
|
|
mi.token_index,
|
|
|
|
TokenContext {
|
2022-08-04 08:01:00 -07:00
|
|
|
token_index: mi.token_index,
|
2022-07-16 05:37:15 -07:00
|
|
|
name: String::new(),
|
|
|
|
mint_info: *mi,
|
|
|
|
mint_info_address: *pk,
|
|
|
|
decimals: u8::MAX,
|
2023-07-05 03:58:42 -07:00
|
|
|
bank: Bank::zeroed(),
|
2022-07-16 05:37:15 -07:00
|
|
|
},
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect::<HashMap<_, _>>();
|
|
|
|
|
|
|
|
// reading the banks is only needed for the token names and decimals
|
|
|
|
// FUTURE: either store the names on MintInfo as well, or maybe don't store them at all
|
|
|
|
// because they are in metaplex?
|
2022-12-16 04:10:46 -08:00
|
|
|
let bank_tuples = fetch_banks(rpc, program, group).await?;
|
2022-07-16 05:37:15 -07:00
|
|
|
for (_, bank) in bank_tuples {
|
|
|
|
let token = tokens.get_mut(&bank.token_index).unwrap();
|
|
|
|
token.name = bank.name().into();
|
|
|
|
token.decimals = bank.mint_decimals;
|
2023-07-05 03:58:42 -07:00
|
|
|
token.bank = bank.clone();
|
2022-07-16 05:37:15 -07:00
|
|
|
}
|
|
|
|
assert!(tokens.values().all(|t| t.decimals != u8::MAX));
|
|
|
|
|
|
|
|
// serum3 markets
|
2022-12-16 04:10:46 -08:00
|
|
|
let serum3_market_tuples = fetch_serum3_markets(rpc, program, group).await?;
|
|
|
|
let serum3_markets_external = stream::iter(serum3_market_tuples.iter())
|
|
|
|
.then(|(_, s)| fetch_raw_account(rpc, s.serum_market_external))
|
|
|
|
.try_collect::<Vec<_>>()
|
|
|
|
.await?;
|
2022-07-16 05:37:15 -07:00
|
|
|
let serum3_markets = serum3_market_tuples
|
|
|
|
.iter()
|
2022-12-16 04:10:46 -08:00
|
|
|
.zip(serum3_markets_external.iter())
|
|
|
|
.map(|((pk, s), market_external_account)| {
|
2022-07-16 05:37:15 -07:00
|
|
|
let market_external: &serum_dex::state::MarketState = bytemuck::from_bytes(
|
|
|
|
&market_external_account.data
|
|
|
|
[5..5 + std::mem::size_of::<serum_dex::state::MarketState>()],
|
|
|
|
);
|
|
|
|
let vault_signer = serum_dex::state::gen_vault_signer_key(
|
|
|
|
market_external.vault_signer_nonce,
|
|
|
|
&s.serum_market_external,
|
|
|
|
&s.serum_program,
|
|
|
|
)
|
|
|
|
.unwrap();
|
2022-12-16 04:10:46 -08:00
|
|
|
(
|
2022-07-16 05:37:15 -07:00
|
|
|
s.market_index,
|
|
|
|
Serum3MarketContext {
|
|
|
|
address: *pk,
|
|
|
|
market: *s,
|
|
|
|
bids: from_serum_style_pubkey(market_external.bids),
|
|
|
|
asks: from_serum_style_pubkey(market_external.asks),
|
|
|
|
event_q: from_serum_style_pubkey(market_external.event_q),
|
|
|
|
req_q: from_serum_style_pubkey(market_external.req_q),
|
|
|
|
coin_vault: from_serum_style_pubkey(market_external.coin_vault),
|
|
|
|
pc_vault: from_serum_style_pubkey(market_external.pc_vault),
|
|
|
|
vault_signer,
|
|
|
|
coin_lot_size: market_external.coin_lot_size,
|
|
|
|
pc_lot_size: market_external.pc_lot_size,
|
|
|
|
},
|
2022-12-16 04:10:46 -08:00
|
|
|
)
|
2022-07-16 05:37:15 -07:00
|
|
|
})
|
2022-12-16 04:10:46 -08:00
|
|
|
.collect::<HashMap<_, _>>();
|
2022-07-16 05:37:15 -07:00
|
|
|
|
|
|
|
// perp markets
|
2022-12-16 04:10:46 -08:00
|
|
|
let perp_market_tuples = fetch_perp_markets(rpc, program, group).await?;
|
2022-07-16 05:37:15 -07:00
|
|
|
let perp_markets = perp_market_tuples
|
|
|
|
.iter()
|
|
|
|
.map(|(pk, pm)| {
|
|
|
|
(
|
|
|
|
pm.perp_market_index,
|
|
|
|
PerpMarketContext {
|
|
|
|
address: *pk,
|
|
|
|
market: *pm,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect::<HashMap<_, _>>();
|
|
|
|
|
|
|
|
// Name lookup tables
|
|
|
|
let token_indexes_by_name = tokens
|
|
|
|
.iter()
|
|
|
|
.map(|(i, t)| (t.name.clone(), *i))
|
|
|
|
.collect::<HashMap<_, _>>();
|
|
|
|
let serum3_market_indexes_by_name = serum3_markets
|
|
|
|
.iter()
|
|
|
|
.map(|(i, s)| (s.market.name().to_string(), *i))
|
|
|
|
.collect::<HashMap<_, _>>();
|
|
|
|
let perp_market_indexes_by_name = perp_markets
|
|
|
|
.iter()
|
|
|
|
.map(|(i, p)| (p.market.name().to_string(), *i))
|
|
|
|
.collect::<HashMap<_, _>>();
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
let group_data = fetch_anchor_account::<Group>(rpc, &group).await?;
|
|
|
|
let address_lookup_tables = group_data
|
|
|
|
.address_lookup_tables
|
|
|
|
.iter()
|
|
|
|
.filter(|&&k| k != Pubkey::default())
|
|
|
|
.cloned()
|
|
|
|
.collect::<Vec<Pubkey>>();
|
|
|
|
|
2022-07-16 05:37:15 -07:00
|
|
|
Ok(MangoGroupContext {
|
|
|
|
group,
|
|
|
|
tokens,
|
|
|
|
token_indexes_by_name,
|
|
|
|
serum3_markets,
|
|
|
|
serum3_market_indexes_by_name,
|
|
|
|
perp_markets,
|
|
|
|
perp_market_indexes_by_name,
|
2022-12-16 04:10:46 -08:00
|
|
|
address_lookup_tables,
|
2023-11-03 03:20:37 -07:00
|
|
|
compute_estimates: ComputeEstimates::default(),
|
2022-07-16 05:37:15 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn derive_health_check_remaining_account_metas(
|
|
|
|
&self,
|
2022-07-25 07:07:53 -07:00
|
|
|
account: &MangoAccountValue,
|
2022-08-04 08:01:00 -07:00
|
|
|
affected_tokens: Vec<TokenIndex>,
|
2023-01-18 01:50:23 -08:00
|
|
|
writable_banks: Vec<TokenIndex>,
|
|
|
|
affected_perp_markets: Vec<PerpMarketIndex>,
|
2023-11-03 03:20:37 -07:00
|
|
|
) -> anyhow::Result<(Vec<AccountMeta>, u32)> {
|
2023-01-18 01:50:23 -08:00
|
|
|
let mut account = account.clone();
|
2023-10-07 12:27:19 -07:00
|
|
|
for affected_token_index in affected_tokens.iter().chain(writable_banks.iter()) {
|
|
|
|
account.ensure_token_position(*affected_token_index)?;
|
2023-01-18 01:50:23 -08:00
|
|
|
}
|
|
|
|
for affected_perp_market_index in affected_perp_markets {
|
|
|
|
let settle_token_index = self
|
|
|
|
.perp(affected_perp_market_index)
|
|
|
|
.market
|
|
|
|
.settle_token_index;
|
|
|
|
account.ensure_perp_position(affected_perp_market_index, settle_token_index)?;
|
|
|
|
}
|
|
|
|
|
2022-07-16 05:37:15 -07:00
|
|
|
// figure out all the banks/oracles that need to be passed for the health check
|
|
|
|
let mut banks = vec![];
|
|
|
|
let mut oracles = vec![];
|
2022-08-18 04:45:31 -07:00
|
|
|
for position in account.active_token_positions() {
|
2022-07-16 05:37:15 -07:00
|
|
|
let mint_info = self.mint_info(position.token_index);
|
2023-01-18 01:50:23 -08:00
|
|
|
banks.push((
|
|
|
|
mint_info.first_bank(),
|
|
|
|
writable_banks.iter().any(|&ti| ti == position.token_index),
|
|
|
|
));
|
2022-07-16 05:37:15 -07:00
|
|
|
oracles.push(mint_info.oracle);
|
|
|
|
}
|
|
|
|
|
2022-08-18 04:45:31 -07:00
|
|
|
let serum_oos = account.active_serum3_orders().map(|&s| s.open_orders);
|
2022-07-16 05:37:15 -07:00
|
|
|
let perp_markets = account
|
2022-08-18 04:45:31 -07:00
|
|
|
.active_perp_positions()
|
2022-07-16 05:37:15 -07:00
|
|
|
.map(|&pa| self.perp_market_address(pa.market_index));
|
2022-09-21 03:04:54 -07:00
|
|
|
let perp_oracles = account
|
|
|
|
.active_perp_positions()
|
|
|
|
.map(|&pa| self.perp(pa.market_index).market.oracle);
|
|
|
|
|
|
|
|
let to_account_meta = |pubkey| AccountMeta {
|
|
|
|
pubkey,
|
|
|
|
is_writable: false,
|
|
|
|
is_signer: false,
|
|
|
|
};
|
2022-07-16 05:37:15 -07:00
|
|
|
|
2023-11-03 03:20:37 -07:00
|
|
|
let accounts = banks
|
2022-07-16 05:37:15 -07:00
|
|
|
.iter()
|
2023-01-18 01:50:23 -08:00
|
|
|
.map(|&(pubkey, is_writable)| AccountMeta {
|
2022-07-16 05:37:15 -07:00
|
|
|
pubkey,
|
2023-01-18 01:50:23 -08:00
|
|
|
is_writable,
|
2022-07-16 05:37:15 -07:00
|
|
|
is_signer: false,
|
|
|
|
})
|
2022-09-21 03:04:54 -07:00
|
|
|
.chain(oracles.into_iter().map(to_account_meta))
|
|
|
|
.chain(perp_markets.map(to_account_meta))
|
|
|
|
.chain(perp_oracles.map(to_account_meta))
|
|
|
|
.chain(serum_oos.map(to_account_meta))
|
2023-11-03 03:20:37 -07:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
let cu = self.compute_estimates.health_for_account(&account);
|
|
|
|
|
|
|
|
Ok((accounts, cu))
|
2022-07-16 05:37:15 -07:00
|
|
|
}
|
2022-09-29 03:59:55 -07:00
|
|
|
|
|
|
|
pub fn derive_health_check_remaining_account_metas_two_accounts(
|
|
|
|
&self,
|
|
|
|
account1: &MangoAccountValue,
|
|
|
|
account2: &MangoAccountValue,
|
2023-04-17 02:30:27 -07:00
|
|
|
affected_tokens: &[TokenIndex],
|
2022-09-29 03:59:55 -07:00
|
|
|
writable_banks: &[TokenIndex],
|
2023-11-03 03:20:37 -07:00
|
|
|
) -> anyhow::Result<(Vec<AccountMeta>, u32)> {
|
2022-09-29 03:59:55 -07:00
|
|
|
// figure out all the banks/oracles that need to be passed for the health check
|
|
|
|
let mut banks = vec![];
|
|
|
|
let mut oracles = vec![];
|
|
|
|
|
|
|
|
let token_indexes = account2
|
|
|
|
.active_token_positions()
|
|
|
|
.chain(account1.active_token_positions())
|
|
|
|
.map(|ta| ta.token_index)
|
2023-04-17 02:30:27 -07:00
|
|
|
.chain(affected_tokens.iter().copied())
|
2022-09-29 03:59:55 -07:00
|
|
|
.unique();
|
|
|
|
|
|
|
|
for token_index in token_indexes {
|
|
|
|
let mint_info = self.mint_info(token_index);
|
|
|
|
let writable_bank = writable_banks.iter().contains(&token_index);
|
|
|
|
banks.push((mint_info.first_bank(), writable_bank));
|
|
|
|
oracles.push(mint_info.oracle);
|
|
|
|
}
|
|
|
|
|
|
|
|
let serum_oos = account2
|
|
|
|
.active_serum3_orders()
|
|
|
|
.chain(account1.active_serum3_orders())
|
|
|
|
.map(|&s| s.open_orders);
|
2022-12-09 07:16:14 -08:00
|
|
|
let perp_market_indexes = account2
|
2022-09-29 03:59:55 -07:00
|
|
|
.active_perp_positions()
|
|
|
|
.chain(account1.active_perp_positions())
|
2022-12-09 07:16:14 -08:00
|
|
|
.map(|&pa| pa.market_index)
|
|
|
|
.unique()
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
let perp_markets = perp_market_indexes
|
|
|
|
.iter()
|
|
|
|
.map(|&index| self.perp_market_address(index));
|
|
|
|
let perp_oracles = perp_market_indexes
|
|
|
|
.iter()
|
|
|
|
.map(|&index| self.perp(index).market.oracle);
|
2022-09-29 03:59:55 -07:00
|
|
|
|
|
|
|
let to_account_meta = |pubkey| AccountMeta {
|
|
|
|
pubkey,
|
|
|
|
is_writable: false,
|
|
|
|
is_signer: false,
|
|
|
|
};
|
|
|
|
|
2023-11-03 03:20:37 -07:00
|
|
|
let accounts = banks
|
2022-09-29 03:59:55 -07:00
|
|
|
.iter()
|
|
|
|
.map(|(pubkey, is_writable)| AccountMeta {
|
|
|
|
pubkey: *pubkey,
|
|
|
|
is_writable: *is_writable,
|
|
|
|
is_signer: false,
|
|
|
|
})
|
|
|
|
.chain(oracles.into_iter().map(to_account_meta))
|
|
|
|
.chain(perp_markets.map(to_account_meta))
|
|
|
|
.chain(perp_oracles.map(to_account_meta))
|
|
|
|
.chain(serum_oos.map(to_account_meta))
|
2023-11-03 03:20:37 -07:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
// Since health is likely to be computed separately for both accounts, we don't use the
|
|
|
|
// unique'd counts to estimate health cu cost.
|
|
|
|
let account1_token_count = account1
|
|
|
|
.active_token_positions()
|
|
|
|
.map(|ta| ta.token_index)
|
|
|
|
.chain(affected_tokens.iter().copied())
|
|
|
|
.unique()
|
|
|
|
.count();
|
|
|
|
let account2_token_count = account2
|
|
|
|
.active_token_positions()
|
|
|
|
.map(|ta| ta.token_index)
|
|
|
|
.chain(affected_tokens.iter().copied())
|
|
|
|
.unique()
|
|
|
|
.count();
|
|
|
|
let cu = self.compute_estimates.health_for_counts(
|
|
|
|
account1_token_count,
|
|
|
|
account1.active_perp_positions().count(),
|
|
|
|
account1.active_serum3_orders().count(),
|
|
|
|
) + self.compute_estimates.health_for_counts(
|
|
|
|
account2_token_count,
|
|
|
|
account2.active_perp_positions().count(),
|
|
|
|
account2.active_serum3_orders().count(),
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok((accounts, cu))
|
2022-09-29 03:59:55 -07:00
|
|
|
}
|
2023-01-21 02:35:31 -08:00
|
|
|
|
|
|
|
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?;
|
|
|
|
Ok(mint_infos.len() > self.tokens.len())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn new_serum3_markets_listed(&self, rpc: &RpcClientAsync) -> anyhow::Result<bool> {
|
|
|
|
let serum3_markets = fetch_serum3_markets(rpc, mango_v4::id(), self.group).await?;
|
|
|
|
Ok(serum3_markets.len() > self.serum3_markets.len())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn new_perp_markets_listed(&self, rpc: &RpcClientAsync) -> anyhow::Result<bool> {
|
|
|
|
let new_perp_markets = fetch_perp_markets(rpc, mango_v4::id(), self.group).await?;
|
|
|
|
Ok(new_perp_markets.len() > self.perp_markets.len())
|
|
|
|
}
|
2022-07-16 05:37:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn from_serum_style_pubkey(d: [u64; 4]) -> Pubkey {
|
2023-05-12 04:54:53 -07:00
|
|
|
let b: [u8; 32] = bytemuck::cast(d);
|
|
|
|
Pubkey::from(b)
|
2022-07-16 05:37:15 -07:00
|
|
|
}
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
async fn fetch_raw_account(rpc: &RpcClientAsync, address: Pubkey) -> Result<Account, ClientError> {
|
|
|
|
rpc.get_account_with_commitment(&address, rpc.commitment())
|
|
|
|
.await?
|
2022-07-16 05:37:15 -07:00
|
|
|
.value
|
|
|
|
.ok_or(ClientError::AccountNotFound)
|
|
|
|
}
|