deribit_http/model/
transfer.rs

1/******************************************************************************
2   Author: Joaquín Béjar García
3   Email: jb@taunais.com
4   Date: 15/9/25
5******************************************************************************/
6use pretty_simple_display::{DebugPretty, DisplaySimple};
7use serde::{Deserialize, Serialize};
8use serde_with::skip_serializing_none;
9
10/// Transfer state enumeration
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12#[serde(rename_all = "snake_case")]
13#[derive(Default)]
14pub enum TransferState {
15    /// Transfer is prepared but not yet confirmed
16    #[default]
17    Prepared,
18    /// Transfer has been confirmed
19    Confirmed,
20    /// Transfer has been cancelled
21    Cancelled,
22    /// Transfer is waiting for admin approval
23    WaitingForAdmin,
24    /// Transfer failed due to insufficient funds
25    InsufficientFunds,
26    /// Transfer failed due to withdrawal limit
27    WithdrawalLimit,
28}
29
30/// Transfer information
31#[skip_serializing_none]
32#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Serialize, Deserialize)]
33pub struct Transfer {
34    /// Transfer ID
35    pub id: i64,
36    /// Currency being transferred
37    pub currency: String,
38    /// Transfer amount
39    pub amount: f64,
40    /// Transfer fee
41    pub fee: f64,
42    /// Destination address
43    pub address: String,
44    /// Blockchain transaction ID
45    pub transaction_id: Option<String>,
46    /// Current transfer state
47    pub state: TransferState,
48    /// Creation timestamp (milliseconds since Unix epoch)
49    pub created_timestamp: i64,
50    /// Last update timestamp (milliseconds since Unix epoch)
51    pub updated_timestamp: i64,
52    /// Confirmation timestamp (milliseconds since Unix epoch)
53    pub confirmed_timestamp: Option<i64>,
54    /// Transfer type description
55    pub transfer_type: Option<String>,
56}
57
58impl Transfer {
59    /// Create a new transfer
60    pub fn new(
61        id: i64,
62        currency: String,
63        amount: f64,
64        fee: f64,
65        address: String,
66        created_timestamp: i64,
67    ) -> Self {
68        Self {
69            id,
70            currency,
71            amount,
72            fee,
73            address,
74            transaction_id: None,
75            state: TransferState::Prepared,
76            created_timestamp,
77            updated_timestamp: created_timestamp,
78            confirmed_timestamp: None,
79            transfer_type: None,
80        }
81    }
82
83    /// Set transaction ID
84    pub fn with_transaction_id(mut self, tx_id: String) -> Self {
85        self.transaction_id = Some(tx_id);
86        self
87    }
88
89    /// Set transfer state
90    pub fn with_state(mut self, state: TransferState) -> Self {
91        self.state = state;
92        self
93    }
94
95    /// Set transfer type
96    pub fn with_type(mut self, transfer_type: String) -> Self {
97        self.transfer_type = Some(transfer_type);
98        self
99    }
100
101    /// Confirm the transfer
102    pub fn confirm(&mut self, timestamp: i64) {
103        self.state = TransferState::Confirmed;
104        self.confirmed_timestamp = Some(timestamp);
105        self.updated_timestamp = timestamp;
106    }
107
108    /// Cancel the transfer
109    pub fn cancel(&mut self, timestamp: i64) {
110        self.state = TransferState::Cancelled;
111        self.updated_timestamp = timestamp;
112    }
113
114    /// Check if transfer is confirmed
115    pub fn is_confirmed(&self) -> bool {
116        matches!(self.state, TransferState::Confirmed)
117    }
118
119    /// Check if transfer is cancelled
120    pub fn is_cancelled(&self) -> bool {
121        matches!(self.state, TransferState::Cancelled)
122    }
123
124    /// Check if transfer is pending
125    pub fn is_pending(&self) -> bool {
126        matches!(
127            self.state,
128            TransferState::Prepared | TransferState::WaitingForAdmin
129        )
130    }
131
132    /// Get net amount (amount - fee)
133    pub fn net_amount(&self) -> f64 {
134        self.amount - self.fee
135    }
136}
137
138/// Collection of transfers
139#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Serialize, Deserialize)]
140pub struct Transfers {
141    /// List of transfers
142    pub transfers: Vec<Transfer>,
143}
144
145impl Transfers {
146    /// Create a new transfers collection
147    pub fn new() -> Self {
148        Self {
149            transfers: Vec::new(),
150        }
151    }
152
153    /// Add a transfer
154    pub fn add(&mut self, transfer: Transfer) {
155        self.transfers.push(transfer);
156    }
157
158    /// Get transfers by currency
159    pub fn by_currency(&self, currency: String) -> Vec<&Transfer> {
160        self.transfers
161            .iter()
162            .filter(|t| t.currency == currency)
163            .collect()
164    }
165
166    /// Get transfers by state
167    pub fn by_state(&self, state: TransferState) -> Vec<&Transfer> {
168        self.transfers.iter().filter(|t| t.state == state).collect()
169    }
170
171    /// Get pending transfers
172    pub fn pending(&self) -> Vec<&Transfer> {
173        self.transfers.iter().filter(|t| t.is_pending()).collect()
174    }
175
176    /// Get confirmed transfers
177    pub fn confirmed(&self) -> Vec<&Transfer> {
178        self.transfers.iter().filter(|t| t.is_confirmed()).collect()
179    }
180
181    /// Calculate total amount by currency
182    pub fn total_amount(&self, currency: String) -> f64 {
183        self.transfers
184            .iter()
185            .filter(|t| t.currency == currency)
186            .map(|t| t.amount)
187            .sum()
188    }
189
190    /// Calculate total fees by currency
191    pub fn total_fees(&self, currency: String) -> f64 {
192        self.transfers
193            .iter()
194            .filter(|t| t.currency == currency)
195            .map(|t| t.fee)
196            .sum()
197    }
198}
199impl Default for Transfers {
200    fn default() -> Self {
201        Self::new()
202    }
203}
204
205/// Subaccount transfer information
206#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Serialize, Deserialize)]
207pub struct SubaccountTransfer {
208    /// Transfer amount
209    pub amount: f64,
210    /// Currency being transferred
211    pub currency: String,
212    /// Destination subaccount ID
213    pub destination: i64,
214    /// Transfer ID
215    pub id: i64,
216    /// Source subaccount ID
217    pub source: i64,
218    /// Transfer state
219    pub state: TransferState,
220    /// Transfer timestamp (milliseconds since Unix epoch)
221    pub timestamp: i64,
222    /// Type of transfer
223    pub transfer_type: String,
224}
225
226impl SubaccountTransfer {
227    /// Create a new subaccount transfer
228    pub fn new(
229        id: i64,
230        amount: f64,
231        currency: String,
232        source: i64,
233        destination: i64,
234        timestamp: i64,
235    ) -> Self {
236        Self {
237            amount,
238            currency,
239            destination,
240            id,
241            source,
242            state: TransferState::Prepared,
243            timestamp,
244            transfer_type: "subaccount".to_string(),
245        }
246    }
247
248    /// Set transfer state
249    pub fn with_state(mut self, state: TransferState) -> Self {
250        self.state = state;
251        self
252    }
253
254    /// Set transfer type
255    pub fn with_type(mut self, transfer_type: String) -> Self {
256        self.transfer_type = transfer_type;
257        self
258    }
259
260    /// Check if transfer is between main account and subaccount
261    pub fn is_main_subaccount_transfer(&self) -> bool {
262        self.source == 0 || self.destination == 0
263    }
264
265    /// Check if transfer is between subaccounts
266    pub fn is_subaccount_to_subaccount(&self) -> bool {
267        self.source != 0 && self.destination != 0
268    }
269}