Update SWQOS submit behavior
This commit is contained in:
+80
-39
@@ -187,8 +187,8 @@ pub struct SimpleBuyParams {
|
||||
/// Recent blockhash for non-nonce transactions.
|
||||
///
|
||||
/// The SDK intentionally does not fetch blockhash on the hot path. Use
|
||||
/// [`SimpleBuyParams::with_durable_nonce`] instead when submitting multiple
|
||||
/// SWQoS lanes against a pinned nonce.
|
||||
/// [`SimpleBuyParams::with_durable_nonce`] only when the caller specifically
|
||||
/// wants durable-nonce transactions.
|
||||
pub recent_blockhash: Option<Hash>,
|
||||
/// Protocol-specific parameters, for example `DexParamEnum::PumpFun(...)`.
|
||||
pub extension_params: DexParamEnum,
|
||||
@@ -196,12 +196,15 @@ pub struct SimpleBuyParams {
|
||||
pub gas_fee_strategy: GasFeeStrategy,
|
||||
/// ATA creation/close behavior. See [`AccountPolicy`].
|
||||
pub account_policy: AccountPolicy,
|
||||
/// Optional Address Lookup Table to reduce transaction size.
|
||||
pub address_lookup_table_account: Option<AddressLookupTableAccount>,
|
||||
/// Optional Address Lookup Tables to reduce transaction size.
|
||||
/// Pass one element for a single ALT or multiple elements for multi-ALT.
|
||||
pub address_lookup_table_accounts: Vec<AddressLookupTableAccount>,
|
||||
/// Wait until the transaction is confirmed before returning.
|
||||
pub wait_tx_confirmed: bool,
|
||||
/// Fast-submit mode only: wait for every SWQoS route's submit response so all
|
||||
/// signatures can be returned.
|
||||
/// Wait for every SWQoS route's submit response so all signatures can be
|
||||
/// returned. Useful when confirming through poll-any semantics or monitoring
|
||||
/// route variants externally. Recent-blockhash variants are not mutually
|
||||
/// exclusive; durable nonce variants are.
|
||||
pub wait_for_all_submits: bool,
|
||||
/// Durable nonce info. Mutually exclusive with `recent_blockhash`.
|
||||
pub durable_nonce: Option<DurableNonceInfo>,
|
||||
@@ -235,12 +238,15 @@ pub struct SimpleSellParams {
|
||||
pub gas_fee_strategy: GasFeeStrategy,
|
||||
/// ATA creation/close behavior. See [`AccountPolicy`].
|
||||
pub account_policy: AccountPolicy,
|
||||
/// Optional Address Lookup Table to reduce transaction size.
|
||||
pub address_lookup_table_account: Option<AddressLookupTableAccount>,
|
||||
/// Optional Address Lookup Tables to reduce transaction size.
|
||||
/// Pass one element for a single ALT or multiple elements for multi-ALT.
|
||||
pub address_lookup_table_accounts: Vec<AddressLookupTableAccount>,
|
||||
/// Wait until the transaction is confirmed before returning.
|
||||
pub wait_tx_confirmed: bool,
|
||||
/// Fast-submit mode only: wait for every SWQoS route's submit response so all
|
||||
/// signatures can be returned.
|
||||
/// Wait for every SWQoS route's submit response so all signatures can be
|
||||
/// returned. Useful when confirming through poll-any semantics or monitoring
|
||||
/// route variants externally. Recent-blockhash variants are not mutually
|
||||
/// exclusive; durable nonce variants are.
|
||||
pub wait_for_all_submits: bool,
|
||||
/// Durable nonce info. Mutually exclusive with `recent_blockhash`.
|
||||
pub durable_nonce: Option<DurableNonceInfo>,
|
||||
@@ -281,7 +287,7 @@ impl SimpleBuyParams {
|
||||
extension_params,
|
||||
gas_fee_strategy,
|
||||
account_policy: AccountPolicy::Auto,
|
||||
address_lookup_table_account: None,
|
||||
address_lookup_table_accounts: Vec::new(),
|
||||
wait_tx_confirmed: false,
|
||||
wait_for_all_submits: false,
|
||||
durable_nonce: None,
|
||||
@@ -330,9 +336,10 @@ impl SimpleBuyParams {
|
||||
self
|
||||
}
|
||||
|
||||
/// Attach an Address Lookup Table to reduce transaction size.
|
||||
pub fn address_lookup_table_account(mut self, value: AddressLookupTableAccount) -> Self {
|
||||
self.address_lookup_table_account = Some(value);
|
||||
/// Attach Address Lookup Tables to reduce transaction size.
|
||||
/// Pass one element for a single ALT or multiple elements for multi-ALT.
|
||||
pub fn address_lookup_table_accounts(mut self, values: Vec<AddressLookupTableAccount>) -> Self {
|
||||
self.address_lookup_table_accounts = values;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -352,7 +359,7 @@ impl SimpleBuyParams {
|
||||
self
|
||||
}
|
||||
|
||||
/// In fast-submit mode, wait for all SWQoS submit responses and return every signature.
|
||||
/// Wait for all SWQoS submit responses and return submitted signatures.
|
||||
pub fn wait_for_all_submits(mut self, value: bool) -> Self {
|
||||
self.wait_for_all_submits = value;
|
||||
self
|
||||
@@ -401,7 +408,7 @@ impl SimpleSellParams {
|
||||
extension_params,
|
||||
gas_fee_strategy,
|
||||
account_policy: AccountPolicy::Auto,
|
||||
address_lookup_table_account: None,
|
||||
address_lookup_table_accounts: Vec::new(),
|
||||
wait_tx_confirmed: false,
|
||||
wait_for_all_submits: false,
|
||||
durable_nonce: None,
|
||||
@@ -449,9 +456,10 @@ impl SimpleSellParams {
|
||||
self
|
||||
}
|
||||
|
||||
/// Attach an Address Lookup Table to reduce transaction size.
|
||||
pub fn address_lookup_table_account(mut self, value: AddressLookupTableAccount) -> Self {
|
||||
self.address_lookup_table_account = Some(value);
|
||||
/// Attach Address Lookup Tables to reduce transaction size.
|
||||
/// Pass one element for a single ALT or multiple elements for multi-ALT.
|
||||
pub fn address_lookup_table_accounts(mut self, values: Vec<AddressLookupTableAccount>) -> Self {
|
||||
self.address_lookup_table_accounts = values;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -471,7 +479,7 @@ impl SimpleSellParams {
|
||||
self
|
||||
}
|
||||
|
||||
/// In fast-submit mode, wait for all SWQoS submit responses and return every signature.
|
||||
/// Wait for all SWQoS submit responses and return submitted signatures.
|
||||
pub fn wait_for_all_submits(mut self, value: bool) -> Self {
|
||||
self.wait_for_all_submits = value;
|
||||
self
|
||||
@@ -782,14 +790,16 @@ pub struct TradeBuyParams {
|
||||
/// Protocol-specific parameters (PumpFun, Raydium, etc.)
|
||||
pub extension_params: DexParamEnum,
|
||||
// Extended configuration
|
||||
/// Optional address lookup table for transaction size optimization
|
||||
pub address_lookup_table_account: Option<AddressLookupTableAccount>,
|
||||
/// Optional address lookup tables for transaction size optimization.
|
||||
/// Pass one element for a single ALT or multiple elements for multi-ALT.
|
||||
pub address_lookup_table_accounts: Vec<AddressLookupTableAccount>,
|
||||
/// Whether to wait for transaction confirmation before returning
|
||||
pub wait_tx_confirmed: bool,
|
||||
/// Fast-submit only (`wait_tx_confirmed = false`): when true, wait for every
|
||||
/// SWQOS route's HTTP submit response so all submitted signatures are
|
||||
/// returned. Set to true when confirming externally against a pinned
|
||||
/// durable nonce; defaults to false. See `SwapParams.wait_for_all_submits`.
|
||||
/// When true, wait for every SWQOS route's HTTP submit response so all
|
||||
/// submitted signatures are returned. This applies whether SDK confirmation
|
||||
/// is enabled or the caller monitors externally. Recent-blockhash route
|
||||
/// variants are not mutually exclusive; durable nonce variants are.
|
||||
/// Defaults to false. See `SwapParams.wait_for_all_submits`.
|
||||
pub wait_for_all_submits: bool,
|
||||
/// Whether to create input token associated token account
|
||||
pub create_input_token_ata: bool,
|
||||
@@ -839,14 +849,16 @@ pub struct TradeSellParams {
|
||||
/// Protocol-specific parameters (PumpFun, Raydium, etc.)
|
||||
pub extension_params: DexParamEnum,
|
||||
// Extended configuration
|
||||
/// Optional address lookup table for transaction size optimization
|
||||
pub address_lookup_table_account: Option<AddressLookupTableAccount>,
|
||||
/// Optional address lookup tables for transaction size optimization.
|
||||
/// Pass one element for a single ALT or multiple elements for multi-ALT.
|
||||
pub address_lookup_table_accounts: Vec<AddressLookupTableAccount>,
|
||||
/// Whether to wait for transaction confirmation before returning
|
||||
pub wait_tx_confirmed: bool,
|
||||
/// Fast-submit only (`wait_tx_confirmed = false`): when true, wait for every
|
||||
/// SWQOS route's HTTP submit response so all submitted signatures are
|
||||
/// returned. Set to true when confirming externally against a pinned
|
||||
/// durable nonce; defaults to false. See `SwapParams.wait_for_all_submits`.
|
||||
/// When true, wait for every SWQOS route's HTTP submit response so all
|
||||
/// submitted signatures are returned. This applies whether SDK confirmation
|
||||
/// is enabled or the caller monitors externally. Recent-blockhash route
|
||||
/// variants are not mutually exclusive; durable nonce variants are.
|
||||
/// Defaults to false. See `SwapParams.wait_for_all_submits`.
|
||||
pub wait_for_all_submits: bool,
|
||||
/// Whether to create output token associated token account
|
||||
pub create_output_token_ata: bool,
|
||||
@@ -906,7 +918,7 @@ impl From<SimpleBuyParams> for TradeBuyParams {
|
||||
slippage_basis_points: params.slippage_basis_points,
|
||||
recent_blockhash: params.recent_blockhash,
|
||||
extension_params: params.extension_params,
|
||||
address_lookup_table_account: params.address_lookup_table_account,
|
||||
address_lookup_table_accounts: params.address_lookup_table_accounts,
|
||||
wait_tx_confirmed: params.wait_tx_confirmed,
|
||||
wait_for_all_submits: params.wait_for_all_submits,
|
||||
create_input_token_ata,
|
||||
@@ -942,7 +954,7 @@ impl From<SimpleSellParams> for TradeSellParams {
|
||||
recent_blockhash: params.recent_blockhash,
|
||||
with_tip: params.with_tip,
|
||||
extension_params: params.extension_params,
|
||||
address_lookup_table_account: params.address_lookup_table_account,
|
||||
address_lookup_table_accounts: params.address_lookup_table_accounts,
|
||||
wait_tx_confirmed: params.wait_tx_confirmed,
|
||||
wait_for_all_submits: params.wait_for_all_submits,
|
||||
create_output_token_ata,
|
||||
@@ -1381,7 +1393,7 @@ impl TradingClient {
|
||||
output_token_program: None,
|
||||
input_amount: Some(params.input_token_amount),
|
||||
slippage_basis_points: params.slippage_basis_points,
|
||||
address_lookup_table_account: params.address_lookup_table_account,
|
||||
address_lookup_table_accounts: params.address_lookup_table_accounts,
|
||||
recent_blockhash: params.recent_blockhash,
|
||||
wait_tx_confirmed: params.wait_tx_confirmed,
|
||||
protocol_params,
|
||||
@@ -1509,7 +1521,7 @@ impl TradingClient {
|
||||
output_token_program: None,
|
||||
input_amount: Some(params.input_token_amount),
|
||||
slippage_basis_points: params.slippage_basis_points,
|
||||
address_lookup_table_account: params.address_lookup_table_account,
|
||||
address_lookup_table_accounts: params.address_lookup_table_accounts,
|
||||
recent_blockhash: params.recent_blockhash,
|
||||
wait_tx_confirmed: params.wait_tx_confirmed,
|
||||
protocol_params,
|
||||
@@ -1860,7 +1872,7 @@ mod tests {
|
||||
extension_params: dummy_pumpfun_params(),
|
||||
gas_fee_strategy: GasFeeStrategy::new(),
|
||||
account_policy: AccountPolicy::HotPathMinimal,
|
||||
address_lookup_table_account: None,
|
||||
address_lookup_table_accounts: Vec::new(),
|
||||
wait_tx_confirmed: false,
|
||||
wait_for_all_submits: false,
|
||||
durable_nonce: None,
|
||||
@@ -1891,7 +1903,7 @@ mod tests {
|
||||
extension_params: dummy_pumpfun_params(),
|
||||
gas_fee_strategy: GasFeeStrategy::new(),
|
||||
account_policy: AccountPolicy::Auto,
|
||||
address_lookup_table_account: None,
|
||||
address_lookup_table_accounts: Vec::new(),
|
||||
wait_tx_confirmed: false,
|
||||
wait_for_all_submits: false,
|
||||
durable_nonce: None,
|
||||
@@ -1931,6 +1943,35 @@ mod tests {
|
||||
assert!(!low.wait_tx_confirmed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_buy_builder_maps_multiple_lookup_tables() {
|
||||
let alt1 = AddressLookupTableAccount {
|
||||
key: Pubkey::new_unique(),
|
||||
addresses: vec![Pubkey::new_unique()],
|
||||
};
|
||||
let alt2 = AddressLookupTableAccount {
|
||||
key: Pubkey::new_unique(),
|
||||
addresses: vec![Pubkey::new_unique()],
|
||||
};
|
||||
|
||||
let simple = SimpleBuyParams::new(
|
||||
DexType::PumpFun,
|
||||
TradeTokenType::SOL,
|
||||
Pubkey::new_unique(),
|
||||
BuyAmount::ExactInput(10_000),
|
||||
dummy_pumpfun_params(),
|
||||
Hash::new_unique(),
|
||||
GasFeeStrategy::new(),
|
||||
)
|
||||
.address_lookup_table_accounts(vec![alt1.clone(), alt2.clone()]);
|
||||
|
||||
let low: TradeBuyParams = simple.into();
|
||||
|
||||
assert_eq!(low.address_lookup_table_accounts.len(), 2);
|
||||
assert_eq!(low.address_lookup_table_accounts[0].key, alt1.key);
|
||||
assert_eq!(low.address_lookup_table_accounts[1].key, alt2.key);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_buy_builder_can_use_durable_nonce() {
|
||||
let nonce_account = Pubkey::new_unique();
|
||||
@@ -1970,7 +2011,7 @@ mod tests {
|
||||
extension_params: dummy_pumpfun_params(),
|
||||
gas_fee_strategy: GasFeeStrategy::new(),
|
||||
account_policy: AccountPolicy::Auto,
|
||||
address_lookup_table_account: None,
|
||||
address_lookup_table_accounts: Vec::new(),
|
||||
wait_tx_confirmed: false,
|
||||
wait_for_all_submits: false,
|
||||
durable_nonce: None,
|
||||
|
||||
@@ -365,7 +365,7 @@ mod tests {
|
||||
output_token_program: None,
|
||||
input_amount: Some(100_000),
|
||||
slippage_basis_points: Some(100),
|
||||
address_lookup_table_account: None,
|
||||
address_lookup_table_accounts: Vec::new(),
|
||||
recent_blockhash: None,
|
||||
wait_tx_confirmed: false,
|
||||
protocol_params: DexParamEnum::Bonk(bonk_params()),
|
||||
|
||||
@@ -347,7 +347,7 @@ mod tests {
|
||||
output_token_program: None,
|
||||
input_amount: Some(100_000),
|
||||
slippage_basis_points: Some(100),
|
||||
address_lookup_table_account: None,
|
||||
address_lookup_table_accounts: Vec::new(),
|
||||
recent_blockhash: None,
|
||||
wait_tx_confirmed: false,
|
||||
protocol_params: DexParamEnum::MeteoraDammV2(protocol_params),
|
||||
|
||||
@@ -944,7 +944,7 @@ mod tests {
|
||||
output_token_program: None,
|
||||
input_amount: Some(10_000_000),
|
||||
slippage_basis_points: Some(300),
|
||||
address_lookup_table_account: None,
|
||||
address_lookup_table_accounts: Vec::new(),
|
||||
recent_blockhash: None,
|
||||
wait_tx_confirmed: false,
|
||||
protocol_params: DexParamEnum::PumpFun(params),
|
||||
@@ -1125,7 +1125,7 @@ mod tests {
|
||||
150_000,
|
||||
500_000,
|
||||
&business_instructions,
|
||||
None,
|
||||
&[],
|
||||
Some(solana_hash::Hash::new_unique()),
|
||||
None,
|
||||
"PumpFun",
|
||||
@@ -1160,7 +1160,7 @@ mod tests {
|
||||
150_000,
|
||||
500_000,
|
||||
&business_instructions,
|
||||
None,
|
||||
&[],
|
||||
Some(solana_hash::Hash::new_unique()),
|
||||
None,
|
||||
"PumpFun",
|
||||
|
||||
@@ -572,7 +572,7 @@ mod tests {
|
||||
output_token_program: None,
|
||||
input_amount: Some(100_000),
|
||||
slippage_basis_points: Some(100),
|
||||
address_lookup_table_account: None,
|
||||
address_lookup_table_accounts: Vec::new(),
|
||||
recent_blockhash: None,
|
||||
wait_tx_confirmed: false,
|
||||
protocol_params: DexParamEnum::PumpSwap(pumpswap_params()),
|
||||
|
||||
@@ -365,7 +365,7 @@ mod tests {
|
||||
output_token_program: None,
|
||||
input_amount: Some(100_000),
|
||||
slippage_basis_points: Some(100),
|
||||
address_lookup_table_account: None,
|
||||
address_lookup_table_accounts: Vec::new(),
|
||||
recent_blockhash: None,
|
||||
wait_tx_confirmed: false,
|
||||
protocol_params: DexParamEnum::RaydiumAmmV4(protocol_params),
|
||||
|
||||
@@ -377,7 +377,7 @@ mod tests {
|
||||
output_token_program: None,
|
||||
input_amount: Some(100_000),
|
||||
slippage_basis_points: Some(100),
|
||||
address_lookup_table_account: None,
|
||||
address_lookup_table_accounts: Vec::new(),
|
||||
recent_blockhash: None,
|
||||
wait_tx_confirmed: false,
|
||||
protocol_params: DexParamEnum::RaydiumCpmm(cpmm_params()),
|
||||
|
||||
@@ -36,7 +36,7 @@ pub fn build_transaction(
|
||||
unit_limit: u32,
|
||||
unit_price: u64,
|
||||
business_instructions: &[Instruction],
|
||||
address_lookup_table_account: Option<&AddressLookupTableAccount>,
|
||||
address_lookup_table_accounts: &[AddressLookupTableAccount],
|
||||
recent_blockhash: Option<Hash>,
|
||||
middleware_manager: Option<&Arc<MiddlewareManager>>,
|
||||
protocol_name: &str,
|
||||
@@ -51,7 +51,7 @@ pub fn build_transaction(
|
||||
unit_limit,
|
||||
unit_price,
|
||||
business_instructions,
|
||||
address_lookup_table_account,
|
||||
address_lookup_table_accounts,
|
||||
recent_blockhash,
|
||||
middleware_manager,
|
||||
protocol_name,
|
||||
@@ -74,7 +74,7 @@ pub fn build_transaction(
|
||||
with_tip && tip_amount > 0.0,
|
||||
unit_limit,
|
||||
unit_price,
|
||||
address_lookup_table_account.is_some()
|
||||
address_lookup_table_accounts.len()
|
||||
);
|
||||
}
|
||||
if serialized_len <= PACKET_DATA_SIZE {
|
||||
@@ -93,7 +93,7 @@ fn build_transaction_inner(
|
||||
unit_limit: u32,
|
||||
unit_price: u64,
|
||||
business_instructions: &[Instruction],
|
||||
address_lookup_table_account: Option<&AddressLookupTableAccount>,
|
||||
address_lookup_table_accounts: &[AddressLookupTableAccount],
|
||||
recent_blockhash: Option<Hash>,
|
||||
middleware_manager: Option<&Arc<MiddlewareManager>>,
|
||||
protocol_name: &str,
|
||||
@@ -127,7 +127,7 @@ fn build_transaction_inner(
|
||||
build_versioned_transaction(
|
||||
payer,
|
||||
instructions,
|
||||
address_lookup_table_account,
|
||||
address_lookup_table_accounts,
|
||||
blockhash,
|
||||
middleware_manager,
|
||||
protocol_name,
|
||||
@@ -138,7 +138,7 @@ fn build_transaction_inner(
|
||||
fn build_versioned_transaction(
|
||||
payer: &Arc<Keypair>,
|
||||
instructions: Vec<Instruction>,
|
||||
address_lookup_table_account: Option<&AddressLookupTableAccount>,
|
||||
address_lookup_table_accounts: &[AddressLookupTableAccount],
|
||||
blockhash: Hash,
|
||||
middleware_manager: Option<&Arc<MiddlewareManager>>,
|
||||
protocol_name: &str,
|
||||
@@ -156,7 +156,7 @@ fn build_versioned_transaction(
|
||||
let build_result = builder.build_zero_alloc(
|
||||
&payer.pubkey(),
|
||||
&full_instructions,
|
||||
address_lookup_table_account,
|
||||
address_lookup_table_accounts,
|
||||
blockhash,
|
||||
);
|
||||
release_builder(builder);
|
||||
@@ -190,7 +190,7 @@ mod tests {
|
||||
80_000,
|
||||
100_000,
|
||||
&business_instructions,
|
||||
None,
|
||||
&[],
|
||||
Some(Hash::new_unique()),
|
||||
None,
|
||||
"test",
|
||||
|
||||
@@ -47,12 +47,13 @@ const SWQOS_POOL_WORKERS: usize = 18;
|
||||
const SWQOS_QUEUE_CAP: usize = 128;
|
||||
const SWQOS_DEDICATED_DEFAULT_THREADS: usize = 18;
|
||||
const FAST_SUBMIT_RESULT_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
const FAST_SUBMIT_DRAIN_GRACE: Duration = Duration::from_millis(20);
|
||||
|
||||
/// Shared across all jobs in one batch; built once, cloned as single Arc per job (minimal hot-path clone).
|
||||
struct SwqosSharedContext {
|
||||
payer: Arc<Keypair>,
|
||||
instructions: Arc<Vec<Instruction>>,
|
||||
address_lookup_table_account: Option<AddressLookupTableAccount>,
|
||||
address_lookup_table_accounts: Arc<Vec<AddressLookupTableAccount>>,
|
||||
recent_blockhash: Option<Hash>,
|
||||
durable_nonce: Option<DurableNonceInfo>,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
@@ -92,7 +93,7 @@ async fn run_one_swqos_job(job: SwqosJob) {
|
||||
job.unit_limit,
|
||||
job.unit_price,
|
||||
s.instructions.as_ref(),
|
||||
s.address_lookup_table_account.as_ref(),
|
||||
s.address_lookup_table_accounts.as_slice(),
|
||||
s.recent_blockhash,
|
||||
s.middleware_manager.as_ref(),
|
||||
s.protocol_name,
|
||||
@@ -508,10 +509,11 @@ impl ResultCollector {
|
||||
}
|
||||
}
|
||||
|
||||
/// 等待全部任务完成(不等待链上确认),然后收集并返回所有签名。用于「多路提交」时返回多笔签名。
|
||||
/// 等待全部任务完成(不等待链上确认),然后收集并返回所有已返回的签名。
|
||||
/// 轮询间隔 2ms,避免 50ms 间隔在最后一笔返回时多等几十 ms 拉高 submit 耗时。
|
||||
/// Re-enabled via `SwapParams.wait_for_all_submits` for callers that confirm
|
||||
/// externally against a pinned durable nonce and need every submitted sig.
|
||||
/// Re-enabled via `SwapParams.wait_for_all_submits` for callers that need
|
||||
/// every submitted signature, either for external monitoring or for
|
||||
/// executor-level poll-any confirmation after parallel submit.
|
||||
async fn wait_for_all_submitted(
|
||||
&self,
|
||||
timeout_secs: u64,
|
||||
@@ -525,21 +527,10 @@ impl ResultCollector {
|
||||
}
|
||||
tokio::time::sleep(poll_interval).await;
|
||||
}
|
||||
// 「不等待链上确认」仍会等各 SWQOS 的 HTTP 回包;主循环在收齐或触达 `timeout_secs` 后结束。
|
||||
// 若主窗口到时仍有未回包通道,晚到的 TaskResult 若立刻 drain 会丢签名——仅在该路径上拉长 grace。
|
||||
let all_submitted = self.completed_count.load(Ordering::Acquire) >= self.total_tasks;
|
||||
if all_submitted {
|
||||
tokio::task::yield_now().await;
|
||||
} else {
|
||||
tokio::time::sleep(Duration::from_millis(600)).await;
|
||||
while self.completed_count.load(Ordering::Acquire) < self.total_tasks {
|
||||
if start.elapsed() > primary + Duration::from_secs(6) {
|
||||
break;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(20)).await;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(120)).await;
|
||||
}
|
||||
// Bound the opt-in "all submits" path tightly. A slow relay must not
|
||||
// delay poll-any confirmation by multiple seconds after the submit
|
||||
// window; give only a short grace for a just-finished worker to publish.
|
||||
tokio::time::sleep(FAST_SUBMIT_DRAIN_GRACE).await;
|
||||
self.get_first()
|
||||
}
|
||||
}
|
||||
@@ -608,7 +599,7 @@ pub async fn execute_parallel(
|
||||
swqos_clients: &[Arc<SwqosClient>],
|
||||
payer: Arc<Keypair>,
|
||||
instructions: Vec<Instruction>,
|
||||
address_lookup_table_account: Option<AddressLookupTableAccount>,
|
||||
address_lookup_table_accounts: Vec<AddressLookupTableAccount>,
|
||||
recent_blockhash: Option<Hash>,
|
||||
durable_nonce: Option<DurableNonceInfo>,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
@@ -660,17 +651,13 @@ pub async fn execute_parallel(
|
||||
return Err(anyhow!("No available gas fee strategy configs"));
|
||||
}
|
||||
|
||||
if is_buy && selected_task_configs.len() > 1 && durable_nonce.is_none() {
|
||||
return Err(anyhow!("Multiple swqos transactions require durable_nonce to be set.",));
|
||||
}
|
||||
|
||||
// Task preparation completed: one shared context (clone once per batch), then minimal per-task data.
|
||||
let channel_count = selected_task_configs.len().max(1);
|
||||
let collector = Arc::new(ResultCollector::new(channel_count));
|
||||
let shared = Arc::new(SwqosSharedContext {
|
||||
payer,
|
||||
instructions,
|
||||
address_lookup_table_account,
|
||||
address_lookup_table_accounts: Arc::new(address_lookup_table_accounts),
|
||||
recent_blockhash,
|
||||
durable_nonce,
|
||||
middleware_manager,
|
||||
@@ -848,4 +835,18 @@ mod tests {
|
||||
assert_eq!(selected[0].gas_fee_config.2.cu_price, 700_000);
|
||||
assert_eq!(selected[0].gas_fee_config.2.tip, 0.0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn wait_for_all_submitted_timeout_is_bounded() {
|
||||
let collector = ResultCollector::new(1);
|
||||
let start = Instant::now();
|
||||
|
||||
let result = collector.wait_for_all_submitted(0).await;
|
||||
|
||||
assert!(result.is_none());
|
||||
assert!(
|
||||
start.elapsed() < Duration::from_millis(150),
|
||||
"wait_for_all_submitted should not add multi-second grace after timeout"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ impl TradeExecutor for GenericTradeExecutor {
|
||||
total_start.as_ref().map(|s| s.elapsed()).unwrap_or(Duration::ZERO);
|
||||
let before_submit_us = (params.log_enabled && crate::common::sdk_log::sdk_log_enabled())
|
||||
.then(crate::common::clock::now_micros);
|
||||
let address_lookup_table_accounts = params.address_lookup_table_accounts.clone();
|
||||
|
||||
if params.simulate {
|
||||
let send_start = crate::common::sdk_log::sdk_log_enabled().then(Instant::now);
|
||||
@@ -103,7 +104,7 @@ impl TradeExecutor for GenericTradeExecutor {
|
||||
params.rpc,
|
||||
params.payer,
|
||||
final_instructions,
|
||||
params.address_lookup_table_account,
|
||||
address_lookup_table_accounts,
|
||||
params.recent_blockhash,
|
||||
params.durable_nonce,
|
||||
params.middleware_manager,
|
||||
@@ -157,16 +158,16 @@ impl TradeExecutor for GenericTradeExecutor {
|
||||
}
|
||||
|
||||
let need_confirm = params.wait_tx_confirmed;
|
||||
// When the caller confirms externally (need_confirm = false) and opts in
|
||||
// via SwapParams.wait_for_all_submits, return every route's signature so
|
||||
// pinned-nonce confirmation can poll all of them.
|
||||
let wait_for_all_submits = !need_confirm && params.wait_for_all_submits;
|
||||
// Each SWQOS lane may submit a distinct transaction because relay tips
|
||||
// can use different accounts, so confirmation must be able to poll every
|
||||
// returned signature when the caller opts in.
|
||||
let wait_for_all_submits = params.wait_for_all_submits;
|
||||
let sender_config = params.sender_concurrency_config();
|
||||
let result = execute_parallel(
|
||||
params.swqos_clients.as_slice(),
|
||||
params.payer,
|
||||
final_instructions,
|
||||
params.address_lookup_table_account,
|
||||
address_lookup_table_accounts,
|
||||
params.recent_blockhash,
|
||||
params.durable_nonce,
|
||||
params.middleware_manager,
|
||||
@@ -253,7 +254,7 @@ async fn simulate_transaction(
|
||||
rpc: Option<Arc<SolanaRpcClient>>,
|
||||
payer: Arc<Keypair>,
|
||||
instructions: Vec<Instruction>,
|
||||
address_lookup_table_account: Option<AddressLookupTableAccount>,
|
||||
address_lookup_table_accounts: Vec<AddressLookupTableAccount>,
|
||||
recent_blockhash: Option<Hash>,
|
||||
durable_nonce: Option<DurableNonceInfo>,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
@@ -288,7 +289,7 @@ async fn simulate_transaction(
|
||||
unit_limit,
|
||||
unit_price,
|
||||
&instructions,
|
||||
address_lookup_table_account.as_ref(),
|
||||
address_lookup_table_accounts.as_slice(),
|
||||
recent_blockhash,
|
||||
middleware_manager.as_ref(),
|
||||
protocol_name,
|
||||
|
||||
@@ -61,7 +61,7 @@ pub struct SwapParams {
|
||||
pub output_token_program: Option<Pubkey>,
|
||||
pub input_amount: Option<u64>,
|
||||
pub slippage_basis_points: Option<u64>,
|
||||
pub address_lookup_table_account: Option<AddressLookupTableAccount>,
|
||||
pub address_lookup_table_accounts: Vec<AddressLookupTableAccount>,
|
||||
pub recent_blockhash: Option<Hash>,
|
||||
pub wait_tx_confirmed: bool,
|
||||
pub protocol_params: DexParamEnum,
|
||||
@@ -82,13 +82,15 @@ pub struct SwapParams {
|
||||
pub simulate: bool,
|
||||
/// Whether to output SDK logs (from TradeConfig.log_enabled).
|
||||
pub log_enabled: bool,
|
||||
/// Fast-submit only (`wait_tx_confirmed = false`): when true, wait for every
|
||||
/// SWQOS route's HTTP submit response so all submitted signatures are
|
||||
/// returned. Defaults to false (post-4.0.11 behaviour: return after the
|
||||
/// first route accepts). Set to true when the caller does its own on-chain
|
||||
/// confirmation against a pinned durable nonce — only one route's tx can
|
||||
/// land, but the caller cannot know which in advance, so it needs every
|
||||
/// signature to poll via `getSignatureStatuses`.
|
||||
/// When true, wait for every SWQOS route's HTTP submit response before
|
||||
/// returning so the result includes all submitted signatures.
|
||||
///
|
||||
/// This is useful when confirmation or external monitoring polls all
|
||||
/// signatures: each route can submit a distinct transaction because relay
|
||||
/// tips may use different accounts. With a durable nonce, at most one route
|
||||
/// can consume the nonce; with a recent blockhash, multiple route variants
|
||||
/// may be valid, so callers must choose strategy and account state
|
||||
/// accordingly. Defaults to false for lower submit latency.
|
||||
pub wait_for_all_submits: bool,
|
||||
/// Use dedicated sender threads (internal; set via client.with_dedicated_sender_threads()).
|
||||
pub use_dedicated_sender_threads: bool,
|
||||
|
||||
@@ -55,7 +55,7 @@ impl PreallocatedTxBuilder {
|
||||
///
|
||||
/// # 交易版本自动选择
|
||||
///
|
||||
/// - **有地址查找表** (`lookup_table = Some`): 使用 `VersionedMessage::V0`
|
||||
/// - **有地址查找表** (`lookup_tables` 非空): 使用 `VersionedMessage::V0`
|
||||
/// - 支持地址查找表压缩
|
||||
/// - 减少交易大小
|
||||
/// - 需要 RPC 支持 V0
|
||||
@@ -69,11 +69,11 @@ impl PreallocatedTxBuilder {
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// // 无查找表 -> Legacy 消息
|
||||
/// let msg = builder.build_zero_alloc(&payer, &ixs, None, blockhash);
|
||||
/// let msg = builder.build_zero_alloc(&payer, &ixs, &[], blockhash);
|
||||
/// assert!(matches!(msg, VersionedMessage::Legacy(_)));
|
||||
///
|
||||
/// // 有查找表 -> V0 消息
|
||||
/// let msg = builder.build_zero_alloc(&payer, &ixs, Some(table_key), blockhash);
|
||||
/// let msg = builder.build_zero_alloc(&payer, &ixs, &[lookup_table], blockhash);
|
||||
/// assert!(matches!(msg, VersionedMessage::V0(_)));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
@@ -81,17 +81,17 @@ impl PreallocatedTxBuilder {
|
||||
&mut self,
|
||||
payer: &Pubkey,
|
||||
instructions: &[Instruction],
|
||||
address_lookup_table_account: Option<&AddressLookupTableAccount>,
|
||||
address_lookup_table_accounts: &[AddressLookupTableAccount],
|
||||
recent_blockhash: Hash,
|
||||
) -> Result<VersionedMessage> {
|
||||
self.reset();
|
||||
self.instructions.extend_from_slice(instructions);
|
||||
|
||||
if let Some(alt) = address_lookup_table_account {
|
||||
if !address_lookup_table_accounts.is_empty() {
|
||||
let message = v0::Message::try_compile(
|
||||
payer,
|
||||
&self.instructions,
|
||||
std::slice::from_ref(alt),
|
||||
address_lookup_table_accounts,
|
||||
recent_blockhash,
|
||||
)?;
|
||||
Ok(VersionedMessage::V0(message))
|
||||
|
||||
Reference in New Issue
Block a user