1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
use crate::common::GasFeeStrategyType;
use crate::swqos::{SwqosConfig, SwqosType};
use solana_commitment_config::CommitmentConfig;
use std::hash::{Hash, Hasher};
/// Infrastructure-only configuration (wallet-independent)
/// Can be shared across multiple wallets using the same RPC/SWQOS setup
#[derive(Debug, Clone)]
pub struct InfrastructureConfig {
pub rpc_url: String,
pub swqos_configs: Vec<SwqosConfig>,
pub commitment: CommitmentConfig,
/// When true, SWQOS sender threads use the *last* N cores instead of the first N. Reduces contention with main thread / default tokio workers that often use low-numbered cores. Default false.
pub swqos_cores_from_end: bool,
/// Global MEV protection flag. When true, SWQOS providers that support MEV protection
/// (Astralane, BlockRazor, Glaive) use MEV-protected endpoints/modes. Glaive HTTP adds
/// `mev-protect=true`; Glaive QUIC sets auth-frame flag bit 0. Default false.
pub mev_protection: bool,
}
impl InfrastructureConfig {
pub fn new(
rpc_url: String,
swqos_configs: Vec<SwqosConfig>,
commitment: CommitmentConfig,
) -> Self {
Self {
rpc_url,
swqos_configs,
commitment,
swqos_cores_from_end: false,
mev_protection: false,
}
}
/// Create from TradeConfig (extract infrastructure-only settings)
pub fn from_trade_config(config: &TradeConfig) -> Self {
Self {
rpc_url: config.rpc_url.clone(),
swqos_configs: config.swqos_configs.clone(),
commitment: config.commitment.clone(),
swqos_cores_from_end: config.swqos_cores_from_end,
mev_protection: config.mev_protection,
}
}
/// Generate a cache key for this infrastructure configuration
pub fn cache_key(&self) -> String {
use std::collections::hash_map::DefaultHasher;
let mut hasher = DefaultHasher::new();
self.hash(&mut hasher);
format!("{:x}", hasher.finish())
}
}
// Manual Hash implementation since CommitmentConfig doesn't implement Hash
impl Hash for InfrastructureConfig {
fn hash<H: Hasher>(&self, state: &mut H) {
self.rpc_url.hash(state);
self.swqos_configs.hash(state);
format!("{:?}", self.commitment).hash(state);
self.swqos_cores_from_end.hash(state);
self.mev_protection.hash(state);
}
}
impl PartialEq for InfrastructureConfig {
fn eq(&self, other: &Self) -> bool {
self.rpc_url == other.rpc_url
&& self.swqos_configs == other.swqos_configs
&& self.commitment == other.commitment
&& self.swqos_cores_from_end == other.swqos_cores_from_end
&& self.mev_protection == other.mev_protection
}
}
impl Eq for InfrastructureConfig {}
/// Solana transaction message version used for newly built trades.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum TradeTransactionVersion {
/// Use the V0-compatible mode: Legacy without ALTs, V0 when ALTs are supplied.
#[default]
V0,
/// Build a V1 message with inline transaction configuration. V1 does not support ALTs.
V1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SwqosSubmitTiming {
pub swqos_type: SwqosType,
pub strategy_type: GasFeeStrategyType,
pub submit_done_us: i64,
}
/// Pump.fun instruction version, independent of the Solana transaction format.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum PumpFunVersion {
Legacy,
V2,
#[default]
V3,
}
#[derive(Debug, Clone)]
pub struct TradeConfig {
/// Default Pump.fun version for unversioned requests and curve route legs.
pub pumpfun_version: PumpFunVersion,
/// Instance-scoped bridge registry and subscription snapshot handle.
pub pumpswap_routing: Option<std::sync::Arc<crate::client::PumpSwapRouting>>,
pub rpc_url: String,
pub swqos_configs: Vec<SwqosConfig>,
pub commitment: CommitmentConfig,
/// Transaction construction mode. V0 (default) preserves Legacy messages when no ALT is used.
pub transaction_version: TradeTransactionVersion,
/// Whether to create WSOL ATA on startup (default: true)
/// If true, SDK will check WSOL ATA on initialization and create if not exists
pub create_wsol_ata_on_startup: bool,
/// Whether to use seed optimization for all ATA operations (default: true)
pub use_seed_optimize: bool,
/// Whether to output all SDK logs (timing, SWQOS submit/confirm, WSOL, blacklist, etc.). Default false to keep synchronous output off the hot path.
pub log_enabled: bool,
/// Whether to check minimum tip per SWQOS provider (filter out configs below min). Default false to save latency.
pub check_min_tip: bool,
/// When true, SWQOS uses the *last* N cores (instead of the first N). Use when main thread / tokio use low-numbered cores to reduce CPU contention. Default false.
pub swqos_cores_from_end: bool,
/// Global MEV protection flag. When true, SWQOS providers that support MEV protection
/// (Astralane, BlockRazor, Glaive) use their MEV-protected endpoints/modes. Glaive HTTP
/// adds `mev-protect=true`; Glaive QUIC sets auth-frame flag bit 0. Default false.
pub mev_protection: bool,
}
impl TradeConfig {
/// Create a new TradeConfig using the builder pattern.
///
/// # Available builder methods
/// - `.create_wsol_ata_on_startup(bool)` — check & create WSOL ATA on init (default: true)
/// - `.use_seed_optimize(bool)` — seed optimization for ATA ops (default: true)
/// - `.log_enabled(bool)` — SDK timing/SWQOS logs (default: false)
/// - `.check_min_tip(bool)` — filter SWQOS below min tip (default: false)
/// - `.swqos_cores_from_end(bool)` — bind SWQOS to last N cores (default: false)
/// - `.mev_protection(bool)` — MEV protection for Astralane/BlockRazor/Glaive (default: false)
/// - `.transaction_version(version)` — use V0-compatible mode (default) or V1
/// - `.pumpfun_version(version)` — Pump.fun instructions: V3 (default), V2 or Legacy
///
/// # Example
/// ```rust,ignore
/// let config = TradeConfig::builder(rpc_url, swqos_configs, commitment)
/// .transaction_version(TradeTransactionVersion::V1)
/// .mev_protection(true)
/// .check_min_tip(true)
/// .log_enabled(false)
/// .build();
/// ```
pub fn builder(
rpc_url: String,
swqos_configs: Vec<SwqosConfig>,
commitment: CommitmentConfig,
) -> TradeConfigBuilder {
TradeConfigBuilder::new(rpc_url, swqos_configs, commitment)
}
/// Shortcut: create a TradeConfig with all defaults. Equivalent to `builder(...).build()`.
pub fn new(
rpc_url: String,
swqos_configs: Vec<SwqosConfig>,
commitment: CommitmentConfig,
) -> Self {
Self::builder(rpc_url, swqos_configs, commitment).build()
}
}
/// Builder for [`TradeConfig`]. Created via [`TradeConfig::builder`].
///
/// All fields are optional and pre-filled with sensible defaults.
/// Call `.build()` to produce the final [`TradeConfig`].
#[derive(Debug, Clone)]
pub struct TradeConfigBuilder {
pumpfun_version: PumpFunVersion,
pumpswap_routing: Option<std::sync::Arc<crate::client::PumpSwapRouting>>,
rpc_url: String,
swqos_configs: Vec<SwqosConfig>,
commitment: CommitmentConfig,
transaction_version: TradeTransactionVersion,
create_wsol_ata_on_startup: bool,
use_seed_optimize: bool,
log_enabled: bool,
check_min_tip: bool,
swqos_cores_from_end: bool,
mev_protection: bool,
}
impl TradeConfigBuilder {
fn new(rpc_url: String, swqos_configs: Vec<SwqosConfig>, commitment: CommitmentConfig) -> Self {
Self {
pumpfun_version: PumpFunVersion::default(),
pumpswap_routing: None,
rpc_url,
swqos_configs,
commitment,
transaction_version: TradeTransactionVersion::default(),
create_wsol_ata_on_startup: true,
use_seed_optimize: true,
log_enabled: false,
check_min_tip: false,
swqos_cores_from_end: false,
mev_protection: false,
}
}
/// Check and create WSOL ATA on SDK initialization. Default: `true`.
pub fn create_wsol_ata_on_startup(mut self, v: bool) -> Self {
self.create_wsol_ata_on_startup = v;
self
}
/// Enable seed optimization for all ATA operations. Default: `true`.
pub fn use_seed_optimize(mut self, v: bool) -> Self {
self.use_seed_optimize = v;
self
}
/// Select the Solana transaction construction mode. Default: [`TradeTransactionVersion::V0`].
pub fn transaction_version(mut self, version: TradeTransactionVersion) -> Self {
self.transaction_version = version;
self
}
/// Select the default Pump.fun instruction version. Default: V3.
pub fn pumpfun_version(mut self, version: PumpFunVersion) -> Self {
self.pumpfun_version = version;
self
}
/// Enable synchronous SDK diagnostics explicitly. Default: `false` for low-latency trading.
pub fn log_enabled(mut self, v: bool) -> Self {
self.log_enabled = v;
self
}
/// Filter out SWQOS providers whose tip is below their minimum requirement.
/// Adds a small check on the hot path; disable for lowest latency. Default: `false`.
pub fn check_min_tip(mut self, v: bool) -> Self {
self.check_min_tip = v;
self
}
/// Bind SWQOS sender threads to the *last* N CPU cores instead of the first N.
/// Useful when main thread / tokio workers occupy low-numbered cores. Default: `false`.
pub fn swqos_cores_from_end(mut self, v: bool) -> Self {
self.swqos_cores_from_end = v;
self
}
/// Enable global MEV protection. When `true`:
/// - **Astralane QUIC** uses port `9000`; **Astralane HTTP** adds `mev-protect=true`
/// - **BlockRazor** uses `mode=sandwichMitigation` (skips blacklisted Leader slots)
/// - **Glaive HTTP** adds `mev-protect=true`; **Glaive QUIC** sets auth-frame flag bit 0
///
/// May reduce landing speed. Default: `false`.
pub fn mev_protection(mut self, v: bool) -> Self {
self.mev_protection = v;
self
}
/// Configure reusable PumpSwap bridge pools; publish live snapshots off the hot path.
pub fn pumpswap_routing(
mut self,
routing: std::sync::Arc<crate::client::PumpSwapRouting>,
) -> Self {
self.pumpswap_routing = Some(routing);
self
}
/// Consume the builder and produce a [`TradeConfig`].
pub fn build(self) -> TradeConfig {
TradeConfig {
pumpswap_routing: self.pumpswap_routing,
rpc_url: self.rpc_url,
swqos_configs: self.swqos_configs,
commitment: self.commitment,
transaction_version: self.transaction_version,
pumpfun_version: self.pumpfun_version,
create_wsol_ata_on_startup: self.create_wsol_ata_on_startup,
use_seed_optimize: self.use_seed_optimize,
log_enabled: self.log_enabled,
check_min_tip: self.check_min_tip,
swqos_cores_from_end: self.swqos_cores_from_end,
mev_protection: self.mev_protection,
}
}
}
pub type SolanaRpcClient = solana_client::nonblocking::rpc_client::RpcClient;
pub type AnyResult<T> = anyhow::Result<T>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn trade_config_defaults_to_v0() {
let config = TradeConfig::new(
"http://localhost:8899".to_owned(),
Vec::new(),
CommitmentConfig::processed(),
);
assert_eq!(config.transaction_version, TradeTransactionVersion::V0);
assert_eq!(config.pumpfun_version, PumpFunVersion::V3);
assert!(!config.log_enabled);
}
#[test]
fn trade_config_builder_selects_v1() {
let config = TradeConfig::builder(
"http://localhost:8899".to_owned(),
Vec::new(),
CommitmentConfig::processed(),
)
.transaction_version(TradeTransactionVersion::V1)
.pumpfun_version(PumpFunVersion::V2)
.log_enabled(true)
.build();
assert_eq!(config.transaction_version, TradeTransactionVersion::V1);
assert_eq!(config.pumpfun_version, PumpFunVersion::V2);
assert!(config.log_enabled);
}
}