Merge pull request #16 from blockworks-foundation/max/batch
improve tx submission
This commit is contained in:
commit
5f76154894
|
@ -3241,6 +3241,7 @@ dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"bincode",
|
||||||
"borsh 0.9.3",
|
"borsh 0.9.3",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|
|
@ -60,6 +60,7 @@ solana-program = "1.9.17"
|
||||||
mango = { git = "https://github.com/blockworks-foundation/mango-v3.git", tag = "v3.6.0", default-features = false }
|
mango = { git = "https://github.com/blockworks-foundation/mango-v3.git", tag = "v3.6.0", default-features = false }
|
||||||
mango-common = { git = "https://github.com/blockworks-foundation/mango-v3.git", tag = "v3.6.0" }
|
mango-common = { git = "https://github.com/blockworks-foundation/mango-v3.git", tag = "v3.6.0" }
|
||||||
mango-feeds-connector = { git = "https://github.com/blockworks-foundation/mango-feeds.git", branch = "ckamm/solana-versions2", default-features = false, features = ["solana-1-15"] }
|
mango-feeds-connector = { git = "https://github.com/blockworks-foundation/mango-feeds.git", branch = "ckamm/solana-versions2", default-features = false, features = ["solana-1-15"] }
|
||||||
|
bincode = "1.3.3"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
|
|
@ -106,8 +106,11 @@ pub fn start(
|
||||||
keeper_instruction: Some(KeeperInstruction::ConsumeEvents),
|
keeper_instruction: Some(KeeperInstruction::ConsumeEvents),
|
||||||
};
|
};
|
||||||
|
|
||||||
let ok = tpu_manager.send_transaction(&tx, tx_send_record).await;
|
let tpu_manager = tpu_manager.clone();
|
||||||
trace!("send tx={:?} ok={ok}", tx.signatures[0]);
|
tokio::spawn(async move {
|
||||||
|
let ok = tpu_manager.send_transaction(&tx, tx_send_record).await;
|
||||||
|
trace!("send tx={:?} ok={ok}", tx.signatures[0]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
use log::warn;
|
||||||
use solana_sdk::compute_budget::ComputeBudgetInstruction;
|
use solana_sdk::compute_budget::ComputeBudgetInstruction;
|
||||||
|
use tokio::spawn;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
@ -103,15 +105,14 @@ fn create_cache_perp_markets_instructions(perp_markets: &[PerpMarketCache]) -> I
|
||||||
to_sdk_instruction(ix)
|
to_sdk_instruction(ix)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_transaction(
|
pub fn prepare_transaction(
|
||||||
tpu_manager: TpuManager,
|
|
||||||
mut ixs: Vec<Instruction>,
|
mut ixs: Vec<Instruction>,
|
||||||
blockhash: Arc<RwLock<Hash>>,
|
recent_blockhash: &Hash,
|
||||||
current_slot: Arc<AtomicU64>,
|
current_slot: Arc<AtomicU64>,
|
||||||
payer: &Keypair,
|
payer: &Keypair,
|
||||||
prioritization_fee: u64,
|
prioritization_fee: u64,
|
||||||
keeper_instruction: KeeperInstruction,
|
keeper_instruction: KeeperInstruction,
|
||||||
) {
|
) -> (Transaction, TransactionSendRecord) {
|
||||||
// add a noop with a current timestamp to ensure unique txs
|
// add a noop with a current timestamp to ensure unique txs
|
||||||
ixs.push(noop::timestamp());
|
ixs.push(noop::timestamp());
|
||||||
// add priority fees
|
// add priority fees
|
||||||
|
@ -119,7 +120,6 @@ pub async fn send_transaction(
|
||||||
prioritization_fee,
|
prioritization_fee,
|
||||||
));
|
));
|
||||||
let mut tx = Transaction::new_unsigned(Message::new(&ixs, Some(&payer.pubkey())));
|
let mut tx = Transaction::new_unsigned(Message::new(&ixs, Some(&payer.pubkey())));
|
||||||
let recent_blockhash = blockhash.read().await;
|
|
||||||
tx.sign(&[payer], *recent_blockhash);
|
tx.sign(&[payer], *recent_blockhash);
|
||||||
|
|
||||||
let tx_send_record = TransactionSendRecord {
|
let tx_send_record = TransactionSendRecord {
|
||||||
|
@ -131,7 +131,7 @@ pub async fn send_transaction(
|
||||||
priority_fees: prioritization_fee,
|
priority_fees: prioritization_fee,
|
||||||
keeper_instruction: Some(keeper_instruction),
|
keeper_instruction: Some(keeper_instruction),
|
||||||
};
|
};
|
||||||
tpu_manager.send_transaction(&tx, tx_send_record).await;
|
return (tx, tx_send_record);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_update_and_cache_quote_banks(
|
pub fn create_update_and_cache_quote_banks(
|
||||||
|
@ -184,76 +184,74 @@ pub fn start_keepers(
|
||||||
let quote_root_bank_ix =
|
let quote_root_bank_ix =
|
||||||
create_update_and_cache_quote_banks(&perp_markets, quote_root_bank, quote_node_banks);
|
create_update_and_cache_quote_banks(&perp_markets, quote_root_bank, quote_node_banks);
|
||||||
|
|
||||||
let blockhash = blockhash.clone();
|
|
||||||
|
|
||||||
while !exit_signal.load(Ordering::Relaxed) {
|
while !exit_signal.load(Ordering::Relaxed) {
|
||||||
send_transaction(
|
let recent_blockhash = blockhash.read().await.to_owned();
|
||||||
tpu_manager.clone(),
|
|
||||||
|
let mut tx_batch = vec![];
|
||||||
|
tx_batch.push(prepare_transaction(
|
||||||
cache_prices.clone(),
|
cache_prices.clone(),
|
||||||
blockhash.clone(),
|
&recent_blockhash,
|
||||||
current_slot.clone(),
|
current_slot.clone(),
|
||||||
&authority,
|
&authority,
|
||||||
prioritization_fee,
|
prioritization_fee,
|
||||||
KeeperInstruction::CachePrice,
|
KeeperInstruction::CachePrice,
|
||||||
)
|
));
|
||||||
.await;
|
|
||||||
|
|
||||||
send_transaction(
|
tx_batch.push(prepare_transaction(
|
||||||
tpu_manager.clone(),
|
|
||||||
quote_root_bank_ix.clone(),
|
quote_root_bank_ix.clone(),
|
||||||
blockhash.clone(),
|
&recent_blockhash,
|
||||||
current_slot.clone(),
|
current_slot.clone(),
|
||||||
&authority,
|
&authority,
|
||||||
prioritization_fee,
|
prioritization_fee,
|
||||||
KeeperInstruction::UpdateAndCacheQuoteRootBank,
|
KeeperInstruction::UpdateAndCacheQuoteRootBank,
|
||||||
)
|
));
|
||||||
.await;
|
|
||||||
|
|
||||||
for updates in update_funding_ix.chunks(3) {
|
for updates in update_funding_ix.chunks(3) {
|
||||||
send_transaction(
|
tx_batch.push(prepare_transaction(
|
||||||
tpu_manager.clone(),
|
|
||||||
updates.to_vec(),
|
updates.to_vec(),
|
||||||
blockhash.clone(),
|
&recent_blockhash,
|
||||||
current_slot.clone(),
|
current_slot.clone(),
|
||||||
&authority,
|
&authority,
|
||||||
prioritization_fee,
|
prioritization_fee,
|
||||||
KeeperInstruction::UpdateFunding,
|
KeeperInstruction::UpdateFunding,
|
||||||
)
|
));
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
|
tx_batch.push(prepare_transaction(
|
||||||
send_transaction(
|
|
||||||
tpu_manager.clone(),
|
|
||||||
root_update_ixs.clone(),
|
root_update_ixs.clone(),
|
||||||
blockhash.clone(),
|
&recent_blockhash,
|
||||||
current_slot.clone(),
|
current_slot.clone(),
|
||||||
&authority,
|
&authority,
|
||||||
prioritization_fee,
|
prioritization_fee,
|
||||||
KeeperInstruction::UpdateRootBanks,
|
KeeperInstruction::UpdateRootBanks,
|
||||||
)
|
));
|
||||||
.await;
|
|
||||||
|
|
||||||
send_transaction(
|
tx_batch.push(prepare_transaction(
|
||||||
tpu_manager.clone(),
|
|
||||||
update_perp_cache.clone(),
|
update_perp_cache.clone(),
|
||||||
blockhash.clone(),
|
&recent_blockhash,
|
||||||
current_slot.clone(),
|
current_slot.clone(),
|
||||||
&authority,
|
&authority,
|
||||||
prioritization_fee,
|
prioritization_fee,
|
||||||
KeeperInstruction::UpdatePerpCache,
|
KeeperInstruction::UpdatePerpCache,
|
||||||
)
|
));
|
||||||
.await;
|
|
||||||
|
|
||||||
send_transaction(
|
tx_batch.push(prepare_transaction(
|
||||||
tpu_manager.clone(),
|
|
||||||
cache_root_bank_ix.clone(),
|
cache_root_bank_ix.clone(),
|
||||||
blockhash.clone(),
|
&recent_blockhash,
|
||||||
current_slot.clone(),
|
current_slot.clone(),
|
||||||
&authority,
|
&authority,
|
||||||
prioritization_fee,
|
prioritization_fee,
|
||||||
KeeperInstruction::CacheRootBanks,
|
KeeperInstruction::CacheRootBanks,
|
||||||
)
|
));
|
||||||
.await;
|
|
||||||
|
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:?}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -123,7 +123,7 @@ pub async fn main() -> anyhow::Result<()> {
|
||||||
// continuosly fetch blockhash
|
// continuosly fetch blockhash
|
||||||
let rpc_client = Arc::new(RpcClient::new_with_commitment(
|
let rpc_client = Arc::new(RpcClient::new_with_commitment(
|
||||||
json_rpc_url.to_string(),
|
json_rpc_url.to_string(),
|
||||||
CommitmentConfig::confirmed(),
|
CommitmentConfig::finalized(),
|
||||||
));
|
));
|
||||||
let exit_signal = Arc::new(AtomicBool::new(false));
|
let exit_signal = Arc::new(AtomicBool::new(false));
|
||||||
let latest_blockhash = get_latest_blockhash(&rpc_client.clone()).await;
|
let latest_blockhash = get_latest_blockhash(&rpc_client.clone()).await;
|
||||||
|
|
|
@ -21,7 +21,10 @@ use solana_sdk::{
|
||||||
compute_budget, hash::Hash, instruction::Instruction, message::Message, signature::Keypair,
|
compute_budget, hash::Hash, instruction::Instruction, message::Message, signature::Keypair,
|
||||||
signer::Signer, transaction::Transaction,
|
signer::Signer, transaction::Transaction,
|
||||||
};
|
};
|
||||||
use tokio::{sync::RwLock, task::JoinHandle};
|
use tokio::{
|
||||||
|
sync::RwLock,
|
||||||
|
task::{self, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
helpers::{to_sdk_instruction, to_sp_pk},
|
helpers::{to_sdk_instruction, to_sp_pk},
|
||||||
|
@ -170,6 +173,7 @@ pub async fn send_mm_transactions(
|
||||||
100,
|
100,
|
||||||
1000,
|
1000,
|
||||||
);
|
);
|
||||||
|
let mut batch_to_send = Vec::with_capacity(perp_market_caches.len());
|
||||||
for (i, c) in perp_market_caches.iter().enumerate() {
|
for (i, c) in perp_market_caches.iter().enumerate() {
|
||||||
let prioritization_fee = prioritization_fee_by_market[i];
|
let prioritization_fee = prioritization_fee_by_market[i];
|
||||||
let mut tx = create_ask_bid_transaction(
|
let mut tx = create_ask_bid_transaction(
|
||||||
|
@ -182,7 +186,7 @@ pub async fn send_mm_transactions(
|
||||||
let recent_blockhash = *blockhash.read().await;
|
let recent_blockhash = *blockhash.read().await;
|
||||||
tx.sign(&[mango_account_signer], recent_blockhash);
|
tx.sign(&[mango_account_signer], recent_blockhash);
|
||||||
|
|
||||||
let tx_send_record = TransactionSendRecord {
|
let record = TransactionSendRecord {
|
||||||
signature: tx.signatures[0],
|
signature: tx.signatures[0],
|
||||||
sent_at: Utc::now(),
|
sent_at: Utc::now(),
|
||||||
sent_slot: slot.load(Ordering::Acquire),
|
sent_slot: slot.load(Ordering::Acquire),
|
||||||
|
@ -191,10 +195,15 @@ pub async fn send_mm_transactions(
|
||||||
priority_fees: prioritization_fee,
|
priority_fees: prioritization_fee,
|
||||||
keeper_instruction: None,
|
keeper_instruction: None,
|
||||||
};
|
};
|
||||||
if !tpu_manager.send_transaction(&tx, tx_send_record).await {
|
batch_to_send.push((tx, record));
|
||||||
|
}
|
||||||
|
|
||||||
|
let tpu_manager = tpu_manager.clone();
|
||||||
|
task::spawn(async move {
|
||||||
|
if !tpu_manager.send_transaction_batch(&batch_to_send).await {
|
||||||
println!("sending failed on tpu client");
|
println!("sending failed on tpu client");
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
use bincode::serialize;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use solana_client::nonblocking::rpc_client::RpcClient;
|
use solana_client::nonblocking::rpc_client::RpcClient;
|
||||||
use solana_client::{connection_cache::ConnectionCache, nonblocking::tpu_client::TpuClient};
|
use solana_client::{connection_cache::ConnectionCache, nonblocking::tpu_client::TpuClient};
|
||||||
use solana_quic_client::{QuicConfig, QuicConnectionManager, QuicPool};
|
use solana_quic_client::{QuicConfig, QuicConnectionManager, QuicPool};
|
||||||
use solana_sdk::signature::Keypair;
|
use solana_sdk::signature::Keypair;
|
||||||
|
use solana_sdk::transaction::Transaction;
|
||||||
use std::{
|
use std::{
|
||||||
net::{IpAddr, Ipv4Addr},
|
net::{IpAddr, Ipv4Addr},
|
||||||
sync::{
|
sync::{
|
||||||
|
@ -142,4 +144,34 @@ impl TpuManager {
|
||||||
|
|
||||||
tpu_client.send_transaction(transaction).await
|
tpu_client.send_transaction(transaction).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn send_transaction_batch(
|
||||||
|
&self,
|
||||||
|
batch: &Vec<(Transaction, TransactionSendRecord)>,
|
||||||
|
) -> bool {
|
||||||
|
let tpu_client = self.get_tpu_client().await;
|
||||||
|
|
||||||
|
for (_tx, record) in batch {
|
||||||
|
self.stats.inc_send(&record.keeper_instruction);
|
||||||
|
|
||||||
|
let tx_sent_record = self.tx_send_record.clone();
|
||||||
|
let sent = tx_sent_record.send(record.clone());
|
||||||
|
if sent.is_err() {
|
||||||
|
warn!(
|
||||||
|
"sending error on channel : {}",
|
||||||
|
sent.err().unwrap().to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tpu_client
|
||||||
|
.try_send_wire_transaction_batch(
|
||||||
|
batch
|
||||||
|
.iter()
|
||||||
|
.map(|(tx, _)| serialize(tx).expect("serialization should succeed"))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue