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"
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",

View File

@ -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" }

View File

@ -1,3 +1,5 @@
use solana_clap_utils::input_validators::is_valid_percentage;
use {
clap::{crate_description, crate_name, App, Arg, ArgMatches},
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 mango_cluster: String,
pub txs_batch_size: Option<usize>,
pub priority_fees_proba: u8,
}
impl Default for Config {
@ -37,6 +40,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 +172,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)
.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`
@ -243,5 +257,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("Percentage of transactions having prioritization fees"),
None => 0,
};
args
}

View File

@ -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,
})
}

View File

@ -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();
}

View File

@ -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::<Arc<TpuClient<QuicPool, QuicConnectionManager, QuicConfig>>>::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<TpuClient<QuicPool, QuicConnectionManager, QuicConfig>>,
>::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();

View File

@ -10,17 +10,19 @@ 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},
matching::Side,
};
use rand::{distributions::Uniform, prelude::Distribution};
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 +36,7 @@ pub fn create_ask_bid_transaction(
c: &PerpMarketCache,
mango_account_pk: Pubkey,
mango_account_signer: &Keypair,
prioritization_fee: u64,
) -> Transaction {
let mango_account_signer_pk = to_sp_pk(&mango_account_signer.pubkey());
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:{:?}",
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(
cancel_all_perp_orders(
&c.mango_program_pk,
@ -55,6 +66,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 +94,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,28 +122,58 @@ 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()),
))
}
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(
quotes_per_second: u64,
perp_market_caches: &Vec<PerpMarketCache>,
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_signer: &Keypair,
blockhash: Arc<RwLock<Hash>>,
slot: &AtomicU64,
prioritization_fee_proba: 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_by_market = generate_random_fees(prioritization_fee_proba, perp_market_caches.len(), 100, 1000);
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() {
tx.sign(&[mango_account_signer], *recent_blockhash);
@ -143,6 +186,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,
});
if sent.is_err() {
println!(
@ -159,11 +203,14 @@ pub fn send_mm_transactions_batched(
quotes_per_second: u64,
perp_market_caches: &Vec<PerpMarketCache>,
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_signer: &Keypair,
blockhash: Arc<RwLock<Hash>>,
slot: &AtomicU64,
prioritization_fee_proba: u8,
) {
let mut transactions = Vec::<_>::with_capacity(txs_batch_size);
@ -171,22 +218,29 @@ pub fn send_mm_transactions_batched(
// update quotes 2x per second
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_for_tx = generate_random_fees(prioritization_fee_proba, txs_batch_size, 100, 1000);
for i in 0..txs_batch_size {
let prioritization_fee = prioritization_fee_for_tx[i];
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 +249,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 +275,13 @@ pub fn start_market_making_threads(
exit_signal: Arc<AtomicBool>,
blockhash: Arc<RwLock<Hash>>,
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,
quotes_per_second: u64,
txs_batch_size: Option<usize>,
prioritization_fee_proba: u8,
) -> Vec<JoinHandle<()>> {
account_keys_parsed
.iter()
@ -247,7 +305,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 +312,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 +325,7 @@ pub fn start_market_making_threads(
&mango_account_signer,
blockhash.clone(),
current_slot.as_ref(),
prioritization_fee_proba,
);
} else {
send_mm_transactions(
@ -278,6 +337,7 @@ pub fn start_market_making_threads(
&mango_account_signer,
blockhash.clone(),
current_slot.as_ref(),
prioritization_fee_proba,
);
}

View File

@ -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)]