From 71f0e5cc13f15a38c5da336a48fdb7846363c581 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Tue, 5 Dec 2023 13:23:11 +0100 Subject: [PATCH] client: configurable jupiter urls (#800) --- Cargo.lock | 75 ++++++++++++++++++++++++++-- bin/liquidator/src/main.rs | 27 +++++++--- bin/liquidator/src/trigger_tcs.rs | 8 ++- bin/settler/src/settle.rs | 2 +- lib/client/Cargo.toml | 1 + lib/client/src/client.rs | 83 ++++++++++++++++++++++++------- lib/client/src/jupiter/v4.rs | 4 +- lib/client/src/jupiter/v6.rs | 7 ++- lib/client/src/lib.rs | 3 ++ 9 files changed, 169 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 27b94eaca..aa168354c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1508,14 +1508,38 @@ dependencies = [ "zeroize", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + [[package]] name = "darling" version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.3", + "darling_macro 0.20.3", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.67", + "quote 1.0.33", + "strsim 0.10.0", + "syn 1.0.109", ] [[package]] @@ -1532,13 +1556,24 @@ dependencies = [ "syn 2.0.37", ] +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core 0.14.4", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "darling_macro" version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ - "darling_core", + "darling_core 0.20.3", "quote 1.0.33", "syn 2.0.37", ] @@ -1617,6 +1652,37 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_builder" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +dependencies = [ + "darling 0.14.4", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" +dependencies = [ + "derive_builder_core", + "syn 1.0.109", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -3379,6 +3445,7 @@ dependencies = [ "atty", "base64 0.13.1", "bincode", + "derive_builder", "fixed 1.11.0 (git+https://github.com/blockworks-foundation/fixed.git?branch=v1.11.0-borsh0_10-mango)", "futures 0.3.28", "itertools", @@ -5779,7 +5846,7 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ - "darling", + "darling 0.20.3", "proc-macro2 1.0.67", "quote 1.0.33", "syn 2.0.37", diff --git a/bin/liquidator/src/main.rs b/bin/liquidator/src/main.rs index 58cba09fd..cda897543 100644 --- a/bin/liquidator/src/main.rs +++ b/bin/liquidator/src/main.rs @@ -158,6 +158,14 @@ struct Cli { #[clap(long, env, value_enum, default_value = "v6")] jupiter_version: JupiterVersionArg, + /// override the url to jupiter v4 + #[clap(long, env, default_value = "https://quote-api.jup.ag/v4")] + jupiter_v4_url: String, + + /// override the url to jupiter v6 + #[clap(long, env, default_value = "https://quote-api.jup.ag/v6")] + jupiter_v6_url: String, + /// report liquidator's existence and pubkey #[clap(long, env, value_enum, default_value = "true")] telemetry: BoolArg, @@ -188,18 +196,21 @@ async fn main() -> anyhow::Result<()> { let rpc_timeout = Duration::from_secs(10); let cluster = Cluster::Custom(rpc_url.clone(), ws_url.clone()); let commitment = CommitmentConfig::processed(); - let client = Client::new( - cluster.clone(), - commitment, - liqor_owner.clone(), - Some(rpc_timeout), - TransactionBuilderConfig { + let client = Client::builder() + .cluster(cluster.clone()) + .commitment(commitment) + .fee_payer(Some(liqor_owner.clone())) + .timeout(Some(rpc_timeout)) + .jupiter_v4_url(cli.jupiter_v4_url) + .jupiter_v6_url(cli.jupiter_v6_url) + .transaction_builder_config(TransactionBuilderConfig { prioritization_micro_lamports: (cli.prioritization_micro_lamports > 0) .then_some(cli.prioritization_micro_lamports), // Liquidation and tcs triggers set their own budgets, this is a default for other tx compute_budget_per_instruction: Some(250_000), - }, - ); + }) + .build() + .unwrap(); // The representation of current on-chain account data let chain_data = Arc::new(RwLock::new(chain_data::ChainData::new())); diff --git a/bin/liquidator/src/trigger_tcs.rs b/bin/liquidator/src/trigger_tcs.rs index 56fa5bceb..f49bd5122 100644 --- a/bin/liquidator/src/trigger_tcs.rs +++ b/bin/liquidator/src/trigger_tcs.rs @@ -1105,14 +1105,12 @@ impl Context { solana_sdk::compute_budget::ComputeBudgetInstruction::set_compute_unit_limit( self.config.compute_limit_for_trigger, ); + let fee_payer = self.mango_client.client.fee_payer(); TransactionBuilder { instructions: vec![compute_ix], address_lookup_tables: vec![], - payer: self.mango_client.client.fee_payer.pubkey(), - signers: vec![ - self.mango_client.owner.clone(), - self.mango_client.client.fee_payer.clone(), - ], + payer: fee_payer.pubkey(), + signers: vec![self.mango_client.owner.clone(), fee_payer], config: self.mango_client.client.transaction_builder_config, } }; diff --git a/bin/settler/src/settle.rs b/bin/settler/src/settle.rs index 4bfb1ac72..af938006a 100644 --- a/bin/settler/src/settle.rs +++ b/bin/settler/src/settle.rs @@ -267,7 +267,7 @@ struct SettleBatchProcessor<'a> { impl<'a> SettleBatchProcessor<'a> { fn transaction(&self) -> anyhow::Result { let client = &self.mango_client.client; - let fee_payer = client.fee_payer.clone(); + let fee_payer = client.fee_payer(); TransactionBuilder { instructions: self.instructions.clone().to_instructions(), diff --git a/lib/client/Cargo.toml b/lib/client/Cargo.toml index f9abc9fe0..64bc97b1b 100644 --- a/lib/client/Cargo.toml +++ b/lib/client/Cargo.toml @@ -15,6 +15,7 @@ async-channel = "1.6" async-once-cell = { version = "0.4.2", features = ["unpin"] } async-trait = "0.1.52" atty = "0.2" +derive_builder = "0.12.0" fixed = { workspace = true, features = ["serde", "borsh"] } futures = "0.3.25" itertools = "0.10.3" diff --git a/lib/client/src/client.rs b/lib/client/src/client.rs index df081b1c9..9b157a6d5 100644 --- a/lib/client/src/client.rs +++ b/lib/client/src/client.rs @@ -49,17 +49,55 @@ use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey, signer::Si pub const MAX_ACCOUNTS_PER_TRANSACTION: usize = 64; // very close to anchor_client::Client, which unfortunately has no accessors or Clone -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Builder)] pub struct Client { + /// RPC url + /// + /// Defaults to Cluster::Mainnet, using the public crowded mainnet-beta rpc endpoint. + /// Should usually be overridden with a custom rpc endpoint. + #[builder(default = "Cluster::Mainnet")] pub cluster: Cluster, - pub fee_payer: Arc, + + /// Transaction fee payer. Needs to be set to send transactions. + pub fee_payer: Option>, + + /// Commitment for interacting with the chain. Defaults to processed. + #[builder(default = "CommitmentConfig::processed()")] pub commitment: CommitmentConfig, + + /// Timeout, defaults to 60s + #[builder(default = "Some(Duration::from_secs(60))")] pub timeout: Option, + + #[builder(default)] pub transaction_builder_config: TransactionBuilderConfig, + + /// Defaults to a preflight check at processed commitment + #[builder(default = "ClientBuilder::default_rpc_send_transaction_config()")] pub rpc_send_transaction_config: RpcSendTransactionConfig, + + #[builder(default = "\"https://quote-api.jup.ag/v4\".into()")] + pub jupiter_v4_url: String, + + #[builder(default = "\"https://quote-api.jup.ag/v6\".into()")] + pub jupiter_v6_url: String, +} + +impl ClientBuilder { + pub fn default_rpc_send_transaction_config() -> RpcSendTransactionConfig { + RpcSendTransactionConfig { + preflight_commitment: Some(CommitmentLevel::Processed), + ..Default::default() + } + } } impl Client { + pub fn builder() -> ClientBuilder { + ClientBuilder::default() + } + + /// Prefer using the builder() pub fn new( cluster: Cluster, commitment: CommitmentConfig, @@ -67,17 +105,14 @@ impl Client { timeout: Option, transaction_builder_config: TransactionBuilderConfig, ) -> Self { - Self { - cluster, - fee_payer, - commitment, - timeout, - transaction_builder_config, - rpc_send_transaction_config: RpcSendTransactionConfig { - preflight_commitment: Some(CommitmentLevel::Processed), - ..Default::default() - }, - } + Self::builder() + .cluster(cluster) + .commitment(commitment) + .fee_payer(Some(fee_payer)) + .timeout(timeout) + .transaction_builder_config(transaction_builder_config) + .build() + .unwrap() } pub fn rpc_async(&self) -> RpcClientAsync { @@ -96,6 +131,13 @@ impl Client { ) -> anyhow::Result { fetch_anchor_account(&self.rpc_async(), address).await } + + pub fn fee_payer(&self) -> Arc { + self.fee_payer + .as_ref() + .expect("fee payer must be set") + .clone() + } } // todo: might want to integrate geyser, websockets, or simple http polling for keeping data fresh @@ -1617,11 +1659,12 @@ impl MangoClient { &self, instructions: Vec, ) -> anyhow::Result { + let fee_payer = self.client.fee_payer(); TransactionBuilder { instructions, address_lookup_tables: self.mango_address_lookup_tables().await?, - payer: self.client.fee_payer.pubkey(), - signers: vec![self.owner.clone(), self.client.fee_payer.clone()], + payer: fee_payer.pubkey(), + signers: vec![self.owner.clone(), fee_payer], config: self.client.transaction_builder_config, } .send_and_confirm(&self.client) @@ -1632,11 +1675,12 @@ impl MangoClient { &self, instructions: Vec, ) -> anyhow::Result { + let fee_payer = self.client.fee_payer(); TransactionBuilder { instructions, address_lookup_tables: self.mango_address_lookup_tables().await?, - payer: self.client.fee_payer.pubkey(), - signers: vec![self.client.fee_payer.clone()], + payer: fee_payer.pubkey(), + signers: vec![fee_payer], config: self.client.transaction_builder_config, } .send_and_confirm(&self.client) @@ -1647,11 +1691,12 @@ impl MangoClient { &self, instructions: Vec, ) -> anyhow::Result { + let fee_payer = self.client.fee_payer(); TransactionBuilder { instructions, address_lookup_tables: vec![], - payer: self.client.fee_payer.pubkey(), - signers: vec![self.client.fee_payer.clone()], + payer: fee_payer.pubkey(), + signers: vec![fee_payer], config: self.client.transaction_builder_config, } .simulate(&self.client) diff --git a/lib/client/src/jupiter/v4.rs b/lib/client/src/jupiter/v4.rs index 5c68818bf..29cf2be06 100644 --- a/lib/client/src/jupiter/v4.rs +++ b/lib/client/src/jupiter/v4.rs @@ -107,7 +107,7 @@ impl<'a> JupiterV4<'a> { let response = self .mango_client .http_client - .get("https://quote-api.jup.ag/v4/quote") + .get(format!("{}/quote", self.mango_client.client.jupiter_v4_url)) .query(&[ ("inputMint", input_mint.to_string()), ("outputMint", output_mint.to_string()), @@ -158,7 +158,7 @@ impl<'a> JupiterV4<'a> { let swap_response = self .mango_client .http_client - .post("https://quote-api.jup.ag/v4/swap") + .post(format!("{}/swap", self.mango_client.client.jupiter_v4_url)) .json(&SwapRequest { route: route.clone(), user_public_key: self.mango_client.owner.pubkey().to_string(), diff --git a/lib/client/src/jupiter/v6.rs b/lib/client/src/jupiter/v6.rs index 2d8b93b3c..f3d554085 100644 --- a/lib/client/src/jupiter/v6.rs +++ b/lib/client/src/jupiter/v6.rs @@ -183,7 +183,7 @@ impl<'a> JupiterV6<'a> { let response = self .mango_client .http_client - .get("https://quote-api.jup.ag/v6/quote") + .get(format!("{}/quote", self.mango_client.client.jupiter_v6_url)) .query(&[ ("inputMint", input_mint.to_string()), ("outputMint", output_mint.to_string()), @@ -263,7 +263,10 @@ impl<'a> JupiterV6<'a> { let swap_response = self .mango_client .http_client - .post("https://quote-api.jup.ag/v6/swap-instructions") + .post(format!( + "{}/swap-instructions", + self.mango_client.client.jupiter_v6_url + )) .json(&SwapRequest { user_public_key: owner.to_string(), wrap_and_unwrap_sol: false, diff --git a/lib/client/src/lib.rs b/lib/client/src/lib.rs index 31f5ae936..559f6f5b7 100644 --- a/lib/client/src/lib.rs +++ b/lib/client/src/lib.rs @@ -17,3 +17,6 @@ pub mod perp_pnl; pub mod snapshot_source; mod util; pub mod websocket_source; + +#[macro_use] +extern crate derive_builder;