Merge pull request #19 from godmodegalactus/adding_prioritization_fees

adding prioritization fees for market makers
This commit is contained in:
galactus 2023-02-22 17:22:17 +01:00 committed by GitHub
commit 70af47d9fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 133 additions and 36 deletions

10
Cargo.lock generated
View File

@ -2453,6 +2453,15 @@ version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" 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]] [[package]]
name = "itertools" name = "itertools"
version = "0.9.0" version = "0.9.0"
@ -4899,6 +4908,7 @@ dependencies = [
"csv", "csv",
"fixed", "fixed",
"fixed-macro", "fixed-macro",
"iter_tools",
"log", "log",
"mango", "mango",
"mango-common", "mango-common",

View File

@ -23,6 +23,7 @@ serde = "1.0.136"
serde_derive = "1.0.103" serde_derive = "1.0.103"
serde_json = "1.0.79" serde_json = "1.0.79"
serde_yaml = "0.8.23" 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 = { 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" } mango-common = { git = "https://github.com/blockworks-foundation/mango-v3.git", branch = "mango_bencher_compatible" }

View File

@ -1,3 +1,5 @@
use solana_clap_utils::input_validators::is_valid_percentage;
use { use {
clap::{crate_description, crate_name, App, Arg, ArgMatches}, clap::{crate_description, crate_name, App, Arg, ArgMatches},
solana_clap_utils::input_validators::{is_url, is_url_or_moniker}, solana_clap_utils::input_validators::{is_url, is_url_or_moniker},
@ -20,6 +22,7 @@ pub struct Config {
pub block_data_save_file: String, pub block_data_save_file: String,
pub mango_cluster: String, pub mango_cluster: String,
pub txs_batch_size: Option<usize>, pub txs_batch_size: Option<usize>,
pub priority_fees_proba: u8,
} }
impl Default for Config { impl Default for Config {
@ -37,6 +40,7 @@ impl Default for Config {
block_data_save_file: String::new(), block_data_save_file: String::new(),
mango_cluster: "testnet.0".to_string(), mango_cluster: "testnet.0".to_string(),
txs_batch_size: None, txs_batch_size: None,
priority_fees_proba: 0,
} }
} }
} }
@ -168,6 +172,16 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
.required(false) .required(false)
.help("If specified, transactions are send in batches of specified size"), .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)
.validator(is_valid_percentage)
.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` /// Parses a clap `ArgMatches` structure into a `Config`
@ -243,5 +257,10 @@ pub fn extract_args(matches: &ArgMatches) -> Config {
args.txs_batch_size = matches args.txs_batch_size = matches
.value_of("batch-size") .value_of("batch-size")
.map(|batch_size_str| batch_size_str.parse().expect("can't parse 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("Percentage of transactions having prioritization fees"),
None => 0,
};
args args
} }

View File

@ -64,6 +64,7 @@ pub fn process_signature_confirmation_batch(
market_maker: tx_record.market_maker.to_string(), market_maker: tx_record.market_maker.to_string(),
slot_processed: tx_record.sent_slot, slot_processed: tx_record.sent_slot,
timed_out: false, timed_out: false,
priority_fees: tx_record.priority_fees,
}); });
debug!( debug!(
@ -371,6 +372,7 @@ pub fn confirmations_by_blocks(
slot_processed: slot, slot_processed: slot,
slot_leader: slot_leader.clone(), slot_leader: slot_leader.clone(),
timed_out: false, timed_out: false,
priority_fees: transaction_record.priority_fees,
}) })
} }

View File

@ -161,6 +161,7 @@ pub fn write_transaction_data_into_csv(
slot_processed: 0, slot_processed: 0,
successful: false, successful: false,
timed_out: true, timed_out: true,
priority_fees: timeout_record.priority_fees,
}) })
.unwrap(); .unwrap();
} }

View File

@ -13,18 +13,19 @@ use solana_bench_mango::{
states::{BlockData, PerpMarketCache, TransactionConfirmRecord, TransactionSendRecord}, states::{BlockData, PerpMarketCache, TransactionConfirmRecord, TransactionSendRecord},
}; };
use solana_client::{ 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 solana_sdk::commitment_config::CommitmentConfig;
use std::{ use std::{
fs, fs,
net::{IpAddr, Ipv4Addr},
sync::{ sync::{
atomic::{AtomicBool, AtomicU64, Ordering}, atomic::{AtomicBool, AtomicU64, Ordering},
Arc, RwLock, Arc, RwLock,
}, },
thread::{Builder, JoinHandle}, net::{IpAddr, Ipv4Addr}, thread::{Builder, JoinHandle},
}; };
fn main() { fn main() {
@ -46,6 +47,7 @@ fn main() {
block_data_save_file, block_data_save_file,
mango_cluster, mango_cluster,
txs_batch_size, txs_batch_size,
priority_fees_proba,
.. ..
} = &cli_config; } = &cli_config;
@ -89,21 +91,20 @@ fn main() {
None None
}; };
let tpu_client_pool = Arc::new(RotatingQueue::<Arc<TpuClient<QuicPool, QuicConnectionManager, QuicConfig>>>::new( let tpu_client_pool = Arc::new(RotatingQueue::<
number_of_tpu_clients, Arc<TpuClient<QuicPool, QuicConnectionManager, QuicConfig>>,
|| { >::new(number_of_tpu_clients, || {
let quic_connection_cache = quic_connection_cache.clone(); let quic_connection_cache = quic_connection_cache.clone();
Arc::new( Arc::new(
TpuClient::new_with_connection_cache( TpuClient::new_with_connection_cache(
rpc_clients.get().clone(), rpc_clients.get().clone(),
&websocket_url, &websocket_url,
solana_client::tpu_client::TpuClientConfig::default(), solana_client::tpu_client::TpuClientConfig::default(),
quic_connection_cache.unwrap(), quic_connection_cache.unwrap(),
)
.unwrap(),
) )
}, .unwrap(),
)); )
}));
info!( info!(
"accounts:{:?} markets:{:?} quotes_per_second:{:?} expected_tps:{:?} duration:{:?}", "accounts:{:?} markets:{:?} quotes_per_second:{:?} expected_tps:{:?} duration:{:?}",
@ -147,6 +148,7 @@ fn main() {
&duration, &duration,
*quotes_per_second, *quotes_per_second,
*txs_batch_size, *txs_batch_size,
*priority_fees_proba,
); );
let duration = duration.clone(); let duration = duration.clone();
let quotes_per_second = quotes_per_second.clone(); let quotes_per_second = quotes_per_second.clone();

View File

@ -10,17 +10,19 @@ use std::{
use chrono::Utc; use chrono::Utc;
use crossbeam_channel::Sender; use crossbeam_channel::Sender;
use iter_tools::Itertools;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use mango::{ use mango::{
instruction::{cancel_all_perp_orders, place_perp_order2}, instruction::{cancel_all_perp_orders, place_perp_order2},
matching::Side, matching::Side,
}; };
use rand::{distributions::Uniform, prelude::Distribution};
use solana_client::tpu_client::TpuClient; use solana_client::tpu_client::TpuClient;
use solana_program::pubkey::Pubkey; use solana_program::pubkey::Pubkey;
use solana_quic_client::{QuicPool, QuicConnectionManager, QuicConfig}; use solana_quic_client::{QuicConfig, QuicConnectionManager, QuicPool};
use solana_sdk::{ use solana_sdk::{
hash::Hash, instruction::Instruction, message::Message, signature::Keypair, signer::Signer, compute_budget, hash::Hash, instruction::Instruction, message::Message, signature::Keypair,
transaction::Transaction, signer::Signer, transaction::Transaction,
}; };
use crate::{ use crate::{
@ -34,6 +36,7 @@ pub fn create_ask_bid_transaction(
c: &PerpMarketCache, c: &PerpMarketCache,
mango_account_pk: Pubkey, mango_account_pk: Pubkey,
mango_account_signer: &Keypair, mango_account_signer: &Keypair,
prioritization_fee: u64,
) -> Transaction { ) -> Transaction {
let mango_account_signer_pk = to_sp_pk(&mango_account_signer.pubkey()); let mango_account_signer_pk = to_sp_pk(&mango_account_signer.pubkey());
let offset = rand::random::<i8>() as i64; let offset = rand::random::<i8>() as i64;
@ -42,6 +45,14 @@ pub fn create_ask_bid_transaction(
"price:{:?} price_quote_lots:{:?} order_base_lots:{:?} offset:{:?} spread:{:?}", "price:{:?} price_quote_lots:{:?} order_base_lots:{:?} offset:{:?} spread:{:?}",
c.price, c.price_quote_lots, c.order_base_lots, offset, spread c.price, c.price_quote_lots, c.order_base_lots, offset, spread
); );
let mut instructions = vec![];
if prioritization_fee > 0 {
let pfees = compute_budget::ComputeBudgetInstruction::set_compute_unit_price(
prioritization_fee,
);
instructions.push(pfees);
}
let cancel_ix: Instruction = to_sdk_instruction( let cancel_ix: Instruction = to_sdk_instruction(
cancel_all_perp_orders( cancel_all_perp_orders(
&c.mango_program_pk, &c.mango_program_pk,
@ -55,6 +66,7 @@ pub fn create_ask_bid_transaction(
) )
.unwrap(), .unwrap(),
); );
instructions.push(cancel_ix);
let place_bid_ix: Instruction = to_sdk_instruction( let place_bid_ix: Instruction = to_sdk_instruction(
place_perp_order2( place_perp_order2(
@ -82,6 +94,7 @@ pub fn create_ask_bid_transaction(
) )
.unwrap(), .unwrap(),
); );
instructions.push(place_bid_ix);
let place_ask_ix: Instruction = to_sdk_instruction( let place_ask_ix: Instruction = to_sdk_instruction(
place_perp_order2( place_perp_order2(
@ -109,28 +122,58 @@ pub fn create_ask_bid_transaction(
) )
.unwrap(), .unwrap(),
); );
instructions.push(place_ask_ix);
Transaction::new_unsigned(Message::new( Transaction::new_unsigned(Message::new(
&[cancel_ix, place_bid_ix, place_ask_ix], instructions.as_slice(),
Some(&mango_account_signer.pubkey()), Some(&mango_account_signer.pubkey()),
)) ))
} }
fn generate_random_fees(prioritization_fee_proba : u8, n: usize, min_fee: u64, max_fee: u64) -> Vec<u64> {
let mut rng = rand::thread_rng();
let range = Uniform::from(min_fee..max_fee);
let range_probability = Uniform::from(1..100);
(0..n)
.map(|_| {
if prioritization_fee_proba == 0 {
0
} else {
if range_probability.sample(&mut rng) <= prioritization_fee_proba {
range.sample(&mut rng) as u64
} else {
0
}
}
})
.collect()
}
pub fn send_mm_transactions( pub fn send_mm_transactions(
quotes_per_second: u64, quotes_per_second: u64,
perp_market_caches: &Vec<PerpMarketCache>, perp_market_caches: &Vec<PerpMarketCache>,
tx_record_sx: &Sender<TransactionSendRecord>, tx_record_sx: &Sender<TransactionSendRecord>,
tpu_client_pool: Arc<RotatingQueue<Arc<TpuClient<QuicPool, QuicConnectionManager, QuicConfig>>>>, tpu_client_pool: Arc<
RotatingQueue<Arc<TpuClient<QuicPool, QuicConnectionManager, QuicConfig>>>,
>,
mango_account_pk: Pubkey, mango_account_pk: Pubkey,
mango_account_signer: &Keypair, mango_account_signer: &Keypair,
blockhash: Arc<RwLock<Hash>>, blockhash: Arc<RwLock<Hash>>,
slot: &AtomicU64, slot: &AtomicU64,
prioritization_fee_proba: u8,
) { ) {
let mango_account_signer_pk = to_sp_pk(&mango_account_signer.pubkey()); let mango_account_signer_pk = to_sp_pk(&mango_account_signer.pubkey());
// update quotes 2x per second // update quotes 2x per second
for _ in 0..quotes_per_second { for _ in 0..quotes_per_second {
for c in perp_market_caches.iter() { let prioritization_fee_by_market = generate_random_fees(prioritization_fee_proba, perp_market_caches.len(), 100, 1000);
let mut tx = create_ask_bid_transaction(c, mango_account_pk, &mango_account_signer); for (i,c) in perp_market_caches.iter().enumerate() {
let prioritization_fee = prioritization_fee_by_market[i];
let mut tx = create_ask_bid_transaction(
c,
mango_account_pk,
&mango_account_signer,
prioritization_fee,
);
if let Ok(recent_blockhash) = blockhash.read() { if let Ok(recent_blockhash) = blockhash.read() {
tx.sign(&[mango_account_signer], *recent_blockhash); tx.sign(&[mango_account_signer], *recent_blockhash);
@ -143,6 +186,7 @@ pub fn send_mm_transactions(
sent_slot: slot.load(Ordering::Acquire), sent_slot: slot.load(Ordering::Acquire),
market_maker: mango_account_signer_pk, market_maker: mango_account_signer_pk,
market: c.perp_market_pk, market: c.perp_market_pk,
priority_fees: prioritization_fee,
}); });
if sent.is_err() { if sent.is_err() {
println!( println!(
@ -159,11 +203,14 @@ pub fn send_mm_transactions_batched(
quotes_per_second: u64, quotes_per_second: u64,
perp_market_caches: &Vec<PerpMarketCache>, perp_market_caches: &Vec<PerpMarketCache>,
tx_record_sx: &Sender<TransactionSendRecord>, tx_record_sx: &Sender<TransactionSendRecord>,
tpu_client_pool: Arc<RotatingQueue<Arc<TpuClient<QuicPool, QuicConnectionManager, QuicConfig>>>>, tpu_client_pool: Arc<
RotatingQueue<Arc<TpuClient<QuicPool, QuicConnectionManager, QuicConfig>>>,
>,
mango_account_pk: Pubkey, mango_account_pk: Pubkey,
mango_account_signer: &Keypair, mango_account_signer: &Keypair,
blockhash: Arc<RwLock<Hash>>, blockhash: Arc<RwLock<Hash>>,
slot: &AtomicU64, slot: &AtomicU64,
prioritization_fee_proba: u8,
) { ) {
let mut transactions = Vec::<_>::with_capacity(txs_batch_size); let mut transactions = Vec::<_>::with_capacity(txs_batch_size);
@ -171,22 +218,29 @@ pub fn send_mm_transactions_batched(
// update quotes 2x per second // update quotes 2x per second
for _ in 0..quotes_per_second { for _ in 0..quotes_per_second {
for c in perp_market_caches.iter() { for c in perp_market_caches.iter() {
for _ in 0..txs_batch_size { let prioritization_fee_for_tx = generate_random_fees(prioritization_fee_proba, txs_batch_size, 100, 1000);
transactions.push(create_ask_bid_transaction( for i in 0..txs_batch_size {
c, let prioritization_fee = prioritization_fee_for_tx[i];
mango_account_pk, transactions.push(
&mango_account_signer, (create_ask_bid_transaction(
)); c,
mango_account_pk,
&mango_account_signer,
prioritization_fee,
),prioritization_fee)
);
} }
if let Ok(recent_blockhash) = blockhash.read() { if let Ok(recent_blockhash) = blockhash.read() {
for tx in &mut transactions { 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(); let tpu_client = tpu_client_pool.get();
if tpu_client 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() .is_err()
{ {
error!("Sending batch failed"); error!("Sending batch failed");
@ -195,11 +249,12 @@ pub fn send_mm_transactions_batched(
for tx in &transactions { for tx in &transactions {
let sent = tx_record_sx.send(TransactionSendRecord { let sent = tx_record_sx.send(TransactionSendRecord {
signature: tx.signatures[0], signature: tx.0.signatures[0],
sent_at: Utc::now(), sent_at: Utc::now(),
sent_slot: slot.load(Ordering::Acquire), sent_slot: slot.load(Ordering::Acquire),
market_maker: mango_account_signer_pk, market_maker: mango_account_signer_pk,
market: c.perp_market_pk, market: c.perp_market_pk,
priority_fees: tx.1 as u64,
}); });
if sent.is_err() { if sent.is_err() {
error!( error!(
@ -220,10 +275,13 @@ pub fn start_market_making_threads(
exit_signal: Arc<AtomicBool>, exit_signal: Arc<AtomicBool>,
blockhash: Arc<RwLock<Hash>>, blockhash: Arc<RwLock<Hash>>,
current_slot: Arc<AtomicU64>, current_slot: Arc<AtomicU64>,
tpu_client_pool: Arc<RotatingQueue<Arc<TpuClient<QuicPool, QuicConnectionManager, QuicConfig>>>>, tpu_client_pool: Arc<
RotatingQueue<Arc<TpuClient<QuicPool, QuicConnectionManager, QuicConfig>>>,
>,
duration: &Duration, duration: &Duration,
quotes_per_second: u64, quotes_per_second: u64,
txs_batch_size: Option<usize>, txs_batch_size: Option<usize>,
prioritization_fee_proba: u8,
) -> Vec<JoinHandle<()>> { ) -> Vec<JoinHandle<()>> {
account_keys_parsed account_keys_parsed
.iter() .iter()
@ -247,7 +305,6 @@ pub fn start_market_making_threads(
mango_account_pk mango_account_pk
); );
//sleep(Duration::from_secs(10)); //sleep(Duration::from_secs(10));
let tx_record_sx = tx_record_sx.clone(); let tx_record_sx = tx_record_sx.clone();
Builder::new() Builder::new()
@ -255,6 +312,7 @@ pub fn start_market_making_threads(
.spawn(move || { .spawn(move || {
for _i in 0..duration.as_secs() { for _i in 0..duration.as_secs() {
let start = Instant::now(); let start = Instant::now();
// send market maker transactions // send market maker transactions
if let Some(txs_batch_size) = txs_batch_size.clone() { if let Some(txs_batch_size) = txs_batch_size.clone() {
send_mm_transactions_batched( send_mm_transactions_batched(
@ -267,6 +325,7 @@ pub fn start_market_making_threads(
&mango_account_signer, &mango_account_signer,
blockhash.clone(), blockhash.clone(),
current_slot.as_ref(), current_slot.as_ref(),
prioritization_fee_proba,
); );
} else { } else {
send_mm_transactions( send_mm_transactions(
@ -278,6 +337,7 @@ pub fn start_market_making_threads(
&mango_account_signer, &mango_account_signer,
blockhash.clone(), blockhash.clone(),
current_slot.as_ref(), current_slot.as_ref(),
prioritization_fee_proba,
); );
} }

View File

@ -12,6 +12,7 @@ pub struct TransactionSendRecord {
pub sent_slot: Slot, pub sent_slot: Slot,
pub market_maker: Pubkey, pub market_maker: Pubkey,
pub market: Pubkey, pub market: Pubkey,
pub priority_fees: u64,
} }
#[derive(Clone, Serialize)] #[derive(Clone, Serialize)]
@ -29,6 +30,7 @@ pub struct TransactionConfirmRecord {
pub block_hash: String, pub block_hash: String,
pub slot_processed: Slot, pub slot_processed: Slot,
pub timed_out: bool, pub timed_out: bool,
pub priority_fees: u64,
} }
#[derive(Clone)] #[derive(Clone)]