Skip to main content

deribit_http/model/
wallet.rs

1/******************************************************************************
2   Author: Joaquín Béjar García
3   Email: jb@taunais.com
4   Date: 2025
5******************************************************************************/
6//! Wallet-related models for deposit addresses and address book operations.
7
8use pretty_simple_display::{DebugPretty, DisplaySimple};
9use serde::{Deserialize, Serialize};
10use serde_with::skip_serializing_none;
11
12/// Address book entry type.
13///
14/// Specifies the type of address book entry for wallet operations.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16#[serde(rename_all = "snake_case")]
17#[repr(u8)]
18pub enum AddressBookType {
19    /// Address used for transfers between accounts
20    Transfer = 0,
21    /// Address used for external withdrawals
22    Withdrawal = 1,
23    /// Address used as deposit source identification
24    DepositSource = 2,
25}
26
27impl AddressBookType {
28    /// Returns the string representation of the address book type.
29    #[must_use]
30    #[inline]
31    pub fn as_str(&self) -> &'static str {
32        match self {
33            AddressBookType::Transfer => "transfer",
34            AddressBookType::Withdrawal => "withdrawal",
35            AddressBookType::DepositSource => "deposit_source",
36        }
37    }
38}
39
40impl std::fmt::Display for AddressBookType {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        write!(f, "{}", self.as_str())
43    }
44}
45
46/// Withdrawal priority level for blockchain transactions.
47///
48/// Higher priority levels result in faster transaction confirmation
49/// but incur higher fees.
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
51#[serde(rename_all = "snake_case")]
52#[repr(u8)]
53#[derive(Default)]
54pub enum WithdrawalPriorityLevel {
55    /// Lowest priority with minimal fees
56    VeryLow = 0,
57    /// Low priority
58    Low = 1,
59    /// Medium priority (default for most currencies)
60    Mid = 2,
61    /// High priority (default for BTC)
62    #[default]
63    High = 3,
64    /// Very high priority
65    VeryHigh = 4,
66    /// Extreme high priority
67    ExtremeHigh = 5,
68    /// Insane priority with maximum fees for fastest confirmation
69    Insane = 6,
70}
71
72impl WithdrawalPriorityLevel {
73    /// Returns the string representation of the priority level.
74    #[must_use]
75    #[inline]
76    pub fn as_str(&self) -> &'static str {
77        match self {
78            WithdrawalPriorityLevel::VeryLow => "very_low",
79            WithdrawalPriorityLevel::Low => "low",
80            WithdrawalPriorityLevel::Mid => "mid",
81            WithdrawalPriorityLevel::High => "high",
82            WithdrawalPriorityLevel::VeryHigh => "very_high",
83            WithdrawalPriorityLevel::ExtremeHigh => "extreme_high",
84            WithdrawalPriorityLevel::Insane => "insane",
85        }
86    }
87}
88
89impl std::fmt::Display for WithdrawalPriorityLevel {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        write!(f, "{}", self.as_str())
92    }
93}
94
95/// Deposit address information returned by the Deribit API.
96///
97/// Contains details about a cryptocurrency deposit address
98/// including the address itself and associated metadata.
99#[skip_serializing_none]
100#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
101pub struct DepositAddress {
102    /// The cryptocurrency deposit address
103    pub address: String,
104    /// Currency symbol (e.g., "BTC", "ETH", "USDC")
105    pub currency: String,
106    /// Address type (e.g., "deposit")
107    #[serde(rename = "type")]
108    pub address_type: Option<String>,
109    /// Timestamp when the address was created, in milliseconds since Unix epoch
110    pub creation_timestamp: Option<u64>,
111    /// Status of the address
112    pub status: Option<String>,
113}
114
115impl DepositAddress {
116    /// Creates a new deposit address with the required fields.
117    #[must_use]
118    pub fn new(address: String, currency: String) -> Self {
119        Self {
120            address,
121            currency,
122            address_type: None,
123            creation_timestamp: None,
124            status: None,
125        }
126    }
127}
128
129/// Address book entry containing wallet address information.
130///
131/// Represents an entry in the user's address book, which can be used
132/// for withdrawals, transfers, or deposit source identification.
133#[skip_serializing_none]
134#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
135pub struct AddressBookEntry {
136    /// Address in proper format for the currency
137    pub address: String,
138    /// Currency symbol (e.g., "BTC", "ETH", "USDC")
139    pub currency: String,
140    /// Type of address book entry
141    #[serde(rename = "type")]
142    pub entry_type: Option<String>,
143    /// User-defined label for the address
144    pub label: Option<String>,
145    /// Timestamp when the entry was created, in milliseconds since Unix epoch
146    pub creation_timestamp: Option<u64>,
147    /// Timestamp when the entry was last updated, in milliseconds since Unix epoch
148    pub update_timestamp: Option<u64>,
149    /// Whether the user agreed to share information with third parties
150    pub agreed: Option<bool>,
151    /// Whether the address belongs to the user (personal/un-hosted wallet)
152    pub personal: Option<bool>,
153    /// Whether the address belongs to an unhosted wallet
154    pub unhosted: Option<bool>,
155    /// Tag for XRP addresses (destination tag)
156    pub tag: Option<String>,
157    /// Name of the beneficiary VASP (Virtual Asset Service Provider)
158    pub beneficiary_vasp_name: Option<String>,
159    /// DID (Decentralized Identifier) of the beneficiary VASP
160    pub beneficiary_vasp_did: Option<String>,
161    /// Website of the beneficiary VASP
162    pub beneficiary_vasp_website: Option<String>,
163    /// First name of the beneficiary (if a person)
164    pub beneficiary_first_name: Option<String>,
165    /// Last name of the beneficiary (if a person)
166    pub beneficiary_last_name: Option<String>,
167    /// Company name of the beneficiary (if a company)
168    pub beneficiary_company_name: Option<String>,
169    /// Geographical address of the beneficiary
170    pub beneficiary_address: Option<String>,
171}
172
173impl AddressBookEntry {
174    /// Creates a new address book entry with the required fields.
175    #[must_use]
176    pub fn new(address: String, currency: String) -> Self {
177        Self {
178            address,
179            currency,
180            entry_type: None,
181            label: None,
182            creation_timestamp: None,
183            update_timestamp: None,
184            agreed: None,
185            personal: None,
186            unhosted: None,
187            tag: None,
188            beneficiary_vasp_name: None,
189            beneficiary_vasp_did: None,
190            beneficiary_vasp_website: None,
191            beneficiary_first_name: None,
192            beneficiary_last_name: None,
193            beneficiary_company_name: None,
194            beneficiary_address: None,
195        }
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202
203    #[test]
204    fn test_address_book_type_as_str() {
205        assert_eq!(AddressBookType::Transfer.as_str(), "transfer");
206        assert_eq!(AddressBookType::Withdrawal.as_str(), "withdrawal");
207        assert_eq!(AddressBookType::DepositSource.as_str(), "deposit_source");
208    }
209
210    #[test]
211    fn test_address_book_type_display() {
212        assert_eq!(format!("{}", AddressBookType::Transfer), "transfer");
213        assert_eq!(format!("{}", AddressBookType::Withdrawal), "withdrawal");
214    }
215
216    #[test]
217    fn test_address_book_type_serialization() {
218        let json = serde_json::to_string(&AddressBookType::Withdrawal).unwrap();
219        assert_eq!(json, "\"withdrawal\"");
220
221        let deserialized: AddressBookType = serde_json::from_str("\"transfer\"").unwrap();
222        assert_eq!(deserialized, AddressBookType::Transfer);
223    }
224
225    #[test]
226    fn test_withdrawal_priority_level_as_str() {
227        assert_eq!(WithdrawalPriorityLevel::VeryLow.as_str(), "very_low");
228        assert_eq!(WithdrawalPriorityLevel::Low.as_str(), "low");
229        assert_eq!(WithdrawalPriorityLevel::Mid.as_str(), "mid");
230        assert_eq!(WithdrawalPriorityLevel::High.as_str(), "high");
231        assert_eq!(WithdrawalPriorityLevel::VeryHigh.as_str(), "very_high");
232        assert_eq!(
233            WithdrawalPriorityLevel::ExtremeHigh.as_str(),
234            "extreme_high"
235        );
236        assert_eq!(WithdrawalPriorityLevel::Insane.as_str(), "insane");
237    }
238
239    #[test]
240    fn test_withdrawal_priority_level_default() {
241        assert_eq!(
242            WithdrawalPriorityLevel::default(),
243            WithdrawalPriorityLevel::High
244        );
245    }
246
247    #[test]
248    fn test_withdrawal_priority_level_serialization() {
249        let json = serde_json::to_string(&WithdrawalPriorityLevel::Mid).unwrap();
250        assert_eq!(json, "\"mid\"");
251
252        let deserialized: WithdrawalPriorityLevel = serde_json::from_str("\"high\"").unwrap();
253        assert_eq!(deserialized, WithdrawalPriorityLevel::High);
254    }
255
256    #[test]
257    fn test_deposit_address_new() {
258        let addr = DepositAddress::new("bc1qtest123".to_string(), "BTC".to_string());
259        assert_eq!(addr.address, "bc1qtest123");
260        assert_eq!(addr.currency, "BTC");
261        assert!(addr.address_type.is_none());
262    }
263
264    #[test]
265    fn test_deposit_address_serialization() {
266        let addr = DepositAddress {
267            address: "0xtest123".to_string(),
268            currency: "ETH".to_string(),
269            address_type: Some("deposit".to_string()),
270            creation_timestamp: Some(1234567890000),
271            status: Some("active".to_string()),
272        };
273
274        let json = serde_json::to_string(&addr).unwrap();
275        assert!(json.contains("\"address\":\"0xtest123\""));
276        assert!(json.contains("\"currency\":\"ETH\""));
277        assert!(json.contains("\"type\":\"deposit\""));
278    }
279
280    #[test]
281    fn test_address_book_entry_new() {
282        let entry = AddressBookEntry::new("bc1qtest456".to_string(), "BTC".to_string());
283        assert_eq!(entry.address, "bc1qtest456");
284        assert_eq!(entry.currency, "BTC");
285        assert!(entry.label.is_none());
286        assert!(entry.agreed.is_none());
287    }
288
289    #[test]
290    fn test_address_book_entry_serialization() {
291        let entry = AddressBookEntry {
292            address: "bc1qtest789".to_string(),
293            currency: "BTC".to_string(),
294            entry_type: Some("withdrawal".to_string()),
295            label: Some("Main wallet".to_string()),
296            creation_timestamp: Some(1234567890000),
297            update_timestamp: None,
298            agreed: Some(true),
299            personal: Some(false),
300            unhosted: None,
301            tag: None,
302            beneficiary_vasp_name: Some("Test VASP".to_string()),
303            beneficiary_vasp_did: None,
304            beneficiary_vasp_website: None,
305            beneficiary_first_name: Some("John".to_string()),
306            beneficiary_last_name: Some("Doe".to_string()),
307            beneficiary_company_name: None,
308            beneficiary_address: Some("123 Main St".to_string()),
309        };
310
311        let json = serde_json::to_string(&entry).unwrap();
312        assert!(json.contains("\"address\":\"bc1qtest789\""));
313        assert!(json.contains("\"type\":\"withdrawal\""));
314        assert!(json.contains("\"label\":\"Main wallet\""));
315        assert!(json.contains("\"agreed\":true"));
316    }
317
318    #[test]
319    fn test_address_book_entry_deserialization() {
320        let json = r#"{
321            "address": "bc1qtest",
322            "currency": "BTC",
323            "type": "transfer",
324            "label": "Test",
325            "creation_timestamp": 1234567890000,
326            "agreed": true,
327            "personal": false,
328            "beneficiary_first_name": "Jane",
329            "beneficiary_last_name": "Smith"
330        }"#;
331
332        let entry: AddressBookEntry = serde_json::from_str(json).unwrap();
333        assert_eq!(entry.address, "bc1qtest");
334        assert_eq!(entry.currency, "BTC");
335        assert_eq!(entry.entry_type, Some("transfer".to_string()));
336        assert_eq!(entry.label, Some("Test".to_string()));
337        assert_eq!(entry.agreed, Some(true));
338        assert_eq!(entry.personal, Some(false));
339        assert_eq!(entry.beneficiary_first_name, Some("Jane".to_string()));
340    }
341}