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;
17#[cfg(feature = "sign")]
18use crate::utils::sign::{Domain, L1Action, UserAction};
19use crate::utils::{Address, NonceClock};
20use std::sync::{Mutex, PoisonError};
21
22pub use http::{
23 Accepted, ApiError, ExchangeResponse, Http, Method, Request, Response, Spent, Status, Transport, TransportError,
24 exchange, info, info_request, user_request,
25};
26pub use network::{
27 L1_CHAIN_ID, MAINNET_HYPE, MAINNET_USDC, Network, TESTNET_HYPE, TESTNET_USDC, Token, USDC_INDEX, UnknownNetwork,
28};
29
30pub type Clock = fn() -> u64;
33
34#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
37pub enum SendError {
38 #[error(transparent)]
39 Sign(#[from] SignError),
40 #[error(transparent)]
41 Api(#[from] ApiError),
42}
43
44pub struct Client<H, S = ()> {
47 api: Api<H>,
48 signer: S,
49 vault: Option<Address>,
50 clock: Clock,
51 nonces: Mutex<NonceClock>,
52}
53
54impl<H: Http> Client<H> {
55 pub fn reader(http: H, network: Network, clock: Clock) -> Self {
57 Client::new(http, network, clock, ())
58 }
59}
60
61impl<H: Http, S> Client<H, S> {
62 pub fn new(http: H, network: Network, clock: Clock, signer: S) -> Self {
65 Client {
66 api: Api::new(http, network, clock),
67 signer,
68 vault: None,
69 clock,
70 nonces: Mutex::new(NonceClock::new()),
71 }
72 }
73
74 #[must_use]
77 pub fn acting_for(mut self, vault: Address) -> Self {
78 self.vault = Some(vault);
79 self
80 }
81
82 #[must_use]
83 pub const fn network(&self) -> Network {
84 self.api.network()
85 }
86
87 #[must_use]
88 pub const fn http(&self) -> &H {
89 self.api.http()
90 }
91
92 #[must_use]
93 pub const fn signer(&self) -> &S {
94 &self.signer
95 }
96
97 pub fn spent(&self) -> Spent {
99 self.api.spent()
100 }
101
102 pub fn next_nonce(&self) -> u64 {
105 self.nonces.lock().unwrap_or_else(PoisonError::into_inner).next((self.clock)())
106 }
107}
108
109#[cfg(feature = "sign")]
113impl<H: Http, S: Signer> Client<H, S> {
114 pub async fn send(&self, action: &impl L1Action) -> Result<Accepted, SendError> {
116 self.send_at(action, self.next_nonce()).await
117 }
118
119 pub async fn send_at(&self, action: &impl L1Action, nonce: u64) -> Result<Accepted, SendError> {
122 let typed = action.typed(nonce, self.vault, self.network());
123 let signature = self.signer.sign_typed(&Domain::EXCHANGE, &typed).await?;
124 self.check(&typed.digest(&Domain::EXCHANGE), &signature)?;
125 Ok(exchange(&self.api, action.body(nonce, &signature, self.vault)).await?)
126 }
127
128 pub async fn send_user(&self, action: &impl UserAction) -> Result<Accepted, SendError> {
132 let network = self.network();
133 let domain = Domain::user_signed(network);
134 let typed = action.typed(network);
135 let signature = self.signer.sign_typed(&domain, &typed).await?;
136 self.check(&typed.digest(&domain), &signature)?;
137 Ok(exchange(&self.api, action.body(&signature, network)).await?)
138 }
139
140 fn check(&self, digest: &[u8; 32], signature: &crate::account::Signature) -> Result<(), SignError> {
142 crate::account::verify(digest, signature, self.signer.address())
143 }
144}
145
146impl<H: Http, S> Transport for Client<H, S> {
147 fn post(&self, path: &str, body: String) -> impl Future<Output = Result<String, TransportError>> {
148 self.api.post(path, body)
149 }
150}