Skip to content

feat: QoL #74

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ pub enum Error {
SignatureFailure(String),
#[error("Vault address not found")]
VaultAddressNotFound,
#[error("Subscription error: {0:?}")]
SubscriptionError(String),
}
4 changes: 2 additions & 2 deletions src/exchange/exchange_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use super::cancel::ClientCancelRequestCloid;
use super::order::{MarketCloseParams, MarketOrderParams};
use super::{ClientLimit, ClientOrder};

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ExchangeClient {
pub http_client: HttpClient,
pub wallet: LocalWallet,
Expand Down Expand Up @@ -124,7 +124,7 @@ impl ExchangeClient {
})
}

async fn post(
pub async fn post(
&self,
action: serde_json::Value,
signature: Signature,
Expand Down
2 changes: 1 addition & 1 deletion src/info/info_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ pub enum InfoRequest {
},
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct InfoClient {
pub http_client: HttpClient,
pub(crate) ws_manager: Option<WsManager>,
Expand Down
5 changes: 4 additions & 1 deletion src/info/sub_structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,14 @@ pub struct Vip {
pub ntl_cutoff: String,
}

#[derive(Deserialize, Debug)]
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct UserTokenBalance {
pub coin: String,
pub hold: String,
pub total: String,
pub token: u32,
pub entry_ntl: String,
}

#[derive(Deserialize, Clone, Debug)]
Expand Down
24 changes: 12 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
#![deny(unreachable_pub)]
mod consts;
mod errors;
mod exchange;
mod helpers;
mod info;
mod market_maker;
mod meta;
mod prelude;
mod proxy_digest;
mod req;
mod signature;
mod ws;
pub mod consts;
pub mod errors;
pub mod exchange;
pub mod helpers;
pub mod info;
pub mod market_maker;
pub mod meta;
pub mod prelude;
pub mod proxy_digest;
pub mod req;
pub mod signature;
pub mod ws;
pub use consts::{EPSILON, LOCAL_API_URL, MAINNET_API_URL, TESTNET_API_URL};
pub use errors::Error;
pub use exchange::*;
Expand Down
26 changes: 21 additions & 5 deletions src/meta.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use std::collections::HashMap;

use ethers::abi::ethereum_types::H128;
use serde::Deserialize;
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Debug, Clone)]
pub struct Meta {
pub universe: Vec<AssetMeta>,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Debug, Clone, Serialize)]
pub struct SpotMeta {
pub universe: Vec<SpotAssetMeta>,
pub tokens: Vec<TokenInfo>,
Expand Down Expand Up @@ -52,7 +52,22 @@ pub enum SpotMetaAndAssetCtxs {
Context(Vec<SpotAssetContext>),
}

#[derive(Deserialize, Debug, Clone)]
impl SpotMetaAndAssetCtxs {
pub fn get_spot_meta(&self) -> &SpotMeta {
match self {
SpotMetaAndAssetCtxs::SpotMeta(meta) => meta,
_ => panic!("Not a spot meta"),
}
}
pub fn get_context(&self) -> &Vec<SpotAssetContext> {
match self {
SpotMetaAndAssetCtxs::Context(ctxs) => ctxs,
_ => panic!("Not a context"),
}
}
}

#[derive(Deserialize, Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotAssetContext {
pub day_ntl_vlm: String,
Expand All @@ -70,7 +85,7 @@ pub struct AssetMeta {
pub sz_decimals: u32,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotAssetMeta {
pub tokens: [usize; 2],
Expand All @@ -79,7 +94,7 @@ pub struct SpotAssetMeta {
pub is_canonical: bool,
}

#[derive(Debug, Deserialize, Clone)]
#[derive(Debug, Deserialize, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenInfo {
pub name: String,
Expand All @@ -88,4 +103,5 @@ pub struct TokenInfo {
pub index: usize,
pub token_id: H128,
pub is_canonical: bool,
pub full_name: Option<String>,
}
2 changes: 1 addition & 1 deletion src/req.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ struct ErrorData {
msg: String,
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct HttpClient {
pub client: Client,
pub base_url: String,
Expand Down
11 changes: 11 additions & 0 deletions src/ws/message_types.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use crate::ws::sub_structs::*;
use serde::Deserialize;

#[derive(Deserialize, Clone, Debug)]
pub struct SubscriptionError {
pub data: String,
}

#[derive(Deserialize, Clone, Debug)]
pub struct Trades {
pub data: Vec<Trade>,
Expand Down Expand Up @@ -55,3 +60,9 @@ pub struct Notification {
pub struct WebData2 {
pub data: WebData2Data,
}

#[derive(Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct ActiveAssetCtx {
pub data: ActiveAssetCtxData,
}
45 changes: 45 additions & 0 deletions src/ws/sub_structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,3 +279,48 @@ pub struct NotificationData {
pub struct WebData2Data {
pub user: H160,
}

#[derive(Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct ActiveAssetCtxData {
pub coin: String,
pub ctx: ActiveAssetCtxEnum,
}

#[derive(Deserialize, Clone, Debug)]
#[serde(untagged)]
pub enum ActiveAssetCtxEnum {
Perps(PerpsAssetCtx),
Spot(SpotAssetCtx),
}
#[derive(Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PerpsAssetCtx {
pub day_ntl_vlm: u64,
pub prev_day_px: u64,
pub mark_px: u64,
pub mid_px: Option<u64>,
pub funding: u64,
pub open_interest: u64,
pub oracle_px: u64,
}

#[derive(Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct SpotAssetDetails {
#[serde(rename = "prevDayPx")]
pub prev_day_px: String, // Ensure this matches the JSON field exactly
pub day_ntl_vlm: String,
pub mark_px: String,
pub mid_px: String,
pub circulating_supply: String,
pub coin: String,
pub total_supply: String,
pub day_base_vlm: String,
}

#[derive(Deserialize, Clone, Debug)]
pub struct SpotAssetCtx {
pub coin: String,
pub data: SpotAssetDetails,
}
44 changes: 35 additions & 9 deletions src/ws/ws_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@ use tokio_tungstenite::{

use ethers::types::H160;

use super::{ActiveAssetCtx, SpotAssetCtx, SubscriptionError};

#[derive(Debug)]
struct SubscriptionData {
sending_channel: UnboundedSender<Message>,
subscription_id: u32,
id: String,
}
#[derive(Debug)]

#[derive(Debug, Clone)]
pub(crate) struct WsManager {
stop_flag: Arc<AtomicBool>,
writer: Arc<Mutex<SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, protocol::Message>>>,
Expand All @@ -60,6 +63,7 @@ pub enum Subscription {
UserNonFundingLedgerUpdates { user: H160 },
Notification { user: H160 },
WebData2 { user: H160 },
ActiveAssetCtx { coin: String },
}

#[derive(Deserialize, Clone, Debug)]
Expand All @@ -80,6 +84,9 @@ pub enum Message {
UserNonFundingLedgerUpdates(UserNonFundingLedgerUpdates),
Notification(Notification),
WebData2(WebData2),
ActiveAssetCtx(ActiveAssetCtx),
ActiveSpotAssetCtx(SpotAssetCtx),
Error(SubscriptionError),
Pong,
}

Expand Down Expand Up @@ -261,6 +268,27 @@ impl WsManager {
Message::SubscriptionResponse | Message::Pong => Ok(String::default()),
Message::NoData => Ok("".to_string()),
Message::HyperliquidError(err) => Ok(format!("hyperliquid error: {err:?}")),
Message::ActiveAssetCtx(active_asset_ctx) => {
serde_json::to_string(&Subscription::ActiveAssetCtx {
coin: active_asset_ctx.data.coin.clone(),
})
.map_err(|e| Error::JsonParse(e.to_string()))
}
Message::ActiveSpotAssetCtx(active_spot_asset_ctx) => {
serde_json::to_string(&Subscription::ActiveAssetCtx {
coin: active_spot_asset_ctx.data.coin.clone(),
})
.map_err(|e| Error::JsonParse(e.to_string()))
}
Message::Error(err) => {
let error_str = err.data.to_string();
let identifier = error_str
.split("Invalid subscription ")
.nth(1)
.unwrap_or("Invalid subscription")
.to_string();
Ok(identifier)
}
}
}

Expand All @@ -274,8 +302,10 @@ impl WsManager {
if !data.starts_with('{') {
return Ok(());
}

let message = serde_json::from_str::<Message>(&data)
.map_err(|e| Error::JsonParse(e.to_string()))?;

let identifier = WsManager::get_identifier(&message)?;
if identifier.is_empty() {
return Ok(());
Expand Down Expand Up @@ -376,15 +406,11 @@ impl WsManager {
) -> Result<u32> {
let mut subscriptions = self.subscriptions.lock().await;

let identifier_entry = if let Subscription::UserEvents { user: _ } =
serde_json::from_str::<Subscription>(&identifier)
.map_err(|e| Error::JsonParse(e.to_string()))?
{
let subscription = serde_json::from_str::<Subscription>(&identifier)
.map_err(|e| Error::JsonParse(e.to_string()))?;
let identifier_entry = if let Subscription::UserEvents { user: _ } = subscription {
"userEvents".to_string()
} else if let Subscription::OrderUpdates { user: _ } =
serde_json::from_str::<Subscription>(&identifier)
.map_err(|e| Error::JsonParse(e.to_string()))?
{
} else if let Subscription::OrderUpdates { user: _ } = subscription {
"orderUpdates".to_string()
} else {
identifier.clone()
Expand Down