Skip to main content

deribit_http/model/request/
wallet.rs

1/******************************************************************************
2   Author: Joaquín Béjar García
3   Email: jb@taunais.com
4   Date: 2025
5******************************************************************************/
6//! Request models for wallet operations.
7
8use crate::model::wallet::{AddressBookType, WithdrawalPriorityLevel};
9use serde::{Deserialize, Serialize};
10use serde_with::skip_serializing_none;
11
12/// Request parameters for adding an address to the address book.
13///
14/// Used with the `/private/add_to_address_book` endpoint.
15#[skip_serializing_none]
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct AddToAddressBookRequest {
18    /// Currency symbol (e.g., "BTC", "ETH", "USDC")
19    pub currency: String,
20    /// Address book entry type
21    #[serde(rename = "type")]
22    pub address_type: AddressBookType,
23    /// Address in proper format for the currency
24    pub address: String,
25    /// User-defined label for the address
26    pub label: Option<String>,
27    /// Tag for XRP addresses (destination tag)
28    pub tag: Option<String>,
29}
30
31impl AddToAddressBookRequest {
32    /// Creates a new request to add an address to the address book.
33    #[must_use]
34    pub fn new(currency: String, address_type: AddressBookType, address: String) -> Self {
35        Self {
36            currency,
37            address_type,
38            address,
39            label: None,
40            tag: None,
41        }
42    }
43
44    /// Sets the label for the address book entry.
45    #[must_use]
46    pub fn with_label(mut self, label: String) -> Self {
47        self.label = Some(label);
48        self
49    }
50
51    /// Sets the tag for XRP addresses.
52    #[must_use]
53    pub fn with_tag(mut self, tag: String) -> Self {
54        self.tag = Some(tag);
55        self
56    }
57}
58
59/// Request parameters for updating an address in the address book.
60///
61/// Used with the `/private/update_in_address_book` endpoint.
62/// This endpoint allows providing beneficiary information for travel rule compliance.
63#[skip_serializing_none]
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct UpdateInAddressBookRequest {
66    /// Currency symbol (e.g., "BTC", "ETH", "USDC")
67    pub currency: String,
68    /// Address book entry type
69    #[serde(rename = "type")]
70    pub address_type: AddressBookType,
71    /// Address in proper format for the currency
72    pub address: String,
73    /// User-defined label for the address
74    pub label: String,
75    /// Whether the user agrees to share information with third parties
76    pub agreed: bool,
77    /// Whether the address belongs to the user (personal/un-hosted wallet)
78    pub personal: bool,
79    /// Name of the beneficiary VASP (Virtual Asset Service Provider)
80    pub beneficiary_vasp_name: String,
81    /// DID (Decentralized Identifier) of the beneficiary VASP
82    pub beneficiary_vasp_did: String,
83    /// Geographical address of the beneficiary
84    pub beneficiary_address: String,
85    /// Website of the beneficiary VASP (required if VASP not in known list)
86    pub beneficiary_vasp_website: Option<String>,
87    /// First name of the beneficiary (if a person)
88    pub beneficiary_first_name: Option<String>,
89    /// Last name of the beneficiary (if a person)
90    pub beneficiary_last_name: Option<String>,
91    /// Company name of the beneficiary (if a company)
92    pub beneficiary_company_name: Option<String>,
93    /// Tag for XRP addresses (destination tag)
94    pub tag: Option<String>,
95}
96
97impl UpdateInAddressBookRequest {
98    /// Creates a new request to update an address in the address book.
99    ///
100    /// # Arguments
101    ///
102    /// * `currency` - Currency symbol (e.g., "BTC", "ETH")
103    /// * `address_type` - Type of address book entry
104    /// * `address` - The cryptocurrency address
105    /// * `label` - User-defined label for the address
106    /// * `agreed` - Whether user agrees to share info with third parties
107    /// * `personal` - Whether the address belongs to the user
108    /// * `beneficiary_vasp_name` - Name of the beneficiary VASP
109    /// * `beneficiary_vasp_did` - DID of the beneficiary VASP
110    /// * `beneficiary_address` - Geographical address of the beneficiary
111    #[must_use]
112    #[allow(clippy::too_many_arguments)]
113    pub fn new(
114        currency: String,
115        address_type: AddressBookType,
116        address: String,
117        label: String,
118        agreed: bool,
119        personal: bool,
120        beneficiary_vasp_name: String,
121        beneficiary_vasp_did: String,
122        beneficiary_address: String,
123    ) -> Self {
124        Self {
125            currency,
126            address_type,
127            address,
128            label,
129            agreed,
130            personal,
131            beneficiary_vasp_name,
132            beneficiary_vasp_did,
133            beneficiary_address,
134            beneficiary_vasp_website: None,
135            beneficiary_first_name: None,
136            beneficiary_last_name: None,
137            beneficiary_company_name: None,
138            tag: None,
139        }
140    }
141
142    /// Sets the beneficiary VASP website.
143    #[must_use]
144    pub fn with_beneficiary_vasp_website(mut self, website: String) -> Self {
145        self.beneficiary_vasp_website = Some(website);
146        self
147    }
148
149    /// Sets the beneficiary first name.
150    #[must_use]
151    pub fn with_beneficiary_first_name(mut self, first_name: String) -> Self {
152        self.beneficiary_first_name = Some(first_name);
153        self
154    }
155
156    /// Sets the beneficiary last name.
157    #[must_use]
158    pub fn with_beneficiary_last_name(mut self, last_name: String) -> Self {
159        self.beneficiary_last_name = Some(last_name);
160        self
161    }
162
163    /// Sets the beneficiary company name.
164    #[must_use]
165    pub fn with_beneficiary_company_name(mut self, company_name: String) -> Self {
166        self.beneficiary_company_name = Some(company_name);
167        self
168    }
169
170    /// Sets the tag for XRP addresses.
171    #[must_use]
172    pub fn with_tag(mut self, tag: String) -> Self {
173        self.tag = Some(tag);
174        self
175    }
176}
177
178/// Request parameters for withdrawing funds.
179///
180/// Used with the `/private/withdraw` endpoint.
181#[skip_serializing_none]
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct WithdrawRequest {
184    /// Currency symbol (e.g., "BTC", "ETH", "USDC")
185    pub currency: String,
186    /// Withdrawal address (must be in address book)
187    pub address: String,
188    /// Amount to withdraw
189    pub amount: f64,
190    /// Withdrawal priority level (affects fees and confirmation time)
191    pub priority: Option<WithdrawalPriorityLevel>,
192}
193
194impl WithdrawRequest {
195    /// Creates a new withdrawal request.
196    #[must_use]
197    pub fn new(currency: String, address: String, amount: f64) -> Self {
198        Self {
199            currency,
200            address,
201            amount,
202            priority: None,
203        }
204    }
205
206    /// Sets the withdrawal priority level.
207    #[must_use]
208    pub fn with_priority(mut self, priority: WithdrawalPriorityLevel) -> Self {
209        self.priority = Some(priority);
210        self
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217
218    #[test]
219    fn test_add_to_address_book_request_new() {
220        let req = AddToAddressBookRequest::new(
221            "BTC".to_string(),
222            AddressBookType::Withdrawal,
223            "bc1qtest123".to_string(),
224        );
225        assert_eq!(req.currency, "BTC");
226        assert_eq!(req.address_type, AddressBookType::Withdrawal);
227        assert_eq!(req.address, "bc1qtest123");
228        assert!(req.label.is_none());
229        assert!(req.tag.is_none());
230    }
231
232    #[test]
233    fn test_add_to_address_book_request_with_options() {
234        let req = AddToAddressBookRequest::new(
235            "XRP".to_string(),
236            AddressBookType::Transfer,
237            "rTest123".to_string(),
238        )
239        .with_label("My XRP wallet".to_string())
240        .with_tag("12345".to_string());
241
242        assert_eq!(req.label, Some("My XRP wallet".to_string()));
243        assert_eq!(req.tag, Some("12345".to_string()));
244    }
245
246    #[test]
247    fn test_add_to_address_book_request_serialization() {
248        let req = AddToAddressBookRequest::new(
249            "BTC".to_string(),
250            AddressBookType::Withdrawal,
251            "bc1qtest".to_string(),
252        )
253        .with_label("Test".to_string());
254
255        let json = serde_json::to_string(&req).unwrap();
256        assert!(json.contains("\"currency\":\"BTC\""));
257        assert!(json.contains("\"type\":\"withdrawal\""));
258        assert!(json.contains("\"address\":\"bc1qtest\""));
259        assert!(json.contains("\"label\":\"Test\""));
260    }
261
262    #[test]
263    fn test_update_in_address_book_request_new() {
264        let req = UpdateInAddressBookRequest::new(
265            "BTC".to_string(),
266            AddressBookType::Withdrawal,
267            "bc1qtest".to_string(),
268            "Main wallet".to_string(),
269            true,
270            false,
271            "Test VASP".to_string(),
272            "did:example:123".to_string(),
273            "123 Main St".to_string(),
274        );
275
276        assert_eq!(req.currency, "BTC");
277        assert_eq!(req.address_type, AddressBookType::Withdrawal);
278        assert!(req.agreed);
279        assert!(!req.personal);
280        assert!(req.beneficiary_vasp_website.is_none());
281    }
282
283    #[test]
284    fn test_update_in_address_book_request_with_options() {
285        let req = UpdateInAddressBookRequest::new(
286            "ETH".to_string(),
287            AddressBookType::Transfer,
288            "0xtest".to_string(),
289            "Test".to_string(),
290            true,
291            false,
292            "VASP".to_string(),
293            "did:test".to_string(),
294            "Address".to_string(),
295        )
296        .with_beneficiary_first_name("John".to_string())
297        .with_beneficiary_last_name("Doe".to_string())
298        .with_beneficiary_vasp_website("https://vasp.example.com".to_string());
299
300        assert_eq!(req.beneficiary_first_name, Some("John".to_string()));
301        assert_eq!(req.beneficiary_last_name, Some("Doe".to_string()));
302        assert_eq!(
303            req.beneficiary_vasp_website,
304            Some("https://vasp.example.com".to_string())
305        );
306    }
307
308    #[test]
309    fn test_withdraw_request_new() {
310        let req = WithdrawRequest::new("BTC".to_string(), "bc1qtest".to_string(), 0.5);
311        assert_eq!(req.currency, "BTC");
312        assert_eq!(req.address, "bc1qtest");
313        assert!((req.amount - 0.5).abs() < f64::EPSILON);
314        assert!(req.priority.is_none());
315    }
316
317    #[test]
318    fn test_withdraw_request_with_priority() {
319        let req = WithdrawRequest::new("BTC".to_string(), "bc1qtest".to_string(), 1.0)
320            .with_priority(WithdrawalPriorityLevel::High);
321
322        assert_eq!(req.priority, Some(WithdrawalPriorityLevel::High));
323    }
324
325    #[test]
326    fn test_withdraw_request_serialization() {
327        let req = WithdrawRequest::new("ETH".to_string(), "0xtest".to_string(), 2.5)
328            .with_priority(WithdrawalPriorityLevel::Mid);
329
330        let json = serde_json::to_string(&req).unwrap();
331        assert!(json.contains("\"currency\":\"ETH\""));
332        assert!(json.contains("\"address\":\"0xtest\""));
333        assert!(json.contains("\"priority\":\"mid\""));
334    }
335}