From 37cfc81c83c8bf50f1ed5518a6c609e2fdb47aaf Mon Sep 17 00:00:00 2001 From: Godmode Galactus Date: Tue, 21 Feb 2023 11:04:02 +0100 Subject: [PATCH] adding prioritization fees for market makers --- Cargo.lock | 10 ++++ Cargo.toml | 1 + src/cli.rs | 17 +++++++ src/confirmation_strategies.rs | 2 + src/helpers.rs | 1 + src/main.rs | 36 +++++++------- src/market_markers.rs | 88 +++++++++++++++++++++++++++------- src/states.rs | 2 + 8 files changed, 124 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2122467..8baf4a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2453,6 +2453,15 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +[[package]] +name = "iter_tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531cafdc99b3b3252bb32f5620e61d56b19415efc19900b12d1b2e7483854897" +dependencies = [ + "itertools 0.10.5", +] + [[package]] name = "itertools" version = "0.9.0" @@ -4899,6 +4908,7 @@ dependencies = [ "csv", "fixed", "fixed-macro", + "iter_tools", "log", "mango", "mango-common", diff --git a/Cargo.toml b/Cargo.toml index 6101b85..589fd5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ serde = "1.0.136" serde_derive = "1.0.103" serde_json = "1.0.79" serde_yaml = "0.8.23" +iter_tools = "0.1.4" mango = { git = "https://github.com/blockworks-foundation/mango-v3.git", branch = "mango_bencher_compatible", default-features = false, features = ["no-entrypoint"] } mango-common = { git = "https://github.com/blockworks-foundation/mango-v3.git", branch = "mango_bencher_compatible" } diff --git a/src/cli.rs b/src/cli.rs index ae29f66..8906097 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -20,6 +20,7 @@ pub struct Config { pub block_data_save_file: String, pub mango_cluster: String, pub txs_batch_size: Option, + pub priority_fees_proba: u8, } impl Default for Config { @@ -37,6 +38,7 @@ impl Default for Config { block_data_save_file: String::new(), mango_cluster: "testnet.0".to_string(), txs_batch_size: None, + priority_fees_proba: 0, } } } @@ -168,6 +170,16 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> { .required(false) .help("If specified, transactions are send in batches of specified size"), ) + .arg( + Arg::with_name("prioritization-fees") + .long("prioritization-fees") + .value_name("UINT") + .min_values(1) + .max_values(100) + .takes_value(true) + .required(false) + .help("Takes percentage of transaction we want to add random prioritization fees to, prioritization fees are random number between 100-1000"), + ) } /// Parses a clap `ArgMatches` structure into a `Config` @@ -243,5 +255,10 @@ pub fn extract_args(matches: &ArgMatches) -> Config { args.txs_batch_size = matches .value_of("batch-size") .map(|batch_size_str| batch_size_str.parse().expect("can't parse batch-size")); + + args.priority_fees_proba = match matches.value_of("prioritization-fees") { + Some(x) => x.parse().expect("Prioritization fees is between 1-100"), + None => 0, + }; args } diff --git a/src/confirmation_strategies.rs b/src/confirmation_strategies.rs index 8aabbb8..ac35a68 100644 --- a/src/confirmation_strategies.rs +++ b/src/confirmation_strategies.rs @@ -64,6 +64,7 @@ pub fn process_signature_confirmation_batch( market_maker: tx_record.market_maker.to_string(), slot_processed: tx_record.sent_slot, timed_out: false, + priority_fees: tx_record.priority_fees, }); debug!( @@ -371,6 +372,7 @@ pub fn confirmations_by_blocks( slot_processed: slot, slot_leader: slot_leader.clone(), timed_out: false, + priority_fees: transaction_record.priority_fees, }) } diff --git a/src/helpers.rs b/src/helpers.rs index 54c26f6..5163764 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -161,6 +161,7 @@ pub fn write_transaction_data_into_csv( slot_processed: 0, successful: false, timed_out: true, + priority_fees: timeout_record.priority_fees, }) .unwrap(); } diff --git a/src/main.rs b/src/main.rs index b83cbff..a6c22e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,18 +13,19 @@ use solana_bench_mango::{ states::{BlockData, PerpMarketCache, TransactionConfirmRecord, TransactionSendRecord}, }; use solana_client::{ - rpc_client::RpcClient, tpu_client::TpuClient, connection_cache::ConnectionCache, + connection_cache::ConnectionCache, rpc_client::RpcClient, tpu_client::TpuClient, }; -use solana_quic_client::{QuicPool, QuicConnectionManager, QuicConfig}; +use solana_quic_client::{QuicConfig, QuicConnectionManager, QuicPool}; use solana_sdk::commitment_config::CommitmentConfig; use std::{ fs, + net::{IpAddr, Ipv4Addr}, sync::{ atomic::{AtomicBool, AtomicU64, Ordering}, Arc, RwLock, }, - thread::{Builder, JoinHandle}, net::{IpAddr, Ipv4Addr}, + thread::{Builder, JoinHandle}, }; fn main() { @@ -46,6 +47,7 @@ fn main() { block_data_save_file, mango_cluster, txs_batch_size, + priority_fees_proba, .. } = &cli_config; @@ -89,21 +91,20 @@ fn main() { None }; - let tpu_client_pool = Arc::new(RotatingQueue::>>::new( - number_of_tpu_clients, - || { - let quic_connection_cache = quic_connection_cache.clone(); - Arc::new( - TpuClient::new_with_connection_cache( - rpc_clients.get().clone(), - &websocket_url, - solana_client::tpu_client::TpuClientConfig::default(), - quic_connection_cache.unwrap(), - ) - .unwrap(), + let tpu_client_pool = Arc::new(RotatingQueue::< + Arc>, + >::new(number_of_tpu_clients, || { + let quic_connection_cache = quic_connection_cache.clone(); + Arc::new( + TpuClient::new_with_connection_cache( + rpc_clients.get().clone(), + &websocket_url, + solana_client::tpu_client::TpuClientConfig::default(), + quic_connection_cache.unwrap(), ) - }, - )); + .unwrap(), + ) + })); info!( "accounts:{:?} markets:{:?} quotes_per_second:{:?} expected_tps:{:?} duration:{:?}", @@ -147,6 +148,7 @@ fn main() { &duration, *quotes_per_second, *txs_batch_size, + *priority_fees_proba, ); let duration = duration.clone(); let quotes_per_second = quotes_per_second.clone(); diff --git a/src/market_markers.rs b/src/market_markers.rs index e07f45a..c3cae29 100644 --- a/src/market_markers.rs +++ b/src/market_markers.rs @@ -10,6 +10,7 @@ use std::{ use chrono::Utc; use crossbeam_channel::Sender; +use iter_tools::Itertools; use log::{debug, error, info, warn}; use mango::{ instruction::{cancel_all_perp_orders, place_perp_order2}, @@ -17,10 +18,10 @@ use mango::{ }; use solana_client::tpu_client::TpuClient; use solana_program::pubkey::Pubkey; -use solana_quic_client::{QuicPool, QuicConnectionManager, QuicConfig}; +use solana_quic_client::{QuicConfig, QuicConnectionManager, QuicPool}; use solana_sdk::{ - hash::Hash, instruction::Instruction, message::Message, signature::Keypair, signer::Signer, - transaction::Transaction, + compute_budget, hash::Hash, instruction::Instruction, message::Message, signature::Keypair, + signer::Signer, transaction::Transaction, }; use crate::{ @@ -34,6 +35,7 @@ pub fn create_ask_bid_transaction( c: &PerpMarketCache, mango_account_pk: Pubkey, mango_account_signer: &Keypair, + prioritization_fees: u32, ) -> Transaction { let mango_account_signer_pk = to_sp_pk(&mango_account_signer.pubkey()); let offset = rand::random::() as i64; @@ -42,6 +44,14 @@ pub fn create_ask_bid_transaction( "price:{:?} price_quote_lots:{:?} order_base_lots:{:?} offset:{:?} spread:{:?}", c.price, c.price_quote_lots, c.order_base_lots, offset, spread ); + let mut instructions = vec![]; + if prioritization_fees > 0 { + let pfees = compute_budget::ComputeBudgetInstruction::set_compute_unit_price( + prioritization_fees as u64, + ); + instructions.push(pfees); + } + let cancel_ix: Instruction = to_sdk_instruction( cancel_all_perp_orders( &c.mango_program_pk, @@ -55,6 +65,7 @@ pub fn create_ask_bid_transaction( ) .unwrap(), ); + instructions.push(cancel_ix); let place_bid_ix: Instruction = to_sdk_instruction( place_perp_order2( @@ -82,6 +93,7 @@ pub fn create_ask_bid_transaction( ) .unwrap(), ); + instructions.push(place_bid_ix); let place_ask_ix: Instruction = to_sdk_instruction( place_perp_order2( @@ -109,9 +121,10 @@ pub fn create_ask_bid_transaction( ) .unwrap(), ); + instructions.push(place_ask_ix); Transaction::new_unsigned(Message::new( - &[cancel_ix, place_bid_ix, place_ask_ix], + instructions.as_slice(), Some(&mango_account_signer.pubkey()), )) } @@ -120,17 +133,34 @@ pub fn send_mm_transactions( quotes_per_second: u64, perp_market_caches: &Vec, tx_record_sx: &Sender, - tpu_client_pool: Arc>>>, + tpu_client_pool: Arc< + RotatingQueue>>, + >, mango_account_pk: Pubkey, mango_account_signer: &Keypair, blockhash: Arc>, slot: &AtomicU64, + prioritization_fees: u8, ) { let mango_account_signer_pk = to_sp_pk(&mango_account_signer.pubkey()); // update quotes 2x per second for _ in 0..quotes_per_second { for c in perp_market_caches.iter() { - let mut tx = create_ask_bid_transaction(c, mango_account_pk, &mango_account_signer); + let prioritization_fee = if prioritization_fees != 0 { + if rand::random::() % 99 + 1 <= prioritization_fees { + rand::random::() % 900 + 100 + } else { + 0 + } + } else { + 0 + }; + let mut tx = create_ask_bid_transaction( + c, + mango_account_pk, + &mango_account_signer, + prioritization_fee, + ); if let Ok(recent_blockhash) = blockhash.read() { tx.sign(&[mango_account_signer], *recent_blockhash); @@ -143,6 +173,7 @@ pub fn send_mm_transactions( sent_slot: slot.load(Ordering::Acquire), market_maker: mango_account_signer_pk, market: c.perp_market_pk, + priority_fees: prioritization_fee as u64, }); if sent.is_err() { println!( @@ -159,11 +190,14 @@ pub fn send_mm_transactions_batched( quotes_per_second: u64, perp_market_caches: &Vec, tx_record_sx: &Sender, - tpu_client_pool: Arc>>>, + tpu_client_pool: Arc< + RotatingQueue>>, + >, mango_account_pk: Pubkey, mango_account_signer: &Keypair, blockhash: Arc>, slot: &AtomicU64, + prioritization_fees: u8, ) { let mut transactions = Vec::<_>::with_capacity(txs_batch_size); @@ -172,21 +206,37 @@ pub fn send_mm_transactions_batched( for _ in 0..quotes_per_second { for c in perp_market_caches.iter() { for _ in 0..txs_batch_size { - transactions.push(create_ask_bid_transaction( - c, - mango_account_pk, - &mango_account_signer, + let prioritization_fee = if prioritization_fees != 0 { + if rand::random::() % 99 + 1 <= prioritization_fees { + rand::random::() % 900 + 100 + } else { + 0 + } + } else { + 0 + }; + + transactions.push(( + create_ask_bid_transaction( + c, + mango_account_pk, + &mango_account_signer, + prioritization_fee, + ), + prioritization_fee, )); } if let Ok(recent_blockhash) = blockhash.read() { for tx in &mut transactions { - tx.sign(&[mango_account_signer], *recent_blockhash); + tx.0.sign(&[mango_account_signer], *recent_blockhash); } } let tpu_client = tpu_client_pool.get(); if tpu_client - .try_send_transaction_batch(&transactions) + .try_send_transaction_batch( + &transactions.iter().map(|x| x.0.clone()).collect_vec().as_slice(), + ) .is_err() { error!("Sending batch failed"); @@ -195,11 +245,12 @@ pub fn send_mm_transactions_batched( for tx in &transactions { let sent = tx_record_sx.send(TransactionSendRecord { - signature: tx.signatures[0], + signature: tx.0.signatures[0], sent_at: Utc::now(), sent_slot: slot.load(Ordering::Acquire), market_maker: mango_account_signer_pk, market: c.perp_market_pk, + priority_fees: tx.1 as u64, }); if sent.is_err() { error!( @@ -220,10 +271,13 @@ pub fn start_market_making_threads( exit_signal: Arc, blockhash: Arc>, current_slot: Arc, - tpu_client_pool: Arc>>>, + tpu_client_pool: Arc< + RotatingQueue>>, + >, duration: &Duration, quotes_per_second: u64, txs_batch_size: Option, + prioritization_fees: u8, ) -> Vec> { account_keys_parsed .iter() @@ -247,7 +301,6 @@ pub fn start_market_making_threads( mango_account_pk ); //sleep(Duration::from_secs(10)); - let tx_record_sx = tx_record_sx.clone(); Builder::new() @@ -255,6 +308,7 @@ pub fn start_market_making_threads( .spawn(move || { for _i in 0..duration.as_secs() { let start = Instant::now(); + // send market maker transactions if let Some(txs_batch_size) = txs_batch_size.clone() { send_mm_transactions_batched( @@ -267,6 +321,7 @@ pub fn start_market_making_threads( &mango_account_signer, blockhash.clone(), current_slot.as_ref(), + prioritization_fees, ); } else { send_mm_transactions( @@ -278,6 +333,7 @@ pub fn start_market_making_threads( &mango_account_signer, blockhash.clone(), current_slot.as_ref(), + prioritization_fees, ); } diff --git a/src/states.rs b/src/states.rs index cbe0912..10478a8 100644 --- a/src/states.rs +++ b/src/states.rs @@ -12,6 +12,7 @@ pub struct TransactionSendRecord { pub sent_slot: Slot, pub market_maker: Pubkey, pub market: Pubkey, + pub priority_fees: u64, } #[derive(Clone, Serialize)] @@ -29,6 +30,7 @@ pub struct TransactionConfirmRecord { pub block_hash: String, pub slot_processed: Slot, pub timed_out: bool, + pub priority_fees: u64, } #[derive(Clone)]