1use crate::constants::{END_CURSOR, INITIAL_CURSOR};
2use crate::endpoints::*;
3use crate::errors::ClobError;
4use crate::http_helpers::{RequestOptions, get, post};
6use crate::order_builder::{BuilderConfig as ObBuilderConfig, OrderBuilder};
7use crate::signer_adapter::EthersSigner;
8use crate::types::OrderBookSummary;
9use crate::types::{ApiKeyCreds, ApiKeyRaw};
10use crate::types::{
11 Notification, OpenOrder, Order, OrderResponse, OrderType, Reward, SignedOrder, Trade,
12 UserMarketOrder, UserOrder,
13};
14use serde::Deserialize;
16use serde_json::Value;
17
18#[allow(dead_code)]
19#[derive(Deserialize)]
20struct OkResp {
21 ok: bool,
22}
23
24#[allow(dead_code)]
25#[derive(Deserialize)]
26struct SuccessResp {
27 success: bool,
28}
29#[derive(Deserialize)]
31#[serde(untagged)]
32enum MaybeVec<T> {
33 Vec(Vec<T>),
34 Data { data: Vec<T> },
35}
36
37impl<T> MaybeVec<T> {
38 fn into_vec(self) -> Vec<T> {
39 match self {
40 MaybeVec::Vec(v) => v,
41 MaybeVec::Data { data } => data,
42 }
43 }
44}
45
46#[derive(Deserialize)]
47#[serde(untagged)]
48enum MaybeItem<T> {
49 Item(T),
50 Data { data: T },
51}
52
53impl<T> MaybeItem<T> {
54 fn into_item(self) -> T {
55 match self {
56 MaybeItem::Item(i) => i,
57 MaybeItem::Data { data } => data,
58 }
59 }
60}
61use std::collections::HashMap;
62use std::sync::Arc;
63
64#[derive(Clone, Debug, serde::Serialize)]
67pub struct OrderPayloadParity {
68 #[serde(rename = "orderID")]
69 pub order_id: String,
70}
71
72impl OrderPayloadParity {
73 pub fn new(order_id: impl Into<String>) -> Self {
74 Self {
75 order_id: order_id.into(),
76 }
77 }
78}
79
80pub struct ClobClient {
81 pub host: String,
82 pub chain_id: i64,
83 pub signer: Option<Arc<EthersSigner>>,
84 pub creds: Option<ApiKeyCreds>,
85 pub use_server_time: bool,
86 pub tick_sizes: HashMap<String, String>,
87 pub neg_risk: HashMap<String, bool>,
88 pub fee_rates: HashMap<String, f64>,
89 pub builder_signer: Option<builder_signing_sdk_rs::BuilderSigner>,
91 pub builder_config: Option<ObBuilderConfig>,
93}
94
95#[derive(Clone, Debug, serde::Serialize)]
97pub struct OrderMarketCancelParams {
98 #[serde(skip_serializing_if = "Option::is_none")]
99 pub market: Option<String>,
100 #[serde(skip_serializing_if = "Option::is_none")]
101 pub asset_id: Option<String>,
102}
103
104impl ClobClient {
105 #[allow(non_snake_case)]
112 pub async fn getMarkets(
113 &self,
114 params: Option<std::collections::HashMap<String, String>>,
115 ) -> Result<Vec<crate::types::Market>, ClobError> {
116 self.get_markets(params).await
117 }
118
119 #[allow(non_snake_case)]
121 pub async fn getMarket(
122 &self,
123 market_id: &str,
124 params: Option<std::collections::HashMap<String, String>>,
125 ) -> Result<crate::types::MarketSummary, ClobError> {
126 self.get_market(market_id, params).await
127 }
128
129 #[allow(non_snake_case)]
130 pub async fn getOrderBook(&mut self, token_id: &str) -> Result<OrderBookSummary, ClobError> {
131 self.get_order_book(token_id).await
132 }
133
134 #[allow(non_snake_case)]
135 pub async fn getTickSize(&mut self, token_id: &str) -> Result<String, ClobError> {
136 self.get_tick_size(token_id).await
137 }
138
139 #[allow(non_snake_case)]
140 pub async fn getNegRisk(&mut self, token_id: &str) -> Result<bool, ClobError> {
141 self.get_neg_risk(token_id).await
142 }
143
144 #[allow(non_snake_case)]
145 pub async fn getFeeRate(&mut self, token_id: &str) -> Result<f64, ClobError> {
146 self.get_fee_rate(token_id).await
147 }
148
149 #[allow(non_snake_case)]
150 pub async fn getOpenOrders(
151 &self,
152 params: Option<std::collections::HashMap<String, String>>,
153 ) -> Result<Vec<SignedOrder>, ClobError> {
154 self.get_open_orders(params).await
155 }
156
157 #[allow(non_snake_case)]
158 pub async fn getOrder(&self, order_id: &str) -> Result<OpenOrder, ClobError> {
159 self.get_order(order_id).await
160 }
161
162 #[allow(non_snake_case)]
163 pub async fn postSignedOrder(
164 &self,
165 signed_order: &SignedOrder,
166 ) -> Result<OrderResponse, ClobError> {
167 self.post_signed_order(signed_order, OrderType::GTC, false)
168 .await
169 }
170
171 #[allow(non_snake_case)]
172 pub async fn postOrders(
173 &self,
174 orders: Vec<SignedOrder>,
175 defer_exec: bool,
176 ) -> Result<Vec<Order>, ClobError> {
177 self.post_orders(orders, defer_exec).await
178 }
179
180 #[allow(non_snake_case)]
181 pub async fn createOrder(
182 &mut self,
183 user_order: UserOrder,
184 options_tick: Option<&str>,
185 ) -> Result<SignedOrder, ClobError> {
186 self.create_order(user_order, options_tick).await
187 }
188
189 #[allow(non_snake_case)]
190 pub async fn createMarketOrder(
191 &mut self,
192 user_market_order: UserMarketOrder,
193 options_tick: Option<&str>,
194 ) -> Result<SignedOrder, ClobError> {
195 self.create_market_order(user_market_order, options_tick)
196 .await
197 }
198
199 #[allow(non_snake_case)]
200 pub async fn createAndPostOrder(
201 &mut self,
202 user_order: UserOrder,
203 options_tick: Option<&str>,
204 ) -> Result<OrderResponse, ClobError> {
205 self.create_and_post_order(user_order, options_tick, None)
206 .await
207 }
208
209 #[allow(non_snake_case)]
210 pub async fn createAndPostMarketOrder(
211 &mut self,
212 user_market_order: UserMarketOrder,
213 options_tick: Option<&str>,
214 ) -> Result<OrderResponse, ClobError> {
215 self.create_and_post_market_order(user_market_order, options_tick, None)
216 .await
217 }
218
219 #[allow(non_snake_case)]
221 pub async fn cancelOrder(&self, payload: OrderPayloadParity) -> Result<Value, ClobError> {
222 self.cancel_order_payload_raw(payload).await
223 }
224
225 #[allow(non_snake_case)]
226 pub async fn cancelOrders(&self, order_ids: Vec<String>) -> Result<Vec<Order>, ClobError> {
227 self.cancel_orders(order_ids).await
228 }
229
230 #[allow(non_snake_case)]
231 pub async fn cancelAll(&self) -> Result<Vec<Order>, ClobError> {
232 self.cancel_all().await
233 }
234
235 #[allow(non_snake_case)]
236 pub async fn cancelMarketOrders(
237 &self,
238 payload: OrderMarketCancelParams,
239 ) -> Result<Vec<Order>, ClobError> {
240 self.cancel_market_orders(payload).await
241 }
242
243 #[allow(non_snake_case)]
244 pub async fn isOrderScoring(
245 &self,
246 params: Option<std::collections::HashMap<String, String>>,
247 ) -> Result<crate::types::OrderScoring, ClobError> {
248 self.is_order_scoring(params).await
249 }
250
251 #[allow(non_snake_case)]
252 pub async fn areOrdersScoring(
253 &self,
254 order_ids: Option<Vec<String>>,
255 ) -> Result<crate::types::OrdersScoring, ClobError> {
256 self.are_orders_scoring(order_ids).await
257 }
258
259 #[allow(non_snake_case)]
260 pub async fn getTrades(
261 &self,
262 params: Option<std::collections::HashMap<String, String>>,
263 only_first_page: bool,
264 next_cursor: Option<String>,
265 ) -> Result<Vec<Value>, ClobError> {
266 self.get_trades(params, only_first_page, next_cursor).await
267 }
268
269 #[allow(non_snake_case)]
270 pub async fn getTradesTyped(
271 &self,
272 params: Option<std::collections::HashMap<String, String>>,
273 only_first_page: bool,
274 next_cursor: Option<String>,
275 ) -> Result<Vec<Trade>, ClobError> {
276 self.get_trades_typed(params, only_first_page, next_cursor)
277 .await
278 }
279
280 #[allow(non_snake_case)]
281 pub async fn getNotifications(&self) -> Result<Vec<Notification>, ClobError> {
282 self.get_notifications().await
283 }
284
285 #[allow(non_snake_case)]
286 pub async fn dropNotifications(&self, ids: Option<&Vec<String>>) -> Result<(), ClobError> {
287 self.drop_notifications(ids).await
288 }
289
290 #[allow(non_snake_case)]
291 pub async fn getBalanceAllowance(
292 &self,
293 params: Option<std::collections::HashMap<String, String>>,
294 ) -> Result<crate::types::BalanceAllowanceResponse, ClobError> {
295 self.get_balance_allowance(params).await
296 }
297
298 #[allow(non_snake_case)]
299 pub async fn updateBalanceAllowance(
300 &self,
301 params: Option<std::collections::HashMap<String, String>>,
302 ) -> Result<(), ClobError> {
303 self.update_balance_allowance(params).await
304 }
305
306 #[allow(non_snake_case)]
307 pub async fn getTime(&self) -> Result<u64, ClobError> {
308 self.get_server_time().await
309 }
310
311 #[allow(non_snake_case)]
313 pub async fn getApiKeys(&self) -> Result<Vec<crate::types::ApiKeyCreds>, ClobError> {
314 self.get_api_keys().await
315 }
316 #[allow(non_snake_case)]
317 pub async fn deleteApiKey(&self) -> Result<(), ClobError> {
318 self.delete_api_key().await
319 }
320 #[allow(non_snake_case)]
321 pub async fn getClosedOnlyMode(&self) -> Result<crate::types::BanStatus, ClobError> {
322 self.get_closed_only_mode().await
323 }
324
325 #[allow(non_snake_case)]
327 pub async fn createApiKey(&self, nonce: Option<u64>) -> Result<ApiKeyCreds, ClobError> {
328 self.create_api_key(nonce).await
329 }
330 #[allow(non_snake_case)]
331 pub async fn deriveApiKey(
332 &self,
333 params: Option<std::collections::HashMap<String, String>>,
334 ) -> Result<ApiKeyCreds, ClobError> {
335 self.derive_api_key(params).await
336 }
337
338 #[allow(non_snake_case)]
340 pub async fn createBuilderApiKey(&self) -> Result<ApiKeyCreds, ClobError> {
341 self.create_builder_api_key().await
342 }
343 #[allow(non_snake_case)]
344 pub async fn getBuilderApiKeys(&self) -> Result<Vec<ApiKeyCreds>, ClobError> {
345 self.get_builder_api_keys().await
346 }
347 #[allow(non_snake_case)]
348 pub async fn revokeBuilderApiKey(&self) -> Result<(), ClobError> {
349 self.revoke_builder_api_key().await
350 }
351
352 #[allow(non_snake_case)]
354 pub async fn getSimplifiedMarkets(
355 &self,
356 params: Option<std::collections::HashMap<String, String>>,
357 ) -> Result<Vec<crate::types::Market>, ClobError> {
358 self.get_simplified_markets(params).await
359 }
360 #[allow(non_snake_case)]
361 pub async fn getSamplingMarkets(
362 &self,
363 params: Option<std::collections::HashMap<String, String>>,
364 ) -> Result<Vec<crate::types::Market>, ClobError> {
365 self.get_sampling_markets(params).await
366 }
367 #[allow(non_snake_case)]
368 pub async fn getSamplingSimplifiedMarkets(
369 &self,
370 params: Option<std::collections::HashMap<String, String>>,
371 ) -> Result<Vec<crate::types::Market>, ClobError> {
372 self.get_sampling_simplified_markets(params).await
373 }
374 #[allow(non_snake_case)]
375 pub async fn getOrderBooks(
376 &self,
377 params: Option<std::collections::HashMap<String, String>>,
378 ) -> Result<Vec<crate::types::OrderBookSummary>, ClobError> {
379 self.get_order_books(params).await
380 }
381 #[allow(non_snake_case)]
382 pub async fn getMidpoint(
383 &self,
384 params: Option<std::collections::HashMap<String, String>>,
385 ) -> Result<Vec<crate::types::MarketPrice>, ClobError> {
386 self.get_midpoint(params).await
387 }
388 #[allow(non_snake_case)]
389 pub async fn getMidpoints(
390 &self,
391 params: Option<std::collections::HashMap<String, String>>,
392 ) -> Result<Vec<crate::types::MarketPrice>, ClobError> {
393 self.get_midpoints(params).await
394 }
395 #[allow(non_snake_case)]
396 pub async fn getPrices(
397 &self,
398 params: Option<std::collections::HashMap<String, String>>,
399 ) -> Result<Vec<crate::types::MarketPrice>, ClobError> {
400 self.get_prices(params).await
401 }
402 #[allow(non_snake_case)]
403 pub async fn getSpreads(
404 &self,
405 params: Option<std::collections::HashMap<String, String>>,
406 ) -> Result<Vec<crate::types::MarketPrice>, ClobError> {
407 self.get_spreads(params).await
408 }
409 #[allow(non_snake_case)]
410 pub async fn getLastTradesPrices(
411 &self,
412 params: Option<std::collections::HashMap<String, String>>,
413 ) -> Result<Vec<crate::types::MarketPrice>, ClobError> {
414 self.get_last_trades_prices(params).await
415 }
416 #[allow(non_snake_case)]
417 pub async fn getPricesHistory(
418 &self,
419 params: Option<std::collections::HashMap<String, String>>,
420 ) -> Result<Vec<crate::types::MarketPrice>, ClobError> {
421 self.get_prices_history(params).await
422 }
423 #[allow(non_snake_case)]
424 pub async fn getMarketTradesEvents(
425 &self,
426 market_id: &str,
427 params: Option<std::collections::HashMap<String, String>>,
428 ) -> Result<Vec<crate::types::Trade>, ClobError> {
429 self.get_market_trades_events(market_id, params).await
430 }
431
432 #[allow(non_snake_case)]
434 pub async fn getBuilderTrades(
435 &self,
436 params: Option<std::collections::HashMap<String, String>>,
437 ) -> Result<Vec<crate::types::BuilderTrade>, ClobError> {
438 self.get_builder_trades(params).await
439 }
440
441 #[allow(non_snake_case)]
443 pub async fn getEarningsForUserForDay(
444 &self,
445 params: Option<std::collections::HashMap<String, String>>,
446 ) -> Result<Vec<crate::types::Reward>, ClobError> {
447 self.get_earnings_for_user_for_day(params).await
448 }
449 #[allow(non_snake_case)]
450 pub async fn getEarningsForUserForDayTyped(
451 &self,
452 params: Option<std::collections::HashMap<String, String>>,
453 ) -> Result<Vec<crate::types::Reward>, ClobError> {
454 self.get_rewards_user_for_day_typed(params).await
455 }
456 #[allow(non_snake_case)]
457 pub async fn getTotalEarningsForUserForDay(
458 &self,
459 params: Option<std::collections::HashMap<String, String>>,
460 ) -> Result<Vec<crate::types::Reward>, ClobError> {
461 self.get_total_earnings_for_user_for_day(params).await
462 }
463 #[allow(non_snake_case)]
464 pub async fn getLiquidityRewardPercentages(
465 &self,
466 params: Option<std::collections::HashMap<String, String>>,
467 ) -> Result<std::collections::HashMap<String, f64>, ClobError> {
468 self.get_liquidity_reward_percentages(params).await
469 }
470 #[allow(non_snake_case)]
471 pub async fn getRewardsMarketsCurrent(
472 &self,
473 params: Option<std::collections::HashMap<String, String>>,
474 ) -> Result<Vec<crate::types::Reward>, ClobError> {
475 self.get_rewards_markets_current(params).await
476 }
477 #[allow(non_snake_case)]
478 pub async fn getRewardsEarningsPercentages(
479 &self,
480 params: Option<std::collections::HashMap<String, String>>,
481 ) -> Result<Vec<crate::types::Reward>, ClobError> {
482 self.get_rewards_earnings_percentages(params).await
483 }
484 pub fn new(
486 host: &str,
487 chain_id: i64,
488 signer: Option<Arc<EthersSigner>>,
489 creds: Option<ApiKeyCreds>,
490 use_server_time: bool,
491 ) -> Self {
492 Self {
493 host: if let Some(stripped) = host.strip_suffix('/') {
494 stripped.to_string()
495 } else {
496 host.to_string()
497 },
498 chain_id,
499 signer,
500 creds,
501 use_server_time,
502 tick_sizes: HashMap::new(),
503 neg_risk: HashMap::new(),
504 fee_rates: HashMap::new(),
505 builder_signer: None,
506 builder_config: None,
507 }
508 }
509
510 pub fn with_builder_signer(
512 mut self,
513 key: String,
514 secret_b64: String,
515 passphrase: String,
516 ) -> Self {
517 let creds = builder_signing_sdk_rs::BuilderApiKeyCreds {
518 key,
519 secret: secret_b64,
520 passphrase,
521 };
522 self.builder_signer = Some(builder_signing_sdk_rs::BuilderSigner::new(creds));
523 self
524 }
525
526 pub fn with_builder_config(mut self, cfg: ObBuilderConfig) -> Self {
528 self.builder_config = Some(cfg);
529 self
530 }
531
532 pub async fn get_order_book(&mut self, token_id: &str) -> Result<OrderBookSummary, ClobError> {
533 let endpoint = format!("{}{}", self.host, GET_ORDER_BOOK);
534 let mut params = std::collections::HashMap::new();
535 params.insert("token_id".to_string(), token_id.to_string());
536 let opts = RequestOptions {
537 headers: None,
538 data: None,
539 params: Some(params),
540 };
541 let val = get(&endpoint, Some(opts)).await?;
542 let obs: OrderBookSummary =
543 serde_json::from_value(val).map_err(|e| ClobError::Other(e.to_string()))?;
544 Ok(obs)
545 }
546
547 pub async fn get_tick_size(&mut self, token_id: &str) -> Result<String, ClobError> {
548 if let Some(v) = self.tick_sizes.get(token_id) {
549 return Ok(v.clone());
550 }
551 let endpoint = format!("{}{}", self.host, GET_TICK_SIZE);
552 let mut params = std::collections::HashMap::new();
553 params.insert("token_id".to_string(), token_id.to_string());
554 let opts = RequestOptions {
555 headers: None,
556 data: None,
557 params: Some(params),
558 };
559 let val = get(&endpoint, Some(opts)).await?;
560 let tick = val
561 .get("minimum_tick_size")
562 .and_then(|v| v.as_str())
563 .ok_or(ClobError::Other("invalid tick response".to_string()))?;
564 self.tick_sizes
565 .insert(token_id.to_string(), tick.to_string());
566 Ok(tick.to_string())
567 }
568
569 pub async fn get_neg_risk(&mut self, token_id: &str) -> Result<bool, ClobError> {
570 if let Some(v) = self.neg_risk.get(token_id) {
571 return Ok(*v);
572 }
573 let endpoint = format!("{}{}", self.host, GET_NEG_RISK);
574 let mut params = std::collections::HashMap::new();
575 params.insert("token_id".to_string(), token_id.to_string());
576 let opts = RequestOptions {
577 headers: None,
578 data: None,
579 params: Some(params),
580 };
581 let val = get(&endpoint, Some(opts)).await?;
582 let rr = val
583 .get("neg_risk")
584 .and_then(|v| v.as_bool())
585 .ok_or(ClobError::Other("invalid neg risk response".to_string()))?;
586 self.neg_risk.insert(token_id.to_string(), rr);
587 Ok(rr)
588 }
589
590 pub async fn get_fee_rate(&mut self, token_id: &str) -> Result<f64, ClobError> {
591 if let Some(v) = self.fee_rates.get(token_id) {
592 return Ok(*v);
593 }
594 let endpoint = format!("{}{}", self.host, GET_FEE_RATE);
595 let mut params = std::collections::HashMap::new();
596 params.insert("token_id".to_string(), token_id.to_string());
597 let opts = RequestOptions {
598 headers: None,
599 data: None,
600 params: Some(params),
601 };
602 let val = get(&endpoint, Some(opts)).await?;
603 let fee = val
604 .get("base_fee")
605 .and_then(|v| v.as_f64())
606 .ok_or(ClobError::Other("invalid fee response".to_string()))?;
607 self.fee_rates.insert(token_id.to_string(), fee);
608 Ok(fee)
609 }
610
611 pub async fn post_order(&self, signed_order: &SignedOrder) -> Result<OrderResponse, ClobError> {
615 self.post_signed_order(signed_order, OrderType::GTC, false)
617 .await
618 }
619
620 pub async fn get_api_keys(&self) -> Result<Vec<crate::types::ApiKeyCreds>, ClobError> {
621 if self.creds.is_none() {
622 return Err(ClobError::Other("L2 creds required".to_string()));
623 }
624 let signer_arc = self
625 .signer
626 .as_ref()
627 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
628 let signer_ref: &EthersSigner = signer_arc.as_ref();
629 let endpoint = format!("{}{}", self.host, GET_API_KEYS);
630 let ts = if self.use_server_time {
631 Some(self.get_server_time().await?)
632 } else {
633 None
634 };
635 let headers = crate::headers::create_l2_headers(
636 signer_ref,
637 self.creds.as_ref().unwrap(),
638 "GET",
639 GET_API_KEYS,
640 None,
641 ts,
642 )
643 .await?;
644 let resp: crate::types::ApiKeysResponse = crate::http_helpers::get_typed(
645 &endpoint,
646 Some(RequestOptions::<Value> {
647 headers: Some(headers),
648 data: None,
649 params: None,
650 }),
651 )
652 .await?;
653 Ok(resp.api_keys)
654 }
655
656 pub async fn get_closed_only_mode(&self) -> Result<crate::types::BanStatus, ClobError> {
657 if self.creds.is_none() {
658 return Err(ClobError::Other("L2 creds required".to_string()));
659 }
660 let signer_arc = self
661 .signer
662 .as_ref()
663 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
664 let signer_ref: &EthersSigner = signer_arc.as_ref();
665 let endpoint = format!("{}{}", self.host, CLOSED_ONLY);
666 let ts = if self.use_server_time {
667 Some(self.get_server_time().await?)
668 } else {
669 None
670 };
671 let headers = crate::headers::create_l2_headers(
672 signer_ref,
673 self.creds.as_ref().unwrap(),
674 "GET",
675 CLOSED_ONLY,
676 None,
677 ts,
678 )
679 .await?;
680 let resp: crate::types::BanStatus = crate::http_helpers::get_typed(
681 &endpoint,
682 Some(RequestOptions::<Value> {
683 headers: Some(headers),
684 data: None,
685 params: None,
686 }),
687 )
688 .await?;
689 Ok(resp)
690 }
691
692 pub async fn delete_api_key(&self) -> Result<(), ClobError> {
693 if self.creds.is_none() {
694 return Err(ClobError::Other("L2 creds required".to_string()));
695 }
696 let signer_arc = self
697 .signer
698 .as_ref()
699 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
700 let signer_ref: &EthersSigner = signer_arc.as_ref();
701 let endpoint = format!("{}{}", self.host, DELETE_API_KEY);
702 let ts = if self.use_server_time {
703 Some(self.get_server_time().await?)
704 } else {
705 None
706 };
707 let headers = crate::headers::create_l2_headers(
708 signer_ref,
709 self.creds.as_ref().unwrap(),
710 "DELETE",
711 DELETE_API_KEY,
712 None,
713 ts,
714 )
715 .await?;
716 let _val: () = crate::http_helpers::del_typed::<(), Value>(
717 &endpoint,
718 Some(RequestOptions::<Value> {
719 headers: Some(headers),
720 data: None,
721 params: None,
722 }),
723 )
724 .await?;
725 Ok(())
726 }
727
728 pub async fn get_trades(
729 &self,
730 params: Option<std::collections::HashMap<String, String>>,
731 only_first_page: bool,
732 next_cursor: Option<String>,
733 ) -> Result<Vec<Value>, ClobError> {
734 if self.creds.is_none() {
735 return Err(ClobError::Other("L2 creds required".to_string()));
736 }
737 let signer_arc = self
738 .signer
739 .as_ref()
740 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
741 let signer_ref: &EthersSigner = signer_arc.as_ref();
742 let ts = if self.use_server_time {
743 Some(self.get_server_time().await?)
744 } else {
745 None
746 };
747 let headers = crate::headers::create_l2_headers(
748 signer_ref,
749 self.creds.as_ref().unwrap(),
750 "GET",
751 GET_TRADES,
752 None,
753 ts,
754 )
755 .await?;
756 let mut results: Vec<Value> = vec![];
757 let mut cursor = next_cursor.unwrap_or_else(|| INITIAL_CURSOR.to_string());
758 while cursor != END_CURSOR {
759 if only_first_page && cursor != INITIAL_CURSOR {
760 break;
761 }
762 let mut p = params.clone().unwrap_or_default();
763 p.insert("next_cursor".to_string(), cursor.clone());
764 let val = get(
765 &format!("{}{}", self.host, GET_TRADES),
766 Some(RequestOptions {
767 headers: Some(headers.clone()),
768 data: None,
769 params: Some(p),
770 }),
771 )
772 .await?;
773 let data = val
774 .get("data")
775 .and_then(|v| v.as_array())
776 .cloned()
777 .unwrap_or_default();
778 for item in data {
779 results.push(item);
780 }
781 cursor = val
782 .get("next_cursor")
783 .and_then(|v| v.as_str())
784 .unwrap_or(END_CURSOR)
785 .to_string();
786 }
787 Ok(results)
788 }
789
790 pub async fn get_trades_typed(
792 &self,
793 params: Option<std::collections::HashMap<String, String>>,
794 only_first_page: bool,
795 next_cursor: Option<String>,
796 ) -> Result<Vec<Trade>, ClobError> {
797 let vals = self
798 .get_trades(params, only_first_page, next_cursor)
799 .await?;
800 let mut trades: Vec<Trade> = Vec::new();
801 for v in vals {
802 let t: Trade =
803 serde_json::from_value(v).map_err(|e| ClobError::Other(e.to_string()))?;
804 trades.push(t);
805 }
806 Ok(trades)
807 }
808
809 pub async fn get_notifications(&self) -> Result<Vec<Notification>, ClobError> {
810 if self.creds.is_none() {
811 return Err(ClobError::Other("L2 creds required".to_string()));
812 }
813 let signer_arc = self
814 .signer
815 .as_ref()
816 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
817 let signer_ref: &EthersSigner = signer_arc.as_ref();
818 let endpoint = format!("{}{}", self.host, GET_NOTIFICATIONS);
819 let mut params = std::collections::HashMap::new();
820 params.insert("signature_type".to_string(), "EOA".to_string());
821 let ts = if self.use_server_time {
822 Some(self.get_server_time().await?)
823 } else {
824 None
825 };
826 let headers = crate::headers::create_l2_headers(
827 signer_ref,
828 self.creds.as_ref().unwrap(),
829 "GET",
830 GET_NOTIFICATIONS,
831 None,
832 ts,
833 )
834 .await?;
835 let resp: MaybeVec<Notification> = crate::http_helpers::get_typed(
836 &endpoint,
837 Some(RequestOptions::<Value> {
838 headers: Some(headers),
839 data: None,
840 params: Some(params),
841 }),
842 )
843 .await?;
844 Ok(resp.into_vec())
845 }
846
847 pub async fn get_notifications_typed(&self) -> Result<Vec<Notification>, ClobError> {
849 self.get_notifications().await
851 }
852
853 pub async fn drop_notifications(&self, ids: Option<&Vec<String>>) -> Result<(), ClobError> {
854 if self.creds.is_none() {
855 return Err(ClobError::Other("L2 creds required".to_string()));
856 }
857 let signer_arc = self
858 .signer
859 .as_ref()
860 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
861 let signer_ref: &EthersSigner = signer_arc.as_ref();
862 let endpoint = format!("{}{}", self.host, DROP_NOTIFICATIONS);
863 let params = crate::http_helpers::parse_drop_notification_params(ids);
864 let ts = if self.use_server_time {
865 Some(self.get_server_time().await?)
866 } else {
867 None
868 };
869 let headers = crate::headers::create_l2_headers(
870 signer_ref,
871 self.creds.as_ref().unwrap(),
872 "DELETE",
873 DROP_NOTIFICATIONS,
874 None,
875 ts,
876 )
877 .await?;
878 let _raw: SuccessResp = crate::http_helpers::del_typed(
879 &endpoint,
880 Some(RequestOptions::<Value> {
881 headers: Some(headers),
882 data: None,
883 params: Some(params),
884 }),
885 )
886 .await?;
887 Ok(())
888 }
889
890 pub async fn get_balance_allowance(
891 &self,
892 params: Option<std::collections::HashMap<String, String>>,
893 ) -> Result<crate::types::BalanceAllowanceResponse, ClobError> {
894 if self.creds.is_none() {
895 return Err(ClobError::Other("L2 creds required".to_string()));
896 }
897 let signer_arc = self
898 .signer
899 .as_ref()
900 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
901 let signer_ref: &EthersSigner = signer_arc.as_ref();
902 let ts = if self.use_server_time {
903 Some(self.get_server_time().await?)
904 } else {
905 None
906 };
907 let headers = crate::headers::create_l2_headers(
908 signer_ref,
909 self.creds.as_ref().unwrap(),
910 "GET",
911 GET_BALANCE_ALLOWANCE,
912 None,
913 ts,
914 )
915 .await?;
916 let resp: crate::types::BalanceAllowanceResponse = crate::http_helpers::get_typed(
917 &format!("{}{}", self.host, GET_BALANCE_ALLOWANCE),
918 Some(RequestOptions::<Value> {
919 headers: Some(headers),
920 data: None,
921 params,
922 }),
923 )
924 .await?;
925 Ok(resp)
926 }
927
928 pub async fn update_balance_allowance(
929 &self,
930 params: Option<std::collections::HashMap<String, String>>,
931 ) -> Result<(), ClobError> {
932 if self.creds.is_none() {
933 return Err(ClobError::Other("L2 creds required".to_string()));
934 }
935 let signer_arc = self
936 .signer
937 .as_ref()
938 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
939 let signer_ref: &EthersSigner = signer_arc.as_ref();
940 let ts = if self.use_server_time {
941 Some(self.get_server_time().await?)
942 } else {
943 None
944 };
945 let headers = crate::headers::create_l2_headers(
946 signer_ref,
947 self.creds.as_ref().unwrap(),
948 "GET",
949 UPDATE_BALANCE_ALLOWANCE,
950 None,
951 ts,
952 )
953 .await?;
954 let _resp: OkResp = crate::http_helpers::get_typed(
956 &format!("{}{}", self.host, UPDATE_BALANCE_ALLOWANCE),
957 Some(RequestOptions::<Value> {
958 headers: Some(headers),
959 data: None,
960 params,
961 }),
962 )
963 .await?;
964 Ok(())
965 }
966
967 pub async fn get_rewards_user_for_day_typed(
970 &self,
971 params: Option<std::collections::HashMap<String, String>>,
972 ) -> Result<Vec<Reward>, ClobError> {
973 let val = crate::http_helpers::get(
974 &format!("{}{}", self.host, GET_EARNINGS_FOR_USER_FOR_DAY),
975 Some(RequestOptions {
976 headers: None,
977 data: None,
978 params,
979 }),
980 )
981 .await?;
982
983 let arr = if let Some(a) = val.get("data").and_then(|v| v.as_array()) {
984 a.clone()
985 } else if val.is_array() {
986 val.as_array().cloned().unwrap_or_default()
987 } else {
988 Vec::new()
989 };
990 let mut out: Vec<Reward> = Vec::new();
991 for v in arr {
992 let r: Reward =
993 serde_json::from_value(v).map_err(|e| ClobError::Other(e.to_string()))?;
994 out.push(r);
995 }
996 Ok(out)
997 }
998
999 pub async fn create_order(
1000 &self,
1001 user_order: UserOrder,
1002 options_tick: Option<&str>,
1003 ) -> Result<SignedOrder, ClobError> {
1004 self.can_l1_auth()?;
1006 let signer_arc = self
1007 .signer
1008 .as_ref()
1009 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1010 let signer_ref: &EthersSigner = signer_arc.as_ref();
1011 let ob = if let Some(cfg) = &self.builder_config {
1012 OrderBuilder::with_config(signer_ref, self.chain_id, cfg)
1013 } else {
1014 OrderBuilder::new(signer_ref, self.chain_id, None, None)
1015 };
1016 let exchange_addr = self.resolve_exchange_address(&user_order.token_id);
1018 let signed = ob
1019 .build_order(&exchange_addr, &user_order, options_tick.unwrap_or("0.01"))
1020 .await?;
1021 Ok(signed)
1022 }
1023
1024 pub async fn create_market_order(
1025 &self,
1026 user_market_order: UserMarketOrder,
1027 options_tick: Option<&str>,
1028 ) -> Result<SignedOrder, ClobError> {
1029 self.can_l1_auth()?;
1030 let signer_arc = self
1031 .signer
1032 .as_ref()
1033 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1034 let signer_ref: &EthersSigner = signer_arc.as_ref();
1035 let ob = if let Some(cfg) = &self.builder_config {
1036 OrderBuilder::with_config(signer_ref, self.chain_id, cfg)
1037 } else {
1038 OrderBuilder::new(signer_ref, self.chain_id, None, None)
1039 };
1040 let exchange_addr = self.resolve_exchange_address(&user_market_order.token_id);
1041 let signed = ob
1042 .build_market_order(
1043 &exchange_addr,
1044 &user_market_order,
1045 options_tick.unwrap_or("0.01"),
1046 )
1047 .await?;
1048 Ok(signed)
1049 }
1050
1051 fn resolve_exchange_address(&self, _token_id: &str) -> String {
1054 let neg = self
1055 .builder_config
1056 .as_ref()
1057 .and_then(|c| c.neg_risk)
1058 .unwrap_or(false);
1059 match (self.chain_id, neg) {
1060 (137, false) => "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E".to_string(),
1061 (137, true) => "0xC5d563A36AE78145C45a50134d48A1215220f80a".to_string(),
1062 (80002, false) => "0xdFE02Eb6733538f8Ea35D585af8DE5958AD99E40".to_string(),
1063 (80002, true) => "0xC5d563A36AE78145C45a50134d48A1215220f80a".to_string(),
1064 (_other, _) => "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E".to_string(),
1065 }
1066 }
1067
1068 pub async fn post_orders(
1071 &self,
1072 args: Vec<SignedOrder>,
1073 _defer_exec: bool,
1074 ) -> Result<Vec<Order>, ClobError> {
1075 if self.creds.is_none() {
1077 return Err(ClobError::Other("L2 creds required".to_string()));
1078 }
1079 let signer_arc = self
1080 .signer
1081 .as_ref()
1082 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1083 let signer_ref: &EthersSigner = signer_arc.as_ref();
1084 let body_str = serde_json::to_string(&args).map_err(|e| ClobError::Other(e.to_string()))?;
1085 let ts = if self.use_server_time {
1087 Some(self.get_server_time().await?)
1088 } else {
1089 None
1090 };
1091 let mut headers = crate::headers::create_l2_headers(
1092 signer_ref,
1093 self.creds.as_ref().unwrap(),
1094 "POST",
1095 POST_ORDERS,
1096 Some(&body_str),
1097 ts,
1098 )
1099 .await?;
1100 if let Some(b) = &self.builder_signer {
1101 let b_payload = b
1102 .create_builder_header_payload("POST", POST_ORDERS, Some(&body_str), None)
1103 .map_err(|e| ClobError::Other(format!("builder header error: {}", e)))?;
1104 headers = crate::headers::inject_builder_headers(headers, &b_payload);
1105 }
1106 let endpoint = format!("{}{}", self.host, POST_ORDERS);
1107 let raw: MaybeVec<Order> = crate::http_helpers::post_typed(
1108 &endpoint,
1109 Some(RequestOptions {
1110 headers: Some(headers),
1111 data: Some(args),
1112 params: None,
1113 }),
1114 )
1115 .await?;
1116 Ok(raw.into_vec())
1117 }
1118
1119 pub async fn post_signed_order(
1122 &self,
1123 signed_order: &SignedOrder,
1124 order_type: OrderType,
1125 defer_exec: bool,
1126 ) -> Result<OrderResponse, ClobError> {
1127 if self.creds.is_none() {
1129 return Err(ClobError::Other("L2 creds required".to_string()));
1130 }
1131 let signer_arc = self
1132 .signer
1133 .as_ref()
1134 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1135 let signer_ref: &EthersSigner = signer_arc.as_ref();
1136
1137 let owner = self.creds.as_ref().unwrap().key.clone();
1140
1141 let new_order =
1143 crate::utilities::order_to_json(signed_order, &owner, order_type, defer_exec);
1144
1145 let body_str =
1146 serde_json::to_string(&new_order).map_err(|e| ClobError::Other(e.to_string()))?;
1147 let ts = if self.use_server_time {
1149 Some(self.get_server_time().await?)
1150 } else {
1151 None
1152 };
1153 let mut headers = crate::headers::create_l2_headers(
1154 signer_ref,
1155 self.creds.as_ref().unwrap(),
1156 "POST",
1157 POST_ORDER,
1158 Some(&body_str),
1159 ts,
1160 )
1161 .await?;
1162 if let Some(b) = &self.builder_signer {
1164 let b_payload = b
1165 .create_builder_header_payload("POST", POST_ORDER, Some(&body_str), None)
1166 .map_err(|e| ClobError::Other(format!("builder header error: {}", e)))?;
1167 headers = crate::headers::inject_builder_headers(headers, &b_payload);
1168 }
1169 let endpoint = format!("{}{}", self.host, POST_ORDER);
1170 let opts = RequestOptions {
1171 headers: Some(headers),
1172 data: Some(new_order),
1173 params: None,
1174 };
1175 let res: MaybeItem<OrderResponse> =
1176 crate::http_helpers::post_typed(&endpoint, Some(opts)).await?;
1177 Ok(res.into_item())
1178 }
1179
1180 pub async fn post_signed_order_typed(
1183 &self,
1184 signed_order: &SignedOrder,
1185 order_type: OrderType,
1186 defer_exec: bool,
1187 ) -> Result<OrderResponse, ClobError> {
1188 self.post_signed_order(signed_order, order_type, defer_exec)
1189 .await
1190 }
1191
1192 pub async fn create_and_post_order(
1195 &mut self,
1196 user_order: UserOrder,
1197 options_tick: Option<&str>,
1198 order_type: Option<OrderType>,
1199 ) -> Result<OrderResponse, ClobError> {
1200 let tick = if let Some(t) = options_tick {
1202 t.to_string()
1203 } else if let Some(cfg_tick) = self
1204 .builder_config
1205 .as_ref()
1206 .and_then(|c| c.tick_size.as_ref())
1207 {
1208 cfg_tick.clone()
1209 } else {
1210 "0.01".to_string()
1211 };
1212 let signed = self.create_order(user_order, Some(&tick)).await?;
1213 let order_type = order_type.unwrap_or(OrderType::GTC);
1214 self.post_signed_order(&signed, order_type, false).await
1215 }
1216
1217 pub async fn create_and_post_market_order(
1220 &mut self,
1221 user_market_order: UserMarketOrder,
1222 options_tick: Option<&str>,
1223 order_type: Option<OrderType>,
1224 ) -> Result<OrderResponse, ClobError> {
1225 let tick = if let Some(t) = options_tick {
1227 t.to_string()
1228 } else if let Some(cfg_tick) = self
1229 .builder_config
1230 .as_ref()
1231 .and_then(|c| c.tick_size.as_ref())
1232 {
1233 cfg_tick.clone()
1234 } else {
1235 "0.01".to_string()
1236 };
1237 let signed = self
1238 .create_market_order(user_market_order, Some(&tick))
1239 .await?;
1240 let order_type = order_type.unwrap_or(OrderType::FOK);
1241 self.post_signed_order(&signed, order_type, false).await
1242 }
1243
1244 pub async fn post_orders_typed(
1247 &self,
1248 orders: Vec<SignedOrder>,
1249 _defer_exec: bool,
1250 ) -> Result<Value, ClobError> {
1251 if self.creds.is_none() {
1252 return Err(ClobError::Other("L2 creds required".to_string()));
1253 }
1254 let signer_arc = self
1255 .signer
1256 .as_ref()
1257 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1258 let signer_ref: &EthersSigner = signer_arc.as_ref();
1259 let body_str =
1260 serde_json::to_string(&orders).map_err(|e| ClobError::Other(e.to_string()))?;
1261 let ts = if self.use_server_time {
1262 Some(self.get_server_time().await?)
1263 } else {
1264 None
1265 };
1266 let headers = crate::headers::create_l2_headers(
1267 signer_ref,
1268 self.creds.as_ref().unwrap(),
1269 "POST",
1270 POST_ORDERS,
1271 Some(&body_str),
1272 ts,
1273 )
1274 .await?;
1275 let headers = if let Some(b) = &self.builder_signer {
1276 let b_payload = b
1277 .create_builder_header_payload("POST", POST_ORDERS, Some(&body_str), None)
1278 .map_err(|e| ClobError::Other(format!("builder header error: {}", e)))?;
1279 crate::headers::inject_builder_headers(headers, &b_payload)
1280 } else {
1281 headers
1282 };
1283 let endpoint = format!("{}{}", self.host, POST_ORDERS);
1284 let res = crate::http_helpers::post_typed::<Value, Vec<SignedOrder>>(
1285 &endpoint,
1286 Some(RequestOptions {
1287 headers: Some(headers),
1288 data: Some(orders),
1289 params: None,
1290 }),
1291 )
1292 .await?;
1293 Ok(res)
1294 }
1295
1296 pub async fn post_orders_typed_parsed(
1300 &self,
1301 orders: Vec<SignedOrder>,
1302 _defer_exec: bool,
1303 ) -> Result<Vec<Order>, ClobError> {
1304 if self.creds.is_none() {
1306 return Err(ClobError::Other("L2 creds required".to_string()));
1307 }
1308 let signer_arc = self
1309 .signer
1310 .as_ref()
1311 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1312 let signer_ref: &EthersSigner = signer_arc.as_ref();
1313 let body_str =
1314 serde_json::to_string(&orders).map_err(|e| ClobError::Other(e.to_string()))?;
1315 let ts = if self.use_server_time {
1316 Some(self.get_server_time().await?)
1317 } else {
1318 None
1319 };
1320 let headers = crate::headers::create_l2_headers(
1321 signer_ref,
1322 self.creds.as_ref().unwrap(),
1323 "POST",
1324 POST_ORDERS,
1325 Some(&body_str),
1326 ts,
1327 )
1328 .await?;
1329 let headers = if let Some(b) = &self.builder_signer {
1330 let b_payload = b
1331 .create_builder_header_payload("POST", POST_ORDERS, Some(&body_str), None)
1332 .map_err(|e| ClobError::Other(format!("builder header error: {}", e)))?;
1333 crate::headers::inject_builder_headers(headers, &b_payload)
1334 } else {
1335 headers
1336 };
1337 let endpoint = format!("{}{}", self.host, POST_ORDERS);
1338 let raw: MaybeVec<Order> = crate::http_helpers::post_typed(
1339 &endpoint,
1340 Some(RequestOptions {
1341 headers: Some(headers),
1342 data: Some(orders),
1343 params: None,
1344 }),
1345 )
1346 .await?;
1347 Ok(raw.into_vec())
1348 }
1349
1350 pub async fn cancel_all(&self) -> Result<Vec<Order>, ClobError> {
1351 if self.creds.is_none() {
1352 return Err(ClobError::Other("L2 creds required".to_string()));
1353 }
1354 let signer_arc = self
1355 .signer
1356 .as_ref()
1357 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1358 let signer_ref: &EthersSigner = signer_arc.as_ref();
1359 let ts = if self.use_server_time {
1360 Some(self.get_server_time().await?)
1361 } else {
1362 None
1363 };
1364 let mut headers = crate::headers::create_l2_headers(
1365 signer_ref,
1366 self.creds.as_ref().unwrap(),
1367 "DELETE",
1368 CANCEL_ALL,
1369 None,
1370 ts,
1371 )
1372 .await?;
1373 if let Some(b) = &self.builder_signer {
1374 let b_payload = b
1375 .create_builder_header_payload("DELETE", CANCEL_ALL, None, None)
1376 .map_err(|e| ClobError::Other(format!("builder header error: {}", e)))?;
1377 headers = crate::headers::inject_builder_headers(headers, &b_payload);
1378 }
1379 let endpoint = format!("{}{}", self.host, CANCEL_ALL);
1380 let raw: MaybeVec<Order> = crate::http_helpers::del_typed(
1381 &endpoint,
1382 Some(RequestOptions::<Value> {
1383 headers: Some(headers),
1384 data: None,
1385 params: None,
1386 }),
1387 )
1388 .await?;
1389 Ok(raw.into_vec())
1390 }
1391
1392 pub async fn cancel_market_orders(
1393 &self,
1394 payload: OrderMarketCancelParams,
1395 ) -> Result<Vec<Order>, ClobError> {
1396 if self.creds.is_none() {
1397 return Err(ClobError::Other("L2 creds required".to_string()));
1398 }
1399 let signer_arc = self
1400 .signer
1401 .as_ref()
1402 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1403 let signer_ref: &EthersSigner = signer_arc.as_ref();
1404 let body_str =
1405 serde_json::to_string(&payload).map_err(|e| ClobError::Other(e.to_string()))?;
1406 let ts = if self.use_server_time {
1407 Some(self.get_server_time().await?)
1408 } else {
1409 None
1410 };
1411 let mut headers = crate::headers::create_l2_headers(
1412 signer_ref,
1413 self.creds.as_ref().unwrap(),
1414 "DELETE",
1415 CANCEL_MARKET_ORDERS,
1416 Some(&body_str),
1417 ts,
1418 )
1419 .await?;
1420 if let Some(b) = &self.builder_signer {
1421 let b_payload = b
1422 .create_builder_header_payload(
1423 "DELETE",
1424 CANCEL_MARKET_ORDERS,
1425 Some(&body_str),
1426 None,
1427 )
1428 .map_err(|e| ClobError::Other(format!("builder header error: {}", e)))?;
1429 headers = crate::headers::inject_builder_headers(headers, &b_payload);
1430 }
1431 let endpoint = format!("{}{}", self.host, CANCEL_MARKET_ORDERS);
1432 let body_val =
1434 serde_json::to_value(&payload).map_err(|e| ClobError::Other(e.to_string()))?;
1435 let raw: MaybeVec<Order> = crate::http_helpers::del_typed(
1436 &endpoint,
1437 Some(RequestOptions::<Value> {
1438 headers: Some(headers),
1439 data: Some(body_val),
1440 params: None,
1441 }),
1442 )
1443 .await?;
1444 Ok(raw.into_vec())
1445 }
1446
1447 pub async fn is_order_scoring(
1448 &self,
1449 params: Option<std::collections::HashMap<String, String>>,
1450 ) -> Result<crate::types::OrderScoring, ClobError> {
1451 if self.creds.is_none() {
1452 return Err(ClobError::Other("L2 creds required".to_string()));
1453 }
1454 let signer_arc = self
1455 .signer
1456 .as_ref()
1457 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1458 let signer_ref: &EthersSigner = signer_arc.as_ref();
1459 let mut headers = crate::headers::create_l2_headers(
1460 signer_ref,
1461 self.creds.as_ref().unwrap(),
1462 "GET",
1463 IS_ORDER_SCORING,
1464 None,
1465 if self.use_server_time {
1466 Some(self.get_server_time().await?)
1467 } else {
1468 None
1469 },
1470 )
1471 .await?;
1472 if let Some(b) = &self.builder_signer {
1473 let b_payload = b
1474 .create_builder_header_payload("GET", IS_ORDER_SCORING, None, None)
1475 .map_err(|e| ClobError::Other(format!("builder header error: {}", e)))?;
1476 headers = crate::headers::inject_builder_headers(headers, &b_payload);
1477 }
1478 let resp: crate::types::OrderScoring = crate::http_helpers::get_typed(
1479 &format!("{}{}", self.host, IS_ORDER_SCORING),
1480 Some(RequestOptions::<Value> {
1481 headers: Some(headers),
1482 data: None,
1483 params,
1484 }),
1485 )
1486 .await?;
1487 Ok(resp)
1488 }
1489
1490 pub async fn are_orders_scoring(
1491 &self,
1492 order_ids: Option<Vec<String>>,
1493 ) -> Result<crate::types::OrdersScoring, ClobError> {
1494 if self.creds.is_none() {
1495 return Err(ClobError::Other("L2 creds required".to_string()));
1496 }
1497 let signer_arc = self
1498 .signer
1499 .as_ref()
1500 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1501 let signer_ref: &EthersSigner = signer_arc.as_ref();
1502 let body_str =
1503 serde_json::to_string(&order_ids).map_err(|e| ClobError::Other(e.to_string()))?;
1504 let ts = if self.use_server_time {
1505 Some(self.get_server_time().await?)
1506 } else {
1507 None
1508 };
1509 let mut headers = crate::headers::create_l2_headers(
1510 signer_ref,
1511 self.creds.as_ref().unwrap(),
1512 "POST",
1513 ARE_ORDERS_SCORING,
1514 Some(&body_str),
1515 ts,
1516 )
1517 .await?;
1518 if let Some(b) = &self.builder_signer {
1519 let b_payload = b
1520 .create_builder_header_payload("POST", ARE_ORDERS_SCORING, Some(&body_str), None)
1521 .map_err(|e| ClobError::Other(format!("builder header error: {}", e)))?;
1522 headers = crate::headers::inject_builder_headers(headers, &b_payload);
1523 }
1524 let body_json =
1526 serde_json::to_value(&order_ids).map_err(|e| ClobError::Other(e.to_string()))?;
1527 let resp: crate::types::OrdersScoring = crate::http_helpers::post_typed(
1528 &format!("{}{}", self.host, ARE_ORDERS_SCORING),
1529 Some(RequestOptions::<Value> {
1530 headers: Some(headers),
1531 data: Some(body_json),
1532 params: None,
1533 }),
1534 )
1535 .await?;
1536 Ok(resp)
1537 }
1538
1539 pub async fn cancel_order_payload(
1541 &self,
1542 payload: OrderPayloadParity,
1543 ) -> Result<Order, ClobError> {
1544 if self.creds.is_none() {
1545 return Err(ClobError::Other("L2 creds required".to_string()));
1546 }
1547 let creds = self.creds.as_ref().unwrap();
1548 let signer_arc = self
1549 .signer
1550 .as_ref()
1551 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1552 let signer_ref: &EthersSigner = signer_arc.as_ref();
1553 let body_str =
1554 serde_json::to_string(&payload).map_err(|e| ClobError::Other(e.to_string()))?;
1555 let ts = if self.use_server_time {
1556 Some(self.get_server_time().await?)
1557 } else {
1558 None
1559 };
1560 let mut headers = crate::headers::create_l2_headers(
1561 signer_ref,
1562 creds,
1563 "DELETE",
1564 CANCEL_ORDER,
1565 Some(&body_str),
1566 ts,
1567 )
1568 .await?;
1569 if let Some(b) = &self.builder_signer {
1570 let b_payload = b
1571 .create_builder_header_payload("DELETE", CANCEL_ORDER, Some(&body_str), None)
1572 .map_err(|e| ClobError::Other(format!("builder header error: {}", e)))?;
1573 headers = crate::headers::inject_builder_headers(headers, &b_payload);
1574 }
1575 let endpoint = format!("{}{}", self.host, CANCEL_ORDER);
1576 let body_val =
1578 serde_json::to_value(&payload).map_err(|e| ClobError::Other(e.to_string()))?;
1579 let opts: RequestOptions<Value> = RequestOptions::<Value> {
1580 headers: Some(headers),
1581 data: Some(body_val),
1582 params: None,
1583 };
1584 let raw: MaybeItem<Order> = crate::http_helpers::del_typed(&endpoint, Some(opts)).await?;
1585 Ok(raw.into_item())
1586 }
1587
1588 pub async fn cancel_order_payload_raw(
1590 &self,
1591 payload: OrderPayloadParity,
1592 ) -> Result<Value, ClobError> {
1593 if self.creds.is_none() {
1594 return Err(ClobError::Other("L2 creds required".to_string()));
1595 }
1596 let creds = self.creds.as_ref().unwrap();
1597 let signer_arc = self
1598 .signer
1599 .as_ref()
1600 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1601 let signer_ref: &EthersSigner = signer_arc.as_ref();
1602 let body_str =
1603 serde_json::to_string(&payload).map_err(|e| ClobError::Other(e.to_string()))?;
1604 let ts = if self.use_server_time {
1605 Some(self.get_server_time().await?)
1606 } else {
1607 None
1608 };
1609 let mut headers = crate::headers::create_l2_headers(
1610 signer_ref,
1611 creds,
1612 "DELETE",
1613 CANCEL_ORDER,
1614 Some(&body_str),
1615 ts,
1616 )
1617 .await?;
1618 if let Some(b) = &self.builder_signer {
1619 let b_payload = b
1620 .create_builder_header_payload("DELETE", CANCEL_ORDER, Some(&body_str), None)
1621 .map_err(|e| ClobError::Other(format!("builder header error: {}", e)))?;
1622 headers = crate::headers::inject_builder_headers(headers, &b_payload);
1623 }
1624 let endpoint = format!("{}{}", self.host, CANCEL_ORDER);
1625 let body_val =
1626 serde_json::to_value(&payload).map_err(|e| ClobError::Other(e.to_string()))?;
1627 let opts: RequestOptions<Value> = RequestOptions::<Value> {
1628 headers: Some(headers),
1629 data: Some(body_val),
1630 params: None,
1631 };
1632 let raw: MaybeItem<Value> = crate::http_helpers::del_typed(&endpoint, Some(opts)).await?;
1633 Ok(raw.into_item())
1634 }
1635
1636 pub async fn cancel_orders(&self, order_ids: Vec<String>) -> Result<Vec<Order>, ClobError> {
1637 if self.creds.is_none() {
1638 return Err(ClobError::Other("L2 creds required".to_string()));
1639 }
1640 let creds = self.creds.as_ref().unwrap();
1641 let signer_arc = self
1642 .signer
1643 .as_ref()
1644 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1645 let signer_ref: &EthersSigner = signer_arc.as_ref();
1646 let body_str =
1647 serde_json::to_string(&order_ids).map_err(|e| ClobError::Other(e.to_string()))?;
1648 let ts = if self.use_server_time {
1649 Some(self.get_server_time().await?)
1650 } else {
1651 None
1652 };
1653 let mut headers = crate::headers::create_l2_headers(
1654 signer_ref,
1655 creds,
1656 "DELETE",
1657 CANCEL_ORDERS,
1658 Some(&body_str),
1659 ts,
1660 )
1661 .await?;
1662 if let Some(b) = &self.builder_signer {
1663 let b_payload = b
1664 .create_builder_header_payload("DELETE", CANCEL_ORDERS, Some(&body_str), None)
1665 .map_err(|e| ClobError::Other(format!("builder header error: {}", e)))?;
1666 headers = crate::headers::inject_builder_headers(headers, &b_payload);
1667 }
1668 let endpoint = format!("{}{}", self.host, CANCEL_ORDERS);
1669 let opts = RequestOptions {
1670 headers: Some(headers),
1671 data: Some(order_ids.clone()),
1672 params: None,
1673 };
1674 let raw: MaybeVec<Order> = crate::http_helpers::del_typed(&endpoint, Some(opts)).await?;
1675 Ok(raw.into_vec())
1676 }
1677
1678 pub async fn get_order(&self, order_id: &str) -> Result<OpenOrder, ClobError> {
1679 self.get_order_typed(order_id).await
1681 }
1682
1683 pub async fn get_order_typed(&self, order_id: &str) -> Result<OpenOrder, ClobError> {
1685 if self.creds.is_none() {
1687 return Err(ClobError::Other("L2 creds required".to_string()));
1688 }
1689 let signer_arc = self
1690 .signer
1691 .as_ref()
1692 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1693 let signer_ref: &EthersSigner = signer_arc.as_ref();
1694 let request_path = format!("{}{}", GET_ORDER, order_id);
1696 let endpoint = format!("{}{}", self.host, request_path);
1697 let ts = if self.use_server_time {
1698 Some(self.get_server_time().await?)
1699 } else {
1700 None
1701 };
1702 let headers = crate::headers::create_l2_headers(
1703 signer_ref,
1704 self.creds.as_ref().unwrap(),
1705 "GET",
1706 &request_path,
1707 None,
1708 ts,
1709 )
1710 .await?;
1711 let opts = RequestOptions {
1712 headers: Some(headers),
1713 data: None,
1714 params: None,
1715 };
1716 let val = get(&endpoint, Some(opts)).await?;
1717 if val.is_object() && val.get("id").is_some() {
1719 let o: OpenOrder =
1720 serde_json::from_value(val).map_err(|e| ClobError::Other(e.to_string()))?;
1721 Ok(o)
1722 } else if let Some(d) = val.get("data") {
1723 let o: OpenOrder =
1724 serde_json::from_value(d.clone()).map_err(|e| ClobError::Other(e.to_string()))?;
1725 Ok(o)
1726 } else {
1727 Err(ClobError::Other(
1728 "unexpected order response shape".to_string(),
1729 ))
1730 }
1731 }
1732
1733 pub async fn get_open_orders(
1734 &self,
1735 params: Option<std::collections::HashMap<String, String>>,
1736 ) -> Result<Vec<SignedOrder>, ClobError> {
1737 self.get_open_orders_typed(params).await
1739 }
1740
1741 pub async fn get_open_orders_typed(
1743 &self,
1744 params: Option<std::collections::HashMap<String, String>>,
1745 ) -> Result<Vec<SignedOrder>, ClobError> {
1746 let endpoint = format!("{}{}", self.host, GET_OPEN_ORDERS);
1747 let opts = RequestOptions {
1748 headers: None,
1749 data: None,
1750 params,
1751 };
1752 let val = get(&endpoint, Some(opts)).await?;
1753 let arr = if val.is_array() {
1755 val
1756 } else if let Some(d) = val.get("data") {
1757 d.clone()
1758 } else {
1759 return Err(ClobError::Other(
1760 "unexpected open orders response shape".to_string(),
1761 ));
1762 };
1763 let orders: Vec<SignedOrder> =
1764 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
1765 Ok(orders)
1766 }
1767
1768 pub async fn get_markets(
1769 &self,
1770 params: Option<std::collections::HashMap<String, String>>,
1771 ) -> Result<Vec<crate::types::Market>, ClobError> {
1772 let endpoint = format!("{}{}", self.host, GET_MARKETS);
1773 let opts = RequestOptions {
1774 headers: None,
1775 data: None,
1776 params,
1777 };
1778 let val = get(&endpoint, Some(opts)).await?;
1779 let arr = if val.is_array() {
1781 val
1782 } else if let Some(d) = val.get("data") {
1783 d.clone()
1784 } else {
1785 return Err(ClobError::Other(
1786 "unexpected markets response shape".to_string(),
1787 ));
1788 };
1789 let markets: Vec<crate::types::Market> =
1790 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
1791 Ok(markets)
1792 }
1793
1794 pub async fn get_market(
1795 &self,
1796 market_id: &str,
1797 params: Option<std::collections::HashMap<String, String>>,
1798 ) -> Result<crate::types::MarketSummary, ClobError> {
1799 let endpoint = format!("{}{}{}", self.host, GET_MARKET, market_id);
1800 let opts = RequestOptions {
1801 headers: None,
1802 data: None,
1803 params,
1804 };
1805 let val = get(&endpoint, Some(opts)).await?;
1806 if val.get("market").is_some() {
1808 let m: crate::types::MarketSummary =
1809 serde_json::from_value(val).map_err(|e| ClobError::Other(e.to_string()))?;
1810 Ok(m)
1811 } else if let Some(d) = val.get("data") {
1812 let m: crate::types::MarketSummary =
1813 serde_json::from_value(d.clone()).map_err(|e| ClobError::Other(e.to_string()))?;
1814 Ok(m)
1815 } else {
1816 Err(ClobError::Other(
1817 "unexpected market response shape".to_string(),
1818 ))
1819 }
1820 }
1821
1822 pub async fn get_simplified_markets(
1823 &self,
1824 params: Option<std::collections::HashMap<String, String>>,
1825 ) -> Result<Vec<crate::types::Market>, ClobError> {
1826 let endpoint = format!("{}{}", self.host, GET_SIMPLIFIED_MARKETS);
1827 let opts = RequestOptions {
1828 headers: None,
1829 data: None,
1830 params,
1831 };
1832 let val = get(&endpoint, Some(opts)).await?;
1833 let arr = if val.is_array() {
1834 val
1835 } else if let Some(d) = val.get("data") {
1836 d.clone()
1837 } else {
1838 return Err(ClobError::Other(
1839 "unexpected simplified markets response shape".to_string(),
1840 ));
1841 };
1842 let markets: Vec<crate::types::Market> =
1843 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
1844 Ok(markets)
1845 }
1846
1847 pub async fn get_sampling_markets(
1848 &self,
1849 params: Option<std::collections::HashMap<String, String>>,
1850 ) -> Result<Vec<crate::types::Market>, ClobError> {
1851 let endpoint = format!("{}{}", self.host, GET_SAMPLING_MARKETS);
1852 let opts = RequestOptions {
1853 headers: None,
1854 data: None,
1855 params,
1856 };
1857 let val = get(&endpoint, Some(opts)).await?;
1858 let arr = if val.is_array() {
1859 val
1860 } else if let Some(d) = val.get("data") {
1861 d.clone()
1862 } else {
1863 return Err(ClobError::Other(
1864 "unexpected sampling markets response shape".to_string(),
1865 ));
1866 };
1867 let markets: Vec<crate::types::Market> =
1868 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
1869 Ok(markets)
1870 }
1871
1872 pub async fn get_server_time(&self) -> Result<u64, ClobError> {
1873 let endpoint = format!("{}{}", self.host, TIME);
1874 let val = get(&endpoint, None).await?;
1875 if val.is_number() {
1877 Ok(val
1878 .as_u64()
1879 .ok_or(ClobError::Other("invalid time value".to_string()))?)
1880 } else if val.get("time").is_some() {
1881 Ok(val
1882 .get("time")
1883 .and_then(|v| v.as_u64())
1884 .ok_or(ClobError::Other("invalid time value".to_string()))?)
1885 } else {
1886 Err(ClobError::Other(
1887 "unexpected server time response".to_string(),
1888 ))
1889 }
1890 }
1891
1892 fn can_l1_auth(&self) -> Result<(), ClobError> {
1893 if self.signer.is_none() {
1894 return Err(ClobError::Other("L1 auth required".to_string()));
1895 }
1896 Ok(())
1897 }
1898
1899 pub async fn create_api_key(&self, nonce: Option<u64>) -> Result<ApiKeyCreds, ClobError> {
1900 self.can_l1_auth()?;
1901 let signer_arc = self.signer.as_ref().unwrap();
1902 let signer_ref: &EthersSigner = signer_arc.as_ref();
1903 let ts = if self.use_server_time {
1904 Some(self.get_server_time().await?)
1905 } else {
1906 None
1907 };
1908 let headers =
1909 crate::headers::create_l1_headers(signer_ref, self.chain_id as i32, nonce, ts).await?;
1910 let endpoint = format!("{}{}", self.host, CREATE_API_KEY);
1911 let opts = RequestOptions {
1912 headers: Some(headers),
1913 data: None,
1914 params: None,
1915 };
1916 let val = post(&endpoint, Some(opts)).await?;
1917 let api_raw: ApiKeyRaw =
1919 serde_json::from_value(val).map_err(|e| ClobError::Other(e.to_string()))?;
1920 let api_key = ApiKeyCreds {
1921 key: api_raw.api_key,
1922 secret: api_raw.secret,
1923 passphrase: api_raw.passphrase,
1924 };
1925 Ok(api_key)
1926 }
1927
1928 pub async fn derive_api_key(
1930 &self,
1931 params: Option<std::collections::HashMap<String, String>>,
1932 ) -> Result<ApiKeyCreds, ClobError> {
1933 self.can_l1_auth()?;
1934 let signer_arc = self.signer.as_ref().unwrap();
1935 let signer_ref: &EthersSigner = signer_arc.as_ref();
1936 let ts = if self.use_server_time {
1937 Some(self.get_server_time().await?)
1938 } else {
1939 None
1940 };
1941 let headers =
1942 crate::headers::create_l1_headers(signer_ref, self.chain_id as i32, None, ts).await?;
1943 let endpoint = format!("{}{}", self.host, DERIVE_API_KEY);
1944 let opts = RequestOptions {
1945 headers: Some(headers),
1946 data: None,
1947 params,
1948 };
1949 let val = get(&endpoint, Some(opts)).await?;
1950 let api_raw: ApiKeyRaw =
1952 serde_json::from_value(val).map_err(|e| ClobError::Other(e.to_string()))?;
1953 let api_key = ApiKeyCreds {
1954 key: api_raw.api_key,
1955 secret: api_raw.secret,
1956 passphrase: api_raw.passphrase,
1957 };
1958 Ok(api_key)
1959 }
1960
1961 pub async fn create_builder_api_key(&self) -> Result<crate::types::ApiKeyCreds, ClobError> {
1962 if self.creds.is_none() {
1963 return Err(ClobError::Other("L2 creds required".to_string()));
1964 }
1965 let signer_arc = self
1966 .signer
1967 .as_ref()
1968 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
1969 let signer_ref: &EthersSigner = signer_arc.as_ref();
1970 let ts = if self.use_server_time {
1971 Some(self.get_server_time().await?)
1972 } else {
1973 None
1974 };
1975 let headers = crate::headers::create_l2_headers(
1976 signer_ref,
1977 self.creds.as_ref().unwrap(),
1978 "POST",
1979 CREATE_BUILDER_API_KEY,
1980 None,
1981 ts,
1982 )
1983 .await?;
1984 let endpoint = format!("{}{}", self.host, CREATE_BUILDER_API_KEY);
1985 let resp: crate::types::ApiKeyCreds = crate::http_helpers::post_typed(
1986 &endpoint,
1987 Some(RequestOptions::<Value> {
1988 headers: Some(headers),
1989 data: None,
1990 params: None,
1991 }),
1992 )
1993 .await?;
1994 Ok(resp)
1995 }
1996
1997 pub async fn get_builder_api_keys(&self) -> Result<Vec<crate::types::ApiKeyCreds>, ClobError> {
1998 if self.creds.is_none() {
1999 return Err(ClobError::Other("L2 creds required".to_string()));
2000 }
2001 let signer_arc = self
2002 .signer
2003 .as_ref()
2004 .ok_or(ClobError::Other("L1 signer required".to_string()))?;
2005 let signer_ref: &EthersSigner = signer_arc.as_ref();
2006 let ts = if self.use_server_time {
2007 Some(self.get_server_time().await?)
2008 } else {
2009 None
2010 };
2011 let headers = crate::headers::create_l2_headers(
2012 signer_ref,
2013 self.creds.as_ref().unwrap(),
2014 "GET",
2015 GET_BUILDER_API_KEYS,
2016 None,
2017 ts,
2018 )
2019 .await?;
2020 let endpoint = format!("{}{}", self.host, GET_BUILDER_API_KEYS);
2021 let resp: Vec<crate::types::ApiKeyCreds> = crate::http_helpers::get_typed(
2022 &endpoint,
2023 Some(RequestOptions::<Value> {
2024 headers: Some(headers),
2025 data: None,
2026 params: None,
2027 }),
2028 )
2029 .await?;
2030 Ok(resp)
2031 }
2032
2033 pub async fn revoke_builder_api_key(&self) -> Result<(), ClobError> {
2034 if self.builder_signer.is_none() {
2035 return Err(ClobError::Other("Builder signer required".to_string()));
2036 }
2037 let b_signer = self.builder_signer.as_ref().unwrap();
2038 let headers_map = b_signer
2039 .create_builder_header_payload("DELETE", REVOKE_BUILDER_API_KEY, None, None)
2040 .map_err(|e| ClobError::Other(format!("builder header error: {}", e)))?;
2041
2042 let mut headers = crate::headers::Headers::new();
2043 for (k, v) in headers_map {
2044 headers.insert(k, v);
2045 }
2046
2047 let endpoint = format!("{}{}", self.host, REVOKE_BUILDER_API_KEY);
2048 let _val: () = crate::http_helpers::del_typed::<(), Value>(
2049 &endpoint,
2050 Some(RequestOptions::<Value> {
2051 headers: Some(headers),
2052 data: None,
2053 params: None,
2054 }),
2055 )
2056 .await?;
2057 Ok(())
2058 }
2059
2060 pub async fn get_sampling_simplified_markets(
2061 &self,
2062 params: Option<std::collections::HashMap<String, String>>,
2063 ) -> Result<Vec<crate::types::Market>, ClobError> {
2064 let endpoint = format!("{}{}", self.host, GET_SAMPLING_SIMPLIFIED_MARKETS);
2065 let opts = RequestOptions {
2066 headers: None,
2067 data: None,
2068 params,
2069 };
2070 let val = get(&endpoint, Some(opts)).await?;
2071 let arr = if val.is_object() && val.get("data").is_some() {
2072 val.get("data").cloned().unwrap_or_default()
2073 } else if val.is_array() {
2074 val
2075 } else {
2076 return Err(ClobError::Other(
2077 "unexpected sampling simplified markets response shape".to_string(),
2078 ));
2079 };
2080 let markets: Vec<crate::types::Market> =
2081 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2082 Ok(markets)
2083 }
2084
2085 pub async fn get_order_books(
2086 &self,
2087 params: Option<std::collections::HashMap<String, String>>,
2088 ) -> Result<Vec<crate::types::OrderBookSummary>, ClobError> {
2089 let endpoint = format!("{}{}", self.host, GET_ORDER_BOOKS);
2090 let opts = RequestOptions {
2091 headers: None,
2092 data: None,
2093 params,
2094 };
2095 let val = get(&endpoint, Some(opts)).await?;
2096 let arr = if val.is_array() {
2097 val
2098 } else if let Some(d) = val.get("data") {
2099 d.clone()
2100 } else {
2101 return Err(ClobError::Other(
2102 "unexpected order books response shape".to_string(),
2103 ));
2104 };
2105 let books: Vec<crate::types::OrderBookSummary> =
2106 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2107 Ok(books)
2108 }
2109
2110 pub async fn get_midpoint(
2111 &self,
2112 params: Option<std::collections::HashMap<String, String>>,
2113 ) -> Result<Vec<crate::types::MarketPrice>, ClobError> {
2114 let endpoint = format!("{}{}", self.host, GET_MIDPOINT);
2115 let opts = RequestOptions {
2116 headers: None,
2117 data: None,
2118 params,
2119 };
2120 let val = get(&endpoint, Some(opts)).await?;
2121 let arr = if val.is_array() {
2122 val
2123 } else if let Some(d) = val.get("data") {
2124 d.clone()
2125 } else {
2126 return Err(ClobError::Other(
2127 "unexpected midpoint response shape".to_string(),
2128 ));
2129 };
2130 let prices: Vec<crate::types::MarketPrice> =
2131 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2132 Ok(prices)
2133 }
2134
2135 pub async fn get_midpoints(
2136 &self,
2137 params: Option<std::collections::HashMap<String, String>>,
2138 ) -> Result<Vec<crate::types::MarketPrice>, ClobError> {
2139 let endpoint = format!("{}{}", self.host, GET_MIDPOINTS);
2140 let opts = RequestOptions {
2141 headers: None,
2142 data: None,
2143 params,
2144 };
2145 let val = get(&endpoint, Some(opts)).await?;
2146 let arr = if val.is_array() {
2147 val
2148 } else if let Some(d) = val.get("data") {
2149 d.clone()
2150 } else {
2151 return Err(ClobError::Other(
2152 "unexpected midpoints response shape".to_string(),
2153 ));
2154 };
2155 let prices: Vec<crate::types::MarketPrice> =
2156 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2157 Ok(prices)
2158 }
2159
2160 pub async fn get_prices(
2161 &self,
2162 params: Option<std::collections::HashMap<String, String>>,
2163 ) -> Result<Vec<crate::types::MarketPrice>, ClobError> {
2164 let endpoint = format!("{}{}", self.host, GET_PRICES);
2165 let opts = RequestOptions {
2166 headers: None,
2167 data: None,
2168 params,
2169 };
2170 let val = get(&endpoint, Some(opts)).await?;
2171 let arr = if val.is_array() {
2172 val
2173 } else if let Some(d) = val.get("data") {
2174 d.clone()
2175 } else {
2176 return Err(ClobError::Other(
2177 "unexpected prices response shape".to_string(),
2178 ));
2179 };
2180 let prices: Vec<crate::types::MarketPrice> =
2181 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2182 Ok(prices)
2183 }
2184
2185 pub async fn get_spreads(
2186 &self,
2187 params: Option<std::collections::HashMap<String, String>>,
2188 ) -> Result<Vec<crate::types::MarketPrice>, ClobError> {
2189 let endpoint = format!("{}{}", self.host, GET_SPREADS);
2190 let opts = RequestOptions {
2191 headers: None,
2192 data: None,
2193 params,
2194 };
2195 let val = get(&endpoint, Some(opts)).await?;
2196 let arr = if val.is_array() {
2197 val
2198 } else if let Some(d) = val.get("data") {
2199 d.clone()
2200 } else {
2201 return Err(ClobError::Other(
2202 "unexpected spreads response shape".to_string(),
2203 ));
2204 };
2205 let prices: Vec<crate::types::MarketPrice> =
2206 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2207 Ok(prices)
2208 }
2209
2210 pub async fn get_last_trades_prices(
2211 &self,
2212 params: Option<std::collections::HashMap<String, String>>,
2213 ) -> Result<Vec<crate::types::MarketPrice>, ClobError> {
2214 let endpoint = format!("{}{}", self.host, GET_LAST_TRADES_PRICES);
2215 let opts = RequestOptions {
2216 headers: None,
2217 data: None,
2218 params,
2219 };
2220 let val = get(&endpoint, Some(opts)).await?;
2221 let arr = if val.is_array() {
2222 val
2223 } else if let Some(d) = val.get("data") {
2224 d.clone()
2225 } else {
2226 return Err(ClobError::Other(
2227 "unexpected last trades prices response shape".to_string(),
2228 ));
2229 };
2230 let prices: Vec<crate::types::MarketPrice> =
2231 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2232 Ok(prices)
2233 }
2234
2235 pub async fn get_prices_history(
2236 &self,
2237 params: Option<std::collections::HashMap<String, String>>,
2238 ) -> Result<Vec<crate::types::MarketPrice>, ClobError> {
2239 let endpoint = format!("{}{}", self.host, GET_PRICES_HISTORY);
2240 let opts = RequestOptions {
2241 headers: None,
2242 data: None,
2243 params,
2244 };
2245 let val = get(&endpoint, Some(opts)).await?;
2246 let arr = if val.is_array() {
2248 val
2249 } else if let Some(d) = val.get("data") {
2250 d.clone()
2251 } else {
2252 return Err(ClobError::Other(
2253 "unexpected prices history response shape".to_string(),
2254 ));
2255 };
2256 let prices: Vec<crate::types::MarketPrice> =
2257 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2258 Ok(prices)
2259 }
2260
2261 pub async fn get_market_trades_events(
2262 &self,
2263 market_id: &str,
2264 params: Option<std::collections::HashMap<String, String>>,
2265 ) -> Result<Vec<crate::types::Trade>, ClobError> {
2266 let endpoint = format!("{}{}{}", self.host, GET_MARKET_TRADES_EVENTS, market_id);
2267 let opts = RequestOptions {
2268 headers: None,
2269 data: None,
2270 params,
2271 };
2272 let val = get(&endpoint, Some(opts)).await?;
2273 let arr = if val.is_array() {
2274 val
2275 } else if let Some(d) = val.get("data") {
2276 d.clone()
2277 } else {
2278 return Err(ClobError::Other(
2279 "unexpected market trades events response shape".to_string(),
2280 ));
2281 };
2282 let trades: Vec<crate::types::Trade> =
2283 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2284 Ok(trades)
2285 }
2286
2287 pub async fn get_earnings_for_user_for_day(
2289 &self,
2290 params: Option<std::collections::HashMap<String, String>>,
2291 ) -> Result<Vec<crate::types::Reward>, ClobError> {
2292 let endpoint = format!("{}{}", self.host, GET_EARNINGS_FOR_USER_FOR_DAY);
2293 let opts = RequestOptions {
2294 headers: None,
2295 data: None,
2296 params,
2297 };
2298 let val = get(&endpoint, Some(opts)).await?;
2299 let arr = if val.is_array() {
2300 val
2301 } else if let Some(d) = val.get("data") {
2302 d.clone()
2303 } else {
2304 return Err(ClobError::Other(
2305 "unexpected earnings response shape".to_string(),
2306 ));
2307 };
2308 let rewards: Vec<crate::types::Reward> =
2309 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2310 Ok(rewards)
2311 }
2312
2313 pub async fn get_total_earnings_for_user_for_day(
2314 &self,
2315 params: Option<std::collections::HashMap<String, String>>,
2316 ) -> Result<Vec<crate::types::Reward>, ClobError> {
2317 let endpoint = format!("{}{}", self.host, GET_TOTAL_EARNINGS_FOR_USER_FOR_DAY);
2318 let opts = RequestOptions {
2319 headers: None,
2320 data: None,
2321 params,
2322 };
2323 let val = get(&endpoint, Some(opts)).await?;
2324 let arr = if val.is_array() {
2325 val
2326 } else if let Some(d) = val.get("data") {
2327 d.clone()
2328 } else {
2329 return Err(ClobError::Other(
2330 "unexpected total earnings response shape".to_string(),
2331 ));
2332 };
2333 let rewards: Vec<crate::types::Reward> =
2334 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2335 Ok(rewards)
2336 }
2337
2338 pub async fn get_total_earnings_for_user_for_day_typed(
2340 &self,
2341 params: Option<std::collections::HashMap<String, String>>,
2342 ) -> Result<Vec<crate::types::Reward>, ClobError> {
2343 let val = self.get_total_earnings_for_user_for_day(params).await?;
2344 Ok(val)
2345 }
2346
2347 pub async fn get_liquidity_reward_percentages(
2348 &self,
2349 params: Option<std::collections::HashMap<String, String>>,
2350 ) -> Result<std::collections::HashMap<String, f64>, ClobError> {
2351 let endpoint = format!("{}{}", self.host, GET_LIQUIDITY_REWARD_PERCENTAGES);
2352 let opts = RequestOptions {
2353 headers: None,
2354 data: None,
2355 params,
2356 };
2357 let val = get(&endpoint, Some(opts)).await?;
2358 let obj = if val.is_object() {
2360 val
2361 } else if let Some(d) = val.get("data") {
2362 d.clone()
2363 } else {
2364 return Err(ClobError::Other(
2365 "unexpected liquidity percentages response shape".to_string(),
2366 ));
2367 };
2368 let map: std::collections::HashMap<String, f64> =
2369 serde_json::from_value(obj).map_err(|e| ClobError::Other(e.to_string()))?;
2370 Ok(map)
2371 }
2372
2373 pub async fn get_liquidity_reward_percentages_typed(
2375 &self,
2376 params: Option<std::collections::HashMap<String, String>>,
2377 ) -> Result<std::collections::HashMap<String, f64>, ClobError> {
2378 let val = self.get_liquidity_reward_percentages(params).await?;
2379 Ok(val)
2380 }
2381
2382 pub async fn get_rewards_markets_current(
2383 &self,
2384 params: Option<std::collections::HashMap<String, String>>,
2385 ) -> Result<Vec<crate::types::Reward>, ClobError> {
2386 let endpoint = format!("{}{}", self.host, GET_REWARDS_MARKETS_CURRENT);
2387 let opts = RequestOptions {
2388 headers: None,
2389 data: None,
2390 params,
2391 };
2392 let val = get(&endpoint, Some(opts)).await?;
2393 let arr = if val.is_array() {
2394 val
2395 } else if let Some(d) = val.get("data") {
2396 d.clone()
2397 } else {
2398 return Err(ClobError::Other(
2399 "unexpected rewards markets response shape".to_string(),
2400 ));
2401 };
2402 let rewards: Vec<crate::types::Reward> =
2403 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2404 Ok(rewards)
2405 }
2406
2407 pub async fn get_rewards_markets(
2408 &self,
2409 market_id: &str,
2410 params: Option<std::collections::HashMap<String, String>>,
2411 ) -> Result<Vec<crate::types::Reward>, ClobError> {
2412 let endpoint = format!("{}{}{}", self.host, GET_REWARDS_MARKETS, market_id);
2413 let opts = RequestOptions {
2414 headers: None,
2415 data: None,
2416 params,
2417 };
2418 let val = get(&endpoint, Some(opts)).await?;
2419 let arr = if val.is_array() {
2420 val
2421 } else if let Some(d) = val.get("data") {
2422 d.clone()
2423 } else {
2424 return Err(ClobError::Other(
2425 "unexpected rewards markets response shape".to_string(),
2426 ));
2427 };
2428 let rewards: Vec<crate::types::Reward> =
2429 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2430 Ok(rewards)
2431 }
2432
2433 pub async fn get_rewards_markets_typed(
2435 &self,
2436 market_id: &str,
2437 params: Option<std::collections::HashMap<String, String>>,
2438 ) -> Result<Vec<crate::types::Reward>, ClobError> {
2439 let val = self.get_rewards_markets(market_id, params).await?;
2440 Ok(val)
2441 }
2442
2443 pub async fn get_rewards_earnings_percentages(
2444 &self,
2445 params: Option<std::collections::HashMap<String, String>>,
2446 ) -> Result<Vec<crate::types::Reward>, ClobError> {
2447 let endpoint = format!("{}{}", self.host, GET_REWARDS_EARNINGS_PERCENTAGES);
2448 let opts = RequestOptions {
2449 headers: None,
2450 data: None,
2451 params,
2452 };
2453 let val = get(&endpoint, Some(opts)).await?;
2454 let arr = if val.is_array() {
2455 val
2456 } else if let Some(d) = val.get("data") {
2457 d.clone()
2458 } else {
2459 return Err(ClobError::Other(
2460 "unexpected rewards earnings percentages response shape".to_string(),
2461 ));
2462 };
2463 let rewards: Vec<crate::types::Reward> =
2464 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2465 Ok(rewards)
2466 }
2467
2468 pub async fn get_rewards_earnings_percentages_typed(
2470 &self,
2471 params: Option<std::collections::HashMap<String, String>>,
2472 ) -> Result<Vec<crate::types::Reward>, ClobError> {
2473 let val = self.get_rewards_earnings_percentages(params).await?;
2474 Ok(val)
2475 }
2476
2477 pub async fn get_builder_trades(
2478 &self,
2479 params: Option<std::collections::HashMap<String, String>>,
2480 ) -> Result<Vec<crate::types::BuilderTrade>, ClobError> {
2481 if self.builder_signer.is_none() {
2482 return Err(ClobError::Other("Builder signer required".to_string()));
2483 }
2484 let b_signer = self.builder_signer.as_ref().unwrap();
2485 let headers_map = b_signer
2486 .create_builder_header_payload("GET", GET_BUILDER_TRADES, None, None)
2487 .map_err(|e| ClobError::Other(format!("builder header error: {}", e)))?;
2488
2489 let mut headers = crate::headers::Headers::new();
2491 for (k, v) in headers_map {
2492 headers.insert(k, v);
2493 }
2494
2495 let endpoint = format!("{}{}", self.host, GET_BUILDER_TRADES);
2496 let opts = RequestOptions {
2497 headers: Some(headers),
2498 data: None,
2499 params,
2500 };
2501 let val = get(&endpoint, Some(opts)).await?;
2502 let arr = if val.is_array() {
2503 val
2504 } else if let Some(d) = val.get("data") {
2505 d.clone()
2506 } else {
2507 return Err(ClobError::Other(
2508 "unexpected builder trades response shape".to_string(),
2509 ));
2510 };
2511 let trades: Vec<crate::types::BuilderTrade> =
2512 serde_json::from_value(arr).map_err(|e| ClobError::Other(e.to_string()))?;
2513 Ok(trades)
2514 }
2515
2516 pub async fn get_builder_trades_typed(
2518 &self,
2519 params: Option<std::collections::HashMap<String, String>>,
2520 ) -> Result<Vec<crate::types::BuilderTrade>, ClobError> {
2521 self.get_builder_trades(params).await
2522 }
2523
2524 pub async fn get_earnings_for_user_for_day_typed(
2526 &self,
2527 params: Option<std::collections::HashMap<String, String>>,
2528 ) -> Result<Vec<crate::types::Reward>, ClobError> {
2529 let val = self.get_earnings_for_user_for_day(params).await?;
2530 Ok(val)
2531 }
2532
2533 pub async fn get_rewards_markets_current_typed(
2535 &self,
2536 params: Option<std::collections::HashMap<String, String>>,
2537 ) -> Result<Vec<crate::types::Reward>, ClobError> {
2538 let val = self.get_rewards_markets_current(params).await?;
2539 Ok(val)
2540 }
2541}