2023-04-19 04:29:26 -07:00
|
|
|
use log::warn;
|
2023-04-14 05:18:04 -07:00
|
|
|
use solana_sdk::compute_budget::ComputeBudgetInstruction;
|
2023-04-19 04:29:26 -07:00
|
|
|
use tokio::spawn;
|
2023-04-14 05:18:04 -07:00
|
|
|
|
2023-02-23 02:02:57 -08:00
|
|
|
use {
|
2023-03-15 08:13:25 -07:00
|
|
|
crate::{
|
|
|
|
helpers::to_sdk_instruction,
|
2023-04-14 05:04:35 -07:00
|
|
|
noop,
|
2023-03-15 08:13:25 -07:00
|
|
|
states::{KeeperInstruction, PerpMarketCache, TransactionSendRecord},
|
|
|
|
tpu_manager::TpuManager,
|
|
|
|
},
|
|
|
|
chrono::Utc,
|
2023-02-23 02:02:57 -08:00
|
|
|
iter_tools::Itertools,
|
2023-02-24 06:12:14 -08:00
|
|
|
solana_program::pubkey::Pubkey,
|
2023-02-23 02:02:57 -08:00
|
|
|
solana_sdk::{
|
2023-02-23 07:24:26 -08:00
|
|
|
hash::Hash, instruction::Instruction, message::Message, signature::Keypair, signer::Signer,
|
|
|
|
transaction::Transaction,
|
2023-02-23 02:02:57 -08:00
|
|
|
},
|
2023-03-14 05:39:19 -07:00
|
|
|
std::sync::{
|
2023-03-15 08:13:25 -07:00
|
|
|
atomic::{AtomicBool, AtomicU64, Ordering},
|
2023-03-14 05:39:19 -07:00
|
|
|
Arc,
|
2023-02-21 00:49:20 -08:00
|
|
|
},
|
2023-03-14 05:39:19 -07:00
|
|
|
tokio::{sync::RwLock, task::JoinHandle},
|
2023-02-21 00:49:20 -08:00
|
|
|
};
|
|
|
|
|
2023-02-23 02:02:57 -08:00
|
|
|
fn create_root_bank_update_instructions(perp_markets: &[PerpMarketCache]) -> Vec<Instruction> {
|
2023-02-21 00:49:20 -08:00
|
|
|
perp_markets
|
|
|
|
.iter()
|
|
|
|
.map(|perp_market| {
|
|
|
|
let ix = mango::instruction::update_root_bank(
|
|
|
|
&perp_market.mango_program_pk,
|
|
|
|
&perp_market.mango_group_pk,
|
|
|
|
&perp_market.mango_cache_pk,
|
|
|
|
&perp_market.root_bank,
|
|
|
|
perp_market.node_banks.as_slice(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
to_sdk_instruction(ix)
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2023-02-23 07:24:26 -08:00
|
|
|
fn create_update_fundings_instructions(perp_markets: &[PerpMarketCache]) -> Vec<Instruction> {
|
|
|
|
perp_markets
|
|
|
|
.iter()
|
|
|
|
.map(|perp_market| {
|
|
|
|
let ix = mango::instruction::update_funding(
|
|
|
|
&perp_market.mango_program_pk,
|
|
|
|
&perp_market.mango_group_pk,
|
|
|
|
&perp_market.mango_cache_pk,
|
|
|
|
&perp_market.perp_market_pk,
|
|
|
|
&perp_market.bids,
|
|
|
|
&perp_market.asks,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
to_sdk_instruction(ix)
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_cache_root_bank_instruction(perp_markets: &[PerpMarketCache]) -> Instruction {
|
|
|
|
let mango_program_pk = perp_markets[0].mango_program_pk;
|
|
|
|
let mango_group_pk = perp_markets[0].mango_group_pk;
|
|
|
|
let mango_cache_pk = perp_markets[0].mango_cache_pk;
|
|
|
|
let root_banks = perp_markets.iter().map(|x| x.root_bank).collect_vec();
|
|
|
|
|
|
|
|
let ix = mango::instruction::cache_root_banks(
|
|
|
|
&mango_program_pk,
|
|
|
|
&mango_group_pk,
|
|
|
|
&mango_cache_pk,
|
|
|
|
root_banks.as_slice(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
to_sdk_instruction(ix)
|
|
|
|
}
|
|
|
|
|
2023-02-23 05:41:12 -08:00
|
|
|
fn create_update_price_cache_instructions(perp_markets: &[PerpMarketCache]) -> Instruction {
|
|
|
|
let mango_program_pk = perp_markets[0].mango_program_pk;
|
|
|
|
let mango_group_pk = perp_markets[0].mango_group_pk;
|
|
|
|
let mango_cache_pk = perp_markets[0].mango_cache_pk;
|
|
|
|
let price_oracles = perp_markets.iter().map(|x| x.price_oracle).collect_vec();
|
|
|
|
|
|
|
|
let ix = mango::instruction::cache_prices(
|
|
|
|
&mango_program_pk,
|
|
|
|
&mango_group_pk,
|
|
|
|
&mango_cache_pk,
|
2023-02-23 07:24:26 -08:00
|
|
|
price_oracles.as_slice(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
2023-02-23 05:41:12 -08:00
|
|
|
to_sdk_instruction(ix)
|
2023-02-21 00:49:20 -08:00
|
|
|
}
|
|
|
|
|
2023-02-23 02:02:57 -08:00
|
|
|
fn create_cache_perp_markets_instructions(perp_markets: &[PerpMarketCache]) -> Instruction {
|
|
|
|
let mango_program_pk = perp_markets[0].mango_program_pk;
|
|
|
|
let mango_group_pk = perp_markets[0].mango_group_pk;
|
|
|
|
let mango_cache_pk = perp_markets[0].mango_cache_pk;
|
|
|
|
let perp_market_pks = perp_markets.iter().map(|x| x.perp_market_pk).collect_vec();
|
|
|
|
let ix = mango::instruction::cache_perp_markets(
|
|
|
|
&mango_program_pk,
|
|
|
|
&mango_group_pk,
|
|
|
|
&mango_cache_pk,
|
|
|
|
perp_market_pks.as_slice(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
to_sdk_instruction(ix)
|
|
|
|
}
|
|
|
|
|
2023-04-19 04:29:26 -07:00
|
|
|
pub fn prepare_transaction(
|
2023-04-14 04:45:22 -07:00
|
|
|
mut ixs: Vec<Instruction>,
|
2023-04-19 04:29:26 -07:00
|
|
|
recent_blockhash: &Hash,
|
2023-03-15 08:13:25 -07:00
|
|
|
current_slot: Arc<AtomicU64>,
|
2023-02-21 00:49:20 -08:00
|
|
|
payer: &Keypair,
|
2023-03-15 08:13:25 -07:00
|
|
|
prioritization_fee: u64,
|
|
|
|
keeper_instruction: KeeperInstruction,
|
2023-04-19 04:29:26 -07:00
|
|
|
) -> (Transaction, TransactionSendRecord) {
|
2023-04-14 05:04:35 -07:00
|
|
|
// add a noop with a current timestamp to ensure unique txs
|
2023-04-14 05:18:04 -07:00
|
|
|
ixs.push(noop::timestamp());
|
2023-04-14 04:45:22 -07:00
|
|
|
// add priority fees
|
2023-04-14 05:18:04 -07:00
|
|
|
ixs.push(ComputeBudgetInstruction::set_compute_unit_price(
|
|
|
|
prioritization_fee,
|
|
|
|
));
|
2023-04-14 04:45:22 -07:00
|
|
|
let mut tx = Transaction::new_unsigned(Message::new(&ixs, Some(&payer.pubkey())));
|
2023-02-23 02:02:57 -08:00
|
|
|
tx.sign(&[payer], *recent_blockhash);
|
2023-03-15 08:13:25 -07:00
|
|
|
|
|
|
|
let tx_send_record = TransactionSendRecord {
|
|
|
|
signature: tx.signatures[0],
|
|
|
|
sent_at: Utc::now(),
|
|
|
|
sent_slot: current_slot.load(Ordering::Acquire),
|
|
|
|
market_maker: None,
|
|
|
|
market: None,
|
|
|
|
priority_fees: prioritization_fee,
|
|
|
|
keeper_instruction: Some(keeper_instruction),
|
|
|
|
};
|
2023-07-06 23:12:57 -07:00
|
|
|
(tx, tx_send_record)
|
2023-02-21 00:49:20 -08:00
|
|
|
}
|
|
|
|
|
2023-02-24 06:12:14 -08:00
|
|
|
pub fn create_update_and_cache_quote_banks(
|
|
|
|
perp_markets: &[PerpMarketCache],
|
|
|
|
quote_root_bank: Pubkey,
|
|
|
|
quote_node_banks: Vec<Pubkey>,
|
|
|
|
) -> Vec<Instruction> {
|
|
|
|
let mango_program_pk = perp_markets[0].mango_program_pk;
|
|
|
|
let mango_group_pk = perp_markets[0].mango_group_pk;
|
|
|
|
let mango_cache_pk = perp_markets[0].mango_cache_pk;
|
|
|
|
|
|
|
|
let ix_update = mango::instruction::update_root_bank(
|
|
|
|
&mango_program_pk,
|
|
|
|
&mango_group_pk,
|
|
|
|
&mango_cache_pk,
|
|
|
|
"e_root_bank,
|
|
|
|
quote_node_banks.as_slice(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
let ix_cache = mango::instruction::cache_root_banks(
|
|
|
|
&mango_program_pk,
|
|
|
|
&mango_group_pk,
|
|
|
|
&mango_cache_pk,
|
|
|
|
&[quote_root_bank],
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
vec![to_sdk_instruction(ix_update), to_sdk_instruction(ix_cache)]
|
|
|
|
}
|
|
|
|
|
2023-07-06 23:12:57 -07:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2023-02-23 02:02:57 -08:00
|
|
|
pub fn start_keepers(
|
2023-02-21 00:49:20 -08:00
|
|
|
exit_signal: Arc<AtomicBool>,
|
2023-03-15 08:13:25 -07:00
|
|
|
tpu_manager: TpuManager,
|
2023-02-21 00:49:20 -08:00
|
|
|
perp_markets: Vec<PerpMarketCache>,
|
|
|
|
blockhash: Arc<RwLock<Hash>>,
|
2023-03-15 08:13:25 -07:00
|
|
|
current_slot: Arc<AtomicU64>,
|
2023-02-21 00:49:20 -08:00
|
|
|
authority: &Keypair,
|
2023-02-24 06:12:14 -08:00
|
|
|
quote_root_bank: Pubkey,
|
|
|
|
quote_node_banks: Vec<Pubkey>,
|
2023-03-15 08:13:25 -07:00
|
|
|
prioritization_fee: u64,
|
2023-02-21 00:49:20 -08:00
|
|
|
) -> JoinHandle<()> {
|
|
|
|
let authority = Keypair::from_bytes(&authority.to_bytes()).unwrap();
|
2023-03-14 05:39:19 -07:00
|
|
|
tokio::spawn(async move {
|
2023-03-15 08:13:25 -07:00
|
|
|
let current_slot = current_slot.clone();
|
|
|
|
|
2023-04-14 04:45:22 -07:00
|
|
|
let root_update_ixs = create_root_bank_update_instructions(&perp_markets);
|
|
|
|
let cache_prices = vec![create_update_price_cache_instructions(&perp_markets)];
|
|
|
|
let update_perp_cache = vec![create_cache_perp_markets_instructions(&perp_markets)];
|
|
|
|
let cache_root_bank_ix = vec![create_cache_root_bank_instruction(&perp_markets)];
|
|
|
|
let update_funding_ix = create_update_fundings_instructions(&perp_markets);
|
|
|
|
let quote_root_bank_ix =
|
2023-03-14 05:39:19 -07:00
|
|
|
create_update_and_cache_quote_banks(&perp_markets, quote_root_bank, quote_node_banks);
|
|
|
|
|
|
|
|
while !exit_signal.load(Ordering::Relaxed) {
|
2023-04-19 04:29:26 -07:00
|
|
|
let recent_blockhash = blockhash.read().await.to_owned();
|
|
|
|
|
|
|
|
let mut tx_batch = vec![];
|
|
|
|
tx_batch.push(prepare_transaction(
|
2023-04-14 04:45:22 -07:00
|
|
|
cache_prices.clone(),
|
2023-04-19 04:29:26 -07:00
|
|
|
&recent_blockhash,
|
2023-03-15 08:13:25 -07:00
|
|
|
current_slot.clone(),
|
2023-03-14 05:39:19 -07:00
|
|
|
&authority,
|
2023-03-15 08:13:25 -07:00
|
|
|
prioritization_fee,
|
|
|
|
KeeperInstruction::CachePrice,
|
2023-04-19 04:29:26 -07:00
|
|
|
));
|
2023-03-14 05:39:19 -07:00
|
|
|
|
2023-04-19 04:29:26 -07:00
|
|
|
tx_batch.push(prepare_transaction(
|
2023-04-14 04:45:22 -07:00
|
|
|
quote_root_bank_ix.clone(),
|
2023-04-19 04:29:26 -07:00
|
|
|
&recent_blockhash,
|
2023-03-15 08:13:25 -07:00
|
|
|
current_slot.clone(),
|
2023-03-14 05:39:19 -07:00
|
|
|
&authority,
|
2023-03-15 08:13:25 -07:00
|
|
|
prioritization_fee,
|
|
|
|
KeeperInstruction::UpdateAndCacheQuoteRootBank,
|
2023-04-19 04:29:26 -07:00
|
|
|
));
|
2023-03-14 05:39:19 -07:00
|
|
|
|
|
|
|
for updates in update_funding_ix.chunks(3) {
|
2023-04-19 04:29:26 -07:00
|
|
|
tx_batch.push(prepare_transaction(
|
2023-04-14 04:45:22 -07:00
|
|
|
updates.to_vec(),
|
2023-04-19 04:29:26 -07:00
|
|
|
&recent_blockhash,
|
2023-03-15 08:13:25 -07:00
|
|
|
current_slot.clone(),
|
|
|
|
&authority,
|
|
|
|
prioritization_fee,
|
|
|
|
KeeperInstruction::UpdateFunding,
|
2023-04-19 04:29:26 -07:00
|
|
|
));
|
2023-02-21 00:49:20 -08:00
|
|
|
}
|
2023-04-19 04:29:26 -07:00
|
|
|
tx_batch.push(prepare_transaction(
|
2023-04-14 04:45:22 -07:00
|
|
|
root_update_ixs.clone(),
|
2023-04-19 04:29:26 -07:00
|
|
|
&recent_blockhash,
|
2023-03-15 08:13:25 -07:00
|
|
|
current_slot.clone(),
|
2023-03-14 05:39:19 -07:00
|
|
|
&authority,
|
2023-03-15 08:13:25 -07:00
|
|
|
prioritization_fee,
|
|
|
|
KeeperInstruction::UpdateRootBanks,
|
2023-04-19 04:29:26 -07:00
|
|
|
));
|
2023-03-14 05:39:19 -07:00
|
|
|
|
2023-04-19 04:29:26 -07:00
|
|
|
tx_batch.push(prepare_transaction(
|
2023-04-14 04:45:22 -07:00
|
|
|
update_perp_cache.clone(),
|
2023-04-19 04:29:26 -07:00
|
|
|
&recent_blockhash,
|
2023-03-15 08:13:25 -07:00
|
|
|
current_slot.clone(),
|
2023-03-14 05:39:19 -07:00
|
|
|
&authority,
|
2023-03-15 08:13:25 -07:00
|
|
|
prioritization_fee,
|
|
|
|
KeeperInstruction::UpdatePerpCache,
|
2023-04-19 04:29:26 -07:00
|
|
|
));
|
2023-03-14 05:39:19 -07:00
|
|
|
|
2023-04-19 04:29:26 -07:00
|
|
|
tx_batch.push(prepare_transaction(
|
2023-04-14 04:45:22 -07:00
|
|
|
cache_root_bank_ix.clone(),
|
2023-04-19 04:29:26 -07:00
|
|
|
&recent_blockhash,
|
2023-03-15 08:13:25 -07:00
|
|
|
current_slot.clone(),
|
2023-03-14 05:39:19 -07:00
|
|
|
&authority,
|
2023-03-15 08:13:25 -07:00
|
|
|
prioritization_fee,
|
|
|
|
KeeperInstruction::CacheRootBanks,
|
2023-04-19 04:29:26 -07:00
|
|
|
));
|
|
|
|
|
|
|
|
let start_slot = current_slot.load(Ordering::Relaxed);
|
|
|
|
let start_time = Utc::now();
|
|
|
|
let tpu_manager = tpu_manager.clone();
|
|
|
|
spawn(async move {
|
|
|
|
if !tpu_manager.send_transaction_batch(&tx_batch).await {
|
|
|
|
warn!("issue when sending batch started slot={start_slot} time={start_time} hash={recent_blockhash:?}");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-03-14 05:39:19 -07:00
|
|
|
std::thread::sleep(std::time::Duration::from_secs(1));
|
|
|
|
}
|
|
|
|
})
|
2023-02-21 00:49:20 -08:00
|
|
|
}
|