Skip to main content

hypercall_db_diesel/
mmp.rs

1//! MmpConfigReader + MmpConfigWriter implementations for DatabaseHandler.
2//!
3//! Persists Market Maker Protection configs (interval, freeze time, Greek
4//! limits) per wallet+currency pair.
5
6use anyhow::Result;
7use diesel::prelude::*;
8use diesel::RunQueryDsl;
9
10use hypercall_types::WalletAddress;
11
12use crate::database_handler::DatabaseHandler;
13
14impl hypercall_db::MmpConfigReader for DatabaseHandler {
15    fn get_mmp_config_sync(
16        &self,
17        wallet: &WalletAddress,
18        currency: &str,
19    ) -> Result<Option<hypercall_db::MmpConfigRecord>> {
20        use crate::schema::mmp_configs::dsl;
21
22        let mut conn = self.pool().get()?;
23        let result = dsl::mmp_configs
24            .filter(dsl::wallet_address.eq(wallet))
25            .filter(dsl::currency.eq(currency))
26            .first::<crate::models::MmpConfig>(&mut conn)
27            .optional()?;
28        Ok(result.map(Into::into))
29    }
30
31    fn get_all_mmp_configs_for_wallet_sync(
32        &self,
33        wallet: &WalletAddress,
34    ) -> Result<Vec<hypercall_db::MmpConfigRecord>> {
35        use crate::schema::mmp_configs::dsl;
36
37        let mut conn = self.pool().get()?;
38        let results = dsl::mmp_configs
39            .filter(dsl::wallet_address.eq(wallet))
40            .load::<crate::models::MmpConfig>(&mut conn)?;
41        Ok(results.into_iter().map(Into::into).collect())
42    }
43
44    fn get_all_mmp_configs_sync(&self) -> Result<Vec<hypercall_db::MmpConfigRecord>> {
45        use crate::schema::mmp_configs::dsl;
46
47        let mut conn = self.pool().get()?;
48        let results = dsl::mmp_configs
49            .filter(dsl::enabled.eq(true))
50            .load::<crate::models::MmpConfig>(&mut conn)?;
51        Ok(results.into_iter().map(Into::into).collect())
52    }
53}
54
55impl hypercall_db::MmpConfigWriter for DatabaseHandler {
56    fn save_mmp_config_sync(&self, config: &hypercall_db::MmpConfigRecord) -> Result<()> {
57        let new_config = crate::models::NewMmpConfig {
58            wallet_address: config.wallet_address,
59            currency: config.currency.clone(),
60            interval_ms: config.interval_ms,
61            frozen_time_ms: config.frozen_time_ms,
62            qty_limit: config.qty_limit,
63            delta_limit: config.delta_limit,
64            vega_limit: config.vega_limit,
65            enabled: config.enabled,
66        };
67
68        let mut conn = self.pool().get()?;
69        diesel::insert_into(crate::schema::mmp_configs::table)
70            .values(&new_config)
71            .on_conflict((
72                crate::schema::mmp_configs::wallet_address,
73                crate::schema::mmp_configs::currency,
74            ))
75            .do_update()
76            .set(&new_config)
77            .execute(&mut conn)?;
78        Ok(())
79    }
80
81    fn delete_mmp_config_sync(&self, wallet: &WalletAddress, currency: &str) -> Result<()> {
82        use crate::schema::mmp_configs::dsl;
83
84        let mut conn = self.pool().get()?;
85        diesel::update(
86            dsl::mmp_configs
87                .filter(dsl::wallet_address.eq(wallet))
88                .filter(dsl::currency.eq(currency)),
89        )
90        .set(dsl::enabled.eq(false))
91        .execute(&mut conn)?;
92        Ok(())
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use crate::test_helpers::TestDb;
99    use hypercall_db::*;
100    use hypercall_types::wallet_address::test_wallet;
101    use rust_decimal_macros::dec;
102
103    #[tokio::test]
104    async fn mmp_config_write_read_roundtrip() {
105        let test_db = TestDb::new().await.unwrap();
106        let db = test_db.handler.as_ref();
107        let wallet = test_wallet(5);
108
109        let config = MmpConfigRecord {
110            wallet_address: wallet,
111            currency: "BTC".to_string(),
112            interval_ms: 1000,
113            frozen_time_ms: 5000,
114            qty_limit: Some(dec!(10)),
115            delta_limit: None,
116            vega_limit: Some(dec!(100)),
117            enabled: true,
118            created_at: None,
119            updated_at: None,
120        };
121
122        db.save_mmp_config_sync(&config).unwrap();
123        let loaded = db.get_mmp_config_sync(&wallet, "BTC").unwrap().unwrap();
124        assert_eq!(loaded.wallet_address, wallet);
125        assert_eq!(loaded.currency, "BTC");
126        assert_eq!(loaded.interval_ms, 1000);
127        assert_eq!(loaded.qty_limit, Some(dec!(10)));
128        assert!(loaded.enabled);
129    }
130
131    #[tokio::test]
132    async fn mmp_config_get_all_for_wallet() {
133        let test_db = TestDb::new().await.unwrap();
134        let db = test_db.handler.as_ref();
135        let wallet = test_wallet(6);
136
137        for currency in ["BTC", "ETH", "SOL"] {
138            let config = MmpConfigRecord {
139                wallet_address: wallet,
140                currency: currency.to_string(),
141                interval_ms: 1000,
142                frozen_time_ms: 5000,
143                qty_limit: None,
144                delta_limit: None,
145                vega_limit: None,
146                enabled: true,
147                created_at: None,
148                updated_at: None,
149            };
150            db.save_mmp_config_sync(&config).unwrap();
151        }
152
153        let all = db.get_all_mmp_configs_for_wallet_sync(&wallet).unwrap();
154        assert_eq!(all.len(), 3);
155    }
156
157    #[tokio::test]
158    async fn mmp_config_delete_disables() {
159        let test_db = TestDb::new().await.unwrap();
160        let db = test_db.handler.as_ref();
161        let wallet = test_wallet(7);
162
163        let config = MmpConfigRecord {
164            wallet_address: wallet,
165            currency: "BTC".to_string(),
166            interval_ms: 1000,
167            frozen_time_ms: 5000,
168            qty_limit: None,
169            delta_limit: None,
170            vega_limit: None,
171            enabled: true,
172            created_at: None,
173            updated_at: None,
174        };
175        db.save_mmp_config_sync(&config).unwrap();
176        db.delete_mmp_config_sync(&wallet, "BTC").unwrap();
177
178        // delete_mmp_config sets enabled=false, not actual deletion
179        let loaded = db.get_mmp_config_sync(&wallet, "BTC").unwrap().unwrap();
180        assert!(!loaded.enabled);
181    }
182}