Skip to main content

polyoxide_clob/api/
notifications.rs

1use polyoxide_core::{HttpClient, QueryBuilder};
2use serde::{Deserialize, Serialize};
3
4use crate::{
5    account::{Credentials, Signer, Wallet},
6    error::ClobError,
7    request::{AuthMode, Request},
8    types::SignatureType,
9};
10
11/// Notifications namespace for notification operations
12#[derive(Clone)]
13pub struct Notifications {
14    pub(crate) http_client: HttpClient,
15    pub(crate) wallet: Wallet,
16    pub(crate) credentials: Credentials,
17    pub(crate) signer: Signer,
18    pub(crate) chain_id: u64,
19    pub(crate) signature_type: SignatureType,
20}
21
22impl Notifications {
23    fn l2_auth(&self) -> AuthMode {
24        AuthMode::L2 {
25            address: self.wallet.address(),
26            credentials: self.credentials.clone(),
27            signer: self.signer.clone(),
28        }
29    }
30
31    /// List notifications for the current user.
32    ///
33    /// The CLOB API requires a `signature_type` query parameter to derive the
34    /// account address; it is taken from the client configuration.
35    pub fn list(&self) -> Request<Vec<Notification>> {
36        Request::get(
37            self.http_client.clone(),
38            "/notifications",
39            self.l2_auth(),
40            self.chain_id,
41        )
42        .query("signature_type", self.signature_type as u8)
43    }
44
45    /// Drop (dismiss) notifications by ID
46    pub async fn drop(&self, ids: impl Into<Vec<String>>) -> Result<serde_json::Value, ClobError> {
47        #[derive(Serialize)]
48        struct Body {
49            ids: Vec<String>,
50        }
51
52        Request::<serde_json::Value>::delete(
53            self.http_client.clone(),
54            "/notifications",
55            self.l2_auth(),
56            self.chain_id,
57        )
58        .body(&Body { ids: ids.into() })?
59        .send()
60        .await
61    }
62}
63
64/// A notification from the CLOB API.
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct Notification {
67    pub id: String,
68    #[serde(rename = "type")]
69    pub notification_type: u32,
70    pub owner: String,
71    #[serde(default)]
72    pub payload: serde_json::Value,
73    #[serde(default)]
74    pub timestamp: Option<String>,
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn notification_deserializes() {
83        let json = r#"{
84            "id": "notif-1",
85            "type": 1,
86            "owner": "0xabc123",
87            "payload": {"order_id": "order-456", "side": "BUY"},
88            "timestamp": "2024-01-01T00:00:00Z"
89        }"#;
90        let notif: Notification = serde_json::from_str(json).unwrap();
91        assert_eq!(notif.id, "notif-1");
92        assert_eq!(notif.notification_type, 1);
93        assert_eq!(notif.owner, "0xabc123");
94        assert_eq!(notif.payload["order_id"], "order-456");
95        assert_eq!(notif.timestamp.as_deref(), Some("2024-01-01T00:00:00Z"));
96    }
97
98    #[test]
99    fn notification_null_payload() {
100        let json = r#"{
101            "id": "notif-2",
102            "type": 0,
103            "owner": "0xdef456",
104            "payload": null
105        }"#;
106        let notif: Notification = serde_json::from_str(json).unwrap();
107        assert_eq!(notif.id, "notif-2");
108        assert_eq!(notif.notification_type, 0);
109        assert!(notif.payload.is_null());
110        assert!(notif.timestamp.is_none());
111    }
112
113    #[test]
114    fn notification_missing_payload() {
115        let json = r#"{
116            "id": "notif-3",
117            "type": 2,
118            "owner": "0x789"
119        }"#;
120        let notif: Notification = serde_json::from_str(json).unwrap();
121        assert_eq!(notif.id, "notif-3");
122        assert_eq!(notif.notification_type, 2);
123        assert!(notif.payload.is_null());
124    }
125}