Skip to main content

hypercall/client/wallet_client/
orders.rs

1use super::WalletClient;
2use hypercall_types::Side;
3use sonic_rs::{json, Value};
4
5impl WalletClient {
6    pub async fn place_order(
7        &self,
8        base_url: &str,
9        symbol: &str,
10        side: Side,
11        price: f64,
12        size: f64,
13    ) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
14        // Generate nonce and sign the order
15        let nonce = self.next_nonce();
16        let side_str = format!("{:?}", side);
17        let price_str = price.to_string();
18        let size_str = size.to_string();
19        let client_id = format!("cli_{}", nonce);
20
21        let signature = self
22            .sign_place_order(
23                symbol, &side_str, &size_str, &price_str, "gtc", &client_id, nonce,
24            )
25            .await?;
26
27        let response = self
28            .http_client
29            .post(format!("{}/order", base_url))
30            .json(&json!({
31                "wallet": self.wallet_address,
32                "symbol": symbol,
33                "side": side_str,
34                "price": price_str,
35                "size": size_str,
36                "tif": "gtc",
37                "client_id": client_id,
38                "nonce": nonce,
39                "signature": signature,
40            }))
41            .send()
42            .await?;
43
44        let status = response.status();
45        let body = response.text().await?;
46
47        if !status.is_success() {
48            return Err(format!("Failed to place order: {} - {}", status, body).into());
49        }
50
51        Ok(sonic_rs::from_str(&body)?)
52    }
53
54    pub async fn cancel_order(
55        &self,
56        base_url: &str,
57        order_id: u64,
58    ) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
59        // Generate nonce and sign the cancel order
60        let nonce = self.next_nonce();
61        let order_id_str = order_id.to_string();
62
63        let signature = self.sign_cancel_order(&order_id_str, nonce).await?;
64
65        let response = self
66            .http_client
67            .delete(format!("{}/order", base_url))
68            .json(&json!({
69                "wallet": self.wallet_address,
70                "order_id": order_id,  // Send as number
71                "nonce": nonce,
72                "signature": signature,
73            }))
74            .send()
75            .await?;
76
77        let status = response.status();
78        let body = response.text().await?;
79
80        if !status.is_success() {
81            return Err(format!("Failed to cancel order: {} - {}", status, body).into());
82        }
83
84        Ok(sonic_rs::from_str(&body)?)
85    }
86
87    /// Place multiple orders in a single bulk request
88    pub async fn bulk_place_orders(
89        &self,
90        base_url: &str,
91        orders: Vec<(String, Side, f64, f64)>, // (symbol, side, price, size)
92    ) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
93        let mut order_requests = Vec::with_capacity(orders.len());
94        for (symbol, side, price, size) in orders {
95            let nonce = self.next_nonce();
96            let side_str = format!("{:?}", side);
97            let price_str = price.to_string();
98            let size_str = size.to_string();
99            let client_id = format!("cli_{}", nonce);
100
101            let signature = self
102                .sign_place_order(
103                    &symbol, &side_str, &size_str, &price_str, "gtc", &client_id, nonce,
104                )
105                .await?;
106
107            order_requests.push(json!({
108                "wallet": self.wallet_address,
109                "symbol": symbol,
110                "side": side_str,
111                "price": price_str,
112                "size": size_str,
113                "tif": "gtc",
114                "client_id": client_id,
115                "nonce": nonce,
116                "signature": signature,
117            }));
118        }
119
120        let response = self
121            .http_client
122            .post(format!("{}/bulk_order", base_url))
123            .json(&json!({
124                "orders": order_requests
125            }))
126            .send()
127            .await?;
128
129        let status = response.status();
130        let body = response.text().await?;
131
132        if !status.is_success() {
133            return Err(format!("Failed to place bulk orders: {} - {}", status, body).into());
134        }
135
136        Ok(sonic_rs::from_str(&body)?)
137    }
138
139    /// Place multiple orders in a single bulk request with builder code for fee sharing
140    pub async fn bulk_place_orders_with_builder(
141        &self,
142        base_url: &str,
143        orders: Vec<(String, Side, f64, f64)>, // (symbol, side, price, size)
144        builder_code_address: Option<&str>,
145    ) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
146        let mut order_requests = Vec::with_capacity(orders.len());
147        for (symbol, side, price, size) in orders {
148            let nonce = self.next_nonce();
149            let side_str = format!("{:?}", side);
150            let price_str = price.to_string();
151            let size_str = size.to_string();
152            let client_id = format!("cli_{}", nonce);
153
154            let signature = self
155                .sign_place_order(
156                    &symbol, &side_str, &size_str, &price_str, "gtc", &client_id, nonce,
157                )
158                .await?;
159
160            let mut order_json = json!({
161                "wallet": self.wallet_address,
162                "symbol": symbol,
163                "side": side_str,
164                "price": price_str,
165                "size": size_str,
166                "tif": "gtc",
167                "client_id": client_id,
168                "nonce": nonce,
169                "signature": signature,
170            });
171
172            // Add builder_code_address if provided
173            if let Some(builder) = builder_code_address {
174                order_json["builder_code_address"] = json!(builder);
175            }
176
177            order_requests.push(order_json);
178        }
179
180        let response = self
181            .http_client
182            .post(format!("{}/bulk_order", base_url))
183            .json(&json!({
184                "orders": order_requests
185            }))
186            .send()
187            .await?;
188
189        let status = response.status();
190        let body = response.text().await?;
191
192        if !status.is_success() {
193            return Err(format!("Failed to place bulk orders: {} - {}", status, body).into());
194        }
195
196        Ok(sonic_rs::from_str(&body)?)
197    }
198
199    /// Cancel multiple orders by order_id in a single bulk request
200    pub async fn bulk_cancel_orders(
201        &self,
202        base_url: &str,
203        order_ids: Vec<u64>,
204    ) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
205        let mut cancel_requests = Vec::with_capacity(order_ids.len());
206        for order_id in order_ids {
207            let nonce = self.next_nonce();
208            let order_id_str = order_id.to_string();
209
210            let signature = self.sign_cancel_order(&order_id_str, nonce).await?;
211
212            cancel_requests.push(json!({
213                "wallet": self.wallet_address,
214                "order_id": order_id,
215                "nonce": nonce,
216                "signature": signature,
217            }));
218        }
219
220        let response = self
221            .http_client
222            .delete(format!("{}/bulk_order", base_url))
223            .json(&json!({
224                "cancels": cancel_requests
225            }))
226            .send()
227            .await?;
228
229        let status = response.status();
230        let body = response.text().await?;
231
232        if !status.is_success() {
233            return Err(format!("Failed to cancel bulk orders: {} - {}", status, body).into());
234        }
235
236        Ok(sonic_rs::from_str(&body)?)
237    }
238
239    /// Place an order and return the raw HTTP response (including headers).
240    /// Useful for testing rate limit headers and error responses.
241    pub async fn place_order_raw(
242        &self,
243        base_url: &str,
244        symbol: &str,
245        side: Side,
246        price: f64,
247        size: f64,
248    ) -> Result<reqwest::Response, Box<dyn std::error::Error + Send + Sync>> {
249        let nonce = self.next_nonce();
250        let side_str = format!("{:?}", side);
251        let price_str = price.to_string();
252        let size_str = size.to_string();
253        let client_id = format!("cli_{}", nonce);
254
255        let signature = self
256            .sign_place_order(
257                symbol, &side_str, &size_str, &price_str, "gtc", &client_id, nonce,
258            )
259            .await?;
260
261        let response = self
262            .http_client
263            .post(format!("{}/order", base_url))
264            .json(&json!({
265                "wallet": self.wallet_address,
266                "symbol": symbol,
267                "side": side_str,
268                "price": price_str,
269                "size": size_str,
270                "tif": "gtc",
271                "client_id": client_id,
272                "nonce": nonce,
273                "signature": signature,
274            }))
275            .send()
276            .await?;
277
278        Ok(response)
279    }
280
281    /// Cancel an order and return the raw HTTP response (including headers).
282    /// Useful for testing rate limit headers and error responses.
283    pub async fn cancel_order_raw(
284        &self,
285        base_url: &str,
286        order_id: u64,
287    ) -> Result<reqwest::Response, Box<dyn std::error::Error + Send + Sync>> {
288        let nonce = self.next_nonce();
289        let order_id_str = order_id.to_string();
290
291        let signature = self.sign_cancel_order(&order_id_str, nonce).await?;
292
293        let response = self
294            .http_client
295            .delete(format!("{}/order", base_url))
296            .json(&json!({
297                "wallet": self.wallet_address,
298                "order_id": order_id,
299                "nonce": nonce,
300                "signature": signature,
301            }))
302            .send()
303            .await?;
304
305        Ok(response)
306    }
307}