df_hyperliquid_core/client/
mod.rs1pub mod evm;
6pub mod http;
7#[cfg(feature = "native")]
8pub mod native;
9pub mod network;
10#[cfg(feature = "web")]
11pub mod web;
12
13use crate::account::SignError;
14#[cfg(feature = "sign")]
15use crate::account::Signer;
16use crate::client::http::Api;
17use crate::client::http::INFO;
18#[cfg(feature = "sign")]
19use crate::utils::sign::{Domain, L1Action, UserAction};
20use crate::utils::{Address, NonceClock};
21use std::sync::{Mutex, PoisonError};
22
23pub use http::{
24 Accepted, ApiError, ExchangeResponse, Http, Method, OFFLINE, Request, Response, Spent, Status, Transport,
25 TransportError, exchange, info, info_request, user_request,
26};
27pub use network::{
28 L1_CHAIN_ID, MAINNET_HYPE, MAINNET_USDC, Network, TESTNET_HYPE, TESTNET_USDC, Token, USDC_INDEX, UnknownNetwork,
29};
30
31pub type Clock = fn() -> u64;
34
35#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
38pub enum SendError {
39 #[error(transparent)]
40 Sign(#[from] SignError),
41 #[error(transparent)]
42 Api(#[from] ApiError),
43}
44
45pub struct Client<H, S = ()> {
48 api: Api<H>,
49 signer: S,
50 vault: Option<Address>,
51 clock: Clock,
52 nonces: Mutex<NonceClock>,
53}
54
55impl<H: Http, S> std::fmt::Debug for Client<H, S> {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 f.debug_struct("Client").field("network", &self.network()).field("vault", &self.vault).finish_non_exhaustive()
59 }
60}
61
62impl<H: Http> Client<H> {
63 pub fn reader(http: H, network: Network, clock: Clock) -> Self {
65 Client::new(http, network, clock, ())
66 }
67}
68
69impl<H: Http, S> Client<H, S> {
70 pub fn new(http: H, network: Network, clock: Clock, signer: S) -> Self {
73 Client {
74 api: Api::new(http, network, clock),
75 signer,
76 vault: None,
77 clock,
78 nonces: Mutex::new(NonceClock::new()),
79 }
80 }
81
82 #[must_use]
85 pub fn acting_for(mut self, vault: Address) -> Self {
86 self.vault = Some(vault);
87 self
88 }
89
90 #[must_use]
91 pub const fn network(&self) -> Network {
92 self.api.network()
93 }
94
95 #[must_use]
96 pub const fn http(&self) -> &H {
97 self.api.http()
98 }
99
100 #[must_use]
101 pub const fn signer(&self) -> &S {
102 &self.signer
103 }
104
105 pub fn spent(&self) -> Spent {
107 self.api.spent()
108 }
109
110 pub async fn warm(&self) {
115 let _ = self.api.post(INFO, info_request("exchangeStatus", &[])).await;
116 }
117
118 pub fn next_nonce(&self) -> u64 {
121 self.nonces.lock().unwrap_or_else(PoisonError::into_inner).next((self.clock)())
122 }
123}
124
125#[cfg(feature = "sign")]
129impl<H: Http, S: Signer> Client<H, S> {
130 pub async fn send(&self, action: &impl L1Action) -> Result<Accepted, SendError> {
132 self.send_at(action, self.next_nonce()).await
133 }
134
135 pub async fn send_at(&self, action: &impl L1Action, nonce: u64) -> Result<Accepted, SendError> {
138 let typed = action.typed(nonce, self.vault, self.network());
139 let signature = self.signer.sign_typed(&Domain::EXCHANGE, &typed).await?;
140 self.check(&typed.digest(&Domain::EXCHANGE), &signature)?;
141 Ok(exchange(&self.api, action.body(nonce, &signature, self.vault)).await?)
142 }
143
144 pub async fn send_user(&self, action: &impl UserAction) -> Result<Accepted, SendError> {
148 let network = self.network();
149 let domain = Domain::user_signed(network);
150 let typed = action.typed(network);
151 let signature = self.signer.sign_typed(&domain, &typed).await?;
152 self.check(&typed.digest(&domain), &signature)?;
153 Ok(exchange(&self.api, action.body(&signature, network)).await?)
154 }
155
156 fn check(&self, digest: &[u8; 32], signature: &crate::account::Signature) -> Result<(), SignError> {
158 crate::account::verify(digest, signature, self.signer.address())
159 }
160}
161
162impl<H: Http, S> Transport for Client<H, S> {
163 fn post(&self, path: &str, body: String) -> impl Future<Output = Result<String, TransportError>> {
164 self.api.post(path, body)
165 }
166}