diff --git a/rust/src/trading.rs b/rust/src/trading.rs index a87797c..20d5f12 100644 --- a/rust/src/trading.rs +++ b/rust/src/trading.rs @@ -30,7 +30,7 @@ use bullet_exchange_interface::types::{MarketId, OrderType, Side}; use crate::generated::types::SubmitTxResponse; use crate::types::{CallMessage, UserAction}; -use crate::{Client, SDKError, SDKResult, Transaction}; +use crate::{Client, SDKError, SDKResult}; // ── Order construction helpers ────────────────────────────────────────────── @@ -127,8 +127,7 @@ impl Client { replace, sub_account_index, }); - let signed = Transaction::builder().call_message(call_msg).client(self).build()?; - self.send_transaction(&signed).await + self.send_call_message(call_msg).await } /// Cancel specific orders on a market. Signs and submits the transaction. @@ -157,8 +156,7 @@ impl Client { ) -> SDKResult { let call_msg = CallMessage::User(UserAction::CancelOrders { market_id, orders, sub_account_index }); - let signed = Transaction::builder().call_message(call_msg).client(self).build()?; - self.send_transaction(&signed).await + self.send_call_message(call_msg).await } /// Cancel all orders on a specific market. Signs and submits the transaction. @@ -175,8 +173,7 @@ impl Client { ) -> SDKResult { let call_msg = CallMessage::User(UserAction::CancelMarketOrders { market_id, sub_account_index }); - let signed = Transaction::builder().call_message(call_msg).client(self).build()?; - self.send_transaction(&signed).await + self.send_call_message(call_msg).await } /// Cancel all orders across all markets. Signs and submits the transaction. @@ -191,8 +188,7 @@ impl Client { sub_account_index: Option, ) -> SDKResult { let call_msg = CallMessage::User(UserAction::CancelAllOrders { sub_account_index }); - let signed = Transaction::builder().call_message(call_msg).client(self).build()?; - self.send_transaction(&signed).await + self.send_call_message(call_msg).await } // ── Account query convenience methods ───────────────────────────────── @@ -292,8 +288,7 @@ impl Client { ) -> SDKResult { let call_msg = CallMessage::User(UserAction::AmendOrders { market_id, orders, sub_account_index }); - let signed = Transaction::builder().call_message(call_msg).client(self).build()?; - self.send_transaction(&signed).await + self.send_call_message(call_msg).await } } diff --git a/rust/src/transaction_builder.rs b/rust/src/transaction_builder.rs index b251f86..6172de1 100644 --- a/rust/src/transaction_builder.rs +++ b/rust/src/transaction_builder.rs @@ -355,6 +355,35 @@ impl Client { Err(self.submit_tx_api_error(error).await?) } + /// Build, sign, and submit a call message, retrying once if the chain hash + /// changed since startup (401 invalid signature → schema refresh → re-sign). + /// + /// Unlike calling `send_transaction` directly, this never returns + /// `TransactionOutdated` — the retry is handled internally, and if the + /// refreshed hash also fails the error is returned as `ApiError`. + pub async fn send_call_message( + &self, + call_message: CallMessage, + ) -> SDKResult { + let signed = + Transaction::builder().call_message(call_message.clone()).client(self).build()?; + match self.send_transaction(&signed).await { + Err(SDKError::TransactionOutdated) => { + // chain hash was refreshed; re-sign with the new hash and retry once. + // submit directly so a second 401 comes back as ApiError, not TransactionOutdated + let signed = + Transaction::builder().call_message(call_message).client(self).build()?; + let body = Transaction::to_base64(&signed)?; + match self.client().submit_tx(&SubmitTxRequest { body }).await { + Ok(r) => Ok(r.into_inner()), + Err(ErrorResponse(r)) => Err(SDKError::ApiError(r.into_inner())), + Err(e) => Err(e.into()), + } + } + other => other, + } + } + async fn submit_tx_api_error(&self, error: ApiErrorResponse) -> SDKResult { if error.status == 401 && error.message.contains("Invalid signature") { self.update_schema().await?; diff --git a/wasm/src/transaction_builder.rs b/wasm/src/transaction_builder.rs index f44d2f6..47b9fb2 100644 --- a/wasm/src/transaction_builder.rs +++ b/wasm/src/transaction_builder.rs @@ -480,9 +480,7 @@ impl WasmTradingApi { &self, msg: WasmCallMessage, ) -> WasmResult { - let signed = - RustTransaction::builder().call_message(msg.inner).client(&self.inner).build()?; - let resp = self.inner.send_transaction(&signed).await?; + let resp = self.inner.send_call_message(msg.inner).await?; Ok(crate::generated::WasmSubmitTxResponse(resp)) } }