2024-02-13 03:39:28 -08:00
|
|
|
use std::{
|
|
|
|
collections::HashSet,
|
|
|
|
sync::Arc,
|
|
|
|
time::Instant,
|
|
|
|
time::{Duration, SystemTime, UNIX_EPOCH},
|
|
|
|
};
|
2022-05-06 07:45:04 -07:00
|
|
|
|
2022-05-27 22:05:34 -07:00
|
|
|
use crate::MangoClient;
|
2024-02-13 03:39:28 -08:00
|
|
|
use anyhow::Context;
|
2022-08-08 04:40:33 -07:00
|
|
|
use itertools::Itertools;
|
2022-05-06 05:19:49 -07:00
|
|
|
|
2024-02-13 03:39:28 -08:00
|
|
|
use anchor_lang::{__private::bytemuck::cast_ref, solana_program, Discriminator};
|
2022-05-06 07:45:04 -07:00
|
|
|
use futures::Future;
|
2024-02-13 03:39:28 -08:00
|
|
|
use mango_v4::{
|
|
|
|
accounts_zerocopy::AccountReader,
|
|
|
|
state::{
|
|
|
|
EventQueue, EventType, FillEvent, Group, MangoAccount, MangoAccountValue, OutEvent,
|
|
|
|
TokenIndex,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
use mango_v4_client::{
|
|
|
|
account_fetcher_fetch_anchor_account, AccountFetcher, PerpMarketContext, PreparedInstructions,
|
|
|
|
RpcAccountFetcher, TransactionBuilder,
|
|
|
|
};
|
2023-03-29 07:51:32 -07:00
|
|
|
use prometheus::{register_histogram, Encoder, Histogram, IntCounter, Registry};
|
2022-05-27 22:05:34 -07:00
|
|
|
use solana_sdk::{
|
|
|
|
instruction::{AccountMeta, Instruction},
|
|
|
|
pubkey::Pubkey,
|
2024-02-14 02:32:57 -08:00
|
|
|
signature::Signature,
|
2022-05-27 22:05:34 -07:00
|
|
|
};
|
2024-02-07 03:52:32 -08:00
|
|
|
use tokio::task::JoinHandle;
|
2023-07-11 23:38:38 -07:00
|
|
|
use tracing::*;
|
2023-03-29 07:51:32 -07:00
|
|
|
use warp::Filter;
|
|
|
|
|
|
|
|
lazy_static::lazy_static! {
|
|
|
|
pub static ref METRICS_REGISTRY: Registry = Registry::new_custom(Some("keeper".to_string()), None).unwrap();
|
|
|
|
pub static ref METRIC_UPDATE_TOKENS_SUCCESS: IntCounter =
|
|
|
|
IntCounter::new("update_tokens_success", "Successful update token transactions").unwrap();
|
|
|
|
pub static ref METRIC_UPDATE_TOKENS_FAILURE: IntCounter =
|
|
|
|
IntCounter::new("update_tokens_failure", "Failed update token transactions").unwrap();
|
|
|
|
pub static ref METRIC_CONSUME_EVENTS_SUCCESS: IntCounter =
|
|
|
|
IntCounter::new("consume_events_success", "Successful consume events transactions").unwrap();
|
|
|
|
pub static ref METRIC_CONSUME_EVENTS_FAILURE: IntCounter =
|
|
|
|
IntCounter::new("consume_events_failure", "Failed consume events transactions").unwrap();
|
|
|
|
pub static ref METRIC_UPDATE_FUNDING_SUCCESS: IntCounter =
|
|
|
|
IntCounter::new("update_funding_success", "Successful update funding transactions").unwrap();
|
|
|
|
pub static ref METRIC_UPDATE_FUNDING_FAILURE: IntCounter =
|
|
|
|
IntCounter::new("update_funding_failure", "Failed update funding transactions").unwrap();
|
|
|
|
pub static ref METRIC_CONFIRMATION_TIMES: Histogram = register_histogram!(
|
|
|
|
"confirmation_times", "Transaction confirmation times",
|
|
|
|
vec![1000.0, 3000.0, 5000.0, 7000.0, 10000.0, 15000.0, 20000.0, 30000.0, 40000.0, 50000.0, 60000.0]
|
|
|
|
).unwrap();
|
|
|
|
}
|
2022-05-06 05:19:49 -07:00
|
|
|
|
2022-07-31 00:25:11 -07:00
|
|
|
// TODO: move instructions into the client proper
|
|
|
|
|
2023-03-29 07:51:32 -07:00
|
|
|
async fn serve_metrics() {
|
|
|
|
METRICS_REGISTRY
|
|
|
|
.register(Box::new(METRIC_UPDATE_TOKENS_SUCCESS.clone()))
|
|
|
|
.unwrap();
|
|
|
|
METRICS_REGISTRY
|
|
|
|
.register(Box::new(METRIC_UPDATE_TOKENS_FAILURE.clone()))
|
|
|
|
.unwrap();
|
|
|
|
METRICS_REGISTRY
|
|
|
|
.register(Box::new(METRIC_CONSUME_EVENTS_SUCCESS.clone()))
|
|
|
|
.unwrap();
|
|
|
|
METRICS_REGISTRY
|
|
|
|
.register(Box::new(METRIC_CONSUME_EVENTS_FAILURE.clone()))
|
|
|
|
.unwrap();
|
|
|
|
METRICS_REGISTRY
|
|
|
|
.register(Box::new(METRIC_UPDATE_FUNDING_SUCCESS.clone()))
|
|
|
|
.unwrap();
|
|
|
|
METRICS_REGISTRY
|
|
|
|
.register(Box::new(METRIC_UPDATE_FUNDING_FAILURE.clone()))
|
|
|
|
.unwrap();
|
|
|
|
METRICS_REGISTRY
|
|
|
|
.register(Box::new(METRIC_CONFIRMATION_TIMES.clone()))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let metrics_route = warp::path!("metrics").map(|| {
|
|
|
|
let mut buffer = Vec::<u8>::new();
|
|
|
|
let encoder = prometheus::TextEncoder::new();
|
|
|
|
encoder
|
|
|
|
.encode(&METRICS_REGISTRY.gather(), &mut buffer)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
String::from_utf8(buffer.clone()).unwrap()
|
|
|
|
});
|
|
|
|
println!("Metrics server starting on port 9091");
|
|
|
|
warp::serve(metrics_route).run(([0, 0, 0, 0], 9091)).await;
|
|
|
|
}
|
|
|
|
|
2022-05-06 07:45:04 -07:00
|
|
|
pub async fn runner(
|
|
|
|
mango_client: Arc<MangoClient>,
|
|
|
|
debugging_handle: impl Future,
|
2022-11-21 10:35:04 -08:00
|
|
|
interval_update_banks: u64,
|
|
|
|
interval_consume_events: u64,
|
|
|
|
interval_update_funding: u64,
|
2023-12-05 04:22:24 -08:00
|
|
|
interval_check_for_changes_and_abort: u64,
|
2024-02-13 03:39:28 -08:00
|
|
|
interval_charge_collateral_fees: u64,
|
2024-02-24 01:04:30 -08:00
|
|
|
max_cu_when_batching: u32,
|
2024-02-07 03:52:32 -08:00
|
|
|
extra_jobs: Vec<JoinHandle<()>>,
|
2022-05-06 07:45:04 -07:00
|
|
|
) -> Result<(), anyhow::Error> {
|
2022-05-27 22:05:34 -07:00
|
|
|
let handles1 = mango_client
|
2022-07-16 05:37:15 -07:00
|
|
|
.context
|
|
|
|
.tokens
|
|
|
|
.keys()
|
2022-12-02 06:48:43 -08:00
|
|
|
// TODO: grouping tokens whose oracle might have less confidencen e.g. ORCA with the rest, fails whole ix
|
2022-08-08 04:40:33 -07:00
|
|
|
// TokenUpdateIndexAndRate is known to take max 71k cu
|
|
|
|
// from cargo test-bpf local tests
|
2022-11-01 12:15:18 -07:00
|
|
|
// chunk size of 8 seems to be max before encountering "VersionedTransaction too large" issues
|
|
|
|
.chunks(8)
|
2022-08-08 04:40:33 -07:00
|
|
|
.into_iter()
|
|
|
|
.map(|chunk| {
|
|
|
|
loop_update_index_and_rate(
|
|
|
|
mango_client.clone(),
|
|
|
|
chunk.copied().collect::<Vec<TokenIndex>>(),
|
2022-11-21 10:35:04 -08:00
|
|
|
interval_update_banks,
|
2022-08-08 04:40:33 -07:00
|
|
|
)
|
|
|
|
})
|
2022-05-06 05:19:49 -07:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
2022-05-27 22:05:34 -07:00
|
|
|
let handles2 = mango_client
|
2022-07-16 05:37:15 -07:00
|
|
|
.context
|
|
|
|
.perp_markets
|
2022-05-27 22:05:34 -07:00
|
|
|
.values()
|
2023-02-14 23:14:28 -08:00
|
|
|
.filter(|perp|
|
|
|
|
// MNGO-PERP-OLD
|
2023-12-05 04:22:24 -08:00
|
|
|
perp.perp_market_index != 1)
|
2022-11-21 10:35:04 -08:00
|
|
|
.map(|perp| {
|
|
|
|
loop_consume_events(
|
|
|
|
mango_client.clone(),
|
|
|
|
perp.address,
|
2023-12-05 04:22:24 -08:00
|
|
|
perp,
|
2022-11-21 10:35:04 -08:00
|
|
|
interval_consume_events,
|
|
|
|
)
|
|
|
|
})
|
2022-05-06 05:19:49 -07:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
2022-05-27 22:05:34 -07:00
|
|
|
let handles3 = mango_client
|
2022-07-16 05:37:15 -07:00
|
|
|
.context
|
|
|
|
.perp_markets
|
2022-05-27 22:05:34 -07:00
|
|
|
.values()
|
2023-02-14 23:14:28 -08:00
|
|
|
.filter(|perp|
|
|
|
|
// MNGO-PERP-OLD
|
2023-12-05 04:22:24 -08:00
|
|
|
perp.perp_market_index != 1)
|
2022-11-21 10:35:04 -08:00
|
|
|
.map(|perp| {
|
|
|
|
loop_update_funding(
|
|
|
|
mango_client.clone(),
|
|
|
|
perp.address,
|
2023-12-05 04:22:24 -08:00
|
|
|
perp,
|
2022-11-21 10:35:04 -08:00
|
|
|
interval_update_funding,
|
|
|
|
)
|
|
|
|
})
|
2022-05-17 06:59:47 -07:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
2022-05-06 05:19:49 -07:00
|
|
|
futures::join!(
|
|
|
|
futures::future::join_all(handles1),
|
2022-05-06 07:45:04 -07:00
|
|
|
futures::future::join_all(handles2),
|
2022-05-17 06:59:47 -07:00
|
|
|
futures::future::join_all(handles3),
|
2024-02-24 01:04:30 -08:00
|
|
|
loop_charge_collateral_fees(
|
|
|
|
mango_client.clone(),
|
|
|
|
interval_charge_collateral_fees,
|
|
|
|
max_cu_when_batching
|
|
|
|
),
|
2023-12-05 04:22:24 -08:00
|
|
|
MangoClient::loop_check_for_context_changes_and_abort(
|
2023-01-21 02:35:31 -08:00
|
|
|
mango_client.clone(),
|
2023-12-05 04:22:24 -08:00
|
|
|
Duration::from_secs(interval_check_for_changes_and_abort),
|
2023-01-21 02:35:31 -08:00
|
|
|
),
|
2023-03-29 07:51:32 -07:00
|
|
|
serve_metrics(),
|
|
|
|
debugging_handle,
|
2024-02-07 03:52:32 -08:00
|
|
|
futures::future::join_all(extra_jobs),
|
2022-05-06 05:19:49 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2022-05-27 22:05:34 -07:00
|
|
|
|
2022-08-08 04:40:33 -07:00
|
|
|
pub async fn loop_update_index_and_rate(
|
|
|
|
mango_client: Arc<MangoClient>,
|
|
|
|
token_indices: Vec<TokenIndex>,
|
2022-11-21 10:35:04 -08:00
|
|
|
interval: u64,
|
2022-08-08 04:40:33 -07:00
|
|
|
) {
|
2024-01-09 02:25:55 -08:00
|
|
|
let mut interval = mango_v4_client::delay_interval(Duration::from_secs(interval));
|
2022-05-27 22:05:34 -07:00
|
|
|
loop {
|
|
|
|
interval.tick().await;
|
|
|
|
|
|
|
|
let client = mango_client.clone();
|
2022-06-27 02:27:17 -07:00
|
|
|
|
2022-08-08 04:40:33 -07:00
|
|
|
let token_indices_clone = token_indices.clone();
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
let token_names = token_indices_clone
|
|
|
|
.iter()
|
|
|
|
.map(|token_index| client.context.token(*token_index).name.to_owned())
|
|
|
|
.join(",");
|
2022-06-27 02:27:17 -07:00
|
|
|
|
2023-02-16 07:20:37 -08:00
|
|
|
let mut instructions = vec![];
|
2022-12-16 04:10:46 -08:00
|
|
|
for token_index in token_indices_clone.iter() {
|
|
|
|
let token = client.context.token(*token_index);
|
2023-12-05 04:22:24 -08:00
|
|
|
let banks_for_a_token = token.banks();
|
|
|
|
let oracle = token.oracle;
|
2022-05-27 22:05:34 -07:00
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
let mut ix = Instruction {
|
|
|
|
program_id: mango_v4::id(),
|
|
|
|
accounts: anchor_lang::ToAccountMetas::to_account_metas(
|
|
|
|
&mango_v4::accounts::TokenUpdateIndexAndRate {
|
2023-12-05 04:22:24 -08:00
|
|
|
group: token.group,
|
2022-12-16 04:10:46 -08:00
|
|
|
mint_info: token.mint_info_address,
|
|
|
|
oracle,
|
|
|
|
instructions: solana_program::sysvar::instructions::id(),
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
),
|
|
|
|
data: anchor_lang::InstructionData::data(
|
|
|
|
&mango_v4::instruction::TokenUpdateIndexAndRate {},
|
|
|
|
),
|
|
|
|
};
|
|
|
|
let mut banks = banks_for_a_token
|
|
|
|
.iter()
|
|
|
|
.map(|bank_pubkey| AccountMeta {
|
|
|
|
pubkey: *bank_pubkey,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: true,
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
2023-09-11 04:37:11 -07:00
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
ix.accounts.append(&mut banks);
|
2023-09-11 04:37:11 -07:00
|
|
|
|
|
|
|
let sim_result = match client.simulate(vec![ix.clone()]).await {
|
|
|
|
Ok(response) => response.value,
|
|
|
|
Err(e) => {
|
|
|
|
error!(token.name, "simulation request error: {e:?}");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(e) = sim_result.err {
|
|
|
|
error!(token.name, "simulation error: {e:?} {:?}", sim_result.logs);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
instructions.push(ix);
|
|
|
|
}
|
|
|
|
let pre = Instant::now();
|
|
|
|
let sig_result = client
|
|
|
|
.send_and_confirm_permissionless_tx(instructions)
|
|
|
|
.await;
|
2022-05-29 00:07:15 -07:00
|
|
|
|
2023-03-29 07:51:32 -07:00
|
|
|
let confirmation_time = pre.elapsed().as_millis();
|
|
|
|
METRIC_CONFIRMATION_TIMES.observe(confirmation_time as f64);
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
if let Err(e) = sig_result {
|
2023-03-29 07:51:32 -07:00
|
|
|
METRIC_UPDATE_TOKENS_FAILURE.inc();
|
2023-07-11 23:38:38 -07:00
|
|
|
info!(
|
2022-12-16 04:10:46 -08:00
|
|
|
"metricName=UpdateTokensV4Failure tokens={} durationMs={} error={}",
|
2023-07-11 23:38:38 -07:00
|
|
|
token_names, confirmation_time, e
|
2022-12-16 04:10:46 -08:00
|
|
|
);
|
2023-07-11 23:38:38 -07:00
|
|
|
error!("{:?}", e)
|
2022-12-16 04:10:46 -08:00
|
|
|
} else {
|
2023-03-29 07:51:32 -07:00
|
|
|
METRIC_UPDATE_TOKENS_SUCCESS.inc();
|
2023-07-11 23:38:38 -07:00
|
|
|
info!(
|
2022-12-16 04:10:46 -08:00
|
|
|
"metricName=UpdateTokensV4Success tokens={} durationMs={}",
|
2023-07-11 23:38:38 -07:00
|
|
|
token_names, confirmation_time,
|
2022-12-16 04:10:46 -08:00
|
|
|
);
|
2023-07-11 23:38:38 -07:00
|
|
|
info!("{:?}", sig_result);
|
2022-05-29 00:07:15 -07:00
|
|
|
}
|
2022-05-27 22:05:34 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn loop_consume_events(
|
|
|
|
mango_client: Arc<MangoClient>,
|
|
|
|
pk: Pubkey,
|
2023-12-05 04:22:24 -08:00
|
|
|
perp_market: &PerpMarketContext,
|
2022-11-21 10:35:04 -08:00
|
|
|
interval: u64,
|
2022-05-27 22:05:34 -07:00
|
|
|
) {
|
2024-01-09 02:25:55 -08:00
|
|
|
let mut interval = mango_v4_client::delay_interval(Duration::from_secs(interval));
|
2022-05-27 22:05:34 -07:00
|
|
|
loop {
|
|
|
|
interval.tick().await;
|
|
|
|
|
|
|
|
let client = mango_client.clone();
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
let find_accounts = || async {
|
2022-09-22 23:23:37 -07:00
|
|
|
let mut num_of_events = 0;
|
2022-12-16 04:10:46 -08:00
|
|
|
let mut event_queue: EventQueue = client
|
|
|
|
.client
|
|
|
|
.rpc_anchor_account(&perp_market.event_queue)
|
|
|
|
.await?;
|
2022-05-27 22:05:34 -07:00
|
|
|
|
|
|
|
// TODO: future, choose better constant of how many max events to pack
|
|
|
|
// TODO: future, choose better constant of how many max mango accounts to pack
|
2022-12-15 07:42:00 -08:00
|
|
|
let mut set = HashSet::new();
|
2022-05-27 22:05:34 -07:00
|
|
|
for _ in 0..10 {
|
|
|
|
let event = match event_queue.peek_front() {
|
|
|
|
None => break,
|
|
|
|
Some(e) => e,
|
|
|
|
};
|
|
|
|
match EventType::try_from(event.event_type)? {
|
|
|
|
EventType::Fill => {
|
|
|
|
let fill: &FillEvent = cast_ref(event);
|
2022-12-15 07:42:00 -08:00
|
|
|
set.insert(fill.maker);
|
|
|
|
set.insert(fill.taker);
|
2022-05-27 22:05:34 -07:00
|
|
|
}
|
|
|
|
EventType::Out => {
|
|
|
|
let out: &OutEvent = cast_ref(event);
|
2022-12-15 07:42:00 -08:00
|
|
|
set.insert(out.owner);
|
2022-05-27 22:05:34 -07:00
|
|
|
}
|
|
|
|
EventType::Liquidate => {}
|
|
|
|
}
|
|
|
|
event_queue.pop_front()?;
|
2022-12-16 04:10:46 -08:00
|
|
|
num_of_events += 1;
|
2022-09-22 23:23:37 -07:00
|
|
|
}
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
if num_of_events == 0 {
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Some((set, num_of_events)))
|
|
|
|
};
|
|
|
|
|
|
|
|
let event_info: anyhow::Result<Option<(HashSet<Pubkey>, u32)>> = find_accounts().await;
|
|
|
|
|
|
|
|
let (event_accounts, num_of_events) = match event_info {
|
|
|
|
Ok(Some(x)) => x,
|
|
|
|
Ok(None) => continue,
|
|
|
|
Err(err) => {
|
2023-07-11 23:38:38 -07:00
|
|
|
error!("preparing consume_events ams: {err:?}");
|
2022-12-16 04:10:46 -08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut event_ams = event_accounts
|
2022-12-15 07:42:00 -08:00
|
|
|
.iter()
|
|
|
|
.map(|key| -> AccountMeta {
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: *key,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: true,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
let pre = Instant::now();
|
|
|
|
let ix = Instruction {
|
|
|
|
program_id: mango_v4::id(),
|
|
|
|
accounts: {
|
|
|
|
let mut ams = anchor_lang::ToAccountMetas::to_account_metas(
|
|
|
|
&mango_v4::accounts::PerpConsumeEvents {
|
|
|
|
group: perp_market.group,
|
|
|
|
perp_market: pk,
|
|
|
|
event_queue: perp_market.event_queue,
|
2022-05-27 22:05:34 -07:00
|
|
|
},
|
2022-12-16 04:10:46 -08:00
|
|
|
None,
|
2022-08-24 00:53:46 -07:00
|
|
|
);
|
2022-12-16 04:10:46 -08:00
|
|
|
ams.append(&mut event_ams);
|
|
|
|
ams
|
|
|
|
},
|
|
|
|
data: anchor_lang::InstructionData::data(&mango_v4::instruction::PerpConsumeEvents {
|
|
|
|
limit: 10,
|
|
|
|
}),
|
|
|
|
};
|
2022-05-27 22:05:34 -07:00
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
let sig_result = client.send_and_confirm_permissionless_tx(vec![ix]).await;
|
2022-05-29 00:07:15 -07:00
|
|
|
|
2023-03-29 07:51:32 -07:00
|
|
|
let confirmation_time = pre.elapsed().as_millis();
|
|
|
|
METRIC_CONFIRMATION_TIMES.observe(confirmation_time as f64);
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
if let Err(e) = sig_result {
|
2023-03-29 07:51:32 -07:00
|
|
|
METRIC_CONSUME_EVENTS_FAILURE.inc();
|
2023-07-11 23:38:38 -07:00
|
|
|
info!(
|
2022-12-16 04:10:46 -08:00
|
|
|
"metricName=ConsumeEventsV4Failure market={} durationMs={} consumed={} error={}",
|
2023-12-05 04:22:24 -08:00
|
|
|
perp_market.name,
|
2023-03-29 07:51:32 -07:00
|
|
|
confirmation_time,
|
2022-12-16 04:10:46 -08:00
|
|
|
num_of_events,
|
|
|
|
e.to_string()
|
|
|
|
);
|
2023-07-11 23:38:38 -07:00
|
|
|
error!("{:?}", e)
|
2022-12-16 04:10:46 -08:00
|
|
|
} else {
|
2023-03-29 07:51:32 -07:00
|
|
|
METRIC_CONSUME_EVENTS_SUCCESS.inc();
|
2023-07-11 23:38:38 -07:00
|
|
|
info!(
|
2022-12-16 04:10:46 -08:00
|
|
|
"metricName=ConsumeEventsV4Success market={} durationMs={} consumed={}",
|
2023-12-05 04:22:24 -08:00
|
|
|
perp_market.name, confirmation_time, num_of_events,
|
2022-12-16 04:10:46 -08:00
|
|
|
);
|
2023-07-11 23:38:38 -07:00
|
|
|
info!("{:?}", sig_result);
|
2022-05-29 00:07:15 -07:00
|
|
|
}
|
2022-05-27 22:05:34 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn loop_update_funding(
|
|
|
|
mango_client: Arc<MangoClient>,
|
|
|
|
pk: Pubkey,
|
2023-12-05 04:22:24 -08:00
|
|
|
perp_market: &PerpMarketContext,
|
2022-11-21 10:35:04 -08:00
|
|
|
interval: u64,
|
2022-05-27 22:05:34 -07:00
|
|
|
) {
|
2024-01-09 02:25:55 -08:00
|
|
|
let mut interval = mango_v4_client::delay_interval(Duration::from_secs(interval));
|
2022-05-27 22:05:34 -07:00
|
|
|
loop {
|
|
|
|
interval.tick().await;
|
|
|
|
|
|
|
|
let client = mango_client.clone();
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
let pre = Instant::now();
|
|
|
|
let ix = Instruction {
|
|
|
|
program_id: mango_v4::id(),
|
|
|
|
accounts: anchor_lang::ToAccountMetas::to_account_metas(
|
|
|
|
&mango_v4::accounts::PerpUpdateFunding {
|
|
|
|
group: perp_market.group,
|
|
|
|
perp_market: pk,
|
|
|
|
bids: perp_market.bids,
|
|
|
|
asks: perp_market.asks,
|
|
|
|
oracle: perp_market.oracle,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
),
|
|
|
|
data: anchor_lang::InstructionData::data(&mango_v4::instruction::PerpUpdateFunding {}),
|
|
|
|
};
|
|
|
|
let sig_result = client.send_and_confirm_permissionless_tx(vec![ix]).await;
|
2022-05-29 00:07:15 -07:00
|
|
|
|
2023-03-29 07:51:32 -07:00
|
|
|
let confirmation_time = pre.elapsed().as_millis();
|
|
|
|
METRIC_CONFIRMATION_TIMES.observe(confirmation_time as f64);
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
if let Err(e) = sig_result {
|
2023-03-29 07:51:32 -07:00
|
|
|
METRIC_UPDATE_FUNDING_FAILURE.inc();
|
2023-07-11 23:38:38 -07:00
|
|
|
error!(
|
2022-12-16 04:10:46 -08:00
|
|
|
"metricName=UpdateFundingV4Error market={} durationMs={} error={}",
|
2023-12-05 04:22:24 -08:00
|
|
|
perp_market.name,
|
2023-03-29 07:51:32 -07:00
|
|
|
confirmation_time,
|
2022-12-16 04:10:46 -08:00
|
|
|
e.to_string()
|
|
|
|
);
|
2023-07-11 23:38:38 -07:00
|
|
|
error!("{:?}", e)
|
2022-12-16 04:10:46 -08:00
|
|
|
} else {
|
2023-03-29 07:51:32 -07:00
|
|
|
METRIC_UPDATE_FUNDING_SUCCESS.inc();
|
2023-07-11 23:38:38 -07:00
|
|
|
info!(
|
2022-12-16 04:10:46 -08:00
|
|
|
"metricName=UpdateFundingV4Success market={} durationMs={}",
|
2023-12-05 04:22:24 -08:00
|
|
|
perp_market.name, confirmation_time,
|
2022-12-16 04:10:46 -08:00
|
|
|
);
|
2023-07-11 23:38:38 -07:00
|
|
|
info!("{:?}", sig_result);
|
2022-05-29 00:07:15 -07:00
|
|
|
}
|
2022-05-27 22:05:34 -07:00
|
|
|
}
|
|
|
|
}
|
2024-02-13 03:39:28 -08:00
|
|
|
|
2024-02-24 01:04:30 -08:00
|
|
|
pub async fn loop_charge_collateral_fees(
|
|
|
|
mango_client: Arc<MangoClient>,
|
|
|
|
interval: u64,
|
|
|
|
max_cu_when_batching: u32,
|
|
|
|
) {
|
2024-02-13 03:39:28 -08:00
|
|
|
if interval == 0 {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make a new one separate from the mango_client.account_fetcher,
|
|
|
|
// because we don't want cached responses
|
|
|
|
let fetcher = RpcAccountFetcher {
|
|
|
|
rpc: mango_client.client.new_rpc_async(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let group: Group = account_fetcher_fetch_anchor_account(&fetcher, &mango_client.context.group)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let collateral_fee_interval = group.collateral_fee_interval;
|
|
|
|
|
|
|
|
let mut interval = mango_v4_client::delay_interval(Duration::from_secs(interval));
|
|
|
|
loop {
|
|
|
|
interval.tick().await;
|
|
|
|
|
2024-02-24 01:04:30 -08:00
|
|
|
match charge_collateral_fees_inner(
|
|
|
|
&mango_client,
|
|
|
|
&fetcher,
|
|
|
|
collateral_fee_interval,
|
|
|
|
max_cu_when_batching,
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
{
|
2024-02-13 03:39:28 -08:00
|
|
|
Ok(()) => {}
|
|
|
|
Err(err) => {
|
|
|
|
error!("charge_collateral_fees error: {err:?}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn charge_collateral_fees_inner(
|
|
|
|
client: &MangoClient,
|
|
|
|
fetcher: &RpcAccountFetcher,
|
|
|
|
collateral_fee_interval: u64,
|
2024-02-24 01:04:30 -08:00
|
|
|
max_cu_when_batching: u32,
|
2024-02-13 03:39:28 -08:00
|
|
|
) -> anyhow::Result<()> {
|
|
|
|
let mango_accounts = fetcher
|
|
|
|
.fetch_program_accounts(&mango_v4::id(), MangoAccount::DISCRIMINATOR)
|
|
|
|
.await
|
|
|
|
.context("fetching mango accounts")?
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(
|
|
|
|
|(pk, data)| match MangoAccountValue::from_bytes(&data.data()[8..]) {
|
|
|
|
Ok(acc) => Some((pk, acc)),
|
|
|
|
Err(err) => {
|
|
|
|
error!(pk=%pk, "charge_collateral_fees could not parse account: {err:?}");
|
|
|
|
None
|
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut ix_to_send = Vec::new();
|
|
|
|
let now_ts = SystemTime::now()
|
|
|
|
.duration_since(UNIX_EPOCH)
|
|
|
|
.unwrap()
|
|
|
|
.as_secs() as u64;
|
|
|
|
for (pk, account) in mango_accounts {
|
|
|
|
let should_reset =
|
|
|
|
collateral_fee_interval == 0 && account.fixed.last_collateral_fee_charge > 0;
|
|
|
|
let should_charge = collateral_fee_interval > 0
|
|
|
|
&& now_ts > account.fixed.last_collateral_fee_charge + collateral_fee_interval;
|
|
|
|
if !(should_reset || should_charge) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let ixs = match client
|
|
|
|
.token_charge_collateral_fees_instruction((&pk, &account))
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
Ok(ixs) => ixs,
|
|
|
|
Err(err) => {
|
|
|
|
error!(pk=%pk, "charge_collateral_fees could not build instruction: {err:?}");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ix_to_send.push(ixs);
|
|
|
|
}
|
|
|
|
|
2024-02-14 02:32:57 -08:00
|
|
|
let txsigs = send_batched_log_errors_no_confirm(
|
2024-02-13 03:39:28 -08:00
|
|
|
client.transaction_builder().await?,
|
|
|
|
&client.client,
|
|
|
|
&ix_to_send,
|
2024-02-24 01:04:30 -08:00
|
|
|
max_cu_when_batching,
|
2024-02-13 03:39:28 -08:00
|
|
|
)
|
|
|
|
.await;
|
2024-02-14 02:32:57 -08:00
|
|
|
info!("charge collateral fees: {:?}", txsigs);
|
2024-02-13 03:39:28 -08:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Try to batch the instructions into transactions and send them
|
|
|
|
async fn send_batched_log_errors_no_confirm(
|
|
|
|
mut tx_builder: TransactionBuilder,
|
|
|
|
client: &mango_v4_client::Client,
|
|
|
|
ixs_list: &[PreparedInstructions],
|
2024-02-24 01:04:30 -08:00
|
|
|
max_cu: u32,
|
2024-02-14 02:32:57 -08:00
|
|
|
) -> Vec<Signature> {
|
|
|
|
let mut txsigs = Vec::new();
|
|
|
|
|
2024-02-13 03:39:28 -08:00
|
|
|
let mut current_batch = PreparedInstructions::new();
|
|
|
|
for ixs in ixs_list {
|
|
|
|
let previous_batch = current_batch.clone();
|
|
|
|
current_batch.append(ixs.clone());
|
|
|
|
|
|
|
|
tx_builder.instructions = current_batch.clone().to_instructions();
|
2024-02-24 01:04:30 -08:00
|
|
|
if !tx_builder.transaction_size().is_ok() || current_batch.cu > max_cu {
|
2024-02-13 03:39:28 -08:00
|
|
|
tx_builder.instructions = previous_batch.to_instructions();
|
|
|
|
match tx_builder.send(client).await {
|
|
|
|
Err(err) => error!("could not send transaction: {err:?}"),
|
2024-02-14 02:32:57 -08:00
|
|
|
Ok(txsig) => txsigs.push(txsig),
|
2024-02-13 03:39:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
current_batch = ixs.clone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !current_batch.is_empty() {
|
|
|
|
tx_builder.instructions = current_batch.to_instructions();
|
|
|
|
match tx_builder.send(client).await {
|
|
|
|
Err(err) => error!("could not send transaction: {err:?}"),
|
2024-02-14 02:32:57 -08:00
|
|
|
Ok(txsig) => txsigs.push(txsig),
|
2024-02-13 03:39:28 -08:00
|
|
|
}
|
|
|
|
}
|
2024-02-14 02:32:57 -08:00
|
|
|
|
|
|
|
txsigs
|
2024-02-13 03:39:28 -08:00
|
|
|
}
|