Skip to main content

hypercall/client/wallet_client/
mod.rs

1mod directives;
2mod margin;
3mod onchain;
4mod orders;
5mod queries;
6mod signing;
7mod websocket;
8mod withdrawals;
9
10use hypercall_auth::SignatureRecovery;
11use hypercall_types::ws_protocol::WsMessage;
12use hypercall_types::WalletAddress;
13use std::sync::{
14    atomic::{AtomicU64, Ordering},
15    Arc,
16};
17use tokio::sync::{mpsc, Mutex};
18use tokio::time::Duration;
19use tokio_tungstenite::tungstenite;
20
21use alloy::sol_types::Eip712Domain;
22
23/// Default HTTP timeout for API requests (30 seconds)
24const DEFAULT_HTTP_TIMEOUT: Duration = Duration::from_secs(30);
25
26#[derive(Debug, Clone)]
27pub struct WalletClient {
28    pub wallet_address: WalletAddress,
29    pub private_key: String, // Private key for on-chain transactions and EIP-712 signing
30    pub signing_chain_id: u64,
31    ws_messages: Arc<Mutex<Vec<WsMessage>>>,
32    ws_tx: Arc<Mutex<Option<mpsc::UnboundedSender<tungstenite::Message>>>>,
33    nonce_counter: Arc<AtomicU64>,
34    http_client: reqwest::Client,
35}
36
37impl WalletClient {
38    pub async fn new(wallet_address: WalletAddress, private_key: String) -> Self {
39        let http_client = reqwest::Client::builder()
40            .timeout(DEFAULT_HTTP_TIMEOUT)
41            .connect_timeout(Duration::from_secs(10))
42            .build()
43            .expect("Failed to build HTTP client");
44
45        Self {
46            wallet_address,
47            private_key,
48            signing_chain_id: 998,
49            ws_messages: Arc::new(Mutex::new(Vec::new())),
50            ws_tx: Arc::new(Mutex::new(None)),
51            nonce_counter: Arc::new(AtomicU64::new(
52                std::time::SystemTime::now()
53                    .duration_since(std::time::UNIX_EPOCH)
54                    .unwrap()
55                    .as_millis() as u64,
56            )),
57            http_client,
58        }
59    }
60
61    /// Get next nonce for EIP-712 signatures
62    pub(super) fn next_nonce(&self) -> u64 {
63        self.nonce_counter.fetch_add(1, Ordering::SeqCst)
64    }
65
66    /// Get Hypercall EIP-712 domain
67    pub(super) fn hypercall_domain(&self) -> Eip712Domain {
68        SignatureRecovery::hypercall_domain(self.signing_chain_id)
69    }
70
71    pub(super) fn input_error(
72        message: impl Into<String>,
73    ) -> Box<dyn std::error::Error + Send + Sync> {
74        Box::new(std::io::Error::new(
75            std::io::ErrorKind::InvalidInput,
76            message.into(),
77        ))
78    }
79}