Skip to main content

rust_okx/ws/request/
envelope.rs

1//! Common WebSocket request envelopes.
2
3use serde::Serialize;
4
5use crate::ws::Arg;
6
7/// Subscribe or unsubscribe request body.
8///
9/// OKX docs: <https://www.okx.com/docs-v5/en/#overview-websocket-subscribe>
10#[derive(Debug, Serialize)]
11#[non_exhaustive]
12pub struct ChannelRequest<'a> {
13    /// Optional client message ID (up to 32 case-sensitive alphanumeric characters).
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub id: Option<&'a str>,
16    /// `subscribe` or `unsubscribe`.
17    pub op: &'a str,
18    /// Channel arguments.
19    pub args: &'a [Arg],
20}
21
22impl<'a> ChannelRequest<'a> {
23    /// Build a subscription request.
24    pub fn subscribe(args: &'a [Arg]) -> Self {
25        Self {
26            id: None,
27            op: "subscribe",
28            args,
29        }
30    }
31
32    /// Build an unsubscription request.
33    pub fn unsubscribe(args: &'a [Arg]) -> Self {
34        Self {
35            id: None,
36            op: "unsubscribe",
37            args,
38        }
39    }
40
41    /// Attach a client message ID.
42    pub fn id(mut self, id: &'a str) -> Self {
43        self.id = Some(id);
44        self
45    }
46}
47
48/// Private WebSocket login request body.
49///
50/// OKX docs: <https://www.okx.com/docs-v5/en/#overview-websocket-login>
51#[derive(Debug, Serialize)]
52#[non_exhaustive]
53pub struct LoginRequest<'a> {
54    /// Always `login`.
55    pub op: &'static str,
56    /// OKX requires exactly one login argument.
57    pub args: [LoginArg<'a>; 1],
58}
59
60impl<'a> LoginRequest<'a> {
61    /// Build a login request from one authentication argument.
62    pub fn new(arg: LoginArg<'a>) -> Self {
63        Self {
64            op: "login",
65            args: [arg],
66        }
67    }
68}
69
70/// Authentication argument inside [`LoginRequest`].
71#[derive(Debug, Serialize)]
72#[serde(rename_all = "camelCase")]
73#[non_exhaustive]
74pub struct LoginArg<'a> {
75    /// API key.
76    pub api_key: &'a str,
77    /// API key passphrase.
78    pub passphrase: &'a str,
79    /// Unix timestamp in seconds used for signing.
80    pub timestamp: &'a str,
81    /// Base64-encoded HMAC-SHA256 signature.
82    pub sign: String,
83}
84
85impl<'a> LoginArg<'a> {
86    /// Build a login argument from already-computed authentication values.
87    pub fn new(
88        api_key: &'a str,
89        passphrase: &'a str,
90        timestamp: &'a str,
91        sign: impl Into<String>,
92    ) -> Self {
93        Self {
94            api_key,
95            passphrase,
96            timestamp,
97            sign: sign.into(),
98        }
99    }
100}
101
102/// Generic WebSocket trade-operation envelope.
103///
104/// OKX docs: <https://www.okx.com/docs-v5/en/#overview-websocket>
105#[derive(Debug, Serialize)]
106#[serde(rename_all = "camelCase")]
107#[non_exhaustive]
108pub struct OperationRequest<'a, A> {
109    /// Client-provided request ID.
110    pub id: String,
111    /// OKX operation name.
112    pub op: String,
113    /// Optional request effective deadline in Unix milliseconds.
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub exp_time: Option<String>,
116    /// Operation arguments.
117    pub args: &'a [A],
118}
119
120impl<'a, A> OperationRequest<'a, A> {
121    /// Build an operation request without an expiration deadline.
122    pub fn new(id: impl Into<String>, op: impl Into<String>, args: &'a [A]) -> Self {
123        Self {
124            id: id.into(),
125            op: op.into(),
126            exp_time: None,
127            args,
128        }
129    }
130
131    /// Set the request effective deadline in Unix milliseconds.
132    pub fn exp_time(mut self, exp_time: impl Into<String>) -> Self {
133        self.exp_time = Some(exp_time.into());
134        self
135    }
136}