1use serde_json::{json, Value};
6use std::sync::Arc;
7
8use crate::client::HyperliquidSDKInner;
9use crate::error::Result;
10
11pub struct HyperCore {
13 inner: Arc<HyperliquidSDKInner>,
14}
15
16impl HyperCore {
17 pub(crate) fn new(inner: Arc<HyperliquidSDKInner>) -> Self {
18 Self { inner }
19 }
20
21 fn hypercore_url(&self) -> String {
23 self.inner.hypercore_url()
24 }
25
26 async fn rpc(&self, method: &str, params: Value) -> Result<Value> {
28 let url = self.hypercore_url();
29
30 let body = json!({
31 "jsonrpc": "2.0",
32 "method": method,
33 "params": params,
34 "id": 1,
35 });
36
37 let response = self
38 .inner
39 .http_client
40 .post(&url)
41 .json(&body)
42 .send()
43 .await?;
44
45 let status = response.status();
46 let text = response.text().await?;
47
48 if !status.is_success() {
49 return Err(crate::Error::NetworkError(format!(
50 "HyperCore request failed {}: {}",
51 status, text
52 )));
53 }
54
55 let result: Value = serde_json::from_str(&text)?;
56
57 if let Some(error) = result.get("error") {
59 let message = error
60 .get("message")
61 .and_then(|m| m.as_str())
62 .unwrap_or("Unknown error");
63 return Err(crate::Error::ApiError {
64 code: crate::error::ErrorCode::Unknown,
65 message: message.to_string(),
66 guidance: "Check your HyperCore request parameters.".to_string(),
67 raw: Some(error.to_string()),
68 });
69 }
70
71 Ok(result.get("result").cloned().unwrap_or(Value::Null))
72 }
73
74 pub async fn latest_block_number(&self, stream: Option<&str>) -> Result<u64> {
80 let stream = stream.unwrap_or("trades");
81 let result = self.rpc("hl_getLatestBlockNumber", json!({"stream": stream})).await?;
82 result
83 .as_u64()
84 .ok_or_else(|| crate::Error::JsonError("Invalid block number".to_string()))
85 }
86
87 pub async fn get_block(&self, block_number: u64, stream: Option<&str>) -> Result<Value> {
89 let stream = stream.unwrap_or("trades");
90 self.rpc("hl_getBlock", json!([stream, block_number])).await
91 }
92
93 pub async fn get_batch_blocks(
95 &self,
96 from_block: u64,
97 to_block: u64,
98 stream: Option<&str>,
99 ) -> Result<Value> {
100 let stream = stream.unwrap_or("trades");
101 self.rpc("hl_getBatchBlocks", json!({"stream": stream, "from": from_block, "to": to_block}))
102 .await
103 }
104
105 pub async fn latest_blocks(&self, stream: Option<&str>, count: Option<u32>) -> Result<Value> {
107 let stream = stream.unwrap_or("trades");
108 let count = count.unwrap_or(10);
109 self.rpc("hl_getLatestBlocks", json!({"stream": stream, "count": count})).await
110 }
111
112 pub async fn latest_trades(&self, count: Option<u32>, coin: Option<&str>) -> Result<Value> {
119 let count = count.unwrap_or(10);
120 let blocks = self.latest_blocks(Some("trades"), Some(count)).await?;
121
122 let mut trades = Vec::new();
123 if let Some(blocks_arr) = blocks.get("blocks").and_then(|b| b.as_array()) {
124 for block in blocks_arr {
125 if let Some(events) = block.get("events").and_then(|e| e.as_array()) {
126 for event in events {
127 if let Some(arr) = event.as_array() {
128 if arr.len() >= 2 {
129 let user = &arr[0];
130 if let Some(trade) = arr.get(1) {
131 if let Some(c) = coin {
133 if trade.get("coin").and_then(|tc| tc.as_str()) != Some(c) {
134 continue;
135 }
136 }
137 let mut trade_obj = trade.clone();
138 if let Some(obj) = trade_obj.as_object_mut() {
139 obj.insert("user".to_string(), user.clone());
140 }
141 trades.push(trade_obj);
142 }
143 }
144 }
145 }
146 }
147 }
148 }
149 Ok(json!(trades))
150 }
151
152 pub async fn latest_orders(&self, count: Option<u32>) -> Result<Value> {
154 let count = count.unwrap_or(10);
155 let blocks = self.latest_blocks(Some("orders"), Some(count)).await?;
156
157 let mut orders = Vec::new();
158 if let Some(blocks_arr) = blocks.get("blocks").and_then(|b| b.as_array()) {
159 for block in blocks_arr {
160 if let Some(events) = block.get("events").and_then(|e| e.as_array()) {
161 for event in events {
162 if let Some(arr) = event.as_array() {
163 if arr.len() >= 2 {
164 let user = &arr[0];
165 if let Some(order) = arr.get(1) {
166 let mut order_obj = order.clone();
167 if let Some(obj) = order_obj.as_object_mut() {
168 obj.insert("user".to_string(), user.clone());
169 }
170 orders.push(order_obj);
171 }
172 }
173 }
174 }
175 }
176 }
177 }
178 Ok(json!(orders))
179 }
180
181 pub async fn latest_book_updates(&self, count: Option<u32>, coin: Option<&str>) -> Result<Value> {
183 let count = count.unwrap_or(10);
184 let blocks = self.latest_blocks(Some("book_updates"), Some(count)).await?;
185
186 let mut updates = Vec::new();
187 if let Some(blocks_arr) = blocks.get("blocks").and_then(|b| b.as_array()) {
188 for block in blocks_arr {
189 if let Some(events) = block.get("events").and_then(|e| e.as_array()) {
190 for event in events {
191 if let Some(c) = coin {
193 if event.get("coin").and_then(|ec| ec.as_str()) != Some(c) {
194 continue;
195 }
196 }
197 updates.push(event.clone());
198 }
199 }
200 }
201 }
202 Ok(json!(updates))
203 }
204
205 pub async fn latest_twap(&self, count: Option<u32>) -> Result<Value> {
207 let count = count.unwrap_or(10);
208 self.latest_blocks(Some("twap"), Some(count)).await
209 }
210
211 pub async fn latest_events(&self, count: Option<u32>) -> Result<Value> {
213 let count = count.unwrap_or(10);
214 self.latest_blocks(Some("events"), Some(count)).await
215 }
216
217 pub async fn list_dexes(&self) -> Result<Value> {
223 self.rpc("hl_listDexes", json!({})).await
224 }
225
226 pub async fn list_markets(&self, dex: Option<&str>) -> Result<Value> {
228 if let Some(d) = dex {
229 self.rpc("hl_listMarkets", json!({"dex": d})).await
230 } else {
231 self.rpc("hl_listMarkets", json!({})).await
232 }
233 }
234
235 pub async fn open_orders(&self, user: &str) -> Result<Value> {
241 self.rpc("hl_openOrders", json!({"user": user})).await
242 }
243
244 pub async fn order_status(&self, user: &str, oid: u64) -> Result<Value> {
246 self.rpc("hl_orderStatus", json!({"user": user, "oid": oid}))
247 .await
248 }
249
250 pub async fn preflight(
252 &self,
253 coin: &str,
254 is_buy: bool,
255 limit_px: &str,
256 sz: &str,
257 user: &str,
258 reduce_only: bool,
259 order_type: Option<&Value>,
260 ) -> Result<Value> {
261 let mut params = json!({
262 "coin": coin,
263 "isBuy": is_buy,
264 "limitPx": limit_px,
265 "sz": sz,
266 "user": user,
267 "reduceOnly": reduce_only,
268 });
269 if let Some(ot) = order_type {
270 params["orderType"] = ot.clone();
271 }
272 self.rpc("hl_preflight", params).await
273 }
274
275 pub async fn get_max_builder_fee(&self, user: &str, builder: &str) -> Result<Value> {
281 self.rpc("hl_getMaxBuilderFee", json!({"user": user, "builder": builder}))
282 .await
283 }
284
285 pub async fn build_order(
291 &self,
292 coin: &str,
293 is_buy: bool,
294 limit_px: &str,
295 sz: &str,
296 user: &str,
297 reduce_only: bool,
298 order_type: Option<&Value>,
299 cloid: Option<&str>,
300 ) -> Result<Value> {
301 let mut params = json!({
302 "coin": coin,
303 "isBuy": is_buy,
304 "limitPx": limit_px,
305 "sz": sz,
306 "user": user,
307 "reduceOnly": reduce_only,
308 });
309 if let Some(ot) = order_type {
310 params["orderType"] = ot.clone();
311 }
312 if let Some(c) = cloid {
313 params["cloid"] = json!(c);
314 }
315 self.rpc("hl_buildOrder", params).await
316 }
317
318 pub async fn build_cancel(&self, coin: &str, oid: u64, user: &str) -> Result<Value> {
320 self.rpc("hl_buildCancel", json!({"coin": coin, "oid": oid, "user": user}))
321 .await
322 }
323
324 pub async fn build_modify(
326 &self,
327 coin: &str,
328 oid: u64,
329 user: &str,
330 limit_px: Option<&str>,
331 sz: Option<&str>,
332 is_buy: Option<bool>,
333 ) -> Result<Value> {
334 let mut params = json!({"coin": coin, "oid": oid, "user": user});
335 if let Some(px) = limit_px {
336 params["limitPx"] = json!(px);
337 }
338 if let Some(s) = sz {
339 params["sz"] = json!(s);
340 }
341 if let Some(buy) = is_buy {
342 params["isBuy"] = json!(buy);
343 }
344 self.rpc("hl_buildModify", params).await
345 }
346
347 pub async fn build_approve_builder_fee(
349 &self,
350 user: &str,
351 builder: &str,
352 max_fee_rate: &str,
353 nonce: u64,
354 ) -> Result<Value> {
355 self.rpc(
356 "hl_buildApproveBuilderFee",
357 json!({
358 "user": user,
359 "builder": builder,
360 "maxFeeRate": max_fee_rate,
361 "nonce": nonce,
362 }),
363 )
364 .await
365 }
366
367 pub async fn build_revoke_builder_fee(
369 &self,
370 user: &str,
371 builder: &str,
372 nonce: u64,
373 ) -> Result<Value> {
374 self.rpc(
375 "hl_buildRevokeBuilderFee",
376 json!({
377 "user": user,
378 "builder": builder,
379 "nonce": nonce,
380 }),
381 )
382 .await
383 }
384
385 pub async fn send_order(
391 &self,
392 action: &Value,
393 signature: &str,
394 nonce: u64,
395 ) -> Result<Value> {
396 self.rpc(
397 "hl_sendOrder",
398 json!({"action": action, "signature": signature, "nonce": nonce}),
399 )
400 .await
401 }
402
403 pub async fn send_cancel(
405 &self,
406 action: &Value,
407 signature: &str,
408 nonce: u64,
409 ) -> Result<Value> {
410 self.rpc(
411 "hl_sendCancel",
412 json!({"action": action, "signature": signature, "nonce": nonce}),
413 )
414 .await
415 }
416
417 pub async fn send_modify(
419 &self,
420 action: &Value,
421 signature: &str,
422 nonce: u64,
423 ) -> Result<Value> {
424 self.rpc(
425 "hl_sendModify",
426 json!({"action": action, "signature": signature, "nonce": nonce}),
427 )
428 .await
429 }
430
431 pub async fn send_approval(&self, action: &Value, signature: &str) -> Result<Value> {
433 self.rpc(
434 "hl_sendApproval",
435 json!({"action": action, "signature": signature}),
436 )
437 .await
438 }
439
440 pub async fn send_revocation(&self, action: &Value, signature: &str) -> Result<Value> {
442 self.rpc(
443 "hl_sendRevocation",
444 json!({"action": action, "signature": signature}),
445 )
446 .await
447 }
448
449 pub async fn subscribe(&self, subscription: &Value) -> Result<Value> {
455 self.rpc("hl_subscribe", json!({"subscription": subscription}))
456 .await
457 }
458
459 pub async fn unsubscribe(&self, subscription: &Value) -> Result<Value> {
461 self.rpc("hl_unsubscribe", json!({"subscription": subscription}))
462 .await
463 }
464}